同步前后端联调与文档更新

This commit is contained in:
2026-04-02 09:25:05 +08:00
parent af43beadb0
commit 6964e26ec9
113 changed files with 4317 additions and 293 deletions

View File

@@ -1,11 +1,79 @@
import { loadBackendAuthTokens } from '../../utils/backendAuth'
import { finishSession } from '../../utils/backendApi'
import { loadBackendAuthTokens, loadBackendBaseUrl } from '../../utils/backendAuth'
import { clearSessionRecoverySnapshot, loadSessionRecoverySnapshot } from '../../game/core/sessionRecovery'
import { getBackendSessionContextFromLaunchEnvelope, prepareMapPageUrlForRecovery } from '../../utils/gameLaunch'
Page({
onLoad() {
const recoverySnapshot = loadSessionRecoverySnapshot()
if (recoverySnapshot) {
this.promptRecoveryAtEntry()
return
}
this.redirectToDefaultEntry()
},
redirectToDefaultEntry() {
const tokens = loadBackendAuthTokens()
const url = tokens && tokens.accessToken
? '/pages/home/home'
: '/pages/login/login'
wx.redirectTo({ url })
},
promptRecoveryAtEntry() {
const recoverySnapshot = loadSessionRecoverySnapshot()
if (!recoverySnapshot) {
this.redirectToDefaultEntry()
return
}
wx.showModal({
title: '恢复对局',
content: '检测到上次有未正常结束的对局,是否继续恢复?',
confirmText: '继续恢复',
cancelText: '放弃',
success: (result) => {
if (result.confirm) {
wx.redirectTo({
url: prepareMapPageUrlForRecovery(recoverySnapshot.launchEnvelope),
})
return
}
const sessionContext = getBackendSessionContextFromLaunchEnvelope(recoverySnapshot.launchEnvelope)
if (!sessionContext) {
clearSessionRecoverySnapshot()
wx.showToast({
title: '已放弃上次对局',
icon: 'none',
duration: 1400,
})
this.redirectToDefaultEntry()
return
}
finishSession({
baseUrl: loadBackendBaseUrl(),
sessionId: sessionContext.sessionId,
sessionToken: sessionContext.sessionToken,
status: 'cancelled',
summary: {},
})
.catch(() => {
// 放弃恢复不阻塞进入业务页;失败只丢给后续状态页处理。
})
.finally(() => {
clearSessionRecoverySnapshot()
wx.showToast({
title: '已放弃上次对局',
icon: 'none',
duration: 1400,
})
this.redirectToDefaultEntry()
})
},
})
},
})

View File

@@ -7,6 +7,7 @@ import {
type MapEngineViewState,
} from '../../engine/map/mapEngine'
import {
getBackendSessionContextFromLaunchEnvelope,
getDemoGameLaunchEnvelope,
resolveGameLaunchEnvelope,
type GameLaunchEnvelope,
@@ -177,6 +178,9 @@ let currentRemoteMapConfig: RemoteMapConfig | undefined
let systemSettingsLockLifetimeActive = false
let syncedBackendSessionStartId = ''
let syncedBackendSessionFinishId = ''
let shouldAutoRestoreRecoverySnapshot = false
const DEBUG_MOCK_CHANNEL_ID_STORAGE_KEY = 'cmr.debug.mockChannelId.v1'
const DEBUG_MOCK_AUTO_CONNECT_STORAGE_KEY = 'cmr.debug.autoConnectMockSources.v1'
let lastCenterScaleRulerStablePatch: Pick<
MapPageData,
| 'centerScaleRulerVisible'
@@ -405,6 +409,42 @@ function updateStoredUserSettings(patch: Partial<StoredUserSettings>) {
)
}
function loadStoredMockChannelId(): string {
try {
const value = wx.getStorageSync(DEBUG_MOCK_CHANNEL_ID_STORAGE_KEY)
if (typeof value === 'string' && value.trim().length > 0) {
return value.trim()
}
} catch (_error) {
// Ignore storage read failures and fall back to default.
}
return 'default'
}
function persistMockChannelId(channelId: string) {
try {
wx.setStorageSync(DEBUG_MOCK_CHANNEL_ID_STORAGE_KEY, channelId)
} catch (_error) {
// Ignore storage write failures in debug preference persistence.
}
}
function loadMockAutoConnectEnabled(): boolean {
try {
return wx.getStorageSync(DEBUG_MOCK_AUTO_CONNECT_STORAGE_KEY) === true
} catch (_error) {
return false
}
}
function persistMockAutoConnectEnabled(enabled: boolean) {
try {
wx.setStorageSync(DEBUG_MOCK_AUTO_CONNECT_STORAGE_KEY, enabled)
} catch (_error) {
// Ignore storage write failures in debug preference persistence.
}
}
function buildResolvedSystemSettingsPatch(
resolvedSettings: ResolvedSystemSettingsState,
): Partial<MapPageData> {
@@ -446,26 +486,7 @@ function hasExplicitLaunchOptions(options?: MapPageLaunchOptions | null): boolea
}
function getCurrentBackendSessionContext(): { sessionId: string; sessionToken: string } | null {
const business = currentGameLaunchEnvelope.business
if (!business || !business.sessionId || !business.sessionToken) {
return null
}
return {
sessionId: business.sessionId,
sessionToken: business.sessionToken,
}
}
function getBackendSessionContextFromLaunchEnvelope(envelope: GameLaunchEnvelope | null | undefined): { sessionId: string; sessionToken: string } | null {
if (!envelope || !envelope.business || !envelope.business.sessionId || !envelope.business.sessionToken) {
return null
}
return {
sessionId: envelope.business.sessionId,
sessionToken: envelope.business.sessionToken,
}
return getBackendSessionContextFromLaunchEnvelope(currentGameLaunchEnvelope)
}
function getCurrentBackendBaseUrl(): string {
@@ -908,6 +929,7 @@ Page({
clearSessionRecoveryPersistTimer()
syncedBackendSessionStartId = ''
syncedBackendSessionFinishId = ''
shouldAutoRestoreRecoverySnapshot = options && options.recoverSession === '1'
currentGameLaunchEnvelope = resolveGameLaunchEnvelope(options)
if (!hasExplicitLaunchOptions(options)) {
const recoverySnapshot = loadSessionRecoverySnapshot()
@@ -918,6 +940,8 @@ Page({
currentSystemSettingsConfig = undefined
currentRemoteMapConfig = undefined
systemSettingsLockLifetimeActive = false
const storedMockChannelId = loadStoredMockChannelId()
const shouldAutoConnectMockSources = loadMockAutoConnectEnabled()
const systemInfo = wx.getSystemInfoSync()
const statusBarHeight = systemInfo.statusBarHeight || 0
const menuButtonRect = wx.getMenuButtonBoundingClientRect()
@@ -1215,9 +1239,9 @@ Page({
mockBridgeConnected: false,
mockBridgeStatusText: '未连接',
mockBridgeUrlText: 'wss://gs.gotomars.xyz/mock-gps',
mockChannelIdText: 'default',
mockChannelIdText: storedMockChannelId,
mockBridgeUrlDraft: 'wss://gs.gotomars.xyz/mock-gps',
mockChannelIdDraft: 'default',
mockChannelIdDraft: storedMockChannelId,
mockCoordText: '--',
mockSpeedText: '--',
heartRateSourceMode: 'real',
@@ -1314,6 +1338,10 @@ Page({
centerTileY: 0,
tileSizePx: 0,
}),
}, () => {
if (shouldAutoConnectMockSources) {
this.handleConnectAllMockSources()
}
})
},
@@ -1359,6 +1387,7 @@ Page({
currentRemoteMapConfig = undefined
systemSettingsLockLifetimeActive = false
currentGameLaunchEnvelope = getDemoGameLaunchEnvelope()
shouldAutoRestoreRecoverySnapshot = false
stageCanvasAttached = false
},
@@ -1499,6 +1528,34 @@ Page({
})
},
restoreRecoverySnapshot(snapshot: SessionRecoverySnapshot) {
systemSettingsLockLifetimeActive = true
this.applyRuntimeSystemSettings(true)
const restored = mapEngine ? mapEngine.restoreSessionRecoveryRuntimeSnapshot(snapshot.runtime) : false
if (!restored) {
clearSessionRecoverySnapshot()
wx.showToast({
title: '恢复失败,已回到初始状态',
icon: 'none',
duration: 1600,
})
return false
}
this.setData({
showResultScene: false,
showDebugPanel: false,
showGameInfoPanel: false,
showSystemSettingsPanel: false,
})
const sessionContext = getCurrentBackendSessionContext()
if (sessionContext) {
syncedBackendSessionStartId = sessionContext.sessionId
}
this.syncSessionRecoveryLifecycle('running')
return true
},
syncSessionRecoveryLifecycle(status: MapPageData['gameSessionStatus']) {
if (status === 'running') {
this.persistSessionRecoverySnapshot()
@@ -1528,6 +1585,12 @@ Page({
return
}
if (shouldAutoRestoreRecoverySnapshot) {
shouldAutoRestoreRecoverySnapshot = false
this.restoreRecoverySnapshot(snapshot)
return
}
wx.showModal({
title: '恢复对局',
content: '检测到上次有未正常结束的对局,是否继续恢复?',
@@ -1539,32 +1602,9 @@ Page({
return
}
systemSettingsLockLifetimeActive = true
this.applyRuntimeSystemSettings(true)
const restored = mapEngine ? mapEngine.restoreSessionRecoveryRuntimeSnapshot(snapshot.runtime) : false
if (!restored) {
clearSessionRecoverySnapshot()
wx.showToast({
title: '恢复失败,已回到初始状态',
icon: 'none',
duration: 1600,
})
return
}
this.setData({
showResultScene: false,
showDebugPanel: false,
showGameInfoPanel: false,
showSystemSettingsPanel: false,
})
const sessionContext = getCurrentBackendSessionContext()
if (sessionContext) {
syncedBackendSessionStartId = sessionContext.sessionId
}
this.syncSessionRecoveryLifecycle('running')
},
})
this.restoreRecoverySnapshot(snapshot)
},
})
},
compileCurrentRuntimeProfile(lockLifetimeActive = isSystemSettingsLockLifetimeActive()) {
@@ -1890,7 +1930,13 @@ Page({
if (!mapEngine) {
return
}
mapEngine.handleSetMockChannelId(this.data.mockChannelIdDraft)
const channelId = (this.data.mockChannelIdDraft || '').trim() || 'default'
this.setData({
mockChannelIdDraft: channelId,
})
persistMockChannelId(channelId)
persistMockAutoConnectEnabled(true)
mapEngine.handleSetMockChannelId(channelId)
mapEngine.handleSetMockLocationBridgeUrl(this.data.mockBridgeUrlDraft)
mapEngine.handleSetMockHeartRateBridgeUrl(this.data.mockHeartRateBridgeUrlDraft)
mapEngine.handleSetMockDebugLogBridgeUrl(this.data.mockDebugLogBridgeUrlDraft)
@@ -1914,8 +1960,13 @@ Page({
},
handleSaveMockChannelId() {
const channelId = (this.data.mockChannelIdDraft || '').trim() || 'default'
this.setData({
mockChannelIdDraft: channelId,
})
persistMockChannelId(channelId)
if (mapEngine) {
mapEngine.handleSetMockChannelId(this.data.mockChannelIdDraft)
mapEngine.handleSetMockChannelId(channelId)
}
},
@@ -1932,6 +1983,7 @@ Page({
},
handleDisconnectMockLocationBridge() {
persistMockAutoConnectEnabled(false)
if (mapEngine) {
mapEngine.handleDisconnectMockLocationBridge()
}
@@ -1980,6 +2032,7 @@ Page({
},
handleDisconnectMockDebugLogBridge() {
persistMockAutoConnectEnabled(false)
if (mapEngine) {
mapEngine.handleDisconnectMockDebugLogBridge()
}
@@ -1992,6 +2045,7 @@ Page({
},
handleDisconnectMockHeartRateBridge() {
persistMockAutoConnectEnabled(false)
if (mapEngine) {
mapEngine.handleDisconnectMockHeartRateBridge()
}

View File

@@ -802,7 +802,7 @@
<view class="debug-section__desc">定位模拟、心率模拟、调试日志与方向状态</view>
</view>
<view class="control-row">
<view class="control-chip control-chip--primary" bindtap="handleConnectAllMockSources">一键连接开发调试源</view>
<view class="control-chip {{mockBridgeConnected && mockHeartRateBridgeConnected && mockDebugLogBridgeConnected ? 'control-chip--active' : 'control-chip--secondary'}}" bindtap="handleConnectAllMockSources">一键连接开发调试源</view>
<view class="control-chip control-chip--secondary" bindtap="handleOpenWebViewTest">测试 H5</view>
</view>
<view class="info-panel__row info-panel__row--stack">