where('game_id', $gameId) ->where('department_id', $departmentId) ->where('status', 1) ->where('total_remaining', '>', 0) ->where('daily_remaining', '>', 0) ->lockForUpdate() ->get() ->toArray(); if (empty($prizes)) { DB::rollBack(); return [ 'success' => false, 'message' => '当前游戏不可用' ]; } $game = Game::query()->select('id', 'game_type', 'consume')->where('id', $gameId)->first()->toArray(); // 2. 计算中奖结果 $result = $this->calculateWinning($player,$prizes, $game['consume']); $prizeId = $result['prize_id']; $prizeName = $result['prize_name']; $prizeType = $result['prize_type']; $game['department_id'] = $departmentId; // 3. 记录抽奖信息 $this->createRecord($player->id, $result, $game, $ip); if ($result['prize_type'] == 3) { $message = '很遗憾,未中奖'; } else { $message = '恭喜获得' . $prizeName; } DB::commit(); return [ 'prize_id' => $prizeId, 'prize_name' => $prizeName, 'prize_type' => $prizeType, 'consume' => $game['consume'], 'message' => $message ]; } catch (Exception $e) { DB::rollBack(); Log::error("抽奖失败:{$e->getMessage()}", [ 'user_id' => $player->id, 'trace' => $e->getTraceAsString() ]); return [ 'success' => false, 'message' => '抽奖过程异常,请稍后再试' ]; } } /** * 计算中奖结果 * @param array $prizes 有效奖品列表 * @return array 中奖信息 * @throws Exception */ private function calculateWinning($player, array $prizes, $consume): array { // 计算总概率权重 $totalProb = array_sum(array_column($prizes, 'probability')); if ($totalProb <= 0) { return ['prize_id' => null, 'type' => Prize::PRIZE_TYPE_LOSE, 'prize_name' => '未中奖', 'prize_pic' => null]; } $player->wallet->decrement('money', $consume); // 生成随机数(0到总权重之间) $random = mt_rand(1, $totalProb * 10000) / 10000; // 提高精度 $currentSum = 0; foreach ($prizes as $prize) { $currentSum += $prize['probability']; if ($random <= $currentSum) { // 检查并扣减库存 $this->deductStock($prize['id']); return [ 'prize_id' => $prize['id'], 'prize_type' => $prize['type'], 'prize_name' => $prize['name'], 'prize_pic' => $prize['pic'] ]; } } return ['prize_id' => null, 'type' => Prize::PRIZE_TYPE_LOSE, 'prize_name' => '未中奖', 'prize_pic' => null]; } /** * 扣减库存 * @param int $prizeId 奖品ID * @throws Exception */ private function deductStock(int $prizeId): void { $prize = Prize::query()->findOrFail($prizeId); // 再次检查库存(防止并发超卖) if ($prize->total_remaining <= 0 || $prize->daily_remaining <= 0) { throw new \Exception("奖品库存不足"); } $prize->decrement('total_remaining'); $prize->decrement('daily_remaining'); $prize->save(); } /** * 创建抽奖记录 * @param int $userId 用户ID * @param array|null $prize 奖品 * @param string $ip IP地址 */ private function createRecord(int $userId, ?array $prize, array $game, string $ip): void { DrawRecord::query()->create([ 'uid' => $userId, 'prize_id' => $prize['prize_id'], 'prize_type' => $prize['prize_type'], 'prize_name' => $prize['prize_name'], 'prize_pic' => $prize['prize_pic'], 'game_id' => $game['id'], 'consume' => $game['consume'], 'game_type' => $game['game_type'], 'department_id' => $game['department_id'], 'draw_time' => date('Y-m-d H:i:s'), 'ip' => $ip ]); } }