From 203e478b65845643ceb192de851a7c108b61e8ff Mon Sep 17 00:00:00 2001 From: zhenhui <1276357500@qq.com> Date: Fri, 24 Apr 2026 15:25:15 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96websocket=E8=BF=9E=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/controller/game/Live.php | 1 + app/common/service/GameLiveService.php | 13 ++++++++++-- docs/36字花-移动端接口设计草案.md | 25 +++++++++++++++++++++++ web/src/lang/backend/en/game/live.ts | 1 + web/src/lang/backend/zh-cn/game/live.ts | 1 + web/src/views/backend/game/live/index.vue | 8 ++++++++ 6 files changed, 47 insertions(+), 2 deletions(-) diff --git a/app/admin/controller/game/Live.php b/app/admin/controller/game/Live.php index 60df060..142acf9 100644 --- a/app/admin/controller/game/Live.php +++ b/app/admin/controller/game/Live.php @@ -52,6 +52,7 @@ class Live extends Backend $topics = [ 'admin.live.snapshot', 'admin.live.opened', + 'jackpot.hit', 'period.tick', 'period.locked', 'period.opened', diff --git a/app/common/service/GameLiveService.php b/app/common/service/GameLiveService.php index 134b86c..0ce08dd 100644 --- a/app/common/service/GameLiveService.php +++ b/app/common/service/GameLiveService.php @@ -385,14 +385,24 @@ final class GameLiveService } self::publishPublicPeriodOpened((string) $record['period_no'], $finalNumber, $now); self::publishPublicPeriodPayout((string) $record['period_no'], $finalNumber, $payoutUntil); + $jackpotHits = is_array($settleOut['jackpot_hits'] ?? null) ? $settleOut['jackpot_hits'] : []; 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'] : [], + 'jackpot_hits' => $jackpotHits, 'server_time' => $now, ]); + if ($jackpotHits !== []) { + GameWebSocketEventBus::publish('jackpot.hit', [ + 'period_id' => $rid, + 'period_no' => (string) $record['period_no'], + 'result_number' => $finalNumber, + 'hits' => $jackpotHits, + 'server_time' => $now, + ]); + } self::publishSnapshot(null); return [ @@ -618,7 +628,6 @@ final class GameLiveService { $snapshot = self::buildSnapshot($recordId); self::publishPublicPeriodTick($snapshot); - GameWebSocketEventBus::publish('admin.live.snapshot', $snapshot); } /** diff --git a/docs/36字花-移动端接口设计草案.md b/docs/36字花-移动端接口设计草案.md index 3a81779..06536c2 100644 --- a/docs/36字花-移动端接口设计草案.md +++ b/docs/36字花-移动端接口设计草案.md @@ -697,6 +697,31 @@ - 订阅资金流:`{"action":"subscribe","topics":["bet.accepted","wallet.changed"]}` - 订阅托管流:`{"action":"subscribe","topics":["auto.spin.progress","wallet.changed"]}` +#### 7.1.1 消息协议字段定义(联调口径) + +- 客户端 -> 服务端: + - `action`:动作名(当前约定 `ping` / `subscribe`) + - `topics`:仅 `subscribe` 时必填,表示要订阅的主题列表(数组) +- 服务端 -> 客户端: + - `event`:事件名(如 `period.tick`、`wallet.changed`、`jackpot.hit`) + - `topic`:所属主题(通常与 `event` 一致;用于前端按主题路由) + - `data`:业务载荷(对象) + - `server_time`:服务端时间戳(秒,倒计时与对时基准) + +#### 7.1.2 订阅行为说明 + +- **仅建立连接不会自动下发全部业务消息**;客户端需要发送 `subscribe` 明确订阅主题。 +- 成功订阅后服务端返回:`{"event":"ws.subscribed","topics":[...]}`。 +- 若未订阅主题,通常只能收到握手首帧(`ws.connected`)和心跳回包(`pong`)。 + +#### 7.1.3 推送频率与触发规则(当前实现) + +- `period.tick`:**每秒一次**(用于倒计时、状态同步)。 +- `admin.live.snapshot`:**每秒一次**(后台实时对局页全量快照)。 +- `period.opened` / `period.payout` / `admin.live.opened`:按开奖流程阶段触发(事件触发型,非固定频率)。 +- `wallet.changed`:仅在余额发生变更时推送(如下注扣款、充值入账、派彩入账)。 +- `jackpot.hit`:**仅在本期存在中大奖命中用户时推送**;无命中不推送。 + ### 7.1A 后台连接方式(管理端联调) - 后台菜单:仅保留一个菜单 `连接服务器websocket`,用于统一联调 WebSocket diff --git a/web/src/lang/backend/en/game/live.ts b/web/src/lang/backend/en/game/live.ts index 286617c..3f3d79b 100644 --- a/web/src/lang/backend/en/game/live.ts +++ b/web/src/lang/backend/en/game/live.ts @@ -24,6 +24,7 @@ export default { ws_connect: 'Connect WS', ws_disconnect: 'Disconnect WS', ws_log_empty: 'No WebSocket logs yet.', + jackpot_hit_tip: 'Jackpot winners detected in this round.', candidate_title: 'Candidate payout estimates', number: 'Number', estimated_loss: 'Estimated payout', diff --git a/web/src/lang/backend/zh-cn/game/live.ts b/web/src/lang/backend/zh-cn/game/live.ts index aa36bb8..340980e 100644 --- a/web/src/lang/backend/zh-cn/game/live.ts +++ b/web/src/lang/backend/zh-cn/game/live.ts @@ -24,6 +24,7 @@ export default { ws_connect: '连接WS', ws_disconnect: '断开WS', ws_log_empty: '暂无 WebSocket 日志。', + jackpot_hit_tip: '本期出现中大奖用户,请查看开奖信息。', candidate_title: '候选号码赔付预估', number: '号码', estimated_loss: '预估赔付', diff --git a/web/src/views/backend/game/live/index.vue b/web/src/views/backend/game/live/index.vue index 25d30a7..6878249 100644 --- a/web/src/views/backend/game/live/index.vue +++ b/web/src/views/backend/game/live/index.vue @@ -305,6 +305,14 @@ function handleWsPayload(raw: unknown): void { mergeLiveSnapshot(parsed.data as anyObj) return } + if (event === 'jackpot.hit' && parsed.data && typeof parsed.data === 'object') { + const jackpotData = parsed.data as anyObj + const hits = Array.isArray(jackpotData.hits) ? jackpotData.hits : [] + if (hits.length > 0) { + ElMessage.success(t('game.live.jackpot_hit_tip')) + } + return + } if (event === 'period.tick' && parsed.data && typeof parsed.data === 'object') { const periodData = parsed.data as anyObj if (typeof periodData.server_time === 'number') {