1.优化getPlayerInfo接口
This commit is contained in:
61
server/app/api/cache/UserCache.php
vendored
61
server/app/api/cache/UserCache.php
vendored
@@ -238,6 +238,17 @@ class UserCache
|
||||
return (int) config('api.player_cache_ttl', 300);
|
||||
}
|
||||
|
||||
/** /api/v1/getPlayerInfo 快照 key 前缀 */
|
||||
private static function playerInfoSnapshotPrefix(): string
|
||||
{
|
||||
return config('api.player_info_snapshot_prefix', 'api:v1:player_info:');
|
||||
}
|
||||
|
||||
private static function playerInfoSnapshotTtl(): int
|
||||
{
|
||||
return (int) config('api.player_info_snapshot_ttl', 180);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按 username 缓存玩家信息(仅 id + username,供中间件注入 request->player 后使用)
|
||||
* 登录/信息变更时需调用 deletePlayerByUsername 失效
|
||||
@@ -280,6 +291,54 @@ class UserCache
|
||||
return false;
|
||||
}
|
||||
$key = self::playerCachePrefix() . $username;
|
||||
return Cache::delete($key);
|
||||
$r1 = Cache::delete($key);
|
||||
$snapKey = self::playerInfoSnapshotPrefix() . $username;
|
||||
$r2 = Cache::delete($snapKey);
|
||||
return $r1 || $r2;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 getPlayerInfo 接口用的玩家公开信息快照(未命中返回 null)
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
public static function getPlayerInfoSnapshotByUsername(string $username): ?array
|
||||
{
|
||||
if ($username === '') {
|
||||
return null;
|
||||
}
|
||||
if (self::playerInfoSnapshotTtl() <= 0) {
|
||||
return null;
|
||||
}
|
||||
$key = self::playerInfoSnapshotPrefix() . $username;
|
||||
$val = Cache::get($key);
|
||||
if ($val === null || $val === '') {
|
||||
return null;
|
||||
}
|
||||
$data = json_decode((string) $val, true);
|
||||
if (!is_array($data) || !array_key_exists('username', $data)) {
|
||||
return null;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入 getPlayerInfo 返回体快照(已脱敏数组)
|
||||
* @param array<string, mixed> $info
|
||||
*/
|
||||
public static function setPlayerInfoSnapshotByUsername(string $username, array $info): bool
|
||||
{
|
||||
if ($username === '' || empty($info)) {
|
||||
return false;
|
||||
}
|
||||
$ttl = self::playerInfoSnapshotTtl();
|
||||
if ($ttl <= 0) {
|
||||
return true;
|
||||
}
|
||||
$key = self::playerInfoSnapshotPrefix() . $username;
|
||||
$payload = json_encode($info, JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE);
|
||||
if ($payload === false) {
|
||||
return false;
|
||||
}
|
||||
return Cache::set($key, $payload, $ttl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,14 @@ class GameController extends BaseController
|
||||
'sort',
|
||||
];
|
||||
|
||||
/** getPlayerInfo 仅查询需返回的列,减轻 IO(敏感/内部字段不入库查询) */
|
||||
private const PLAYER_INFO_DB_FIELDS = [
|
||||
'id', 'username', 'phone', 'uid', 'name', 'status', 'coin', 'is_up', 'admin_id',
|
||||
'total_ticket_count', 'paid_ticket_count', 'free_ticket_count', 'free_ticket',
|
||||
'total_win_coin',
|
||||
'create_time', 'update_time',
|
||||
];
|
||||
|
||||
/**
|
||||
* 获取游戏列表
|
||||
* POST 参数:lang(可选,zh/en,默认 zh)
|
||||
@@ -130,24 +138,31 @@ class GameController extends BaseController
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
* POST 参数:username
|
||||
* 返回 DicePlayer 中非敏感信息
|
||||
* 参数:username(POST JSON / 表单 / Query 均可,input 合并读取降低偶发空参)
|
||||
* 返回 DicePlayer 中非敏感信息;短期 Redis 快照 + 窄字段查询降低延迟
|
||||
*/
|
||||
public function getPlayerInfo(Request $request): Response
|
||||
{
|
||||
$username = trim((string) ($request->post('username', '')));
|
||||
$usernameRaw = $request->input('username', '');
|
||||
$username = is_string($usernameRaw) ? trim($usernameRaw) : '';
|
||||
|
||||
if ($username === '') {
|
||||
return $this->fail('username is required', ReturnCode::PARAMS_ERROR);
|
||||
}
|
||||
|
||||
$player = DicePlayer::where('username', $username)->find();
|
||||
$cached = UserCache::getPlayerInfoSnapshotByUsername($username);
|
||||
if ($cached !== null) {
|
||||
return $this->success($cached);
|
||||
}
|
||||
|
||||
$player = DicePlayer::field(self::PLAYER_INFO_DB_FIELDS)->where('username', $username)->find();
|
||||
if (!$player) {
|
||||
return $this->fail('User not found', ReturnCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
$hidden = ['password', 'lottery_config_id', 't1_weight', 't2_weight', 't3_weight', 't4_weight', 't5_weight', 'delete_time'];
|
||||
$info = $player->hidden($hidden)->toArray();
|
||||
UserCache::setPlayerInfoSnapshotByUsername($username, $info);
|
||||
|
||||
return $this->success($info);
|
||||
}
|
||||
|
||||
@@ -47,8 +47,9 @@ class AuthTokenMiddleware implements MiddlewareInterface
|
||||
throw new ApiException('auth-token invalid', ReturnCode::TOKEN_INVALID);
|
||||
}
|
||||
|
||||
$currentToken = AuthTokenCache::getTokenByAgentId($agentId);
|
||||
if ($currentToken === null || $currentToken !== $token) {
|
||||
// 单次 Redis:token → agent_id,与 JWT 内 agent_id 一致即有效(减少一次按 agent 取当前 token 的往返)
|
||||
$agentIdFromStore = AuthTokenCache::getAgentIdByToken($token);
|
||||
if ($agentIdFromStore === null || $agentIdFromStore !== $agentId) {
|
||||
throw new ApiException('auth-token invalid or expired', ReturnCode::TOKEN_INVALID);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,4 +34,7 @@ return [
|
||||
// 玩家信息按 username 缓存(Token 中间件用),0 表示不缓存
|
||||
'player_cache_ttl' => (int) env('API_PLAYER_CACHE_TTL', 300),
|
||||
'player_cache_prefix' => env('API_PLAYER_CACHE_PREFIX', 'api:player:'),
|
||||
// /api/v1/getPlayerInfo 返回体快照(Redis),0 表示不缓存;变更玩家数据时需 deletePlayerByUsername 失效
|
||||
'player_info_snapshot_ttl' => (int) env('API_PLAYER_INFO_SNAPSHOT_TTL', 180),
|
||||
'player_info_snapshot_prefix' => env('API_PLAYER_INFO_SNAPSHOT_PREFIX', 'api:v1:player_info:'),
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user