← 返回文档导航
状态 设计评审中
触发端 App + WhatsApp
删除口径 全链路物理删除
目标号码 WhatsApp 官号复用
实现服务 core service
本方案定义 BeeX 账号注销流程:用户在 App 内输入登录密码完成交互确认,后端创建注销申请并返回注销验证码;App 携带验证码跳转 WhatsApp,用户把验证码发送给 BeeX WhatsApp 官号;后端通过 WhatsApp webhook 收到用户消息后校验验证码和发送方,执行账号全链路物理删除。
注意:/api/v1/user/security/login-password/verify 只负责验证密码是否正确,不返回 verificationToken,也不作为后端创建注销申请的强校验前置;是否允许继续创建注销申请由 App 交互控制。
1 · 目标与边界
本期目标
- App 点击注销账号后,先弹登录密码确认弹窗。
- 密码验证通过后,App 调后端创建注销申请。
- 后端返回注销验证码和 WhatsApp 跳转链接。
- 用户通过 WhatsApp 把验证码发给 BeeX WhatsApp 官号。
- webhook 收到用户消息后校验发送方和验证码,执行全链路物理删除。
明确边界
login-password/verify 不创建后端意图,不返回 token。
account-deletion/intents 不校验密码验证意图。
- 本方案按用户确认的“全链路物理删除”设计。
- 删除会影响历史订单、佣金、邀请关系、钱包、提现、风控和财务记录。
- 删除成功后账号不可恢复。
高风险说明:全链路物理删除会破坏历史财务、邀请统计和审计追溯。后端必须把这个能力限制在“用户发起 + 用户 WhatsApp 发送方匹配 + 删除验证码 + webhook 签名校验 + 幂等删除”的闭环内。
2 · 主流程
sequenceDiagram
participant App
participant API as BeeX API
participant DB
participant WA as WhatsApp
participant Official as BeeX WA官号
participant Webhook as /webhooks/whatsapp
App->>API: POST /api/v1/user/security/login-password/verify
API-->>App: verified=true / false
App->>API: POST /api/v1/account-deletion/intents
API->>DB: 创建 PENDING 注销申请 + 生成 deletionCode
API-->>App: intentId + deletionCode + whatsappUrl + expiresAt
App->>WA: 打开 whatsappUrl,带 DELETE deletionCode 文案
WA->>Official: 用户发送注销验证码
Official->>Webhook: Meta webhook 推送用户消息
Webhook->>DB: 校验 from 匹配用户 WhatsApp/手机号 + PENDING 验证码
Webhook->>DB: 事务执行全链路物理删除
Webhook-->>Official: 回复用户删除成功 / 失败原因
App 交互要求:只有 verified=true 才展示“继续注销”或直接调用创建注销申请;如果 verified=false,停留在密码弹窗并提示密码错误。后端不强绑定这两步。
3 · 接口草案
3.1 校验登录密码
| 项目 | 说明 |
| 接口 | POST /api/v1/user/security/login-password/verify |
| 调用方 | App / H5 我的-账号安全-注销账号 |
| 作用 | 只验证当前用户输入的登录密码是否正确。 |
| 后端状态 | 不创建注销意图,不返回 verificationToken,不写密码验证流水。 |
| 参数位置 | 参数名 | 类型 | 必填 | 说明 |
| Header / Query | X-Beex-User-Id / userId | string | 是 | 当前登录用户 ID,沿用现有用户安全接口解析方式。 |
| Body | password | string | 是 | 用户输入的登录密码。 |
| 返回字段 | 类型 | 说明 |
data.verified | boolean | 密码是否验证通过。 |
3.2 创建注销申请
| 项目 | 说明 |
| 接口 | POST /api/v1/account-deletion/intents |
| 调用方 | App 在密码验证通过后调用。 |
| 作用 | 创建一次注销申请,生成验证码和 WhatsApp 跳转链接。 |
| 后端校验 | 校验用户存在、状态可注销、没有未完成注销申请;不校验密码验证意图。 |
| 参数位置 | 参数名 | 类型 | 必填 | 说明 |
| Header / Query | X-Beex-User-Id / userId | string | 是 | 当前登录用户 ID。 |
| Body | reason | string | 否 | 用户选择或填写的注销原因。 |
| Body | deviceId | string | 否 | 发起注销的设备 ID,用于审计。 |
| 返回字段 | 类型 | 说明 |
data.intentId | string | 注销申请 ID。 |
data.deletionCode | string | 注销验证码,例如 BX-DEL-8K3P2Q。 |
data.whatsappUrl | string | 跳转 BeeX WhatsApp 官号的链接,文本预填 DELETE {deletionCode}。目标号码复用 seahub.whatsapp.official-number。 |
data.expiresAt | string | 验证码过期时间。 |
4 · WhatsApp 用户确认
flowchart TD
IN["用户 WA 消息进入 /webhooks/whatsapp"]:::in --> DUP["现有 wa_message_id 幂等落库"]:::step
DUP --> PARSE{"文本是否匹配 DELETE BX-DEL-xxxx?"}:::decision
PARSE -->|"否"| BOT
PARSE -->|"是"| FIND["查 account_deletion_intents
PENDING + 未过期 + code 匹配"]:::step
FIND --> HIT{"是否命中?"}:::decision
HIT -->|"否"| FAIL["回复:验证码无效或已过期"]:::fail
HIT -->|"是"| SENDER{"from 是否匹配注销用户
WhatsApp/手机号?"}:::decision
SENDER -->|"否"| BOT["交给现有 Bot 路由"]:::step
SENDER -->|"是"| DEL["执行全链路物理删除"]:::danger
DEL --> OK["标记 DELETED + 回复成功"]:::ok
classDef in fill:#eff6ff,stroke:#3b82f6,color:#1e40af;
classDef step fill:#ffffff,stroke:#e8d9b8,color:#1a1410;
classDef decision fill:#fef3c7,stroke:#e07c00,color:#7a4f24,stroke-width:2px;
classDef fail fill:#fff7ed,stroke:#fb923c,color:#9a3412;
classDef danger fill:#fff5f5,stroke:#dc2626,color:#991b1b,stroke-width:2px;
classDef ok fill:#f0fdf4,stroke:#15803d,color:#15803d;
| 规则 | 说明 |
| 目标号码 | 复用 WhatsApp 登录的官号配置 seahub.whatsapp.official-number,不新增注销专用号码配置。 |
| 发送方校验 | webhook 入站 from 必须匹配注销申请用户的 whatsapp_number 或 phone,否则不执行删除。 |
| 消息格式 | 建议固定 DELETE BX-DEL-XXXXXX,避免误触。 |
| 验证码 TTL | 建议 10 分钟,过期后 intent 置为 EXPIRED 或查询时视为过期。 |
| 幂等 | 同一 wa_message_id 只能处理一次;同一 intent 只有 PENDING 才能删除。 |
| 回复 | 成功、失败、过期都向用户 WhatsApp 号回复明确结果。 |
5 · 全链路物理删除范围
执行策略:先根据 userId 收集订单 ID、支付 ID、提现 ID、申诉 ID 等二级关联 ID,再在同一个事务里按依赖顺序删除。不能先删 users,否则后续关联数据无法完整定位。
| 域 | 表 | 删除条件 |
| 账号主体 | users | id = userId,最后删除。 |
| 认证与会话 | user_identities、user_sessions、phone_otp_intents、provider_sms_messages | user_id = userId 或通过 OTP intent 关联。 |
| WhatsApp | whatsapp_login_intent、whatsapp_users、wa_inbound_messages、wa_outbound_messages | 按 user_id 和用户手机号 / WhatsApp 号删除。 |
| 分享与点击 | share_logs、share_clicks、affiliate_clicks | share_user_id、click_user_id、user_id 命中。 |
| 订单与佣金 | affiliate_orders、platform_order_index、commission_records | 用户作为买家、受益人或来源用户时全部删除。 |
| 活动奖励 | activity_campaign_reward_records | user_id = userId。 |
| 钱包与支付 | user_wallets、wallet_ledger、payment_orders、payment_webhook_events | 用户钱包、用户支付单及关联 webhook 事件。 |
| 提现 | payout_profiles、withdraw_requests、payout_transactions | user_id = userId,以及提现关联出款交易。 |
| 邀请关系 | referral_relation、referral_closure、team_daily_stats | 用户作为本人、父级、根节点、祖先、后代时全部删除。 |
| 风控与申诉 | risk_events、cashback_claims、claim_attachments | user_id = userId,附件按 claim ID 删除。 |
| 审计与其他 | account_merge_logs、financial_journal、h5_package_update_logs | 用户作为来源、目标、操作者或账务主体时删除。 |
5.1 · 物理删除的唯一例外:活动参加账本
注意:上表把 activity_campaign_reward_records、risk_events 等参加/风控痕迹一并物理删除。这会导致「每身份限参加一次」的活动(新人券、首单礼、限领券…)被
注销→同手机号重注册反复参加(对系统是全新 userId、全新数据)。
唯一保留项:campaign_participation_ledger(活动参加账本)不参与物理删除。
它只存
脱敏哈希(手机号 HMAC)+ 活动范围键,不含可识别个人信息,属于反欺诈最小留存,是删除权的合法例外。
限次活动执行前查它、命中即拦,杜绝注销重注册绕过限次。详见
活动「每身份限参加 N 次」· 跨账号去重设计。
6 · 表结构草案
| 字段 | 类型 | 说明 |
id | varchar(64) | 注销申请 ID。 |
country_code | varchar(8) | 国家码。 |
user_id | varchar(64) | 发起注销的用户。 |
deletion_code | varchar(32) | 注销验证码,唯一。 |
status | varchar(32) | PENDING / DELETED / EXPIRED / FAILED。 |
reason | varchar(255) | 用户填写或选择的原因。 |
device_id | varchar(128) | 发起设备。 |
expires_at | datetime | 验证码过期时间。 |
confirmed_by_whatsapp_number | varchar(32) | 确认删除的用户 WA 号。 |
confirmed_wa_message_id | varchar(128) | 确认消息 ID,用于幂等审计。 |
delete_started_at | datetime | 开始删除时间。 |
deleted_at | datetime | 删除完成时间。 |
failure_reason | varchar(512) | 失败原因。 |
created_at / updated_at | datetime | 创建和更新时间。 |
这张表是删除动作的最小审计凭证。由于用户要求全链路物理删除,建议保留该表记录;否则删除完成后无法证明是谁在什么时间确认了删除。
7 · 安全与风险控制
| 风险 | 控制方案 | 优先级 |
| 伪造 WhatsApp webhook | 补 X-Hub-Signature-256 HMAC-SHA256 校验,密钥为 seahub.whatsapp.meta-app-secret。 | 必须 |
| 他人触发删除 | 删除验证码命中后,还要校验入站 from 是否匹配注销申请用户的 WhatsApp/手机号。 | 必须 |
| 误触删除 | 验证码必须带固定前缀和命令,例如 DELETE BX-DEL-XXXXXX。 | 必须 |
| 重复删除 | wa_message_id 幂等 + intent 状态机,只有 PENDING 可执行。 | 必须 |
| 删除中断 | 单事务删除;异常时回滚并标记 intent 为 FAILED。 | 必须 |
| 注销重注册绕过限次活动 | 物理删除抹掉参加痕迹后,同手机号重注册可反复参加「限一次」活动。控制方案:保留campaign_participation_ledger活动参加账本(脱敏哈希)做跨账号去重,见 活动限参加次数设计。 | 必须 |
| 历史财务破坏 | 产品已确认全链路物理删除;上线前需要业务、财务、风控共同确认。 | 评审确认 |
8 · 验收点
| 场景 | 期望结果 |
| 密码错误 | login-password/verify 返回 verified=false,App 不继续创建注销申请。 |
| 密码正确 | 返回 verified=true,App 可创建注销申请。 |
| 创建注销申请 | 返回 deletionCode 和 whatsappUrl,表中状态为 PENDING。 |
| 他人发验证码 | webhook 不执行删除,消息交给现有 Bot 路由。 |
| 本人发错误验证码 | 不执行删除,回复验证码无效或已过期。 |
| 本人发正确验证码 | 执行全链路物理删除,intent 标记 DELETED。 |
| Meta 重推同一消息 | 不重复执行删除。 |
| 删除后登录 | 用户无法通过手机号、WhatsApp、密码或会话继续登录。 |
| 删除后查询 | 用户、订单、佣金、钱包、提现、邀请关系等用户相关数据按删除范围消失。 |