# 功能测试报告 — 2026-07 母版补齐 & 导出件「Excel 打不开」修复 - 日期:2026-09-16 - 范围:汇总大表(`/api/report/export/summaryWorkbook`)2026-07 / 2026-08 导出;`docs/生成汇总大表/2026年7月道路运输量汇总表.xlsx` 母版补齐 - 关联:`docs/问题汇总/问题与注意事项汇总_2026-09-16.md` 第十三节 ## 一、背景 《 货运》页「累计」列口径在 2026-09-16 已在 **8 月母版** 落地(见第十二节)。用户确认「可以补齐」,要求把同一口径补到 **7 月母版**,并解决 7 月导出件无法用 Excel 打开的问题。 ## 二、测试环境 | 项 | 值 | |---|---| | 后端 | Spring Boot,8090(修复后已重启) | | 前端 | Vue,8080 | | 数据库 | MySQL 127.0.0.1:3308 / traffic_audit | | Excel 判据 | Excel COM `Workbooks.Open`(每例独立实例,测后强杀孤儿进程) | | 结构判据 | Python `zipfile` + `xml.etree` 逐部件校验、逐格 `` 解析 | ## 三、发现的根因(两处,均为本次修复对象) ### 3.1 导出件打不开:残留 `` 的非法单元格 坏件里存在 19 个**模式非法**的格子: ``` 67.067.00 ``` ``(inlineStr)只允许出现在 `t="inlineStr"` 的格里;`t="n"` 却带 `` 属于非法,Excel 直接判定「无法读取的内容」拒绝打开(COM 表现为「不能取得类 Workbooks 的 Open 属性」,连修复模式 `CorruptLoad=1` 也打不开)。 **成因**:7 月母版由 **openpyxl** 写出(`docProps/app.xml` = `Microsoft Excel Compatible / Openpyxl 3.1.5`,文本用 `` 存储)。导出时别的写入路径只改 `t`/``,**不清旧 ``**;而 8 月母版是 Excel 原生(文本走 sharedStrings),所以同样代码下 8 月件正常、7 月件坏。 **为什么只有 7 月坏**:7 月存在同期备份母版 `_备份_2026年7月道路运输量汇总表_清理前_20260904.xlsx`,`restoreSummaryMonthlyBlocks` 会整格回填;回填写入 inlineStr 母版格时留下旧 ``。8 月没有该 `_备份_*` 文件,不走回填,因此不触发。 **定位方法**(可复用):坏件与好件除 worksheets/styles/sharedStrings 外**全部部件逐字节相同** → 用 zip 杂交 + Excel 逐一开档做 leave-one-out,定位到 **sheet9《公交》(17 格) + sheet12《轨道、轮渡》(2 格)**。 ### 3.2 母版补齐被备份母版「生成又复活」 补好 7 月母版后导出,`货运` 页 2022/2021 累计**又变回旧写死常量** —— 因为 `restoreSummaryMonthlyBlocks` 把备份母版(清理前、仍是旧常量)整格回填覆盖了母版的新公式。 ## 四、修复内容 | # | 位置 | 改动 | |---|---|---| | 1 | `ReportExportService.exportSummaryWorkbook` | 落盘前新增 `normalizeStaleInlineStrings(wb)`:把所有「非 inlineStr 却残留 ``」的格子清掉(幂等),并打 warn 日志 | | 2 | `ReportExportService.restoreSummaryMonthlyBlocks` | 新增 `skipFreightHistoryBlocks`:《 货运》页 **T 列及以后**(2025/2024/2023/2022/2021 年度块 + 各年累计/累计同比列)永不从备份母版回填,避免“生成又复活”(与既有「中口径排名」左块保护同一思路) | | 3 | `docs/生成汇总大表/2026年7月道路运输量汇总表.xlsx` | Excel COM 补齐:2025/2024/2023/2022/2021 五个年度块共 **360** 个块内格(`r7/r9/r8/r10 = SUM(17 市州)`、`r5=r7+r9`、`r6=r8+r10`)+ **324** 个累计格(`DX=SUM(DK:DV)`、`EM=SUM(EA:EL)`、`DY=IFERROR(DX/SUM(EA:EL)-1,"")`),`CalculateFullRebuild` 后保存 | > 备份:`docs/生成汇总大表/_bak_2026年7月道路运输量汇总表_补齐前_20260916.xlsx`(**故意不用 `_备份_` 前缀**,否则会被 `resolveSummaryDonor` 当成 donor 回填)。 ## 五、用例表 | # | 用例 | 输入 | 预期 | 实际 | 结论 | |---|---|---|---|---|---| | 1 | 坏件复现 | 2026-07 导出(修复前) | 可打开 | Excel 报「不能取得类 Workbooks 的 Open 属性」,重试 2 次仍失败 | 复现 | | 2 | 环境排除 | 10KB 小件 / 8 月母版 / 7 月母版 / 09-08 旧 7 月导出 | — | 全部可打开;仅新 7 月导出失败 | 确认非环境问题 | | 3 | 路径排除 | 坏件复制到另一目录改名 | 仍失败 | 仍失败 | 确认是文件内容问题 | | 4 | 部件级杂交 | 坏件 + 好件的 styles/sharedStrings/worksheets | 定位坏部件 | 坏件 styles + 坏件 sst + **好件全部 sheets** → 可打开 | 定位到 worksheets | | 5 | leave-one-out | 19 个 sheet 逐个换成坏件版 | 找出坏 sheet | 仅 **sheet9《公交》/ sheet12《轨道、轮渡》** 失败 | 定位到 2 页 | | 6 | 负向:非法格计数 | 修复前导出件 | — | 19 个 `t="n"` 带 `` | 命中根因 | | 7 | 正向:修复后 2026-07 | 重启后端后导出 | 可打开、19 页、累计对 | OK sheets=19;`DX5=144979.2825`(`=SUM(DK5:DV5)`)、`DY5=-0.101235487797891`、`EM5=161309.53162`、`CU5=173045.296428512` | 通过 | | 8 | 正向:修复后 2026-08 | 同上 | 可打开、累计不变 | OK sheets=19;`DZ5=144979.2825`、`EO5=161309.53162`、`CW5=173045.296428512`、`S5=130311.892698249` | 通过 | | 9 | 跨月一致性 | 7 月 vs 8 月同口径格子 | 完全相等 | 2022 累计 / 2021 累计 / 2023 累计 三个月度 → 三个值**完全相等** | 通过 | | 10 | 非法格复扫 | 修复后 7 月导出 | 0 个 | 0 个 | 通过 | | 11 | 母版可打开 | 补齐后用 Excel 打开 7 月母版 | 可打开 | 可打开 | 通过 | | 12 | 残留数据复核 | `2026-09` 五张表计数 | 全 0 | 全 0(h204 / investment_monthly / h2032 / audit_result / import_batch) | 通过 | ## 六、发现的问题与处置 1. **7 月导出件打不开**(P0):`normalizeStaleInlineStrings` 根治;导出件从 941,960 字节(坏)→ 1,310,693 字节(好)。 2. **备份母版回填让补齐失效**(P0):`skipFreightHistoryBlocks` 保护 `货运` T 列及以后。 3. **Excel COM 会留孤儿进程**:每例测完必须强杀本次新建的 `EXCEL` 进程,否则后续 `Workbooks.Open` 会**间歇性假报**同样的错(本次排查初期被此干扰过)。属排查工具坑,已记入问题汇总。 4. **注意**:本次**未**改动 8 月母版与任何入库数据。 ## 七、结论 - 2026-07 / 2026-08 两个导出件均可被 Excel 正常打开(各 19 页),且《 货运》页 2021/2022/2023 累计列口径**完全一致**。 - 7 月母版已按 8 月母版口径补齐(年度块 360 格 + 累计 324 格),并从 openpyxl 血缘转为 Excel 原生。 - 用户要求的「残留数据全部清除」经复核为 0 残留。 ## 八、遗留 - 2026-06 / 2026-09 无母版 → 预览返回 `ready=False`「未找到模板文件」,需新建母版才能出数(用户第 5 问的延伸)。 - 仓库根目录 `_*.*` 临时排查文件(含本次 `_hyb_*` / `_loo_*` / `_v2_*` / `_final_*`)未删,待用户同意。