优化后台实时对局页面

This commit is contained in:
2026-04-24 15:03:41 +08:00
parent fd324f2882
commit 2ce128e63a
6 changed files with 209 additions and 22 deletions

View File

@@ -3,6 +3,7 @@
namespace app\admin\controller\game;
use app\common\controller\Backend;
use app\common\library\admin\WebSocketConfigHelper;
use app\common\service\GameLiveService;
use app\common\service\GameRecordService;
use support\Response;
@@ -38,6 +39,41 @@ class Live extends Backend
return $this->success('', GameLiveService::buildSnapshot($recordId));
}
/**
* 后台实时对局 WebSocket 配置(管理员联调专用)。
*/
public function wsConfig(WebmanRequest $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) {
return $response;
}
$topics = [
'admin.live.snapshot',
'admin.live.opened',
'period.tick',
'period.locked',
'period.opened',
'period.payout',
'bet.accepted',
'wallet.changed',
'auto.spin.progress',
];
return $this->success('', [
'name' => 'ws.admin.live',
'ws_url' => WebSocketConfigHelper::wsUrl(),
'connect_tip' => '后台实时对局页将自动订阅管理员全量主题(含本局下注、候选号、开奖与派彩信息)。',
'subscribe_topics' => $topics,
'sample_messages' => [
'{"action":"ping"}',
'{"action":"subscribe","topics":["admin.live.snapshot","admin.live.opened"]}',
'{"action":"subscribe","topics":["period.tick","period.opened","wallet.changed"]}',
],
]);
}
public function calculate(WebmanRequest $request): Response
{
$response = $this->initializeBackend($request);

View File

@@ -385,6 +385,14 @@ final class GameLiveService
}
self::publishPublicPeriodOpened((string) $record['period_no'], $finalNumber, $now);
self::publishPublicPeriodPayout((string) $record['period_no'], $finalNumber, $payoutUntil);
GameWebSocketEventBus::publish('admin.live.opened', [
'period_id' => $rid,
'period_no' => (string) $record['period_no'],
'result_number' => $finalNumber,
'payout_until' => $payoutUntil,
'jackpot_hits' => is_array($settleOut['jackpot_hits'] ?? null) ? $settleOut['jackpot_hits'] : [],
'server_time' => $now,
]);
self::publishSnapshot(null);
return [
@@ -610,6 +618,7 @@ final class GameLiveService
{
$snapshot = self::buildSnapshot($recordId);
self::publishPublicPeriodTick($snapshot);
GameWebSocketEventBus::publish('admin.live.snapshot', $snapshot);
}
/**

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace app\process;
use app\common\service\GameWebSocketEventBus;
use app\common\service\GameLiveService;
use Workerman\Connection\TcpConnection;
use Workerman\Timer;
@@ -17,6 +18,7 @@ class GameWebSocketServer
private static array $connections = [];
private static bool $eventBusConsumerStarted = false;
private static bool $adminSnapshotTickerStarted = false;
/**
* 从 Redis 队列拉取事件并推送给已订阅连接。
@@ -60,9 +62,52 @@ class GameWebSocketServer
});
}
/**
* 兜底直推admin.live.snapshot 每秒主动构建并广播。
* 目的:即使 Redis 队列不可用,也能保证 /admin/game/live 实时看到对局变化。
*/
private static function ensureAdminLiveSnapshotTicker(): void
{
if (self::$adminSnapshotTickerStarted) {
return;
}
self::$adminSnapshotTickerStarted = true;
Timer::add(1, static function (): void {
$hasAdminSubscriber = false;
foreach (self::$connections as $connection) {
$topics = $connection->topics ?? [];
if (is_array($topics) && in_array('admin.live.snapshot', $topics, true)) {
$hasAdminSubscriber = true;
break;
}
}
if (!$hasAdminSubscriber) {
return;
}
$snapshot = GameLiveService::buildSnapshot(null);
$payload = json_encode([
'event' => 'admin.live.snapshot',
'topic' => 'admin.live.snapshot',
'data' => $snapshot,
'server_time' => time(),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
if (!is_string($payload) || $payload === '') {
return;
}
foreach (self::$connections as $connection) {
$topics = $connection->topics ?? [];
if (!is_array($topics) || !in_array('admin.live.snapshot', $topics, true)) {
continue;
}
$connection->send($payload);
}
});
}
public function onWorkerStart(): void
{
self::ensureEventBusConsumer();
self::ensureAdminLiveSnapshotTicker();
}
public function onConnect(TcpConnection $connection): void
@@ -74,6 +119,7 @@ class GameWebSocketServer
public function onWebSocketConnect(TcpConnection $connection): void
{
self::ensureEventBusConsumer();
self::ensureAdminLiveSnapshotTicker();
$connection->send(json_encode([
'event' => 'ws.connected',
'message' => 'WebSocket connected',