client_framework/docs/video_home_data_flow.md
2026-04-22 23:08:39 +08:00

135 lines
8.5 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.

# 视频首页数据获取流程(以首款换皮应用 FunyMee 为参考)
本文描述**框架侧**在典型换皮应用(首款为 **FunyMee**)中,从冷启动到「视频首页」可展示分类与模板列表的**数据链路与调用顺序**。FunyMee 宿主 UI 不在本仓库内;流程以 `client_proxy_framework` 中的 `FrameworkAuthService``ExtConfigRuntime``VideoHomeRuntime``ImageApi` 为准,与代码注释中引用的线网接口(如 `GET /v1/image/img2video/tasks`)一致。
---
## 1. 前提与配置
1. **`ClientBootstrap.initFromAsset`**:加载 `skin_config.json`,构建 `SkinConfig``ApiClient.init`使后续请求走统一代理、AES 与 V2 包装(见 `lib/src/bootstrap/client_bootstrap.dart`)。
2. **`FrameworkAuthService.init` + `start`**:宿主实现 `AuthServiceCallbacks`(设备 ID、签名等由框架编排登录与 `common_info`(见 `lib/src/services/auth_service.dart`)。
3. **`skin_config.json`** 中与首页相关的片段:
- **`extConfig`**`keys.showVideoMenu` 映射到线网布尔(示例中为 `go_run` / `need_wait` 等,见 `lib/src/config/skin_config.example.json``items``taskItemMapping` 决定 `common_info` 下发的运营位卡片如何解析为与任务列表同形的 `ExtConfigItem`
- **`videoHome`**`imagesTabLabel``imagesTabFirst` 控制「Images」虚拟 Tab 的文案及与接口分类的排序(见 `lib/src/config/skin_config.dart`)。
4. **`fieldMapping`**FunyMee 等换皮下线网字段与逻辑字段不一致时,响应体会先经映射再进入实体解析(注释示例见 `lib/src/services/image_api.dart`)。
---
## 2. 总览时序
```mermaid
sequenceDiagram
participant Host as 宿主 main / UI
participant Boot as ClientBootstrap
participant Auth as FrameworkAuthService
participant User as UserApi / ProxyClient
participant Ext as ExtConfigRuntime
participant VH as VideoHomeRuntime
participant Img as ImageApi
Host->>Boot: initFromAsset(skin_config)
Host->>Auth: init(callbacks); start()
Auth->>User: POST /v1/user/fast_login
User-->>Auth: userToken, userId
Auth->>User: GET /v1/user/common_info
User-->>Auth: extConfig 等
Auth->>Ext: applyCommonInfoSuccess
Auth->>VH: hydrateAfterCommonInfo (异步)
VH->>Img: GET /v1/image/img2video/categories
Img-->>VH: 分类列表
VH->>VH: 合并 Tabs接口分类 + ext items → Images
VH->>Img: GET /v1/image/img2video/tasks?categoryId=…
Img-->>VH: 当前分类模板列表
```
---
## 3. 分阶段说明
### 3.1 启动与快速登录
- 延迟与重试策略由 `FrameworkAuthService.start` 控制(默认启动延迟、登录重试等)。
- **`UserApi.fastLogin`**`POST /v1/user/fast_login`。线网内层 headers 保留 `pkg`,但**不注入** `User_token`(见 `UserApi` / `ProxyClient` 注释。Query `type` 未传时默认为 **`gg`**Google Play 归因)。`FrameworkAuthService` 强制 `type=gg``referer` 优先 Play Install Referrer取不到时用 `utm_source=google-play&utm_medium=organic`
- 成功后框架将返回的 `userToken` 写入 **`ApiClient.instance.setUserToken`**,此后代理请求自动附带 `pkg``User_token`(见 `ProxyClient` 行为与 `UserApi` 说明)。
### 3.2 归因上报(与首页数据并行准备)
- 在拉取 `common_info` 之前,若存在 Adjust / Play 等 referrer会依次调用 **`UserApi.referrer`**`POST /v1/user/referrer`)上报,不阻塞后续 `common_info` 的成功与否结论,但属于同一 `_reportReferrersAndLoadCommonInfo` 流程(见 `auth_service.dart`)。
### 3.3 通用信息与 extConfig
- **`UserApi.getCommonInfo`**`GET /v1/user/common_info`query 含 `app`iOS/Android 后端渠道,与 `skin_config.backend` 一致)、`pkg``userId``deviceId` 等(见 `user_api.dart`)。
- 成功时调用 **`ExtConfigRuntime.applyCommonInfoSuccess`**
-`extConfig` 与本地 `extConfig.defaults` 浅合并;
-`extConfigKeySchema` 解析为 **`ExtConfigData`**(含 `showVideoMenu``items` 等)(见 `ext_config_runtime.dart``ext_config_models.dart`)。
- **`ExtConfigRuntime.commonInfoSucceeded`**:宿主可用「登录完成且 `common_info` 成功」再展示主业务界面(见 `ext_config_runtime.dart` 注释建议)。
### 3.4 视频首页运行时水合hydrate
`common_info` **成功**后,`FrameworkAuthService` 会 **fire-and-forget** 调用 **`VideoHomeRuntime.hydrateAfterCommonInfo`**(不阻塞 `loginComplete` 的完成)(见 `auth_service.dart`)。
`hydrateAfterCommonInfo` 的进入条件与行为(见 `video_home_runtime.dart`
| 条件 | 行为 |
|------|------|
| `userId` 为空,或 `ExtConfigData.showVideoMenu != true` | **`VideoHomeRuntime.reset`**,不拉分类、无 images Tab |
| 否则 | 置 `snapshot.loading = true`,再请求分类列表 |
水合步骤概要:
1. **`ImageApi.getCategoryList`** → `GET /v1/image/img2video/categories`,得到服务端分类(`CategoryItem`,含 `id` / `name`)。
2.**`ExtConfigRuntime.data`** 读取 `items`,过滤 **`ExtConfigItem.isUsableOnHome`**,非空则存在 **Images** 虚拟 Tab文案为 `AppConfig.videoHomeImagesTabLabel`(来自 `skin_config.videoHome.imagesTabLabel`)。
3. **Tab 顺序**:由 `videoHomeImagesTabFirst` 决定是先 Images 还是先接口分类。
4. **默认选中 Tab**:默认选中**第一个服务端分类**(非 Images若仅有 Images Tab 则下标为 0并发水合与用户提前切换时有保护逻辑`video_home_runtime.dart` 内注释)。
5. 水合结束后调用 **`VideoHomeRuntime.ensureTabItems(selectedTabIndex)`**,为当前 Tab 拉取模板数据(见下节)。
若分类接口失败且没有任何 Tab 可构建,`snapshot.error` 会携带失败信息。
### 3.5 按 Tab 拉取模板列表
- **Images Tab**:数据直接来自 **`ExtConfigData.items`**(已在水合前解析),**不再**请求 `img2video/tasks`
- **服务端分类 Tab**:首次选中该 Tab 时,**`VideoHomeRuntime.ensureTabItems`** 调用 **`ImageApi.getImg2VideoTasks(categoryId: id)`** → `GET /v1/image/img2video/tasks?categoryId=...`(与 FunyMee 文档一致)。结果通过 `ExtConfigItem.fromTaskItem` 写入 **`VideoHomeSnapshot.networkItemsByCategoryId`**,并按分类 id 缓存,避免重复请求。
---
## 4. 宿主 UI 对接要点
- **监听状态**`VideoHomeRuntime.snapshot``VideoHomeRuntime.selectedTabIndex``ValueNotifier`,可用 `ValueListenableBuilder` 或类似方式驱动顶栏 Tab 与内容区。
- **切换 Tab**:切换 `selectedTabIndex` 后应调用 **`VideoHomeRuntime.ensureTabItems(newIndex)`**(框架水合末尾已对初始 Tab 调用一次),以按需加载该分类下的模板列表。
- **登录 / common_info 失败**`ExtConfigRuntime.commonInfoSucceeded == false``userId` 为空时,不会进入视频首页水合;宿主应展示错误态或重试入口。
---
## 5. 相关 HTTP 接口汇总
| 顺序 | 方法 | 路径 | 用途 |
|------|------|------|------|
| 1 | POST | `/v1/user/fast_login` | 设备登录,取得 token |
| 2 | POST | `/v1/user/referrer` | 可选,归因上报 |
| 3 | GET | `/v1/user/common_info` | 开关与 `extConfig`(含首页运营位 `items` |
| 4 | GET | `/v1/image/img2video/categories` | 视频首页顶栏服务端分类 |
| 5 | GET | `/v1/image/img2video/tasks` | 某分类下模板列表query`categoryId` |
所有经 `ProxyClient` 的业务请求均走 **`skin_config` 中的 `proxyPath`**body 为加密后的代理载荷;响应经解密与 **`fieldMapping`** 还原为逻辑字段后再解析实体。
---
## 6. 代码索引(便于跳转)
| 模块 | 路径 |
|------|------|
| 启动换皮配置 | `lib/src/bootstrap/client_bootstrap.dart` |
| 登录与 common_info 编排 | `lib/src/services/auth_service.dart` |
| 用户接口 | `lib/src/services/user_api.dart` |
| 图/视频分类与任务列表 | `lib/src/services/image_api.dart` |
| extConfig 运行时 | `lib/src/config/ext_config_runtime.dart` |
| 视频首页 Tab 与缓存 | `lib/src/config/video_home_runtime.dart` |
| extConfig / items 解析 | `lib/src/config/ext_config_models.dart` |
| JSON 配置示例 | `lib/src/config/skin_config.example.json` |
---
## 7. 与《创建新换皮应用》的关系
从零搭建宿主工程、资产路径与 `main` 初始化顺序,仍以 **[create_new_skin_app.md](create_new_skin_app.md)** 为准;本文仅补充 **「视频首页」在框架内的数据流**,与 FunyMee 首推路线一致。