1.修复游戏实时对局/admin/game/live页面的报错
This commit is contained in:
@@ -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',
|
||||
];
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user