hyapp-server/docs/ExploreTab-Flutter-App对接文档.md
2026-05-26 01:02:12 +08:00

269 lines
5.9 KiB
Markdown
Raw Permalink 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.

# Explore tab Flutter App 对接文档
本文档定义 App 端 Explore 页 H5 tab 的读取接口。后台在 `HYApp 管理平台 / APP配置 / Explore配置` 维护 tab 名称和 H5 链接App 只读取已启用配置并按服务端返回顺序展示。
## 基础约定
本地网关地址:
```text
http://127.0.0.1:13000
```
所有 `/api/v1` 业务接口使用统一响应外壳:
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_xxx",
"data": {}
}
```
客户端成功判断:
```text
HTTP 200 && code == "OK"
```
失败时保留 `request_id`,用于后端排查日志。
## App 标识 Header
Explore tab 按 App 隔离,接口没有 query/body 形式的 `app_code` 参数。gateway 通过 Header 解析 App
| Header | 必填 | 说明 |
| --- | --- | --- |
| `X-App-Code` | 推荐 | 内部 App code例如 `lalu`。 |
| `X-HY-App-Code` | 否 | `X-App-Code` 的兼容别名。 |
| `X-App-Package` | 推荐二选一 | App 包名,例如 `com.org.laluparty`。传包名时 gateway 可解析到内部 `app_code`。 |
| `X-App-Platform` | 包名解析时推荐 | 平台,例如 `android``ios`。 |
当前开发默认 `app_code``lalu`。Flutter 仍建议显式传 `X-App-Code``X-App-Package`,避免多 App 环境串配置。
## 获取 Explore tab 列表
接口地址:
```http
GET /api/v1/app/explore-tabs
```
完整本地地址:
```text
http://127.0.0.1:13000/api/v1/app/explore-tabs
```
鉴权:
```text
不需要 Bearer token
```
服务端行为:
- 只返回当前 App 已启用的 Explore tabs。
- 后台关闭的 tab 不下发给 App。
- 返回顺序为后台 `sort_order` 升序,同排序时按较新的记录优先。
### 请求 Header
```http
X-App-Code: lalu
```
或:
```http
X-App-Package: com.org.laluparty
X-App-Platform: android
```
### Query 参数
无。
### Request Body
无。
### curl 示例
```bash
curl -X GET 'http://127.0.0.1:13000/api/v1/app/explore-tabs' \
-H 'X-App-Code: lalu'
```
包名解析方式:
```bash
curl -X GET 'http://127.0.0.1:13000/api/v1/app/explore-tabs' \
-H 'X-App-Package: com.org.laluparty' \
-H 'X-App-Platform: android'
```
### 成功响应
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_xxx",
"data": {
"items": [
{
"id": 7,
"app_code": "lalu",
"tab": "Farm",
"h5_url": "https://www.baidu.com",
"enabled": true,
"sort_order": 1,
"updated_at_ms": 1700000000000
}
],
"total": 1
}
}
```
### `data` 字段
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `items` | array | Explore tab 列表。 |
| `total` | int | 本次返回数量。 |
### `items[]` 字段
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `id` | int | 后台配置记录 ID只用于排查或本地缓存 key不作为业务路由。 |
| `app_code` | string | tab 所属 App例如 `lalu`。 |
| `tab` | string | App 展示的 tab 文案。 |
| `h5_url` | string | 点击 tab 后加载的 H5 地址。 |
| `enabled` | bool | 是否启用。App 接口只返回 `true` 的记录。 |
| `sort_order` | int | 展示排序,数值越小越靠前。 |
| `updated_at_ms` | int | 更新时间Unix epoch milliseconds。 |
### 空列表响应
当后台没有启用的 Explore tab 时,返回空数组,不是错误:
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_xxx",
"data": {
"items": [],
"total": 0
}
}
```
### 错误响应
配置读取异常时:
```json
{
"code": "UPSTREAM_ERROR",
"message": "upstream service error",
"request_id": "req_xxx"
}
```
客户端处理建议:
- `UPSTREAM_ERROR`:展示空态或保留上一次本地缓存。
-`OK`:不要使用 `data`
- 记录 `request_id` 到客户端日志,便于问题排查。
## Flutter 数据模型
```dart
class ExploreTabList {
final List<ExploreTabItem> items;
final int total;
ExploreTabList({
required this.items,
required this.total,
});
factory ExploreTabList.fromJson(Map<String, dynamic> json) {
final rawItems = json['items'] as List<dynamic>? ?? const [];
return ExploreTabList(
items: rawItems
.map((e) => ExploreTabItem.fromJson(e as Map<String, dynamic>))
.toList(),
total: json['total'] as int? ?? rawItems.length,
);
}
}
class ExploreTabItem {
final int id;
final String appCode;
final String tab;
final String h5Url;
final bool enabled;
final int sortOrder;
final int updatedAtMs;
ExploreTabItem({
required this.id,
required this.appCode,
required this.tab,
required this.h5Url,
required this.enabled,
required this.sortOrder,
required this.updatedAtMs,
});
factory ExploreTabItem.fromJson(Map<String, dynamic> json) {
return ExploreTabItem(
id: json['id'] as int? ?? 0,
appCode: json['app_code'] as String? ?? '',
tab: json['tab'] as String? ?? '',
h5Url: json['h5_url'] as String? ?? '',
enabled: json['enabled'] as bool? ?? false,
sortOrder: json['sort_order'] as int? ?? 0,
updatedAtMs: json['updated_at_ms'] as int? ?? 0,
);
}
}
```
## Flutter Dio 示例
```dart
Future<ExploreTabList> fetchExploreTabs(Dio dio) async {
final response = await dio.get(
'/api/v1/app/explore-tabs',
options: Options(
headers: {
'X-App-Code': 'lalu',
},
),
);
final body = response.data as Map<String, dynamic>;
if (body['code'] != 'OK') {
throw Exception('${body['code']}: ${body['message']} request_id=${body['request_id']}');
}
return ExploreTabList.fromJson(body['data'] as Map<String, dynamic>);
}
```
## 展示和跳转规则
- App 按 `items` 返回顺序展示,不需要二次排序。
- `tab` 为空或 `h5_url` 为空时,建议 App 跳过该项,避免展示不可点击入口。
- 点击 tab 后打开 `h5_url`,由 H5 页面自身处理内部导航。
- 如果接口失败App 可以使用本地缓存的上一版 tabs没有缓存时展示空态。