|
@@ -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)
|