UserStatus 迁移、DeviceAuth 拒绝非 active、admin-h5 CTA; Reviewer Closed。Human 授权 LOOP_AUTHORIZATION(免逐闸确认)。 Co-authored-by: Cursor <cursoragent@cursor.com>
157 lines
5.4 KiB
Markdown
157 lines
5.4 KiB
Markdown
# Feature Spec: 账户生命周期 / UserStatus(Ops · ECR-013B)
|
||
|
||
> Status: `Active`(**Closed** · Feature Complete)· Map: `§7 运营后台` · Phase: `Ops-D`
|
||
> Parent: **WAVE0-FROZEN** (`27f27a1`) · Predecessor: **ECR-013A Closed**
|
||
> ESS: `docs/ECR/ECR-013B-account-lifecycle.md`(**Closed**)
|
||
> Capability: `AccountLifecycle` · BC: `Identity_Profile`
|
||
> 模板:`docs/WAVE0/contracts/OPS-CONTRACT-TEMPLATE.md`
|
||
|
||
---
|
||
|
||
## ESS 门禁
|
||
|
||
1. Change Level = **L2** → 已 Approved + Coding auth + Implemented + **Closed**
|
||
2. ~~Contract Definition only~~ — 已完成
|
||
3. 实现证据:`docs/TEST_REPORT/ECR-013B.md` · `docs/CODE_REVIEW/ECR-013B.md`
|
||
|
||
---
|
||
|
||
## L0 Capability
|
||
|
||
| 字段 | 内容 |
|
||
|------|------|
|
||
| Capability ID | `AccountLifecycle` |
|
||
| Purpose | 以可审计状态机管理 C 端用户账户启停与封禁,阻断违规会话 |
|
||
| Why now | `users.status` 字段已存在但无运营迁移与强制拒绝;013A 权限面就绪后可挂状态写权限 |
|
||
| Non-goals | soft-delete / `deleted`;UGC 社区封禁;真支付;Admin 账号启停(已有 admin.status);推送通知 |
|
||
|
||
---
|
||
|
||
## L1 Bounded Context
|
||
|
||
| 字段 | 内容 |
|
||
|------|------|
|
||
| Primary BC | `Identity_Profile` |
|
||
| owns | `User` · `UserStatus` · `AccountStateTransition` |
|
||
| does_not_own | `AdminRole` · `Membership` · `Payment` · `BanRecord`(Account_Risk 后置加深) |
|
||
| allowed | `Admin_Auth_Audit.write_audit`(经 admin 调用) |
|
||
| forbidden | `Payment` · `Membership.write` · soft-delete User |
|
||
|
||
权威:`.ai/domain/boundary-rules.md` · `Account_Risk` 仅允许经明确服务触发 `UserStatus.transition`。
|
||
|
||
---
|
||
|
||
## 1. 功能定义
|
||
|
||
| 字段 | 内容 |
|
||
|------|------|
|
||
| Name | Account Lifecycle / UserStatus |
|
||
| Purpose | 运营可迁移用户状态;非 active 会话在 C 端被拒绝 |
|
||
| Business Goal | 风险处置最小闭环,不引入社区/UGC |
|
||
|
||
| In | Out |
|
||
|---|---|
|
||
| 状态机 `active/disabled/banned/suspended` | soft-delete / GDPR 擦除 |
|
||
| Admin 迁移 API + AuditLog + Transition 记录 | 站内推送 / 短信 |
|
||
| DeviceAuth / Bearer 对非 active 拒绝 | AdminAccount.status(013A 外) |
|
||
| 权限码 `admin.users.status.write` | 行级数据 ACL |
|
||
|
||
---
|
||
|
||
## L2 Domain
|
||
|
||
| Entity | 不变式 / 状态机 |
|
||
|--------|----------------|
|
||
| `UserStatus` | 取值冻结:`active` · `disabled` · `banned` · `suspended`;非法值拒写 |
|
||
| `User.status` | 与 `UserStatus` 同值;默认 `active`(已有列) |
|
||
| `AccountStateTransition` | 只追加;记录 from→to · admin_id · reason · created_at |
|
||
|
||
### 合法迁移
|
||
|
||
```text
|
||
active → disabled | banned | suspended
|
||
disabled → active | banned
|
||
suspended → active | banned | disabled
|
||
banned → active | disabled
|
||
```
|
||
|
||
其它边 → **400**。同状态写 → **400**(幂等拒绝,避免空审计噪音)。
|
||
|
||
### C 端效应
|
||
|
||
| Status | DeviceAuth / 已登录 Bearer |
|
||
|--------|------------------------------|
|
||
| `active` | 放行 |
|
||
| `disabled` / `banned` / `suspended` | **401**(或 403 统一码,实现轮定一)+ 不可发新 session |
|
||
|
||
---
|
||
|
||
## L3 API Contract(意图 · 实现轮同步 OpenAPI)
|
||
|
||
前缀:`/api/v1/admin` · AdminAuth · 信封 `{code,message,data}`
|
||
|
||
| Method | Path | 权限 | 语义 |
|
||
|--------|------|------|------|
|
||
| POST | `/users/:id/status` | `admin.users.status.write` | 迁移 UserStatus;写 Transition + AuditLog |
|
||
| GET | `/users/:id/status-transitions` | `admin.users.read` | 最近迁移列表(limit) |
|
||
|
||
既有 `GET /users` · `GET /users/:id` 已暴露 `status` — 保持;实现轮确认枚举文档化。
|
||
|
||
`contract_diff`:`docs/CONTRACT_DIFF/ECR-013B.yaml`
|
||
|
||
RBAC catalog **additive**:`admin.users.status.write` → 种子写入 `super_admin`(migration)。
|
||
|
||
---
|
||
|
||
## L4 Acceptance Criteria
|
||
|
||
### Functional
|
||
|
||
| ID | Given | When | Then |
|
||
|----|-------|------|------|
|
||
| AC-F-01 | 用户 `active` | POST status=`banned` + reason | 200;GET user.status=`banned` |
|
||
| AC-F-02 | 用户 `banned` | POST status=`active` | 200;可再次 DeviceAuth |
|
||
| AC-F-03 | 非法边(如 `active`→`active`) | POST | **400** |
|
||
| AC-F-04 | GET status-transitions | — | 含最近 from/to/admin/reason |
|
||
|
||
### Security
|
||
|
||
| ID | Given | When | Then |
|
||
|----|-------|------|------|
|
||
| AC-S-01 | Admin 无 `admin.users.status.write` | POST status | **403** + deny audit |
|
||
| AC-S-02 | 用户 `banned` | C 端带原 Bearer 访问受保护 API | **401/403** |
|
||
| AC-S-03 | 仅 DeviceAuth 无 Admin | POST `/admin/users/:id/status` | **401** |
|
||
| AC-S-04 | 无 Admin session | GET transitions | **401** |
|
||
|
||
### Performance
|
||
|
||
| ID | Given | When | Then |
|
||
|----|-------|------|------|
|
||
| AC-P-01 | transitions ≤1000 行/用户 | GET transitions limit=50 | 本机 P95 **< 500ms** |
|
||
|
||
### Observability
|
||
|
||
| ID | Given | When | Then |
|
||
|----|-------|------|------|
|
||
| AC-O-01 | 迁移成功 | — | AuditLog action=`users.status.transition` |
|
||
| AC-O-02 | 迁移成功 | — | `account_state_transitions` 有对应行 |
|
||
|
||
---
|
||
|
||
## Forbidden(本切片)
|
||
|
||
- soft-delete / `deleted` 状态
|
||
- 真支付 · UGC · Prompt · 兑换码 · Crisis 深化
|
||
- 改 Admin RBAC 模型(仅 **additive** 一枚 permission)
|
||
- 自动开下一 ECR
|
||
|
||
---
|
||
|
||
## Implementation Notes(实现轮才执行)
|
||
|
||
| 项 | 内容 |
|
||
|----|------|
|
||
| Migration | YES(transitions 表;permission 种子;必要时 CHECK/注释枚举) |
|
||
| Packages | service account/lifecycle · DeviceAuth/session 拒绝 · admin handler · admin-h5 用户详情最小 CTA |
|
||
| Depends | ECR-013A Closed(permission middleware) |
|