Files

171 lines
9.3 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.
# K2 客户端第二批:分类 + 观看记录 + 收藏列表 + 下载授权管理
> 依据:`docs/plans/INBOX.md` 2026-10-17 `[待处理] K2` 条目。
> 客户端基线:`ReelShort` `develop` / `00280ca`。
> 接口唯一权威:`FruitServer/docs/api-phase1.md` §0、§1.3、§1.5、§1.6。
> 本批只做 K2,完成后停下写回执,不连做 K3/K4。
## 一、范围与完成口径
### K2-A 分类
- 接 `GET /app/categories`,分类 id 全程按字符串处理。
- 发现页分类条改为服务端分类;保留现有分类条样式。
- 分类内容只从 `GET /app/discover/feed` 的 `sections[]` 中读取
`code == "category-{id}"` 的分区,不在客户端拼装分类请求。
- 分类在发现 feed 中没有对应分区时显示空态。
- 保留 `hot` / `new` 入口与既有样式;其内容同样只按发现 feed 中对应 `code`
分区读取,缺失时显示空态。
### K2-B 观看记录页
- 接 `GET /app/watch/recent?limit=20`、`DELETE /app/watch/progress/{dramaId}`、
`DELETE /app/watch/progress`。
- 最近记录先去重 `dramaId`,再按 id 补 `GET /app/dramas/{id}` 卡片数据。
- 详情补取并发上限 4,同页按 `dramaId` 只请求一次。
- 单条详情失败只影响该条:显示“加载失败”占位与单项重试,其余记录正常渲染。
- 独立“观看记录”页:封面、剧名、集号、进度、更新时间;点击详情并续看;
单条删除、清空均有二次确认;删除采用乐观更新,失败回滚并提示。
- 加载 / 空 / 失败可重试三态;未登录使用现有登录引导。
### K2-C 收藏列表页
- 复用 `GET /app/favorites`、`DELETE /app/favorites/{dramaId}`、
`POST /app/favorites/toggle`,不增加分页参数。
- 独立“我的收藏”页,复用卡片数据模型与列表卡片视觉;点击进详情。
- 取消收藏采用乐观更新,失败回滚并提示。
- 加载 / 空 / 失败可重试三态;未登录使用现有登录引导。
### K2-D 下载授权管理
- 接契约 §1.6 全部 8 个接口:
`GET /app/downloads/policy`、`PUT /app/downloads/settings`、
`POST /app/downloads`、`GET /app/downloads`、
`GET /app/downloads/{episodeId}/url`、`POST /app/downloads/{episodeId}/progress`、
`DELETE /app/downloads/{episodeId}`、`DELETE /app/downloads`。
- 详情页与播放页增加下载入口;后端拒绝时直接展示后端 `message`。
- 独立“我的下载”页:策略卡、授权列表、单条删除、清空、仅 Wi-Fi、最高清晰度。
- 授权状态与任务状态完全使用后端字符串,客户端不新增状态枚举、不自行判断付费/配额。
- 加载 / 空 / 失败可重试三态;未登录使用现有登录引导。
- 明确不做:文件分片落盘、断点续传、离线播放、`licenseKey` 本地校验。
`url` 与 `progress` 本批只提供并测试 API 方法,不驱动实际下载任务。
## 二、实施顺序与 TDD
1. 基线:从 `ReelShort/develop` 建 `feature/flutter-k2-library`。
2. K2-A:先写分类解析、`category-{id}` 映射、无分区空态和筛选状态测试,
确认红灯后实现;再跑该块门禁。
3. K2-B:先写控制器测试覆盖详情去重、并发上限 4、单条失败降级、
删除/清空乐观更新与回滚;确认红灯后实现 API、repository、controller、页面;
再跑门禁。
4. K2-C:先写独立收藏页取消收藏失败回滚、三态与登录引导测试,
确认红灯后实现页面与导航;再跑门禁。
5. K2-D:先写 DTO 字符串 ID/时间解析、策略/列表渲染、授权失败透传、
设置更新、删除/清空和未登录测试;确认红灯后实现;
`ApiClient` 仅补契约所需的 `PUT` 能力;再跑门禁。
6. 三份 ARB 一次补齐并生成 l10n;全量跑
`dart analyze lib test`、`flutter test`、`flutter build apk --debug`。
7. 门禁全绿后合并回 `develop` 并 push;不动 `main`。
8. 在 `PROGRESS.md` 与 INBOX K2 条目追加回执;真机验证交用户执行。
## 三、预计影响文件
### 数据层
- 修改:`lib/core/data/remote/api_client.dart`(补 `put`)
- 修改:`lib/core/data/models/watch_progress.dart`(补 `updatedAt`)
- 修改:`lib/core/data/remote/watch_api.dart`(补清空)
- 修改:`lib/core/data/repositories/watch_history_repository.dart`(补清空)
- 新增:`lib/core/data/models/content_category.dart`
- 新增:`lib/core/data/remote/category_api.dart`
- 新增:`lib/core/data/repositories/category_repository.dart`
- 新增:`lib/core/data/services/category_controller.dart`
- 新增:`lib/core/data/models/watch_record.dart`
- 新增:`lib/core/data/services/watch_records_controller.dart`
- 新增:`lib/core/data/models/download_policy.dart`
- 新增:`lib/core/data/models/download_license.dart`
- 新增:`lib/core/data/remote/download_api.dart`
- 新增:`lib/core/data/repositories/download_repository.dart`
- 新增:`lib/core/data/services/download_controller.dart`
### 页面与共享 UI
- 修改:`lib/app.dart`(新仓库/控制器注入)
- 修改:`lib/core/widgets/auth_gate.dart`(页面级登录引导复用)
- 修改:`lib/pages/discover/discover_logic.dart`
- 修改:`lib/pages/discover/discover_view.dart`
- 修改:`lib/pages/home/home_view.dart`(我的页入口接线)
- 修改:`lib/pages/mine/mine_view.dart`(观看记录、收藏、下载入口)
- 修改:`lib/pages/drama_detail/drama_detail_view.dart`(下载入口)
- 修改:`lib/pages/player/player_view.dart`(当前集下载入口)
- 新增:`lib/pages/watch_records/watch_records_binding.dart`
- 新增:`lib/pages/watch_records/watch_records_view.dart`
- 新增:`lib/pages/favorites/favorites_binding.dart`
- 新增:`lib/pages/favorites/favorites_view.dart`
- 新增:`lib/pages/downloads/downloads_binding.dart`
- 新增:`lib/pages/downloads/downloads_view.dart`
- 视复用需要新增列表卡片/状态组件,预计位于 `lib/core/widgets/` 或各页面 `widgets/`。
### 文案
- 修改:`lib/l10n/app_en.arb`
- 修改:`lib/l10n/app_zh.arb`
- 修改:`lib/l10n/app_id.arb`
- 重新生成:`lib/l10n/app_localizations*.dart`
### 测试
- 修改:`test/catalog/catalog_controller_test.dart` 及分类筛选相关测试
- 修改:`test/discover/discover_logic_test.dart`
- 修改:`test/mine/mine_logic_test.dart`
- 修改:`test/library/favorite_controller_test.dart`
- 新增:分类 API/控制器与 section 映射测试
- 新增:观看记录 API、并发加载、单项降级、删除/清空回滚和页面三态测试
- 新增:收藏独立页、取消收藏回滚、三态和登录引导测试
- 新增:下载 DTO/API/controller/page 测试
- 新增:未登录 `AuthGate` 页面测试
## 四、契约缺口(本批登记,不改契约/后端)
1. `GET /app/watch/recent` 未返回剧名与封面,客户端只能先取最近记录,
再对去重后的 `dramaId` 调详情补齐卡片;代价是 N+1 请求。
建议后续在 `ProgressRespVO` 补卡片字段(需 specs 负责人改契约)。
2. `GET /app/favorites` 没有分页参数并返回全量数组,与
`TC-RECORD-04` 的“分页正确”不一致。本批按全量列表实现,
不自造 `page/size`。
3. `api-phase1.md` 没有搜索接口,本批不做搜索,也不以本地模糊匹配冒充搜索接口。
## 五、风险与控制
1. 记录页 N+1 详情请求可能放大流量和延迟:严格去重、并发上限 4、页内缓存,
单项失败降级,不在失败后自动无限重试。
2. 页面销毁时异步详情仍可能返回:controller 的 `notify` 做 disposed 保护,
不把迟到结果写回已退出页面。
3. `updatedAt` 为后端 `LocalDateTime` 字符串,解析失败只能显示占位,
不能让整条记录解析失败。
4. 下载契约里授权与任务状态均为字符串,客户端只原样展示/映射现有文案,
不根据未知值猜测业务状态。
5. 下载地址字段 `playUrl` 后端当前未填充,本批不做实际下载;不能据此宣称
“可离线播放”。
6. 本机可能缺少 Android SDK,`flutter build apk --debug` 若失败将保留原始输出,
回执标记“未验证”,不静默跳过。
7. 真机/模拟器可达性由用户验证;开发机 `curl` 不作为设备证据。
## 六、待确认(不阻塞本批)
1. 发现页 `hot` / `new` 入口保留,但当前后端 `discoverFeed()` 只生成
`category-{id}` 分区。若后续要展示热门/最新内容,需要后端在发现 feed 下发
对应分区;在此之前按契约显示空态。
2. 收藏分页与观看记录卡片字段待契约负责人后续补足;本批按 INBOX 降级方案执行。
3. 下载真实落盘、断点续传与离线播放需要存储、权限、加密播放方案,另立批次。
## 七、完成定义
- 四块均有“红 → 绿”测试证据。
- `dart analyze lib test` 零告警,`flutter test` 全过。
- `flutter build apk --debug` 通过,或保留缺 SDK 的原始失败输出并标记未验证。
- 三语言 ARB 齐全且生成物已更新。
- 独立记录、收藏、下载页和分类条均接契约接口;未登录入口可完成登录引导。
- 不改 `main`、不改 specs、不改 FruitServer 后端、不做搜索。
- `PROGRESS.md` 与 INBOX K2 回执包含验证步骤、预期结果、提交号与原始门禁输出。