handler()->set($lockKey, 1, 'NX', 'EX', 10); if (!$lock) { throw new Exception('操作太频繁,请稍后再试'); } try { // 8. 开始抽奖流程 return static::processDrawLottery($activity, $userId, $triggerType, $triggerOrderId, $triggerAmount); } finally { // 释放锁 Cache::store('redis')->handler()->del($lockKey); } } /** * 处理抽奖核心逻辑 */ private static function processDrawLottery($activity, $userId, $triggerType, $triggerOrderId, $triggerAmount) { // 开启事务 Db::startTrans(); try { // 1. 获取可用奖品 $prizes = static::getAvailablePrizes($activity); if (empty($prizes)) { throw new Exception('暂无可抽取的奖品'); } // 2. 执行抽奖算法 $selectedPrize = static::executeLotteryAlgorithm($prizes); // 3. 减少库存 if (!static::decreasePrizeStock($selectedPrize)) { throw new Exception('奖品库存不足'); } // 4. 消耗用户抽奖机会 $userChance = LotteryChanceService::getUserChance($activity->id, $userId); if (!LotteryChanceService::useChance($userChance)) { throw new Exception('抽奖机会使用失败'); } // 5. 创建抽奖记录 $isWin = $selectedPrize->type != LotteryEnum::PRIZE_TYPE_NO_PRIZE; $winInfo = $isWin ? static::buildWinInfo($selectedPrize) : []; $drawRecord = LotteryRecordService::createDrawRecord( $activity->id, $userId, $selectedPrize->id, $isWin, $triggerType, $triggerOrderId, $triggerAmount, $winInfo ); // 6. 如果中奖,创建中奖记录 $winRecord = null; if ($isWin) { $winRecord = LotteryRecordService::createWinRecord( $drawRecord->id, $activity->id, $userId, $selectedPrize->id, $selectedPrize->name, $selectedPrize->type, static::buildPrizeValue($selectedPrize) ); // 自动发放奖品 if ($selectedPrize->deliver_type == LotteryEnum::DELIVER_TYPE_AUTO) { LotteryRecordService::autoDeliverPrize($winRecord, $selectedPrize); } } // 7. 更新活动统计 static::updateActivityStats($activity, $isWin); // 提交事务 Db::commit(); // 8. 返回抽奖结果 return static::buildDrawResult($drawRecord, $selectedPrize, $winRecord); } catch (Exception $e) { Db::rollback(); throw $e; } } /** * 获取可用奖品列表 */ private static function getAvailablePrizes($activity) { $prizes = static::getValidPrizes($activity->id); // 如果开启按人数解锁功能 if ($activity->unlock_by_people) { $currentPeopleCount = LotteryChanceService::getActivityParticipants($activity->id); $prizes = $prizes->filter(function($prize) use ($currentPeopleCount) { return static::isPrizeUnlocked($prize, $currentPeopleCount); }); } return $prizes; } /** * 执行抽奖算法(基于概率权重) */ private static function executeLotteryAlgorithm($prizes) { // 计算总概率 $totalProbability = $prizes->sum('probability'); // 生成随机数 $randomNumber = mt_rand(1, $totalProbability * 100) / 100; // 按概率选择奖品 $currentProbability = 0; foreach ($prizes as $prize) { $currentProbability += $prize->probability; if ($randomNumber <= $currentProbability) { return $prize; } } // 保底返回第一个奖品(通常是未中奖) return $prizes->first(); } /** * 验证用户资格 */ private static function validateUserQualification($activity, $userId) { // 检查用户群体限制 switch ($activity->user_limit_type) { case LotteryEnum::USER_LIMIT_ALL: return true; case LotteryEnum::USER_LIMIT_LEVEL: return static::checkUserLevel($userId, $activity->user_limit_value); case LotteryEnum::USER_LIMIT_TAG: return static::checkUserTag($userId, $activity->user_limit_value); default: return false; } } /** * 检查用户等级 */ private static function checkUserLevel($userId, $limitValue) { if (empty($limitValue)) { return true; } $user = \app\common\model\User::find($userId); return $user && in_array($user->level, (array)$limitValue); } /** * 检查用户标签 */ private static function checkUserTag($userId, $limitValue) { if (empty($limitValue)) { return true; } // 这里需要根据实际的用户标签系统实现 // 暂时返回true return true; } /** * 检查用户抽奖次数限制 */ private static function checkUserDrawLimit($activity, $userId) { if (!$activity->person_limit_num) { return true; } $drawCount = LotteryRecordService::getUserDrawCount($activity->id, $userId); return $drawCount < $activity->person_limit_num; } /** * 检查订单是否已抽奖 */ private static function hasDrawnForOrder($activityId, $userId, $orderId) { return LotteryRecordService::hasUserDrawnForOrder($activityId, $userId, $orderId); } /** * 构建中奖信息 */ private static function buildWinInfo($prize) { return [ 'prize_id' => $prize->id, 'prize_name' => $prize->name, 'prize_type' => $prize->type, 'prize_image' => $prize->image, 'win_prompt' => $prize->win_prompt ]; } /** * 构建奖品价值信息 */ private static function buildPrizeValue($prize) { $prizeValue = [ 'type' => $prize->type, 'name' => $prize->name, 'image' => $prize->image ]; switch ($prize->type) { case LotteryEnum::PRIZE_TYPE_COUPON: $prizeValue['coupon_id'] = $prize->coupon_id; break; case LotteryEnum::PRIZE_TYPE_REDPACK: $prizeValue['amount'] = $prize->amount; break; case LotteryEnum::PRIZE_TYPE_SHOP_GOODS: $prizeValue['goods_id'] = $prize->goods_id; $prizeValue['goods_sku_id'] = $prize->goods_sku_id; break; case LotteryEnum::PRIZE_TYPE_CODE: $prizeValue['exchange_code'] = static::getAvailableExchangeCode($prize); break; } return $prizeValue; } /** * 自动发放奖品 */ private static function autoDeliverPrize($winRecord, $prize) { try { switch ($prize->type) { case LotteryEnum::PRIZE_TYPE_COUPON: static::deliverCoupon($winRecord, $prize); break; case LotteryEnum::PRIZE_TYPE_REDPACK: static::deliverRedPacket($winRecord, $prize); break; case LotteryEnum::PRIZE_TYPE_CODE: static::deliverExchangeCode($winRecord, $prize); break; case LotteryEnum::PRIZE_TYPE_SHOP_GOODS: static::deliverGoods($winRecord, $prize); break; } } catch (Exception $e) { LotteryRecordService::markWinRecordAsFailed($winRecord, $e->getMessage()); } } /** * 发放优惠券 */ private static function deliverCoupon($winRecord, $prize) { // 这里调用优惠券发放接口 // 示例代码,需要根据实际优惠券系统实现 LotteryRecordService::markWinRecordAsDelivered($winRecord, ['coupon_id' => $prize->coupon_id]); } /** * 发放红包 */ private static function deliverRedPacket($winRecord, $prize) { // 这里调用红包发放接口 // 示例代码,需要根据实际红包系统实现 LotteryRecordService::markWinRecordAsDelivered($winRecord, ['amount' => $prize->amount]); } /** * 发放兑换码 */ private static function deliverExchangeCode($winRecord, $prize) { $code = static::getAvailableExchangeCode($prize); if ($code) { LotteryRecordService::setWinRecordExchangeCode($winRecord, $code); static::markExchangeCodeUsed($prize, $code); LotteryRecordService::markWinRecordAsDelivered($winRecord, ['exchange_code' => $code]); } else { throw new Exception('兑换码已用完'); } } /** * 发放商城商品 */ private static function deliverGoods($winRecord, $prize) { // 这里可以自动加入购物车或创建订单 // 示例代码,需要根据实际商城系统实现 LotteryRecordService::markWinRecordAsDelivered($winRecord, [ 'goods_id' => $prize->goods_id, 'goods_sku_id' => $prize->goods_sku_id ]); } /** * 更新活动统计 */ private static function updateActivityStats($activity, $isWin) { $activity->total_draw_count += 1; if ($isWin) { $activity->total_win_count += 1; } $activity->save(); } /** * 构建抽奖结果 */ private static function buildDrawResult($drawRecord, $prize, $winRecord = null) { $result = [ 'draw_id' => $drawRecord->id, 'is_win' => $drawRecord->is_win, 'prize' => [ 'id' => $prize->id, 'name' => $prize->name, 'type' => $prize->type, 'type_text' => $prize->type_text, 'image' => $prize->image, 'win_prompt' => $prize->win_prompt ], 'draw_time' => $drawRecord->draw_time ]; if ($winRecord) { $result['win_record_id'] = $winRecord->id; $result['deliver_status'] = $winRecord->deliver_status; $result['exchange_code'] = $winRecord->exchange_code; } return $result; } /** * 获取用户抽奖机会 * @param int $activityId 活动ID * @param int $userId 用户ID * @return array */ public static function getUserChances($activityId, $userId) { $userChance = LotteryChanceService::getUserChance($activityId, $userId); if (!$userChance) { return [ 'total_chances' => 0, 'used_chances' => 0, 'remain_chances' => 0 ]; } 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 ]; } // ============ 从LotteryPrize模型移过来的业务逻辑方法 ============ /** * 检查奖品库存是否充足 */ public static function hasPrizeStock(LotteryPrize $prize, $quantity = 1) { return $prize->remain_stock >= $quantity; } /** * 减少奖品库存 */ public static function decreasePrizeStock(LotteryPrize $prize, $quantity = 1) { if (!static::hasPrizeStock($prize, $quantity)) { return false; } $prize->remain_stock -= $quantity; $prize->win_count += $quantity; return $prize->save(); } /** * 获取可用的兑换码 */ public static function getAvailableExchangeCode(LotteryPrize $prize) { if ($prize->type != LotteryEnum::PRIZE_TYPE_CODE) { return null; } $allCodes = $prize->exchange_codes_list; $usedCodes = $prize->used_codes_list; $availableCodes = array_diff($allCodes, $usedCodes); if (empty($availableCodes)) { return null; } return array_shift($availableCodes); } /** * 标记兑换码为已使用 */ public static function markExchangeCodeUsed(LotteryPrize $prize, $code) { $usedCodes = $prize->used_codes_list; if (!in_array($code, $usedCodes)) { $usedCodes[] = $code; $prize->used_codes = json_encode($usedCodes); return $prize->save(); } return true; } /** * 获取有效奖品(库存大于0且状态正常) */ public static function getValidPrizes($activityId) { return LotteryPrize::where('activity_id', $activityId) ->where('status', 1) ->where('remain_stock', '>', 0) ->order('sort_order', 'asc') ->select(); } /** * 检查奖品是否已解锁(按人数解锁功能) */ public static function isPrizeUnlocked(LotteryPrize $prize, $currentPeopleCount) { if (empty($prize->unlock_people_num)) { return true; } return $currentPeopleCount >= $prize->unlock_people_num; } /** * 检查活动是否正在进行 */ public static function isActivityRunning(LotteryActivity $activity) { $now = time(); return $activity->status == LotteryEnum::STATUS_ONGOING && $activity->start_time <= $now && $activity->end_time >= $now; } /** * 检查活动是否已结束 */ public static function isActivityEnded(LotteryActivity $activity) { $now = time(); return $activity->status == LotteryEnum::STATUS_ENDED || $activity->end_time < $now; } /** * 检查活动是否未开始 */ public static function isActivityNotStarted(LotteryActivity $activity) { $now = time(); return $activity->status == LotteryEnum::STATUS_NOT_STARTED && $activity->start_time > $now; } /** * 检查活动是否已暂停 */ public static function isActivitySuspended(LotteryActivity $activity) { return $activity->status == LotteryEnum::STATUS_SUSPENDED; } /** * 检查活动是否已取消 */ public static function isActivityCancelled(LotteryActivity $activity) { return $activity->status == LotteryEnum::STATUS_CANCELLED; } /** * 获取正在进行的活动 */ public static function getRunningActivities() { $now = time(); return LotteryActivity::where('status', LotteryEnum::STATUS_ONGOING) ->where('start_time', '<=', $now) ->where('end_time', '>=', $now) ->select(); } /** * 获取未开始的活动 */ public static function getNotStartedActivities() { $now = time(); return LotteryActivity::where('status', LotteryEnum::STATUS_NOT_STARTED) ->where('start_time', '>', $now) ->select(); } /** * 获取已结束的活动 */ public static function getEndedActivities() { $now = time(); return LotteryActivity::where('status', LotteryEnum::STATUS_ENDED) ->orWhere('end_time', '<', $now) ->select(); } /** * 获取可显示的活动(排除逻辑状态) */ public static function getDisplayableActivities() { $displayableStatuses = array_keys(LotteryEnum::getActivityStatusMap()); return LotteryActivity::whereIn('status', $displayableStatuses)->select(); } /** * 验证活动状态是否有效 */ public static function isValidActivityStatus(LotteryActivity $activity) { return LotteryEnum::isValidActivityStatus($activity->status); } /** * 验证开奖方式是否有效 */ public static function isValidLotteryType(LotteryActivity $activity) { return LotteryEnum::isValidLotteryType($activity->lottery_type); } }