# 交通货运统计审核系统 — 问题与注意事项汇总 > 用途:把项目运行、数据导入、报表生成、仓库协作中所有「踩过的坑」集中记录,供后续开发与运维快速查阅。 > 维护方式:新发现问题请直接在本文件追加,并注明日期。 > 最后更新: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 行明细」。