# 功能测试报告_汇总表「公式扩页 + 生成件重算」_2026-09-18 ## 一、功能说明 修复《道路运输量汇总表》生成件的三条主线问题(用户 2026-09-18 提出): | 主线 | 现象 | 根因 | 本次处置 | |---|---|---|---| | 1 | 城市客运 / 中口径明细 8 月整列为空、累计=1~7 月 | `SummaryWorkbookFiller.ensureMonthFormulas()` 的**页范围**只含 6 个台账页,漏了这两页(同比公式用的页范围却是含的,所以出现"同比列有公式、当月值列空白") | 页范围改用 `isMonthLedgerSheet`(含 城市客运 / 中口径明细 / 公路总客运 / 中口径客运量) | | 2 | 公交/出租/网约车/城市分市州的**全省行**停在上一期 | 全省行是 `=C9+C13+…`(Σ17市州)公式,但生成件**不带缓存值**,预览/二次处理只读到母版上一期的旧值 | 生成收尾新增 `recalcWorkbookFormulas()`,逐格 `evaluateFormulaCell` 把结果写回缓存值;失败兜底 `fullCalcOnLoad=true` | | 3(部分) | 中口径排名数据不对 | `K3/O3` 是 `SUM(K4:K20)` 公式(同样无缓存值);`M3/Q3` 同比是写死的库内全省口径 | 本次只解决"SUM 无缓存值";写死同比口径 + `loadMidClassMap` 缺两维**留待下一步**(改动会动口径,需先确认) | 改动文件: - `traffic-audit-server/src/main/java/com/trafficaudit/reportexport/calc/SummaryWorkbookFiller.java` - `ensureMonthFormulas()` 页范围修正;新增派生页结构性常量行按月沿用;新增 `isDerivedFormulaSheet()` / `structuralConstant()`;移除已无用的 `isFilledLedgerSheet()`;加 logger。 - `traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java` - 新增 `recalcWorkbookFormulas(XSSFWorkbook)`,并在 `exportSummaryWorkbook()` 写文件前调用。 ## 二、测试环境 - 后端 Spring Boot(8090)、前端 Vue(8080),`start-dev.ps1` 启动;MySQL 127.0.0.1:3308 / traffic_audit。 - 母版:`docs/生成汇总大表/2026年8月道路运输量汇总表.xlsx`(同期备份母版 `_备份_2026年8月…` **不存在**,日志 WARN,属已知背景)。 - 报表期:`2026-08`;入口:`GET /api/report/export/summaryWorkbook?period=2026-08&mode=month`。 - 产物:`_tmp/_qa_out/gen_2026-08_after.xlsx`(1,329,471 B,生成耗时 12.6 s)。 - 证据:`_tmp/_qa_out/{verify2.txt, verify3.txt, verify5.txt, diff_old_new.txt, after_cols.txt, xml_check.txt, v_cmp.txt}`。 - 后端日志:`汇总工作簿公式重算完成:成功 40713 格,失败 0 格`、`派生页结构性常量行按月沿用 4 格(第 8 月)`。 ## 三、用例表 ### 正向用例 | # | 输入 | 预期 | 实际 | 结论 | |---|---|---|---|---| | TC1 | 生成 2026-08 汇总表,检查「城市客运」Q 列(8 月当月值) | 每行应有当月值公式(跨表引用右移) | Q4~Q39 共 36 格全部有公式,如 `Q9 = =公交!Q16+出租!Q16+网约车!Q16+'轨道、轮渡'!Q9` | 通过 | | TC2 | 同上,检查「中口径明细」Q 列 | 180 行有当月值(公式或结构性常量) | 180 格有值(176 公式 + 4 常量) | 通过 | | TC3 | 抽查「中口径明细」结构性常量行(武汉 城际城乡巡游出租/网约车 = 0) | 按月沿用 0,不留空 | Q13=0.0、Q14=0.0、Q107=0.0、Q108=0.0,日志记「沿用 4 格」 | 通过 | | TC4 | 核对全省行 = Σ17 市州(独立求和 vs 缓存值) | 两者相等 | 公交 12315.37 = 12315.37;出租 5786.81 = 5786.81;网约车 4997.74 = 4997.74 | 通过 | | TC5 | 检查 8 月累计列(S) | 应含 8 月(=C+E+…+Q) | 公交 S5=110776.34、出租 S5=48069.02、网约车 S5=33864.94、中口径明细 S5=31528.06(改动前分别停在 1~7 月) | 通过 | | TC6 | 检查「中口径排名」全省行 | `=SUM(K4:K20)` 应有缓存值 = Σ17市州 | K3 缓存 24481.7407(与手工 Σ17 市州一致)、O3 缓存 1131273.449 | 通过 | | TC7 | 检查公式是否被破坏(重算是否误用 evaluateInCell) | 公式与缓存值并存(`` + ``) | XML 全部为 `……`,公式保留 | 通过 | | TC8 | 检查「城市汇总」「中口径分析」等跨页页 | 随报表期刷新,不受本次改动影响 | 城市汇总 总客运量 269628.07(1-8月),母版为 237412.33(1-7月) | 通过 | ### 负向 / 边界用例 | # | 输入 | 预期 | 实际 | 结论 | |---|---|---|---|---| | TC9 | 母版同期备份(`_备份_2026年8月…`)缺失 | 不中断生成,记 WARN | 日志 `汇总工作簿未找到同期备份母版…`,正常出件 | 通过(已知背景) | | TC10 | 公式重算遇到不支持函数/外部引用 | 跳过该格并计数,不中断导出 | 本次失败 0 格;代码保留逐格 try-catch + 失败样例日志 | 通过(未触发) | | TC11 | 派生页「全月同常量」判定被误用于台账页 | 台账页不得沿用上月数值(防"结转") | `structuralConstant()` 仅在 `isDerivedFormulaSheet()` 为真时生效(6 个台账页不适用) | 通过(逻辑隔离) | | TC12 | 重算误伤数值格(非公式格) | 只处理 `CellType.FORMULA` 的格 | 遍历时跳过所有非公式格 | 通过 | ### 入库抽查(库 → 汇总表逐项对齐) | 对象 | 库(`city_bus_monthly` 2026-08) | 生成件 | 结论 | |---|---|---|---| | 武汉市 客运量 | 3856.83 | 公交页 Q9 = 3856.83 | 一致 | | 黄石市 客运量 | 879.12 | 公交页 Q13 = 879.12 | 一致 | | 17 市州合计 | 12315.37 | 公交页 Q5(全省行缓存)= 12315.37 | 一致 | | 武汉市 城市内客运量 | 3837.20 | 公交页 Q11 = 3837.20(城市客运页 Q6 引用它) | 一致 | ## 四、回归比对(改动前 vs 改动后) 逐格对比改动前后两次生成件(`diff_old_new.txt`): | 页 | 差异格数 | |---|---| | 城市客运 | 36 | | 中口径明细 | 180 | | 其余 17 页(含货运、公交、出租、网约车、班线包车、排名页、跨页合成页) | 0 | | **合计** | **216** | 差异全部落在本次目标页,且都是「空 → 公式」;其它页逐格一致,无回归。 ## 五、发现的问题与处置 1. **`FormulaEvaluator.evaluate(Cell)` 无副作用**(POI 4.1.0):首次实现用了 `evaluate()`,日志显示"成功 40713 格"但文件里没有缓存值(`` 无 ``)。改用 `evaluateFormulaCell(Cell)` 后写入正常。已把结论写进代码注释。 2. **中口径排名同比仍为写死值**:SUMMARY 里 `M3=0.7424 / Q3=0.2317` 仍是母版/库内全省口径,与 Σ17 市州(24481.74)口径不一致 —— 需按第 3 步统一(涉及口径判定,未在本次改动)。 3. **独立《生成_公路旅客中口径明细》口径缺口**:`loadMidClassMap()` 只装"公路班线 + 城际城乡公交"两维,缺巡游出租、网约车 —— 未改,留待第 3 步。 4. **库内 h2031「全省」行 ≠ Σ17 市州**(1-8 月 27524.49 vs 24481.74,差 3042.75):属业务口径问题,建议业务侧核对,不阻塞。 ## 六、结论 - 用户报的"8 月没数据 / 全省行停在上一期"两条主线**已修复并经回归验证**:城市客运、中口径明细 8 月列补齐;全省行、累计列、排名 SUM 均带正确缓存值。 - 改动面可控(216 格,全部命中目标页),无回归。 - 第 3 步(中口径排名同比口径统一 + `loadMidClassMap` 补两维)待用户确认后实施。