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);
|
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 后使用)
|
* 按 username 缓存玩家信息(仅 id + username,供中间件注入 request->player 后使用)
|
||||||
* 登录/信息变更时需调用 deletePlayerByUsername 失效
|
* 登录/信息变更时需调用 deletePlayerByUsername 失效
|
||||||
@@ -280,6 +291,54 @@ class UserCache
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$key = self::playerCachePrefix() . $username;
|
$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',
|
'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)
|
* POST 参数:lang(可选,zh/en,默认 zh)
|
||||||
@@ -130,24 +138,31 @@ class GameController extends BaseController
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取用户信息
|
* 获取用户信息
|
||||||
* POST 参数:username
|
* 参数:username(POST JSON / 表单 / Query 均可,input 合并读取降低偶发空参)
|
||||||
* 返回 DicePlayer 中非敏感信息
|
* 返回 DicePlayer 中非敏感信息;短期 Redis 快照 + 窄字段查询降低延迟
|
||||||
*/
|
*/
|
||||||
public function getPlayerInfo(Request $request): Response
|
public function getPlayerInfo(Request $request): Response
|
||||||
{
|
{
|
||||||
$username = trim((string) ($request->post('username', '')));
|
$usernameRaw = $request->input('username', '');
|
||||||
|
$username = is_string($usernameRaw) ? trim($usernameRaw) : '';
|
||||||
|
|
||||||
if ($username === '') {
|
if ($username === '') {
|
||||||
return $this->fail('username is required', ReturnCode::PARAMS_ERROR);
|
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) {
|
if (!$player) {
|
||||||
return $this->fail('User not found', ReturnCode::NOT_FOUND);
|
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'];
|
$hidden = ['password', 'lottery_config_id', 't1_weight', 't2_weight', 't3_weight', 't4_weight', 't5_weight', 'delete_time'];
|
||||||
$info = $player->hidden($hidden)->toArray();
|
$info = $player->hidden($hidden)->toArray();
|
||||||
|
UserCache::setPlayerInfoSnapshotByUsername($username, $info);
|
||||||
|
|
||||||
return $this->success($info);
|
return $this->success($info);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,8 +47,9 @@ class AuthTokenMiddleware implements MiddlewareInterface
|
|||||||
throw new ApiException('auth-token invalid', ReturnCode::TOKEN_INVALID);
|
throw new ApiException('auth-token invalid', ReturnCode::TOKEN_INVALID);
|
||||||
}
|
}
|
||||||
|
|
||||||
$currentToken = AuthTokenCache::getTokenByAgentId($agentId);
|
// 单次 Redis:token → agent_id,与 JWT 内 agent_id 一致即有效(减少一次按 agent 取当前 token 的往返)
|
||||||
if ($currentToken === null || $currentToken !== $token) {
|
$agentIdFromStore = AuthTokenCache::getAgentIdByToken($token);
|
||||||
|
if ($agentIdFromStore === null || $agentIdFromStore !== $agentId) {
|
||||||
throw new ApiException('auth-token invalid or expired', ReturnCode::TOKEN_INVALID);
|
throw new ApiException('auth-token invalid or expired', ReturnCode::TOKEN_INVALID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,4 +34,7 @@ return [
|
|||||||
// 玩家信息按 username 缓存(Token 中间件用),0 表示不缓存
|
// 玩家信息按 username 缓存(Token 中间件用),0 表示不缓存
|
||||||
'player_cache_ttl' => (int) env('API_PLAYER_CACHE_TTL', 300),
|
'player_cache_ttl' => (int) env('API_PLAYER_CACHE_TTL', 300),
|
||||||
'player_cache_prefix' => env('API_PLAYER_CACHE_PREFIX', 'api:player:'),
|
'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