Files
uniprop-manual/prop-acc/scenarios/meter/read-batch-via-excel-import.md
2026-05-26 00:18:06 +08:00

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 批量)
批量抄表
Excel 导入抄表
read-batch-via-excel-import
MeterReadingsImporter
场景-Excel 批量抄表
场景
prop-acc
计量表
抄表
批量导入
业务人员
抄表员
已发布 meter 2026-05-26 2026-05-22

场景:一次导入整月所有读数(Excel 批量)

中型物业(几百到上千张表)、未上集抄系统的,抄表员月底一次性把全社区抄表数据填入 Excel,业务人员上传导入

典型情境

[!example] 真实情境 嘉禾花园 300 户 + 公共部位 + 商铺,合计 1,200 张表(水电气混)。未上集抄。抄表员李师傅 + 团队每月最后一周集中抄表 5-7 天,完成后:

  1. 整理 Excel(1,200 行,每行一张表的本月读数)
  2. 王主管下载"抄表读数模板"
  3. 抄表数据填入模板对应列
  4. 上传导入

业务人员视角

第 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 → 上传 → 提交。

系统:

  1. 解析 Excel(走 BaseImporter)
  2. 每行校验(meter 存在?asset 匹配?读数合法?)
  3. 批量建 MeterReading(每行一条,source=manual,operated_by=导入操作员,无 photo_url —— 走批量没拍照)
  4. 算 consumption
  5. 报告:成功 N 条,失败 M 条

[!warning] 批量导入无 photo_url Excel 模板没法批量上传照片。批量导入的 reading photo_url 为空

业务上推荐:抄表员独立留照片(手机相册按月归档),业户事后争议时翻照片(虽然不在系统里)。或者:

第 4 步:核对

导入后:

  • MetersNeedingReadingListWidget 显示"本月未抄表"清单 → 此时应 0 条(或个别遗漏)
  • 抽样验证几张表的 reading 数据正确
  • HighConsumptionReadingsListWidget 看是否有异常用量

第 5 步:触发账单生成

导入完成后:

  • 自动触发(MeterReadingsImporter 完成后默认调 GenerateBillsFromMeterReadingsAction)
  • 手动触发(ListMeters 上的"生成账单"按钮,选刚导入的 readings)

详见 bill-generation-pipeline

系统流程

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. 第 1-5 周(月初):正常工作 + 部分非紧急表抄(若有时间)
  2. 第 25-30 日(月末):集中抄表
    • 按楼栋 + 单元顺序(避免漏)
    • 每户:开门(若有人)/ 看公共表(若装在外)
    • 读表 → 拍照 → 填模板
  3. 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 配置)。

异常分支

相关文档