# 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 items; final int total; ExploreTabList({ required this.items, required this.total, }); factory ExploreTabList.fromJson(Map json) { final rawItems = json['items'] as List? ?? const []; return ExploreTabList( items: rawItems .map((e) => ExploreTabItem.fromJson(e as Map)) .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 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 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; if (body['code'] != 'OK') { throw Exception('${body['code']}: ${body['message']} request_id=${body['request_id']}'); } return ExploreTabList.fromJson(body['data'] as Map); } ``` ## 展示和跳转规则 - App 按 `items` 返回顺序展示,不需要二次排序。 - `tab` 为空或 `h5_url` 为空时,建议 App 跳过该项,避免展示不可点击入口。 - 点击 tab 后打开 `h5_url`,由 H5 页面自身处理内部导航。 - 如果接口失败,App 可以使用本地缓存的上一版 tabs;没有缓存时展示空态。