完善活动运营域与联调标准化

This commit is contained in:
2026-04-03 13:11:41 +08:00
parent 0e28f70bad
commit 129ea935db
56 changed files with 11004 additions and 196 deletions

View File

@@ -19,6 +19,12 @@ type EventPreparePageData = {
assignmentMode: string
variantModeText: string
variantSummaryText: string
presentationText: string
contentBundleText: string
runtimePlaceText: string
runtimeMapText: string
runtimeVariantText: string
runtimeRouteCodeText: string
selectedVariantId: string
selectedVariantText: string
selectableVariants: Array<{
@@ -85,6 +91,24 @@ function formatVariantSummary(result: BackendEventPlayResult): string {
return preview
}
function formatPresentationSummary(result: BackendEventPlayResult): string {
const currentPresentation = result.currentPresentation
if (!currentPresentation) {
return '当前未声明展示版本'
}
return `${currentPresentation.presentationId || '--'} / ${currentPresentation.templateKey || '--'} / ${currentPresentation.version || '--'}`
}
function formatContentBundleSummary(result: BackendEventPlayResult): string {
const currentContentBundle = result.currentContentBundle
if (!currentContentBundle) {
return '当前未声明内容包版本'
}
return `${currentContentBundle.bundleId || '--'} / ${currentContentBundle.bundleType || '--'} / ${currentContentBundle.version || '--'}`
}
function resolveSelectedVariantId(
currentVariantId: string,
assignmentMode?: string | null,
@@ -184,6 +208,12 @@ Page({
assignmentMode: '',
variantModeText: '--',
variantSummaryText: '--',
presentationText: '--',
contentBundleText: '--',
runtimePlaceText: '待 launch 确认',
runtimeMapText: '待 launch 确认',
runtimeVariantText: '待 launch 确认',
runtimeRouteCodeText: '待 launch 确认',
selectedVariantId: '',
selectedVariantText: '当前无需手动指定赛道',
selectableVariants: [],
@@ -277,6 +307,20 @@ Page({
assignmentMode: result.play.assignmentMode || '',
variantModeText: formatAssignmentMode(result.play.assignmentMode),
variantSummaryText: formatVariantSummary(result),
presentationText: formatPresentationSummary(result),
contentBundleText: formatContentBundleSummary(result),
runtimePlaceText: '待 launch.runtime 确认',
runtimeMapText: '待 launch.runtime 确认',
runtimeVariantText: selectedVariant
? selectedVariant.name
: (result.play.courseVariants && result.play.courseVariants[0]
? result.play.courseVariants[0].name
: '待 launch 确认'),
runtimeRouteCodeText: selectedVariant
? selectedVariant.routeCodeText
: (result.play.courseVariants && result.play.courseVariants[0] && result.play.courseVariants[0].routeCode
? result.play.courseVariants[0].routeCode || '待 launch 确认'
: '待 launch 确认'),
selectedVariantId,
selectedVariantText: selectedVariant
? `${selectedVariant.name} / ${selectedVariant.routeCodeText}`
@@ -512,6 +556,8 @@ Page({
selectedVariantText: selectedVariant
? `${selectedVariant.name} / ${selectedVariant.routeCodeText}`
: '当前无需手动指定赛道',
runtimeVariantText: selectedVariant ? selectedVariant.name : '待 launch 确认',
runtimeRouteCodeText: selectedVariant ? selectedVariant.routeCodeText : '待 launch 确认',
selectableVariants,
})
},

View File

@@ -16,6 +16,40 @@
<view class="summary">当前选择:{{selectedVariantText}}</view>
</view>
<view class="panel">
<view class="panel__title">活动运营摘要</view>
<view class="summary">当前阶段先展示活动运营对象摘要,不展开复杂 schema。</view>
<view class="row">
<view class="row__label">展示版本</view>
<view class="row__value">{{presentationText}}</view>
</view>
<view class="row">
<view class="row__label">内容包版本</view>
<view class="row__value">{{contentBundleText}}</view>
</view>
</view>
<view class="panel">
<view class="panel__title">运行对象摘要</view>
<view class="summary">当前阶段以前端已知信息预览,最终绑定以后端 `launch.runtime` 为准。</view>
<view class="row">
<view class="row__label">地点</view>
<view class="row__value">{{runtimePlaceText}}</view>
</view>
<view class="row">
<view class="row__label">地图</view>
<view class="row__value">{{runtimeMapText}}</view>
</view>
<view class="row">
<view class="row__label">赛道</view>
<view class="row__value">{{runtimeVariantText}}</view>
</view>
<view class="row">
<view class="row__label">RouteCode</view>
<view class="row__value">{{runtimeRouteCodeText}}</view>
</view>
</view>
<view class="panel" wx:if="{{assignmentMode === 'manual' && selectableVariants.length}}">
<view class="panel__title">赛道选择</view>
<view class="summary">当前活动要求手动指定赛道。这里的选择会随 launch 一起带给后端,最终绑定以后端返回为准。</view>

View File

@@ -11,6 +11,8 @@ type EventPageData = {
statusText: string
variantModeText: string
variantSummaryText: string
presentationText: string
contentBundleText: string
}
function formatAssignmentMode(mode?: string | null): string {
@@ -38,6 +40,30 @@ function formatVariantSummary(result: BackendEventPlayResult): string {
return `${variants.length} 条赛道,可选 ${selectable.length} 条:${preview}${suffix}`
}
function formatPresentationSummary(result: BackendEventPlayResult): string {
const currentPresentation = result.currentPresentation
if (!currentPresentation) {
return '当前未声明展示版本'
}
const presentationId = currentPresentation.presentationId || '--'
const templateKey = currentPresentation.templateKey || '--'
const version = currentPresentation.version || '--'
return `${presentationId} / ${templateKey} / ${version}`
}
function formatContentBundleSummary(result: BackendEventPlayResult): string {
const currentContentBundle = result.currentContentBundle
if (!currentContentBundle) {
return '当前未声明内容包版本'
}
const bundleId = currentContentBundle.bundleId || '--'
const bundleType = currentContentBundle.bundleType || '--'
const version = currentContentBundle.version || '--'
return `${bundleId} / ${bundleType} / ${version}`
}
function getAccessToken(): string | null {
const app = getApp<IAppOption>()
const tokens = app.globalData && app.globalData.backendAuthTokens
@@ -57,6 +83,8 @@ Page({
statusText: '待加载',
variantModeText: '--',
variantSummaryText: '--',
presentationText: '--',
contentBundleText: '--',
} as EventPageData,
onLoad(query: { eventId?: string }) {
@@ -112,6 +140,8 @@ Page({
statusText: result.play.canLaunch ? '可启动' : '当前不可启动',
variantModeText: formatAssignmentMode(result.play.assignmentMode),
variantSummaryText: formatVariantSummary(result),
presentationText: formatPresentationSummary(result),
contentBundleText: formatContentBundleSummary(result),
})
},

View File

@@ -13,6 +13,8 @@
<view class="summary">状态:{{statusText}}</view>
<view class="summary">赛道模式:{{variantModeText}}</view>
<view class="summary">赛道摘要:{{variantSummaryText}}</view>
<view class="summary">展示版本:{{presentationText}}</view>
<view class="summary">内容包版本:{{contentBundleText}}</view>
<view class="actions">
<button class="btn btn--secondary" bindtap="handleRefresh">刷新</button>
<button class="btn btn--primary" bindtap="handleLaunch">前往准备页</button>

View File

@@ -12,6 +12,8 @@ type HomePageData = {
channelText: string
ongoingSessionText: string
recentSessionText: string
ongoingRuntimeText: string
recentRuntimeText: string
cards: BackendCardResult[]
}
@@ -26,6 +28,18 @@ function formatSessionSummary(session?: BackendEntryHomeResult['ongoingSession']
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<IAppOption>()
const tokens = app.globalData && app.globalData.backendAuthTokens
@@ -43,6 +57,8 @@ Page({
channelText: '--',
ongoingSessionText: '无',
recentSessionText: '无',
ongoingRuntimeText: '运行对象 --',
recentRuntimeText: '运行对象 --',
cards: [],
} as HomePageData,
@@ -92,6 +108,8 @@ Page({
channelText: `${result.channel.displayName} / ${result.channel.code}`,
ongoingSessionText: formatSessionSummary(result.ongoingSession),
recentSessionText: formatSessionSummary(result.recentSession),
ongoingRuntimeText: formatRuntimeSummary(result.ongoingSession),
recentRuntimeText: formatRuntimeSummary(result.recentSession),
cards: result.cards || [],
})
},

View File

@@ -11,7 +11,9 @@
<view class="panel__title">当前状态</view>
<view class="summary">{{statusText}}</view>
<view class="summary">进行中:{{ongoingSessionText}}</view>
<view class="summary">进行中运行对象:{{ongoingRuntimeText}}</view>
<view class="summary">最近一局:{{recentSessionText}}</view>
<view class="summary">最近一局运行对象:{{recentRuntimeText}}</view>
<view class="actions">
<button class="btn btn--secondary" bindtap="handleRefresh">刷新首页</button>
<button class="btn btn--ghost" bindtap="handleOpenRecentResult">查看结果</button>

View File

@@ -809,6 +809,25 @@ function buildEmptyResultSceneSnapshot(): MapEngineResultSnapshot {
}
}
function buildRuntimeSummaryRows(envelope: GameLaunchEnvelope): MapEngineGameInfoRow[] {
const runtime = envelope.runtime
const variantName = envelope.variant ? (envelope.variant.variantName || envelope.variant.variantId || null) : null
const variantRouteCode = envelope.variant ? (envelope.variant.routeCode || null) : null
if (!runtime) {
return []
}
const rows: MapEngineGameInfoRow[] = []
rows.push({ label: '运行绑定', value: runtime.runtimeBindingId || '--' })
rows.push({ label: '地点', value: runtime.placeName || runtime.placeId || '--' })
rows.push({ label: '地图', value: runtime.mapName || runtime.mapId || '--' })
rows.push({ label: '赛道集', value: runtime.courseSetId || '--' })
rows.push({ label: '赛道版本', value: runtime.courseVariantId || variantName || '--' })
rows.push({ label: 'RouteCode', value: runtime.routeCode || variantRouteCode || '--' })
rows.push({ label: '瓦片版本', value: runtime.tileReleaseId || '--' })
return rows
}
Page({
data: {
showDebugPanel: false,
@@ -1640,6 +1659,7 @@ Page({
const app = getApp<IAppOption>()
if (app.globalData) {
app.globalData.pendingResultSnapshot = snapshot
app.globalData.pendingResultLaunchEnvelope = currentGameLaunchEnvelope
}
},
@@ -2422,6 +2442,7 @@ Page({
const snapshot = mapEngine.getGameInfoSnapshot()
const localRows = snapshot.localRows.concat([
...buildRuntimeSummaryRows(currentGameLaunchEnvelope),
{ label: '比例尺开关', value: this.data.showCenterScaleRuler ? '开启' : '关闭' },
{ label: '比例尺锚点', value: this.data.centerScaleRulerAnchorMode === 'compass-center' ? '指北针圆心' : '屏幕中心' },
{ label: '按钮习惯', value: this.data.sideButtonPlacement === 'right' ? '右手' : '左手' },
@@ -2450,7 +2471,7 @@ Page({
resultSceneSubtitle: snapshot.subtitle,
resultSceneHeroLabel: snapshot.heroLabel,
resultSceneHeroValue: snapshot.heroValue,
resultSceneRows: snapshot.rows,
resultSceneRows: snapshot.rows.concat(buildRuntimeSummaryRows(currentGameLaunchEnvelope)),
})
},

View File

@@ -1,6 +1,7 @@
import { loadBackendAuthTokens, loadBackendBaseUrl } from '../../utils/backendAuth'
import { getSessionResult } from '../../utils/backendApi'
import type { MapEngineResultSnapshot } from '../../engine/map/mapEngine'
import type { GameLaunchEnvelope } from '../../utils/gameLaunch'
type ResultPageData = {
sessionId: string
@@ -41,6 +42,56 @@ function formatRouteSummary(input: {
return '默认赛道'
}
function formatRuntimeValue(...candidates: Array<string | null | undefined>): string {
for (let index = 0; index < candidates.length; index += 1) {
const value = candidates[index]
if (typeof value === 'string' && value.trim().length > 0) {
return value.trim()
}
}
return '--'
}
function appendRuntimeRows(
rows: Array<{ label: string; value: string }>,
options: {
runtime?: {
runtimeBindingId?: string | null
placeId?: string | null
placeName?: string | null
mapId?: string | null
mapName?: string | null
tileReleaseId?: string | null
courseSetId?: string | null
courseVariantId?: string | null
routeCode?: string | null
} | null
variantName?: string | null
routeCode?: string | null
},
) {
if (!options.runtime) {
return rows
}
return rows.concat([
{ label: '运行绑定', value: formatRuntimeValue(options.runtime.runtimeBindingId) },
{ label: '地点', value: formatRuntimeValue(options.runtime.placeName, options.runtime.placeId) },
{ label: '地图', value: formatRuntimeValue(options.runtime.mapName, options.runtime.mapId) },
{ label: '赛道集', value: formatRuntimeValue(options.runtime.courseSetId) },
{ label: '赛道版本', value: formatRuntimeValue(options.runtime.courseVariantId, options.variantName) },
{ label: 'RouteCode', value: formatRuntimeValue(options.runtime.routeCode, options.routeCode) },
{ label: '瓦片版本', value: formatRuntimeValue(options.runtime.tileReleaseId) },
])
}
function loadPendingResultLaunchEnvelope(): GameLaunchEnvelope | null {
const app = getApp<IAppOption>()
return app.globalData && app.globalData.pendingResultLaunchEnvelope
? app.globalData.pendingResultLaunchEnvelope
: null
}
Page({
data: {
sessionId: '',
@@ -75,17 +126,22 @@ Page({
return
}
const pendingLaunchEnvelope = loadPendingResultLaunchEnvelope()
this.setData({
statusText: '正在加载结果',
sessionTitleText: snapshot.title,
sessionSubtitleText: snapshot.subtitle,
rows: [
rows: appendRuntimeRows([
{ label: snapshot.heroLabel, value: snapshot.heroValue },
...snapshot.rows.map((row) => ({
label: row.label,
value: row.value,
})),
],
], {
runtime: pendingLaunchEnvelope && pendingLaunchEnvelope.runtime ? pendingLaunchEnvelope.runtime : null,
variantName: pendingLaunchEnvelope && pendingLaunchEnvelope.variant ? pendingLaunchEnvelope.variant.variantName : null,
routeCode: pendingLaunchEnvelope && pendingLaunchEnvelope.variant ? pendingLaunchEnvelope.variant.routeCode : null,
}),
})
if (app.globalData) {
@@ -110,11 +166,12 @@ Page({
accessToken,
sessionId,
})
const pendingLaunchEnvelope = loadPendingResultLaunchEnvelope()
this.setData({
statusText: '单局结果加载完成',
sessionTitleText: result.session.eventName || result.session.eventDisplayName || result.session.eventId || result.session.id || result.session.sessionId,
sessionSubtitleText: `${result.session.status || result.session.sessionStatus} / ${result.result.status} / ${formatRouteSummary(result.session)}`,
rows: [
rows: appendRuntimeRows([
{ label: '赛道版本', value: formatRouteSummary(result.session) },
{ label: '最终得分', value: formatValue(result.result.finalScore) },
{ label: '最终用时(秒)', value: formatValue(result.result.finalDurationSec) },
@@ -123,8 +180,16 @@ Page({
{ label: '累计里程(m)', value: formatValue(result.result.distanceMeters) },
{ label: '平均速度(km/h)', value: formatValue(result.result.averageSpeedKmh) },
{ label: '最大心率', value: formatValue(result.result.maxHeartRateBpm) },
],
], {
runtime: result.session.runtime || (pendingLaunchEnvelope && pendingLaunchEnvelope.runtime ? pendingLaunchEnvelope.runtime : null),
variantName: result.session.variantName || (pendingLaunchEnvelope && pendingLaunchEnvelope.variant ? pendingLaunchEnvelope.variant.variantName : null),
routeCode: result.session.routeCode || (pendingLaunchEnvelope && pendingLaunchEnvelope.variant ? pendingLaunchEnvelope.variant.routeCode : null),
}),
})
const app = getApp<IAppOption>()
if (app.globalData) {
app.globalData.pendingResultLaunchEnvelope = null
}
} catch (error) {
const message = error && (error as { message?: string }).message ? (error as { message: string }).message : '未知错误'
this.setData({

View File

@@ -10,6 +10,7 @@ type ResultsPageData = {
statusText: string
scoreText: string
routeText: string
runtimeText: string
}>
}
@@ -35,6 +36,18 @@ function formatRouteSummary(result: BackendSessionResultView): string {
return '默认赛道'
}
function formatRuntimeSummary(result: BackendSessionResultView): string {
const runtime = result.session.runtime
if (!runtime) {
return '运行对象 --'
}
const placeText = runtime.placeName || runtime.placeId || '--'
const mapText = runtime.mapName || runtime.mapId || '--'
const variantText = runtime.courseVariantId || result.session.variantName || result.session.variantId || '--'
return `地点 ${placeText} / 地图 ${mapText} / 赛道 ${variantText}`
}
function buildResultCardView(result: BackendSessionResultView) {
return {
sessionId: result.session.id,
@@ -42,6 +55,7 @@ function buildResultCardView(result: BackendSessionResultView) {
statusText: `${result.result.status} / ${result.session.status}`,
scoreText: `得分 ${result.result.finalScore || '--'} / 用时 ${result.result.finalDurationSec || '--'}s`,
routeText: `赛道 ${formatRouteSummary(result)}`,
runtimeText: formatRuntimeSummary(result),
}
}

View File

@@ -19,6 +19,7 @@
<view class="result-card__meta">{{item.statusText}}</view>
<view class="result-card__meta">{{item.scoreText}}</view>
<view class="result-card__meta">{{item.routeText}}</view>
<view class="result-card__meta">{{item.runtimeText}}</view>
</view>
</view>
</view>