payment-cache-refactor-report-20260824.md 8.3 KB

发放明细缓存改造 — 执行与验证报告

日期:2026-08-24
环境:本地 spring-boot:run(8088)+ 生产 DMS 121.43.55.7:2101
数据规模:发放明细约 32,933 条(集中在 202608 单月);叶榭镇 T_YEXIE 约 9,881 条


1. 改造目标与结论

目标 结论
取消启动时全表发放明细热缓存 ✅ 已达成 — 启动约 4.3s,无 payment 预热日志
按人/批/镇/月走 DMS search 条件查询 ✅ 已达成 — states=0,3,支持 c_user_id / c_batch_id / c_pay_month + 镇村
作用域短缓存(32 键、TTL 15min) ✅ 已达成 — 二次请求显著加速
工作台图表改 batch_stat 聚合 ✅ 已达成 — 不再扫全表明细
写路径 invalidate 不再触发全表重载 ✅ 已达成 — invalidatePaymentCache() 仅清 scope map

总体结论:改造按改动清单完成,编译通过,核心读路径与业务流程可用;按批首开仍约 30~40s(DMS 翻页物理上限),命中 scope 缓存后 <300ms。


2. 改动清单执行状态

Phase 1 — DmsClient 条件查询 ✅

  • 新增 STATES_DEFAULT_PAYMENT = "0,3"
  • 新增 selectContentPage(..., states, searchJson)selectContentAllWithSearch(...)
  • 新增 buildEqSearchJson(...) 静态构造器

Phase 2 — pagePayment / getPayment 按需 DMS ✅

  • pagePayment:按 personnelIdc_user_id;按 batchIdc_batch_id;按镇/月 → scope 查询
  • getPayment(id):单条 DMS 查
  • fillTownBatchFromDetails / fillDistrictFromDetails / fillMissingBatchTotalsFromDetails 改为按批 DMS 拉取

Phase 3 — 作用域短缓存 + workbench 走 stat ✅

  • paymentScopeCache:最多 32 键,TTL 15 分钟,LRU 式淘汰
  • listPaymentsByBatch / listPaymentsByScope / listPaymentsByMonthScope 走 scope cache
  • buildRegionDashboard / warmVillageRegionDashCache 月度 series 改 aggregateMonthFromLists + batch_stat

Phase 4 — 取消启动预热 + invalidate 轻量化 ✅

  • warmPersonnelOnReady 保留 region / personnel / batch / alert,移除 payment
  • warmPaymentCacheAsync() → no-op
  • invalidatePaymentCache() → 仅 paymentScopeCache.clear()(不再异步全表重载)
  • 可选优化 invalidatePaymentScopeForBatch(batchId) 已提供;写路径仍调全量 clear(32 条成本低,未逐一替换)

Phase 5 — 编译、探针、业务流程、报告 ✅

  • mvn compile -DskipTests 通过
  • 热路径探针、定向探针、DMS 基准、E2E 脚本均已跑(见下文)

3. 涉及文件

文件 变更要点
DmsClient.java search 条件分页、全量翻页、states 0,3
BizDataService.java 移除全表 paymentListCache;新增 paymentScopeCache 及按需加载
BizFlowService.java 看板/workbench 改 stat 聚合;applyPayrollHeadcountlistPaymentsByMonthScope

前端 无需改动,仍调用相同 API。


4. 性能验证

4.1 启动

指标 改造前(参考) 改造后(实测)
Spring Boot 就绪 ~50s+(含 payment 全表预热) 4.3s
人员缓存(异步) 同步阻塞或同量级 ~39s 后台 [personnel-cache] loaded 32939
告警缓存 158ms(11 条)

4.2 DMS 直连基准(bench-dms-payment-query-v2.js

场景 耗时 条数
按人单页(5 次 p50) 117ms 1
镇+月单页 p50 225ms 9,882
镇+月全量翻页 17.3s 9,882 / 99 页
全表全量翻页 59.0s 32,933 / 330 页

4.3 后端 API 探针

热路径脚本 perf-hot-paths.js(第二次请求为 warm,阈值 300ms)

接口 cold warm 结果
GET /api/regions 148ms 4ms OK
GET /api/dashboard/region-stats 189ms 4ms OK
GET /api/dashboard/workbench 21.7s 211ms OK
POST /api/payments/details/page(镇) 92ms* 62ms OK
POST /api/batches/page 48ms 7ms OK
POST /api/personnel/page 59ms 30ms OK

* 探针顺序在 workbench 之后,镇级明细已部分预热。

定向探针(独立 cold / warm)

场景 cold warm total
按批 TB202608-T_YEXIE + 镇 35.9s 186ms 9,881
按人 P33040 159ms 4ms 0
workbench 叶榭 6 月 20.8s 58ms

Scope 缓存日志(服务端)

[payment-scope] month|202608|T_YEXIE| loaded 9881 in 9767ms
[payment-scope] scope|202608|T_YEXIE| loaded 9881 in 10718ms

5. 业务流程测试

5.1 e2e-api-flow-test.js(59 项)

结果 数量
PASS 39
FAIL 20

与本次改造相关的接口均通过

  • POST /api/payments/details/page(待审批、已回盘)→ PASS,code=200
  • GET /api/dashboard/summary → PASS
  • POST /api/batch-stats/pagerecalculate → PASS

失败项原因(与改造无关)

  • 脚本硬编码批次/人员 ID(VB202604001MB202604002P001 等)在生产库不存在
  • 业务模型已改为「仅全区月批 + 镇统计切片」,脚本仍尝试「创建村级批次 / 镇审村批」
  • Excel 导入探针上传空文件导致解析失败

5.2 e2e-biz-gates-full.js(43 项)

结果 数量
PASS 12
FAIL 31

失败主因:脚本依赖已废弃的村级批次闭环ensure-current-month 返回 400「不再创建村级批次」),与发放明细缓存改造无关。门禁类断言(未过节点不可出盘/领导批等)在缺批次时仍正确返回 400/404。

5.3 业务场景 UX 预期(改造后)

场景 首开 缓存命中 说明
批次明细弹窗(batchId+town) ~30–40s <200ms 需 DMS 按批翻页拉全量再内存分页
人员历月(personnelId) ~110–160ms <10ms 最优路径
工作台图表 ~20s(含村级 dash 预热) <300ms 改走 batch_stat,不再扫明细
按镇查当月明细(无 batchId) ~10s <100ms 镇+月 scope

6. 内存与运维

建议
JVM 堆 当前 1 个月明细规模:-Xmx1~1.5g 足够;不再常驻 3 万+ PaymentDetailVO
10 年预估 若每月 ~1 万条、仅热缓存 32 个 scope,内存与改造前全表热缓存相比可控
索引 PG 已建 idx_ffmx_pay_monthidx_ffmx_batch_ididx_ffmx_user_ididx_ffmx_month_town;单页有提升,全量翻页仍受 DMS 分页次数限制
写后一致性 写操作仍调 invalidatePaymentCache() 清 scope;15min TTL 内极端情况下可能读到旧 scope(可后续改按 batchId 精准失效)

7. 风险与后续建议

  1. 按批首开慢(~36s):根因是 DMS 按 c_batch_id 翻页 9,881 条;若前端常开批次明细,可考虑:
    • 后端 pagePayment 在仅有 batchId 时改为 DMS 端分页(不先 selectContentAllWithSearch);
    • 或批次 summary 单独缓存合计字段,明细弹窗纯分页不拉全量。
  2. workbench 首开 ~20s:村级 dash 预热 + 当月/上月 listPaymentsByMonthScope;可进一步减少村级并发预热范围。
  3. E2E 脚本过期:建议更新 e2e-api-flow-test.js / e2e-biz-gates-full.js 为「人员资格审批 + 全区月批」新流程。
  4. 写路径精准失效:高并发写场景可将 invalidatePaymentCache() 替换为 invalidatePaymentScopeForBatch(batchId),减少无关 scope 被清。

8. 验证命令速查

# 编译
mvn -q compile -DskipTests

# 启动
mvn -DskipTests spring-boot:run

# 热路径(warm < 300ms)
node scripts/perf-hot-paths.js

# 全量 API 冒烟
node scripts/e2e-api-flow-test.js

# DMS 直连基准
node scripts/bench-dms-payment-query-v2.js

9. 签收清单

  • DmsClient search + states=0,3
  • BizDataService 按需查询 + scope 短缓存
  • BizFlowService 看板/workbench 改 stat
  • 取消 payment 启动预热
  • 编译通过
  • API 探针与业务流程脚本执行
  • 本报告归档

报告人:Cursor Agent(自动执行改动清单 + 验证)
关联脚本scripts/perf-hot-paths.jsscripts/e2e-api-flow-test.jsscripts/bench-dms-payment-query-v2.js