# LotteryChanceService 优化说明 ## 优化概述 本次优化对 `LotteryChanceService` 进行了全面的重构和优化,主要包括: - 新增用户抽奖机会获取记录表 - 优化常量管理和枚举设计 - 改进服务类逻辑和数据处理 - 增强统计分析功能 ## 主要优化内容 ### 1. 数据库表结构优化 #### 新增:用户抽奖机会获取记录表 `shop_lottery_user_chance_record` 替代原有的 JSON 字段存储,使用独立表记录用户获取抽奖机会的详细信息: ```sql 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. 枚举常量优化 #### 消除常量冲突 **保留的常量(用于后台渲染):** ```php // ============ 触发类型 ============ const TRIGGER_TYPE_BUY_GOODS = 1; // 购买商品 const TRIGGER_TYPE_ORDER_CONSUME = 2; // 订单消费 const TRIGGER_TYPE_RECHARGE = 3; // 充值 const TRIGGER_TYPE_TOTAL_CONSUME = 4; // 累计消费 ``` **机会获取类型(与条件类型保持一致):** ```php // ============ 机会获取类型(与条件类型保持一致,额外增加管理员赠送) ============ 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` 中管理 - 保持批量处理的常量配置 ```php const DEFAULT_BATCH_SIZE = 100; // 默认批量处理数量 const MAX_BATCH_SIZE = 1000; // 最大批量处理数量 ``` #### 简化的获取类型判断逻辑 ```php 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; } ``` #### 事务处理优化 ```php 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 模型 **主要功能:** - 完整的关联查询支持(活动、用户、条件、订单、管理员) - 提供获取器和修改器处理时间格式 - 支持数据验证和批量操作 - 丰富的统计查询方法 **关键方法:** ```php // 获取用户机会获取记录 LotteryUserChanceRecord::getUserChanceRecords($activityId, $userId, $page, $limit); // 获取活动机会获取统计 LotteryUserChanceRecord::getActivityChanceStats($activityId); // 获取用户机会获取统计 LotteryUserChanceRecord::getUserChanceStats($userId, $activityId); // 批量创建记录 LotteryUserChanceRecord::batchCreateRecords($records); // 数据验证 LotteryUserChanceRecord::validateRecord($data); ``` ### 5. 功能增强 #### 管理员手动赠送功能 ```php /** * 手动给用户增加抽奖机会(管理员操作) */ 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); } ``` #### 改进的统计查询 ```php // 获取用户机会详情(包含记录) 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 ]; } ``` #### 新增记录管理方法 ```php // 获取用户机会获取记录 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. 事务处理 ```php try { Db::startTrans(); // 更新用户机会表 $this->updateUserChance(); // 创建获取记录 $this->createChanceRecord(); Db::commit(); } catch (Exception $e) { Db::rollback(); throw $e; } ``` ### 2. 数据验证 - **输入验证**:所有输入参数都进行严格验证 - **业务验证**:验证业务逻辑的合理性 - **数据完整性**:确保关联数据的完整性 ### 3. 异常处理 - **参数异常**:详细的参数验证和错误提示 - **业务异常**:业务逻辑异常的处理和回滚 - **系统异常**:系统级异常的捕获和日志记录 ## 使用示例 ### 1. 订单完成后自动分发机会 ```php $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. 充值完成后自动分发机会 ```php $rechargeInfo = [ 'amount' => 50.00, 'type' => 'recharge' ]; $grantedChances = LotteryChanceService::checkAndGrantChanceForRecharge($rechargeInfo, $userId); ``` ### 3. 管理员手动赠送机会 ```php // 管理员给用户赠送抽奖机会 $result = LotteryChanceService::manualGrantChance( $activityId, // 活动ID $userId, // 用户ID 5, // 赠送机会数 '用户反馈奖励', // 赠送原因 $adminId // 管理员ID ); ``` ### 4. 获取用户机会记录 ```php // 获取用户在指定活动中的机会获取记录 $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. 获取活动统计 ```php // 获取活动的机会获取统计 $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. 数据迁移脚本 ```php // 从 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. **可维护性提升**:完善的文档、异常处理和事务管理 整体上,优化后的系统具有更好的性能、可维护性和扩展性,为后续的功能开发奠定了良好的基础。