修改缓存方式

This commit is contained in:
2026-04-20 10:31:14 +08:00
parent 025cce3e3e
commit 92fb40ae80
19 changed files with 512 additions and 57 deletions

View File

@@ -0,0 +1,279 @@
<?php
declare(strict_types=1);
namespace app\common\service;
use app\common\model\User;
use support\Redis;
use support\think\Db;
use Throwable;
/**
* 热点数据 Redis 缓存game_config、game_record、user 全行 JSON。
* Redis 不可用时自动回退为直连数据库。
*/
final class GameHotDataRedis
{
private const KEY_GC = 'dfw:v1:gc:';
private const KEY_GR_ID = 'dfw:v1:gr:id:';
private const KEY_GR_ACTIVE = 'dfw:v1:gr:active';
private const KEY_GR_LATEST = 'dfw:v1:gr:latest';
private const KEY_USER = 'dfw:v1:user:';
public static function enabled(): bool
{
return config('game_hot_cache.enabled', true) === true;
}
/**
* @return array<string, mixed>|null
*/
public static function gameConfigRow(string $configKey): ?array
{
if ($configKey === '') {
return null;
}
if (self::enabled()) {
$cached = self::redisGet(self::KEY_GC . $configKey);
if ($cached !== null && $cached !== '') {
$decoded = json_decode($cached, true);
if (is_array($decoded)) {
return $decoded;
}
}
}
$row = Db::name('game_config')->where('config_key', $configKey)->find();
if (!$row) {
return null;
}
if (self::enabled()) {
$ttl = self::intConfig('ttl_game_config', 86400);
self::redisSetEx(self::KEY_GC . $configKey, $ttl, json_encode($row, JSON_UNESCAPED_UNICODE));
}
return $row;
}
public static function gameConfigForget(string $configKey): void
{
if ($configKey === '') {
return;
}
self::redisDel(self::KEY_GC . $configKey);
}
/**
* @return array<string, mixed>|null
*/
public static function gameRecordById(int $id): ?array
{
if ($id <= 0) {
return null;
}
$key = self::KEY_GR_ID . $id;
if (self::enabled()) {
$cached = self::redisGet($key);
if ($cached !== null && $cached !== '') {
$decoded = json_decode($cached, true);
if (is_array($decoded)) {
return $decoded;
}
}
}
$row = Db::name('game_record')->where('id', $id)->find();
if (!$row) {
return null;
}
if (self::enabled()) {
$ttl = self::intConfig('ttl_game_record', 60);
self::redisSetEx($key, $ttl, json_encode($row, JSON_UNESCAPED_UNICODE));
}
return $row;
}
/**
* 当前未完全结束的对局status ∈ 0,1,2,3 中 id 最大的一条)
*
* @return array<string, mixed>|null
*/
public static function gameRecordActive(): ?array
{
if (self::enabled()) {
$cached = self::redisGet(self::KEY_GR_ACTIVE);
if ($cached !== null && $cached !== '') {
$decoded = json_decode($cached, true);
if (is_array($decoded)) {
return $decoded;
}
}
}
$row = Db::name('game_record')->whereIn('status', [0, 1, 2, 3])->order('id', 'desc')->find();
if (!$row) {
return null;
}
if (self::enabled()) {
$ttl = self::intConfig('ttl_game_record', 60);
self::redisSetEx(self::KEY_GR_ACTIVE, $ttl, json_encode($row, JSON_UNESCAPED_UNICODE));
}
return $row;
}
/**
* 最新一条对局(含已结束),与 GameRecord::order('id','desc')->find() 一致
*
* @return array<string, mixed>|null
*/
public static function gameRecordLatest(): ?array
{
if (self::enabled()) {
$cached = self::redisGet(self::KEY_GR_LATEST);
if ($cached !== null && $cached !== '') {
$decoded = json_decode($cached, true);
if (is_array($decoded)) {
return $decoded;
}
}
}
$row = Db::name('game_record')->order('id', 'desc')->find();
if (!$row) {
return null;
}
if (self::enabled()) {
$ttl = self::intConfig('ttl_game_record', 60);
self::redisSetEx(self::KEY_GR_LATEST, $ttl, json_encode($row, JSON_UNESCAPED_UNICODE));
}
return $row;
}
/**
* 任意 game_record 变更后调用:删除活跃局、最新局及指定 id 缓存
*/
public static function gameRecordForget(?int $id = null): void
{
$keys = [self::KEY_GR_ACTIVE, self::KEY_GR_LATEST];
if ($id !== null && $id > 0) {
$keys[] = self::KEY_GR_ID . $id;
}
self::redisDel(...$keys);
}
/**
* @return array<string, mixed>|null
*/
public static function userRow(int $userId): ?array
{
if ($userId <= 0) {
return null;
}
$key = self::KEY_USER . $userId;
if (self::enabled()) {
$cached = self::redisGet($key);
if ($cached !== null && $cached !== '') {
$decoded = json_decode($cached, true);
if (is_array($decoded)) {
return $decoded;
}
}
}
$row = Db::name('user')->where('id', $userId)->find();
if (!$row) {
return null;
}
if (self::enabled()) {
$ttl = self::intConfig('ttl_user', 90);
self::redisSetEx($key, $ttl, json_encode($row, JSON_UNESCAPED_UNICODE));
}
return $row;
}
public static function userRemember(User $user): void
{
if (!self::enabled()) {
return;
}
$id = $user->getAttr('id');
$uid = filter_var($id, FILTER_VALIDATE_INT);
if ($uid === false || $uid <= 0) {
return;
}
$ttl = self::intConfig('ttl_user', 90);
self::redisSetEx(self::KEY_USER . $uid, $ttl, json_encode($user->toArray(), JSON_UNESCAPED_UNICODE));
}
public static function userForget(int $userId): void
{
if ($userId <= 0) {
return;
}
self::redisDel(self::KEY_USER . $userId);
}
/**
* 用缓存行构造已存在库的 User供 Auth 等高频读)
*/
public static function userModelFromCacheOrDb(int $userId): ?User
{
$row = self::userRow($userId);
if ($row === null) {
return null;
}
return (new User())->newInstance($row);
}
private static function intConfig(string $name, int $default): int
{
$v = config('game_hot_cache.' . $name, $default);
if (is_int($v)) {
return $v > 0 ? $v : $default;
}
if (is_numeric($v)) {
$n = filter_var($v, FILTER_VALIDATE_INT);
if ($n === false) {
return $default;
}
return $n > 0 ? $n : $default;
}
return $default;
}
private static function redisGet(string $key): ?string
{
try {
$v = Redis::get($key);
if ($v === false || $v === null) {
return null;
}
if (!is_string($v)) {
return null;
}
return $v;
} catch (Throwable) {
return null;
}
}
private static function redisSetEx(string $key, int $ttl, string $value): void
{
try {
Redis::setEx($key, $ttl, $value);
} catch (Throwable) {
}
}
private static function redisDel(string ...$keys): void
{
if ($keys === []) {
return;
}
try {
Redis::del(...$keys);
} catch (Throwable) {
}
}
}