From 24f30b5ef222c68e449b5f7c2713b8a483c1144c Mon Sep 17 00:00:00 2001 From: zhenhui <1276357500@qq.com> Date: Tue, 26 May 2026 13:58:07 +0800 Subject: [PATCH] =?UTF-8?q?1.=E4=BF=AE=E5=A4=8D=E6=B8=B8=E6=88=8F=E5=AE=9E?= =?UTF-8?q?=E6=97=B6=E5=AF=B9=E5=B1=80/admin/game/live=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E7=9A=84=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/common/service/GameHotDataRedis.php | 54 +++++++++++++++++++++++- app/common/service/GameLiveService.php | 22 ++++++++-- app/common/service/GameRecordService.php | 5 ++- 3 files changed, 75 insertions(+), 6 deletions(-) diff --git a/app/common/service/GameHotDataRedis.php b/app/common/service/GameHotDataRedis.php index beeecc1..021c8b1 100644 --- a/app/common/service/GameHotDataRedis.php +++ b/app/common/service/GameHotDataRedis.php @@ -151,6 +151,48 @@ final class GameHotDataRedis } } + /** + * 派彩宽限期已结束但缓存仍为 status=3(导致页面倒计时 0、无法开新期)。 + */ + public static function isExpiredPayoutRecord(array $row): bool + { + if ((int) ($row['status'] ?? -1) !== 3) { + return false; + } + $until = (int) ($row['payout_until'] ?? 0); + + return $until <= 0 || $until <= time(); + } + + /** + * 下注/封盘期缓存与库不一致或已超时未开奖(period_start 过久仍为 0/1)。 + */ + public static function isStaleOpenPeriodRecord(array $row, int $periodSeconds): bool + { + $st = (int) ($row['status'] ?? -1); + if (!in_array($st, [0, 1], true)) { + return false; + } + $start = (int) ($row['period_start_at'] ?? 0); + if ($start <= 0) { + return false; + } + + return time() - $start > $periodSeconds + 2; + } + + /** + * 缓存行疑似过期时,按库回写 Redis 后再由调用方重新读取。 + */ + public static function gameRecordRevalidateFromDbIfStale(array $row, int $periodSeconds = 30): void + { + if (!self::isExpiredPayoutRecord($row) && !self::isStaleOpenPeriodRecord($row, $periodSeconds)) { + return; + } + $id = (int) ($row['id'] ?? 0); + self::gameRecordSyncCachesAfterDbWrite($id > 0 ? $id : null); + } + /** * @return array|null */ @@ -165,7 +207,11 @@ final class GameHotDataRedis if ($cached !== null && $cached !== '') { $decoded = json_decode($cached, true); if (is_array($decoded)) { - return $decoded; + if (self::isExpiredPayoutRecord($decoded)) { + self::gameRecordSyncCachesAfterDbWrite($id); + } else { + return $decoded; + } } } } @@ -192,7 +238,11 @@ final class GameHotDataRedis if ($cached !== null && $cached !== '') { $decoded = json_decode($cached, true); if (is_array($decoded)) { - return $decoded; + if (self::isExpiredPayoutRecord($decoded)) { + self::gameRecordRefreshAggregateCaches(); + } else { + return $decoded; + } } } } diff --git a/app/common/service/GameLiveService.php b/app/common/service/GameLiveService.php index 446dbce..8057a2f 100644 --- a/app/common/service/GameLiveService.php +++ b/app/common/service/GameLiveService.php @@ -261,6 +261,15 @@ final class GameLiveService public static function buildSnapshot(?int $recordId = null): array { $record = self::resolveRecord($recordId); + if ($record) { + $periodSeconds = self::getConfigInt(self::KEY_PERIOD_SECONDS, 30); + GameHotDataRedis::gameRecordRevalidateFromDbIfStale($record, $periodSeconds); + $record = self::resolveRecord($recordId); + } + if ($record && GameHotDataRedis::isExpiredPayoutRecord($record)) { + self::finalizePayoutGrace(); + $record = self::resolveRecord($recordId); + } if (!$record) { return self::emptySnapshotPayload(); } @@ -684,10 +693,14 @@ final class GameLiveService 'payout_until' => null, 'update_time' => time(), ]); - GameRecordService::createNextRecordAfterDraw(); + if (GameRecordService::isLiveRuntimeEnabled()) { + GameRecordService::createNextRecordRow(); + } Db::commit(); - } catch (Throwable) { + } catch (Throwable $e) { Db::rollback(); + Log::warning('finalizePayoutGrace failed: ' . $e->getMessage(), ['record_id' => $id]); + return; } GameHotDataCoordinator::afterGameRecordCommitted($id); @@ -915,12 +928,15 @@ final class GameLiveService if ($payoutUntil === false || $payoutUntil <= 0) { return; } + $now = time(); + if ($payoutUntil <= $now) { + return; + } $periodId = filter_var($record['id'] ?? 0, FILTER_VALIDATE_INT); if ($periodId === false) { $periodId = 0; } $periodNo = is_string($record['period_no'] ?? null) ? (string) $record['period_no'] : ''; - $now = time(); $resultNumber = null; $resultParsed = filter_var($record['result_number'] ?? null, FILTER_VALIDATE_INT); if ($resultParsed !== false && $resultParsed > 0) { diff --git a/app/common/service/GameRecordService.php b/app/common/service/GameRecordService.php index ed3a184..6e3e167 100644 --- a/app/common/service/GameRecordService.php +++ b/app/common/service/GameRecordService.php @@ -127,7 +127,10 @@ final class GameRecordService self::upsertConfig(self::KEY_AUTO_CREATE, $v, 'int', '是否允许自动创建下一局(全局仅一局)', $now); } - private static function createNextRecordRow(): string + /** + * 派彩结单后插入新一期(调用方须已保证无其它进行中局)。 + */ + public static function createNextRecordRow(): string { $periodNo = self::generatePeriodNo(); $now = time();