1.修复游戏实时对局/admin/game/live页面的报错

This commit is contained in:
2026-05-26 13:51:45 +08:00
parent 3a2af4d7c2
commit 40247af0d6
2 changed files with 104 additions and 29 deletions

View File

@@ -57,7 +57,9 @@ class Live extends Backend
'period.locked',
'period.opened',
'period.payout',
'period.payout.tick',
'bet.accepted',
'bet.win',
'wallet.changed',
'auto.spin.progress',
];

View File

@@ -37,7 +37,7 @@
>
{{ t('game.live.void_btn') }}
</el-button>
<el-button :loading="loading" :disabled="asideOperationLocked" @click="loadSnapshot">{{ t('Refresh') }}</el-button>
<el-button :loading="loading" :disabled="asideOperationLocked" @click="loadSnapshot({ force: true })">{{ t('Refresh') }}</el-button>
</div>
</div>
</div>
@@ -203,6 +203,7 @@
import { computed, onMounted, onUnmounted, reactive, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { ElMessage } from 'element-plus'
import axios from 'axios'
import createAxios from '/@/utils/axios'
interface Snapshot {
@@ -270,6 +271,8 @@ const clockTick = ref(0)
let clockTimer: number | null = null
let payoutStuckRefreshTimer: number | null = null
let fallbackPollTimer: number | null = null
/** 合并并发 snapshot 请求,避免 axios 重复请求取消导致控制台报错 */
let snapshotLoadPromise: Promise<void> | null = null
const wsLoading = ref(false)
const wsReady = ref(false)
@@ -330,7 +333,7 @@ function handleWsPayload(raw: unknown): void {
return
}
if (event === 'admin.live.opened') {
void loadSnapshot()
void loadSnapshot({ force: true })
return
}
if (event === 'jackpot.hit' && parsed.data && typeof parsed.data === 'object') {
@@ -341,19 +344,63 @@ function handleWsPayload(raw: unknown): void {
}
return
}
if (event === 'period.payout.tick' && parsed.data && typeof parsed.data === 'object') {
mergePeriodPayoutTick(parsed.data as anyObj)
return
}
if (event === 'period.tick' && parsed.data && typeof parsed.data === 'object') {
const periodData = parsed.data as anyObj
handlePeriodTickEvent(parsed.data as anyObj)
return
}
if (event === 'bet.accepted' && parsed.data && typeof parsed.data === 'object') {
if (!wsConnected.value) {
void loadSnapshot()
}
return
}
}
/** 用 period.tick 轻量字段刷新倒计时(不触发 HTTP避免与 WS 每秒 snapshot 冲突) */
function mergePeriodTickFields(periodData: anyObj): void {
if (typeof periodData.server_time === 'number') {
syncServerClock(periodData.server_time)
}
if (typeof periodData.countdown === 'number') {
snapshot.remaining_seconds = Math.max(0, periodData.countdown)
}
if (typeof periodData.bet_close_in === 'number') {
snapshot.bet_remaining_seconds = Math.max(0, periodData.bet_close_in)
}
}
function mergePeriodPayoutTick(data: anyObj): void {
if (typeof data.server_time === 'number') {
syncServerClock(data.server_time)
}
const remain = numberValue(data.payout_remaining_seconds)
if (remain !== null) {
snapshot.payout_remaining_seconds = Math.max(0, remain)
snapshot.is_payout_phase = remain > 0 || snapshot.is_payout_phase === true
}
}
function handlePeriodTickEvent(periodData: anyObj): void {
mergePeriodTickFields(periodData)
const status = typeof periodData.status === 'string' ? periodData.status : ''
const periodNo = typeof periodData.period_no === 'string' ? periodData.period_no : ''
const currentNo = typeof snapshot.record?.period_no === 'string' ? snapshot.record.period_no : ''
if (status === 'betting' && periodNo !== '' && periodNo !== currentNo) {
void loadSnapshot()
} else if (status === 'finished' && snapshot.is_payout_phase) {
void loadSnapshot({ force: true })
return
}
if (status === 'finished' && snapshot.is_payout_phase) {
if (!wsConnected.value) {
void loadSnapshot()
}
return
}
if (currentNo === '' && periodNo !== '') {
void loadSnapshot({ force: true })
}
}
@@ -643,16 +690,40 @@ const payoutRemainingLive = computed(() => {
return snapshot.payout_remaining_seconds ?? 0
})
async function loadSnapshot() {
function isRequestCanceled(err: unknown): boolean {
return axios.isCancel(err) || (err instanceof Error && err.name === 'CanceledError')
}
async function loadSnapshot(options?: { force?: boolean }): Promise<void> {
if (!options?.force && wsConnected.value && snapshot.record) {
return
}
if (snapshotLoadPromise) {
return snapshotLoadPromise
}
snapshotLoadPromise = (async () => {
loading.value = true
try {
const res = await createAxios({ url: '/admin/game.Live/snapshot', method: 'get', showCodeMessage: false })
const res = await createAxios({
url: '/admin/game.Live/snapshot',
method: 'get',
showCodeMessage: false,
showErrorMessage: false,
cancelDuplicateRequest: false,
})
if (res.code === 1 && res.data) {
mergeLiveSnapshot(res.data as anyObj)
}
} catch (err) {
if (!isRequestCanceled(err)) {
throw err
}
} finally {
loading.value = false
snapshotLoadPromise = null
}
})()
return snapshotLoadPromise
}
async function onRuntimeSwitch(val: boolean | string | number): void {
@@ -679,10 +750,10 @@ async function onRuntimeSwitch(val: boolean | string | number): void {
if (res.code === 1 && res.data) {
mergeLiveSnapshot(res.data as anyObj)
} else {
await loadSnapshot()
await loadSnapshot({ force: true })
}
} catch {
await loadSnapshot()
await loadSnapshot({ force: true })
} finally {
runtimeSwitchLoading.value = false
pendingRuntimeTarget.value = null
@@ -768,7 +839,7 @@ async function onDrawWithNumber(targetNumber: number) {
},
showSuccessMessage: true,
})
await loadSnapshot()
await loadSnapshot({ force: true })
} finally {
drawLoading.value = false
}
@@ -816,7 +887,9 @@ function schedulePayoutEndRefresh(delayMs: number): void {
if (!snapshot.is_payout_phase) {
return
}
void loadSnapshot()
if (!wsConnected.value) {
void loadSnapshot({ force: true })
}
}, delayMs)
}
@@ -827,11 +900,11 @@ onMounted(async () => {
clockTick.value++
}, 1000)
fallbackPollTimer = window.setInterval(() => {
if (snapshot.is_payout_phase || snapshot.maintenance_ui) {
void loadSnapshot()
if (!wsConnected.value) {
void loadSnapshot({ force: true })
}
}, 5000)
await loadSnapshot()
}, 3000)
await loadSnapshot({ force: true })
await reloadWsConfig()
connectWs()
})