# A03 账号注销与后台清理

Web 注销确认、官方邮箱验证码、结果恢复与后台任务已写入原项目。身份及清理复用 Better Auth 1.7.7、已有 PostgreSQL verification、E00 command 和 Outbox；定时任务复用 Payload 3.90.2 Jobs 及其官方迁移。正式 API、App、OTA 未发布；App 注销页面、离线文件／许可／密钥清理仍待接入。

## 已实现的流程

1. 已验证账号创建服务端注销意图，随机 operationId，5 分钟有效。当前新鲜会话本身不足以注销。
2. 使用官方 sign-in 邮箱 OTP 重新登录。只有意图创建后、同 user.id 的实际新会话可绑定证明。验证码、会话 token 不写入注销收据。
3. 官方 delete-user 的 beforeDelete 检查证明、当前实际会话、账号及到期时间。直接删除、旧会话、其他账号、删除链接 callback 和 token 分支不能绕过确认。
4. 已有 E00 事务提交原 operation 的 terminal、撤销 App 批准、释放待播放预留，并写原 Outbox。等待 owner 锁期间再次检查证明有效期及实际会话行。
5. 撤销身份会话，清个人消费记录，再消费短期证明。官方框架删除身份；查实际身份残留后才确认完成。
6. 如果终态之后断线或删除失败，可信任务调用 processPending，继续原收据。恢复不需要旧账号重新登录，不创建替代账号、不重发奖励。失败保持原事件待处理，租约到期后继续。

删除结果以原操作状态为准，HTTP 成功不能代替真实残留检查。故障测试已证实：框架可能吞掉用户快照查询错误而跳过删除。本段不会把这种情况标记为已完成。

## 接口

| 接口 | 约束 |
| --- | --- |
| POST /api/consumer/account/delete/intent，空 JSON | 已验证官方账号；不接受 userId、sessionId、设备证明等客户端身份 |
| 官方 POST /api/auth/email-otp/send-verification-otp | type=sign-in；现有 6 位、5 分钟、次数限制、轮换与哈希存储保持原框架行为 |
| 官方 POST /api/auth/sign-in/email-otp | 通过 fetchOptions.headers 传 x-dramivio-deletion-intent；框架验证后绑定真实 newSession |
| 官方 POST /api/auth/delete-user，空 JSON | 相同意图头；业务 beforeDelete 检查绑定证明，不把自定义值填入官方 token |
| GET /api/consumer/account/delete/status?operationId=... | 只返回 awaiting_verification、ready、deleting、deleted 与必要短期到期时间；随机操作无法读取旧邮箱、片单、进度 |

状态查询精确跳过旧身份、来源和 viewer 中间件，因此会话已撤销、旧来源权限已变化时仍可查询。网站同源、方法与不缓存检查保留。其他账号接口继续走原身份体系。注销本身由官方登录路由处理；网站未增加任意 userId 删除入口。

## Web 页面与恢复

“我的 → 账号安全 → 注销账号”：先说明删除和必要收据保留，勾选确认后创建意图；向当前实际邮箱发送官方验证码，验证后提交注销。中文与英文使用同一流程，原生 dialog、输入标签、Enter 提交、Escape 关闭及 44px 操作按钮保持现有页面习惯。

重新验证会更换官方会话。现有身份隔离要求完整刷新页面，因此当前 tab 的 sessionStorage 只保存站点、操作编号、旧会话 ID、旧 UID、到期时间与执行阶段。邮箱、验证码、session token 不持久化。恢复继续核对实际官方账号，其他账号不能沿用原页面操作。

提交前记录阶段；成功响应、断线、刷新都以同一操作状态查询为准，不自动重复提交 delete-user。验证码响应丢失后只核对新实际会话和原意图；确定的错误验证码可重新输入，429 使用服务端等待秒数。注销完成或已进入终态后清当前 owner 的本地进度队列，身份失效仍复用原刷新机制。未进入终态的取消不会发起删除。恢复结果不能根据尚未初始化的空身份退出其他账号：既有 viewer 归属参与检查，终态清理前再读取官方会话，只允许已无会话或原 owner。

浏览器不能保存恢复记录时，停止进入注销提交，明确提示换浏览器。必要的短期恢复记录与身份存储分开，是为了保留已有全页身份隔离，并非第二套认证。

![320px 中文确认](web-deletion-preview/mobile-zh-confirm.png)

![实际 Go／官方框架／数据库联调完成页面](web-deletion-preview/real-go-framework-pg-deleted.png)

## 成熟后台任务

Payload 的 `account-cleanup` 任务每分钟处理最多 20 条原注销 Outbox；官方队列承担调度、重试、状态与错误记录。任务无用户输入，私有平台接口只接受空 JSON；任务输出只有处理计数，错误统一脱敏。成功后删除 Payload 任务及日志，避免每分钟累计无用记录；原业务收据不因此移除。

后台需同时配置 `DRAMIVIO_PLATFORM_ORIGIN`（HTTP loopback origin）和 `DRAMIVIO_BRIDGE_TOKEN`，才在常驻 Next 进程启用一套 `autoRun`。没有连接配置时任务页仍可读，周期运行关闭。不要另外为同一队列增加第二套 cron。依据[Payload 官方调度说明](https://payloadcms.com/docs/jobs-queue/overview)，静态 Pages 不运行这类任务。

管理员可查看任务与统计；游客、消费者及只读运营不能查看或触发。通用任务创建、更新、删除继续拒绝，运行／排队／取消使用框架访问控制。任务不会把终态收据以外的普通管理停用账户删除。

新增表仅为 Payload 官方生成的任务和统计表，位于独立管理数据库。生产保持 schema push 关闭；迁移必须在真实上线步骤显式执行。本次仅在隔离 RAM PostgreSQL 执行，未连接既有数据。

![正式构建的后台任务页](admin-preview/account-cleanup-jobs-1280.png)

## 清理与保留

| 立即清理或失效 | 处理 |
| --- | --- |
| 追剧、逐集位置、观看区间、播放上报、逐剧范围与个人状态行 | 按实际 FK 顺序删除；E00 保留终态拒绝迟到上报 |
| 搜索词、筛选快照与个人 AI 日计数 | 删除；快照 INSERT、模型预算写入与同 owner 终态锁串行，阻止迟到写入复活 |
| 身份账号、登录会话、身份关联账户 | 通过官方 helper 删除；查 user/session/account 实际剩余行 |
| 邀请码、未到账关系/奖励 | 邀请码撤销；未到账奖励阻止、释放持有预留 |
| 邀请观看明细及其重复 command proof、本人申诉原文、原始 IP、非必要个人设备关联 | 删除或脱敏 |

已入账奖励、共享设备已用次数、原操作收据和旧 UID 终态保留最少账务/去重事实。已到账但丢 ACK 时，仅恢复原奖励，不另发。另一方有效申诉及已结案的拒绝原因、审核时间保持原结果。相同邮箱再次注册产生新的 user.id；旧任务固定旧 UID，不会按邮箱删除新账号。

当前没有统一审计期限及压缩任务，不能宣称“全部关联记录均已物理删除”或“所有数据 N 天后自动消失”。短期 OTP/验证记录由官方用途和真实 TTL 失效；未能精确归属的换邮箱验证码不做模糊删除，以免误删另一账号。审计压缩需同时限定旧操作接受范围，后续单独验收。

## 源码接入点

- services/platform/src/account-deletion.mjs：可导入 createConsumerDeletion；begin、verified、beforeDelete、afterDelete、status、processPending。
- auth.mjs、runtime.mjs、platform.mjs：官方登录/删除钩子和实际身份组合。
- state.mjs、referral.mjs：幂等终态清理。搜索和模型预算复用 catalog.mjs 的现 owner 锁。
- entitlements.mjs：敏感终态命令可携带受信 authSessionId；Outbox 可按既有 kind/commandActorId/operationId 领取，不误消费普通管理停用任务。
- internal/app/consumer_account.go：网站意图和结果查询适配。
- internal/webui/consumer-deletion-api.js、consumer-deletion.js：普通可导入函数，复用当前 auth、dialog 和进度清理。
- services/admin/src/deletion-jobs.mjs、config.mjs、payload.config.mjs：官方队列、私有平台连接、访问控制与运行配置。

业务连接释放后才调用官方 helper；单连接池下也不会嵌套等待。成功的官方删除路由后立即处理本次 Outbox。失败恢复已接入成熟队列配置，并验证官方调度、保留失败任务与成功重试；真实常驻服务尚未发布，不能宣称线上周期任务已运行。

## 验证

真实 PostgreSQL 18 RAM、官方 Better Auth HTTP 与实际邮箱 OTP 路由；邮件仅在隔离环境本地截取，无真实 SMTP。

- 注销专项 16/16：15 个子场景及顶层组，包括权限确认、证明到期、个人清理、安装撤权、清理失败恢复、SDK 无声跳过删除、私有任务接口权限、迟到 AI、普通管理停用保护、同邮箱新身份、未到账与已到账奖励、另一方申诉和并发确认。
- 全平台 149/149，20 个顶层组。旧会话、双邮箱、额度、邀请、搜索及进度回归保留。
- Web Node 全量 153/153，其中新增注销接口 14 项；实际 Chrome 注销 15 项，另保留账号安全 15 项、登录 16 项回归。浏览器故障场景使用 HTTP 协议夹具，不与真实框架联调混计。
- 真实 Chrome＋现有 Go＋官方 Better Auth＋RAM PostgreSQL 联调 24 项：新增注销端到端 3 项，核对邮箱、同一删除 POST、实际身份／个人记录清理、原收据与匿名结果查询。无真实邮件投递。
- 后台正式 Next 构建与实际 Chrome 17 项通过，包括任务集合和匿名权限。服务端清理调度仍只在隔离环境验证。
- Payload 后台全量 35/35，其中新增任务 8 项：隔离数据库的实际迁移、失败保留、官方重试、官方调度不重复排队和权限；任务中的网络故障使用替身，私有服务实际清理由上述后端专项单列证明。
- Go 新接口/官方代理/原身份 4 个定向测试通过；TestConsumer、TestViewer、TestBrowser 相关回归通过。Go 接口测试为实际路由配协议替身，单列于官方 HTTP+PG 证据。
- Web／任务子 Agent 发现启动时旧注销记录让新账号反复刷新的 P2；root 实际 Chrome 复现并最小修复，独立复跑 14 项接口、15 项浏览器及 1 个新增启动顺序后关闭。见[Web 注销与任务复审](dramivio-web-deletion-review.md)。
- 对抗复审发现并修复已结案邀请结果被注销重写的 P2；独立证据见[注销后端复审](dramivio-account-deletion-review.md)。

历史观看证据在奖励清理测试中使用明确的隔离夹具，验证的是原账务收据恢复，不代表真实供应商签名、前台心跳或正式奖励发行已接入。

## 待完成范围

App 当前账号的离线许可、密钥、文件与下载队列清理需要先建立明确归属；旧全局缓存不能按当前登录账号猜测归属。App 注销入口、真机断网／空间／后台任务测试、统一审计期限与正式常驻调度仍待验收。Web 与后台接线通过不代表完整 A03 或整个产品已完成。
