get(); $lang = $request->header('lang', 'zh'); if (!is_string($lang) || $lang === '') { $lang = 'zh'; } $langLower = strtolower($lang); $isEn = $langLower === 'en' || str_starts_with($langLower, 'en-'); $data = []; foreach ($rows as $row) { $group = $row->group ?? ''; if (!isset($data[$group])) { $data[$group] = []; } $title = $row->title; $value = $row->value; if ($isEn) { $titleEn = $row->title_en ?? ''; $valueEn = $row->value_en ?? ''; if ($titleEn !== '') { $title = $titleEn; } if ($valueEn !== '') { $value = $valueEn; } } $data[$group][] = [ 'name' => $row->name, 'title' => $title, 'value' => $value, 'create_time' => $row->create_time, 'update_time' => $row->update_time, ]; } return $this->success($data); } /** * 购买抽奖券 * POST /api/game/buyLotteryTickets * header: token(由 TokenMiddleware 注入 request->player_id) * body: count = 1 | 5 | 10(1次/100coin, 5次/500coin, 10次/1000coin) */ public function buyLotteryTickets(Request $request): Response { $userId = (int) ($request->player_id ?? 0); $count = (int) $request->post('count', 0); if (!in_array($count, [1, 5, 10], true)) { return $this->fail('购买抽奖券错误', ReturnCode::PARAMS_ERROR); } try { $logic = new GameLogic(); $data = $logic->buyLotteryTickets($userId, $count); return $this->success($data); } catch (ApiException $e) { $msg = $e->getMessage(); if ($msg === '平台币不足') { $player = DicePlayer::find($userId); $coin = $player ? (float) $player->coin : 0; return $this->success(['coin' => $coin], $msg); } return $this->fail($msg, ReturnCode::BUSINESS_ERROR); } } /** * 获取彩金池(中奖配置表) * GET /api/game/lotteryPool * header: token * 返回 DiceRewardConfig 列表(彩金池/中奖配置),不包含 tier=BIGWIN */ public function lotteryPool(Request $request): Response { $list = DiceRewardConfig::getCachedList(); $list = array_values(array_filter($list, function ($row) { return (string) ($row['tier'] ?? '') !== 'BIGWIN'; })); $lang = $request->header('lang', 'zh'); if (!is_string($lang) || $lang === '') { $lang = 'zh'; } $langLower = strtolower($lang); $isEn = $langLower === 'en' || str_starts_with($langLower, 'en-'); if ($isEn) { foreach ($list as $index => $row) { $uiEn = ''; if (is_array($row) && array_key_exists('ui_text_en', $row) && $row['ui_text_en'] !== null) { $uiEn = (string) $row['ui_text_en']; } if ($uiEn !== '') { $row['ui_text'] = $uiEn; } $list[$index] = $row; } } return $this->success($list); } /** * 开始游戏(抽奖一局) * POST /api/game/playStart * header: token(由 TokenMiddleware 注入 request->player_id) * body: direction 必传,0=无 1=中奖 */ public function playStart(Request $request): Response { $userId = (int) ($request->player_id ?? 0); $direction = $request->post('direction'); if ($direction !== null) { $direction = (int) $direction; } if (!in_array($direction, [0, 1], true)) { return $this->fail('direction 必须为 0 或 1', ReturnCode::PARAMS_ERROR); } $player = DicePlayer::find($userId); if (!$player) { return $this->fail('用户不存在', ReturnCode::NOT_FOUND); } $minEv = DiceRewardConfig::getCachedMinRealEv(); $minCoin = abs($minEv + 100); $coin = (float) $player->coin; if ($coin < $minCoin) { $msg = ApiLang::translateParams('当前玩家余额%s小于%s无法继续游戏', [$coin, $minCoin], $request); return $this->success([], $msg); } try { $logic = new PlayStartLogic(); $data = $logic->run($userId, (int)$direction); $lang = $request->header('lang', 'zh'); if (!is_string($lang) || $lang === '') { $lang = 'zh'; } $langLower = strtolower($lang); $isEn = $langLower === 'en' || str_starts_with($langLower, 'en-'); if (is_array($data) && array_key_exists('reward_config_id', $data)) { $rewardConfigId = (int) $data['reward_config_id']; if ($rewardConfigId > 0) { $configRow = DiceRewardConfig::getCachedById($rewardConfigId); if ($configRow !== null) { $uiText = ''; $uiTextEn = ''; if (array_key_exists('ui_text', $configRow) && $configRow['ui_text'] !== null) { $uiText = (string) $configRow['ui_text']; } if (array_key_exists('ui_text_en', $configRow) && $configRow['ui_text_en'] !== null) { $uiTextEn = (string) $configRow['ui_text_en']; } if ($isEn && $uiTextEn !== '') { $data['ui_text'] = $uiTextEn; } else { $data['ui_text'] = $uiText; } $data['ui_text_en'] = $uiTextEn; } } } return $this->success($data); } catch (ApiException $e) { return $this->fail($e->getMessage(), ReturnCode::BUSINESS_ERROR); } catch (\Throwable $e) { // 记录抽奖逻辑抛出的真实异常,便于排查“服务超时,没有原因” Log::error('playStart 异常: ' . $e->getMessage(), [ 'file' => $e->getFile(), 'line' => $e->getLine(), 'trace' => $e->getTraceAsString(), 'player_id' => $userId, 'direction' => $direction, ]); $timeoutRecord = null; $timeout_message = ''; $adminId = null; try { $timeoutPlayer = DicePlayer::find($userId); $adminId = ($timeoutPlayer && ($timeoutPlayer->admin_id ?? null)) ? (int) $timeoutPlayer->admin_id : null; } catch (\Throwable $_) { } try { $timeoutRecord = DicePlayRecord::create([ 'player_id' => $userId, 'admin_id' => $adminId, 'lottery_config_id' => 0, 'lottery_type' => 0, 'is_win' => 0, 'win_coin' => 0, 'super_win_coin' => 0, 'reward_win_coin' => 0, 'use_coins' => 0, 'direction' => $direction, 'reward_config_id' => 0, 'start_index' => 0, 'target_index' => 0, 'roll_array' => '[]', 'roll_number' => 0, 'status' => PlayStartLogic::RECORD_STATUS_TIMEOUT, ]); } catch (\Exception $inner) { $timeout_message = $inner->getMessage(); Log::error('游玩记录写入超时: ' . $inner->getMessage()); } $payload = $timeoutRecord ? ['record' => $timeoutRecord->toArray()] : []; $msg = $timeout_message !== '' ? $timeout_message : $e->getMessage(); if ($msg === '') { $msg = '没有原因'; } return $this->fail('服务超时,' . $msg); } } }