# 线上业务接入边界方案 ## 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. 一句话总结 线上系统负责“把用户送进正确的一局游戏”,配置系统负责“定义这局游戏是什么”。