菜单和部分接口返回对语言进行适配

This commit is contained in:
2026-04-20 10:27:01 +08:00
parent 24aab111b5
commit 025cce3e3e
5 changed files with 209 additions and 24 deletions

View File

@@ -0,0 +1,103 @@
<?php
declare(strict_types=1);
/**
* 后台 admin_rule.title 中文(及少数英文占位)→ 英文展示。
* 新增菜单时若标题为中文,请在此补充映射,或在库中直接使用英文标题。
*/
return [
// 通用按钮
'查看' => 'View',
'添加' => 'Add',
'编辑' => 'Edit',
'删除' => 'Delete',
'保存' => 'Save',
'快速排序' => 'Quick sort',
'还原' => 'Restore',
'回滚' => 'Rollback',
'查看详情' => 'View details',
'生成' => 'Generate',
'安装' => 'Install',
'调整状态' => 'Change state',
'卸载' => 'Uninstall',
'更新' => 'Update',
'审核通过' => 'Approve',
'审核驳回' => 'Reject',
// 安装包默认菜单
'控制台' => 'Dashboard',
'权限管理' => 'Permission management',
'角色组管理' => 'Role groups',
'管理员管理' => 'Administrators',
'菜单规则管理' => 'Menu rules',
'管理员日志管理' => 'Admin logs',
'常规管理' => 'Routine',
'系统配置' => 'System configuration',
'附件管理' => 'Attachments',
'个人资料' => 'Profile',
'数据安全管理' => 'Data security',
'数据回收站' => 'Data recycle bin',
'敏感数据修改记录' => 'Sensitive data change log',
'数据回收规则管理' => 'Data recycle rules',
'敏感字段规则管理' => 'Sensitive field rules',
'模块市场' => 'Module store',
'CRUD代码生成' => 'CRUD generator',
// 会员端 user_rule若出现在后台接口中
'我的账户' => 'My account',
'账户概览' => 'Overview',
'积分记录' => 'Score log',
'余额记录' => 'Balance log',
// 业务扩展菜单
'用户管理' => 'User management',
'用户列表' => 'User list',
'订单管理' => 'Order management',
'游戏管理' => 'Game management',
'配置管理' => 'Configuration',
'游戏配置' => 'Game configuration',
'常规配置' => 'General settings',
'记录管理' => 'Records',
'游戏对局' => 'Game rounds',
'游戏对局记录' => 'Game records',
'游戏实时对局' => 'Game live',
'压注订单' => 'Bet orders',
'充值订单' => 'Deposit orders',
'提现订单' => 'Withdraw orders',
'用户钱包流水' => 'User wallet transactions',
'36字花字典' => '36 Zihua dictionary',
'代理与结算' => 'Agents & settlement',
'代理结算周期' => 'Agent settlement periods',
'代理佣金记录' => 'Agent commission records',
'运营' => 'Operations',
'运营公告' => 'Operation notices',
'用户阅读记录' => 'User read log',
'充值档位' => 'Deposit tiers',
'连胜奖励' => 'Win streak rewards',
'连胜降低档位' => 'Streak reduction tiers',
'钱包加减点' => 'Wallet adjust',
'测试' => 'Test',
'推送-对局公共频道' => 'Push: public game period',
'推送-公告广播频道' => 'Push: operation notices',
'推送-用户私有频道' => 'Push: user private',
'渠道管理' => 'Channel management',
// 演示/运营公告标题(若入库为菜单展示)
'系统维护通知(演示)' => 'Maintenance notice (demo)',
'提现风控升级提醒(演示)' => 'Withdraw risk notice (demo)',
'活动奖励发放说明(演示)' => 'Promotion notice (demo)',
// 部分按钮 title 为动作名game/live 等迁移写入)
'index' => 'View',
'snapshot' => 'Snapshot',
'pushConfig' => 'Push config',
'recordSettings' => 'Period settings',
'createNextManual' => 'Create next period manually',
'periodSettings' => 'Period settings',
'manualSettle' => 'Manual settle',
// 其它中文按钮文案
'期号开关' => 'Period toggle',
'手动创建下一期' => 'Create next period manually',
];

View File

@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
return [
'No active game in progress' => 'No active game in progress',
'Current game status does not allow calculation' => 'Current game status does not allow calculation',
'Betting period has not ended; calculation is not available yet' => 'Betting period has not ended; calculation is not available yet',
'Manual draw number is out of the allowed range' => 'Manual draw number is out of the allowed range',
'Calculation completed' => 'Calculation completed',
'Draw number is out of the allowed range' => 'Draw number is out of the allowed range',
'Current game status does not allow scheduling the draw' => 'Current game status does not allow scheduling the draw',
'Betting has not ended; cannot schedule the draw' => 'Betting has not ended; cannot schedule the draw',
'This period has ended; please refresh the page' => 'This period has ended; please refresh the page',
'Draw number scheduled; it will be used when the countdown ends' => 'Draw number scheduled; it will be used when the countdown ends',
'Current game status does not allow drawing' => 'Current game status does not allow drawing',
'Betting period has not ended; drawing is not available yet' => 'Betting period has not ended; drawing is not available yet',
'Period countdown has not ended; cannot draw yet' => 'Period countdown has not ended; cannot draw yet',
'Draw completed; paying out' => 'Draw completed; paying out',
'Game live: settlement error' => 'Game live: settlement error',
'Calculation failed' => 'Calculation failed',
'Please enter the draw number' => 'Please enter the draw number',
'Schedule failed' => 'Schedule failed',
];

View File

@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
return [
'No active game in progress' => '未找到进行中的对局',
'Current game status does not allow calculation' => '当前对局状态不可计算',
'Betting period has not ended; calculation is not available yet' => '下注开放时长未结束,暂不可计算',
'Manual draw number is out of the allowed range' => '手动开奖号码超出允许范围',
'Calculation completed' => '计算完成',
'Draw number is out of the allowed range' => '开奖号码超出允许范围',
'Current game status does not allow scheduling the draw' => '当前对局状态不可预约开奖',
'Betting has not ended; cannot schedule the draw' => '下注尚未结束,无法预约开奖',
'This period has ended; please refresh the page' => '本期倒计时已结束,请刷新页面',
'Draw number scheduled; it will be used when the countdown ends' => '已预约本期开奖号码,倒计时结束后将使用该号码开奖',
'Current game status does not allow drawing' => '当前对局状态不可开奖',
'Betting period has not ended; drawing is not available yet' => '下注开放时长未结束,不可开奖',
'Period countdown has not ended; cannot draw yet' => '本期倒计时未结束,无法开奖',
'Draw completed; paying out' => '开奖完成,派彩中',
'Game live: settlement error' => '对局结算出错',
'Calculation failed' => '计算失败',
'Please enter the draw number' => '请填写开奖号码',
'Schedule failed' => '预约失败',
];

View File

@@ -159,27 +159,27 @@ final class GameLiveService
{
$record = self::resolveRecord($recordId);
if (!$record) {
return ['ok' => false, 'msg' => '未找到进行中的对局'];
return ['ok' => false, 'msg' => __('No active game in progress')];
}
if (!in_array((int) $record['status'], [0, 1], true)) {
return ['ok' => false, 'msg' => '当前对局状态不可计算'];
return ['ok' => false, 'msg' => __('Current game status does not allow calculation')];
}
$periodSeconds = self::getConfigInt(self::KEY_PERIOD_SECONDS, 30);
$betSeconds = self::getConfigInt(self::KEY_BET_SECONDS, 20);
$elapsed = max(0, time() - (int) $record['period_start_at']);
if ($elapsed < $betSeconds) {
return ['ok' => false, 'msg' => '下注开放时长未结束,暂不可计算'];
return ['ok' => false, 'msg' => __('Betting period has not ended; calculation is not available yet')];
}
self::ensureAiLocked((int) $record['id']);
$record = self::reloadRecord((int) $record['id']);
if (!$record) {
return ['ok' => false, 'msg' => '未找到进行中的对局'];
return ['ok' => false, 'msg' => __('No active game in progress')];
}
$pickMax = self::getPickMaxNumberCount();
if ($manualNumber !== null && ($manualNumber < 1 || $manualNumber > self::DRAW_NUMBER_MAX)) {
return ['ok' => false, 'msg' => '手动开奖号码超出允许范围'];
return ['ok' => false, 'msg' => __('Manual draw number is out of the allowed range')];
}
$bets = Db::name('bet_order')->where('period_id', (int) $record['id'])->select()->toArray();
@@ -215,7 +215,7 @@ final class GameLiveService
return [
'ok' => true,
'msg' => '计算完成',
'msg' => __('Calculation completed'),
'record' => $record,
'period_seconds' => $periodSeconds,
'bet_seconds' => $betSeconds,
@@ -234,23 +234,23 @@ final class GameLiveService
public static function scheduleDraw(?int $recordId, int $manualNumber): array
{
if ($manualNumber < 1 || $manualNumber > self::DRAW_NUMBER_MAX) {
return ['ok' => false, 'msg' => '开奖号码超出允许范围'];
return ['ok' => false, 'msg' => __('Draw number is out of the allowed range')];
}
$record = self::resolveRecord($recordId);
if (!$record) {
return ['ok' => false, 'msg' => '未找到进行中的对局'];
return ['ok' => false, 'msg' => __('No active game in progress')];
}
if (!in_array((int) $record['status'], [0, 1], true)) {
return ['ok' => false, 'msg' => '当前对局状态不可预约开奖'];
return ['ok' => false, 'msg' => __('Current game status does not allow scheduling the draw')];
}
$periodSeconds = self::getConfigInt(self::KEY_PERIOD_SECONDS, 30);
$betSeconds = self::getConfigInt(self::KEY_BET_SECONDS, 20);
$elapsed = max(0, time() - (int) $record['period_start_at']);
if ($elapsed < $betSeconds) {
return ['ok' => false, 'msg' => '下注尚未结束,无法预约开奖'];
return ['ok' => false, 'msg' => __('Betting has not ended; cannot schedule the draw')];
}
if ($elapsed >= $periodSeconds) {
return ['ok' => false, 'msg' => '本期倒计时已结束,请刷新页面'];
return ['ok' => false, 'msg' => __('This period has ended; please refresh the page')];
}
self::ensureAiLocked((int) $record['id']);
@@ -258,11 +258,12 @@ final class GameLiveService
'pending_draw_number' => $manualNumber,
'update_time' => time(),
]);
GameHotDataRedis::gameRecordForget((int) $record['id']);
self::publishSnapshot(null);
return [
'ok' => true,
'msg' => '已预约本期开奖号码,倒计时结束后将使用该号码开奖',
'msg' => __('Draw number scheduled; it will be used when the countdown ends'),
];
}
@@ -273,25 +274,25 @@ final class GameLiveService
{
$record = self::resolveRecord($recordId);
if (!$record) {
return ['ok' => false, 'msg' => '未找到进行中的对局'];
return ['ok' => false, 'msg' => __('No active game in progress')];
}
if (!in_array((int) $record['status'], [0, 1], true)) {
return ['ok' => false, 'msg' => '当前对局状态不可开奖'];
return ['ok' => false, 'msg' => __('Current game status does not allow drawing')];
}
$periodSeconds = self::getConfigInt(self::KEY_PERIOD_SECONDS, 30);
$betSeconds = self::getConfigInt(self::KEY_BET_SECONDS, 20);
$elapsed = max(0, time() - (int) $record['period_start_at']);
if ($elapsed < $betSeconds) {
return ['ok' => false, 'msg' => '下注开放时长未结束,不可开奖'];
return ['ok' => false, 'msg' => __('Betting period has not ended; drawing is not available yet')];
}
if ($elapsed < $periodSeconds) {
return ['ok' => false, 'msg' => '本期倒计时未结束,无法开奖'];
return ['ok' => false, 'msg' => __('Period countdown has not ended; cannot draw yet')];
}
self::ensureAiLocked((int) $record['id']);
$record = self::reloadRecord((int) $record['id']);
if (!$record) {
return ['ok' => false, 'msg' => '未找到进行中的对局'];
return ['ok' => false, 'msg' => __('No active game in progress')];
}
$useManual = $manualNumber;
@@ -342,9 +343,11 @@ final class GameLiveService
Db::commit();
} catch (Throwable $e) {
Db::rollback();
return ['ok' => false, 'msg' => $e->getMessage()];
return ['ok' => false, 'msg' => __('Game live: settlement error') . ': ' . $e->getMessage()];
}
GameHotDataRedis::gameRecordForget((int) $record['id']);
try {
GameRecordStatService::refreshForRecordId((int) $record['id']);
} catch (Throwable) {
@@ -357,7 +360,7 @@ final class GameLiveService
return [
'ok' => true,
'msg' => '开奖完成,派彩中',
'msg' => __('Draw completed; paying out'),
'result_number' => $finalNumber,
'estimated_loss' => $finalLoss,
'payout_until' => $payoutUntil,
@@ -392,6 +395,7 @@ final class GameLiveService
Db::rollback();
return;
}
GameHotDataRedis::gameRecordForget($id);
GameRecordStatService::refreshForRecordId($id);
self::publishSnapshot(null);
}
@@ -565,17 +569,17 @@ final class GameLiveService
private static function resolveRecord(?int $recordId): ?array
{
if ($recordId !== null && $recordId > 0) {
$row = Db::name('game_record')->where('id', $recordId)->find();
$row = GameHotDataRedis::gameRecordById($recordId);
if ($row) {
return $row;
}
}
return Db::name('game_record')->whereIn('status', [0, 1, 2, 3])->order('id', 'desc')->find();
return GameHotDataRedis::gameRecordActive();
}
private static function reloadRecord(int $id): ?array
{
$row = Db::name('game_record')->where('id', $id)->find();
$row = GameHotDataRedis::gameRecordById($id);
return $row ?: null;
}
@@ -584,7 +588,7 @@ final class GameLiveService
*/
private static function ensureAiLocked(int $recordId): void
{
$record = Db::name('game_record')->where('id', $recordId)->find();
$record = GameHotDataRedis::gameRecordById($recordId);
if (!$record) {
return;
}
@@ -605,6 +609,7 @@ final class GameLiveService
'status' => 1,
'update_time' => time(),
]);
GameHotDataRedis::gameRecordForget($recordId);
$record['status'] = 1;
self::publishPublicPeriodLocked($record);
}
@@ -624,6 +629,7 @@ final class GameLiveService
$update['status'] = 1;
}
Db::name('game_record')->where('id', $recordId)->update($update);
GameHotDataRedis::gameRecordForget($recordId);
$record = array_merge($record, $update);
if ($st === 0) {
self::publishPublicPeriodLocked($record);
@@ -653,7 +659,7 @@ final class GameLiveService
private static function getConfigInt(string $key, int $default): int
{
$row = Db::name('game_config')->where('config_key', $key)->find();
$row = GameHotDataRedis::gameConfigRow($key);
if (!$row) {
return $default;
}

28
config/game_hot_cache.php Normal file
View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
/**
* 游戏侧热点数据 Redis 缓存(用户 / game_config / game_record
*
* 环境变量见 .env-example 中 GAME_HOT_CACHE_*
*/
$envInt = static function (string $key, int $default): int {
$raw = env($key, null);
if ($raw === null || $raw === '') {
return $default;
}
$n = filter_var($raw, FILTER_VALIDATE_INT);
return ($n === false || $n < 1) ? $default : $n;
};
return [
'enabled' => filter_var(env('GAME_HOT_CACHE_ENABLED', true), FILTER_VALIDATE_BOOLEAN),
/** game_config 行缓存(秒),后台修改会主动删除 */
'ttl_game_config' => $envInt('GAME_HOT_CACHE_TTL_GAME_CONFIG', 86400),
/** game_record 行缓存(秒) */
'ttl_game_record' => $envInt('GAME_HOT_CACHE_TTL_GAME_RECORD', 60),
/** user 行缓存(秒),余额/连胜变更会主动删除 */
'ttl_user' => $envInt('GAME_HOT_CACHE_TTL_USER', 90),
];