vault backup: 2026-05-25 23:27:56
This commit is contained in:
8
.obsidian/workspace.json
vendored
8
.obsidian/workspace.json
vendored
@@ -197,6 +197,10 @@
|
||||
},
|
||||
"active": "b06ed69835363258",
|
||||
"lastOpenFiles": [
|
||||
"prop-acc/scenarios/prepaid/refund-full-resident-moveout.md",
|
||||
"prop-acc/scenarios/prepaid/consume-batch-auto-monthly.md",
|
||||
"prop-acc/scenarios/prepaid/consume-meter-bill.md",
|
||||
"prop-acc/scenarios/prepaid/consume-multiple-bills-priority.md",
|
||||
"prop-acc/scenarios/prepaid/consume-monthly-property-bill.md",
|
||||
"prop-acc/scenarios/prepaid/deposit-via-miniapp-pending.md",
|
||||
"prop-acc/scenarios/prepaid/deposit-additional-topup.md",
|
||||
@@ -221,10 +225,6 @@
|
||||
"prop-acc/scenarios/deposit/force-close-retain.md",
|
||||
"prop-acc/scenarios/deposit/force-close-forfeit.md",
|
||||
"prop-acc/scenarios/deposit/force-close-refund.md",
|
||||
"prop-acc/scenarios/deposit/close-manual-with-zero-balance.md",
|
||||
"prop-acc/scenarios/deposit/close-after-zero-balance.md",
|
||||
"prop-acc/scenarios/deposit/unfreeze-after-mediation.md",
|
||||
"prop-acc/scenarios/deposit/freeze-during-dispute.md",
|
||||
"prop-acc/scenarios/deposit",
|
||||
"prop-acc/concepts/deposit",
|
||||
"prop-acc/scenarios/adhoc",
|
||||
|
||||
224
prop-acc/scenarios/prepaid/consume-batch-auto-monthly.md
Normal file
224
prop-acc/scenarios/prepaid/consume-batch-auto-monthly.md
Normal file
@@ -0,0 +1,224 @@
|
||||
---
|
||||
title: prop-acc · prepaid · 场景 - 月初批量自动抵扣 job(待补)
|
||||
aliases:
|
||||
- 月初自动抵扣
|
||||
- 批量预存款抵账单
|
||||
- consume-batch-auto-monthly
|
||||
- 场景-月初自动抵扣
|
||||
tags:
|
||||
- 场景
|
||||
- prop-acc
|
||||
- 预存款
|
||||
- 消费
|
||||
- 待补
|
||||
audience:
|
||||
- 业务人员
|
||||
- 财务
|
||||
- 产品
|
||||
status: 草稿
|
||||
sub_feature: prepaid
|
||||
last_review: 2026-05-25
|
||||
code_version: 2026-05-22
|
||||
---
|
||||
|
||||
# 场景:月初批量自动抵扣 job(待补)
|
||||
|
||||
> [!warning] 本场景**代码未实现**
|
||||
> 当前所有 consume 操作都需要业务人员**手动后台触发**。本文档描述自动 job 落地后的目标态。设计意图详见 [[auto-deduction-design]]。
|
||||
|
||||
预存款的**产品价值核心**。月初 1 日凌晨,Scheduled job 扫所有 Active + 余额>0 的预存款账户,对每个账户找未付账单,按优先级 ([[consume-multiple-bills-priority]]) 自动抵扣。
|
||||
|
||||
## 典型情境(目标态)
|
||||
|
||||
> [!example] 真实情境(目标态)
|
||||
> 2026 年 6 月 1 日 00:30,系统自动跑 `PrepaidAutoDeductionJob`:
|
||||
>
|
||||
> - 扫描全平台 5 个社区,共 500 个 Active 预存款账户(余额 > 0)
|
||||
> - 对每个账户找该业户的未付账单(物业费 + 水电费 + ...)
|
||||
> - 按 due_at 升序抵扣
|
||||
> - 全部完成耗时 ~5 分钟
|
||||
>
|
||||
> 早上 8 点,500 个业户陆续收到推送:
|
||||
>
|
||||
> | 业户场景 | 推送内容 |
|
||||
> |---|---|
|
||||
> | 余额充足,全抵 | "5 月账单已自动抵扣 ¥1,000,余额 ¥4,000" |
|
||||
> | 余额部分够 | "已抵 ¥800,水电费 ¥200 余额不足,请充值" |
|
||||
> | 账户冻结 | (不推送)|
|
||||
|
||||
## 业户视角(目标态)
|
||||
|
||||
### 您会感受到什么
|
||||
|
||||
- 月初某个早晨突然收到推送
|
||||
- 小程序"我的账单"批量翻为 ✅ Paid
|
||||
- 小程序"我的预存款"流水里多几笔 consume
|
||||
- 收到对应数量的 Receipt(每张账单一张)
|
||||
- **完全无感**(理想状态),不需要任何操作
|
||||
|
||||
### 您要做什么
|
||||
|
||||
只在以下情况要做:
|
||||
|
||||
| 推送内容 | 您要做 |
|
||||
|---|---|
|
||||
| "余额不足,X 账单未付" | 充值 → 业务人员手动补抵 / 等下月自动 job |
|
||||
| "账户冻结无法抵" | 联系物业了解冻结原因 |
|
||||
| "所有账单已抵,余额还剩 ¥X" | 不用做 |
|
||||
|
||||
## 业务人员视角
|
||||
|
||||
### Job 执行前
|
||||
|
||||
业务人员**不需要任何操作**。Scheduled 配置在系统层,运行无需介入。
|
||||
|
||||
### Job 执行过程
|
||||
|
||||
后台监控面板(待开发):
|
||||
|
||||
- 实时进度:已处理 N / 总共 M 账户
|
||||
- 实时统计:已抵账单数、抵扣总额、失败数、跳过数
|
||||
- 失败告警:任何异常立即推送给 ops
|
||||
|
||||
### Job 执行后
|
||||
|
||||
业务人员看汇总报告(后台 / 邮件):
|
||||
|
||||
```markdown
|
||||
# 2026 年 6 月 1 日 PrepaidAutoDeductionJob 报告
|
||||
|
||||
## 统计
|
||||
- 候选账户:500
|
||||
- 全抵成功:380(76%)
|
||||
- 部分抵 / 跳过:80(16%)
|
||||
- 跨社区拦截:0
|
||||
- 账户冻结跳过:8(2%)
|
||||
- 失败(异常):0(0%)
|
||||
- 其余(余额=0、无未付账单):32(6%)
|
||||
|
||||
## 资金动作
|
||||
- 抵扣总额:¥412,300
|
||||
- 涉及账单数:835(平均每户 1.6 张)
|
||||
- 平均抵扣金额:¥513
|
||||
- 最大单户抵扣:¥3,200(陈先生家,水电+物业+电梯)
|
||||
|
||||
## 失败明细
|
||||
(无)
|
||||
|
||||
## 跳过明细(需关注)
|
||||
- 80 户余额不足,合计欠款 ¥45,000
|
||||
- 已发推送
|
||||
- 业务人员可后续手动追缴
|
||||
|
||||
## 冻结跳过
|
||||
- 8 户冻结中
|
||||
- 需业务人员核实是否解冻
|
||||
```
|
||||
|
||||
### 异常介入
|
||||
|
||||
| 场景 | 业务人员动作 |
|
||||
|---|---|
|
||||
| 跳过的余额不足业户 | 联系业户充值 + 后续手动 ConsumeAction |
|
||||
| 冻结跳过业户 | 核实纠纷 / 风控状态 → [[unfreeze-after-verification|解冻]] |
|
||||
| Job 失败(系统异常) | 立即联系运维查日志 |
|
||||
| 某账户重复抵扣(理论上不应该) | 查 transactions 表是否有同一 Bill 被抵两次 → 立即停 job 排查 |
|
||||
|
||||
## 系统流程(目标态)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Scheduler
|
||||
participant Job[PrepaidAutoDeductionJob]
|
||||
participant Account[PrepaidAccount]
|
||||
participant Bill
|
||||
participant Consume[ConsumeFromPrepaidAccountAction]
|
||||
participant 监听器
|
||||
participant 数据库
|
||||
participant 业户
|
||||
|
||||
Note over Scheduler: 2026-06-01 00:30 触发
|
||||
|
||||
Scheduler->>Job: dispatch
|
||||
Job->>数据库: SELECT prepaid_accounts WHERE status=Active AND balance>0
|
||||
|
||||
loop 每个 account
|
||||
Job->>数据库: SELECT bills WHERE community_id=? AND resident_id=? AND status='unpaid' ORDER BY due_at, amount
|
||||
|
||||
loop 每个 bill
|
||||
alt balance >= bill.amount
|
||||
Job->>Consume: handle(account, bill, bill.amount)
|
||||
Consume->>Account: consume()
|
||||
Consume->>Bill: recordPayment() → Paid
|
||||
Consume->>监听器: 触发 CollectionOrderCompleted
|
||||
监听器->>数据库: 建 Receipt
|
||||
Consume->>数据库: 提交事务
|
||||
else 余额不够
|
||||
Job->>Job: 跳过,记日志
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Job->>数据库: 写汇总报告
|
||||
Job-->>Scheduler: 完成
|
||||
Job->>业户: 批量推送通知
|
||||
```
|
||||
|
||||
## Job 的安全设计(目标态)
|
||||
|
||||
| 风险 | 防御 |
|
||||
|---|---|
|
||||
| 同一账户被抵两次(job 重跑) | 每笔 consume 关联 Bill,Bill.status=Paid 后跳过 |
|
||||
| 跨社区误抵 | `ConsumeFromPrepaidAccountAction` 内置守护(consume 模型方法层) |
|
||||
| Frozen 账户被抵 | `canOperate()` 守护(模型层) |
|
||||
| 余额为负 | 事务回滚 + amount ≤ balance 守护 |
|
||||
| Job 长时间运行影响业务 | 分批 chunk(100 / 批);限制最大并发 |
|
||||
| Job 半夜失败无人发现 | 失败告警(Slack / 钉钉 / 短信)|
|
||||
| 业户充值后想立即抵(月中)| 业务人员手动 ConsumeAction(不等下月 job)|
|
||||
|
||||
## 与手动 ConsumeAction 的关系
|
||||
|
||||
| 维度 | 手动 ConsumeAction | 自动 Job |
|
||||
|---|---|---|
|
||||
| 触发 | 业务人员后台点击 | Scheduled(月初)|
|
||||
| 单次范围 | 1 账户 × 1 账单 | 全平台 × 全部账户 × 全部账单 |
|
||||
| 业务场景 | 个案、运维、补抵 | 月度默认流程 |
|
||||
| 通知 | 单笔 Receipt | 批量 Receipt + 汇总推送 |
|
||||
| 复用代码 | `ConsumeFromPrepaidAccountAction` | **同上**(复用,不重写)|
|
||||
|
||||
## 实施前已记录的待讨论项
|
||||
|
||||
详见 [[auto-deduction-design]] "待讨论 / 决策" 段。简略列表:
|
||||
|
||||
- 触发频率(月度 / 周度 / 实时)
|
||||
- 触发时点(月初固定 / 账单生成事件触发)
|
||||
- 优先级排序(due_at / amount / bill_type 组合)
|
||||
- 部分抵扣支持
|
||||
- 失败通知策略
|
||||
- 监控指标
|
||||
|
||||
## 未来扩展
|
||||
|
||||
job 落地后,后续可演化:
|
||||
|
||||
| 演化方向 | 价值 |
|
||||
|---|---|
|
||||
| **小程序"我的账单"显示"将于 X 月 1 日自动扣"** | 业户预期管理,避免临时余额不足惊讶 |
|
||||
| **预扣预警**:月底前 7 天扫描"下月账单 > 当前余额"的业户 → 主动提醒充值 | 减少跳过率,提升用户体验 |
|
||||
| **零余额自动通知**:月初 job 后,余额 = 0 的账户主动推送"请充值" | 提升复购率 |
|
||||
| **跨账户均衡**(若同业户多社区):未来若放开跨社区抵扣 | 提升资金利用率 |
|
||||
|
||||
## 当前替代(job 实现前)
|
||||
|
||||
- 业务人员**月初批量手动**逐户处理(见 [[consume-monthly-property-bill]] + [[consume-multiple-bills-priority]])
|
||||
- 工作量大,容易遗漏 / 顺序错乱
|
||||
- **这就是 job 紧迫性的来源**
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [[auto-deduction-design]]
|
||||
- [[consume-monthly-property-bill]]
|
||||
- [[consume-multiple-bills-priority]]
|
||||
- [[consume-meter-bill]]
|
||||
- [[consume-via-bill-collection-type]]
|
||||
- [[audit-low-balance-and-overdue]]
|
||||
179
prop-acc/scenarios/prepaid/consume-meter-bill.md
Normal file
179
prop-acc/scenarios/prepaid/consume-meter-bill.md
Normal file
@@ -0,0 +1,179 @@
|
||||
---
|
||||
title: prop-acc · prepaid · 场景 - 抵扣计量账单(水电费)
|
||||
aliases:
|
||||
- 抵水电费
|
||||
- 计量账单抵扣
|
||||
- consume-meter-bill
|
||||
- 场景-预存款抵计量账单
|
||||
tags:
|
||||
- 场景
|
||||
- prop-acc
|
||||
- 预存款
|
||||
- 消费
|
||||
- 计量
|
||||
audience:
|
||||
- 业户
|
||||
- 业务人员
|
||||
status: 已发布
|
||||
sub_feature: prepaid
|
||||
last_review: 2026-05-25
|
||||
code_version: 2026-05-22
|
||||
---
|
||||
|
||||
# 场景:抵扣计量账单(水电费)
|
||||
|
||||
业户的水表 / 电表 / 燃气表抄表后生成账单(由 Meter 模块出账,详见未来的 `meter/` 子模块文档),业务人员从业户的预存款余额抵扣。流程与 [[consume-monthly-property-bill|物业费抵扣]]**几乎相同**,差异在**账单的 bill_type** 和**金额浮动性**。
|
||||
|
||||
## 典型情境
|
||||
|
||||
> [!example] 真实情境
|
||||
> 张阿姨家 5 月水表抄表:本月用水 12 吨,@ 4.5 元/吨 = ¥54;电表抄表:用电 280 度,@ 0.6 元/度 = ¥168。两张计量账单合计 ¥222,从张阿姨预存款余额 ¥3,400 抵扣。
|
||||
|
||||
## 业户视角
|
||||
|
||||
### 您会感受到什么
|
||||
|
||||
- 抄表数据通过物业 App / 集抄系统进入系统
|
||||
- 几天内出账单(可能合并为"水电费"一张,也可能水、电分开两张)
|
||||
- 业务人员手动 / 自动从预存款抵扣
|
||||
- 收到收据:"水费 ¥54" + "电费 ¥168"(或合并"水电费 ¥222")
|
||||
- 推送:"5 月水电费 ¥222 已抵扣,余额 ¥3,178"
|
||||
|
||||
### 您要做什么
|
||||
|
||||
什么都不用。看明白即可。**关键差异**:计量账单**金额每月浮动**(取决于用量),业户应:
|
||||
|
||||
- 用量大时确保余额充足(预存款充值要考虑这部分)
|
||||
- 异常用量(突然翻倍)应自查(可能漏水 / 老人忘关电器)
|
||||
- 对账单金额有异议 → 走 [[../adhoc/cancel-amount-error-redo|纠错流程]](见 adhoc 模块,通用)
|
||||
|
||||
## 业务人员视角
|
||||
|
||||
> [!info] 流程基本同物业费抵扣
|
||||
> 看 [[consume-monthly-property-bill]] 完整流程。本场景只补充**计量账单特有**的注意点。
|
||||
|
||||
### 关键差异:bill_type
|
||||
|
||||
| 字段 | 物业费抵扣 | 计量账单抵扣 |
|
||||
|---|---|---|
|
||||
| `Bill.bill_type` | `property_fee` | `meter`(或 `water` / `electricity` / `gas`,看 Bill 模块设计)|
|
||||
| `CollectionOrder.collection_type` | `Bill` | `Bill`(都是 Bill 视角)|
|
||||
| `meta.fund_source` | `prepaid` | `prepaid` |
|
||||
| Receipt 文案 | "物业费 ¥800" | "水费 ¥54" / "电费 ¥168"(看 Bill 的 line items)|
|
||||
|
||||
> 注:具体 bill_type 枚举值看 Bill 模块定义。本文按"计量类"统称。
|
||||
|
||||
### 关键差异:金额来源
|
||||
|
||||
物业费金额是**固定的**(合同约定,每月不变)。计量账单金额是**计算出来的**:
|
||||
|
||||
```
|
||||
本月用量 = 本月抄表 - 上月抄表
|
||||
本月金额 = 用量 × 单价(RatePlan)
|
||||
```
|
||||
|
||||
数据流:Meter 抄表 → MeterReading 记录 → Bill 生成(按 RatePlan 计算金额) → 业务人员抵扣。
|
||||
|
||||
详见 Meter 模块文档(待补)。
|
||||
|
||||
### 关键差异:可能分单或合单
|
||||
|
||||
各物业财务习惯不同:
|
||||
|
||||
| 方式 | 优 | 缺 |
|
||||
|---|---|---|
|
||||
| **分单**(水、电、燃气各一张 Bill)| 业户能看清单项 | 业务人员要抵多张 |
|
||||
| **合单**(一张"5月水电费 ¥222")| 操作快 | 业户看不清各项 |
|
||||
|
||||
系统两种都支持,看 Meter / Bill 模块的出账配置。
|
||||
|
||||
## 系统流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant 集抄系统
|
||||
participant Meter
|
||||
participant Bill
|
||||
participant 财务
|
||||
participant Filament
|
||||
participant Account[PrepaidAccount]
|
||||
participant 数据库
|
||||
|
||||
集抄系统->>Meter: 推送本月抄表数据
|
||||
Meter->>数据库: 写 MeterReading + 计算用量
|
||||
Meter->>Bill: 生成 Bill(bill_type=meter, amount=222)
|
||||
Bill->>数据库: status=Unpaid
|
||||
|
||||
Note over 财务: 几天后业务人员处理
|
||||
|
||||
财务->>Filament: ConsumeAction(选水电费 Bill)
|
||||
Filament->>Account: consume(Bill, 222)
|
||||
Account->>数据库: 建 CO(type=Bill, meta.fund_source=prepaid)
|
||||
Account->>数据库: 建 PrepaidTransaction(consume, 3400→3178)
|
||||
Account->>Bill: recordPayment(222) → Paid
|
||||
Account->>监听器: 触发 CollectionOrderCompleted
|
||||
监听器->>数据库: 建 Receipt("水电费 ¥222")
|
||||
财务-->>业户: 推送 + 收据
|
||||
```
|
||||
|
||||
## 流水台账(累计)
|
||||
|
||||
| 流水 | type | amount | balance_before | balance_after | related_bill_id | 备注 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| ... | (前面省略)| | | | | |
|
||||
| N | consume | 800 | 4200 | 3400 | Bill #5月物业费 | 5 月物业费抵扣 |
|
||||
| **N+1** | **consume** | **222** | **3400** | **3178** | **Bill #5月水电费** | **本场景** |
|
||||
|
||||
## 用量异常的处理
|
||||
|
||||
> [!warning] 用量翻倍 / 异常巨高如何处理
|
||||
>
|
||||
> **场景**:张阿姨家平时月用水 12 吨,5 月用了 80 吨(翻 7 倍)。原因可能是:
|
||||
>
|
||||
> | 原因 | 处理 |
|
||||
> |---|---|
|
||||
> | 水管漏水 | 业户自查,联系物业维修;账单按事实承担(可能可申请减免)|
|
||||
> | 抄表录错 | 走 [[../adhoc/cancel-amount-error-redo]] 流程,反向调整账单 |
|
||||
> | 集抄系统 bug | 运维介入,重新抄表 / 校准 |
|
||||
> | 业户家用水设备故障 | 业户自担,可向物业申请协助维修 |
|
||||
>
|
||||
> 异常账单**不要直接抵扣** —— 先核实再处理,避免业户余额被错误清空。系统不主动识别"用量异常",由业务人员判断。
|
||||
|
||||
## 常见问题
|
||||
|
||||
> [!question] 水电费 Bill 是 Meter 模块生成的,跟其他账单有什么区别?
|
||||
> 唯一差别在 `bill_type` 字段和金额来源(计算 vs 固定)。对预存款 consume 流程**完全透明** —— `ConsumeFromPrepaidAccountAction` 不区分 bill_type,所有 Bill 一视同仁。
|
||||
|
||||
> [!question] 业户预存款余额不够付水电费怎么办?
|
||||
> 同 [[consume-monthly-property-bill]] 处理:
|
||||
> - 跳过该账单 → 推送业户充值
|
||||
> - 业户充值后再抵
|
||||
> - 不部分抵(避免半付状态)
|
||||
|
||||
> [!question] 水、电、燃气分开还是合并出账?
|
||||
> 看 Meter 模块配置 + 物业财务习惯。预存款抵扣端**支持两种**。
|
||||
|
||||
> [!question] 月底 100+ 户的水电费账单要挨个抵,跟物业费一起 100+ 户,业务人员吃得消吗?
|
||||
> 同样痛点 —— 等 [[auto-deduction-design|自动 job]] 落地一起解决。job 实现后,**月初一次 job 同时抵扣物业费 + 水电费 + 其他账单**。
|
||||
|
||||
> [!question] 业户对水电费金额有异议(认为抄表错了)?
|
||||
> 走 Meter / Bill 模块的纠错流程:
|
||||
> 1. 业户提报
|
||||
> 2. 物业核查抄表数据(物理表 vs 录入数据)
|
||||
> 3. 错了 → 走 Bill 的 reverse + reissue 流程(详见 Meter / Bill 模块文档)
|
||||
> 4. 没错 → 沟通业户接受(或走司法纠纷)
|
||||
|
||||
## 异常分支
|
||||
|
||||
- 物业费抵扣 → [[consume-monthly-property-bill]]
|
||||
- 多账单一起抵 → [[consume-multiple-bills-priority]]
|
||||
- 异常用量需 Bill 模块介入 → 见 Meter / Bill 模块(待补)
|
||||
- 月初批量(未来)→ [[consume-batch-auto-monthly]]
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [[consume-monthly-property-bill]]
|
||||
- [[consume-multiple-bills-priority]]
|
||||
- [[consume-via-bill-collection-type]]
|
||||
- [[transaction-types]]
|
||||
- [[auto-deduction-design]]
|
||||
196
prop-acc/scenarios/prepaid/consume-multiple-bills-priority.md
Normal file
196
prop-acc/scenarios/prepaid/consume-multiple-bills-priority.md
Normal file
@@ -0,0 +1,196 @@
|
||||
---
|
||||
title: prop-acc · prepaid · 场景 - 多个未付账单按 due_at 优先级抵扣
|
||||
aliases:
|
||||
- 多账单抵扣优先级
|
||||
- 优先抵最早到期账单
|
||||
- consume-multiple-bills-priority
|
||||
- 场景-多账单优先级抵扣
|
||||
tags:
|
||||
- 场景
|
||||
- prop-acc
|
||||
- 预存款
|
||||
- 消费
|
||||
audience:
|
||||
- 业户
|
||||
- 业务人员
|
||||
status: 已发布
|
||||
sub_feature: prepaid
|
||||
last_review: 2026-05-25
|
||||
code_version: 2026-05-22
|
||||
---
|
||||
|
||||
# 场景:多个未付账单按 due_at 优先级抵扣
|
||||
|
||||
业户某月有**多张未付账单**(物业费、水电费、电梯维护费等),余额需抵多张。**优先抵最早到期的账单**(避免逾期罚款)。本场景描述业务人员的批量抵扣逻辑,也是 [[auto-deduction-design|自动 job]] 的核心算法。
|
||||
|
||||
## 典型情境
|
||||
|
||||
> [!example] 真实情境
|
||||
> 张阿姨家 5 月有 3 张未付账单:
|
||||
>
|
||||
> | 账单 | 金额 | due_at |
|
||||
> |---|---|---|
|
||||
> | 5 月物业费 | ¥800 | 5 月 15 日 |
|
||||
> | 5 月水电费 | ¥1,200 | 5 月 20 日 |
|
||||
> | Q2 电梯维护费 | ¥300 | 5 月 31 日 |
|
||||
>
|
||||
> 合计 ¥2,300,张阿姨账户余额 ¥2,500。**全部能抵**,但顺序很重要:
|
||||
>
|
||||
> 1. 物业费(5 月 15 日)— 最早到期,先抵
|
||||
> 2. 水电费(5 月 20 日)
|
||||
> 3. 电梯维护费(5 月 31 日)
|
||||
>
|
||||
> 抵完余 ¥200。
|
||||
|
||||
## 业户视角
|
||||
|
||||
### 您会感受到什么
|
||||
|
||||
- 5 月底收到 3 张收据(各账单一张),金额对应原账单
|
||||
- 小程序"我的预存款"流水按抵扣时间倒序显示 3 笔 consume
|
||||
- 小程序"我的账单"3 张全部 ✅ Paid
|
||||
- 推送通知"5 月 3 张账单已抵扣,余额 ¥200"
|
||||
|
||||
### 您要做什么
|
||||
|
||||
什么都不用。看明白即可。
|
||||
|
||||
> [!info] 余额够不够全付决定行为
|
||||
> - **够全付**:全部抵,余额剩下的留账户
|
||||
> - **不够全付**:按优先级先抵最早到期的,后面的留 Unpaid 状态 → 推送"余额不足,请充值"
|
||||
|
||||
## 业务人员视角
|
||||
|
||||
### 第 1 步:打开账户
|
||||
|
||||
后台 → 预存款 → 找到张阿姨账户 → 进 `ViewPrepaidAccount`(balance=2500)。
|
||||
|
||||
### 第 2 步:逐张抵扣(手动模式)
|
||||
|
||||
> [!warning] 当前没有"一键全抵"按钮
|
||||
> 业务人员需要**对每张账单各点一次** `ConsumeAction`。这是 [[auto-deduction-design|自动 job]] 要解决的痛点。
|
||||
|
||||
**正确顺序**(按 due_at 升序):
|
||||
|
||||
| 步骤 | 选 Bill | 抵 amount | 之后余额 |
|
||||
|---|---|---|---|
|
||||
| 1 | 5 月物业费(due 5/15) | 800 | 2500 → 1700 |
|
||||
| 2 | 5 月水电费(due 5/20) | 1200 | 1700 → 500 |
|
||||
| 3 | Q2 电梯维护费(due 5/31) | 300 | 500 → 200 |
|
||||
|
||||
每张账单走完整 [[consume-monthly-property-bill]] 流程(Modal → 提交 → 触发监听器 → Receipt)。
|
||||
|
||||
### 第 3 步:核对结果
|
||||
|
||||
- 3 张账单全 ✅ Paid
|
||||
- 账户余额 ¥200
|
||||
- 3 张 Receipt 已生成(分别 ¥800 ¥1,200 ¥300)
|
||||
|
||||
## 优先级排序逻辑(自动 job 用)
|
||||
|
||||
未来自动 job 实现后,**按以下顺序排序未付账单**:
|
||||
|
||||
| 排序键 | 升序/降序 | 业务理由 |
|
||||
|---|---|---|
|
||||
| 1. `due_at` | 升序 | **最早到期的先抵**(避免逾期产生滞纳金 / 影响信用) |
|
||||
| 2. `bill_type` | 自定义("物业费" → "水电费" → "其他") | 物业费是核心服务费,优先 |
|
||||
| 3. `amount` | 升序 | 同优先级下,**小额先抵清**(避免余额不够时多张大账单都半抵)|
|
||||
| 4. `created_at` | 升序 | 兜底:早建的先 |
|
||||
|
||||
伪代码:
|
||||
|
||||
```python
|
||||
unpaid_bills = sorted(
|
||||
bills,
|
||||
key=lambda b: (b.due_at, BILL_TYPE_ORDER[b.bill_type], b.amount, b.created_at)
|
||||
)
|
||||
|
||||
balance = account.balance
|
||||
for bill in unpaid_bills:
|
||||
if balance <= 0:
|
||||
break
|
||||
if balance >= bill.amount:
|
||||
consume(account, bill, bill.amount)
|
||||
balance -= bill.amount
|
||||
else:
|
||||
# 余额不够全付该账单 → 跳过,等业户充值
|
||||
notify(account.resident, "余额不足,无法抵 ¥{bill.amount} 的 {bill.bill_type}")
|
||||
# 或部分抵(看 Bill 是否支持):consume(account, bill, balance); balance = 0; break
|
||||
```
|
||||
|
||||
## 余额不够全付的策略对比
|
||||
|
||||
假设张阿姨账户余额 ¥1,500,3 张账单合计 ¥2,300:
|
||||
|
||||
| 策略 | 行为 | 结果 |
|
||||
|---|---|---|
|
||||
| **全部跳过** | 余额不够任何一笔不动 | 3 张全 Unpaid,余额 ¥1,500 闲置 |
|
||||
| **按优先级抵到不够为止**(推荐) | 物业费 800 → 余 700;水电费 1200 不够跳过;电梯费 300 余 700 ≥ 300 → 抵 → 余 400 | 物业费 + 电梯费 Paid,水电费 Unpaid,余额 ¥400 |
|
||||
| **按优先级抵 + 部分抵末张** | 物业费 800 → 余 700;水电费 1200 > 700 → 抵 700 部分 → 水电费余 500;电梯费 300 余 0 跳过 | 物业费 Paid + 水电费部分付 + 电梯费 Unpaid + 余额 0 |
|
||||
|
||||
**推荐第二种**(按优先级抵到不够为止,不做部分抵)。理由:
|
||||
|
||||
- 简单,Bill 模块不用支持部分付
|
||||
- 业户看到余额还有钱但有账单未付,会主动充值
|
||||
- 避免"抵了一半"的复杂状态
|
||||
|
||||
**第三种**等 Bill 模块支持部分支付后再考虑。
|
||||
|
||||
## 系统流程(手动模式 3 笔操作)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant 财务
|
||||
participant Filament
|
||||
participant Account
|
||||
participant 数据库
|
||||
|
||||
Note over 财务: 余额 2500,3 张未付账单
|
||||
|
||||
财务->>Filament: ConsumeAction(物业费 800)
|
||||
Filament->>Account: consume(物业费, 800)
|
||||
Account->>数据库: balance 2500→1700, Bill Paid
|
||||
|
||||
财务->>Filament: ConsumeAction(水电费 1200)
|
||||
Filament->>Account: consume(水电费, 1200)
|
||||
Account->>数据库: balance 1700→500, Bill Paid
|
||||
|
||||
财务->>Filament: ConsumeAction(电梯费 300)
|
||||
Filament->>Account: consume(电梯费, 300)
|
||||
Account->>数据库: balance 500→200, Bill Paid
|
||||
|
||||
Note over 数据库: 3 张账单 Paid + 3 张 Receipt + 余额 200
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
> [!question] 业务人员漏抵某张账单怎么办?
|
||||
> 单纯漏抵 → 后续发现再抵一次。如果业户因此被收滞纳金,物业可走 [[../adhoc/cancel-amount-error-redo]] 之类的补救路径。
|
||||
|
||||
> [!question] 业务人员抵错优先级(先抵晚到期的)?
|
||||
> 不影响资金正确性(账户余额扣对了,账单状态更新对了)。**业务上**可能让早到期的账单进入逾期。**预防** = 培训业务人员看 due_at,**根治** = 上自动 job。
|
||||
|
||||
> [!question] 跨多个业户批量抵扣可以吗?
|
||||
> 当前不行,只能一个账户一个账户操作。**批量是自动 job 的核心需求**。
|
||||
|
||||
> [!question] 业务人员选错账单(选了别人的)?
|
||||
> Modal 的账单下拉**已经过滤同业户 + 同社区**,理论上选不到别人的。除非 UI / 数据有 bug,否则不会发生。
|
||||
|
||||
> [!question] 部分抵扣场景频繁吗?
|
||||
> 业务上罕见 —— 业户通常一次充够覆盖几个月。如果某业户经常余额不够,业务人员应主动提醒"建议充够 3 个月"。
|
||||
|
||||
## 异常分支
|
||||
|
||||
- 单笔抵扣 → [[consume-monthly-property-bill]]
|
||||
- 计量类账单(水电费)→ [[consume-meter-bill]]
|
||||
- 余额不够 → 告知业户充值
|
||||
- 月初批量(未来)→ [[consume-batch-auto-monthly]]
|
||||
- 账户冻结 → [[unfreeze-after-verification]]
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [[consume-monthly-property-bill]]
|
||||
- [[consume-meter-bill]]
|
||||
- [[consume-batch-auto-monthly]]
|
||||
- [[auto-deduction-design]]
|
||||
- [[transaction-types]]
|
||||
190
prop-acc/scenarios/prepaid/refund-full-resident-moveout.md
Normal file
190
prop-acc/scenarios/prepaid/refund-full-resident-moveout.md
Normal file
@@ -0,0 +1,190 @@
|
||||
---
|
||||
title: prop-acc · prepaid · 场景 - 业户搬走全额退余
|
||||
aliases:
|
||||
- 业户搬走退预存款
|
||||
- 全额退预存款
|
||||
- refund-full-resident-moveout
|
||||
- 场景-业户搬走退预存款
|
||||
tags:
|
||||
- 场景
|
||||
- prop-acc
|
||||
- 预存款
|
||||
- 退款
|
||||
audience:
|
||||
- 业户
|
||||
- 业务人员
|
||||
status: 已发布
|
||||
sub_feature: prepaid
|
||||
last_review: 2026-05-25
|
||||
code_version: 2026-05-22
|
||||
---
|
||||
|
||||
# 场景:业户搬走全额退余
|
||||
|
||||
业户**搬离社区**(卖房 / 退租 / 不再使用本物业),要把预存款账户余额全额退回。退款后**不自动关账**(prepaid 特性),业务人员**主动**走 [[close-resident-moveout|关账]] 流程。
|
||||
|
||||
## 典型情境
|
||||
|
||||
> [!example] 真实情境
|
||||
> 刘先生把 12-3-501 房子卖了,下周搬走。他在预存款账户里还有 ¥3,200(本月物业费扣完之后的余),要全额退回。
|
||||
|
||||
## 业户视角
|
||||
|
||||
### 第 1 步:告知物业搬走
|
||||
|
||||
- 跟物业管家说"我下周搬走,把预存款里的钱退给我"
|
||||
- 提供退款渠道(银行卡 / 微信 / 支付宝)
|
||||
|
||||
### 第 2 步:等退款
|
||||
|
||||
- 业务人员核对账户余额
|
||||
- 操作退款(走线下 + 系统)
|
||||
|
||||
### 第 3 步:收到红字收据 + 退款到账
|
||||
|
||||
- 红字收据"预付款退款 ¥-3,200"
|
||||
- 银行 / 微信收到 ¥3,200
|
||||
|
||||
### 第 4 步:账户被关
|
||||
|
||||
- 后续物业再发账单(若有)→ 不会自动从这个账户扣(已 Closed)
|
||||
- 业户在小程序"我的预存款" 显示 "🔒 已关闭"
|
||||
|
||||
## 业务人员视角
|
||||
|
||||
### 第 1 步:核实业户搬走情况
|
||||
|
||||
- 房屋已过户(看 community_user_profile 状态)
|
||||
- 业户已结清其他费用(无未付账单)
|
||||
- 业户提供退款渠道
|
||||
|
||||
> [!warning] 注意未付账单
|
||||
> 如果业户还有未付账单(物业费 / 水电费等),**先抵扣再退余**:
|
||||
> - 业户余额 ¥3,200,有未付账单 ¥800 → 先 [[consume-monthly-property-bill|抵 800]],余 ¥2,400 → 再退 ¥2,400
|
||||
> - 不要直接退全部 → 否则未付账单仍挂业户身上,变成"搬走后还欠物业钱",催收困难
|
||||
|
||||
### 第 2 步:打开账户做退款
|
||||
|
||||
后台 → 预存款 → 找到刘先生账户(Active,balance=3200)→ 进 `ViewPrepaidAccount` → 点 `RefundAction`(标签"退款")。
|
||||
|
||||
> [!warning] 按钮可见性
|
||||
> `RefundAction` 守护:`canOperate() && balance > 0` + Policy `->authorize('refund')`。Frozen / Closed / 零余额账户灰化。
|
||||
|
||||
Modal 表单:
|
||||
|
||||
| 字段 | 填什么 |
|
||||
|---|---|
|
||||
| **退款金额** | ¥3,200(默认带入当前余额)|
|
||||
| **退款渠道(PaymentChannel)** | 选业户指定回款方式 |
|
||||
| **退款备注** | 必填,如 "业户搬离,12-3-501 已过户,退预存款全额" |
|
||||
|
||||
### 第 3 步:提交
|
||||
|
||||
系统调 `RefundFromPrepaidAccountAction`,事务内:
|
||||
|
||||
1. 校验 `canOperate()`(Active only)
|
||||
2. 校验金额 ≤ 当前余额
|
||||
3. 建 `CollectionOrder`(`type=Prepaid`,`actual_amount=-3200` 红字,`Completed`)
|
||||
4. 调 `PrepaidAccount::refund(3200, ...)`:
|
||||
- 模型层再校验 `canOperate()`(三层防御之模型层兜底)
|
||||
- 加 `PrepaidTransaction`(`type=refund`,`amount=3200`,`balance_before=3200`,`balance_after=0`,关联红字 CO)
|
||||
- 更新 `balance=0`
|
||||
5. **不自动关账**(prepaid 特性,与 deposit 不同 —— 见 [[account-state-machine]] "零余额不自动关账" 段)
|
||||
6. 触发 `CollectionOrderCompleted` → Listener 建红字 Receipt"预付款退款 ¥-3,200"
|
||||
|
||||
### 第 4 步:走线下退款
|
||||
|
||||
- 银行转账:导出回款指令 → 银行办理
|
||||
- 微信:在物业微信号上做退款
|
||||
- 支付宝:同上
|
||||
|
||||
### 第 5 步:主动关账([[close-resident-moveout]])
|
||||
|
||||
退完余额后**账户仍是 Active 状态**,需手动走 `CloseAccountAction` 关掉。这是 prepaid 与 deposit 的关键差异。
|
||||
|
||||
详见 [[close-resident-moveout]]。
|
||||
|
||||
### 第 6 步:把红字收据给业户
|
||||
|
||||
后台找 Receipt → 微信 / 邮件发刘先生。
|
||||
|
||||
## 系统流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant 业户
|
||||
participant 财务
|
||||
participant Filament
|
||||
participant RefundFromPrepaidAccountAction
|
||||
participant 数据库
|
||||
participant 监听器
|
||||
|
||||
Note over 业户,财务: 业户搬走,余额 3200
|
||||
|
||||
财务->>财务: 核实无未付账单(若有,先 consume)
|
||||
财务->>Filament: ViewPrepaidAccount → RefundAction(3200)
|
||||
Filament->>RefundFromPrepaidAccountAction: handle(account, 3200, channel)
|
||||
RefundFromPrepaidAccountAction->>RefundFromPrepaidAccountAction: canOperate()? Active=true
|
||||
RefundFromPrepaidAccountAction->>RefundFromPrepaidAccountAction: 3200 ≤ 3200 yes
|
||||
RefundFromPrepaidAccountAction->>数据库: 开启事务
|
||||
RefundFromPrepaidAccountAction->>数据库: 1. 建 CO(Prepaid, -3200 红字, Completed)
|
||||
RefundFromPrepaidAccountAction->>数据库: 2. account.refund(3200) → balance 3200→0
|
||||
RefundFromPrepaidAccountAction->>数据库: 3. balance=0, **status 仍 Active**
|
||||
RefundFromPrepaidAccountAction->>监听器: 4. 触发 CollectionOrderCompleted
|
||||
监听器->>数据库: 5. 建 Receipt("预付款退款 ¥-3,200")
|
||||
RefundFromPrepaidAccountAction->>数据库: 提交事务
|
||||
Filament-->>财务: 成功(注:account 仍 Active,需手动关账)
|
||||
|
||||
Note over 财务: 接着走关账
|
||||
财务->>Filament: CloseAccountAction → status=Closed
|
||||
财务-->>业户: 银行/微信退 3200 + 红字收据
|
||||
```
|
||||
|
||||
## 与 deposit 退款的关键差异
|
||||
|
||||
| 维度 | deposit 退款(refund-full-no-damage) | prepaid 退款(本场景) |
|
||||
|---|---|---|
|
||||
| 余额清零后状态 | **自动 Closed** | **仍 Active**(可继续充值)|
|
||||
| 关账操作 | 不需要 | **需手动 CloseAccountAction** |
|
||||
| 业务背景 | 装修结束等业务节点 | 业户搬走等长期事件 |
|
||||
| 是否常见 | 高频(每户装修都做) | 低频(业户搬走才做)|
|
||||
|
||||
## 常见问题
|
||||
|
||||
> [!question] 为什么 prepaid 不自动关账?
|
||||
> 详见 [[account-state-machine]] "零余额不自动关账" 段。简言之:预存款账户**一户一账**,频繁开关无意义,业户随时可能继续充值。
|
||||
|
||||
> [!question] 退完不关账户有什么风险?
|
||||
> 几乎无风险:
|
||||
> - 业户搬走后再无消费/充值动作 → 账户保持 0 余额 Active
|
||||
> - 但**长期闲置 Active 账户**会出现在审计扫描里([[audit-low-balance-and-overdue]] 类似),业务上不专业
|
||||
> - 推荐**退完立即关**(走 [[close-resident-moveout]]),清爽
|
||||
|
||||
> [!question] 业户搬走后又租回来或买回来,关了账户怎么办?
|
||||
> 当前**一户一账约束阻塞**(unique 不允许重开)。详见 [[one-account-per-resident]] "已知设计 gap"。业务上目前用:
|
||||
> - 业户重新现金 / 微信付账单(不用预存款)
|
||||
> - 联系运维特殊处理(罕见)
|
||||
|
||||
> [!question] 退款渠道与充值渠道不同可以吗?
|
||||
> 可以,看 [[../deposit/refund-with-payment-channel-switch]] 介绍的换渠道逻辑(deposit 模块,逻辑相同)。
|
||||
|
||||
> [!question] 业户失联但要退预存款怎么办?
|
||||
> 几个选项:
|
||||
> - **暂留 Active**:不操作,等业户出现(余额对业户仍可用)
|
||||
> - **freeze 账户**:走 [[freeze-suspected-fraud|风控冻结]] 流程(reason 改"业户失联待联系")
|
||||
> - **不可走 ForceClose retain**(prepaid 没有 ForceClose,与 deposit 不同)
|
||||
> - 长期失联(>2 年)走业务流程(类似 deposit 的 retain,但 prepaid 当前没有内建机制,需运维介入)
|
||||
|
||||
## 异常分支
|
||||
|
||||
- 退一部分余下继续用 → [[refund-partial-after-consume]]
|
||||
- 账户 Frozen 想退 → 先 [[unfreeze-after-verification|解冻]] 再退(prepaid 没有 ForceClose)
|
||||
- 关账步骤 → [[close-resident-moveout]]
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [[refund-partial-after-consume]]
|
||||
- [[close-resident-moveout]]
|
||||
- [[account-state-machine]]
|
||||
- [[transaction-types]]
|
||||
- [[consume-monthly-property-bill]]
|
||||
Reference in New Issue
Block a user