# 功能测试报告_汇总大表异步导出与缓存_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` 回归(该问题在接口测试中不可见)。