382 lines
6.0 KiB
Markdown
382 lines
6.0 KiB
Markdown
# 原生与 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 条做稳,就足够支撑第一波客户定制需求。
|