hyapp-h5/docs/Flutter-H5-排行榜对接文档.md
2026-05-25 22:45:15 +08:00

268 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Flutter H5 排行榜对接文档
本文说明 Flutter App 如何打开 H5 排行榜、向 H5 传递登录态,以及接收 H5 通过 JSBridge 发回的导航事件。
## 入口地址
本地开发:
```text
http://127.0.0.1:30000/rank/index.html?env=local&token={access_token}&app_code=lalu
```
测试环境:
```text
https://{h5-test-domain}/rank/index.html?env=test&token={access_token}&app_code=lalu
```
线上环境:
```text
https://{h5-domain}/rank/index.html?token={access_token}&app_code=lalu
```
`env` 规则:
| 参数 | 说明 |
| --- | --- |
| 不传 | H5 请求 `https://api.global-interaction.com/` |
| `env=test` | H5 请求 `https://api-test.global-interaction.com/` |
| `env=local` | H5 请求 `http://localhost:13000/`,用于本机 gateway 联调 |
## URL 参数
| 参数 | 必填 | 说明 |
| --- | --- | --- |
| `token` | 是 | App 登录后拿到的 access token。H5 会作为 `Authorization: Bearer <token>` 调后端。 |
| `access_token` | 否 | `token` 的兼容别名。 |
| `accessToken` | 否 | `token` 的兼容别名。 |
| `app_code` | 是 | App 编码,当前传 `lalu`。H5 会作为 `X-App-Code` 请求头。 |
| `appCode` | 否 | `app_code` 的兼容别名。 |
| `env` | 否 | API 环境切换,见上表。 |
H5 启动后会调用:
```http
GET /api/v1/users/me/overview
Authorization: Bearer <token>
X-App-Code: lalu
```
这个接口用于确认登录态并缓存当前用户信息。排行榜数据使用同一个 token 和 app code 请求。
## H5 页面能力
排行榜页面地址:
```text
/rank/index.html
```
页面内包含三个榜单,通过 tab 切换:
| H5 Tab | 后端 `board_type` | 说明 |
| --- | --- | --- |
| `Wealth` | `sent` | 财富榜,按用户送礼总金币排行 |
| `Room` | `room` | 房间榜,按房间收礼总金币排行 |
| `Charm` | `received` | 魅力榜,按用户收礼总金币排行 |
时间维度:
| H5 Tab | 后端 `period` |
| --- | --- |
| `Daily` | `today` |
| `Weekly` | `week` |
| `Monthly` | `month` |
排行榜接口:
```http
GET /api/v1/activities/user-leaderboards?board_type=sent&period=today&page=1&page_size=20
Authorization: Bearer <token>
X-App-Code: lalu
```
H5 会分页加载,`page_size=20`。如果无 token 或接口失败,页面会展示 Figma 默认占位数据,不阻塞页面打开。
## Flutter JSBridge
H5 会按以下优先级尝试发送消息:
1. `window.flutter_inappwebview.callHandler("HyAppBridge", action, payload)`
2. `window.HyAppBridgeChannel.postMessage(jsonString)`
3. `window.FlutterBridge.postMessage(jsonString)`
4. `window.webkit.messageHandlers.HyAppBridge.postMessage({ action, payload })`
Flutter 只需要实现其中一种即可。推荐统一注册 `HyAppBridge`
### 消息格式
`flutter_inappwebview.callHandler`postMessage 的消息是 JSON 字符串:
```json
{
"action": "openUser",
"payload": {
"user_id": "10001"
}
}
```
### H5 发出的 action
| action | payload | 触发场景 | Flutter 行为 |
| --- | --- | --- | --- |
| `ready` | `{ "page": "rank" }` | H5 初始化完成 | 可关闭加载态 |
| `back` | `{}` | 用户点击左上角返回 | 关闭 WebView 或返回上一页 |
| `openUser` | `{ "user_id": "10001" }` | 点击财富榜/魅力榜用户头像、榜单项 | 打开用户详情页 |
| `openRoom` | `{ "room_id": "room_id" }` | 点击房间榜房间头像、榜单项 | 进入或打开房间详情 |
| `openRankNote` | `{ "board_type": "sent" }` | 点击右上角问号 | 当前 H5 已内置说明页Flutter 可只打点,不需要跳转 |
`board_type` 取值:
| 值 | 说明 |
| --- | --- |
| `sent` | 财富榜 |
| `room` | 房间榜 |
| `received` | 魅力榜 |
## Flutter 示例
### flutter_inappwebview
```dart
final url = Uri.parse(
'http://127.0.0.1:30000/rank/index.html'
'?env=local'
'&token=${Uri.encodeComponent(accessToken)}'
'&app_code=lalu',
);
InAppWebView(
initialUrlRequest: URLRequest(url: WebUri(url.toString())),
onWebViewCreated: (controller) {
controller.addJavaScriptHandler(
handlerName: 'HyAppBridge',
callback: (args) async {
final action = args.isNotEmpty ? args[0] as String : '';
final payload = args.length > 1 && args[1] is Map
? Map<String, dynamic>.from(args[1] as Map)
: <String, dynamic>{};
switch (action) {
case 'ready':
// 关闭 loading
break;
case 'back':
Navigator.of(context).maybePop();
break;
case 'openUser':
final userId = payload['user_id']?.toString() ?? '';
if (userId.isNotEmpty) {
// TODO: 打开用户详情页
}
break;
case 'openRoom':
final roomId = payload['room_id']?.toString() ?? '';
if (roomId.isNotEmpty) {
// TODO: 打开房间或房间详情页
}
break;
case 'openRankNote':
// H5 内部已展示说明页,这里可做埋点
break;
}
return {'ok': true};
},
);
},
)
```
### webview_flutter JavaScriptChannel
如果使用 `webview_flutter`,可以注入 `HyAppBridgeChannel`
```dart
final controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..addJavaScriptChannel(
'HyAppBridgeChannel',
onMessageReceived: (message) {
final data = jsonDecode(message.message) as Map<String, dynamic>;
final action = data['action']?.toString() ?? '';
final payload = data['payload'] is Map
? Map<String, dynamic>.from(data['payload'] as Map)
: <String, dynamic>{};
switch (action) {
case 'ready':
break;
case 'back':
Navigator.of(context).maybePop();
break;
case 'openUser':
final userId = payload['user_id']?.toString() ?? '';
break;
case 'openRoom':
final roomId = payload['room_id']?.toString() ?? '';
break;
}
},
)
..loadRequest(Uri.parse(rankUrl));
```
## 后端依赖
H5 依赖以下 Gateway HTTP 接口:
| 接口 | 说明 |
| --- | --- |
| `GET /api/v1/users/me/overview` | 解析 token 后获取当前用户信息 |
| `GET /api/v1/activities/user-leaderboards` | 获取排行榜 |
业务接口返回 envelope
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_xxx",
"data": {}
}
```
H5 也兼容旧的 `code: 0` 成功格式。
## 本地联调
`hyapp-h5` 目录运行:
```bash
make run
```
默认打开:
```text
http://127.0.0.1:30000/rank/index.html?env=local
```
Flutter 真机调本机服务时,`env=local` 对 H5 来说会请求 `http://localhost:13000/`。如果 WebView 运行在真机上,`localhost` 指向手机自身,不是开发机。真机联调建议:
1. 使用测试环境 `env=test`
2. 或把 H5/API 部署到同一局域网可访问地址。
3. 或后续给 H5 增加可配置 `api_base` 参数。
## 验收点
1. Flutter 打开 H5 时带上 `token``app_code`
2. H5 首次加载后发出 `ready`
3. 点击左上角返回Flutter 收到 `back` 并关闭 WebView。
4. 点击财富榜/魅力榜用户Flutter 收到 `openUser`
5. 点击房间榜房间Flutter 收到 `openRoom`
6. 切换 `Wealth / Room / Charm``Daily / Weekly / Monthly`H5 正确请求排行榜接口。
7. 点击右上角问号H5 展示说明页,返回后仍停留在排行榜页。