优化后台实时对局页面
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user