7.6 KiB
title, aliases, tags, audience, status, sub_feature, last_review, code_version
| title | aliases | tags | audience | status | sub_feature | last_review | code_version | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| prop-acc · deposit · 场景 - 冻结状态强制全退并关账 |
|
|
|
已发布 | deposit | 2026-05-25 | 2026-05-22 |
场景:冻结状态强制全退并关账
账户处于 Frozen 状态、还有余额,但纠纷结果明确利向业户(损坏归责不在业户、调解结果全退、司法判决业户胜诉等)。物业用 ForceCloseAction 选 refund disposition,一步完成"解冻 + 全额退还 + 关账"。
典型情境
[!example] 真实情境 陈先生家押金账户因墙面损坏归责争议被冻结。1 个月后第三方机构鉴定结论 —— 损坏与陈先生装修无关(是先前住户造成的)。物业认可结论,要把 ¥5,000 全额退给陈先生并关账户。
不能走普通 refund-full-no-damage,因为账户 Frozen 状态下
canWithdraw=false守护会拦截。需要 ForceClose 走refunddisposition。
业户视角
您会感受到什么
- 收到通知:"您的押金账户已结清,全额 ¥5,000 退还"
- 收到红字收据:"装修保证金退还 ¥-5,000(强制关账退还,事由:鉴定结论无责)"
- 银行 / 微信收到退款
- 小程序"我的押金账户"显示 "🔒 已结清"
您要做什么
- 配合提供退款渠道
- 保管红字收据(税务凭证)
业务人员视角
第 1 步:确认全退判定
- 第三方鉴定 / 司法判决 / 调解协议(书面)
- 物业内部决定"按全退处理"
[!warning] 这是不可逆操作 ForceClose 提交后账户立即 Closed,无法撤销。务必在书面凭证就位后再操作。
第 2 步:打开 Frozen 账户
后台 → 保证金 → 账户列表 → 找 Frozen 账户(状态显示 🧊)→ 进 ViewDepositAccount。
右上角状态管理组只有
UnfreezeAction和ForceCloseAction可点。
第 3 步:点击 ForceCloseAction(标签"强制关账")
Modal 表单(关键是 disposition Radio):
| 字段 | 填什么 |
|---|---|
| 处置方式 (disposition) | 选 ✅ refund(退还) |
| 退款渠道(动态出现) | 选业户指定渠道 |
| 关账事由(memo) | 必填,如 "鉴定结论:墙面损坏与业户装修无关,全额退还" |
[!info] 不复用 RefundFromDepositAccountAction ForceClose 的 refund 逻辑单独写(
ForceCloseDepositAccountAction~230 行),不复用RefundFromDepositAccountAction。理由:
- 后者的
canWithdraw守护刚好挡住 Frozen- 如果复用,普通调用方意外绕过冻结检查,语义边界模糊
- 重复约 60 行核心代码换语义边界清晰,值得
这条权衡详见 issue.md Q3 "第二轮已落地"段。
[!warning] Policy 守护
DepositAccountPolicy::forceClose():
update权限isFrozen() && hasBalance()(只允许 Frozen + 有余额)Active 账户 / Closed 账户 / Frozen 但 balance=0 的账户,按钮都灰化。
第 4 步:提交
系统调 ForceCloseDepositAccountAction(disposition=refund),事务内:
- 校验
isFrozen() && hasBalance() - 建
CollectionOrder(actual_amount=-5000红字,status=Completed) - 加
DepositTransaction(type=refund,amount=5000,balance_before=5000,balance_after=0,关联红字 CO) - 更新
balance=0 - 直接
status=Closed(从 Frozen) - 在
meta.force_closed_disposition=refund、meta.force_closed_memo=...、meta.force_closed_at=...记审计字段 - 触发
CollectionOrderCompleted→ Listener 建红字 Receipt
第 5 步:走线下退款 + 给收据
银行 / 微信退 ¥5,000;红字收据交业户。
系统流程
sequenceDiagram
participant 业户
participant 财务
participant Filament
participant ForceCloseDepositAccountAction
participant 数据库
participant 监听器
Note over 业户,财务: 账户 Frozen + balance=5000,鉴定结论全退
财务->>Filament: ViewDepositAccount → ForceCloseAction (disposition=refund)
Filament->>ForceCloseDepositAccountAction: handle(account, disposition=refund, channel, memo)
ForceCloseDepositAccountAction->>ForceCloseDepositAccountAction: isFrozen() && hasBalance()? yes
ForceCloseDepositAccountAction->>数据库: 开启事务
ForceCloseDepositAccountAction->>数据库: 1. 建 CO (-5000 红字, Completed)
ForceCloseDepositAccountAction->>数据库: 2. 建 DepositTransaction (refund, 5000→0)
ForceCloseDepositAccountAction->>数据库: 3. balance=0, status=Closed(直接 Frozen→Closed)
ForceCloseDepositAccountAction->>数据库: 4. meta.force_closed_disposition=refund 等审计字段
ForceCloseDepositAccountAction->>监听器: 5. 触发 CollectionOrderCompleted
监听器->>数据库: 6. 建 Receipt (强制关账退还 ¥-5,000)
ForceCloseDepositAccountAction->>数据库: 提交事务
Filament-->>财务: 成功通知
财务-->>业户: 银行/微信退 5000 + 红字收据
状态转换
stateDiagram-v2
[*] --> Active
Active --> Frozen : freeze() 纠纷
Frozen --> Closed : ForceClose(refund)<br/>+建红字流水<br/>+全额退款
Closed --> [*]
ForceClose 是唯一能从 Frozen 直接到 Closed(不经过 Active)的路径。
常见问题
[!question] 为什么不先解冻再退? 可以走"解冻 + 普通 Refund"两步组合,效果一样。但 ForceClose 一步到位有几个好处:
- 审计标记:
meta.force_closed_*字段明确记录"这次关账是强制的、什么 disposition、什么事由",未来追溯一眼明白- 避免中间态:Active 状态下账户能被其他人误操作(再缴款 / 再扣罚),ForceClose 跳过这个窗口
- 业务语义清晰:这次关账不是"正常退完关",是"特殊处置关",标志性强
[!question] disposition 选错(原本应该 retain 选了 refund)能改吗? 不能。
DepositTransaction和CollectionOrder都不可变。如果选错:
- 已实际退款给业户 → 找业户协商再缴回,系统记一笔 deposit;然后开新账户继续(原账户已 Closed 永久)
- 还没实际线下退款 → 系统记录已成事实但线下不操作,业务上需在审计备注解释"系统记退款实际未执行";然后在新账户上做正确处置
预防胜于补救:Modal 表单提交前务必再三确认 disposition。
[!question] 退款金额能改成部分吗? 不能。ForceClose 是"一步终结",退款金额自动 = 当前全部余额。如果需要部分退、部分扣、部分保留,不应该用 ForceClose,应该:
- 先 unfreeze-after-mediation 回 Active
- 按比例操作:部分 refund-full-no-damage + 部分 forfeit-damage-public-area
- 余额清零时自动 Closed
[!question] 业户拒绝接受 ForceClose refund 的结果怎么办? 一旦 ForceClose 提交,账户 Closed 不可逆。业户接受与否只影响线下退款流程的执行:
- 业户接受 → 收到退款
- 业户拒收(罕见,觉得"应该是物业全责加罚款") → 走司法
系统层面的账面已平,后续纠纷与账户无关。
[!question] ForceClose 后能反悔(Active 重启)吗? 不能。Closed 永久。详见 account-state-machine。
异常分支
- 全扣不退 → force-close-forfeit
- 资金保留待领 → force-close-retain
- 业户没失联且配合 → 优先 unfreeze-after-mediation + 普通 refund(更标准)