--- title: prop-acc · billing · 场景 - 同业户多账单批量收款 aliases: - 批量收款 - BatchCollectPaymentAction - 一次付多张 - collect-payment-batch - 场景-同业户批量收款 tags: - 场景 - prop-acc - 账单 - 收款 audience: - 业户 - 业务人员 status: 已发布 sub_feature: billing last_review: 2026-05-26 code_version: 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 步:提交 系统在**一个事务**内: 1. 校验每张 Bill 可付(`canBePaid()`) 2. 建 **1 个 CollectionOrder**(`type=Bill`,`actual_amount=+1052`,`status=Completed`) 3. 建 **4 个 CollectionOrderBill**(每张账单一个,各自 `allocated_amount` = 该账单金额) 4. 4 张 Bill 各自 `paid_amount = amount`,`status = Paid` 5. 触发 `CollectionOrderCompleted` 事件 6. Listener 建 Receipt(可能含 4 个 line_items) ### 第 5 步:给收据 后台找到 Receipt → 打印 / 微信发。 ## 系统流程 ```mermaid 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]](已付作废 + 退款) ## 相关文档 - [[bill-vs-collection-order]] - [[collect-payment-single]] - [[collect-via-prepaid-auto]] - [[exception-partial-payment]] - [[../prepaid/auto-deduction-design]]