视觉按最新原型,业务按已确认PRD。现有代码为满足设计而改造。执行见按设计实施与任务卡;当前代码与验证范围见消费者功能页。
App 缓存订单、选集与离线播放
2026-10-04。本轮接通 O01 后端订单与 App 页面,沿用 Better Auth、现有额度账本、Zustand/MMKV、react-native-video、Media3 和 Apple 系统下载。网站只处理订单、许可和来源地址,不下载或保存视频字节。真实来源签发器尚未接入,默认拒绝交付,不先扣次数。
页面与流程
详情页「缓存」进入选集;四列集号,一次最多 20 集。已缓存集不可再选,不占选择名额。确认弹窗显示所选、新增集数和剩余次数;确认后才创建订单。下载页显示排队、授权确认、下载进度、暂停、文件缺失、过期与删除状态,提供播放、暂停、继续、重试和取消。
服务端许可可用不等于文件已下载。只有当前账号、同站点、有效许可和真实原生索引里的完整文件同时匹配,才显示「已缓存」和播放按钮。离线播放复用当前播放器、选集、倍速、App 长按倍速与进度条;只列本机可用集。


以上截图来自实际 RN 页面组件在 Chrome 中的渲染。原生索引、接口、封面和导航是明确的展示适配器,不能作为手机截图或真实媒体下载证据。
次数、队列与恢复
| 操作 | 行为 |
|---|---|
| 预览、加入队列 | 校验账号、批准的 App 会话、来源与 canonical 集映射;不扣次数,不占额度预留 |
| 开始下载 | 原生有空位才执行 prepare;同账号跨组件实例共用调度锁,安排前重新读取原生索引;App 自动队列最多同时安排两个任务,长队列不提前占当日次数 |
| 授权提交 | 使用同一个 canonical 解锁账本;在线与缓存同一集当天只记一次。额度提交与订单许可状态在同一 PostgreSQL 事务中完成 |
| 跨日等待、预留过期 | 真正准备下载时使用当前日额度;失效预留沿用资产更换,不能拿旧日的未提交预留继续扣款 |
| 供应商预检失败 | 释放未提交预留,可重试;设备上限导致提交回滚也释放预留 |
| 签发响应未知 | 保留已提交事实。先按原资产查询供应商,不盲目再次签发或退款;待核对时不提供播放地址 |
| 本地订单保存失败 | 不发创建请求;即使内存保留记录,恢复重放前仍需重新成功保存原订单号 |
| 原生入队结果未知 | 本轮停止安排新集;下一次读取实际索引确认槽位,列表采用真实原生状态,不把正在下载的任务显示为可破坏性重试的失败 |
| 订单请求超时 | 请求前保存原订单号与选集;重启用原号查询,只有权威 404 才重放原创建。恢复不会另造订单号 |
| 取消未知订单 | 同一个订单号确认创建后取消,防止超时请求晚到创建孤儿订单。任何失败保留待清理记录 |
| 取消与原生入队同时发生 | 等待在途入队,补偿删除迟到任务;删除失败阻止忘记订单。正在删除的原生记录继续可见以便重试,不返回可播放 URI |
| 退出、换号、注销 | 继续使用 origin + UID 的原生清理与失败重试;旧结果不能进入新账号队列。终止账号的后台清理删除订单与来源 payload,保留额度审计 |
许可最长七天,同时受来源权利期限、安装批准和实际官方会话期限限制。数据库限制同一账号最多两个有未到期许可的设备;这只是批准设备的账本限制,硬件证明仍待接入,不能当作复制文件防护。
来源、offline policy 或 E00 capability revision 改变后,旧签发的晚结果不交付。已提交的次数保留;不会声称能远程即时收回已经离线的文件。
复用与接口
身份仍由 Better Auth 官方邮箱 OTP 和 Expo 插件处理。Go 根据当前官方消费者登录、已签名的安装标识和来源权限组装 actor;用户 body 不能指定 owner、device、App 身份、issuer、URI 或退款。内部命令继续只由现有私有服务入口接收。
公开接口位于 /api/consumer/offline/:
| 接口 | 方法 | 用途 |
|---|---|---|
| preview | POST | 校验选集与新增次数;不返回视频地址 |
| order | POST | 使用调用者已保存的 orderId 创建元数据订单 |
| status?orderId=… | GET | 当前账号、当前设备查询;不调用签发器、不返回来源地址 |
| prepare | POST | 重新核对官方会话与来源后,为一个资产恢复或签发许可 |
| cancel | POST | 禁止后续交付,释放未提交预留;已提交观看事实保留 |
后端迁移为 006-offline.sql。publishOfflineSourcePolicy 复用已有后台审核记录,只允许明确接受 private_app_policy 的非 DRM 来源及 MP4/HLS 格式;默认关闭。真实 DRM 不能通过改一个标记启用。
createPlatformRuntime({ ..., offlineProviders }) 接受可信服务器适配器注册表。适配器提供普通 preflight(input,{signal})、lookup(input,{signal})、issue(input,{signal}) 方法;资产标识稳定,lookup 必须明确区分 pending、not_issued 和已签发。调用有有限超时;超时不能证明第三方动作未发生。注册表不接受消费者请求传入。
App 使用 createOfflineAPI(owner) 和 createOfflineQueue({ owner,current,read,write,api,native }),可以普通导入和传参。订单沿用现有 juku.downloads.v1 存储,不另建下载器;只持久化剧名、选集、资产状态、许可摘要及本机位置。来源 URI 和请求头仅短暂交给原生下载器,不进入 JS 订单存储。
实际验证
| 检查 | 当前结果 | 范围 |
|---|---|---|
| platform 全量 | 175/175 | 真实隔离 PostgreSQL 18、Better Auth OTP/session 与生产 HTTP/domain;邮件收集和签发 metadata 为受控适配器 |
| O01 作者后端 | 19 个领域、5 个 HTTP 实质子用例通过 | Node 含两个 suite 的计数为 26,已包含在平台全量内 |
| Go 定向回归 | 通过 | 消费者网关、额度/账号及直连/转发/代理边界;内部服务响应为适配器 |
| App 全量及类型 | 256/256,类型通过 | API、元数据队列、真实 React 页面及既有账号/播放回归;原生和网络故障使用明确适配器 |
| 中英文组件页面 | 8 个状态通过 | Chrome,选集/确认、队列/取消;不计真机 |
| Android 完整测试 APK | 已构建、实际产物检查通过 | 本机测试地址、现有 debug 签名;不安装、不发布 |
独立子 Agent 对后端和 App 做了对抗复审,复现、修正和复验见本轮复审。更早的 19 项实际 Android Media3 测试及 119 项 macOS Foundation/AVFoundation 检查仍按各自范围记录,不能混算为本轮真机测试。
后续拆解与验收
- 真实标准 API 来源适配。 接真实离线许可、幂等签发和 lookup;从已批准来源选一部可测试剧。先验证未启用默认拒绝、权利撤销、过期与错误目的地址,再验证真实 MP4/HLS 下载。真实 DRM 单独设计和验收。
- 选清晰度与容量。 依据来源实际版本增加 720p/1080p、预计容量、剩余空间和空间不足处理。不能把转码假作已有版本;当前尚未提供清晰度选择。
- 后台与飞行模式。 当前已入原生的任务由系统继续处理;剩余选集只有 App 活跃时由 JS 申请许可。继续接宿主后台生命周期、重启与任务唤醒,验证冷启动飞行模式的有效身份策略、强退、系统逐出、来电和低内存。
- 离线进度同步。 本轮记录的是本机每个缓存资产的集号、位置和时长,播放/暂停/退出可恢复。接 canonical 的离线待同步记录、跨设备冲突和补传;不得凭客户端位置生成邀请合格观看或额外奖励。
- 完整 iOS 与两端真机。 完整 UIKit/React 构建、iPhone/Android 实际播放、seek、断网、HLS 子请求和删除失败。macOS SDK 检查、语法 parse 和 Android APK 构建不能替代这些步骤。
广告、收费、真实邮件、后台完整运营配置与真实 App 资格继续按既有任务卡实施。网站直连优先、失败才转发的节约流量路线保持;本轮只更新本地代码与静态文档,没有发布 API、安装包或 OTA,也没有提交/推送仓库。