8.6 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 · 场景 - 一次导入整月所有读数(Excel 批量) |
|
|
|
已发布 | meter | 2026-05-26 | 2026-05-22 |
场景:一次导入整月所有读数(Excel 批量)
中型物业(几百到上千张表)、未上集抄系统的,抄表员月底一次性把全社区抄表数据填入 Excel,业务人员上传导入。
典型情境
[!example] 真实情境 嘉禾花园 300 户 + 公共部位 + 商铺,合计 1,200 张表(水电气混)。未上集抄。抄表员李师傅 + 团队每月最后一周集中抄表 5-7 天,完成后:
- 整理 Excel(1,200 行,每行一张表的本月读数)
- 王主管下载"抄表读数模板"
- 抄表数据填入模板对应列
- 上传导入
业务人员视角
第 1 步:下载抄表模板
后台 → 计量表 → 列表 → 顶部 "下载抄表模板" 按钮(ExportMeterReadingsAction,注意:命名误导,实际是下载模板)。
[!info] 命名问题(issue.md Q5)
ExportMeterReadingsAction的名字让人以为是"导出读数"(把数据从系统导出来),实际是**"下载抄表模板"**(给抄表员填的空模板,预填上次读数)。issue.md Q5 待补:重命名为DownloadMeterReadingsTemplateAction。
下载的 Excel 包含:
| 列 | 说明 | 预填 |
|---|---|---|
| 房号 | asset 编号 | ✅ 系统填(对应每张已建表) |
| 表编号 | meter code | ✅ 系统填 |
| 费用类型 | 水/电/燃气 | ✅ 系统填 |
| 上次读数 | 上月该表 reading | ✅ 系统填(供抄表员对比) |
| 本次读数 | 本月该表 reading | ❌ 空,抄表员填 |
| 抄表日期 | 本次 read_at | ❌ 空,抄表员填(或默认月底) |
| 备注 | 选填 | ❌ |
预填上次读数让抄表员对比时有参考(本次 > 上次 才合理),也防止漏填行(看清楚每行该填什么)。
第 2 步:抄表员填本月数据
抄表员李师傅团队整月(或月末几天)做这事:
- 现场抄表 + 拍照(用 App / 纸质本)
- 数据填入模板
- 完成后交回王主管
第 3 步:上传导入
后台 → 计量表 → 列表 → 顶部 "导入抄表数据" 按钮(ImportActionWithExcel + MeterReadingsImporter)→ 选 asset_type → 上传 → 提交。
系统:
- 解析 Excel(走
BaseImporter) - 每行校验(meter 存在?asset 匹配?读数合法?)
- 批量建 MeterReading(每行一条,
source=manual,operated_by=导入操作员,无 photo_url —— 走批量没拍照) - 算 consumption
- 报告:成功 N 条,失败 M 条
[!warning] 批量导入无 photo_url Excel 模板没法批量上传照片。批量导入的 reading photo_url 为空。
业务上推荐:抄表员独立留照片(手机相册按月归档),业户事后争议时翻照片(虽然不在系统里)。或者:
- 高争议表(住宅)走 read-single-meter-manual
- 低争议表(商铺 / 公共)走批量导入(节省时间)
第 4 步:核对
导入后:
MetersNeedingReadingListWidget显示"本月未抄表"清单 → 此时应 0 条(或个别遗漏)- 抽样验证几张表的 reading 数据正确
HighConsumptionReadingsListWidget看是否有异常用量
第 5 步:触发账单生成
导入完成后:
- 自动触发(
MeterReadingsImporter完成后默认调GenerateBillsFromMeterReadingsAction) - 或手动触发(
ListMeters上的"生成账单"按钮,选刚导入的 readings)
系统流程
sequenceDiagram
participant 李师傅
participant 王主管
participant Filament
participant MeterReadingsImporter
participant GenerateBills[GenerateBillsFromMeterReadingsAction]
participant 数据库
王主管->>Filament: 下载抄表模板(预填上次读数)
Filament-->>王主管: 模板.xlsx
王主管->>李师傅: 转发模板
李师傅->>李师傅: 现场抄表 + 填模板
李师傅->>王主管: 已填模板.xlsx
王主管->>Filament: 导入抄表数据 → 选 asset_type + 上传
Filament->>MeterReadingsImporter: parse + chunk 处理(100/批)
loop 每批
MeterReadingsImporter->>数据库: 开启事务
loop 每行
MeterReadingsImporter->>数据库: 校验 meter + asset
alt 通过
MeterReadingsImporter->>数据库: 建 MeterReading
else 失败
MeterReadingsImporter->>MeterReadingsImporter: 记失败行
end
end
alt 全成功
MeterReadingsImporter->>数据库: 提交
else 任一失败
MeterReadingsImporter->>数据库: 回滚整批
end
end
MeterReadingsImporter->>GenerateBills: handle(刚建的 readings)
GenerateBills->>数据库: 批量建 Bill + 回写 reading.bill_id
MeterReadingsImporter-->>Filament: 报告
Filament-->>王主管: 通知 + 失败行下载
抄表员视角(李师傅)
整月抄表流程:
- 第 1-5 周(月初):正常工作 + 部分非紧急表抄(若有时间)
- 第 25-30 日(月末):集中抄表
- 按楼栋 + 单元顺序(避免漏)
- 每户:开门(若有人)/ 看公共表(若装在外)
- 读表 → 拍照 → 填模板
- 30 日 / 月底:整理完整 Excel → 交回王主管
工作量:1,200 张表 / 月 / 1 抄表员 → 约 40 张 / 天 / 5 天工作。比单录(后台一张张点)快 3-5 倍。
业户视角
业户无感知 —— 抄表员上门时业户可能在家也可能不在(公共表 / 燃气表通常装在楼道,不用进户)。
MeterReadingsImporter 双义列名问题(issue.md Q5)
[!warning] 已知 silent corruption 风险 当前
MeterReadingsImporter用一个 Importer 处理两种 Excel layout(住宅单元 vs 商铺/公共),列 label 是"双义"形式(如'层编号/费用类型'),靠$this->options['asset_type']决定列含义。风险:导入时用户选错
asset_type:
- 系统不报错(列存在,数据有值,看着像合法)
- 但数据写到错误字段(silent corruption)
- 业务人员事后核对才发现数据错位
修复(issue.md Q5 待补):拆成
MeterReadingsImporterForUnit+MeterReadingsImporterForShop,每个列含义固定。当前预防:导入时务必仔细确认 asset_type 选项 + 抽样核对前几行数据。
常见问题
[!question] 导入失败的常见原因?
- meter 不存在(asset 或 code 找不到对应表)
- meter 已退役(
is_active=false)- 读数倒走(本次 < 上次)
- 读数格式错(非数字 / 含中文)
- asset_type 选错(silent corruption,不报错但数据错)
[!question] 已导入想撤销? Reading 不可改 / 不可删(若已生成 Bill 更严)。撤销 = 走 exception-readings-locked-after-bill 流程,复杂。
预防:导入前抽样核对 Excel + 选对 asset_type + 小批量先试。
[!question] 同一张表本月被重复导入(填了两次 Excel)? 看
MeterReadingsImporter是否有"同 meter + 同 read_at 不允许"的守护。如果没有 → 系统建两条 reading → 两条都算用量 → 业户被算两遍。严重 bug,需要业务人员核对避免重复导入。
[!question] 部分表本月没抄怎么办?
MetersNeedingReadingListWidget会显示"本月未抄"清单。业务人员可:
- 让抄表员补抄(走 read-single-meter-manual)
- 让业户自报读数(部分物业接受,但需核对)
- 跳过本月(下月一起算,业户账单可能突然变高)
[!question] 商铺 / 公共表与住宅表能合并导入吗? 看 Excel 模板设计。当前asset_type 是必选项,即一次导入只能处理一种类型。要分两次导入(住宅一次,商铺一次,公共一次)。
[!question] 导入完成后立即生成账单还是等月底? 取决于业务流程:
- 导入即生成:
MeterReadingsImporter完成后自动调 GenerateBills(默认推荐)- 手动触发:业务人员后续审核 reading 后再触发生成
当前实现应是"自动生成"模式(看 Importer 配置)。
异常分支
- 单张表录入 → read-single-meter-manual
- 集抄自动 → read-via-iot-remote-source
- 已导入发现错 → exception-readings-locked-after-bill
- 高用量异常 → exception-high-consumption
- 抄表完成率审计 → audit-meters-needing-reading