spec: 分账连续阶梯重算设计文档
This commit is contained in:
parent
9f027b9b7c
commit
4432a33be7
140
docs/superpowers/specs/2026-06-12-分账连续阶梯重算设计.md
Normal file
140
docs/superpowers/specs/2026-06-12-分账连续阶梯重算设计.md
Normal file
@ -0,0 +1,140 @@
|
||||
# 分账连续阶梯重算设计
|
||||
|
||||
> 2026-06-12 | 将按水量分账从"比例均摊"改为"连续阶梯重算"
|
||||
|
||||
---
|
||||
|
||||
## 一、目标
|
||||
|
||||
按水量分账(splitRuleType=COUNT)的计费方式从比例均摊改为连续阶梯重算,实现:
|
||||
|
||||
- 总水费 = 子账单水费之和,与直接算总水量结果一致
|
||||
- 第一笔子账单从第一阶梯开始计费,后续子账单接着前一笔的累计水量继续计算
|
||||
- 原账单的其他费用(污水、垃圾等)暂按比例均摊
|
||||
|
||||
---
|
||||
|
||||
## 二、改动范围
|
||||
|
||||
**后端改动(3 个文件):**
|
||||
|
||||
| 文件 | 改动 |
|
||||
|------|------|
|
||||
| `AccountingAdjustActionServiceImpl` | 重写 `createSplitChildren()` |
|
||||
| `PriceDiffPreviewService` | 新增 `recalculate()` 重载,接受自定义水量和累计起始量 |
|
||||
| `PriceTemplateService` + impl | 新增 `getPriceTemplateByCode(snapCode, templateCode)` 重载 |
|
||||
|
||||
**不改动的:**
|
||||
- Controller、VO、数据库
|
||||
- `cloneChargeDetailForSplit`(费用明细克隆保持现有比例逻辑)
|
||||
- `createSplitChildrenByFeeComp`(按费用组成分账不动)
|
||||
|
||||
---
|
||||
|
||||
## 三、数据流
|
||||
|
||||
```
|
||||
applySplitWriteBack()
|
||||
├── markParentSplit() [不变]
|
||||
│
|
||||
├── createSplitChildren() [改造]
|
||||
│ ├── getPriceTemplateByCode(snap, code) → 原账单当时的水价模板
|
||||
│ ├── charge.getIsLadder() → 阶梯开关
|
||||
│ │
|
||||
│ ├── 逐笔重算 (accumulated 从 0 开始):
|
||||
│ │ ├── recalculate(template, water, accumulated, isLadder, true)
|
||||
│ │ │ → Map<costCode, amount>
|
||||
│ │ ├── setFees(child, feeMap) → 设各费用字段
|
||||
│ │ └── accumulated += water
|
||||
│ │
|
||||
│ └── 返回 children
|
||||
│
|
||||
└── cloneChargeDetailForSplit() [不变,比例复制]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、新增方法签名
|
||||
|
||||
### 4.1 PriceDiffPreviewService.recalculate 重载
|
||||
|
||||
```java
|
||||
/**
|
||||
* 用指定水价模板 + 指定水量 + 累计起始量重算各项费用
|
||||
*
|
||||
* @param template 水价模板
|
||||
* @param billWater 本次水量
|
||||
* @param accumulatedWater 累计起始水量(前面子账单已用的水量)
|
||||
* @param isLadder 是否阶梯计费
|
||||
* @param calculateGarbageFee 是否计算垃圾费
|
||||
* @return costCode → amount
|
||||
*/
|
||||
public Map<String, BigDecimal> recalculate(
|
||||
PriceTemplateDO template,
|
||||
BigDecimal billWater,
|
||||
BigDecimal accumulatedWater,
|
||||
Boolean isLadder,
|
||||
Boolean calculateGarbageFee
|
||||
)
|
||||
```
|
||||
|
||||
实现逻辑:从现有 `recalculate(ChargeDO, PriceTemplateDO, Boolean, Boolean)` 提取核心计算段,用水量参数 `accumulatedWater + billWater` 替代 `charge.getBillWater()`,阶梯判断基于累计量而非单次水量。
|
||||
|
||||
### 4.2 PriceTemplateService.getPriceTemplateByCode 重载
|
||||
|
||||
```java
|
||||
/**
|
||||
* 按快照编号和模板代码查询水价模板
|
||||
* @param snapCode 调价快照编号
|
||||
* @param templateCode 模板代码
|
||||
*/
|
||||
PriceTemplateDO getPriceTemplateByCode(String snapCode, String templateCode);
|
||||
```
|
||||
|
||||
实现:`priceTemplateMapper.selectOne(adjustmentSnapCode, snapCode, code, templateCode)`
|
||||
|
||||
---
|
||||
|
||||
## 五、createSplitChildren 改造
|
||||
|
||||
### 改造前(比例均摊)
|
||||
|
||||
```java
|
||||
List<BigDecimal> ratios = allocateByWaterRatio(billWaters, charge.getBillWater());
|
||||
List<BigDecimal> waterFees = allocateByRatio(charge.getWaterFee(), ratios, 2);
|
||||
// ... 所有费用项按同一比例分
|
||||
```
|
||||
|
||||
### 改造后(连续阶梯重算)
|
||||
|
||||
```java
|
||||
PriceTemplateDO template = priceTemplateService.getPriceTemplateByCode(
|
||||
charge.getAdjustmentSnapCode(), charge.getPriceTemplateCode());
|
||||
|
||||
Boolean isLadder = charge.getIsLadder();
|
||||
BigDecimal accumulated = BigDecimal.ZERO;
|
||||
|
||||
for (BigDecimal water : billWaters) {
|
||||
Map<String, BigDecimal> feeMap = priceDiffPreviewService.recalculate(
|
||||
template, water, accumulated, isLadder, true);
|
||||
|
||||
ChargeDO child = cloneBase(charge, splitAdjustId);
|
||||
child.setBillWater(water);
|
||||
setFeesFromMap(child, feeMap); // 水费/污水/垃圾等
|
||||
children.add(child);
|
||||
accumulated = accumulated.add(water);
|
||||
}
|
||||
```
|
||||
|
||||
`setFeesFromMap` 与 `PriceDiffWriteBackService.setFeesFromMap` 逻辑一致。
|
||||
|
||||
---
|
||||
|
||||
## 六、边界条件
|
||||
|
||||
| 场景 | 处理 |
|
||||
|------|------|
|
||||
| 模板不存在 | 抛出 `invalidParamException("原账单水价模板已失效")` |
|
||||
| billWaters 之和 ≠ charge.billWater | 不强制校验,允许浮点误差 |
|
||||
| isLadder 为 null | 默认 true(阶梯) |
|
||||
| adjustmentSnapCode 为 null | 降级到最新快照 |
|
||||
Loading…
x
Reference in New Issue
Block a user