# 交通货运统计审核系统 — 问题与注意事项汇总 > 用途:把项目运行、数据导入、报表生成、仓库协作中所有「踩过的坑」集中记录,供后续开发与运维快速查阅。 > 维护方式:新发现问题请直接在本文件追加,并注明日期。 > 最后更新:2026-09-16 --- ## 一、速查表(最常见的 10 条) | # | 事项 | 正确做法 | |---|------|----------| | 1 | 启动前后端 | 项目根目录执行 `powershell -NoProfile -ExecutionPolicy Bypass -File start-dev.ps1` | | 2 | 端口占用判断 | 看 `backend.log` / `traffic-audit-web/frontend.log`,**不要**依赖 `Get-NetTCPConnection` | | 3 | 后端就绪标志 | 日志出现 `Started TrafficAuditApplication` + `Tomcat started on port(s): 8090` | | 4 | 前端就绪标志 | 日志出现 `Compiled successfully` + `App running at:` | | 5 | 连数据库 | `mysql.exe --host=127.0.0.1 --port=3308 --user=root --default-character-set=utf8mb4 -D traffic_audit` | | 6 | 停旧进程 | 按 PID 精确停 `cmd → java(maven) → java(app)` 链;**不要杀 node_repl 和用户自己的 cmd 窗口** | | 7 | 脚本报「已添加了具有相同键的项」 | 本机同时存在 `Path` 与 `PATH`,改用 `cmd.exe /c ... > log 2>&1`,**严禁用 `Start-Process`** | | 8 | 写中文文件 | 用 UTF-8;`main.js` 带 BOM 必须写回 BOM;改文件前先确认原文件是 CRLF 还是 LF | | 9 | 提交前 | 先测试 + 写《功能测试报告》+ 更新《工作日结》,再 fetch/合并/推送 | | 10 | 推公司 gitea | 先 `git fetch gitea` 与远端 `main` 合并,**禁止强推** | --- ## 二、启动与进程管理 | 问题 | 原因 | 处置 | |------|------|------| | `Start-Process` 报「已添加了具有相同键的项」 | 环境变量同时存在 `Path` 与 `PATH`(值相同) | 统一用 `cmd.exe /c ... > log 2>&1` 或 `ProcessStartInfo` 启动 | | 前端在沙箱内起不来(`EPERM: lstat`) | node 访问用户目录被沙箱拦截 | `start-dev.ps1` 整体在沙箱外执行 | | 嵌套 `powershell.exe -Command` 引号被破坏 | 外层 shell 先解析一层引号 | 直接运行 `.ps1` 文件,不做嵌套 | | 重启时误杀 Codex 内核 | 按名字杀 `node.exe` 会命中 `node_repl` | 按 PID 精确停后端链 `cmd → java(maven) → java(app)`、前端链 `cmd → node(npm) → cmd → node(vue-cli-service)` | | 前端日志找不到 | 日志在子目录 | `traffic-audit-web/frontend.log`;后端 `backend.log` 在仓库根目录 | **重启标准流程**:列出 `java.exe` 进程 → 停 `app` → `maven` → `cmd` → 运行 `start-dev.ps1`(脚本自动跳过已在监听的端口)。 --- ## 三、服务状态验证 - 沙箱内 `Get-NetTCPConnection` 会因权限「拒绝访问」而**静默失败**,据此判定端口未监听是错的。 - 判断服务状态一律以日志为准;HTTP 探活用: - 后端:`GET http://127.0.0.1:8090/api/auth/captcha-config`(返回 `{"code":200,...}` 即通) - 前端:`http://localhost:8080/#/login` - 前端日志出现 `Proxy error: Could not proxy request /api/... (ECONNREFUSED)` 说明**后端当时没起来**,不是前端问题。 --- ## 四、数据库 - 连接(本地开发库): `& 'C:\Program Files\MySQL\MySQL Server 8.0\bin\mysql.exe' --host=127.0.0.1 --port=3308 --user=root --password=<见 AGENTS.md> --default-character-set=utf8mb4 -D traffic_audit` - **必须写 `--host=127.0.0.1`**:写成 `-h127.0.0.1` 会被解析成 host `127`,连接失败。 - 输出过滤 `2>&1 | Select-String -NotMatch 'Warning'` 可屏蔽密码警告。 - 常用表与关键列: | 表 | 说明 | 关键列 | |----|------|--------| | `freight_turnover_import` | 货运量/周转量 | `report_period`、`region_name`、`freight_m01..m12`、`turnover_m01..m12`、`last_*` | | `h2031_enterprise_monthly` | 公路旅客 H203-1 月报 | `report_period`、`enterprise_code`、`unified_credit_code`、`report_unit` | | `import_batch` | 导入批次 | `import_type`、`report_period`、`file_name`、`success_rows` | | `audit_result` / `audit_run` | 审核结果/运行 | `report_period` | - 排查「某期数据在不在」:`SELECT report_period, COUNT(*) FROM <表> GROUP BY report_period ORDER BY report_period;` - 排查「某期导入来自哪个文件」:查 `import_batch` 的 `file_name`。 - **表结构变更要同步三处**:实际库 DDL、`docs/init.sql`、`docs/database.md`。 - 测试用报表期建议用 `2099-xx`,测完用 `POST /api/import/clear?period=...` 或直接 DELETE 清理,避免污染真实月份下拉项。 --- ## 五、数据导入:文件路径与期次约定 ### 5.1 货运量/周转量(`/api/import/freightTurnover`) | 源文件 | 覆盖期次 | 备注 | |--------|----------|------| | `D:\05_数据分类\高速\定稿-湖北省各市州运输量数据(2026年1-8月).xlsx` | 2021-12 / 2022-12 / 2024-12 / 2025-12 / 2026-08 | **无 2023 年 Sheet** | | `D:\05_数据分类\货运\7月\定稿-湖北省各市州运输量数据(2026年1-7月).xlsx` | 多一个 `2023` Sheet | 2023 年数据的来源;2021/2022/2024 与 8 月文件逐值一致 | - 多年度工作簿识别规则(`detectYearSheets`): 1. **至少 2 个 Sheet**(单 Sheet 文件不识别为多年度工作簿,会走老的月度模板路径); 2. 每个 Sheet 名匹配 `^\d{4}\s*年?$`(`2023年`、`2023` 都可以); 3. 每个 Sheet 首行 A1 含「货运量」、B1 含「月」。 任一条件不满足 → 整体回退老的月度模板解析。 - 每个年度 Sheet 的结构:上方是「货运量(万吨)」块(约 20 行),下方是「周转量(万吨公里)」块;**同一市州名第二次出现即视为周转量块**。 - 报表期语义:**`2021-12` 这类期放的是该年 1–12 月的全年数据**(`m01..m12` 全部有值),不是 12 月单月。该年最后有数据的月份决定期号(2026 年只有 1–8 月 → `2026-08`)。 - 上年同期:多年度文件里自动取**上一年度 Sheet**;若上一年度不存在(如 2021 无 2020 Sheet),`last_*` 为空。 - 只补某一年时,可另存一个只含该年 + 上一年的临时工作簿再导入(注意「至少 2 个 Sheet」规则)。 ### 5.2 公路旅客 H203-1(`/api/import/passengerMonthly`) | 源文件 | 备注 | |--------|------| | `docs\公路旅客\模板_交企统H203-1表_公路旅客运输月度生产情况.xlsx` | 用户修改后的模板 | | `D:\桌面\测试数据\公路旅客\八月\公路旅客运输月度生产情况(2026)_2026-09-09 09_18_45.xls` | 系统导出的老格式样例 | | `D:\需要导入的数据\公路旅客\*.xls` | 历史各月导出文件 | - 解析**按表头名取值**(`buildHeaderMap`),因此模板在中间插列/整体右移**不会**破坏其它字段。 - 新模板在「企业名称」后新增 2 列:`统一社会信用代码`、`填报单位`;老格式文件其实也带这两列(此前代码未读取)。 - 必需列缺失会直接报错,例如选错类型时提示「表头缺少必需列【企业名称】【客运量_总计】」。 ### 5.3 其它 - 导入失败必须在页面显示**行号**(用户要求),新增解析逻辑时不要吞掉行号信息。 --- ## 六、报表生成 - 「汇总大表」预览弹窗由 `ReportExport.vue` 渲染: - 弹窗内**只保留一张指标表**(分类 / 指标 / 数据 / 同比),「中口径」之后不再渲染任何表格; - 弹窗支持拖动(`v-dialog-drag` 指令,拖标题栏)+ 10px 圆角。 - 导出依赖**母版模板文件**:`docs/生成汇总大表/YYYY年M月道路运输量汇总表.xlsx`。缺失时报「母版定位失败:未找到模板文件」,**不是数据问题**。 - 接口层面自查(绕过前端,定位是前端还是后端问题): - 预览:`GET /api/report/preview?period=2026-08&mode=month&types=summaryWorkbook` - 导出:`GET /api/report/export/summaryWorkbook?...` - 失败提示已细分:能区分「连不上服务器」和「后端返回的具体原因」;导出接口是 `responseType:'blob'`,报错时 `data` 也是 Blob,必须先 `text()` 再解析。 --- ## 七、登录与验证码 | 问题 | 原因 | 处置 | |------|------|------| | 登录页出现空白方框 + 「验证码加载失败,请检查网络」 | 前端 `captchaRequired` 默认 `true`,读取 `/api/auth/captcha-config` 失败时静默忽略,页面残留加载不出来的验证码框 | 默认值改 `false`,改为 fail-safe:只有后端明确返回 `required === true` 才显示;请求失败按「不需要验证码」处理 | | 开关位置 | 后端 `application.yml` 的 `app.captcha.required` | 关掉后应显示「测试模式:行为验证码已跳过」 | | 换网络/换 IP 后接口 403 | 跨域白名单 | CORS 需放行内网网段(已修复 `0d2a5fc`) | --- ## 八、文件写入与文本处理的坑 - 本机文件**换行符不统一**:`Login.vue`、`main.js` 是 CRLF;`ReportExport.vue`、`DataImportService.java`、`docs/*.md` 是 LF。用 `[System.IO.File]::WriteAllText` 时必须匹配原换行符,否则后续 `-replace` / 字符串匹配会失败。 - `main.js` 带 BOM(`EF BB BF`),改写时要用 `New-Object System.Text.UTF8Encoding($true)`。 - **`git checkout -- <文件>` 会把 LF 转成 CRLF**(本仓库 `core.autocrlf=true`),会让后续基于 LF 的替换失效;操作前先确认真实换行符。 - PowerShell 多行中文替换建议写 `*.py` 脚本用 `python` 执行(`apply_patch` 在本机不可用)。 - 字符串替换**注意锚点唯一性**:`| enterprise_name | varchar(200) | 企业名称 |` 在 `docs/database.md` 出现 4 次,直接 replace 会改错表;必须先截取目标章节再替换。 - 读 `.xls`:本机 python 无 `xlrd`,可用 Excel COM(`New-Object -ComObject Excel.Application` → `Workbooks.Open` → `Cells.Item(r,c).Text`,记得 `Quit()` + `ReleaseComObject`)。 --- ## 九、浏览器自动化取证的坑 - `tab.playwright.evaluate()` 读 DOM 才可信;AX 树可能读到缓存。 - Playwright locator 对 Element UI 表格内按钮**会超时**(`isVisible()` 恒为 false);改用 AX 索引 `tab.click(index)`。 - `tab.drag([x1,y1],[x2,y2])` 拖拽时,坐标必须落在**当前视口**内(本机内嵌浏览器视口很小,约 319×790),否则报 `Coordinate is outside the active tab content viewport`。 - `tab.click` 的 AX 索引**在页面刷新后会失效**,可能命中别的控件;每次操作前重新 `cua.getTab(...)` 取新索引。 - 截图:`const fs = await import('fs'); fs.writeFileSync(path, Buffer.from(await tab.getScreenshot()))`(`require` 不可用)。 - 改 `.vue` 后需要 `tab.reload()`。 - 临时脚本/文件(`_*.*`)未经用户同意**不要删**。 --- ## 十、Git 双仓库工作流 | 仓库 | remote | 分支 | 地址 | |------|--------|------|------| | 个人 | `origin` | `master` | `https://gitee.com/zhizhijie/traffic-audit.git` | | 公司 | `gitea` | `main` | `http://61.183.254.94:3000/r/trafficAudit.git`(账号 zyj) | 每次改动的固定顺序: 1. 测试 + 写《功能测试报告》 + 更新《工作日结》; 2. `git fetch origin` → 合并个人仓库新提交(避免覆盖用户改动); 3. `git fetch gitea` → 与远端 `main` 对比合并 → **禁止强推**; 4. `git commit` → `git push`。 - 凭据只走本机 git/临时 askpass,**禁止写进仓库任何文件**。 - 公司 gitea 网络不通时记录为遗留问题,**不要**用强推绕过。 --- ## 十一、当前已知遗留问题(截至 2026-09-16) | # | 问题 | 影响 | 状态 | |---|------|------|------| | 1 | 8 月源文件缺 2023 年 Sheet | 已用 7 月文件补录 `2023-12` 解决 | 已解决 | | 2 | 2026-01 ~ 2026-07 的 `last_*` 为空 | 这几期改动前即为空,非本次引入 | 待用户确认是否补 | | 3 | 2026-01 ~ 2026-07 的 H203-1 `unified_credit_code` / `report_unit` 为空 | 新列本期才读取,历史期未回填 | 待用户确认是否重导 | | 4 | 预览弹窗「共 0 行」与「月度覆盖:1~8月均有数据」口径不同 | 易误解 | 待用户确认改文案 | | 5 | 公司 gitea 推送 | 网络无响应,未推成功 | 待补推 | | 6 | 仓库根目录大量 `_*.*` 临时文件 | 体积/可读性 | 经用户同意后再清理 | > 2026-09-16 傍晚更新:遗留问题 4(预览弹窗「共 0 行」文案)已按用户确认修改完毕——汇总大表改为显示「指标 N 项」,其它报表显示「共 N 行明细」。 --- ## 十二、2026-09-16 补充:货运页签「累计」列修复(重要口径) ### 12.1 母版派生口径(必须遵守) 《 货运》页行结构: - 全省块:r5 货运量 / r6 周转量 / r7 其中规上货运量 / r8 其中规上周转量 / r9 其中规下货运量 / r10 其中规下周转量; - 17 个市州块每块 6 行,首行 11,17,23,...,107(武汉市...林区),块内顺序:合计货运量 / 规上货运量 / 规下货运量 / 合计周转量 / 规上周转量 / 规下周转量。 **规则:全省行必须完全由 17 市州行派生。** - r7 / r8 / r9 / r10 = SUM(17 市州对应行); - r5 = m7+m9,r6 = m8+m10。 历史教训:r9/r10(全省规下)曾是**写死常量**,与 17 市州规下之和不等 —— 2023 差 4.6e-3、2022 差 3.1e-3、2021 差 1.5e-4(2024/2025/2026 恰好为 0),导致 6 个累计列里 2022/2023 比源文件人工核算差 0.003 左右、2021 整列为空。2026-09-16 已全部改为公式。 累计列现状(全省行,4 位小数与源文件一致): | 列 | 年度 | 公式 | |---|---|---| | S | 2026(1-8月) | C+E+G+I+K+M+O+Q | | AT | 2025 | V+X+...+AR | | BV | 2024 | AX+AZ+...+BT | | CW | 2023 | BY+CA+...+CU | | DZ | 2022 | =SUM(DM:DX)(原为写死常量) | | EO | 2021 | =SUM(EC:EN)(原为**空**) | | EP | 2021 累计同比 | 保持为空(页内无 2020 块,无基期) | | EA | 2022 累计同比 | =IFERROR(DZ/SUM(EC:EN)-1, 空串)(原为写死常量) | ### 12.2 母版改动方式与文件职责 - **必须用 Excel COM**(改公式 -> CalculateFullRebuild -> Save),**不可用 openpyxl 另存**:会清空全本公式缓存值(曾导致 2021 年湖北省行变 0)。 - 导出件里的公式**无缓存值**:openpyxl(data_only=True) 读出来全是 None,核对数值必须用 Excel COM 重算或自写求值器。 - 文件职责: - YYYY年M月道路运输量汇总表.xlsx = **母版**(唯一数据来源); - 生成_道路运输量汇总表.xlsx = **只作列宽参照**(applySummaryReferenceLayout 只读列宽/隐藏标记,不读数值); - 2026年8月道路运输量汇总表_版式原型.xlsx = 运行时**无任何代码引用**。 - 同期备份母版还原机制:resolveSummaryDonor 只认文件名**以 `_备份_` 开头且含「{年}月」**的文件,命中即整格回填覆盖母版。=> 自建的备份文件/目录**不要**用 `_备份_` 前缀,否则可能被当成 donor 把母版改回旧值。 ### 12.3 文本处理的坑(本机 PowerShell / git) | 问题 | 原因 | 处置 | |---|---|---| | 公式被写成 =SUM(DM5) | "$m7" 被解析为变量 m7;"$r:DX" 里的 $r:DX 被当成「驱动器 r:」而吞掉 | 一律用 ${m} / ${r} 定界 | | 命令整体不执行、无输出、退出码 1 | 一个命令里出现**嵌套的单引号 here-string**,外层在第一个结束符处提前结束 | 一个命令只放一个 here-string;或先 Out-File 写补丁文件,再用 Get-Content -Raw | | apply_patch 传多行参数失败 | .bat 包装器破坏了多行参数 | 直接调 codex.exe --codex-run-as-apply-patch $patch | | git commit -m 报 "outside repository" | PowerShell 用反引号转义,消息里的反斜杠+引号会提前结束字符串 | 用 git commit -F <消息文件> | | PS5.1 报乱码路径「无法找到 ...」 | PS7 的 Out-File -Encoding utf8 写的是**无 BOM** UTF-8,PS5.1 按 GBK 读脚本 | 写 .ps1 时用 -Encoding utf8BOM | ### 12.4 遗留问题增量(截至 2026-09-16 晚) | # | 问题 | 影响 | 状态 | |---|---|---|---| | 7 | h2032_enterprise_monthly 2026-09 脏期(实为 8 月文件误存为 9 月) | 636 行 + import_batch 302 | **已删除**,留可还原 SQL 备份 | | 8 | 《 货运》页累计列 2021/2022/2023 对不上人工核算 | 已修母版派生口径,6 列全部对上 | **已解决** | | 9 | 2026-09 期还在 h204_vehicle_quarterly(1284 行) / investment_monthly(31 行) / audit_result(2061 行) / audit_run(3 行) | 若按 2026-09 出报表会取到脏数据 | 待用户确认是否清理 | | 10 | 2026-07 导出件《 货运》页为 118x145(比 8 月少 2 列) | 7 月母版是「1-4 月骨架」,非完整母版 | 待 7 月母版定稿 | | 11 | 2026年8月道路运输量汇总表_版式原型.xlsx 的 S 列漏 8 月(缺 Q5) | 运行时未被引用,暂无影响 | 待确认该文件去留 | | 12 | 母版经 Excel 重存后体积 959,706 -> 1,492,121 字节(导出件 950,186 -> 1,331,599) | 纯格式开销,数据无影响 | 记录 | 详见《功能测试报告/功能测试报告_货运页签累计列修复与脏期清理_2026-09-16.md》。 --- ## 十三、2026-09-16 补充:2026-07 母版补齐 & 导出件「Excel 打不开」根因(P0) ### 13.1 症状与根因 | 项 | 内容 | |---|---| | 症状 | `GET /api/report/export/summaryWorkbook?period=2026-07&mode=month` 生成的 xlsx,**Excel 打不开**(COM 报「不能取得类 Workbooks 的 Open 属性」;`CorruptLoad=1` 修复模式同样打不开);2026-08 同接口正常 | | 直接原因 | 导出件里有 **19 个模式非法格**:`67.067.00` —— `t="n"` 不允许带 ``(inlineStr 专用) | | 深层原因 | 7 月母版由 **openpyxl** 写出(`docProps/app.xml` = `Microsoft Excel Compatible / Openpyxl 3.1.5`,文本存 ``);8 月母版是 **Excel 原生**(文本走 sharedStrings)。POI 的部分写入路径只改 `t`/``、不清旧 `` | | 触发路径 | 7 月存在 `_备份_2026年7月道路运输量汇总表_清理前_20260904.xlsx` → `restoreSummaryMonthlyBlocks` 整格回填 → 往 inlineStr 格里写值留下旧 ``。8 月无 `_备份_*`,不走回填,故正常 | | 命中位置 | leave-one-out 定位到 **sheet9《公交》17 格 + sheet12《轨道、轮渡》2 格** | ### 13.2 修复(`traffic-audit-server`) 1. **`exportSummaryWorkbook` 新增 `normalizeStaleInlineStrings(wb)`**:落盘前清掉所有「非 `inlineStr` 却残留 ``」的格子(幂等),命中即打 warn 日志。 2. **`restoreSummaryMonthlyBlocks` 新增 `skipFreightHistoryBlocks`**:《 货运》页 **T 列及以后**(2025/2024/2023/2022/2021 年度块 + 各年累计/累计同比列)**永不从备份母版回填** —— 与既有「中口径排名左块永不回填」同一思路,避免补齐被「生成又复活」。 ### 13.3 2026-07 母版补齐(口径与 8 月母版对齐) - 年度块(每列):`r7/r9/r8/r10 = SUM(17 市州对应行)`、`r5 = r7+r9`、`r6 = r8+r10` → 360 格; - 累计列:`DX = SUM(DK:DV)`(2022)、`EM = SUM(EA:EL)`(2021,原**整列为空**)、`DY = IFERROR(DX/SUM(EA:EL)-1,"")`(2022 累计同比,原为**写死常量**)→ 324 格; - 2023 累计 `CU = BW+BY+…+CS` 本来就正确,未改;`EN`(2021 累计同比)按口径保持为空。 - 一手证据:母版 `DX5=144979.2825`(旧常量 `144979.285376043`,差 0.0029 = 全省规下常量与 17 市州之和的差)、`EM5=161309.53162`、`CU5=173045.296428512`,与 8 月母版 `DZ5/EO5/CW5` **完全相等**。 - 备份:`docs/生成汇总大表/_bak_2026年7月道路运输量汇总表_补齐前_20260916.xlsx`。**切勿改名为 `_备份_` 开头**,否则会被 `resolveSummaryDonor` 当 donor 回填。 ### 13.4 本次新增的排查坑 | 问题 | 原因 | 处置 | |---|---|---| | Excel COM **间歇性**报同一个错,导致误判「8 月件也打不开」 | 每次 `New-Object -ComObject Excel.Application` 留下的**孤儿 EXCEL 进程**会污染后续实例 | 每例测完把「本次新建的 EXCEL 进程」强杀再测下一例;`Quit()`+`ReleaseComObject` 并不够 | | 编译报「找不到 isSetSheetData()」 | `CTWorksheet.sheetData` 是 XSD **必填**元素,不生成 `isSetXxx` | 改用 `ctw.getSheetData() != null` | | 部件级 diff 太大跑不动 | 对 519KB 的 `styles.xml` 直接 `difflib` 是 O(n²) | 先按部件比字节尺寸/是否相等,只在必要的小部件上做 diff | ### 13.5 遗留问题增量 | # | 问题 | 影响 | 状态 | |---|---|---|---| | 13 | 2026-06 / 2026-09 无母版 | 预览 `ready=False`「未找到模板文件」,无法出数 | 需先建母版(用户第 5 问的延伸) | | 14 | 仓库根 `_*.*` 临时文件继续增多(`_hyb_*` / `_loo_*` / `_v2_*` / `_final_*` / `_fixed_*` / `_t7_*` / `_poijar`) | 体积/可读性 | 经用户同意后再清理 | > 第 10 项(2026-07 导出件少 2 列 / 7 月母版是「1-4 月骨架」)本节已处理:导出端会**自动补出 5/6/7 月表头与公式**并重算累计公式,母版已补齐 2021/2022/2023 累计列,故关闭该项。