import { clearBackendAuthTokens, loadBackendAuthTokens, loadBackendBaseUrl } from '../../utils/backendAuth' import { finishSession, getEntryHome, type BackendCardResult, type BackendEntryHomeResult } from '../../utils/backendApi' import { reportBackendClientLog } from '../../utils/backendClientLogs' import { setGlobalMockDebugBridgeEnabled } from '../../utils/globalMockDebugBridge' import { clearSessionRecoverySnapshot, loadSessionRecoverySnapshot } from '../../game/core/sessionRecovery' import { getBackendSessionContextFromLaunchEnvelope, prepareMapPageUrlForRecovery } from '../../utils/gameLaunch' const DEFAULT_CHANNEL_CODE = 'mini-demo' const DEFAULT_CHANNEL_TYPE = 'wechat_mini' type HomePageData = { loading: boolean statusText: string userNameText: string tenantText: string channelText: string ongoingSessionText: string recentSessionText: string ongoingRuntimeText: string recentRuntimeText: string ongoingActionHintText: string showOngoingPanel: boolean canRecoverOngoing: boolean canAbandonOngoing: boolean cards: BackendCardResult[] } function formatSessionSummary(session?: BackendEntryHomeResult['ongoingSession'] | null): string { if (!session) { return '无' } const title = session.eventName || session.eventDisplayName || session.eventId || session.id || session.sessionId const status = session.status || session.sessionStatus || '--' const route = session.routeCode || session.variantName || '默认赛道' return `${title} / ${status} / ${route}` } function formatRuntimeSummary(session?: BackendEntryHomeResult['ongoingSession'] | null): string { if (!session || !session.runtime) { return '运行对象 --' } const runtime = session.runtime const placeText = runtime.placeName || runtime.placeId || '--' const mapText = runtime.mapName || runtime.mapId || '--' const variantText = runtime.courseVariantId || session.variantName || session.variantId || '--' return `地点 ${placeText} / 地图 ${mapText} / 赛道 ${variantText}` } function requireAuthToken(): string | null { const app = getApp() const tokens = app.globalData && app.globalData.backendAuthTokens ? app.globalData.backendAuthTokens : loadBackendAuthTokens() return tokens && tokens.accessToken ? tokens.accessToken : null } function getRecoverySnapshotSessionId(): string { const snapshot = loadSessionRecoverySnapshot() if (!snapshot) { return '' } const context = getBackendSessionContextFromLaunchEnvelope(snapshot.launchEnvelope) return context && context.sessionId ? context.sessionId : '' } Page({ data: { loading: false, statusText: '准备加载首页', userNameText: '--', tenantText: '--', channelText: '--', ongoingSessionText: '无', recentSessionText: '无', ongoingRuntimeText: '运行对象 --', recentRuntimeText: '运行对象 --', ongoingActionHintText: '当前没有可恢复的进行中对局', showOngoingPanel: false, canRecoverOngoing: false, canAbandonOngoing: false, cards: [], } as HomePageData, onLoad() { this.loadEntryHome() }, onShow() { this.loadEntryHome() }, async loadEntryHome() { const accessToken = requireAuthToken() if (!accessToken) { wx.redirectTo({ url: '/pages/login/login' }) return } this.setData({ loading: true, statusText: '正在加载首页聚合', }) try { const result = await getEntryHome({ baseUrl: loadBackendBaseUrl(), accessToken, channelCode: DEFAULT_CHANNEL_CODE, channelType: DEFAULT_CHANNEL_TYPE, }) this.applyEntryHomeResult(result) } catch (error) { const message = error && (error as { message?: string }).message ? (error as { message: string }).message : '未知错误' this.setData({ loading: false, statusText: `首页加载失败:${message}`, }) } }, applyEntryHomeResult(result: BackendEntryHomeResult) { const ongoingSession = result.ongoingSession || null const recoverySnapshotSessionId = getRecoverySnapshotSessionId() const canRecoverOngoing = !!ongoingSession && !!recoverySnapshotSessionId && ongoingSession.id === recoverySnapshotSessionId const canAbandonOngoing = canRecoverOngoing const ongoingActionHintText = !ongoingSession ? '当前没有可恢复的进行中对局' : canRecoverOngoing ? '检测到本机仍保留这局的恢复记录,你可以继续恢复或主动放弃。' : '检测到后端存在进行中对局,但本机当前没有匹配的恢复快照。' reportBackendClientLog({ level: 'info', category: 'entry-home', message: 'entry home loaded', details: { ongoingSessionId: result.ongoingSession && result.ongoingSession.id ? result.ongoingSession.id : '', ongoingEventId: result.ongoingSession && result.ongoingSession.eventId ? result.ongoingSession.eventId : '', recentSessionId: result.recentSession && result.recentSession.id ? result.recentSession.id : '', recentEventId: result.recentSession && result.recentSession.eventId ? result.recentSession.eventId : '', cardEventIds: (result.cards || []).map((item) => (item.event && item.event.id ? item.event.id : '')), hasOngoingSession: !!ongoingSession, recoverySnapshotSessionId, canRecoverOngoing, }, }) this.setData({ loading: false, statusText: '首页加载完成', userNameText: result.user.nickname || result.user.publicId || result.user.id, tenantText: `${result.tenant.name} (${result.tenant.code})`, channelText: `${result.channel.displayName} / ${result.channel.code}`, ongoingSessionText: formatSessionSummary(result.ongoingSession), recentSessionText: formatSessionSummary(result.recentSession), ongoingRuntimeText: formatRuntimeSummary(result.ongoingSession), recentRuntimeText: formatRuntimeSummary(result.recentSession), ongoingActionHintText, showOngoingPanel: !!ongoingSession, canRecoverOngoing, canAbandonOngoing, cards: result.cards || [], }) }, handleRefresh() { this.loadEntryHome() }, handleOpenCard(event: WechatMiniprogram.TouchEvent) { const eventId = event.currentTarget.dataset.eventId as string | undefined if (!eventId) { wx.showToast({ title: '该卡片暂无活动入口', icon: 'none', }) return } wx.navigateTo({ url: `/pages/event/event?eventId=${encodeURIComponent(eventId)}`, }) }, handleOpenRecentResult() { wx.navigateTo({ url: '/pages/results/results', }) }, handleOpenEventList() { wx.navigateTo({ url: '/pages/events/events', }) }, handleOpenExperienceMaps() { wx.navigateTo({ url: '/pages/experience-maps/experience-maps', }) }, handleResumeOngoing() { const snapshot = loadSessionRecoverySnapshot() if (!snapshot) { wx.showToast({ title: '本机未找到恢复快照', icon: 'none', }) this.loadEntryHome() return } wx.navigateTo({ url: prepareMapPageUrlForRecovery(snapshot.launchEnvelope), }) }, handleAbandonOngoing() { const snapshot = loadSessionRecoverySnapshot() if (!snapshot) { wx.showToast({ title: '本机未找到恢复快照', icon: 'none', }) this.loadEntryHome() return } const sessionContext = getBackendSessionContextFromLaunchEnvelope(snapshot.launchEnvelope) if (!sessionContext) { clearSessionRecoverySnapshot() wx.showToast({ title: '已清理本机恢复记录', icon: 'none', }) this.loadEntryHome() return } wx.showModal({ title: '放弃进行中的游戏', content: '放弃后,这局游戏会记为已取消,且不会再出现在“进行中”。', confirmText: '确认放弃', cancelText: '先保留', success: (result) => { if (!result.confirm) { return } finishSession({ baseUrl: loadBackendBaseUrl(), sessionId: sessionContext.sessionId, sessionToken: sessionContext.sessionToken, status: 'cancelled', summary: {}, }).catch(() => { wx.showToast({ title: '取消上报失败,请稍后重试', icon: 'none', }) }).finally(() => { clearSessionRecoverySnapshot() this.loadEntryHome() }) }, }) }, handleLogout() { clearBackendAuthTokens() setGlobalMockDebugBridgeEnabled(false) const app = getApp() if (app.globalData) { app.globalData.backendAuthTokens = null } wx.redirectTo({ url: '/pages/login/login', }) }, })