--- title: prop-acc · meter · 场景 - 新社区批量建表 + 初始读数 Excel 导入 aliases: - 批量建表 - 新社区计量表初始化 - init-new-community-batch - 场景-新社区批量建表 tags: - 场景 - prop-acc - 计量表 - 表管理 - 批量导入 audience: - 业务人员 - 抄表员 status: 已发布 sub_feature: meter last_review: 2026-05-26 code_version: 2026-05-22 --- # 场景:新社区批量建表 + 初始读数 Excel 导入 物业**新接管社区**(或老社区从 0 接入本系统),需要**批量建表 + 录初始读数**。通过 `MeterInitializationImporter` + `ImportActionWithExcel` 一次性导入。 ## 典型情境 > [!example] 真实情境 > 平台新签了"嘉禾花园"社区,本月底接管。该社区有 300 户业主 + 公共部位 + 商铺,合计约 1,200 张表(每户水电气 3 张 + 公共部位 + 商铺各类)。 > > 物业财务王主管不可能手工建 1,200 张表,**走批量导入**: > > 1. 抄表员李师傅 + 物业前任团队 出 Excel 表(包含每张表的房号、表号、初始读数) > 2. 王主管在系统下载"建表初始化模板" > 3. 把数据填入模板 → 上传 → 系统批量建表 ## 业务人员视角 ### 第 1 步:下载初始化模板 后台 → 计量表 → 列表 → 顶部 **"下载初始化模板"** 按钮(`ExportMeterInitializationTemplateAction`)。 下载到的 Excel 包含: | 列 | 说明 | 示例 | |---|---|---| | 房号 / 资产编号 | 关联 asset(必填,系统按房号查 asset_id) | 12-3-501 | | 费用类型 | 水费 / 电费 / 燃气费(必填) | 电费 | | 表编号 | 物理表牌号(必填) | E-501 | | 倍率 | multiplier(可选,默认 1)| 1 | | 初始读数 | initial_reading(必填,首次接管时表上的读数)| 0 / 8523 / etc. | | 安装日期 | installed_at(可选,默认导入日)| 2026-05-26 | | 备注 | (可选) | "新装" | 模板列名清晰,业务人员 / 抄表员看得懂。 > [!warning] 模板列含义"双义"问题(已知 issue) > 当前 `MeterInitializationImporter` 用**一个 Importer 处理两种 Excel layout**(住宅单元 vs 商铺/公共)。某些列 label 是"双义"形式,导入选项里选错 `asset_type` 不报错,只是数据写到错误字段(silent corruption)。issue.md Q5 已记录,待拆成两个独立 Importer。**当前预防**:务必仔细选 asset_type。 ### 第 2 步:填写数据 物业 / 抄表员把 1,200 张表的信息填入模板。 关键字段对齐: - 房号:对应系统里 asset 表已存在的编号(若不存在,先到 community 模块建 asset) - 费用类型:对应系统配置的 FeeType(水/电/燃气,各社区独立配置) - 表编号:物业自编(常见 `<费用类型简写>-<房号>` 模式) - 初始读数:**接管当天**的物理表读数(关键!首次接管之前的用量物业不管) ### 第 3 步:上传 + 导入 后台 → 计量表 → 列表 → 顶部 **"导入初始化"** 按钮(`ImportActionWithExcel` + `MeterInitializationImporter`)→ 选 asset_type(住宅 / 商铺)→ 上传 Excel → 提交。 系统: 1. **解析 Excel**(走 `BaseImporter` + `ImportActionWithExcel`,支持 .xlsx / .xls / .csv) 2. **每行校验**(房号 asset 存在?费用类型存在?表编号是否在该社区重复?) 3. **批量建 Meter**(每行一张 Meter 记录) 4. **可选:同时建第一条 MeterReading**(若模板含"初始读数",建初始 reading 来锁定 `previous_reading` 起点) 5. **报告**:成功 N 张,失败 M 张 + 每条失败的原因 > [!info] BaseImporter + chunk rollback > 走 `App\Filament\Importers\BaseImporter`(host 基类,详见 saas-baseline 规范)+ `TransactionalImportCsv` job。一批 100 行任意一行失败 → 该批全回滚。 > > 这避免"部分建好部分没建"的脏中间态。失败的批可下载"失败行" Excel,修复后再导入。 ### 第 4 步:核对 导入后: - 后台 → 计量表 → 按社区过滤 → 看是否 1,200 张表都在 - 抽样核对:打开几张表看初始读数对不对 ### 第 5 步:启动月度抄表 接管下一个月 → 抄表员去现场抄读数(走 [[read-batch-via-excel-import]] 或 [[read-single-meter-manual]])→ 系统按 `current - initial × multiplier` 算用量 → 生成第一份账单(走 [[bill-generation-pipeline]])。 ## 系统流程 ```mermaid sequenceDiagram participant 王主管 participant Filament participant ImportActionWithExcel participant MeterInitializationImporter participant 数据库 Note over 王主管: 已填好 1200 行 Excel 王主管->>Filament: ListMeters → 导入初始化 → 选 asset_type + 上传 Filament->>ImportActionWithExcel: parse .xlsx ImportActionWithExcel->>MeterInitializationImporter: 按 chunk 处理(100 行/批) loop 每批 MeterInitializationImporter->>数据库: 开启事务 loop 每行 MeterInitializationImporter->>数据库: 校验 asset / fee_type / code 唯一 alt 校验通过 MeterInitializationImporter->>数据库: 建 Meter + 可选 initial MeterReading else 校验失败 MeterInitializationImporter->>MeterInitializationImporter: 收集失败行 end end alt 全成功 MeterInitializationImporter->>数据库: 提交 else 任一失败 MeterInitializationImporter->>数据库: 回滚整批 end end MeterInitializationImporter-->>Filament: 报告 "成功 1198,失败 2" Filament-->>王主管: 通知 + 下载失败行 Excel ``` ## 业户视角 业户**不感知**这一步。新接管社区会发个公告"本月起本物业系统升级,各位业户的水电气计量将从 X 月 X 日起按本系统记账"。具体业户看到的: - 接管前最后一份账单(由前任物业 / 自建系统出) - 接管后第一份账单(由本系统出,用量从接管那天起算) 中间**绝不能有"重复账单"或"漏账"** —— 接管时的 `initial_reading` 必须准确反映物理表当时读数。 ## 常见问题 > [!question] 为什么需要"初始读数"? > 系统计算用量公式是 `(current - previous) × multiplier`。新表的"上一次读数"在系统里没有,所以接管时存的 `initial_reading` 就是"`previous_reading` 的起点"。后续每月抄表 → 当前 - 上次 = 用量。 > > 如果不填初始读数 → 第一次抄表算用量会爆炸(`current - 0 = 所有历史用量`),业户被收一笔巨账,投诉。 > [!question] 导入失败的常见原因? > - **房号(asset)不存在**:在 community 模块的 asset 还没建好。先建 asset 再导入表 > - **费用类型不存在**:RatePlan 没配置。先到 FeeType 配置 > - **表编号在该社区重复**(社区内 code 应唯一,虽然 issue.md Q5 提到目前是 nullable + 非 unique 的"待治理"状态) > - **倍率格式错**(非数字 / 负数) > - **初始读数格式错**(非数字 / 负数) > [!question] 失败的行可以单独处理吗? > 可以。导入完成后系统提供"下载失败行"Excel(走 host 的 `TransactionalImportCsv` 机制),业务人员修复后单独导入失败行。已成功的不影响。 > [!question] 同一社区多次导入会重复建表吗? > 视 Importer 实现。若有 unique 校验(asset_id + fee_type_id 不可重复)→ 重复行会失败,需手工合并。若无校验 → 重复建,**灾难**。 > [!question] 老社区已经有一年的抄表历史,接管时怎么办? > 简化做法:**只导入接管那天的状态**(initial_reading = 接管那天的物理读数),历史数据**不进系统**(在 Excel 备查)。 > > 复杂做法:**导入历史 reading 数据**,让本系统有完整历史。需要业务方决定(用户对账复杂度 vs 系统数据完整度的权衡)。 > [!question] 商铺表 / 公共部位表怎么导入? > 同样走 `MeterInitializationImporter`,但**选不同 asset_type**(public / shop)。导入时系统按 asset_type 决定列含义。详见 issue.md Q5"双义列名"问题。 ## 异常分支 - 单张新表(后续装机 / 个别加表)→ [[register-single-meter]] - 老表换新表 → [[replace-broken-meter]] - 抄表(初始化后日常)→ [[read-batch-via-excel-import]] / [[read-single-meter-manual]] ## 相关文档 - [[meter-vs-meter-reading]] - [[register-single-meter]] - [[read-batch-via-excel-import]] - [[bill-generation-pipeline]]