event-prepare.ts 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  1. import { loadBackendAuthTokens, loadBackendBaseUrl } from '../../utils/backendAuth'
  2. import { getEventPlay, launchEvent, type BackendCourseVariantSummary, type BackendEventPlayResult } from '../../utils/backendApi'
  3. import { adaptBackendLaunchResultToEnvelope } from '../../utils/backendLaunchAdapter'
  4. import { formatBackendPlayActionText, formatBackendPlayStatusText } from '../../utils/backendPlayCopy'
  5. import { prepareMapPageUrlForLaunch } from '../../utils/gameLaunch'
  6. import { reportBackendClientLog } from '../../utils/backendClientLogs'
  7. import { HeartRateController } from '../../engine/sensor/heartRateController'
  8. const PREFERRED_HEART_RATE_DEVICE_STORAGE_KEY = 'cmr.preferredHeartRateDevice'
  9. const DEBUG_MOCK_CHANNEL_ID_STORAGE_KEY = 'cmr.debug.mockChannelId.v1'
  10. const DEBUG_MOCK_AUTO_CONNECT_STORAGE_KEY = 'cmr.debug.autoConnectMockSources.v1'
  11. type EventPreparePageData = {
  12. eventId: string
  13. loading: boolean
  14. titleText: string
  15. summaryText: string
  16. releaseText: string
  17. actionText: string
  18. statusText: string
  19. assignmentMode: string
  20. variantModeText: string
  21. variantSummaryText: string
  22. presentationText: string
  23. contentBundleText: string
  24. runtimePlaceText: string
  25. runtimeMapText: string
  26. runtimeVariantText: string
  27. runtimeRouteCodeText: string
  28. selectedVariantId: string
  29. selectedVariantText: string
  30. selectableVariants: Array<{
  31. id: string
  32. name: string
  33. routeCodeText: string
  34. descriptionText: string
  35. selected: boolean
  36. }>
  37. locationStatusText: string
  38. heartRateStatusText: string
  39. heartRateDeviceText: string
  40. heartRateScanText: string
  41. heartRateConnected: boolean
  42. showHeartRateDevicePicker: boolean
  43. locationPermissionGranted: boolean
  44. locationBackgroundPermissionGranted: boolean
  45. heartRateDiscoveredDevices: Array<{
  46. deviceId: string
  47. name: string
  48. rssiText: string
  49. preferred: boolean
  50. connected: boolean
  51. }>
  52. mockSourceStatusText: string
  53. }
  54. function formatAssignmentMode(mode?: string | null): string {
  55. if (mode === 'manual') {
  56. return '手动选择'
  57. }
  58. if (mode === 'random') {
  59. return '随机分配'
  60. }
  61. if (mode === 'server-assigned') {
  62. return '后台指定'
  63. }
  64. return '默认单赛道'
  65. }
  66. function formatVariantSummary(result: BackendEventPlayResult): string {
  67. const variants = result.play.courseVariants || []
  68. if (!variants.length) {
  69. return '当前未声明额外赛道版本,启动时按默认赛道进入。'
  70. }
  71. const preview = variants.map((item) => {
  72. const title = item.routeCode || item.name
  73. return item.selectable === false ? `${title}(固定)` : title
  74. }).join(' / ')
  75. if (result.play.assignmentMode === 'manual') {
  76. return `当前活动支持 ${variants.length} 条赛道。本阶段前端先展示赛道信息,最终绑定以后端 launch 返回为准:${preview}`
  77. }
  78. if (result.play.assignmentMode === 'random') {
  79. return `当前活动支持 ${variants.length} 条赛道,进入地图前由后端随机绑定:${preview}`
  80. }
  81. if (result.play.assignmentMode === 'server-assigned') {
  82. return `当前活动赛道由后台预先指定:${preview}`
  83. }
  84. return preview
  85. }
  86. function formatPresentationSummary(result: BackendEventPlayResult): string {
  87. const currentPresentation = result.currentPresentation
  88. if (!currentPresentation) {
  89. return '当前未声明展示版本'
  90. }
  91. return `${currentPresentation.presentationId || '--'} / ${currentPresentation.templateKey || '--'} / ${currentPresentation.version || '--'}`
  92. }
  93. function formatContentBundleSummary(result: BackendEventPlayResult): string {
  94. const currentContentBundle = result.currentContentBundle
  95. if (!currentContentBundle) {
  96. return '当前未声明内容包版本'
  97. }
  98. return `${currentContentBundle.bundleId || '--'} / ${currentContentBundle.bundleType || '--'} / ${currentContentBundle.version || '--'}`
  99. }
  100. function resolveSelectedVariantId(
  101. currentVariantId: string,
  102. assignmentMode?: string | null,
  103. variants?: BackendCourseVariantSummary[] | null,
  104. ): string {
  105. if (assignmentMode !== 'manual' || !variants || !variants.length) {
  106. return ''
  107. }
  108. const selectable = variants.filter((item) => item.selectable !== false)
  109. if (!selectable.length) {
  110. return ''
  111. }
  112. const currentStillExists = selectable.some((item) => item.id === currentVariantId)
  113. if (currentVariantId && currentStillExists) {
  114. return currentVariantId
  115. }
  116. return selectable[0].id
  117. }
  118. function buildSelectableVariants(
  119. selectedVariantId: string,
  120. assignmentMode?: string | null,
  121. variants?: BackendCourseVariantSummary[] | null,
  122. ) {
  123. if (assignmentMode !== 'manual' || !variants || !variants.length) {
  124. return []
  125. }
  126. return variants
  127. .filter((item) => item.selectable !== false)
  128. .map((item) => ({
  129. id: item.id,
  130. name: item.name,
  131. routeCodeText: item.routeCode || '默认编码',
  132. descriptionText: item.description || '暂无赛道说明',
  133. selected: item.id === selectedVariantId,
  134. }))
  135. }
  136. let prepareHeartRateController: HeartRateController | null = null
  137. function getAccessToken(): string | null {
  138. const app = getApp<IAppOption>()
  139. const tokens = app.globalData && app.globalData.backendAuthTokens
  140. ? app.globalData.backendAuthTokens
  141. : loadBackendAuthTokens()
  142. return tokens && tokens.accessToken ? tokens.accessToken : null
  143. }
  144. function loadPreferredHeartRateDeviceName(): string | null {
  145. try {
  146. const stored = wx.getStorageSync(PREFERRED_HEART_RATE_DEVICE_STORAGE_KEY)
  147. if (!stored || typeof stored !== 'object') {
  148. return null
  149. }
  150. const normalized = stored as { name?: unknown }
  151. return typeof normalized.name === 'string' && normalized.name.trim().length > 0
  152. ? normalized.name.trim()
  153. : '心率带'
  154. } catch (_error) {
  155. return null
  156. }
  157. }
  158. function loadStoredMockChannelId(): string {
  159. try {
  160. const stored = wx.getStorageSync(DEBUG_MOCK_CHANNEL_ID_STORAGE_KEY)
  161. if (typeof stored === 'string' && stored.trim().length > 0) {
  162. return stored.trim()
  163. }
  164. } catch (_error) {
  165. return 'default'
  166. }
  167. return 'default'
  168. }
  169. function loadMockAutoConnectEnabled(): boolean {
  170. try {
  171. return wx.getStorageSync(DEBUG_MOCK_AUTO_CONNECT_STORAGE_KEY) === true
  172. } catch (_error) {
  173. return false
  174. }
  175. }
  176. Page({
  177. data: {
  178. eventId: '',
  179. loading: false,
  180. titleText: '开始前准备',
  181. summaryText: '未加载',
  182. releaseText: '--',
  183. actionText: '--',
  184. statusText: '待加载',
  185. assignmentMode: '',
  186. variantModeText: '--',
  187. variantSummaryText: '--',
  188. presentationText: '--',
  189. contentBundleText: '--',
  190. runtimePlaceText: '待 launch 确认',
  191. runtimeMapText: '待 launch 确认',
  192. runtimeVariantText: '待 launch 确认',
  193. runtimeRouteCodeText: '待 launch 确认',
  194. selectedVariantId: '',
  195. selectedVariantText: '当前无需手动指定赛道',
  196. selectableVariants: [],
  197. locationStatusText: '待进入地图后校验定位权限与实时精度',
  198. heartRateStatusText: '局前心率带连接入口待接入,本轮先保留骨架',
  199. heartRateDeviceText: '--',
  200. heartRateScanText: '未扫描',
  201. heartRateConnected: false,
  202. showHeartRateDevicePicker: false,
  203. locationPermissionGranted: false,
  204. locationBackgroundPermissionGranted: false,
  205. heartRateDiscoveredDevices: [],
  206. mockSourceStatusText: '模拟源调试仍在地图页调试面板中使用',
  207. } as EventPreparePageData,
  208. onLoad(query: { eventId?: string }) {
  209. const eventId = query && query.eventId ? decodeURIComponent(query.eventId) : ''
  210. if (!eventId) {
  211. this.setData({
  212. statusText: '缺少 eventId',
  213. })
  214. return
  215. }
  216. this.setData({ eventId })
  217. this.ensurePrepareHeartRateController()
  218. this.refreshPreparationDeviceState()
  219. this.loadEventPlay(eventId)
  220. },
  221. onShow() {
  222. this.refreshPreparationDeviceState()
  223. },
  224. onUnload() {
  225. if (prepareHeartRateController) {
  226. prepareHeartRateController.destroy()
  227. prepareHeartRateController = null
  228. }
  229. },
  230. async loadEventPlay(eventId?: string) {
  231. const targetEventId = eventId || this.data.eventId
  232. const accessToken = getAccessToken()
  233. if (!accessToken) {
  234. wx.redirectTo({ url: '/pages/login/login' })
  235. return
  236. }
  237. this.setData({
  238. loading: true,
  239. statusText: '正在加载局前准备信息',
  240. })
  241. try {
  242. const result = await getEventPlay({
  243. baseUrl: loadBackendBaseUrl(),
  244. eventId: targetEventId,
  245. accessToken,
  246. })
  247. this.applyEventPlay(result)
  248. } catch (error) {
  249. const message = error && (error as { message?: string }).message ? (error as { message: string }).message : '未知错误'
  250. this.setData({
  251. loading: false,
  252. statusText: `局前准备加载失败:${message}`,
  253. })
  254. }
  255. },
  256. applyEventPlay(result: BackendEventPlayResult) {
  257. const selectedVariantId = resolveSelectedVariantId(
  258. this.data.selectedVariantId,
  259. result.play.assignmentMode,
  260. result.play.courseVariants,
  261. )
  262. const assignmentMode = result.play.assignmentMode ? result.play.assignmentMode : null
  263. const logVariantId = assignmentMode === 'manual' && selectedVariantId ? selectedVariantId : null
  264. const selectableVariants = buildSelectableVariants(
  265. selectedVariantId,
  266. result.play.assignmentMode,
  267. result.play.courseVariants,
  268. )
  269. const selectedVariant = selectableVariants.find((item) => item.id === selectedVariantId) || null
  270. reportBackendClientLog({
  271. level: 'info',
  272. category: 'event-prepare',
  273. message: 'prepare play loaded',
  274. eventId: result.event.id || this.data.eventId || '',
  275. releaseId: result.resolvedRelease && result.resolvedRelease.releaseId
  276. ? result.resolvedRelease.releaseId
  277. : '',
  278. manifestUrl: result.resolvedRelease && result.resolvedRelease.manifestUrl
  279. ? result.resolvedRelease.manifestUrl
  280. : '',
  281. details: {
  282. pageEventId: this.data.eventId || '',
  283. resultEventId: result.event.id || '',
  284. selectedVariantId: logVariantId,
  285. assignmentMode,
  286. },
  287. })
  288. this.setData({
  289. loading: false,
  290. titleText: `${result.event.displayName} / 开始前准备`,
  291. summaryText: result.event.summary || '暂无活动简介',
  292. releaseText: result.resolvedRelease
  293. ? `${result.resolvedRelease.configLabel} / ${result.resolvedRelease.releaseId}`
  294. : '当前无可用 release',
  295. actionText: formatBackendPlayActionText(result.play.primaryAction, result.play.reason),
  296. statusText: formatBackendPlayStatusText(result.play.canLaunch, result.play.primaryAction, result.play.reason),
  297. assignmentMode: result.play.assignmentMode || '',
  298. variantModeText: formatAssignmentMode(result.play.assignmentMode),
  299. variantSummaryText: formatVariantSummary(result),
  300. presentationText: formatPresentationSummary(result),
  301. contentBundleText: formatContentBundleSummary(result),
  302. runtimePlaceText: '待 launch.runtime 确认',
  303. runtimeMapText: '待 launch.runtime 确认',
  304. runtimeVariantText: selectedVariant
  305. ? selectedVariant.name
  306. : (result.play.courseVariants && result.play.courseVariants[0]
  307. ? result.play.courseVariants[0].name
  308. : '待 launch 确认'),
  309. runtimeRouteCodeText: selectedVariant
  310. ? selectedVariant.routeCodeText
  311. : (result.play.courseVariants && result.play.courseVariants[0] && result.play.courseVariants[0].routeCode
  312. ? result.play.courseVariants[0].routeCode || '待 launch 确认'
  313. : '待 launch 确认'),
  314. selectedVariantId,
  315. selectedVariantText: selectedVariant
  316. ? `${selectedVariant.name} / ${selectedVariant.routeCodeText}`
  317. : '当前无需手动指定赛道',
  318. selectableVariants,
  319. })
  320. },
  321. refreshPreparationDeviceState() {
  322. this.refreshLocationPermissionStatus()
  323. this.refreshHeartRatePreparationStatus()
  324. this.refreshMockSourcePreparationStatus()
  325. },
  326. ensurePrepareHeartRateController() {
  327. if (prepareHeartRateController) {
  328. return prepareHeartRateController
  329. }
  330. prepareHeartRateController = new HeartRateController({
  331. onHeartRate: () => {},
  332. onStatus: (message) => {
  333. this.setData({
  334. heartRateStatusText: message,
  335. })
  336. },
  337. onError: (message) => {
  338. this.setData({
  339. heartRateStatusText: message,
  340. })
  341. },
  342. onConnectionChange: (connected, deviceName) => {
  343. this.setData({
  344. heartRateConnected: connected,
  345. heartRateDeviceText: connected ? (deviceName || '心率带') : (deviceName || '--'),
  346. })
  347. this.refreshHeartRatePreparationStatus()
  348. },
  349. onDeviceListChange: (devices) => {
  350. this.setData({
  351. heartRateScanText: devices.length ? `已发现 ${devices.length} 个设备` : '未扫描',
  352. heartRateDiscoveredDevices: devices.map((device) => ({
  353. deviceId: device.deviceId,
  354. name: device.name,
  355. rssiText: typeof device.rssi === 'number' ? `${device.rssi} dBm` : 'RSSI --',
  356. preferred: !!device.isPreferred,
  357. connected: !!prepareHeartRateController
  358. && !!prepareHeartRateController.currentDeviceId
  359. && prepareHeartRateController.currentDeviceId === device.deviceId
  360. && prepareHeartRateController.connected,
  361. })),
  362. })
  363. },
  364. })
  365. return prepareHeartRateController
  366. },
  367. refreshLocationPermissionStatus() {
  368. wx.getSetting({
  369. success: (result) => {
  370. const authSetting = result && result.authSetting
  371. ? result.authSetting as Record<string, boolean | undefined>
  372. : {}
  373. const hasForeground = authSetting['scope.userLocation'] === true
  374. const hasBackground = authSetting['scope.userLocationBackground'] === true
  375. let locationStatusText = '未请求定位权限'
  376. if (hasForeground && hasBackground) {
  377. locationStatusText = '已授权前后台定位'
  378. } else if (hasForeground) {
  379. locationStatusText = '已授权前台定位'
  380. } else if (authSetting['scope.userLocation'] === false) {
  381. locationStatusText = '定位权限被拒绝'
  382. }
  383. this.setData({
  384. locationStatusText,
  385. locationPermissionGranted: hasForeground,
  386. locationBackgroundPermissionGranted: hasBackground,
  387. })
  388. },
  389. fail: () => {
  390. this.setData({
  391. locationStatusText: '无法读取定位权限状态',
  392. locationPermissionGranted: false,
  393. locationBackgroundPermissionGranted: false,
  394. })
  395. },
  396. })
  397. },
  398. handleRequestLocationPermission() {
  399. wx.authorize({
  400. scope: 'scope.userLocation',
  401. success: () => {
  402. this.refreshLocationPermissionStatus()
  403. wx.showToast({
  404. title: '前台定位已授权',
  405. icon: 'none',
  406. })
  407. },
  408. fail: () => {
  409. this.refreshLocationPermissionStatus()
  410. wx.showToast({
  411. title: '请在设置中开启定位权限',
  412. icon: 'none',
  413. })
  414. },
  415. })
  416. },
  417. handleOpenLocationSettings() {
  418. wx.openSetting({
  419. success: () => {
  420. this.refreshLocationPermissionStatus()
  421. },
  422. fail: () => {
  423. wx.showToast({
  424. title: '无法打开设置面板',
  425. icon: 'none',
  426. })
  427. },
  428. })
  429. },
  430. refreshHeartRatePreparationStatus() {
  431. const controller = this.ensurePrepareHeartRateController()
  432. const preferredDeviceName = loadPreferredHeartRateDeviceName()
  433. this.setData({
  434. heartRateStatusText: controller.connected
  435. ? '局前心率带已连接'
  436. : preferredDeviceName
  437. ? `已记住首选设备:${preferredDeviceName}`
  438. : '未设置首选设备,可在此连接或进入地图后连接',
  439. heartRateDeviceText: controller.currentDeviceName || preferredDeviceName || '--',
  440. heartRateScanText: controller.scanning
  441. ? '扫描中'
  442. : (controller.discoveredDevices.length ? `已发现 ${controller.discoveredDevices.length} 个设备` : '未扫描'),
  443. heartRateConnected: controller.connected,
  444. heartRateDiscoveredDevices: controller.discoveredDevices.map((device) => ({
  445. deviceId: device.deviceId,
  446. name: device.name,
  447. rssiText: typeof device.rssi === 'number' ? `${device.rssi} dBm` : 'RSSI --',
  448. preferred: !!device.isPreferred,
  449. connected: !!controller.currentDeviceId && controller.currentDeviceId === device.deviceId && controller.connected,
  450. })),
  451. })
  452. },
  453. refreshMockSourcePreparationStatus() {
  454. const channelId = loadStoredMockChannelId()
  455. const autoConnect = loadMockAutoConnectEnabled()
  456. this.setData({
  457. mockSourceStatusText: autoConnect
  458. ? `自动连接已开启 / 通道 ${channelId}`
  459. : `自动连接未开启 / 通道 ${channelId}`,
  460. })
  461. },
  462. handleRefresh() {
  463. this.loadEventPlay()
  464. },
  465. handleBack() {
  466. wx.navigateBack()
  467. },
  468. handlePrepareHeartRateConnect() {
  469. const controller = this.ensurePrepareHeartRateController()
  470. controller.startScanAndConnect()
  471. this.refreshHeartRatePreparationStatus()
  472. },
  473. handleOpenHeartRateDevicePicker() {
  474. const controller = this.ensurePrepareHeartRateController()
  475. this.setData({
  476. showHeartRateDevicePicker: true,
  477. })
  478. if (!controller.scanning) {
  479. controller.startScanAndConnect()
  480. }
  481. this.refreshHeartRatePreparationStatus()
  482. },
  483. handleCloseHeartRateDevicePicker() {
  484. this.setData({
  485. showHeartRateDevicePicker: false,
  486. })
  487. },
  488. handlePrepareHeartRateDeviceConnect(event: WechatMiniprogram.BaseEvent<{ deviceId?: string }>) {
  489. const deviceId = event.currentTarget.dataset.deviceId
  490. if (!deviceId) {
  491. return
  492. }
  493. const controller = this.ensurePrepareHeartRateController()
  494. controller.connectToDiscoveredDevice(deviceId)
  495. this.setData({
  496. showHeartRateDevicePicker: false,
  497. })
  498. this.refreshHeartRatePreparationStatus()
  499. },
  500. handlePrepareHeartRateDisconnect() {
  501. if (!prepareHeartRateController) {
  502. return
  503. }
  504. prepareHeartRateController.disconnect()
  505. this.setData({
  506. heartRateConnected: false,
  507. })
  508. this.refreshHeartRatePreparationStatus()
  509. },
  510. handlePrepareHeartRateClearPreferred() {
  511. const controller = this.ensurePrepareHeartRateController()
  512. controller.clearPreferredDevice()
  513. this.refreshHeartRatePreparationStatus()
  514. },
  515. handleSelectVariant(event: WechatMiniprogram.BaseEvent<{ variantId?: string }>) {
  516. const variantId = event.currentTarget.dataset.variantId
  517. if (!variantId) {
  518. return
  519. }
  520. const selectableVariants = this.data.selectableVariants.map((item) => ({
  521. ...item,
  522. selected: item.id === variantId,
  523. }))
  524. const selectedVariant = selectableVariants.find((item) => item.id === variantId) || null
  525. this.setData({
  526. selectedVariantId: variantId,
  527. selectedVariantText: selectedVariant
  528. ? `${selectedVariant.name} / ${selectedVariant.routeCodeText}`
  529. : '当前无需手动指定赛道',
  530. runtimeVariantText: selectedVariant ? selectedVariant.name : '待 launch 确认',
  531. runtimeRouteCodeText: selectedVariant ? selectedVariant.routeCodeText : '待 launch 确认',
  532. selectableVariants,
  533. })
  534. },
  535. async handleLaunch() {
  536. const accessToken = getAccessToken()
  537. if (!accessToken) {
  538. wx.redirectTo({ url: '/pages/login/login' })
  539. return
  540. }
  541. if (!this.data.locationPermissionGranted) {
  542. this.setData({
  543. statusText: '进入地图前请先完成定位授权',
  544. })
  545. wx.showToast({
  546. title: '请先授权定位',
  547. icon: 'none',
  548. })
  549. return
  550. }
  551. this.setData({
  552. statusText: '正在创建 session 并进入地图',
  553. })
  554. try {
  555. const assignmentMode = this.data.assignmentMode ? this.data.assignmentMode : null
  556. const selectedVariantId = assignmentMode === 'manual' && this.data.selectedVariantId
  557. ? this.data.selectedVariantId
  558. : null
  559. reportBackendClientLog({
  560. level: 'info',
  561. category: 'event-prepare',
  562. message: 'launch requested',
  563. eventId: this.data.eventId || '',
  564. details: {
  565. pageEventId: this.data.eventId || '',
  566. selectedVariantId,
  567. assignmentMode,
  568. phase: 'launch-requested',
  569. },
  570. })
  571. const app = getApp<IAppOption>()
  572. if (app.globalData) {
  573. const pendingDeviceName = prepareHeartRateController && prepareHeartRateController.currentDeviceName
  574. ? prepareHeartRateController.currentDeviceName
  575. : loadPreferredHeartRateDeviceName()
  576. app.globalData.pendingHeartRateAutoConnect = {
  577. enabled: !!pendingDeviceName,
  578. deviceName: pendingDeviceName || null,
  579. }
  580. }
  581. if (prepareHeartRateController) {
  582. prepareHeartRateController.destroy()
  583. prepareHeartRateController = null
  584. }
  585. const result = await launchEvent({
  586. baseUrl: loadBackendBaseUrl(),
  587. eventId: this.data.eventId,
  588. accessToken,
  589. variantId: this.data.assignmentMode === 'manual' ? this.data.selectedVariantId : undefined,
  590. clientType: 'wechat',
  591. deviceKey: 'mini-dev-device-001',
  592. })
  593. reportBackendClientLog({
  594. level: 'info',
  595. category: 'event-prepare',
  596. message: 'launch response received',
  597. eventId: result.launch.business && result.launch.business.eventId ? result.launch.business.eventId : this.data.eventId || '',
  598. releaseId: result.launch.config && result.launch.config.releaseId ? result.launch.config.releaseId : '',
  599. sessionId: result.launch.business && result.launch.business.sessionId ? result.launch.business.sessionId : '',
  600. manifestUrl: result.launch.resolvedRelease && result.launch.resolvedRelease.manifestUrl
  601. ? result.launch.resolvedRelease.manifestUrl
  602. : '',
  603. details: {
  604. pageEventId: this.data.eventId || '',
  605. launchEventId: result.launch.business && result.launch.business.eventId ? result.launch.business.eventId : '',
  606. launchSessionId: result.launch.business && result.launch.business.sessionId ? result.launch.business.sessionId : '',
  607. configUrl: result.launch.config && result.launch.config.configUrl ? result.launch.config.configUrl : '',
  608. releaseId: result.launch.config && result.launch.config.releaseId ? result.launch.config.releaseId : '',
  609. resolvedReleaseId: result.launch.resolvedRelease && result.launch.resolvedRelease.releaseId
  610. ? result.launch.resolvedRelease.releaseId
  611. : '',
  612. resolvedManifestUrl: result.launch.resolvedRelease && result.launch.resolvedRelease.manifestUrl
  613. ? result.launch.resolvedRelease.manifestUrl
  614. : '',
  615. launchVariantId: result.launch.variant && result.launch.variant.id ? result.launch.variant.id : null,
  616. phase: 'launch-response',
  617. },
  618. })
  619. const envelope = adaptBackendLaunchResultToEnvelope(result)
  620. wx.navigateTo({
  621. url: prepareMapPageUrlForLaunch(envelope),
  622. })
  623. } catch (error) {
  624. const message = error && (error as { message?: string }).message ? (error as { message: string }).message : '未知错误'
  625. this.setData({
  626. statusText: `launch 失败:${message}`,
  627. })
  628. }
  629. },
  630. })