Files
dafuweng-saiadmin6.x/server/app/api/controller/GameController.php

259 lines
9.8 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace app\api\controller;
use support\Log;
use support\Request;
use support\Response;
use support\think\Db;
use app\api\logic\GameLogic;
use app\api\logic\PlayStartLogic;
use app\api\util\ReturnCode;
use app\dice\model\config\DiceConfig;
use app\dice\model\play_record\DicePlayRecord;
use app\dice\model\player\DicePlayer;
use app\dice\model\reward\DiceRewardConfig;
use app\api\controller\BaseController;
use app\api\util\ApiLang;
use plugin\saiadmin\exception\ApiException;
/**
* 游戏相关接口(购买抽奖券等)
*/
class GameController extends BaseController
{
/**
* 获取游戏配置(按 group 分组)
* GET /api/game/config
* header: lang = zh | en
* 返回 data[group] = [ { name, title, value, create_time, update_time }, ... ]
* - lang=zh 或默认title/value = 中文
* - lang=en若存在英文配置则返回 title_en/value_en否则回退到中文
*/
public function config(Request $request): Response
{
$rows = DiceConfig::select('name', 'group', 'title', 'title_en', 'value', 'value_en', 'create_time', 'update_time')->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 | 101次/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('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);
}
/**
* 开始游戏(抽奖一局)
* 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 must be 0 or 1', ReturnCode::PARAMS_ERROR);
}
$player = DicePlayer::find($userId);
if (!$player) {
return $this->fail('User not found', ReturnCode::NOT_FOUND);
}
$minEv = DiceRewardConfig::getCachedMinRealEv();
$minCoin = abs($minEv + 100);
$coin = (float) $player->coin;
if ($coin < $minCoin) {
$msg = ApiLang::translateParams('Balance %s is less than %s, cannot continue', [$coin, $minCoin], $request);
return $this->success([], $msg);
}
$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);
$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('Service timeout: ' . $msg);
} finally {
Db::execute('SELECT RELEASE_LOCK(?)', [$lockName]);
}
}
}