|  | @@ -40,33 +40,44 @@ class LotteryService
 | 
	
		
			
				|  |  |          if (!$activity || !self::isActivityRunning($activity)) {
 | 
	
		
			
				|  |  |              throw new BusinessException('活动不存在或未开始', ErrorCodeEnum::LOTTERY_ACTIVITY_NOT_FOUND);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -        // 2. 验证抽奖时间
 | 
	
		
			
				|  |  | -        if (!self::isActivityRunning($activity)) {
 | 
	
		
			
				|  |  | -            throw new BusinessException('不在抽奖时间内', ErrorCodeEnum::LOTTERY_NOT_IN_TIME);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 2. 验证开奖时间(仅对按时间开奖有效)
 | 
	
		
			
				|  |  | +        if ($activity->lottery_type == LotteryEnum::LOTTERY_TYPE_TIME) {
 | 
	
		
			
				|  |  | +            $now = time();
 | 
	
		
			
				|  |  | +            if ($activity->lottery_time && $now >= $activity->lottery_time) {
 | 
	
		
			
				|  |  | +                throw new BusinessException('开奖时间已过,无法参与', ErrorCodeEnum::LOTTERY_NOT_IN_TIME);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        // 3. 验证用户资格
 | 
	
		
			
				|  |  | +        // 3. 验证参与人数限制(仅对按人数开奖有效)
 | 
	
		
			
				|  |  | +        if ($activity->lottery_type == LotteryEnum::LOTTERY_TYPE_PEOPLE) {
 | 
	
		
			
				|  |  | +            $drawStats = LotteryRecordService::getActivityDrawStats($activityId);
 | 
	
		
			
				|  |  | +            if ($drawStats['total_draw'] >= $activity->lottery_people_num) {
 | 
	
		
			
				|  |  | +                throw new BusinessException('参与人数已满,无法参与', ErrorCodeEnum::LOTTERY_REACH_LIMIT);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        // 4. 验证用户资格
 | 
	
		
			
				|  |  |          if (!static::validateUserQualification($activity, $userId)) {
 | 
	
		
			
				|  |  |              throw new BusinessException('用户不符合参与条件', ErrorCodeEnum::LOTTERY_USER_NOT_QUALIFIED);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        // 4. 检查用户抽奖机会
 | 
	
		
			
				|  |  | +        // 5. 检查用户抽奖机会
 | 
	
		
			
				|  |  |          $userChance = LotteryChanceService::getUserChance($activityId, $userId);
 | 
	
		
			
				|  |  |          if (!$userChance || !LotteryChanceService::hasChance($userChance)) {
 | 
	
		
			
				|  |  |              throw new BusinessException('没有抽奖机会', ErrorCodeEnum::LOTTERY_NO_CHANCE);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        // 5. 检查用户参与次数限制
 | 
	
		
			
				|  |  | +        // 6. 检查用户参与次数限制
 | 
	
		
			
				|  |  |          if (!static::checkUserDrawLimit($activity, $userId)) {
 | 
	
		
			
				|  |  |              throw new BusinessException('已达到参与次数上限', ErrorCodeEnum::LOTTERY_REACH_LIMIT);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        // 6. 防重复抽奖检查(基于订单)
 | 
	
		
			
				|  |  | +        // 7. 防重复抽奖检查(基于订单)
 | 
	
		
			
				|  |  |          if ($triggerOrderId && static::hasDrawnForOrder($activityId, $userId, $triggerOrderId)) {
 | 
	
		
			
				|  |  |              throw new BusinessException('该订单已参与过抽奖', ErrorCodeEnum::LOTTERY_ORDER_ALREADY_DRAWN);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        // TODO: 7. 使用分布式锁防止并发(暂时移除,后续实现)
 | 
	
		
			
				|  |  | +        // TODO: 8. 使用分布式锁防止并发(暂时移除,后续实现)
 | 
	
		
			
				|  |  |          // $lockKey = "lottery_lock_{$activityId}_{$userId}";
 | 
	
		
			
				|  |  |          // $lockAcquired = static::acquireLock($lockKey, 10);
 | 
	
		
			
				|  |  |          // if (!$lockAcquired) {
 | 
	
	
		
			
				|  | @@ -74,8 +85,8 @@ class LotteryService
 | 
	
		
			
				|  |  |          // }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          // try {
 | 
	
		
			
				|  |  | -            // 7. 开始抽奖流程
 | 
	
		
			
				|  |  | -            return static::processDrawLottery($activity, $userId, $triggerType, $triggerOrderId, $triggerAmount);
 | 
	
		
			
				|  |  | +            // 8. 根据开奖方式处理抽奖流程
 | 
	
		
			
				|  |  | +            return static::handleLotteryByType($activity, $userId, $triggerType, $triggerOrderId, $triggerAmount);
 | 
	
		
			
				|  |  |          // } finally {
 | 
	
		
			
				|  |  |          //     // 释放锁
 | 
	
		
			
				|  |  |          //     static::releaseLock($lockKey);
 | 
	
	
		
			
				|  | @@ -83,6 +94,29 @@ class LotteryService
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      /**
 | 
	
		
			
				|  |  | +     * 根据开奖方式处理抽奖
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    private static function handleLotteryByType($activity, $userId, $triggerType, $triggerOrderId, $triggerAmount)
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +        switch ($activity->lottery_type) {
 | 
	
		
			
				|  |  | +            case LotteryEnum::LOTTERY_TYPE_INSTANT:
 | 
	
		
			
				|  |  | +                // 即抽即中:直接执行抽奖
 | 
	
		
			
				|  |  | +                return static::processDrawLottery($activity, $userId, $triggerType, $triggerOrderId, $triggerAmount);
 | 
	
		
			
				|  |  | +                
 | 
	
		
			
				|  |  | +            case LotteryEnum::LOTTERY_TYPE_TIME:
 | 
	
		
			
				|  |  | +                // 按时间开奖:记录参与,等待定时任务开奖
 | 
	
		
			
				|  |  | +                return static::recordParticipation($activity, $userId, $triggerType, $triggerOrderId, $triggerAmount);
 | 
	
		
			
				|  |  | +                
 | 
	
		
			
				|  |  | +            case LotteryEnum::LOTTERY_TYPE_PEOPLE:
 | 
	
		
			
				|  |  | +                // 按人数开奖:记录参与,检查是否达到开奖人数
 | 
	
		
			
				|  |  | +                return static::handlePeopleBasedLottery($activity, $userId, $triggerType, $triggerOrderId, $triggerAmount);
 | 
	
		
			
				|  |  | +                
 | 
	
		
			
				|  |  | +            default:
 | 
	
		
			
				|  |  | +                throw new BusinessException('不支持的开奖方式', ErrorCodeEnum::LOTTERY_ACTIVITY_NOT_FOUND);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  |       * 处理抽奖核心逻辑
 | 
	
		
			
				|  |  |       */
 | 
	
		
			
				|  |  |      private static function processDrawLottery($activity, $userId, $triggerType, $triggerOrderId, $triggerAmount)
 | 
	
	
		
			
				|  | @@ -163,6 +197,78 @@ class LotteryService
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      /**
 | 
	
		
			
				|  |  | +     * 记录参与(用于按时间开奖和按人数开奖)
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    private static function recordParticipation($activity, $userId, $triggerType, $triggerOrderId, $triggerAmount)
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +        // 开启事务
 | 
	
		
			
				|  |  | +        Db::startTrans();
 | 
	
		
			
				|  |  | +        
 | 
	
		
			
				|  |  | +        try {
 | 
	
		
			
				|  |  | +            // 1. 消耗用户抽奖机会
 | 
	
		
			
				|  |  | +            $userChance = LotteryChanceService::getUserChance($activity->id, $userId);
 | 
	
		
			
				|  |  | +            if (!LotteryChanceService::useChance($userChance)) {
 | 
	
		
			
				|  |  | +                throw new Exception('抽奖机会使用失败');
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            // 2. 创建参与记录(未开奖状态)
 | 
	
		
			
				|  |  | +            $drawRecord = LotteryRecordService::createDrawRecord(
 | 
	
		
			
				|  |  | +                $activity->id,
 | 
	
		
			
				|  |  | +                $userId,
 | 
	
		
			
				|  |  | +                0, // 暂时没有奖品ID
 | 
	
		
			
				|  |  | +                0, // 未开奖
 | 
	
		
			
				|  |  | +                $triggerType,
 | 
	
		
			
				|  |  | +                $triggerOrderId,
 | 
	
		
			
				|  |  | +                $triggerAmount,
 | 
	
		
			
				|  |  | +                [] // 暂时没有中奖信息
 | 
	
		
			
				|  |  | +            );
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            // 提交事务
 | 
	
		
			
				|  |  | +            Db::commit();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            // 3. 返回参与结果
 | 
	
		
			
				|  |  | +            return [
 | 
	
		
			
				|  |  | +                'draw_id' => $drawRecord->id,
 | 
	
		
			
				|  |  | +                'is_win' => 0,
 | 
	
		
			
				|  |  | +                'status' => 'waiting', // 等待开奖
 | 
	
		
			
				|  |  | +                'lottery_type' => $activity->lottery_type,
 | 
	
		
			
				|  |  | +                'lottery_time' => $activity->lottery_time ?? 0,
 | 
	
		
			
				|  |  | +                'message' => $activity->lottery_type == LotteryEnum::LOTTERY_TYPE_TIME ? 
 | 
	
		
			
				|  |  | +                           '参与成功,等待' . date('Y-m-d H:i:s', $activity->lottery_time) . '开奖' : 
 | 
	
		
			
				|  |  | +                           '参与成功,等待开奖'
 | 
	
		
			
				|  |  | +            ];
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        } catch (Exception $e) {
 | 
	
		
			
				|  |  | +            Db::rollback();
 | 
	
		
			
				|  |  | +            throw $e;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 处理按人数开奖
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    private static function handlePeopleBasedLottery($activity, $userId, $triggerType, $triggerOrderId, $triggerAmount)
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +        // 先记录参与
 | 
	
		
			
				|  |  | +        $result = static::recordParticipation($activity, $userId, $triggerType, $triggerOrderId, $triggerAmount);
 | 
	
		
			
				|  |  | +        
 | 
	
		
			
				|  |  | +        // 检查是否达到开奖人数
 | 
	
		
			
				|  |  | +        $drawStats = LotteryRecordService::getActivityDrawStats($activity->id);
 | 
	
		
			
				|  |  | +        $participantCount = $drawStats['total_draw'];
 | 
	
		
			
				|  |  | +        
 | 
	
		
			
				|  |  | +        if ($participantCount >= $activity->lottery_people_num) {
 | 
	
		
			
				|  |  | +            // 达到人数,触发开奖(这里可以异步处理)
 | 
	
		
			
				|  |  | +            // TODO: 触发按人数开奖的处理逻辑
 | 
	
		
			
				|  |  | +            $result['message'] = '参与成功,已达到开奖人数,正在开奖中...';
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +            $remainingCount = $activity->lottery_people_num - $participantCount;
 | 
	
		
			
				|  |  | +            $result['message'] = "参与成功,还需要{$remainingCount}人参与即可开奖";
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        
 | 
	
		
			
				|  |  | +        return $result;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  |       * 获取可用奖品列表
 | 
	
		
			
				|  |  |       */
 | 
	
		
			
				|  |  |      private static function getAvailablePrizes($activity)
 |