编辑 | blame | 历史 | 原始文档

功能测试报告_汇总大表异步导出与缓存_2026-09-20

一、功能说明

用户上报(问题 3):**「汇总大表点下载后要过一会才反应下载,现在数据库的数据还不是很多,那数据库的数据多了这个程序会卡死吗?」**

背景:汇总大表导出是**同步请求**——点击后浏览器要等 10~20 秒才拿到文件(母版读取+回填 6.5s、整本约 4 万格公式重算 4.1s、写盘传输 3.4s),期间页面无任何反馈;
且生成过程一直占着 HTTP 请求线程,多人同时点会让容器线程池被长任务占满。

本次改造(用户确认「可以」):

  1. 异步任务化:新增 ReportExportTaskService(固定 1 线程池 report-export-task,daemon)。
    前端提交后立即拿到「任务号」,再按 700ms 轮询进度(步骤 + 百分比 + 已用时长),完成后单独下载文件。
  2. 结果缓存:同一报表期 10 分钟内的成功结果直接复用(fromCache=true),跳过母版读取与公式重算;
    缓存带**数据版本守卫**——数据版本取 SELECT IFNULL(MAX(id),0) FROM import_batch,期间只要有新导入就自动失效(取不到版本时禁用缓存)。
  3. 去重与排队:同一报表期已有任务在跑时直接复用该任务,不会重复排队重算。
  4. 前端进度弹窗:进度条 + 步骤文案 + 已用时长 + 缓存来源时间 + 失败原因;生成中可「后台运行」(收弹窗,完成后仍自动下载),完成后可「保存文件 / 重新生成 / 关闭」。
  5. 进度点:2% 校验母版 → 6% 读取母版 → 12% 还原月度块 → 22% 回填当月数据 → 45% 刷新排名页与跨页合成页 → 70% 重算整本公式 → 95% 写出文件 → 100% 完成。

回答用户疑问:**数据量增大主要是 SQL 聚合变慢,不会「卡死」**;改造后生成走后台线程、同报表期结果可复用、同报表期并发自动去重,
即使多人同时点也只是排队(弹窗显示「排队中」),不会占满 Web 线程。

二、测试环境

  • 后端 Spring Boot 8090(Started TrafficAuditApplication 15:10:53)、前端 Vue 8080(Compiled successfully)
  • 数据库 MySQL localhost:3308/traffic_audit
  • 报表期:2026-08(月报,正常用例)/ 2025-01(无数据,负向用例)
  • 接口脚本:_qa_task.ps1、_qa_task2.ps1、_qa_task3.ps1(保留未删)
  • 浏览器实测:Playwright 1.57 + 本机 Chrome(无头),真实点击「报表生成」页按钮
  • 证据截图:docs/功能测试报告/汇总大表异步导出进度弹窗_2026-09-20.png

三、用例表

3.1 接口层

编号 用例 输入 预期 实际 结论
T-A1 提交异步任务 GET /api/report/exportTask/summaryWorkbook?period=2026-08&mode=month 立即返回 taskId,不阻塞 提交耗时 0.07s,进度 6→22→45→70→95→100,终态 DONE,**14.4s** 通过
T-A2 下载任务文件 GET /api/report/exportTask/{id}/file 返回 xlsx 1,325,109 字节,**1.27s** 通过
T-A3 10 分钟内同报表期复用 再次提交同参数 fromCache=true、同 taskId、近乎瞬时 fromCache=true,**0.01s**,taskId 相同 通过
T-A4 强制重算 提交 refresh=true 新任务号、真实重算 新 taskId,14.2s 重算完成 通过
T-A5 运行中并发去重 连发两次 refresh=true 返回同一 taskId 同一 taskId,11.1s 通过
T-A6 新增导入使缓存失效 临时插入一条 import_batch(id 533→534)后提交 fromCache=false,重新生成 fromCache=false,11.4s 重算;**测试后已 DELETE 清回 id=533** 通过
T-A7 负向:不存在的 taskId GET /exportTask/notexist123 / .../file 返回明确错误 状态接口 code=500「导出任务不存在或已过期(生成结果保留 30 分钟)」;文件接口 HTTP 500 + 同提示 通过
T-A8 负向:未完成就下载 任务 RUNNING 时请求 /file 拒绝并说明 抛「导出任务尚未完成(当前状态:RUNNING)」 通过

3.2 浏览器实测(真实点击)

编号 用例 输入 预期 实际 结论
UI-1 进度弹窗 报表生成 → 汇总大表 → 2026-08 → 点「导出」 立即弹窗并逐档推进 弹窗即时出现,实测采样:0.7s 读取母版工作簿(6%) → 1.5s 回填当月数据(22%) → 2.2s 刷新排名页与跨页合成页(45%) → 7.2s 重算整本公式(约 4 万格)(70%) → 10.1s 写出文件(95%) → 11.6s 完成(100%) 通过
UI-2 完成后自动下载 同上 自动触发保存 下载 生成_道路运输量汇总表_2026-08.xlsx,**1,325,109 字节**,与接口产物同哈希 通过
UI-3 缓存命中提示 关闭后再次点「导出」 提示来源时间 + 「下载」按钮 「上次生成用时 11.3 秒」「本次直接使用 2026-09-20 15:43:50 生成的结果(10 分钟内且期间没有新导入数据)」,按钮变「下载」,秒级完成 通过
UI-4 「重新生成」 缓存态点「重新生成」 真实重算 进度从 0 重新推进(「已用 2.1 秒」),按钮区切回「保存文件/重新生成/后台运行」,23.4s 内完成并自动下载 通过
UI-5 「后台运行」 生成中点「后台运行」 收起弹窗、任务继续、完成仍自动下载 弹窗收起 + 提示「已转入后台生成,完成后会自动下载」,随后自动下载 1,325,109 字节 通过
UI-6 负向:无数据报表期 选 2025-01 → 导出 → 确认「仍要导出」 显示失败原因、可关闭、不可保存 弹窗显示「生成失败|未找到模板文件: 2025年1月道路运输量汇总表.xlsx」,X 可关闭,「保存文件」禁用,「重新生成」可用 通过
UI-7 产物一致性 冷启动件 vs 刷新重算件 数据一致 两次产物解包后**逐条目一致**(无差异条目);三份缓存下载件 SHA256 完全相同 通过

3.3 顺手复验(用户问题 2)

编号 用例 输入 预期 实际 结论
P-1 预览同比项是否补全 汇总大表预览弹窗 8 项同比都在 12 行(货运/城市客运/中口径 × 当月·累计 × 量·周转),同比列齐全 通过
P-2 「累计货物周转量·累计同比 0%」是否为 bug 查库 scale_split_transport 应为取数源本身的值 库内 total_yoy=0.00000346(约 0.0003%)→ 一位小数四舍五入即 0%,非计算错误 通过(非缺陷)

四、发现的问题与处置

  1. ReportExport.vue 出现重复的 computed 键(本次浏览器实测发现,属严重回归)
    新增的异步导出计算属性(taskRunning/taskDone/taskCacheTime)另起了一个 computed: {,
    JS 对象字面量**同名键后者覆盖前者**,导致原有的 username / realName / myModules / visibleModules 计算属性全部丢失,
    「报表生成」页 created() 直接抛 Cannot read properties of undefined (reading 'filter'),**整页白屏不可用**。
    → 已把 4 个计算属性并入原 computed 块(并加注释说明不可另起同名 key),编译通过并浏览器复验正常。
    (接口层测试无法发现此类问题,这也是本次坚持做浏览器点击实测的原因。)
  2. 提交/轮询失败时弹窗会空转:后端统一返回 Result(错误时 HTTP 仍 200、data 为空),
    原代码直接 this.taskInfo = res.data || {} 会得到一个空对象 → 进度停在 0% 且不给提示。
    → 提交与轮询都改为先判 res.code !== 200 || !res.data:提交失败立即显示失败原因;轮询判为「任务已失效」并停止轮询。
  3. 生成中弹窗无出口:原「关闭」按钮 :disabled="!taskDone",生成中三个按钮全灰、右上角 X 也隐藏,用户被锁在弹窗里等 10~20 秒。
    → 改为生成中显示「后台运行」(收弹窗、继续生成、完成自动下载),结束后才显示「关闭」;X 在成功或失败后均可见。
  4. 缓存态文案歧义:命中缓存时元信息仍写「已用 11.3 秒」(其实是上次生成耗时)。
    → 缓存态改为「上次生成用时」,避免误以为本次也算了 11.3 秒。

五、结论

  • 接口用例 8 条、浏览器实测 7 条、复验 2 条,**全部通过**;无遗留未验证项。
  • 汇总大表导出现在是「点击即反馈 → 进度可视 → 完成即下载」,缓存命中时秒级返回;同一报表期并发自动去重,生成不占 Web 线程。
  • 附带修复了会导致报表生成页整页白屏的重复 computed 回归(该问题在接口测试中不可见)。