# 线上业务接入边界方案
> 文档版本：v1.0
> 最后更新：2026-04-02 08:28:05


## 1. 目的

本文档定义小程序接入线上业务 API 时的架构边界，确保以下原则始终成立：

- 游戏玩法仍然完全由配置驱动
- 线上 API 只负责业务编排，不负责定义或污染玩法
- 地图引擎和规则运行时可以继续独立于业务系统运行
- 本地 demo、离线配置、线上赛事三种入口可以共存

本文档适用于以下接入范围：

- 用户管理
- 登录与鉴权
- 首页卡片
- 赛事详情
- 地图详情
- Event 详情
- 报名
- launch 启动
- 后续 session 上报与查询

## 2. 核心结论

线上接入后，系统仍保持两层结构：

- 业务层：决定“用户是谁、能进什么、当前启动什么”
- 游戏层：决定“地图怎么画、规则怎么跑、控制点怎么判定、体验怎么表现”

两层之间只允许通过一个明确的启动模型通信，不允许业务 API 对游戏规则对象做直接写入。

一句话定义：

> API 负责发放启动上下文，配置负责定义游戏本身。

## 3. 分层原则

### 3.1 业务层职责

业务层负责：

- 登录与 token 管理
- 用户资料与身体数据
- 卡片、赛事、地图、Event 列表与详情
- 报名资格校验
- launch 启动资格与 session 凭证发放
- 后续成绩、轨迹、历史记录上传与查询

业务层可以决定：

- 是否允许用户启动
- 当前应启动哪个 Event
- 当前应加载哪份配置或 manifest
- 当前启动绑定的 `session_id`、`session_token`

业务层不可以决定：

- 控制点布局
- 游戏规则
- 打卡判定
- 跳点规则
- 引导、音效、表现策略
- 游戏内内容卡的结构和行为定义

### 3.2 游戏层职责

游戏层负责：

- 地图资源加载
- KML / course / 配置解析
- `GameDefinition` 构建
- 规则插件运行
- 传感器接入
- HUD、反馈、结果页、本地统计
- 游戏内 session 生命周期

游戏层只认配置和本地运行态，不认业务 API 对象。

游戏层不应该直接出现以下业务字段：

- `competition_id`
- `registration_status`
- `access_token`
- `refresh_token`
- `Authorization`
- `user_id`

业务字段可以存在于页面壳层或业务服务层，但不进入规则层。

## 4. 唯一允许的层间模型

业务层和游戏层之间，统一通过 `GameLaunchEnvelope` 通信。

建议结构如下：

```ts
interface GameLaunchEnvelope {
  config: {
    configUrl: string
    configLabel: string
    configChecksumSha256?: string | null
    releaseId?: string | null
    routeCode?: string | null
  }
  business: {
    source: 'demo' | 'competition' | 'direct-event' | 'custom'
    competitionId?: string | null
    eventId?: string | null
    launchRequestId?: string | null
    participantId?: string | null
    sessionId?: string | null
    sessionToken?: string | null
    sessionTokenExpiresAt?: string | null
    realtimeEndpoint?: string | null
    realtimeToken?: string | null
  } | null
}
```

解释：

- `config` 是游戏层真正消费的输入
- `business` 是业务壳保留的上下文
- 地图页可以同时拿到两者，但地图引擎只读取 `config`

## 5. 推荐启动链路

### 5.1 Demo 启动

适用于本地调试、离线测试、玩法验证。

流程：

1. 页面构建 demo `GameLaunchEnvelope`
2. `config.configUrl` 指向 demo 配置
3. `business.source = 'demo'`
4. 跳转地图页
5. 地图页加载配置并启动引擎

特点：

- 不依赖业务 API
- 不依赖登录
- 不依赖 session

### 5.2 线上赛事启动

适用于正式业务入口。

流程：

1. 业务页请求赛事 / Event 详情
2. 用户在业务页完成登录、资格校验、报名确认
3. 用户点击开始，业务页调用 `launch`
4. 后端返回 `session_id`、`session_token`、`release_id`、`manifest_url`、`route_code`
5. 业务层把上述信息转换为 `GameLaunchEnvelope`
6. 地图页只根据 `config` 载入配置
7. 业务壳层保存 `business` 上下文供后续上报使用

注意：

- `launch` 是业务启动，不等于规则层 `startSession`
- 规则层本地开始游戏，仍由引擎按配置驱动

### 5.3 线上直入地图启动

适用于地图详情或 Event 直入。

流程与赛事入口基本一致，区别仅在于：

- 入口页不同
- 资格校验更轻
- `business.source = 'direct-event'`

## 6. manifest 的角色

后端提供的 `manifest_url` 不应直接变成规则层对象。

推荐做法：

- 业务层或适配层下载 manifest
- 将 manifest 解析并映射到当前配置体系
- 输出为当前引擎已支持的配置入口

manifest 是“线上发布描述”，不是“规则运行对象”。

建议把 manifest 适配理解为一个编译过程：

- 输入：后端发布描述
- 输出：当前配置驱动引擎可识别的配置资源

## 7. 目录建议

建议按三层组织代码：

```text
miniprogram/
  services/
    http.ts
    client-api.ts
    auth.ts
  business/
    launch/
      launchBuilder.ts
      launchStore.ts
      manifestAdapter.ts
  utils/
    gameLaunch.ts
  pages/
    login/
    home/
    competition-detail/
    event-detail/
    map/
```

说明：

- `services` 只处理 API 通信
- `business/launch` 只做业务到配置的适配
- `utils/gameLaunch.ts` 定义启动模型和页面跳转协议
- `pages/map` 只做配置加载和游戏承载

## 8. 代码边界约束

### 8.1 允许进入地图页的内容

允许进入地图页：

- `GameLaunchEnvelope`
- `configUrl`
- `configLabel`
- `releaseId`
- `routeCode`
- `sessionToken`

但地图页内部还要继续区分：

- 引擎可读：`config`
- 业务壳可读：`business`

### 8.2 不允许进入引擎的内容

以下内容禁止进入 `MapEngine`、`GameRuntime`、`GameDefinition`：

- 用户信息
- 登录态 token
- 报名状态
- 业务接口返回原始对象
- 赛事详情原始 JSON
- Event 详情原始 JSON

### 8.3 上报也走旁路

后续若接 `punches`、`finish`、`session-uploads`，建议流程如下：

1. 游戏层产生本地事件
2. 页面壳层或业务 service 订阅这些事件
3. 由业务层决定是否调用 API

不要在规则层里直接 `wx.request`。

## 9. 当前项目的落地点

当前项目已具备以下基础：

- 地图页是配置驱动入口
- `remoteMapConfig.ts` 负责远程配置加载
- `MapEngine` 负责本地规则与表现运行
- 已新增 `utils/gameLaunch.ts` 作为启动边界模型

当前建议继续保持：

- `MapEngine` 只接 `RemoteMapConfig`
- 地图页只从 `GameLaunchEnvelope.config` 获取配置入口
- 业务上下文保留在地图页外层或页面壳层

## 10. 分阶段落地建议

### 阶段一：边界固化

目标：

- 地图页彻底改为只接 `GameLaunchEnvelope`
- demo 启动与线上启动走同一套入口协议

验收标准：

- 不再依赖页面内硬编码 URL 作为唯一启动方式
- 业务字段不进入引擎

### 阶段二：业务壳接入

目标：

- 接入登录、首页卡片、赛事详情、Event 详情、报名、launch

验收标准：

- 能从业务页成功进入地图页
- 地图仍由配置驱动启动

### 阶段三：manifest 适配

目标：

- 将后端 `manifest_url` 适配为当前配置体系可消费的输入

验收标准：

- 同一个 Event 的线上发布内容可稳定映射为游戏配置入口

### 阶段四：session 下游联通

目标：

- 补充上报、完成、结果、历史查询

验收标准：

- 业务链路打通
- 规则层仍不直接依赖业务 API

## 11. 必须长期坚持的规则

- 业务 API 不定义玩法
- 配置文件不承载用户态
- 引擎不依赖登录状态
- 引擎不依赖报名状态
- 业务页不直接修改 `GameDefinition`
- 规则层不直接请求业务 API

如果后续出现需求需要绕过这几条规则，应视为架构变更，不应当作普通功能迭代处理。

## 12. 一句话总结

线上系统负责“把用户送进正确的一局游戏”，配置系统负责“定义这局游戏是什么”。


