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次/1coin, 5次/5coin, 10次/10coin) */ // 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('Invalid lottery ticket purchase', 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); } /** * 获取底注配置(全部) * GET/any /api/game/anteConfig * header: token(TokenMiddleware 注入) * 返回:dice_ante_config 列表(包含 mult/is_default 等字段) */ public function anteConfig(Request $request): Response { // 用于后续抽奖校验:在接口中实例化 model,后续逻辑可复用相同的数据读取方式。 $anteConfigModel = new DiceAnteConfig(); $rows = $anteConfigModel->order('id', 'asc')->select()->toArray(); return $this->success($rows); } /** * 开始游戏(抽奖一局) * 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; } $ante = $request->post('ante'); if ($ante !== null) { $ante = (int) $ante; } if (!in_array($direction, [0, 1], true)) { return $this->fail('direction must be 0 or 1', ReturnCode::PARAMS_ERROR); } if (!is_int($ante) || $ante <= 0) { return $this->fail('ante must be a positive integer', ReturnCode::PARAMS_ERROR); } $player = DicePlayer::find($userId); if (!$player) { return $this->fail('User not found', ReturnCode::NOT_FOUND); } $lockName = 'play_start_' . $userId; $lockResult = Db::query('SELECT GET_LOCK(?, 30) as l', [$lockName]); if (empty($lockResult) || (int) ($lockResult[0]['l'] ?? 0) !== 1) { return $this->fail('too many requests, please try again later', ReturnCode::BUSINESS_ERROR); } try { $logic = new PlayStartLogic(); $data = $logic->run($userId, (int) $direction, $ante); $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)) { $rewardTier = array_key_exists('reward_tier', $data) ? (string) ($data['reward_tier'] ?? '') : ''; $targetIndex = array_key_exists('target_index', $data) ? (int) ($data['target_index'] ?? 0) : 0; if ($rewardTier !== 'BIGWIN' && $targetIndex > 0) { $configRow = DiceRewardConfig::getCachedById($targetIndex); 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; } } } $data['tier'] = $data['reward_tier'] ?? ''; 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, 'direction' => $direction, 'reward_tier' => '', '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('Service timeout: ' . $msg); } finally { Db::execute('SELECT RELEASE_LOCK(?)', [$lockName]); } } }