# App 注销与缓存归属

代码已写入 `juku-mobile`，复用 Better Auth 1.7.7 官方 Expo 客户端、现有 MMKV、TanStack Query 和文件适配器。账号注销、恢复界面与缓存归属清理基础已接入；真实离线下载仍待完成，当前下载入口提示暂不可用。API、安装包和 OTA 均未发布。

## 用户流程

“我的 → 账号安全 → 注销账号”：确认后向当前实际邮箱发送验证码，重新验证同一账号，再通过官方 `deleteUser` 提交。界面支持中英文；验证码为 6 位，按钮至少 48px。必要说明只保留注销后果与处理状态。

验证码验证必须产生同一 user.id 的新实际 session，并让服务端原意图进入 ready；客户端不相信 OTP 响应里的 token。429 按服务端等待秒数暂停发送；错误验证码可重新输入，过期后重新确认。

发送删除前保存执行阶段。成功、断线、冷启动均读取原 operation 的状态，自动恢复只发 GET，不重复发送删除。其他账号不能沿用原注销操作；服务端尚未确认终态时不显示“已注销”。

## 恢复和本机清理

现有 MMKV 的同一恢复 key 按 `origin + userId` 保留动作。兼容上一版的单条记录；A 的提交中收据不会被 B 开始或取消自己的操作覆盖，清理只删除对应 operation。记录仅含站点、UID、旧 session ID、operation ID、到期时间与阶段，不存邮箱、验证码、cookie 或会话 token。

确认原状态为 deleting/deleted 后，先读取官方会话；只允许原账号或实际无会话，不能清掉当前另一个账号。随后清理官方本机凭据、原 owner 的进度与已知缓存文件。已提交操作离开页面后仍可恢复。

文件先锁定再删除，删除失败保留 `cleanup_pending` 和重试提示；失败记录不能交给本地播放器。文件适配器只接受 App 媒体目录中的单层文件名，并在删除后核对是否仍存在。MMKV 写入未生效时停止提交；收据删除抛错或未生效时保留终态，允许再次查询并收尾，不重发注销。

旧无归属缓存保持锁定，不能按当前账号猜归属，也不随当前账号清空；提供独立确认的旧缓存清理入口。换号、退出、权威会话失效只清理明确归属的旧账号，迟到的 A 清理不会移除 B 的 viewer 或查询。

## 离线能力边界

| 当前能力 | 尚未完成 |
| --- | --- |
| 文件记录按 origin、UID 隔离；有效期与文件存在检查；本地列表、详情和播放器统一使用 | 来源的真实离线许可发行器、安装证明及许可证验证 |
| v1 无归属文件迁移为锁定，已有文件不猜账号 | Media3 / AVAssetDownloadURLSession 下载桥接、持久密钥与可恢复队列 |
| 失效或换号移除本地播放器 URI；有效期到达后重排定时检查 | 真机飞行模式、强退恢复、磁盘不足、系统后台与真实密钥擦除 |
| 旧 viewer 下载 URL 不再直接创建文件、任务或扣账 | O00/O01 的 preview、order、license、队列及到账完整接线 |

`OfflineAuthorization` 当前只是归属元数据，不是离线许可证；其中 deviceId 不是设备证明。文件删除替代测试不能证明 Android/iOS 已物理擦除，元数据 task 的移除也不是原生下载任务取消。本机 App 文件存储与“网站不落盘视频”的约束分别适用。

## 实际验证

- App 自动测试 203/203、TypeScript 检查通过。其中注销接口 11、实际 React 页面 12、缓存归属 10、本地页面/播放器 4；其余为既有功能回归。
- 注销测试使用已安装的官方 SDK；HTTP 故障、SecureStore、文件和原生控件采用隔离适配器。覆盖正常、错误 OTP、新旧/其他 session、429、请求丢失、匿名恢复、切换账号、存储失败、文件删除失败和 GET-only 重试。
- 独立复审发现并修复两项恢复 P2：B 覆盖 A 的收据、收据移除异常。此前缓存摘要、同 ID 隔离和长有效期 timer 的发现已修复。见[App 独立复审](dramivio-app-deletion-review.md)。
- 实际官方 Expo SDK → 现有 Go → Better Auth → 隔离 RAM PostgreSQL 联调，核对 OTP、新同 owner session、后端 ready、删除提交后模拟响应丢失、原收据、真实无会话及身份表清空。详细检查数和完整组合测试结果见[验证记录](dramivio-consumer-validation.json)。邮件在本地截取，不是 SMTP 投递；原生存储/界面仍为测试适配器。

## 继续实施顺序

1. O00：先复用原生开源下载能力，完成受控合法媒体的 asset/密钥桥接；分开测试首集、暂停恢复、飞行模式与强退，不把在线 URL 当 license。
2. O01：接现有额度与归属记录，加入真实 preview/order/license 和恢复队列；测试重复提交、换号、到期、网络切换及空间不足。
3. A03 真机收尾：测试官方 SecureStore、实际文件、原生任务与密钥擦除，再验证注销、退出、切号、重启和失败重试。最后接正式邮件、常驻任务、审计期限及真实发布。

## 源码位置

| 文件 | 用途 |
| --- | --- |
| src/auth/deletion.ts | 可导入的意图、OTP、提交、状态与恢复存储函数；复用官方 SDK |
| src/screens/DeleteAccountScreen.tsx | 中文/英文确认、验证码及原操作恢复 |
| src/auth/ConsumerAuthGate.tsx、auth/client.ts | 原 owner 清理和官方凭据生命周期 |
| src/playback/offlineOwnership.ts、store/downloads.ts、files.ts | 归属、锁定和受限文件删除 |
| src/screens/DownloadsScreen.tsx、DramaScreen.tsx、LocalPlayerScreen.tsx | 登录与归属限制，阻止失效文件播放 |
| tests/deletion-api.test.cjs、deletion-screen.test.cjs、download-owner.test.cjs、download-screen.test.cjs | 对应模块和失败场景回归 |

这些是当前代码与证据的范围，完整 O00/O01、A03 真机清理和整个产品尚未完成。后台清理及 Web 路线见[账号注销与后台清理](dramivio-account-deletion-implementation.md)。
