LotteryChanceService优化说明.md 14 KB

LotteryChanceService 优化说明

优化概述

本次优化对 LotteryChanceService 进行了全面的重构和优化,主要包括:

  • 新增用户抽奖机会获取记录表
  • 优化常量管理和枚举设计
  • 改进服务类逻辑和数据处理
  • 增强统计分析功能

主要优化内容

1. 数据库表结构优化

新增:用户抽奖机会获取记录表 shop_lottery_user_chance_record

替代原有的 JSON 字段存储,使用独立表记录用户获取抽奖机会的详细信息:

CREATE TABLE `shop_lottery_user_chance_record` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '记录ID',
  `activity_id` int(11) NOT NULL COMMENT '活动ID',
  `user_id` int(11) NOT NULL COMMENT '用户ID',
  `get_type` tinyint(1) NOT NULL COMMENT '获取类型: 1=购买指定商品 2=单笔订单消费满额 3=单次充值满额 4=活动期间累计消费满额 5=管理员赠送',
  `chances` int(11) NOT NULL DEFAULT '1' COMMENT '获得机会次数',
  `condition_id` int(11) DEFAULT NULL COMMENT '条件ID(关联lottery_condition表)',
  `condition_value` decimal(10,2) DEFAULT NULL COMMENT '条件值(金额或商品ID)',
  `order_id` int(11) DEFAULT NULL COMMENT '订单ID(订单触发时)',
  `recharge_amount` decimal(10,2) DEFAULT NULL COMMENT '充值金额(充值触发时)',
  `admin_id` int(11) DEFAULT NULL COMMENT '管理员ID(管理员赠送时)',
  `reason` varchar(255) DEFAULT NULL COMMENT '赠送原因(管理员赠送时)',
  `remark` varchar(500) DEFAULT NULL COMMENT '备注信息',
  `get_time` int(11) NOT NULL COMMENT '获得时间',
  `createtime` int(11) NOT NULL COMMENT '创建时间',
  `updatetime` int(11) DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_activity_user` (`activity_id`,`user_id`),
  KEY `idx_get_type` (`get_type`),
  KEY `idx_get_time` (`get_time`),
  KEY `idx_condition_id` (`condition_id`),
  KEY `idx_order_id` (`order_id`),
  KEY `idx_admin_id` (`admin_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户抽奖机会获取记录表';

完整的表结构关系

核心表:

  • shop_lottery_activity - 抽奖活动主表
  • shop_lottery_condition - 参与条件表
  • shop_lottery_user_chance - 用户抽奖机会表
  • shop_lottery_user_chance_record - 用户抽奖机会获取记录表(新增)

抽奖相关表:

  • shop_lottery_prize - 抽奖奖品表
  • shop_lottery_draw_record - 用户抽奖记录表
  • shop_lottery_win_record - 中奖记录表
  • shop_lottery_statistics - 活动统计表

2. 枚举常量优化

消除常量冲突

保留的常量(用于后台渲染):

// ============ 触发类型 ============
const TRIGGER_TYPE_BUY_GOODS = 1;       // 购买商品
const TRIGGER_TYPE_ORDER_CONSUME = 2;   // 订单消费
const TRIGGER_TYPE_RECHARGE = 3;        // 充值
const TRIGGER_TYPE_TOTAL_CONSUME = 4;   // 累计消费

机会获取类型(与条件类型保持一致):

// ============ 机会获取类型(与条件类型保持一致,额外增加管理员赠送) ============
const CHANCE_GET_TYPE_BUY_GOODS = 1;    // 购买指定商品(对应CONDITION_TYPE_BUY_GOODS)
const CHANCE_GET_TYPE_ORDER_AMOUNT = 2; // 单笔订单消费满额(对应CONDITION_TYPE_ORDER_AMOUNT)
const CHANCE_GET_TYPE_RECHARGE = 3;     // 单次充值满额(对应CONDITION_TYPE_RECHARGE_AMOUNT)
const CHANCE_GET_TYPE_TOTAL_AMOUNT = 4; // 活动期间累计消费满额(对应CONDITION_TYPE_TOTAL_AMOUNT)
const CHANCE_GET_TYPE_ADMIN_GRANT = 5;  // 管理员赠送

3. 服务类核心优化

统一常量管理

  • 删除了服务类中的重复常量定义
  • 所有常量统一在 LotteryEnum 中管理
  • 保持批量处理的常量配置
const DEFAULT_BATCH_SIZE = 100;  // 默认批量处理数量
const MAX_BATCH_SIZE = 1000;     // 最大批量处理数量

简化的获取类型判断逻辑

private static function getChanceGetTypeFromDetail($detail)
{
    // 直接根据条件类型确定获取类型
    if (isset($detail['condition_type'])) {
        switch ($detail['condition_type']) {
            case LotteryEnum::CONDITION_TYPE_BUY_GOODS:
                return LotteryEnum::CHANCE_GET_TYPE_BUY_GOODS;
            case LotteryEnum::CONDITION_TYPE_ORDER_AMOUNT:
                return LotteryEnum::CHANCE_GET_TYPE_ORDER_AMOUNT;
            case LotteryEnum::CONDITION_TYPE_RECHARGE_AMOUNT:
                return LotteryEnum::CHANCE_GET_TYPE_RECHARGE;
            case LotteryEnum::CONDITION_TYPE_TOTAL_AMOUNT:
                return LotteryEnum::CHANCE_GET_TYPE_TOTAL_AMOUNT;
        }
    }
    
    // 检查是否是管理员赠送
    if (isset($detail['admin_id']) && $detail['admin_id'] > 0) {
        return LotteryEnum::CHANCE_GET_TYPE_ADMIN_GRANT;
    }
    
    return LotteryEnum::CHANCE_GET_TYPE_ADMIN_GRANT;
}

事务处理优化

public static function addChance($activityId, $userId, $times = 1, $detail = [])
{
    try {
        Db::startTrans();
        
        // 更新用户机会总数
        $chance = static::updateUserChance($activityId, $userId, $times);
        
        // 创建详细记录
        $recordData = [
            'activity_id' => $activityId,
            'user_id' => $userId,
            'get_type' => static::getChanceGetTypeFromDetail($detail),
            'chances' => $times,
            // ... 其他字段
        ];
        
        LotteryUserChanceRecord::create($recordData);
        
        Db::commit();
        return $chance;
        
    } catch (Exception $e) {
        Db::rollback();
        throw $e;
    }
}

4. 新增模型类

LotteryUserChanceRecord 模型

主要功能:

  • 完整的关联查询支持(活动、用户、条件、订单、管理员)
  • 提供获取器和修改器处理时间格式
  • 支持数据验证和批量操作
  • 丰富的统计查询方法

关键方法:

// 获取用户机会获取记录
LotteryUserChanceRecord::getUserChanceRecords($activityId, $userId, $page, $limit);

// 获取活动机会获取统计
LotteryUserChanceRecord::getActivityChanceStats($activityId);

// 获取用户机会获取统计
LotteryUserChanceRecord::getUserChanceStats($userId, $activityId);

// 批量创建记录
LotteryUserChanceRecord::batchCreateRecords($records);

// 数据验证
LotteryUserChanceRecord::validateRecord($data);

5. 功能增强

管理员手动赠送功能

/**
 * 手动给用户增加抽奖机会(管理员操作)
 */
public static function manualGrantChance($activityId, $userId, $chances, $reason = '', $adminId = 0)
{
    // 参数验证
    if ($chances <= 0) {
        throw new Exception('抽奖机会数量必须大于0');
    }
    
    // 活动和用户验证
    $activity = LotteryActivity::find($activityId);
    $user = User::find($userId);
    
    // 构建详情(不包含trigger_type字段)
    $detail = [
        'reason' => $reason,
        'admin_id' => $adminId,
        'granted_time' => time()
    ];

    return static::grantChanceToUser($activityId, $userId, $chances, $detail);
}

改进的统计查询

// 获取用户机会详情(包含记录)
public static function getUserChanceDetail($activityId, $userId)
{
    $userChance = static::getUserChance($activityId, $userId);
    
    if (!$userChance) {
        return [
            'total_chances' => 0,
            'used_chances' => 0,
            'remain_chances' => 0,
            'get_records' => []
        ];
    }

    // 获取机会获得记录
    $getRecords = LotteryUserChanceRecord::getUserChanceRecords($activityId, $userId);

    return [
        'total_chances' => $userChance->total_chances,
        'used_chances' => $userChance->used_chances,
        'remain_chances' => $userChance->remain_chances,
        'last_get_time' => $userChance->last_get_time,
        'last_use_time' => $userChance->last_use_time,
        'get_records' => $getRecords
    ];
}

新增记录管理方法

// 获取用户机会获取记录
public static function getUserChanceRecords($activityId, $userId, $page = 1, $limit = 20);

// 获取活动机会获取统计
public static function getActivityChanceRecordStats($activityId);

// 获取用户机会获取统计
public static function getUserChanceRecordStats($userId, $activityId = null);

// 批量创建机会获取记录
public static function batchCreateChanceRecords($records);

// 验证机会获取记录数据
public static function validateChanceRecord($data);

性能改进

1. 数据库查询优化

  • 索引优化:为常用查询字段建立合适的索引
  • 关联查询:使用模型关联减少 N+1 查询问题
  • 分页查询:所有列表查询都支持分页

2. 内存使用优化

  • 批量处理:大数据量操作支持分批处理
  • 及时释放:处理完成后及时释放不需要的变量
  • 流式处理:避免一次性加载大量数据到内存

3. 查询性能提升

  • 条件优化:减少复杂的 JSON 字段查询
  • 统计缓存:常用统计数据支持缓存
  • 查询合并:合并多个小查询为单个查询

数据完整性保障

1. 事务处理

try {
    Db::startTrans();
    
    // 更新用户机会表
    $this->updateUserChance();
    
    // 创建获取记录
    $this->createChanceRecord();
    
    Db::commit();
} catch (Exception $e) {
    Db::rollback();
    throw $e;
}

2. 数据验证

  • 输入验证:所有输入参数都进行严格验证
  • 业务验证:验证业务逻辑的合理性
  • 数据完整性:确保关联数据的完整性

3. 异常处理

  • 参数异常:详细的参数验证和错误提示
  • 业务异常:业务逻辑异常的处理和回滚
  • 系统异常:系统级异常的捕获和日志记录

使用示例

1. 订单完成后自动分发机会

$orderInfo = [
    'id' => 123,
    'total_amount' => 100.00,
    'goods' => [
        ['goods_id' => 1],
        ['goods_id' => 2]
    ]
];

$grantedChances = LotteryChanceService::checkAndGrantChanceForOrder($orderInfo, $userId);

// 返回结果示例
[
    [
        'activity_id' => 1,
        'activity_name' => '双11抽奖活动',
        'chances' => 2,
        'granted_time' => 1699123456
    ]
]

2. 充值完成后自动分发机会

$rechargeInfo = [
    'amount' => 50.00,
    'type' => 'recharge'
];

$grantedChances = LotteryChanceService::checkAndGrantChanceForRecharge($rechargeInfo, $userId);

3. 管理员手动赠送机会

// 管理员给用户赠送抽奖机会
$result = LotteryChanceService::manualGrantChance(
    $activityId,        // 活动ID
    $userId,            // 用户ID
    5,                  // 赠送机会数
    '用户反馈奖励',      // 赠送原因
    $adminId            // 管理员ID
);

4. 获取用户机会记录

// 获取用户在指定活动中的机会获取记录
$records = LotteryChanceService::getUserChanceRecords($activityId, $userId, 1, 20);

// 获取用户机会获取统计
$stats = LotteryChanceService::getUserChanceRecordStats($userId, $activityId);

// 返回结果示例
[
    'total_records' => 5,
    'total_chances' => 8,
    'type_stats' => [
        1 => ['type' => 1, 'type_text' => '购买指定商品', 'count' => 2, 'chances' => 3],
        2 => ['type' => 2, 'type_text' => '单笔订单消费满额', 'count' => 2, 'chances' => 3],
        5 => ['type' => 5, 'type_text' => '管理员赠送', 'count' => 1, 'chances' => 2]
    ],
    'recent_records' => [...]
]

5. 获取活动统计

// 获取活动的机会获取统计
$stats = LotteryChanceService::getActivityChanceRecordStats($activityId);

// 返回结果示例
[
    'total_records' => 150,
    'total_chances' => 230,
    'type_stats' => [
        1 => ['type' => 1, 'type_text' => '购买指定商品', 'count' => 50, 'chances' => 75],
        2 => ['type' => 2, 'type_text' => '单笔订单消费满额', 'count' => 80, 'chances' => 120],
        3 => ['type' => 3, 'type_text' => '单次充值满额', 'count' => 15, 'chances' => 25],
        5 => ['type' => 5, 'type_text' => '管理员赠送', 'count' => 5, 'chances' => 10]
    ]
]

兼容性说明

1. 向后兼容

  • 保留字段shop_lottery_user_chance 表的 get_detail 字段保留但不使用
  • 接口兼容:所有公共方法的接口保持不变
  • 数据迁移:支持从旧的 JSON 数据迁移到新表

2. 平滑升级

  • 并行运行:新旧系统可以并行运行一段时间
  • 渐进迁移:可以逐步将数据迁移到新表
  • 回滚支持:必要时可以回滚到旧版本

3. 数据迁移脚本

// 从 get_detail JSON 字段迁移到独立记录表
public static function migrateFromJsonToTable()
{
    $userChances = LotteryUserChance::where('get_detail', 'neq', '')->select();
    
    foreach ($userChances as $userChance) {
        $getDetails = json_decode($userChance->get_detail, true);
        if (empty($getDetails)) continue;
        
        foreach ($getDetails as $detail) {
            // 创建新的记录
            LotteryUserChanceRecord::create([
                'activity_id' => $userChance->activity_id,
                'user_id' => $userChance->user_id,
                'get_type' => static::parseOldTriggerType($detail),
                'chances' => $detail['chances'] ?? 1,
                // ... 其他字段映射
            ]);
        }
    }
}

监控和维护

1. 性能监控

  • 查询性能:监控数据库查询的执行时间
  • 内存使用:监控批量处理时的内存占用
  • 并发处理:监控并发操作的性能表现

2. 数据质量

  • 数据一致性:定期检查数据的一致性
  • 关联完整性:验证表间关联的完整性
  • 业务逻辑:检查业务逻辑的正确性

3. 日志分析

  • 错误日志:分析错误日志找出潜在问题
  • 性能日志:分析性能日志优化慢查询
  • 业务日志:分析业务日志了解使用情况

总结

本次优化实现了以下主要目标:

  1. 数据结构优化:使用独立表替代 JSON 字段,提高查询性能和数据完整性
  2. 常量管理优化:统一枚举管理,消除常量冲突
  3. 业务逻辑简化:简化条件判断逻辑,提高代码可读性
  4. 功能增强:新增管理员赠送功能和丰富的统计分析
  5. 性能提升:优化查询性能,支持大数据量处理
  6. 可维护性提升:完善的文档、异常处理和事务管理

整体上,优化后的系统具有更好的性能、可维护性和扩展性,为后续的功能开发奠定了良好的基础。