vault backup: 2026-05-25 22:32:40

This commit is contained in:
Willie
2026-05-25 22:32:40 +08:00
parent 963d432d62
commit 68ea6dfbe9
6 changed files with 833 additions and 5 deletions

View File

@@ -0,0 +1,165 @@
---
title: prop-acc · deposit · 场景 - 余额清零自动关闭
aliases:
- 押金账户自动关账
- 余额 0 自动 Closed
- close-after-zero-balance
- 场景-押金账户自动关闭
tags:
- 场景
- prop-acc
- 保证金
- 结清
audience:
- 业户
- 业务人员
status: 已发布
sub_feature: deposit
last_review: 2026-05-25
code_version: 2026-05-22
---
# 场景:余额清零自动关闭
押金账户在**最后一笔操作**(退款 / 扣罚)使余额变 0 的瞬间,系统**自动**把账户状态翻为 `Closed`,无需手工再点"关账"。
## 典型情境
> [!example] 真实情境(三种触发)
>
> **情境 A**:张阿姨家装修无损坏,物业全额退 ¥5,000 → 余额 0 → 账户**自动 Closed**。
>
> **情境 B**:陈先生家装修部分损坏,先扣 ¥800,再退余下 ¥4,200 → 余额 0 → 账户**自动 Closed**。
>
> **情境 C**:刘先生违约严重,全部 ¥5,000 全扣无退 → 余额 0 → 账户**自动 Closed**。
## 业户视角
### 您会感受到什么
- 收到**最后一张红字收据**(可能是退款 / 也可能是扣罚)
- 同时收到通知:"您的押金账户已结清,余额 0"
- 小程序"我的押金账户"显示 "🔒 已结清"
- 流水台账仍可查(只读),整张账户的历史可追溯
### 您要做什么
- 妥善保管所有收据(缴款蓝字 + 历次退款 / 扣罚红字)
- **不要试图重启账户** —— 系统设计上不允许;新业务请联系物业开新账户
- 如有任何关于过往流水的疑问 → 联系物业核对(凭收据 + 流水台账)
## 业务人员视角
### 关键认知:不需要手工关账
> [!info] 自动机制
> 任何 `RefundAction` / `ForfeitureAction` 提交后,系统在事务尾部检查 `balance == 0`,**自动**把 `status` 从 `Active` 翻到 `Closed`。
>
> 你不需要(也不应该)在余额变 0 后还跑一次 `CloseAction` —— 那个按钮 UI 上会被守护 `canBeClosed()`(已是 Closed → false),即使强行调也会被 Policy 拦截。
### 触发链路
最常见的几条路径:
| 序号 | 场景 | 触发动作 | 关账方式 |
|---|---|---|---|
| 1 | 无损全退 | `RefundAction(全额)` | 自动 |
| 2 | 部分扣罚 + 退余 | `RefundAction(剩余)` | 自动(退完瞬间)|
| 3 | 全扣无退 | `ForfeitureAction(余额全)` | 自动(扣完瞬间)|
| 4 | 多次扣罚直到清零 | 最后一笔 `ForfeitureAction` | 自动 |
| 5 | 主动关账(罕见,见 [[close-manual-with-zero-balance]]) | `CloseAction` | 手工(账户余额本来就 0) |
### 实现细节(`canBeClosed` + 自动逻辑)
`DepositAccount` 模型上的相关方法:
```php
public function canBeClosed(): bool
{
return $this->balance == 0 && $this->status !== DepositAccountStatus::Closed;
}
```
业务层(`RefundFromDepositAccountAction` / `ForfeitFromDepositAccountAction`)在事务最后会检查并自动调用 `close()`:
```php
// 伪代码
if ($account->canBeClosed()) {
$account->update(['status' => DepositAccountStatus::Closed]);
}
```
### 通知
后台日志记录"账户已自动关账",业户侧通过最后一张 Receipt 同时收到关账信息(Receipt 内容 + 短信)。
## 系统流程
```mermaid
sequenceDiagram
participant 财务
participant Filament
participant RefundFromDepositAccountAction
participant 数据库
Note over 财务: 余额 4200,退最后 4200
财务->>Filament: RefundAction (4200)
Filament->>RefundFromDepositAccountAction: handle(account, 4200, channel)
RefundFromDepositAccountAction->>数据库: 开启事务
RefundFromDepositAccountAction->>数据库: 建 CO(-4200) + Transaction(refund, 4200→0)
RefundFromDepositAccountAction->>数据库: balance=0
Note over RefundFromDepositAccountAction: 检查 canBeClosed() → balance==0 → true
RefundFromDepositAccountAction->>数据库: 自动 status=Closed
RefundFromDepositAccountAction->>数据库: 触发 CollectionOrderCompleted
RefundFromDepositAccountAction->>数据库: 提交事务
```
## 常见问题
> [!question] 余额 0 但 status 还是 Active 怎么办?
> 这是 bug。可能原因:
> - 业务 Action 没调 `canBeClosed()` 检查
> - 事务回滚后状态不一致
>
> 修复:tinker 手工执行 `$account->update(['status' => DepositAccountStatus::Closed])`,然后修复 Action 代码。审计上可写一笔说明备注。
> [!question] 没有任何流水(从未缴款)的账户能关账吗?
> 技术上可以(`balance == 0`)。但**业务上不正常**(开了账户没缴款),应:
> - 查清原因(为什么开了不缴?录错?)
> - 走 `CloseAction` 手工关([[close-manual-with-zero-balance]] 场景)
> - 备注清楚"无缴款记录,关闭"
> [!question] Frozen 账户余额是 0,自动关账吗?
> **不会**。Frozen 状态下不会自动关账,因为冻结期间任何状态变更都需要审慎处理。Frozen 且余额 0 → 先 [[unfreeze-after-mediation|解冻]] → 自动变 Active → 再走 `CloseAction` 手工关(或重启业务再继续操作)。
> [!question] 已关账户能"重新开张"继续用吗?
> 不能。`canBeReopened()` 永远 false。**新业务开新账户**。详见 [[account-state-machine]] "为什么不允许 Reopen" 段。
> [!question] 关账时业户能查看流水吗?
> 能。Closed 账户**只读模式**保留全部历史 —— 流水台账、所有 Receipt、状态变更记录(`meta`)都可查询。这是账户的"历史档案",随时可调出对账。
## 与 ForceClose 的区别
| 维度 | 本场景(自动关账) | [[force-close-refund]] 等 |
|---|---|---|
| 触发条件 | balance==0 时的最后一笔 Refund / Forfeiture | 账户 Frozen + 有余额,但要终结此账户 |
| 状态前置 | Active(Frozen 不会自动) | Frozen + hasBalance |
| Action | 任何减余额到 0 的 Action | 专用 `ForceCloseAction` |
| 资金处置 | 已有的 Refund / Forfeiture 决定 | 由 disposition 选(refund / forfeit / retain) |
| 关账理由 | "余额清零正常关账" | 强制结清,有专门 audit 字段 |
## 异常分支
- 关账时业户提出反悔 → 不可逆;走线下沟通 / 司法
- 余额非 0 想强制关 → 走 [[force-close-refund]] / [[force-close-forfeit]] / [[force-close-retain]]
- 主动关账(无业务往来)→ [[close-manual-with-zero-balance]]
## 相关文档
- [[account-state-machine]]
- [[refund-full-no-damage]]
- [[refund-partial-after-forfeit]]
- [[forfeit-damage-public-area]]
- [[close-manual-with-zero-balance]]
- [[force-close-refund]]