diff --git a/docs/evidence/bugfix/meter-reading-billing-p0-guard-2026-06-20.md b/docs/evidence/bugfix/meter-reading-billing-p0-guard-2026-06-20.md new file mode 100644 index 0000000..488562a --- /dev/null +++ b/docs/evidence/bugfix/meter-reading-billing-p0-guard-2026-06-20.md @@ -0,0 +1,71 @@ +# 抄表开账 P0 保护修复验证记录 + +- 时间:2026-06-20 15:56:43 +0800 +- 仓库:water-backend +- 分支:develop +- 范围:普通开账前置状态校验、取消开账账单状态保护 + +## 修复内容 + +1. `ChargeServiceImpl.validateReadingDataBeforeBilling` + - 普通批量开账新增前置条件:抄表记录必须为 `CheckStateEnum.REVIEWED`。 + - 已开账或已有营业账记录仍优先返回重复开账错误。 + +2. `ChargeServiceImpl.cancelChargeByReadingDataIds` + - 空入参、无关联营业账时直接返回。 + - 关联营业账存在 `PayStateEnum.PAID` 时禁止取消开账。 + - 关联营业账存在 `PayStateEnum.SETTLED` 时禁止取消开账。 + - 禁止场景下不删除营业账明细和营业账主记录。 + +## 红灯验证 + +新增测试后、修复实现前执行: + +```bash +mvn -pl sw-business/sw-business-server -am -Dtest=ChargeServiceBillingValidationTest -Dsurefire.failIfNoSpecifiedTests=false test +``` + +结果: + +- `cancelChargeByReadingDataIds_shouldRejectPaidCharge` 失败:当前实现未抛异常。 +- `generateChargeBatch_shouldRejectMeterRecordedReadingDataWithoutReview` 失败:当前实现未在校验阶段拦截未复核记录。 + +## 绿灯验证 + +修复后执行: + +```bash +mvn -pl sw-business/sw-business-server -am -Dtest=ChargeServiceBillingValidationTest -Dsurefire.failIfNoSpecifiedTests=false test +``` + +结果: + +- Tests run: 7 +- Failures: 0 +- Errors: 0 +- Skipped: 0 +- BUILD SUCCESS + +补充执行: + +```bash +mvn -pl sw-business/sw-business-server -am -Dtest=ReadingDataServiceImplCheckValidationTest,ChargeServiceBillingValidationTest -Dsurefire.failIfNoSpecifiedTests=false test +``` + +结果: + +- Tests run: 8 +- Failures: 0 +- Errors: 0 +- Skipped: 0 +- BUILD SUCCESS + +编译验证: + +```bash +mvn -pl sw-business/sw-business-server -am -DskipTests compile +``` + +结果: + +- BUILD SUCCESS diff --git a/docs/evidence/bugfix/meter-reading-generate-cycle-bugfix-2026-06-20.md b/docs/evidence/bugfix/meter-reading-generate-cycle-bugfix-2026-06-20.md new file mode 100644 index 0000000..c25c2d0 --- /dev/null +++ b/docs/evidence/bugfix/meter-reading-generate-cycle-bugfix-2026-06-20.md @@ -0,0 +1,92 @@ +# 抄表记录定时生成重复数据修复证据 + +## 背景 + +用户反馈:册本配置为“每月抄”,但每天定时器都会生成可修改、可抄表的抄表记录;同一客户同一账期中一条记录开账结账后,其余记录无法继续开账。 + +## 根因 + +后端存在两个抄表生成入口: + +- `ReadingDataGenerateData`:调用 `ReadingDataService.generateReadingDataForAllBooks()`。 +- `meterReadingGenerateJob`:原先直接在 Job 内组装并插入 `biz_reading_data`、`biz_last_reading`,绕过服务层去重和周期判断。 + +原 `meterReadingGenerateJob` 的实现只查询 `status = 0` 的册本,没有使用 `nextReadDate` 控制当前抄表周期,也没有按 `custId + billMonth` 做重复生成保护,因此定时任务每日执行时可能持续插入同客户同账期抄表记录。 + +开账链路通过同客户同账期有效账单校验防止重复开账,因此同月多条抄表记录中,第一条开账后,其余记录会被“本月已开账/已缴费”规则拦截。 + +## 修复内容 + +1. 将 `meterReadingGenerateJob` 精简为统一委托 `ReadingDataService.generateReadingDataForAllBooks()`,删除 Job 内旧的直接插入抄表数据逻辑。 +2. 在 `ReadingDataServiceImpl.generateReadingDataForAllBooks()` 中,当册本 `nextReadDate` 判定为非当前周期时直接跳过该册本,不再生成“非当前周期”的抄表数据。 +3. 新增回归测试: + - `MeterReadingGenerateJobTest`:验证 `meterReadingGenerateJob` 委托服务层统一入口。 + - `ReadingDataServiceImplGenerateTest`:验证非当前周期册本不会查询客户、不会插入抄表数据。 + +## 验证 + +执行命令: + +```bash +mvn -pl sw-business/sw-business-server -am -Dtest=MeterReadingGenerateJobTest,ReadingDataServiceImplGenerateTest -Dsurefire.failIfNoSpecifiedTests=false test +``` + +验证结果: + +- `Tests run: 2, Failures: 0, Errors: 0, Skipped: 0` +- Reactor 构建结果:`BUILD SUCCESS` +- 验证时间:2026-06-20 15:09:01 +08:00 + +## 影响范围 + +- 后端模块:`sw-business-server` +- 涉及链路:抄表记录定时生成、抄表数据批量生成 +- 不涉及:开账互斥规则、账单生成规则、收费结账逻辑 + +## 追加修复:抄表模块逻辑审查问题 + +### 背景 + +在完成重复生成修复后,继续审查抄表录入模块,发现仍存在若干会放大重复数据、筛选错误或账期误判的逻辑风险。 + +### 修复内容 + +1. `ReadingDataServiceImpl.generateReadingDataForAllBooks()` 增加 JVM 内串行保护,降低两个 XXL-JOB 入口或重复触发在同一应用实例内并发生成同客户同账期抄表记录的风险。 +2. `ReadingDataMapper.selectByCustIdAndBillMonth()`、`selectByCustMeterIdAndBillMonth()` 和 `LastReadingMapper.selectByCustIdAndBillMonth()` 改为 `selectList + order by id desc + limit 1`,避免历史重复数据导致 `selectOne` 抛 `TooManyResultsException`。 +3. `ReadingDataServiceImpl.filterCustMeterIdsByConditions()` 将 `meterStatus` 纳入“水表筛选条件”判断,修复只按水表状态筛选时条件被跳过的问题。 +4. Excel 导入抄表账期改为优先按导入行的 `readDate` 推导账期;未传抄表日期时继续回退当前年月,避免跨月导入误查当前月记录。 + +### 回归测试 + +新增或补充以下测试: + +- `ReadingDataMapperTest` +- `LastReadingMapperTest` +- `ReadingDataServiceImplFilterTest` +- `ReadingDataServiceImplImportTest` +- `ReadingDataServiceImplGenerateTest` + +### 验证 + +执行命令: + +```bash +mvn -pl sw-business/sw-business-server -am -Dtest=ReadingDataServiceImplGenerateTest,ReadingDataMapperTest,LastReadingMapperTest,ReadingDataServiceImplFilterTest,ReadingDataServiceImplImportTest -Dsurefire.failIfNoSpecifiedTests=false test +``` + +验证结果: + +- `Tests run: 9, Failures: 0, Errors: 0, Skipped: 0` +- Reactor 构建结果:`BUILD SUCCESS` +- 验证时间:2026-06-20 15:27:12 +08:00 + +编译验证命令: + +```bash +mvn -pl sw-business/sw-business-server -am -DskipTests compile +``` + +验证结果: + +- Reactor 构建结果:`BUILD SUCCESS` +- 验证时间:2026-06-20 15:27:41 +08:00