From 4a658fc67ecd1208de27599d27c966072a5e9083 Mon Sep 17 00:00:00 2001 From: Zhou Weidong <502333560@qq.com> Date: Sat, 17 Oct 2026 15:02:23 +0800 Subject: [PATCH] =?UTF-8?q?docs(plans):=20=E8=A1=A5=E9=BD=90=20K2=20?= =?UTF-8?q?=E7=9C=9F=E6=9C=BA=E9=AA=8C=E6=94=B6=E4=B8=8E=E9=97=A8=E7=A6=81?= =?UTF-8?q?=E5=9B=9E=E6=89=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/plans/INBOX.md | 3 +- docs/plans/PROGRESS.md | 449 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 451 insertions(+), 1 deletion(-) diff --git a/docs/plans/INBOX.md b/docs/plans/INBOX.md index ea73a05..ab3f77d 100644 --- a/docs/plans/INBOX.md +++ b/docs/plans/INBOX.md @@ -152,7 +152,8 @@ ### 七、Codex 回执(2026-10-17) **状态**:K2-A/B/C/D 代码、自动化测试、Android debug 构建与 develop 推送已完成; -真机/模拟器侧业务验证**未验证**,按红线交用户执行。K3/K4 未开始。 +真机/模拟器侧业务验证**未验证**,按红线交用户执行。用户照做清单与测试跳过口径见 +`docs/plans/PROGRESS.md`「动作 17:用户真机验收清单」。K3/K4 未开始。 **提交** diff --git a/docs/plans/PROGRESS.md b/docs/plans/PROGRESS.md index 793c2f0..f3f1529 100644 --- a/docs/plans/PROGRESS.md +++ b/docs/plans/PROGRESS.md @@ -871,3 +871,452 @@ Deleted branch feature/flutter-k2-library (was cc4d7c5). ``` 解读:功能代码和构建门禁已完成;设备网络、实际授权状态与 UI 交互需用户按回执步骤验证。 + +### 动作 17:用户真机验收清单 + +叙述:本动作只补齐验收证据与回执缺项,不新做功能、不改 ReelShort 产品代码。 +本轮重新核对 ReelShort `develop` 为 `45f5f25`,FruitServer 文档开工前为 `698bb78`; +并复跑 `dart analyze lib test`、`flutter test`、`flutter build apk --debug`。 +设备侧取证由用户执行,Codex 未执行。下面所有“预期”都是用户在手机上应看到的业务结果; +开发机 `curl` 只用于检查后端是否启动,不作为手机可达或设备侧验收证据。 + +#### 17.1 本轮门禁复跑原始输出 + +叙述:先确认 8081 监听状态,再按 ReelShort `develop` 顺序复跑三个客户端门禁。 +Flutter 首次在受限沙箱内运行时被拒绝写 SDK 缓存 +(`engine.stamp.tmp` / `engine.realm: Operation not permitted`), +随后在受控环境用同一组命令复跑;下面贴的是复跑成功的原始输出。 + +``` +$ lsof -nP -iTCP:8081 -sTCP:LISTEN +(无输出,说明本轮复跑时 8081 未监听) + +$ flutter devices +Found 3 connected devices: + iPhone (mobile) • 00008101-000875813E42001E • ios • iOS 26.6.1 23G83 + macOS (desktop) • macos • darwin-arm64 • macOS 15.5 24F74 darwin-arm64 + Chrome (web) • chrome • web-javascript • Google Chrome 155.0.8059.80 + +Checking for wireless devices... + +No wireless devices were found. + +Run "flutter emulators" to list and start any available device emulators. +``` + +``` +$ cd /Users/jianghaifeng/Desktop/ReelShort/ReelShort +$ dart analyze lib test +Analyzing lib, test... +No issues found! + +$ flutter test +test/integration/live_watch_flow_test.dart: should_complete_the_watch_flow_when_backend_is_running + 客户端接口服务未启动,已跳过联调测试 +test/integration/live_auth_test.dart: should_surface_business_error_when_sign_in_uses_unknown_email + 客户端接口服务未启动,已跳过联调测试 +test/integration/live_catalog_test.dart: should_parse_banner_and_sections_when_home_feed_is_fetched + 客户端接口服务未启动,已跳过联调测试 +test/integration/live_catalog_test.dart: should_order_episodes_by_number_and_attach_url_when_detail_is_fetched + 客户端接口服务未启动,已跳过联调测试 +test/integration/live_catalog_test.dart: should_return_play_url_when_episode_is_playable + 客户端接口服务未启动,已跳过联调测试 +00:10 +216 ~5: All tests passed! + +$ flutter build apk --debug +Running Gradle task 'assembleDebug'... 2,672ms +✓ Built build/app/outputs/flutter-apk/app-debug.apk +``` + +解读:静态检查零告警;8081 未启动时测试结果是 `+216 ~5`,5 条跳过均来自 live 联调用例的 +`markTestSkipped`,不是失败;Android debug APK 构建成功(缓存命中,`2,672ms`)。 +本轮没有设备侧业务证据,也没有用开发机请求替代手机取证。 + +#### 17.2 从零前置条件 + +以下命令都需要在 Mac 终端执行。第 1 步只在首次搭建环境时执行;已有数据库可跳过。 + +1. 启动 PostgreSQL;首次环境再建角色、数据库并初始化脚本。 + +``` +$ brew services start postgresql@18 +$ export PATH="/opt/homebrew/opt/postgresql@18/bin:$PATH" +$ psql -d postgres -c "CREATE ROLE fruitshorts LOGIN PASSWORD 'fruitshorts';" +$ psql -d postgres -c "CREATE DATABASE fruitshorts OWNER fruitshorts ENCODING 'UTF8';" +$ cd /Users/jianghaifeng/Desktop/ReelShort/FruitServer +$ psql -h 127.0.0.1 -U fruitshorts -d fruitshorts -f sql/schema.sql +$ psql -h 127.0.0.1 -U fruitshorts -d fruitshorts -f sql/data.sql +$ psql -h 127.0.0.1 -U fruitshorts -d fruitshorts -f sql/schema-phase1.sql +$ psql -h 127.0.0.1 -U fruitshorts -d fruitshorts -f sql/partition-maintenance.sql +$ psql -h 127.0.0.1 -U fruitshorts -d fruitshorts -f sql/data-phase1.sql +``` + +预期:五个 SQL 脚本按顺序执行完成,没有主键冲突或表不存在错误。 +重复执行是幂等的;不要颠倒 `partition-maintenance.sql` 与 `data-phase1.sql` 的顺序。 + +2. 构建并启动客户端接口服务 8081。 + +``` +$ cd /Users/jianghaifeng/Desktop/ReelShort/FruitServer +$ mvn -pl fruitshorts-app-api -am package -DskipTests +$ java -jar fruitshorts-app-api/target/fruitshorts-app-api-1.0.0-SNAPSHOT.jar +``` + +预期:启动日志出现 `Started AppApiApplication`;另开终端检查: + +``` +$ lsof -nP -iTCP:8081 -sTCP:LISTEN +java ... TCP *:8081 (LISTEN) +$ curl -fsS http://127.0.0.1:8081/healthz +{"code":0,...,"data":{"status":"UP","app":"fruitshorts-app-api",...}} +``` + +`curl` 只证明开发机上的 8081 已启动,不能证明手机可达。 + +3. 准备手机与局域网。 + +- 手机和 Mac 连接同一个 Wi-Fi;关闭会隔离客户端的访客网络/VPN 代理。 +- iPhone 首次启动 App 时,在系统弹窗中允许「本地网络」权限。 +- Mac 防火墙若拦截 Java,到「系统设置 → 网络 → 防火墙 → 选项」允许 `java` 传入连接。 +- 同网段地址用 `IP=$(ipconfig getifaddr en0)` 获取。若为空,先确认当前 Wi-Fi 网卡名, + 再改用对应接口(如 `en1`)。 + +预期:终端打印形如 `192.168.x.x` 的局域网地址;手机浏览器能访问 +`http://<该地址>:8081/healthz`,页面显示 `"status":"UP"`。 + +4. 获取 Flutter 运行设备号。 + +``` +$ cd /Users/jianghaifeng/Desktop/ReelShort/ReelShort +$ flutter devices +``` + +预期:真机以 `iPhone (mobile) • • ios` 形式出现。本轮机器识别到的设备号是 +`00008101-000875813E42001E`;换手机或重装后必须重新执行 `flutter devices`, +不能继续照抄旧 UDID。也可以执行 `xcrun xctrace list devices` 查 iPhone UDID。 + +这里的“运行设备号”用于 `flutter run -d`。下载绑定使用的设备号是另一个概念: +客户端首次运行会在本地生成并持久化,随后由 `ApiClient` 作为 `X-Device-Id` 发送, +用户不需要也不能在界面上手填。测试文件里的 `dio-check-device`、`k1-live-device` +只是测试固定值,不是生产 App 的设备号。 + +5. 用当前 Mac 的局域网 IP 注入接口地址并运行 App。 + +``` +$ cd /Users/jianghaifeng/Desktop/ReelShort +$ git checkout develop +$ git pull origin develop +$ git rev-parse HEAD +45f5f25640a274edb51bd90b3b5e52c43ba9d9cd + +$ cd ReelShort +$ IP=$(ipconfig getifaddr en0) +$ echo "API_BASE_URL=http://$IP:8081" +$ flutter run -d 00008101-000875813E42001E \ + --dart-define=API_BASE_URL=http://$IP:8081 +``` + +`API_BASE_URL` 是 Dart 编译期 `--dart-define`,不是 `export API_BASE_URL=...` 环境变量; +改了地址必须停止 App 后用新参数重新 `flutter run`,热重载不会替换它。 +真机不能用 `127.0.0.1` 访问 Mac,Android 模拟器才用 `10.0.2.2:8081`。 + +预期:App 启动后首页和发现页能加载封面、标题与分区;手机没有出现网络异常空屏。 +如果页面一直报网络错误,先在手机浏览器验证第 3 步的 `/healthz`,再检查 Mac 防火墙和 +本地网络权限;不要用开发机 `curl` 冒充设备通过。 + +#### 17.3 K2-A 分类验收 + +1. 打开「发现」页。 + +预期:先出现分类/分区加载态,随后分类条显示保留的 `For You`、`Hot`、`New` 入口, +再显示后端 `/app/categories` 返回的分类名称。默认种子数据下包括「都市情感」「重生逆袭」 +「甜宠爱情」「悬疑复仇」;数据库改过名称时以实际后端返回名称为准。 + +2. 依次点「都市情感」「甜宠爱情」。 + +预期:顶部对应分类 chip 高亮;下方只展示 `/app/discover/feed` 中 +`code == "category-{id}"` 的分区卡片。切换分类时内容跟着切换,不会保留上一个分类的卡片。 + +3. 点默认种子中没有已发布内容的「重生逆袭」或「悬疑复仇」。 + +预期:分区区域显示「这个分类暂时没有剧集。」。这是无分区空态,不应出现自行拼装的推荐列表、 +旧分类残留卡片或网络失败文案。 + +4. 切回有内容的分类。 + +预期:卡片恢复显示;封面和标题与后端发现 feed 一致;点卡片可进入对应详情。 + +5. 失败与重试:停止 8081,冷启动 App 后打开「发现」页。 + +预期:分类区域显示「分类加载失败。」和「重试」按钮,不白屏。 +重新启动 8081 后点「重试」,预期分类和分区恢复显示;必要时下拉刷新。 + +设备侧只能直接证明服务端分类名称与分类切换结果;`category-{id}` 的代码映射、 +全量去重和并发/重试细节由 `test/category/category_controller_test.dart` 自动化覆盖。 + +解读:K2-A 的用户验收点是“分类条来自服务端、内容只取对应 `category-{id}` 分区、 +缺失分区显示空态、失败可重试”;真机没有执行这些步骤,当前结论仍为未验证。 + +#### 17.4 K2-B 观看记录验收 + +1. 登录后播放任意一部剧的免费集约 10 秒,返回并进入「我的 → 观看历史」。 + +预期:页面先显示加载态,随后出现该剧卡片;卡片可见封面、剧名、 +「第 N 集」、进度条、`续看至 mm:ss / mm:ss` 和「更新于 ...」。 +即使该剧不在首页/发现已加载目录中,也应正常显示,不能因为目录里匹配不到而消失。 + +2. N+1 合并卡片:对同一部剧先后播放两个不同剧集,再播放另一部剧的一集,重新打开观看历史。 + +预期:同一部剧只出现一张卡片,不因看过多个剧集而重复;卡片展示该剧最新进度和对应集号。 +不同剧各自一张卡片。详情是按 `dramaId` 去重后补取的,单条卡片加载中不应拖住其他卡片。 + +3. 单条删除:点某张卡片右侧的关闭图标。 + +预期:卡片立即从页面消失;返回再进入观看历史后仍然不存在。 + +4. 单删失败回滚:保持页面已加载,停止 8081,再点另一张卡片的关闭图标。 + +预期:卡片先乐观消失,随后回到原位置,并出现「删除记录失败,请重试。」。 +重新启动 8081 后,删除动作可再次执行。 + +5. 清空:点页面右上方「清空」。 + +预期:先弹出「清空观看记录?」及「这会从当前账号移除全部观看记录。」; +点「暂不」列表保持不变;点「清空」后列表变空并显示「还没有观看记录。」。 + +6. 清空失败回滚:有记录且页面已加载时停止 8081,点「清空」并在二次确认中点「清空」。 + +预期:列表先清空,随后恢复原记录,并出现「清空观看记录失败,请重试。」。 + +7. 三态和失败重试:后端正常时进入页面观察加载态;清空后观察空态;停止 8081 后冷启动 App +再进入观看历史,观察「续看进度加载失败。」和「重试」。重启 8081 后点「重试」, +预期记录重新加载。 + +若单条详情请求失败,预期该卡片显示「剧集信息加载失败」与「重试」,其他卡片仍正常显示; +该分支和 4 并发上限由 `test/watch/watch_records_controller_test.dart`、 +`test/watch/watch_records_page_test.dart` 自动化覆盖。真机验收重点看合并卡片和整页不被单条失败拖垮。 + +解读:K2-B 的用户验收点是三态、同一剧 N+1 详情合并为一张卡片、单删与清空的乐观回滚; +自动化测试已覆盖控制器行为,手机上的真实网络与回滚表现仍待用户取证。 + +#### 17.5 K2-C 收藏验收 + +1. 登录后在详情页或播放页点书签图标收藏一部剧。 + +预期:图标立即切换为已收藏状态;失败时图标回到原状态并显示「收藏操作失败,请重试。」。 + +2. 再收藏 1 至 2 部不同的剧,进入「我的 → 我的收藏」。 + +预期:页面展示服务端 `GET /app/favorites` 返回的全部收藏卡片,封面、标题与首页卡片一致; +列表不显示分页控件,也不发送自造的 `page/size`。点击卡片可进入详情。 + +3. 取消收藏:点某张收藏卡片右侧的粉色收藏图标。 + +预期:卡片立即消失;返回再进入我的收藏后仍然不存在。 + +4. 取消收藏失败回滚:保持列表已加载,停止 8081,再点另一张卡片的粉色图标。 + +预期:卡片先乐观消失,随后回到原位置,并出现「收藏操作失败,请重试。」。 +重新启动 8081 后可再次取消。 + +5. 三态和失败重试:后端正常时进入页面观察加载态;取消全部收藏后观察「还没有收藏。」; +停止 8081 后冷启动 App 再进入我的收藏,观察「收藏列表加载失败。」和「重试」。 +重启 8081 后点「重试」,预期列表恢复。 + +6. 服务端状态校准:在另一台设备或另一次登录中取消同一部收藏,再回到当前设备的我的收藏刷新。 + +预期:当前列表以服务端结果为准,不继续显示已被取消的收藏。 + +解读:K2-C 的用户验收点是服务端全量收藏列表、取消收藏后的乐观回滚和三态; +未登录引导另见 17.7,设备侧列表与回滚表现仍待用户取证。 + +#### 17.6 K2-D 下载授权验收 + +1. 登录后进入「我的 → 下载」。 + +预期:先出现加载态,随后显示策略卡: +「剩余 X / Y 个授权」、「授权有效期 N 天」以及后端返回的 `expirePolicyText` 原文; +下面显示「仅 Wi-Fi 下载」开关和「最高清晰度」下拉框。 + +2. 验证 §1.6 的 8 个接口。真机可观察结果如下: + +| # | 接口 | 真机操作与预期可见结果 | +|---|---|---| +| 1 | `GET /app/downloads/policy` | 打开下载页后策略卡出现,配额、授权天数、`expirePolicyText` 来自后端 | +| 2 | `PUT /app/downloads/settings` | 切换仅 Wi-Fi 或清晰度后,页面立即显示新值;离开再进入仍保持 | +| 3 | `POST /app/downloads` | 详情页/播放页点下载图标,出现授权成功或后端拒绝提示 | +| 4 | `GET /app/downloads` | 打开下载页后出现当前设备授权列表;无授权时显示空态 | +| 5 | `GET /app/downloads/{episodeId}/url` | 本批只提供 API 方法,UI 不调用;真机无可观察入口,见自动化测试 | +| 6 | `POST /app/downloads/{episodeId}/progress` | 本批只提供方法,不做文件落盘/进度上报;真机无可观察入口,见自动化测试 | +| 7 | `DELETE /app/downloads/{episodeId}` | 点单条右侧关闭图标后该授权消失,重新进入仍不存在 | +| 8 | `DELETE /app/downloads` | 点「清空」并二次确认后授权列表变空,重新进入仍为空 | + +3. 申请授权成功:进入任意可下载剧集的详情页或播放页,点下载图标(详情页右上角下载按钮, +或播放页右侧下载入口)。 + +预期:服务端接受时出现「下载授权已就绪。」;进入我的下载后能看到对应剧名、集号、 +授权状态、任务状态、进度百分比、清晰度(后端有值时)和到期时间。 + +4. 授予失败透传后端 `message`:选择付费/未解锁剧集,或把当前设备配额用满后再次申请。 + +预期:SnackBar 原样显示后端返回的 `message`,不能替换成前端自造的“付费/未解锁/配额满”判断。 +只有后端没有可用 message 时才回退到「申请下载失败。」。验收时记录手机屏幕上看到的完整原文; +当前环境没有执行这一步,因此具体拒绝文案仍属未验证。 + +5. 仅 Wi-Fi/清晰度成功:切换「仅 Wi-Fi 下载」,再选择 `AUTO`、`720P` 或 `1080P`。 + +预期:开关/下拉值立即变化,没有崩溃或重复选项;退出下载页再进入,值从 +`GET /app/downloads/policy` 重新读取且保持。 + +6. 设置失败回滚:保持下载页已加载,停止 8081,再切换仅 Wi-Fi 或清晰度。 + +预期:值先乐观变化,随后恢复原值,并出现「下载设置更新失败。」。 + +7. 单删和清空。 + +- 点单条右侧关闭图标:预期该授权立即消失,重新进入后仍不存在。 +- 停止 8081 后点另一条关闭图标:预期先消失再恢复,并提示「删除下载授权失败。」。 +- 点「清空」:预期先弹出「清空下载授权?」及「这会吊销当前设备的全部下载授权。」; + 点「暂不」不变,点「清空」后列表为空并显示「还没有下载授权。」。 +- 停止 8081 后清空:预期列表先清空再恢复,并提示「清空下载授权失败。」。 + +8. 状态枚举后端驱动:查看授权卡上「授权:...」「任务:...」后的文本。 + +预期:显示后端原值,如 `ACTIVE/EXPIRED/REVOKED`、 +`QUEUED/DOWNLOADING/PAUSED/COMPLETED/FAILED/CANCELED`,前端不做中文映射或本地推算。 +现有数据只可能展示实际出现的状态;其余枚举由下载 API/控制器测试覆盖, +设备侧未逐一构造全部后端状态,回执不能写成“所有状态已真机验证”。 + +9. 三态和失败重试:后端正常时进入观察加载态;清空授权后观察空态; +停止 8081 后冷启动 App 再进入下载页,观察「下载设置加载失败。」和「重试」。 +重启 8081 后点「重试」,预期策略卡和授权列表恢复。 + +本批不落盘、不断点续传、不提供离线播放;即使授权状态为 `ACTIVE`,也不能据此验收文件已下载。 + +解读:K2-D 的用户验收点是策略卡、8 个接口的可见结果、后端 `message` 透传、 +仅 Wi-Fi/清晰度、单删/清空以及状态枚举原样展示。URL/进度两个接口没有本批 UI 入口, +只能由自动化测试证明方法契约;真机拒绝文案和全部状态仍未验证。 + +#### 17.7 未登录 AuthGate 与失败重试 + +1. 在「我的」页退出登录。 + +预期:账号回到未登录状态。 + +2. 依次进入「我的 → 观看历史」「我的 → 我的收藏」「我的 → 下载」。 + +预期:三个页面都显示登录引导: +「登录后可继续」、「观看记录、收藏、评论和下载需要登录。」和「去登录」按钮; +不显示网络错误、白屏或空列表冒充无数据。 + +3. 在未登录的详情页点收藏或在详情页/播放页点下载。 + +预期:出现底部登录提示;点「去登录」进入登录页,点「暂不」关闭提示且不发业务请求。 + +4. 完成登录后返回观看历史、我的收藏或下载页。 + +预期:页面自动从登录引导切换到加载态,再展示对应数据;不需要杀 App 才能刷新。 + +5. 失败重试统一检查:先让页面加载成功,再停止 8081 并冷启动 App 进入页面。 + +预期:记录页显示「续看进度加载失败。」、收藏页显示「收藏列表加载失败。」、 +下载页显示「下载设置加载失败。」,均有「重试」按钮;重启 8081 后点「重试」能恢复, +不出现白屏或无提示的假成功。 + +解读:未登录时三个独立页面都应由页面级 AuthGate 接住;登录或后端恢复后应重新加载。 +这些手机交互本轮未执行,不能用开发机请求代替。 + +#### 17.8 测试跳过口径差异与稳定复现命令 + +PM 复核事实与本轮复跑结果一致: + +- 8081 未启动:`flutter test` 呈 `+216 ~5`,5 个 live 用例全部由 + `markTestSkipped('客户端接口服务未启动,已跳过联调测试')` 跳过。 +- 8081 已启动:PM 复核呈 `+220 ~1`,4 个 live 用例通过; + 当前演示数据下 `live_watch_flow_test.dart` 仍由播放断言前的 + `markTestSkipped('这部演示剧没有可播剧集,已跳过播放相关断言')` 保留 1 个跳过。 + 如果换了一套有可播剧集的种子数据,这个用例可能通过并呈 `+221 ~0`;以实际输出为准。 + +5 个 live 用例及跳过机制: + +| 文件 | 用例 | 跳过机制 | +|---|---|---| +| `test/integration/live_auth_test.dart` | `should_surface_business_error_when_sign_in_uses_unknown_email` | 捕获连接失败后 `markTestSkipped('客户端接口服务未启动,已跳过联调测试')` | +| `test/integration/live_catalog_test.dart` | `should_parse_banner_and_sections_when_home_feed_is_fetched` | `serverUp()` 失败后 `markTestSkipped('客户端接口服务未启动,已跳过联调测试')` | +| `test/integration/live_catalog_test.dart` | `should_order_episodes_by_number_and_attach_url_when_detail_is_fetched` | 同上 | +| `test/integration/live_catalog_test.dart` | `should_return_play_url_when_episode_is_playable` | 同上 | +| `test/integration/live_watch_flow_test.dart` | `should_complete_the_watch_flow_when_backend_is_running` | 注册失败后跳过“服务未启动”;注册成功但无可播剧集时跳过“没有可播剧集” | + +稳定复现 `+216 ~5`: + +``` +$ lsof -nP -iTCP:8081 -sTCP:LISTEN +(确认无输出) +$ cd /Users/jianghaifeng/Desktop/ReelShort/ReelShort +$ flutter test +00:10 +216 ~5: All tests passed! +``` + +稳定复现 `+220 ~1`: + +``` +# 终端 A:启动 8081 后保持运行 +$ cd /Users/jianghaifeng/Desktop/ReelShort/FruitServer +$ mvn -pl fruitshorts-app-api -am package -DskipTests +$ java -jar fruitshorts-app-api/target/fruitshorts-app-api-1.0.0-SNAPSHOT.jar + +# 终端 B:确认 8081 已监听后跑测试 +$ curl -fsS http://127.0.0.1:8081/healthz +$ cd /Users/jianghaifeng/Desktop/ReelShort/ReelShort +$ flutter test +00:xx +220 ~1: All tests passed! +``` + +这 3 个 live 测试文件把地址硬编码为 `http://127.0.0.1:8081`,不读取 +`--dart-define=API_BASE_URL`。因此复现测试口径必须在同一台 Mac 上启动 8081, +手机同一局域网只用于真机 App 验收,不能替代这里的环回联调。 +本轮没有改这 3 个测试文件或产品代码。 + +解读:`~5` 与 `~1` 的差异来自 8081 是否启动,不是测试代码漂移; +`~1` 还是当前演示数据下播放断言前的跳过,不能把 `~5` 误报成 5 个失败。 + +#### 17.9 提交作者口径差异登记 + +叙述:已发生的历史提交不改写,不回滚,不 force push。下面用原始 Git 输出登记事实。 + +``` +$ git show -s --format='%H%n author: %an <%ae>%n committer: %cn <%ce>%n subject: %s' cc4d7c5 +cc4d7c5a9dc6a4f6f0565a32503cce5973af703c + author: Zhou Weidong <502333560@qq.com> + committer: Zhou Weidong <502333560@qq.com> + subject: feat(flutter): 接入分类记录收藏与下载授权 + +$ git show -s --format='%H%n author: %an <%ae>%n committer: %cn <%ce>%n subject: %s' 45f5f25 +45f5f25640a274edb51bd90b3b5e52c43ba9d9cd + author: Zhou Weidong <502333560@qq.com> + committer: Zhou Weidong <502333560@qq.com> + subject: merge: 合并 K2 客户端分类记录收藏与下载授权 +``` + +差异口径与最终裁定: + +- specs 规范要求提交作者统一为 `Zhou Weidong <502333560@qq.com>`,上述两个历史提交按此口径产生。 +- 用户曾口头要求使用 `jianghaifeng `;该口径已作废,不再作为后续提交依据。 +- 两个历史提交均已 push 到 `develop`;保留原始历史,不通过回滚或 force push 改写,不动 `main`。 +- 已裁定:后续提交统一使用 `Zhou Weidong <502333560@qq.com>`,author 与 committer 均按此口径。 +- 本回执提交 `cfce77a` 曾误用 `jianghaifeng ` / `trae-solo-agent `,现已通过 amend 修正为 `Zhou Weidong <502333560@qq.com>`;amend 后最终提交号以本轮 `git log -2` 原始输出为准。 + +解读:提交作者差异是已发生事实,已推送的历史提交不通过回滚或 force push 改写; +jianghaifeng 口径作废,后续统一使用 Zhou Weidong,本回执提交已通过 amend 校正。 + +#### 17.10 仍未验证的边界 + +- 设备侧业务取证由用户执行,Codex 未执行;本轮没有手机屏幕截图、设备日志或手机到 8081 的连接证据。 +- 具体后端拒绝 `message` 原文尚未在真机触发并记录。 +- 8 个下载接口中,`GET /app/downloads/{episodeId}/url` 与 + `POST /app/downloads/{episodeId}/progress` 本批没有 UI 调用入口,只有自动化 API 契约测试。 +- `ACTIVE/EXPIRED/REVOKED` 与 `QUEUED/DOWNLOADING/PAUSED/COMPLETED/FAILED/CANCELED` + 只验证“前端展示后端原值”的机制;没有逐一构造全部状态做真机验收。 +- 文件分片落盘、断点续传、离线播放、`licenseKey` 本地校验不在 K2 范围,仍未实现,也不能验收。 +- 收藏接口没有分页参数,本批按全量列表验收,不验收服务端分页。 +- 搜索契约没有接口,本批不验收搜索。