视觉按最新原型,业务按已确认PRD。现有代码为满足设计而改造。执行见按设计实施与任务卡;当前代码与验证范围见消费者功能页。
App 原生缓存与账号生命周期
本页保留此前阶段的实现与验收范围。2026-10-04 已追加后端订单、App 多选/队列与离线播放器接线,当前进展见缓存订单与页面及本轮复审。真实签发器、完整 iOS 与真机仍待验收。
本轮将原生下载队列接到现有 App 账号流程,复用 Better Auth、Zustand/MMKV、Media3 和 Apple 系统下载。消费者下载入口仍关闭;本轮不代替来源许可、签发或真机验收。
当前行为
| 场景 | 处理 |
|---|---|
| 冷启动 | 原生任务里的 owner 不作为登录证明;队列暂停、播放锁定。JS 确认账号并完成该账号待清理操作后才配置原生 owner;配置不会自动恢复下载 |
| 退出、权威无会话、会话到期、账号终态 | 原账号进度、明确归属的文件和真实原生队列接到同一个清理函数;旧账号任务停止,离线播放器清空当前媒体项 |
| 换号 | 使用 origin + UID 清理旧账号。A 的迟到结果不能删除 B 的文件、查询或身份;A 清理失败也不阻止 B 配置自己的原生 owner |
| 临时身份服务故障 | 暂停原生访问,保留已有文件和原账号归属;不把网络错误当作已注销。重新确认同一账号后可恢复访问 |
| 清理未完成 | 现有缓存存储保留 pendingNativeCleanup,按原 origin + UID 重试;失败文件保持 cleanup_pending,不能继续播放 |
| 重启、回到前台、手动重试 | 重试原待清理账号,等待真实原生结果。我的页面只显示“本机缓存未清理完成,请重试”及重试按钮,不显示旧账号资料 |
| 同 UID、不同站点 | 原生队列、文件和进度按站点区分;换站点清理旧站点,不移除新站点的身份和查询 |
| 旧安装包缺少桥接 | 明确报告原生能力不可用,不把文件或队列擦除当作成功;这类原生修改需要重新构建安装包 |
清理和恢复
clearOwnerDownloads 使用现有缓存存储保存待清理账号,不新建下载器。原生 clearOwner 的 Promise 与手机旧文件清理均参与结果;同一账号的并发调用共用一次执行。JS 存储失败仍尝试停止/删除真实原生任务,但返回未完成;写入和移除均读取原记录核对,避免未保存的重试状态被当作成功。
原生清理成功不代表旧文件适配器也成功。文件失败记录继续锁定并参与重试;原生失败则保留原 owner 的待处理记录。清理期间禁止重新激活同一 owner;其他账号不受该 owner 的失败牵连。当前账号重试完成后无需重新登录即可重新配置原生 owner。
任意一项进度或 viewer 存储失败,仍执行内存身份失效、私有查询移除与原生清理,再等待结果并报告未完成。viewer 的订阅通知与本机来源状态重置不再被持久化错误跳过。
账号退出仍通过官方 Expo signOut 完成。它等待本机清理结果;本机清理错误不会阻止一次服务端退出,最终保留清理提示。注销页继续按原操作查询结果,清理错误不能把页面标成“已注销”。
原生播放边界
Android 原生 owner 为进程内状态,冷启动不读取旧 active 偏好恢复权限。清理原 owner 时先使当前访问失效,再写清理记录;偏好存储失败也不能恢复旧账号读取。原生 owner 变化通知当前离线播放器,复用 react-native-video 的 clearSrc 停止 ExoPlayer、移除媒体项及旧来源;异步来源准备必须仍是当前 source 才能挂载,避免旧加载结果重新播放。解除订阅随来源更换和组件销毁执行。
iOS 在同样的账号失效事件中,对当前离线 AVPlayer 执行 pause、replaceCurrentItem(nil),并移除 item observer。准备中的本地 item 绑定原生账号、任务版本与当前加载请求;挂载前在同一次主线程执行中重新检查。退出后重新登录同一账号也不能挂载上次留下的 item。暂时写盘错误可在真实保存成功后恢复;损坏记录仍拒绝访问,不能默默重建为空。
仅禁止 DataSource 后续读取不能停掉已经缓冲的画面,因此播放器停止与读取校验分别执行。以上是本进程内账号变化的处理,不是远程即时撤销离线 DRM。
源码与调用
- src/auth/ConsumerAuthGate.tsx:确认账号、冷启动锁定、换号/到期/终态、前台重试和按账号配置原生 owner。
- src/store/downloads.ts:clearOwnerDownloads、retryOwnerDownloads、待清理账号及旧文件恢复。
- src/screens/MyScreen.tsx:中英文重试提示,不泄露原账号资料。
- src/playback/consumerProgress.ts、useConsumerProgress.ts:按 origin + UID 清理旧位置队列。
- react-native-video 的可安装补丁:真实原生 clearOwner、账号与播放器失效接线;不依赖单纯 OTA。
await clearConsumerAccount(queryClient, oldUserId, oldOrigin, currentOrigin);
const result = await retryOwnerDownloads();
两个函数均可普通导入和传参调用。原生集成检查继续使用已有 runOfflineNativeTests({ javaHome, androidHome, logPath })。
当前证据
App 全量 228 项及类型检查通过,新增账号/原生生命周期 16 项,另新增进度站点隔离检查。JS 测试使用实际 React、Zustand/MMKV 接口与官方 Expo SDK;原生桥接、文件和故障 HTTP 是明确适配器。覆盖待清理状态的序列化/重载、暂停与重新确认、A/B 迟到、存储抛错/未生效、原生清理失败、收据移除失败、原 owner 重试和中英文界面。
Android 19 项实际 Media3/SQLite/cache 集成通过;新检查覆盖持久 active 不能自动确认账号、guest 取消真实 DownloadHelper 准备、偏好 commit 失败后已打开读取拒绝,以及实际 ExoPlayer 媒体项停止/移除。媒体 transport 是受控合成字节;ExoPlayer 项目检查未运行解码器或屏幕渲染,不能写成已通过真机视频播放。
iOS 119 项真实 macOS Foundation/AVFoundation SDK 夹具通过,其中实际 AVPlayer 播放项达到 readyToPlay 后,由生产代码复用的 helper 停止并清空;包含账号、迟到请求、实际文件删除与暂时写盘故障恢复。RCTVideo 的 iOS target 语法 parse 通过;没有运行 UIKit/React wrapper 或完整 iOS 编译,也没有证明 iPhone 解码与画面。
Android 完整 assembleRelease 构建通过,检查实际 APK 的原生模块、服务清单、Hermes bundle 与四种 ABI。沿用项目现有 debug 签名、连接本机受控地址;未安装、未发布,不作为正式版本或真机验收。
来源后台任务、系统唤醒前已发生的网络请求、真实许可/密钥和 iOS 所有子请求隔离不能由此证明。
独立复审复现并关闭一项存储失败中断清理的问题;新增 JS 4 项、Android 3 项通过,另独立复跑作者相关 JS 29 项与 Android 19 项。范围不包含该 Agent 自己实现的 iOS 代码或真机。
仍需完成
O01 的来源能力、离线授权签发和额度、消费者缓存列表/多选队列接线;完整 iOS 构建与宿主后台 hook;Android/iOS 安装和飞行模式、seek、后台/强退、空间故障、系统逐出、真实许可/密钥清理。下载入口继续返回 offline_authorization_required。
网站视频不写磁盘的约束不变,原生缓存只写手机 App 沙盒。本轮没有发布真实 API、安装包或 OTA,也没有提交/推送仓库。