feat(account): self-service deletion with a 7-day cooling-off period
Request signs out everywhere and freezes paid routes (423 + DB triggers); signing in within 7 days shows the pending gate with 撤销注销. A periodic idempotent worker purges personal content afterwards and keeps finance rows against a tombstoned identity. Read-only admin list. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
3c8123d912
commit
96adbce3a9
@@ -0,0 +1,50 @@
|
||||
# PROGRESS — 自助注销账号 + 7 天冷静期(2026-09-30)
|
||||
|
||||
分支 `codex/account-deletion-20260930`,基线 `codex/compliance-base-20260930`(2cd37720,含 `lib/legal-entity.ts` 占位主体)。产品决定:注销 = 7 天冷静期后删除;联系方式先用占位。
|
||||
|
||||
## 做了什么
|
||||
|
||||
| 部分 | 位置 |
|
||||
| --- | --- |
|
||||
| 迁移(只加) | `frontend/supabase/migrations/20260930020000_account_deletion_requests.sql`:表 `account_deletion_requests`、函数 `request_account_deletion` / `cancel_account_deletion` / `account_deletion_scheduled_for` / `purge_deleted_account` / `mark_account_deletion_attempt_failed`、扣点拦截触发器 |
|
||||
| API | `GET/POST/DELETE /api/account/deletion`(POST 同源校验 + 必须 `confirm:"注销"`;管理员账号 403) |
|
||||
| 冻结 | 咨询、报告、校正 agent、打开校正、合盘五条付费路由认证后先查,注销中返回 423 `account_deletion_pending`;数据库层 `usage_reservations`、`birth_time_rectification_billing`、`credit_transactions`(amount < 0)BEFORE INSERT 触发器同码拒绝 |
|
||||
| 前端 | 通用设置底部「注销账号」区;`(app)` 布局的「账号注销中」全屏门(撤销注销 / 退出登录) |
|
||||
| 后台 | `/admin/account-deletions` 只读列表(`admin.customers.read`,不显示邮箱) |
|
||||
| 清除任务 | `lib/account-deletion-worker.ts`,由 `instrumentation.ts` 启动 |
|
||||
|
||||
## 删除 vs 去标识
|
||||
|
||||
- **删除**:`public` 下所有以 `user_id` 指向 `auth.users` 的表(外键自动发现 + 带 uuid `user_id` 列的表),以及 `profiles`(按 `id`)。包括对话、星盘档案、个人报告及其任务/分节、校正 case、合盘、记忆等。`created_by` / `updated_by` 这类运营配置列不算用户内容,不删。
|
||||
- **保留并去标识**(`account_deletion_kept_tables()`,前后端同一份名单,测试比对):`payment_orders`、`credit_transactions`、`usage_ledger`、`usage_reservations`、`user_subscriptions`、`user_product_redemptions`、`redemption_attempts`、`redemption_codes`、`credit_request_cancellations`、`birth_time_rectification_billing`、`consultation_requests`、`pricing_experiment_events`、`account_deletion_requests`、`admin_*` 三张。
|
||||
- **身份墓碑**:`identity.users` / `auth.users` 行保留(财务表外键是 cascade,删身份会连带删账),邮箱改为 `deleted+<id>@deleted.invalid`,姓名「已注销用户」,封禁;`sessions`、`accounts`、`two_factors`、`verifications` 删除。
|
||||
- 全部在一个事务里:删不干净(多轮重试外键顺序后仍有剩余)就整体回滚,记 `purge_incomplete`,下次再试。
|
||||
|
||||
## 清除任务怎么跑
|
||||
|
||||
进程启动 60 秒后第一次,之后每 30 分钟一次(`unref` 定时器,globalThis 防重)。每次取最多 20 条到期、`attempt_count < 5` 的 pending 申请,逐条调 `purge_deleted_account`;函数对未到期 / 已撤销 / 已完成返回 skipped,可重复执行。失败记错误码与次数,满 5 次停止重试,后台列表可见。日志只写计数,不写 id 或邮箱。
|
||||
|
||||
## 验证
|
||||
|
||||
| 项 | 结果 |
|
||||
| --- | --- |
|
||||
| `tsc --noEmit` | 0 错 |
|
||||
| `npm run lint` | 0 error / 126 warning(与基线同) |
|
||||
| 新单测 `tests/account-deletion.test.tsx` | 8/8 |
|
||||
| 新 DB 测 `tests/database-account-deletion.test.ts` | 本机无 Docker,skipped |
|
||||
| 全量 `npm test` | 4427 项,fail 25 = 基线 24 条环境失败(同名)+ 1 条合同测试;改后该条通过 |
|
||||
| `next build` | 通过,`/` ○ Static |
|
||||
| 首屏 gzip | 648,688 B(基线约 646,480,+0.34%) |
|
||||
|
||||
改动的既有断言(`frontend/tests/settings-mvp-contract.test.ts`):
|
||||
|
||||
| 原值 | 新值 | 原因 |
|
||||
| --- | --- | --- |
|
||||
| `renderGeneral() { return <ThemePreferencePanel />;` | `return <><ThemePreferencePanel /><AccountDeletionSection /></>;` | 注销入口放通用设置底部,头像菜单不加入口 |
|
||||
|
||||
## 未验证 / 待办
|
||||
|
||||
- DB 测试(请求→会话清空→扣点被拒→撤销→到期清除→内容删、流水留、身份墓碑→重复执行无副作用)需 Docker 跑 `npm run test:db`。
|
||||
- 迁移只在 staging 部署时首次真实应用;清除任务真实执行需等 7 天或在 staging 库手工把 `scheduled_for` 调早。
|
||||
- 无受控 staging 账号,端到端(注销→退出→重登见门→撤销)未走。
|
||||
- 与 fork C(反馈表)若新增带 `user_id` 的表,会被自动归入删除;如需保留投诉记录,需加入保留名单。
|
||||
Reference in New Issue
Block a user