Files
nexus/Project/fonrey/DATA_MODEL/STATE_MACHINE.md
2026-06-04 14:34:32 +08:00

323 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
> For AI assistants: Read this entire file before writing any code. All decisions here are final.
# Fonrey — 统一状态机规范 (STATE_MACHINE)
**定位**:本文档是 Fonrey 项目所有实体生命周期与状态流转的唯一权威定义。
**变更历史**
| 日期 | 变更人 | 变更内容 |
|---|---|---|
| 2026-06-04 | Sisyphus | 初始版本:整合各模块状态机逻辑,对齐 ENUMS v2.2 |
---
## 1. 通用约定
- **状态值命名**:统一采用 `lower_snake_case`,必须与 `DATA_MODEL/ENUMS.md` 完全一致。
- **触发动作**状态迁移必须由显式的动作Action或事件Event触发。
- **操作日志**:所有关键状态迁移(除系统内部中间态外)必须记录对应的 `status_log``audit_log` 表。
- **不可逆规则**:标注为“终态”的状态禁止迁回初始态或中间态。
- **权限要求**:状态迁移接口必须校验操作者的 DataScope 与 Function Permission。
---
## 2. 租户生命周期 (Public.Tenant)
**实体**: `public.tenants` | **字段**: `status`
**参考**: `DATA_MODEL_PUBLIC.md` §2.1, §4.1
```mermaid
stateDiagram-v2
[*] --> creating
creating --> active: 初始化完成
creating --> failed: 初始化失败
active --> suspended: 逾期/违规/申请
suspended --> active: 恢复条件满足
active --> pending_delete: 申请注销
suspended --> pending_delete: 申请注销
pending_delete --> deleted: 30天后/人工确认
deleted --> [*]
```
### 状态枚举值
(domain: `public.tenant.status`)
- `creating`: 创建中
- `active`: 正常
- `suspended`: 已挂起
- `pending_delete`: 待删除
- `deleted`: 已删除
- `failed`: 创建/初始化失败
### 状态迁移表
| 当前状态 | 下一状态 | 触发动作/事件 | 触发角色 | 副作用 | 是否可逆 |
|---|---|---|---|---|---|
| `creating` | `active` | 租户 Schema 初始化成功 | System | 写 `tenant_status_logs` | 否 |
| `creating` | `failed` | 初始化脚本执行失败 | System | 记录错误日志 | 否 |
| `active` | `suspended` | 欠费/违规/License过期 | System/SuperAdmin | 设置 `suspended_until`, 写日志 | 是 |
| `suspended` | `active` | 缴费成功/解封/到期恢复 | System/SuperAdmin | 清除挂起原因 | 是 |
| `active`/`suspended` | `pending_delete` | 提交注销申请 | TenantAdmin | 设置 `deleted_at` | 是 |
| `pending_delete` | `deleted` | 确认注销/静默期结束 | SuperAdmin/System | 物理删除或深度标记 | 否 |
**来源章节**: `DATA_MODEL_PUBLIC.md` §4.1
---
## 3. 客源状态机 (Client)
**实体**: `client.clients` | **字段**: `status`
**参考**: `DATA_MODEL_CLIENT.md` §3.1, §4 | `ENUMS.md` §3.6
```mermaid
stateDiagram-v2
[*] --> buying
[*] --> renting
[*] --> buy_or_rent
state "活跃态" as Active {
buying
renting
buy_or_rent
}
Active --> suspended: 暂时无需求
suspended --> Active: 重新激活
Active --> public: 手动转公/超时自动
suspended --> public: 手动转公
Active --> bought: 转成交(我购)
Active --> rented_done: 转成交(我租)
Active --> invalid: 转无效
suspended --> invalid: 转无效
invalid --> Active: 经理审批恢复
bought --> [*]
rented_done --> [*]
public --> [*]
```
### 状态枚举值
(domain: `client.status`)
- `buying`: 求购
- `renting`: 求租
- `buy_or_rent`: 租购
- `suspended`: 暂缓
- `bought`: 已购 (我购)
- `rented_done`: 已租 (我租)
- `public`: 公客
- `invalid`: 无效
### 状态迁移表
| 当前状态 | 下一状态 | 触发动作/事件 | 触发角色 | 副作用 | 是否可逆 |
|---|---|---|---|---|---|
| `buying`/`renting`/`buy_or_rent` | `suspended` | 修改状态为暂缓 | 经纪人 | 写 `client_status_logs` | 是 |
| `suspended` | `buying`/`renting`/`buy_or_rent` | 重新激活需求 | 经纪人 | 更新 `last_active_at` | 是 |
| `Active`/`suspended` | `public` | 手动转公/超时自动转公 | 经纪人/System | 修改 `client_type='public'` | 否 |
| `Active` | `bought`/`rented_done` | 录入成交合同 | 经纪人 | 修改 `client_type='transacted'` | 否 |
| `Active`/`suspended` | `invalid` | 标记无效 | 经纪人 | 记录 `invalid_reason` | 需审批 |
### 禁迁规则
- `transacted` (bought/rented_done) 状态为绝对终态,禁止迁往任何其他状态。
- `public` 状态客源禁止直接修改为 `private` 状态下的需求状态,必须走领用流程。
**来源章节**: `DATA_MODEL_CLIENT.md` §4, `ENUMS.md` §3.6
---
## 4. 房源状态机 (Property)
**实体**: `property.properties` | **字段**: `status`
**参考**: `DATA_MODEL_PROPERTY.md` §3, §4.1
```mermaid
stateDiagram-v2
[*] --> unlisted
unlisted --> for_sale: 挂牌出售
unlisted --> for_rent: 挂牌出租
state "在盘态" as Listed {
for_sale
for_rent
for_sale_rent
}
Listed --> suspended: 业主暂缓
suspended --> Listed: 重新上架
Listed --> sold: 本司成交
Listed --> sold_elsewhere: 他售
Listed --> rented_elsewhere: 他租
Listed --> unlisted: 下架
sold --> [*]
sold_elsewhere --> [*]
```
### 状态枚举值
(domain: `property.status`)
- `for_sale`: 出售
- `for_rent`: 出租
- `for_sale_rent`: 租售
- `suspended`: 暂缓
- `sold_elsewhere`: 他售
- `rented_elsewhere`: 他租
- `sold`: 成交
- `unlisted`: 未挂牌
### 状态迁移表
| 当前状态 | 下一状态 | 触发动作/事件 | 触发角色 | 副作用 | 是否可逆 |
|---|---|---|---|---|---|
| `unlisted` | `for_sale`/`for_rent` | 房源挂牌 | 经纪人 | 新增 `listing_histories` | 是 |
| `Listed` | `suspended` | 业主通知暂缓 | 经纪人 | 写跟进日志 | 是 |
| `Listed` | `sold` | 本司录入成交 | 经纪人 | 自动结束挂牌历史 | 否 |
| `Listed` | `sold_elsewhere`/`rented_elsewhere` | 核实他司已成交 | 经纪人 | | 否 |
| `Listed` | `unlisted` | 强制下架 | 管理员 | | 是 |
**来源章节**: `DATA_MODEL_PROPERTY.md` §3
---
## 5. 客户/房源等级 (Grade)
**实体**: `client.clients` / `property.properties` | **字段**: `grade`
**参考**: `DATA_MODEL_CLIENT.md` §3.1 | `DATA_MODEL_PROPERTY.md` §4.1
### 状态枚举值
(domain: `client.grade` / `property.grade`)
- `A`: A (急迫)
- `B`: B (较强)
- `C`: C (一般)
- `D`: D (较弱)
- `E`: E (暂不关注) - 仅客源
### 状态迁移表
| 当前状态 | 下一状态 | 触发动作/事件 | 触发角色 | 副作用 | 是否可逆 |
| ---- | ------------------- | ------- | ---- | ----------------------------- | ---- |
| 任意状态 | `A`/`B`/`C`/`D`/`E` | 手动调整等级 | 经纪人 | 写 `status_log` / `follow_log` | 是 |
**来源章节**: `DATA_MODEL_CLIENT.md` §4, `DATA_MODEL_PROPERTY.md` §3
---
## 6. 账号状态机 (Account)
**实体**: `login.user_accounts` | **字段**: `status`
**参考**: `DATA_MODEL_LOGIN.md` §5.1
```mermaid
stateDiagram-v2
[*] --> active: 创建(初始密码)
active --> locked: 密码错误≥5次
locked --> active: 30min到期/手动解锁
active --> disabled: 离职/手动停用
disabled --> active: 复职/手动恢复
```
### 状态枚举值
(domain: `login.user_account.status`)
- `active`: 启用
- `disabled`: 停用
- `locked`: 锁定
### 状态迁移表
| 当前状态 | 下一状态 | 触发动作/事件 | 触发角色 | 副作用 | 是否可逆 |
|---|---|---|---|---|---|
| `active` | `locked` | 密码连续错误5次 | System | 设置 `locked_until` | 是 |
| `locked` | `active` | 时间到期/管理员解锁 | System/Admin | | 是 |
| `active` | `disabled` | 员工离职/手动停用 | Admin/System | 踢出所有 Session | 是 |
**来源章节**: `DATA_MODEL_LOGIN.md` §5.1
---
## 7. 升级事件状态机 (Upgrade Event)
**实体**: `public.upgrade_events` | **字段**: `status`
**参考**: `DATA_MODEL_PUBLIC.md` §4.2
```mermaid
stateDiagram-v2
[*] --> draft
draft --> pre_check: 提交
pre_check --> pre_backup: 健康检查通过
pre_backup --> batch_running: 备份完成
batch_running --> batch_done: 批次租户成功
batch_done --> batch_running: 下一批次
batch_done --> succeeded: 全部完成
batch_running --> halted: 失败/门控拦截
halted --> batch_running: 人工继续
halted --> rollback_running: 人工回滚
rollback_running --> rolled_back: 回滚完成
batch_running --> failed: 严重故障
```
### 状态枚举值
(domain: `public.upgrade_event.status`)
- `draft`: 草稿
- `pre_check`: 预检查
- `pre_backup`: 预备份
- `batch_running`: 批次执行中
- `batch_done`: 批次完成
- `halted`: 已暂停
- `succeeded`: 已成功
- `failed`: 失败
- `rollback_running`: 回滚中
- `rolled_back`: 已回滚
**来源章节**: `DATA_MODEL_PUBLIC.md` §4.2
---
## 8. 其他辅助状态
### 8.1 备份/导出任务 (Backup/Export)
**字段**: `status` | **参考**: `ENUMS.md` §2.3
- `pending` -> `in_progress` -> `success` / `failed`
### 8.2 客户端发布 (Client Release)
**字段**: `status` | **参考**: `ENUMS.md` §2.4
- `draft` -> `published` -> `archived`
- **约束**: 同平台架构下仅允许一个 `published`
### 8.3 员工状态 (Staff)
**字段**: `status` | **参考**: `DATA_MODEL_ORG.md` §4
- `probation` -> `active` -> `resigned`
- `active` <-> `frozen`
### 8.4 审批流 (Approval)
**实体**: `number_holder_approvals` | **字段**: `status`
- `pending` -> `approved` / `rejected`
---
## X. 状态机一致性校验清单
1. **枚举同步**: 状态字段值是否在 `ENUMS.md` 中定义且拼写一致?
2. **日志完备**: 每次 `status` 变更是否伴随对应的 `status_log` 插入动作?
3. **并发控制**: 状态变更是否包含 `version` 乐观锁检查?
4. **终态拦截**: 业务逻辑是否显式禁止从终态(如 `sold`, `deleted`, `rolled_back`)向外迁移?
5. **副作用触发**: 状态变更相关的异步任务(如转公客通知、缓存清理)是否已在 Service 层注册?
---
## 待澄清问题
1. **Client Invalid 恢复路径**: `DATA_MODEL_CLIENT.md` §4 提到“需经理审批后可恢复”,但未定义恢复后的目标状态(是恢复到 `suspended` 还是之前的活跃态?)。
- *涉及文件*: `DATA_MODEL_CLIENT.md`
2. **Property Unlisted 属性**: 房源在 `unlisted` 状态下,其 `attribute` (公/私盘) 是否有意义?若从 `private` 挂牌变为 `unlisted`,再次挂牌时是否应默认保留 `private` 属性?
- *涉及文件*: `DATA_MODEL_PROPERTY.md`
3. **Tenant Deleted 物理性**: `DATA_MODEL_PUBLIC.md` §4.1 提到“硬删除直接到 deleted”但在 §2.1 又说“硬删除直接物理删除行”。需要明确 `deleted` 状态是代表软删除的终点还是物理删除前的标记。
- *涉及文件*: `DATA_MODEL_PUBLIC.md`
4. **Staff Resigned 恢复**: 离职员工复职(`rejoin`)后,其 `status` 是回到 `probation` 还是 `active`
- *涉及文件*: `DATA_MODEL_ORG.md`