245 lines
6.8 KiB
Markdown
245 lines
6.8 KiB
Markdown
|
|
---
|
|||
|
|
title: prop-acc · meter · 倍率与阶梯计价
|
|||
|
|
aliases:
|
|||
|
|
- 倍率
|
|||
|
|
- 阶梯计价
|
|||
|
|
- multiplier
|
|||
|
|
- tiered pricing
|
|||
|
|
- min max 封顶
|
|||
|
|
tags:
|
|||
|
|
- 概念
|
|||
|
|
- prop-acc
|
|||
|
|
- 计量表
|
|||
|
|
- 计费
|
|||
|
|
audience:
|
|||
|
|
- 业务人员
|
|||
|
|
- 财务
|
|||
|
|
- 架构师
|
|||
|
|
status: 已发布
|
|||
|
|
sub_feature: meter
|
|||
|
|
last_review: 2026-05-25
|
|||
|
|
code_version: 2026-05-22
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 倍率与阶梯计价
|
|||
|
|
|
|||
|
|
抄表生成账单的金额由三层叠加算出:**倍率(multiplier)** × **阶梯计价(tiered)** + **min/max 封顶**。这三层都在 `MeterBillCalculator` 内实现(纯算,无 DB IO),是 prop-acc 模块中**最严谨的算法之一**。
|
|||
|
|
|
|||
|
|
## 一句话总览
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
本月用量 = (current_reading - previous_reading) × multiplier
|
|||
|
|
本月金额 = 阶梯计价(本月用量, RatePlan)
|
|||
|
|
最终金额 = clamp(本月金额, min, max)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
详见下文每层展开。
|
|||
|
|
|
|||
|
|
## 第 1 层:倍率(multiplier)
|
|||
|
|
|
|||
|
|
### 用途
|
|||
|
|
|
|||
|
|
工业 / 集团表的**实际计量值远大于表头数字**:
|
|||
|
|
|
|||
|
|
| 表型 | 表头读数 | multiplier | 实际用量 |
|
|||
|
|
|---|---|---|---|
|
|||
|
|
| 家用单相表 | 280 度 | 1 | 280 度 |
|
|||
|
|
| 三相工业表 | 28 度 | **10** | 280 度 |
|
|||
|
|
| 大型集团表(高压侧)| 28 度 | **100** | 2,800 度 |
|
|||
|
|
|
|||
|
|
物理原理:工业表为了在小表头显示大用量,内部有变压 / 分流比。multiplier 就是这个比值。
|
|||
|
|
|
|||
|
|
### 字段精度
|
|||
|
|
|
|||
|
|
```php
|
|||
|
|
// migration
|
|||
|
|
$table->decimal('multiplier', 10, 4);
|
|||
|
|
// model casts
|
|||
|
|
'multiplier' => 'decimal:4',
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
decimal(10,4) 精度足够工业场景(常见 1.0 / 10.0 / 100.0 / 1000.0,极少数 0.5 / 1.25 等特殊变比)。
|
|||
|
|
|
|||
|
|
### 用量计算
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
consumption = (current_reading - previous_reading) × multiplier
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
例:三相表上月 280,本月 308,multiplier=10:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
consumption = (308 - 280) × 10 = 280 度
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
业户账单按"280 度"算,不是"28 度"。
|
|||
|
|
|
|||
|
|
## 第 2 层:阶梯计价(Tiered Pricing)
|
|||
|
|
|
|||
|
|
### 用途
|
|||
|
|
|
|||
|
|
水电气**阶梯递增**:用得越多,单价越高。鼓励节约,符合国家政策。
|
|||
|
|
|
|||
|
|
例:某市水阶梯计价:
|
|||
|
|
|
|||
|
|
| 阶梯 | 月用水(吨)| 单价 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| 第一阶梯 | 0-20 | 3.0 元/吨 |
|
|||
|
|
| 第二阶梯 | 21-30 | 4.5 元/吨 |
|
|||
|
|
| 第三阶梯 | 30 以上 | 6.0 元/吨 |
|
|||
|
|
|
|||
|
|
业户本月用 35 吨:
|
|||
|
|
|
|||
|
|
| 段 | 用量 | 单价 | 段金额 |
|
|||
|
|
|---|---|---|---|
|
|||
|
|
| 0-20 吨(第一阶梯) | 20 | 3.0 | 60 |
|
|||
|
|
| 21-30 吨(第二阶梯) | 10 | 4.5 | 45 |
|
|||
|
|
| 31-35 吨(第三阶梯) | 5 | 6.0 | 30 |
|
|||
|
|
| **合计** | **35** | | **135** |
|
|||
|
|
|
|||
|
|
### Progressive 累进 vs Full-tier 简陋实现
|
|||
|
|
|
|||
|
|
> [!info] 本系统采用 **progressive 累进**(正确算法),不是 full-tier 简陋实现。
|
|||
|
|
|
|||
|
|
| 算法 | 35 吨水 |
|
|||
|
|
|---|---|
|
|||
|
|
| **Progressive 累进**(本系统)| 20 × 3 + 10 × 4.5 + 5 × 6 = 135 元 ✅ |
|
|||
|
|
| Full-tier 简陋(错)| 35 × 6 = 210 元 ❌(整月用量按最高阶梯计)|
|
|||
|
|
| Mixed(更错)| 35 × 4.5 = 157.5 元 ❌ |
|
|||
|
|
|
|||
|
|
progressive 是国家政策标准做法,简陋实现是市场上劣质系统的常见 bug。`MeterBillCalculator::calculateTiered()` 实现得对,所以我们的账单准确。
|
|||
|
|
|
|||
|
|
### RatePlan + RateTier 模型
|
|||
|
|
|
|||
|
|
阶梯定义在 `RatePlan` + `RateTier` 模型里(不属于本子模块,在更通用的费率模块):
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
RatePlan ─── RateTier (1..n)
|
|||
|
|
├── tier=1, lower=0, upper=20, unit_price=3.0
|
|||
|
|
├── tier=2, lower=20, upper=30, unit_price=4.5
|
|||
|
|
└── tier=3, lower=30, upper=null, unit_price=6.0
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`fee_type_id`(在 Meter 上)指向 RatePlan,Calculator 拿到 RatePlan → 按 tier 累加段金额。
|
|||
|
|
|
|||
|
|
## 第 3 层:min / max 封顶
|
|||
|
|
|
|||
|
|
### 用途
|
|||
|
|
|
|||
|
|
防止**极端用量**导致离谱账单:
|
|||
|
|
|
|||
|
|
| 场景 | 问题 | 封顶方案 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| 业户家漏水,1 月用 1000 吨 | 按阶梯算 ~ 6000+ 元 | `max=2000` 封顶,差额走维修保险 |
|
|||
|
|
| 表故障读 0 度 | 业户不付钱了 | `min=20` 兜底(至少收基础费)|
|
|||
|
|
| 业户家整月没人(零用量)| 看物业政策 | `min=20` 仍要收(物业服务费性质)|
|
|||
|
|
|
|||
|
|
### 实现
|
|||
|
|
|
|||
|
|
`RatePlan` 上有 `min_amount` / `max_amount` 字段:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
final_amount = max(min_amount, min(calculated_amount, max_amount))
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
例:计算出 ¥6,000 但 `max_amount=2000` → 实际收 ¥2,000,差额 ¥4,000 由物业 / 维修保险承担(账面体现为"封顶减免")。
|
|||
|
|
|
|||
|
|
### 业务上的取舍
|
|||
|
|
|
|||
|
|
> [!warning] 封顶不是万能的
|
|||
|
|
> max 封顶让业户感激,但物业要承担差额。建议:
|
|||
|
|
>
|
|||
|
|
> - 设合理的 max(覆盖正常波动范围)
|
|||
|
|
> - 异常用量先排查([[exception-high-consumption]])再决定是否减免
|
|||
|
|
> - 封顶降低收入,需评估对物业财务可持续性影响
|
|||
|
|
|
|||
|
|
min 类似:
|
|||
|
|
|
|||
|
|
> [!info] min 的政策意义
|
|||
|
|
> min 通常对应"管网维护费 / 基础服务费",即使零用量也要分摊管网成本。但在国家政策严格的地区,要明确告知业户"min 是什么"。
|
|||
|
|
|
|||
|
|
## 完整算法流程
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart TD
|
|||
|
|
A[抄表 current_reading] --> B[查询上次 reading]
|
|||
|
|
B --> C[计算 consumption =<br/>(current - previous) × multiplier]
|
|||
|
|
C --> D[加载 RatePlan + RateTier]
|
|||
|
|
D --> E[Calculator.calculateTiered<br/>按阶梯累加段金额]
|
|||
|
|
E --> F{有 min/max?}
|
|||
|
|
F -->|有 max & 金额超| G[封顶到 max]
|
|||
|
|
F -->|有 min & 金额低| H[补到 min]
|
|||
|
|
F -->|正常范围| I[原值]
|
|||
|
|
G --> J[最终账单金额]
|
|||
|
|
H --> J
|
|||
|
|
I --> J
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 完整算例
|
|||
|
|
|
|||
|
|
电费阶梯(假设):
|
|||
|
|
|
|||
|
|
| 阶梯 | 用电度数 | 单价 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| 1 | 0-200 | 0.5 |
|
|||
|
|
| 2 | 200-400 | 0.6 |
|
|||
|
|
| 3 | 400+ | 0.8 |
|
|||
|
|
|
|||
|
|
`min_amount=10`,`max_amount=500`。
|
|||
|
|
|
|||
|
|
工业表(三相,multiplier=10),5 月抄表 308,上月 280:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
1. consumption = (308 - 280) × 10 = 280 度
|
|||
|
|
2. 阶梯:200 × 0.5 + 80 × 0.6 = 100 + 48 = 148 元
|
|||
|
|
3. 封顶:10 ≤ 148 ≤ 500 → 不动
|
|||
|
|
4. 最终账单:148 元
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
家用表(multiplier=1),5 月抄表 1100,上月 800:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
1. consumption = (1100 - 800) × 1 = 300 度
|
|||
|
|
2. 阶梯:200 × 0.5 + 100 × 0.6 = 100 + 60 = 160 元
|
|||
|
|
3. 封顶:10 ≤ 160 ≤ 500 → 不动
|
|||
|
|
4. 最终账单:160 元
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
漏水 case(家用表,水阶梯,multiplier=1),本月用 1000 吨:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
1. consumption = 1000 吨
|
|||
|
|
2. 阶梯:20 × 3 + 10 × 4.5 + 970 × 6 = 60 + 45 + 5820 = 5925 元
|
|||
|
|
3. 封顶:5925 > max_amount(假设 2000)→ 封到 2000
|
|||
|
|
4. 最终账单:2000 元(差额 3925 由物业 / 维修保险承担)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 业务人员视角
|
|||
|
|
|
|||
|
|
- **配置阶梯**:在 RatePlan + RateTier 模型里设置(不在 meter 子模块,通常运营 / 财务总监来设)
|
|||
|
|
- **配置倍率**:建表时 / 后续编辑表时填(`MeterForm`,默认 1)
|
|||
|
|
- **核对账单**:看到异常金额时,顺着 Calculator 算法手工验算
|
|||
|
|
|
|||
|
|
## 财务视角
|
|||
|
|
|
|||
|
|
- 阶梯计价 = 政策合规
|
|||
|
|
- 倍率确保工业表账单正确
|
|||
|
|
- min/max 封顶 = 风险控制 + 业户友好(max)
|
|||
|
|
|
|||
|
|
## 业户视角
|
|||
|
|
|
|||
|
|
业户**不需要懂这套算法**,看到的是账单金额。但**对账时**要能理解:
|
|||
|
|
|
|||
|
|
- 上月读数 → 本月读数 → 用量(度 / 吨 / 立方米)
|
|||
|
|
- 用量 → 阶梯单价 → 金额
|
|||
|
|
- 如有封顶 → 凭证显示
|
|||
|
|
|
|||
|
|
## 相关文档
|
|||
|
|
|
|||
|
|
- [[meter-vs-meter-reading]]
|
|||
|
|
- [[bill-generation-pipeline]]
|
|||
|
|
- [[generate-bill-tiered-pricing]]
|
|||
|
|
- [[generate-bill-with-multiplier]]
|
|||
|
|
- [[generate-bill-min-max-cap]]
|
|||
|
|
- [[exception-high-consumption]]
|