# 原生与 H5 Bridge 协议草案 本文档定义当前项目中 **原生小程序** 与 **H5 定制内容页** 之间的基础通信协议。 目标: - 让 H5 能获取当前游戏上下文 - 让 H5 能请求原生能力 - 让原生能接收 H5 的结果回传 - 保持协议简单、稳定、可版本化 - 为后续拍照、录音、小游戏、结果页等扩展留出空间 --- ## 0. 当前适用前提 本规范当前属于: - 协议与实现预留 - 容器与回退机制先行 最近排查已经确认,当前最初使用的是**个人主体**小程序。 在这个前提下,`web-view` 能力本身可能受限。 因此: - Bridge 规范仍然应该先定义 - 容器页与回退机制也应该先实现 - 但在企业主体审核通过前,不应把 H5 接入是否成功完全归因于 bridge 代码本身 详细说明见: - [platform-capability-notes.md](D:/dev/cmr-mini/doc/platform-capability-notes.md) --- ## 1. 协议原则 ### 原则 1:Bridge 要版本化 建议先固定: - `content-v1` - `result-v1` 后续升级时: - 新增 `content-v2` - 新增 `result-v2` 不要直接改旧协议。 ### 原则 2:请求能力最小化 先只开放真正需要的能力,不要一开始做成“大而全总线”。 ### 原则 3:原生控制核心状态 Bridge 只能做: - 展示 - 上报 - 请求能力 不能让 H5 直接改比赛核心状态。 ### 原则 4:消息必须可回执 每个请求都应有明确成功/失败返回,不允许 H5 靠超时猜测。 --- ## 2. 通道模型 建议统一按“请求 / 响应 / 事件”三类消息组织: - `request` H5 请求原生能力 - `response` 原生返回能力执行结果 - `event` 原生主动推送状态变化 推荐消息外壳: ```json { "id": "req-001", "channel": "request", "type": "getGameContext", "payload": {} } ``` 响应: ```json { "id": "req-001", "channel": "response", "type": "getGameContext", "ok": true, "payload": {} } ``` --- ## 3. 原生注入给 H5 的基础上下文 建议至少包含: ```json { "bridgeVersion": "content-v1", "eventId": "sample-score-o-001", "mode": "score-o", "sessionId": "session-001", "sessionStatus": "running", "controlId": "control-3", "controlKind": "control", "title": "湖边步道", "body": "这里适合短暂停留观察周边地形。", "theme": "default-race" } ``` 对于结果页,可扩展为: ```json { "bridgeVersion": "result-v1", "eventId": "sample-score-o-001", "mode": "score-o", "sessionId": "session-001", "summary": { "title": "比赛结束", "heroValue": "120", "rows": [] } } ``` --- ## 4. H5 -> 原生:第一阶段推荐动作 建议第一阶段只支持这几个: ### `close` 作用: - 关闭当前 H5 页面 示例: ```json { "id": "req-001", "channel": "request", "type": "close", "payload": {} } ``` ### `getGameContext` 作用: - 让 H5 主动获取最新上下文 ### `takePhoto` 作用: - 请求原生拍照 ### `recordAudio` 作用: - 请求原生录音 ### `submitResult` 作用: - 把 H5 内的任务结果、表单或作品结果提交回原生 示例: ```json { "id": "req-002", "channel": "request", "type": "submitResult", "payload": { "taskId": "photo-task-1", "status": "completed", "assetId": "img-001" } } ``` --- ## 5. 建议第二阶段可扩展动作 等第一阶段跑稳后,再逐步加入: - `uploadImage` - `uploadAudio` - `getLocation` - `openMiniGame` - `submitForm` - `share` - `restartSession` 这些先不要第一阶段全开。 --- ## 6. 原生 -> H5:统一返回结构 建议统一返回: ```json { "id": "req-002", "channel": "response", "type": "takePhoto", "ok": true, "payload": { "assetId": "img-001", "url": "https://example.com/assets/img-001.jpg" } } ``` 失败时: ```json { "id": "req-002", "channel": "response", "type": "takePhoto", "ok": false, "error": { "code": "USER_CANCELLED", "message": "用户取消拍照" } } ``` --- ## 7. 原生 -> H5:推荐事件 原生可按需主动推送轻量事件: - `contextUpdated` - `sessionFinished` - `sessionExited` - `networkChanged` 但第一阶段要克制,避免高频推送。 不建议第一阶段主动高频推: - GPS 实时位置流 - 指北针实时角度 - HUD 高频数字 这些不适合让 H5 主导。 --- ## 8. 错误码建议 建议第一阶段统一几类错误: - `USER_CANCELLED` - `PERMISSION_DENIED` - `NETWORK_ERROR` - `UNSUPPORTED_ACTION` - `BRIDGE_NOT_READY` - `INTERNAL_ERROR` 这样 H5 侧更容易统一处理。 --- ## 9. 安全与边界 ### 9.1 H5 不直接改核心比赛状态 H5 不能直接决定: - 是否打点成功 - 是否跳点成功 - 是否比赛结束 ### 9.2 H5 只能请求能力 原生决定是否执行: - 拍照 - 录音 - 上传 - 页面关闭 ### 9.3 Bridge 能力按页面类型开放 例如: - 内容页开放 `takePhoto` - 结果页不一定开放 后续可做按 `bridgeVersion` 或 `pageType` 的能力白名单。 --- ## 10. 第一阶段推荐支持范围 建议第一阶段只正式支持: - `close` - `getGameContext` - `takePhoto` - `recordAudio` - `submitResult` 这样足够承接: - 文创详情 - 拍照任务 - 语音留言 - 结果页回传动作 --- ## 11. 不建议第一阶段支持的内容 先不要一上来开放: - 任意写比赛状态 - 任意切换玩法 - 任意修改地图行为 - 任意控制打点 - 高频实时 telemetry 推送 这些都属于核心状态,应该继续由原生掌控。 --- ## 12. 当前建议实施顺序 1. 先实现一个通用 H5 容器页 2. 先跑通 `content-v1` 3. 先支持 5 个最小动作 4. 再跑通一个简单结果页 5. 最后再扩桥接能力 --- ## 13. 当前建议结论 Bridge 的第一阶段目标,不是做成万能总线,而是: **稳定承接定制内容页与结果页的最小需求。** 先把: - 关闭 - 获取上下文 - 拍照 - 录音 - 结果提交 这 5 条做稳,就足够支撑第一波客户定制需求。