7.8 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 · billing · 场景 - 同业户多账单批量收款 |
|
|
|
已发布 | billing | 2026-05-26 | 2026-05-22 |
场景:同业户多账单批量收款
业户本月有多张账单(物业费 + 水电气费 + 其他),想一次性付清。业务人员走 BatchCollectPaymentAction,一笔 CollectionOrder 关联多张 Bill(走 CollectionOrderBill 多对多)。
典型情境
[!example] 真实情境 张阿姨本月有 4 张账单:
账单 金额 5 月物业费 ¥800 5 月水费 ¥54 5 月电费 ¥168 5 月燃气 ¥30 合计 ¥1,052 张阿姨到前台:"4 张账单我一次性付清,微信扫码"。业务人员1 笔操作完成 4 张账单收款。
业户视角
第 1 步:告诉业务人员要付哪些
"我把本月 4 张账单都付了,微信扫码"
第 2 步:确认金额
业务人员说:"4 张账单合计 ¥1,052,微信扫这个码"。
第 3 步:微信付
业户扫码 → 输密码 / 指纹 → 付 ¥1,052。
第 4 步:拿收据
可能是:
- 一张 Receipt 含 4 行明细(物业费 ¥800 / 水费 ¥54 / ...)
- 或 4 张独立 Receipt(每张账单一张)
具体看实现。业户体验上前者更好(一张收据看全部)。
业务人员视角
第 1 步:找业户
后台 → 业户 → 找到张阿姨 → "她的账单"标签 → 看到 4 张 Unpaid。
或:后台 → 账单 → 列表 → 过滤业户=张阿姨 + 状态=Unpaid。
第 2 步:选中多张账单
Table 上勾选 4 张账单 → 顶部 "批量收款" 按钮(BatchCollectPaymentAction)。
或:后台 → 业户视图 → "批量收款"(若 UI 支持单业户聚合)。
第 3 步:Modal 表单
| 字段 | 填什么 |
|---|---|
| 选中账单数 | 4 张(显示) |
| 合计金额 | ¥1,052(自动算) |
| 支付方式 | 微信 |
| 收款银行账户 | 物业微信账户 |
| 备注 | 选填,如 "业户本月全套缴" |
第 4 步:提交
系统在一个事务内:
- 校验每张 Bill 可付(
canBePaid()) - 建 1 个 CollectionOrder(
type=Bill,actual_amount=+1052,status=Completed) - 建 4 个 CollectionOrderBill(每张账单一个,各自
allocated_amount= 该账单金额) - 4 张 Bill 各自
paid_amount = amount,status = Paid - 触发
CollectionOrderCompleted事件 - Listener 建 Receipt(可能含 4 个 line_items)
第 5 步:给收据
后台找到 Receipt → 打印 / 微信发。
系统流程
sequenceDiagram
participant 业户
participant 业务
participant Filament
participant Action[BatchCollectPaymentAction]
participant DB
业务->>Filament: BillsList → 选 4 张 → 批量收款
Filament->>Action: handle(bills, channel, bank)
Action->>Action: 每张 Bill 校验 canBePaid
Action->>DB: 开启事务
Action->>DB: 1. 建 CollectionOrder(+1052, Completed)
loop 每张 Bill
Action->>DB: 2. 建 CollectionOrderBill(allocated=bill.amount)
Action->>DB: 3. Bill.paid_amount = amount, status=Paid
end
Action->>Listener: 4. 触发 CollectionOrderCompleted
Listener->>DB: 5. 建 Receipt(line_items × 4)
Action->>DB: 提交事务
Filament-->>业务: 成功通知
业务-->>业户: 收据(含 4 项明细)
数据示例
收款后:
CollectionOrder(1 条)
id: 67890
collection_type: Bill
actual_amount: +1052
payment_channel: 微信
status: Completed
meta.fund_source: external
CollectionOrderBill(4 条)
| collection_order_id | bill_id | allocated_amount |
|---|---|---|
| 67890 | 物业费 Bill | 800 |
| 67890 | 水费 Bill | 54 |
| 67890 | 电费 Bill | 168 |
| 67890 | 燃气 Bill | 30 |
Bill(4 条更新)
每张 paid_amount = amount,status = Paid。
Receipt(1 条,4 行明细)
collection_order_id: 67890
amount: +1052
line_items: [
{ 物业费(5月), 800 },
{ 水费(5月), 54 },
{ 电费(5月), 168 },
{ 燃气(5月), 30 },
]
与单张收款的对比
| 维度 | collect-payment-single | 批量(本场景) | |---|---|---| | Modal 选账单 | 1 张(从 ViewBill 进入)| 多张(Table 勾选)| | CollectionOrder | 1 个 | 1 个(共用)| | CollectionOrderBill | 1 个 | N 个(每张一个)| | 业务人员操作 | 单笔 | 一笔 | | 业户体验 | 一张一张付(慢)| 一次付清(快)|
批量收款的好处:业户体验更好(一次付),业务人员工作量更小。唯一前提:业户愿意一次付清。
不同支付方式的分摊
业户支付的钱自动按账单原金额比例分摊 到各 Bill。不需要业务人员手动分配。
例:¥1,052 微信付 → 4 个 CollectionOrderBill 各自 allocated_amount = 该账单 amount(全额分配)。
如果业户付不够(部分批量付)
业户只想付 ¥600(不够 ¥1,052)→ 业务人员有几种选择:
| 策略 | 操作 |
|---|---|
| 优先付物业费(默认?) | ¥600 全部分配给物业费 Bill(部分付) |
| 按比例分摊 | 600 × 800/1052 = 456 给物业费,600 × 54/1052 = 31 给水费, ... |
| 业务人员手动决定 | 给业务人员选哪张账单付多少 |
当前实现的具体策略看代码。业务上建议优先付到期早的(避免逾期)。
[!info] 批量收款的"部分付"复杂度 上述场景比单张部分付更复杂。当前
BatchCollectPaymentAction可能只支持全额批量(若金额够付所有选中账单 → 全付;不够 → 走单张部分付逐张操作)。看实现。
业务人员视角:Modal 的预检查
类似 smart-bulk-delete-design 的预检查思路:
- 选中 4 张账单 → Modal 显示"总计 ¥1,052"
- 选中包含已 Paid 的账单 → Modal 显示"4 选 + 1 已付跳过"
- 选中包含 Suspended → Modal 提示 "该账单挂起,无法收款"
[!info] 当前实现的成熟度
BatchCollectPaymentAction的智能 Modal 程度看实现。可能比BulkDeleteBillsAction简单(批删有 issue.md Q6 详细设计,批收款未单独描述)。
常见问题
[!question] 选中跨业户的多张账单能批量收款吗? 业务上不应该(不同业户付的钱不能混)。系统层面应校验同业户,否则拒绝。
例外:家属代付场景(儿子来付父母账单)→ 业务上算同业户的钱。
[!question] 批量收款失败一半怎么办? 事务内全成功或全失败。任一 Bill 校验失败(例如某张已 Paid)→ 整笔回滚 → 业户的钱不被收。
实施上可能优化为"部分成功"(只失败的跳过),但破坏事务原子性,通常不推荐。
[!question] 业户支付的钱比账单合计多? Modal 守护应限制金额 ≤ 合计。如果业务上业户故意多给:
- 找零给业户(系统层面只收账单的金额)
- 或转入业户预存款账户(若有此自动逻辑)
[!question] 不同期次的账单能一起付吗? 可以(账单状态都是 Unpaid 即可)。例如付 4 月物业费 + 5 月物业费 + 5 月水电气。
[!question] 批量收款的 activitylog 怎么记? 一条 CollectionOrder 的 activitylog(event=created)+ 每张 Bill 状态变化的 log。可在 SQL 反查 affected_bill_ids。
异常分支
- 单张付 → collect-payment-single
- 部分付 → exception-partial-payment
- 预存款抵 → collect-via-prepaid-auto
- 业户全付不起,挑某张付 → 走 collect-payment-single 逐张
- 收错了想撤 → void-paid-bill(已付作废 + 退款)