# REV004 / 按水量分账最小闭环设计(2026-04-14) ## 1. 目标 本设计只覆盖 **第一阶段:按水量分账最小闭环**。 目标是: - 审批通过后,能够把一笔未收费原账单按多笔水量拆分,真正生成多张目标子账单; - 先不做“按费用组成分账”; - 先把最小可运行闭环打通,再作为后续扩展基础。 --- ## 2. 为什么第一阶段先做按水量分账 原因: 1. **规则最清晰**:拆分水量之和必须等于原水量。 2. **业务校验最直接**:原账单 -> 多笔子账单,按水量重算金额,容易理解。 3. **比按费用组成更容易守恒**:先处理总水量、总金额守恒,再逐步扩到费用项粒度。 4. **更适合先做执行态样板**:审批通过、执行、回看、失败回滚等流程都能先沉淀下来。 --- ## 3. 最小闭环边界 ### 本阶段要做到 - 提交按水量分账申请 - 保存拆分明细(每笔分账水量) - 审批通过后执行拆账 - 生成多张目标账单 - 原账单标记为已拆分/失效 - 日志与查询可回看 ### 本阶段不做 - 按费用组成分账 - 目标客户跨户分账(如无强需求,可先默认同客户) - 更复杂规则模板 - 开票联动优化 - 自动撤销分账执行结果 --- ## 4. 输入输出定义 ### 4.1 前端最少输入 按水量分账弹窗至少提交: - `sourceChargeId` - `splitItems[]` - `seqNo` - `splitWater` - `reasonCode` - `remark` - `applicant` - `contactMobile` ### 4.2 关键校验 - 原账单必须是未收费 - `splitItems.length >= 2` - 每笔 `splitWater > 0` - `sum(splitWater) = source.billWater` - 必须填写原因 ### 4.3 输出结果 申请提交成功后: - `adjustmentNo` - `resultStatus = PENDING_APPROVAL` 执行成功后应可回看: - 原账单摘要 - 目标子账单列表 - 每张子账单的: - 水量 - 账单金额 - 应收金额 - 违约金(若适用) --- ## 5. 数据模型(MVP) ### 5.1 主表:`biz_split_adjust` 建议 MVP 字段: - `id` - `split_adjust_no` - `source_charge_id` - `split_rule_type` = `BY_WATER` - `reason_code` - `remark` - `approval_status` - `execute_status` - `execute_message` - `executed_at` - `creator / create_time / updater / update_time` ### 5.2 明细表:`biz_split_adjust_detail` 建议 MVP 字段: - `id` - `split_adjust_id` - `seq_no` - `split_water` - `target_charge_id`(执行后回填) - `split_amount`(执行后回填) - `status` - `creator / create_time / updater / update_time` ### 5.3 账单表最小扩展建议 在 `biz_charge` 里增加追溯字段(二选一或组合): - `source_charge_id` - `split_adjust_id` - `charge_origin_type`(如:NORMAL / SPLIT_CHILD) 原账单增加状态表达(二选一): - 新字段标记 `split_status` - 或现有字段中增加来源/失效标识 MVP 阶段关键不是状态字段优雅,而是: > 后续能稳定区分“原账单”和“拆分生成的子账单”。 --- ## 6. 执行流程(MVP) ### Step 1:申请提交 接口:`POST /unsold-split-submit` - 校验基本参数 - 保存 `biz_split_adjust` - 保存 `biz_split_adjust_detail`(只保存水量) - 返回待审批 ### Step 2:审批通过 - 触发内部执行器 - 对 `biz_split_adjust` 加幂等校验:只允许执行一次 - 锁定原账单 `source_charge_id` - 开事务 ### Step 3:生成目标账单 对每个 `split_adjust_detail`: 1. 复制原账单基础信息 2. 将 `bill_water` 改成当前 `split_water` 3. 调用现有计价逻辑重新计算: - `bill_amount` - `extended_amount` - 若适用则重算 `late_fee` 4. 插入新的 `biz_charge` 记录 5. 回填 `target_charge_id / split_amount` ### Step 4:处理原账单 - 原账单置为“已拆分,不可继续收费” - 不直接删除原账单 - 保证历史可追溯 ### Step 5:收尾 - `biz_split_adjust.execute_status = SUCCESS` - 写 operat_log / log detail - 查询面可见结果 ### Step 6:失败回滚 - 任一子账单生成失败:事务回滚 - 主单置 `FAIL` - 记录失败原因 - 不允许出现半拆分状态 --- ## 7. 重算策略建议 MVP 不建议自己重新写一套计价逻辑,建议: - 复用已有账单计算能力 - 输入拆分后的水量 - 走现有价格模板/费用计算规则 原因: - 这样能减少“分账执行”和“普通开账计费”之间的口径偏差 - 也更容易保证后续账单金额解释一致 关键原则: > 分账只是“改变水量输入并生成多张账单”,不要重造一套新的计费引擎。 --- ## 8. 查询回看(MVP) 建议至少补两个查询面: ### 8.1 分账详情查询 - 原账单摘要 - 分账规则类型(BY_WATER) - 每笔拆分水量 - 每笔生成的目标账单 ID / 金额 - 执行状态 / 错误原因 ### 8.2 日志页增强 在现有 `log-detail / log-process` 中,增加: - 原账单 -> 子账单映射摘要 - 执行结果摘要 --- ## 9. 测试建议(MVP) ### 9.1 单测 - 水量守恒校验 - 拆分笔数边界(<2, >12) - 非未收费账单禁止发起 - 执行幂等 ### 9.2 集成测试 - 提交按水量分账申请 -> 待审批 - 审批通过 -> 生成 N 张子账单 - 子账单水量之和 = 原账单水量 - 原账单不可继续收费 - 日志页可回看执行结果 - 执行失败整体回滚 ### 9.3 验收测试 - 一张账单拆成 2 笔 - 一张账单拆成多笔 - 子账单后续可分别收费/开票(若本期不做全链,可先验证可查询/可识别) --- ## 10. MVP 成功标准 MVP 完成时,应满足: 1. 用户可提交按水量分账申请 2. 审批通过后,系统真正生成多张目标账单 3. 子账单水量之和与原账单守恒 4. 原账单被正确标记,不再继续作为普通未收费账单使用 5. 查询与日志能回看这次分账执行结果 --- ## 11. 建议实施顺序 1. 先补 `biz_split_adjust` / `biz_split_adjust_detail` 2. 再补审批通过后的执行器 3. 再补查询回看 4. 最后补前端执行态结果页 一句话: > 先把“按水量分账”做成审批后真正能拆出子账单的最小闭环,再考虑按费用组成扩展。