# 功能测试报告:货运量周转量表多年度工作簿(2021–2026)一次性入库 2026-09-16 ## 功能说明 - 需求:8 月导入的「定稿-湖北省各市州运输量数据(2026年1-8月).xlsx」一个工作簿内含 21–26 年(源文件缺 2023 年)全部年度数据,要求把这些数据全部读入数据库。 - 旧行为:`importFreightTurnover` 只读第 1 个 Sheet,并按「模板_货运量周转量.xlsx」的左右两半(左半今年 1–N 月、右半去年 1–12 月)解析,导致其余年度 Sheet 的数据被直接丢弃。 - 新行为:导入时先判断是否为「多年度工作簿」(Sheet 数 ≥ 2、每个 Sheet 名形如 `2021年`、且首行 A 列含「货运量」、B 列含「月」)。 - 命中:逐 Sheet 解析整年 1–12 月的货运量块与周转量块,**每个年度落一个报表期**(报表期 = 该年最后一个有货运量数据的月份,本次为 `YYYY-12`),上年同期取上一年度 Sheet。 - 未命中:完全回退原有月度解析路径,行为与改动前一致。 - 幂等:仍按报表期「先删后插」;每个年度各写一条 `import_batch` 记录。 ## 涉及改动 - 后端 `traffic-audit-server/src/main/java/com/trafficaudit/dataimport/service/DataImportService.java`(+153 / −19): - `importFreightTurnover` 改为分流入口;原逻辑整体抽为 `importFreightTurnoverByMonth`(逻辑与改动前逐行一致)。 - 新增 `detectYearSheets`(多年度识别)、`importFreightTurnoverByYear`(按年解析 + 上年同期)、`parseWholeYearSheet`(整年 Sheet 解析)、`persistFreightTurnover`(按报表期先删后插 + 记批次)。 - 顶部新增 `import java.util.TreeMap;`。 - 未改数据库结构、未改前端、未改导入接口签名(仍为 `POST /api/import/freightTurnover?period=yyyy-MM`)。 ## 测试环境 - 后端:traffic-audit-server 8090(含本次改动,`start-dev.ps1` 启动成功,backend.log 出现 `Tomcat started on port(s): 8090` / `Started TrafficAuditApplication`)。 - 前端:traffic-audit-web 8080(本次不涉及前端)。 - 数据库:本机 MySQL `127.0.0.1:3308/traffic_audit`,用 mysql CLI 直查核对。 - 源文件:`D:\05_数据分类\高速\定稿-湖北省各市州运输量数据(2026年1-8月).xlsx`,Sheet 为 `['2026年','2025年','2024年','2022年','2021年']`(2026/2025 各 38 行×15 列,2024 48 行×13 列,2022 40 行×15 列,2021 41 行×14 列)。 - 负向/边界用例一律在一次性测试期 `2099-01` ~ `2099-04` 内进行,全程不触碰真实月份;测试后已逐个清空。 ## 用例表 | # | 用例 | 输入 | 预期 | 实际 | 结论 | |---|---|---|---|---|---| | 1 | 正向-多年度导入 | 上传源文件,`period=2026-08` | 21/22/24/25/26 五个年度全部入库 | 返回「共 90 条记录」(5 个报表期 × 18 行);库内新增/覆盖 `2021-12`、`2022-12`、`2024-12`、`2025-12`、`2026-08` | 通过 | | 2 | 正向-入库抽查(逐项对齐) | 源文件逐年逐市州逐月与库内逐项比对(今年货运量/今年周转量/上年货运量/上年周转量) | 完全一致 | 比对 **1080 个单元格,差异 0**(脚本 `_verify_ft_multi.py`) | 通过 | | 3 | 正向-报表期语义 | 查看各期 `report_period` | 每年度一个报表期 | `2021-12 / 2022-12 / 2024-12 / 2025-12 / 2026-08`;源文件无 2023 年,故未生成 `2023-12` | 通过 | | 4 | 正向-上年同期 | 各期 `last_*` 是否取上一年度 Sheet | 有上一年度的有值、无的为空 | `2022-12`、`2025-12`、`2026-08` 各 18 行 `last_*` 全部有值;`2021-12`(无上年)、`2024-12`(缺 2023)为空 | 通过 | | 5 | 正向-导入批次 | 查询 `import_batch` | 每个报表期一条记录 | 首次导入写入 id 324–328(2021-12/2022-12/2024-12/2025-12/2026-08),5 条均 total=success=18、fail=0,file_name 为源文件名 | 通过 | | 6 | 负向-单 Sheet 月度模板回退 | 单 Sheet 月度模板文件 + `period=2099-01` | 识别不命中,回退月度路径 | 返回「共 18 条记录」,与改动前一致 | 通过 | | 7 | 负向-非年度命名多 Sheet 回退 | 两个非年度命名 Sheet(Sheet1/Sheet2)+ `period=2099-02` | 识别不命中,回退月度路径 | `code=200`,18 条 | 通过 | | 8 | 负向-混合命名回退 | 年度名 + 非年度名混合(2021年/2022年/备注说明)+ `period=2099-03` | 识别不命中,回退月度路径 | `code=200`,18 条 | 通过 | | 9 | 负向-非 Excel 文件 | 文本文件当 xlsx 上传 + `period=2099-04` | 报错且不落库 | `{"code":500,"message":"导入失败: Can't open workbook - unsupported file type: UNKNOWN"}` | 通过 | | 10 | 幂等-重复导入 | 同一源文件再导一次 | 不产生重复行、值不变 | 仍返回「共 90 条记录」;导入后再写 id 332–336;**全表 216 行前后 md5 一致**(`a2a733b2c5321a9d5efa9be2e0c5dacb`) | 通过 | | 11 | 回归-测试期清理 | 逐个 `POST /api/import/clear?period=2099-0x` | 测试期数据与批次全部删除 | 各期 dataRows=18、batchRows=1 删除成功;当前库 **0 条 2099 数据、0 条 2099 批次** | 通过 | | 12 | 回归-真实月份总量 | 统计 `freight_turnover_import` | 与导入前口径一致 | 12 个报表期 × 18 行 = **216 行**(2021-12 / 2022-12 / 2024-12 / 2025-12 / 2026-01…2026-08) | 通过 | ## 发现的问题与处置 1. **源文件缺 2023 年 Sheet**:导致不生成 `2023-12` 报表期,`2024-12` 无上年同期(`last_*` 为空)。属源数据缺口,非代码缺陷;已如实反映在库内。 2. **报表期语义为「整年」**:`2021-12` 等期实际承载该年 1–12 月的全年数据(而非 12 月单月)。若后续报表/审核按「单月」口径取这些期,需要用户确认口径;本次未做改动。 3. **重复导入会覆盖同报表期**:新逻辑延续「先删后插」的幂等策略,重导 `2026-08` 会覆盖该期原有值。本次已逐项核对源文件与库内一致(差异 0),无数据损失。 4. **备份不完整(已记录)**:改动前的备份 `_backup_freight_turnover_before_multiyear_20260916.sql` 只有 59642 字节且仅解析出 `2026-04` 一处 INSERT,不能作为完整回滚依据。**不要把该文件当作完整备份使用。** 5. **2026-01…2026-07 的 `last_*` 为空**:经核对备份(`2026-04`)确认这几期在本次改动前 `last_*` 即为空,且本次导入未触碰这些报表期(批次记录只覆盖 5 个期),故非本次改动引入。 ## 结论 - 通过。多年度工作簿(21–26 年)已全部入库,逐年一个报表期、逐年上年同期,1080 个单元格逐项比对零差异;非多年度文件自动回退原有月度路径,负向与边界用例均符合预期;重复导入幂等,测试期已清理干净,真实月份数据与改动前口径一致(216 行 / 12 期)。 - 待用户确认:`2021-12` 等「整年」报表期的业务口径是否符合后续报表/审核取数要求;如需「逐月报表期」,需要另行改造(本次未做)。