134 lines
4.6 KiB
Markdown
134 lines
4.6 KiB
Markdown
|
|
---
|
||
|
|
title: prop-acc · deposit · 押金账户状态机
|
||
|
|
aliases:
|
||
|
|
- 押金账户状态机
|
||
|
|
- DepositAccount 状态机
|
||
|
|
- Active / Frozen / Closed
|
||
|
|
tags:
|
||
|
|
- 概念
|
||
|
|
- prop-acc
|
||
|
|
- 保证金
|
||
|
|
- 状态机
|
||
|
|
audience:
|
||
|
|
- 业户
|
||
|
|
- 业务人员
|
||
|
|
status: 已发布
|
||
|
|
sub_feature: deposit
|
||
|
|
last_review: 2026-05-25
|
||
|
|
code_version: 2026-05-22
|
||
|
|
---
|
||
|
|
|
||
|
|
# 押金账户状态机
|
||
|
|
|
||
|
|
押金账户三种状态:**Active(在押)** / **Frozen(冻结)** / **Closed(已结清)**。
|
||
|
|
|
||
|
|
> [!warning] 重要原则
|
||
|
|
> 一旦 Closed,永远 Closed。**不允许重开**。新业务一律开新账户。理由见本文末"为什么不允许 reopen"。
|
||
|
|
|
||
|
|
## 三状态速查
|
||
|
|
|
||
|
|
| 状态 | 中文 | 何时进入 | 能做什么 |
|
||
|
|
|---|---|---|---|
|
||
|
|
| `Active` | 在押 | 新账户首次缴款后 | 缴款 / 退款 / 扣罚 / 冻结 / 关账(余额 0) |
|
||
|
|
| `Frozen` | 冻结 | 发生纠纷、内审等 | 看流水(只读)/ 解冻 / 强制关账 |
|
||
|
|
| `Closed` | 已结清 | 余额清零正常关账 OR ForceClose | 看流水(只读),没有任何可写操作 |
|
||
|
|
|
||
|
|
## 状态机图
|
||
|
|
|
||
|
|
```mermaid
|
||
|
|
stateDiagram-v2
|
||
|
|
[*] --> Active : 开户 + 首次缴款
|
||
|
|
Active --> Active : 追加缴款 / 部分退款 / 扣罚
|
||
|
|
Active --> Frozen : freeze() 纠纷/审计
|
||
|
|
Frozen --> Active : unfreeze() 调解完成
|
||
|
|
Active --> Closed : close() 余额=0
|
||
|
|
Frozen --> Closed : forceClose() 强制结账
|
||
|
|
Closed --> [*]
|
||
|
|
|
||
|
|
note right of Frozen
|
||
|
|
冻结期间禁止任何资金进出
|
||
|
|
canDeposit = false
|
||
|
|
canWithdraw = false
|
||
|
|
end note
|
||
|
|
|
||
|
|
note right of Closed
|
||
|
|
永久终态
|
||
|
|
canBeReopened = false
|
||
|
|
新业务请开新账户
|
||
|
|
end note
|
||
|
|
```
|
||
|
|
|
||
|
|
## 守护方法(代码层)
|
||
|
|
|
||
|
|
`DepositAccount` 模型上有一组 `can*()` 守护方法,**所有写入 Action 必须先调用**:
|
||
|
|
|
||
|
|
| 方法 | 返回 true 的状态 | 用途 |
|
||
|
|
|---|---|---|
|
||
|
|
| `canDeposit()` | Active **only** | DepositAction 准入 |
|
||
|
|
| `canWithdraw()` | Active **only** | RefundAction / ForfeitureAction 准入 |
|
||
|
|
| `canBeFreezed()` | Active | FreezeAction 准入 |
|
||
|
|
| `canBeUnfreezed()` | Frozen | UnfreezeAction 准入 |
|
||
|
|
| `canBeClosed()` | balance=0 且 ≠Closed | CloseAction 准入 |
|
||
|
|
| `canBeReopened()` | **永远 false** | 占位,刻意禁止 |
|
||
|
|
| `canOperate()` | Active | 复合判断:既不在 Frozen 也不在 Closed |
|
||
|
|
| `hasBalance()` | balance>0 | 配合判定能否 Close / 是否需 ForceClose |
|
||
|
|
| `isAvailable()` | Active | UI 显示"可用"或灰化 |
|
||
|
|
|
||
|
|
> [!info] 关键守护:Frozen 不允许任何资金动作
|
||
|
|
> `canDeposit()` 与 `canWithdraw()` **都只允许 Active**,Frozen 一律拒绝。这条规则比直觉更严:
|
||
|
|
>
|
||
|
|
> - 原本曾允许 `[Active, Frozen]` 都能缴款(看着"反正多存钱不亏")
|
||
|
|
> - 但与"冻结 = 暂停所有交易"的语义矛盾
|
||
|
|
> - 真实风险:纠纷期间装修公司继续往受冻结账户灌钱 → 资金被困、责任更复杂
|
||
|
|
>
|
||
|
|
> 现在两个方向严格一致:Frozen = 完全冻结,只能解冻或 ForceClose。
|
||
|
|
|
||
|
|
## 业务人员视角
|
||
|
|
|
||
|
|
后台账户列表的"状态"列对应这三个值。
|
||
|
|
|
||
|
|
- 看到 `Active`:绿色,可点开操作
|
||
|
|
- 看到 `Frozen`:橙色,所有按钮变灰,只剩 `Unfreeze` / `ForceClose`
|
||
|
|
- 看到 `Closed`:灰色,完全只读,只能看流水
|
||
|
|
|
||
|
|
## 业户视角
|
||
|
|
|
||
|
|
业户**通常感受不到状态机**,只感受到结果:
|
||
|
|
|
||
|
|
- 余额能正常用 → Active
|
||
|
|
- 申请退款被拒,前台告知"账户冻结中,等纠纷处理完才能动" → Frozen
|
||
|
|
- 账户被关 → 收到一张红字收据 + 短信告知"您的押金账户已结清"
|
||
|
|
|
||
|
|
## 为什么不允许 Reopen
|
||
|
|
|
||
|
|
`canBeReopened()` 永远返回 `false`,是**刻意的设计**。
|
||
|
|
|
||
|
|
| 假设允许 reopen | 风险 |
|
||
|
|
|---|---|
|
||
|
|
| Closed → Active 又开放 | 流水台账"已结清"的语义被破坏,审计难追责 |
|
||
|
|
| 业务方:"业户搬回来了就 reopen 老账户" | 鼓励混账;两次业务关系应有清晰边界 |
|
||
|
|
| 系统级:"误操作 close 了能反悔" | close 已经守护"余额 0",误操作不会丢钱;新业务开新账户即可 |
|
||
|
|
|
||
|
|
替代做法:业户搬回来续约,**开新账户**。旧账户保留作为历史台账,与"曾经的业务关系结束"语义一致。
|
||
|
|
|
||
|
|
## 异常路径:Frozen + 有余额 + 想关账?
|
||
|
|
|
||
|
|
矛盾:
|
||
|
|
|
||
|
|
- `Frozen` 不允许 `withdraw`(包括退款 / 扣罚)
|
||
|
|
- `canBeClosed()` 要求余额=0
|
||
|
|
- 又不能 unfreeze 直接关
|
||
|
|
|
||
|
|
这种困境通过 **ForceClose** 解决,见 [[force-close-refund]] / [[force-close-forfeit]] / [[force-close-retain]] 三种 disposition。
|
||
|
|
|
||
|
|
ForceClose 是**唯一**能合法从 Frozen 直接到 Closed 的路径,通过专门的 `DepositAccountPolicy::forceClose()` 守护(`update` 权限 + `isFrozen() && hasBalance()`)。
|
||
|
|
|
||
|
|
## 相关文档
|
||
|
|
|
||
|
|
- [[deposit-account-vs-transaction]]
|
||
|
|
- [[transaction-types]]
|
||
|
|
- [[freeze-during-dispute]]
|
||
|
|
- [[unfreeze-after-mediation]]
|
||
|
|
- [[close-after-zero-balance]]
|
||
|
|
- [[force-close-refund]]
|