# 核心流程
> 文档版本：v1.3
> 最后更新：2026-04-03 18:16:19


## 1. 总流程

```mermaid
flowchart LR
  A["Entry Resolve"] --> B["Auth"]
  B --> C["Home / Cards"]
  C --> D["Event Play"]
  D --> E["Resolve Release"]
  E --> F["Launch Session"]
  F --> G["Client Load Manifest"]
  G --> H["Session Start / Finish"]
  H --> I["Result / History"]
```

补充说明：

- 这条主流程既服务当前小程序，也要服务未来 APP
- 终端差异主要体现在登录方式、设备能力和运行时 UI，不应拆成两套业务流程

## 2. 入口解析

入口层先解决：

- 用户从哪个渠道进来
- 当前归属哪个 `tenant`
- 当前品牌壳和首页卡片应该加载什么

当前对应接口：

- `GET /entry/resolve`
- `GET /home`
- `GET /cards`
- `GET /me/entry-home`

## 3. 登录流程

### 3.1 APP

APP 当前主链是手机号验证码：

1. `POST /auth/sms/send`
2. `POST /auth/login/sms`
3. 返回 `access_token + refresh_token`

说明：

- APP 是未来更强接入端，后端设计必须预留身体资料、设备绑定、遥测摘要等扩展空间

### 3.2 微信小程序

微信小程序当前主链是：

1. 客户端 `wx.login`
2. `POST /auth/login/wechat-mini`
3. 后端换取 `openid`
4. 返回 `access_token + refresh_token`

开发环境也支持 `dev-` 前缀 code。

### 3.3 绑定与合并

当小程序用户后续绑定手机号时：

1. 先发 `bind_mobile` 场景验证码
2. `POST /auth/bind/mobile`
3. 如果手机号已属于别的用户，则合并到手机号主账号

当前策略是：

- 手机号账号优先
- 微信轻账号并入手机号账号

## 4. 首页流程

首页不是固定首页，而是“入口上下文首页”。

当前聚合接口：

- `GET /me/entry-home`

它会返回：

- 当前用户
- 当前 tenant
- 当前 channel
- 当前 cards
- 继续中的 session
- 最近一局 session

## 5. Event Play 流程

活动详情页或开始前准备页不应该只拿 `event`。

它还必须拿到：

- 当前是否可启动
- 当前会落到哪份 `release`
- 当前是否存在多赛道 `variant` 编排
- 是否有 ongoing session
- 当前推荐动作是什么

补充规则：

- `play.canLaunch` 不是“有 event 就能进”
- 它当前表示“当前发布 release 已完整可启动”
- 最小要求为：
  - 已发布 release 存在
  - manifest 存在
  - runtime 已绑定
  - presentation 已绑定
  - content bundle 已绑定

当前聚合接口：

- `GET /events/{eventPublicID}/play`

它会返回：

- `event`
- `release`
- `resolvedRelease`
- `currentPresentation`
- `currentContentBundle`
- `play.assignmentMode`
- `play.courseVariants[]`
- `play.canLaunch`
- `play.primaryAction`
- `play.launchSource`
- `play.ongoingSession`
- `play.recentSession`

当前多赛道第一阶段约束：

- `play.assignmentMode` 只先支持最小口径：
  - `manual`
  - `random`
  - `server-assigned`
- `play.courseVariants[]` 只先返回准备页必需字段：
  - `id`
  - `name`
  - `description`
  - `routeCode`
  - `selectable`

## 6. Launch 流程

### 6.1 当前原则

启动一局游戏时，不是“启动一个 event”。

而是：

> 基于 event 当前可启动的 release，创建一条固化 release 的 session。

### 6.2 当前接口

- `POST /events/{eventPublicID}/launch`

当前请求体支持：

- `releaseId`
- `variantId`
- `clientType`
- `deviceKey`

当前返回会带：

- `launch.source`
- `launch.resolvedRelease`
- `launch.variant`
- `launch.presentation`
- `launch.contentBundle`
- `launch.config`
- `launch.business.sessionId`
- `launch.business.sessionToken`

补充约束：

- `launch` 是统一业务启动入口，不应因为 APP / 小程序差异复制两套接口
- 终端差异通过 `clientType`、`deviceKey`、后续能力声明字段处理

### 6.3 客户端应如何使用

客户端进入游戏前，应以返回中的这几项为准：

- `launch.resolvedRelease.releaseId`
- `launch.resolvedRelease.manifestUrl`
- `launch.resolvedRelease.manifestChecksumSha256`
- `launch.variant.id`
- `launch.variant.assignmentMode`

活动运营域第二阶段第二刀新增建议消费摘要：

- `launch.presentation.presentationId`
- `launch.contentBundle.contentBundleId`

补充说明：

- 如果活动声明了多赛道 variant，`launch` 会返回本局最终绑定的 `variant`
- 前端可以发起选择，但最终绑定以后端 `launch` 返回为准
- 故障恢复不重新分配 variant

而不是再拿 `event` 自己去猜。

## 7. Session 流程

### 7.1 当前接口

- `GET /sessions/{sessionPublicID}`
- `POST /sessions/{sessionPublicID}/start`
- `POST /sessions/{sessionPublicID}/finish`
- `GET /me/sessions`

### 7.2 鉴权模型

查询接口：

- 用 `access_token`

局内动作接口：

- 用 `sessionToken`

这保证了业务登录态和一局游戏运行态是分开的。

### 7.3 当前状态语义

- `launched`：已创建一局，客户端尚未正式开始
- `running`：客户端已开始本局
- `finished`：正常完成
- `failed`：超时或规则失败
- `cancelled`：主动退出或放弃恢复

补充约束：

- `cancelled` 和 `failed` 都不再作为 ongoing session 返回
- “放弃恢复”当前正式收口为 `finish(cancelled)`
- 同一局旧 `sessionToken` 在 `finish(cancelled)` 场景允许继续使用
- 第一阶段若活动声明了多赛道，session 会固化：
  - `assignmentMode`
  - `variantId`
  - `variantName`
  - `routeCode`

### 7.4 幂等要求

- `start` 幂等：
  - `launched` -> `running`
  - 重复 `start` 不应报错
- `finish` 幂等：
  - 第一次进入终态后，重复 `finish` 直接返回当前结果
- 这个约束同时服务小程序故障恢复和未来 APP 重试补报

## 8. 结果流程

### 8.1 当前接口

- `GET /sessions/{sessionPublicID}/result`
- `GET /me/results`

### 8.2 当前 finish payload

`finish` 当前支持上传结果摘要：

- `finalDurationSec`
- `finalScore`
- `completedControls`
- `totalControls`
- `distanceMeters`
- `averageSpeedKmh`
- `maxHeartRateBpm`

### 8.3 结果页约束

结果页应该基于 session 结果查看，不应该回头去查当前 event 当前 release。

因为：

- 一个 event 未来可能发布新版本
- 历史结果必须追溯到当时真实跑过的那份 release
- 如果一场活动存在多个 variant，结果与历史摘要也必须能追溯本局 `variantId`

## 9. 当前最应该坚持的流程约束

业务主线应始终保持为：

`entry -> auth -> event play -> resolve release -> launch -> session -> result`

不要退回成：

`event -> launch -> game`

也不要走成：

`mini event -> mini launch -> mini game`

或：

`app event -> app launch -> app game`

业务接口必须保持统一，终端差异只进入上下文，不进入对象模型分叉。


