7.4 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 · meter · 表更换链 |
|
|
|
已发布 | meter | 2026-05-25 | 2026-05-22 |
表更换链
物理表会老化、损坏、定期校验。旧表换新表后,系统通过 replaced_meter_id 字段把新表指回旧表,初始读数继承(避免业户被白嫖一段用量)。新表编号自动加 -R1 / -R2 / ... 后缀,整条更换链(代代相承)在数据库里可追溯。
为什么要更换链
[!info] 真实情境 张阿姨家电表(编号 E-501)用了 8 年,2026 年 5 月 物业例行校验发现读数跳变(怀疑表内电路老化),要换新表。
换表那天:
- 旧表 E-501 最后读数 = 5,000 度
- 新表 E-501-R1 出厂 = 0 度,但初始读数继承 5,000 度
- 否则:新表从 0 起 → 业户下个月看着账单(假设用了 50 度)→ 但系统算的 "5050 - 0 = 5050 度"全要业户付 → 灾难
更换链保证用量计算连续,业户感知无差异。
数据模型
字段关系
flowchart LR
A[Meter 旧表<br/>E-501<br/>final_reading=5000<br/>is_active=false<br/>decommissioned_at=2026-05-15<br/>decommission_reason=Replaced] -->|被 replaced_meter_id 指回| B[Meter 新表<br/>E-501-R1<br/>initial_reading=5000<br/>is_active=true<br/>installed_at=2026-05-15<br/>replaced_meter_id=旧表 ID]
字段语义
| Meter 字段 | 旧表(被换) | 新表(替代) |
|---|---|---|
code |
E-501 | E-501-R1(nextReplacementCode() 自动算) |
is_active |
false | true |
installed_at |
8 年前 | 2026-05-15(换表当天) |
decommissioned_at |
2026-05-15 | null |
decommission_reason |
Replaced(枚举,见 decommission-and-locking) |
null |
final_reading |
5000(换表前最后读数) | null |
initial_reading |
(历史值,不动) | 5000(继承自旧表) |
replaced_meter_id |
null(无上一代) | 旧表 ID |
换表后的抄表数据
flowchart TD
A[E-501 最后 reading<br/>2026-04-30 → 5000 度] --> B[换表 2026-05-15]
B --> C[E-501-R1 第一次抄表<br/>2026-05-31 → ?]
C --> D[计算 5月用量]
D --> E{用量公式}
E -->|新表上累计读数| F[假设新表读到 50 度<br/>但 initial_reading=5000<br/>所以 current = 5050]
F --> G[consumption = (5050 - 5000) * multiplier = 50 度]
关键:新表 initial_reading=5000 不是抄表的起点,而是用于计算用量的基准。下次抄表时:
current_reading(新表表头读数 + initial_reading)= 50 + 5000 = 5050
previous_reading = 5000(继承自旧表最后读数)
consumption = (current - previous) × multiplier = (5050 - 5000) × 1 = 50
业户付的就是 5 月实际用的 50 度,不是 5050。
[!warning] 抄表员要注意 抄表系统显示给抄表员的数字是物理表头的数字(50),系统内部存的是叠加值(5050)。如果系统设计不一致(让抄表员录 5050),会让人困惑。当前实现需查
MeterReadingsRelationManager/MeterReadingsImporter看具体如何处理。
整条链的追溯
一张表可能多次换:
E-501 (原生) → E-501-R1 (第一次换) → E-501-R2 (第二次换) → E-501-R3 (第三次换)
每张表 replaced_meter_id 指上一代。后台 / API 可以:
// 找当前在役表
$current = Meter::where('asset_id', $assetId)
->where('fee_type_id', $feeType)
->where('is_active', true)
->first();
// 顺着链向上追溯
$predecessors = [];
$cursor = $current;
while ($cursor->replaced_meter_id) {
$cursor = Meter::find($cursor->replaced_meter_id);
$predecessors[] = $cursor;
}
// $predecessors 现在是 [R2, R1, 原生] 的逆序数组
业务上可用于:
- 业户对历史用量有异议:看哪张表抄出来的
- 表更换历史报表
- 长期累计用量
nextReplacementCode() 实现
代码 Meter::nextReplacementCode($oldMeterCode) 算法:
// 伪代码
function nextReplacementCode(string $oldCode): string
{
// E-501 → E-501-R1
// E-501-R1 → E-501-R2
// E-501-R2 → E-501-R3
if (preg_match('/^(.*)-R(\d+)$/', $oldCode, $matches)) {
return $matches[1] . '-R' . ($matches[2] + 1);
}
return $oldCode . '-R1';
}
业务人员不需要手动想新表编号,系统自动算。
操作:ReplaceMeterAction
后台 → 计量表 → 找旧表 → 进 ViewMeter → 点 ReplaceMeterAction。
Modal 表单:
| 字段 | 填什么 |
|---|---|
旧表最后读数(final_reading) |
现场拍照确认,如 5000 |
退役原因(decommission_reason) |
选 Replaced(其他选项见 decommission-and-locking) |
| 退役日期 | 默认今天 |
| 新表编号 | 自动 E-501-R1(可改但不推荐) |
| 新表 multiplier | 默认继承旧表(可改) |
| 新表安装日期 | 默认今天 |
| 备注 | "校验未通过,换新表" |
提交后系统在一个事务内:
- 旧表
is_active=false,decommissioned_at=今天,decommission_reason=Replaced,final_reading=5000 - 建新表
is_active=true,installed_at=今天,replaced_meter_id=旧表.id,initial_reading=5000,multiplier=继承
常见问题
[!question] 旧表读数比新表初始低,会发生吗? 不会。新表的
initial_reading就是旧表的final_reading,逻辑上必然相等。
[!question] 换表时业户家正在用电怎么处理? 实际换表过程要断电断水短暂时间,业户可感知。系统层面:
- 旧表
decommissioned_at和新表installed_at都填换表那天- 中间用电量(几分钟到几小时)的微小差异通常忽略
- 严格的物业可在换表说明里告知业户"换表过程几分钟用电不计费"
[!question] 换表后旧表的历史 MeterReading 还能查吗? 能。每条 reading 都关联
meter_id(旧表 ID),不会因换表丢失。审计可完整追溯。
[!question] 误换表(其实不该换)能撤销吗? 不能直接撤销(MeterReading 不可变,Meter 状态也不轻易回滚)。要修复:
- 物理上把新表退役(
is_active=false,decommission_reason=Removed)- 把旧表重启(
is_active=true,decommissioned_at=null)→ 但这种"复活"操作在 Policy 层可能被守护拒绝(decommission-and-locking)预防胜于补救:换表前确认。
[!question] 同一张表换好多次,链很长怎么办? 链长本身不是问题,系统正常处理。如果链过长(>10 代),通常说明该表频繁出问题,业务上应:
- 排查表的型号 / 安装环境
- 考虑改型号 / 换品牌
- 长链不影响数据查询性能(关联查询逐级递归,但物业表数量通常不大)
[!question] 新表
multiplier与旧表不同可以吗? 可以,但强烈不推荐。如果新换的表倍率不同(例如旧 1x → 新 10x),用量计算公式就变,容易让业户困惑。除非业务上有明确升级原因(从普通家用表换成工业表),否则继承旧表 multiplier。
异常分支
- 表损坏(非校验)→ 走 replace-broken-meter 场景(meter_decommission_reason=Damaged)
- 不换表只退役 → decommission-without-replacement