用户需要把系统打包部署到另一台机器。本轮交付三件事:
package-deploy.py(新增)一条命令完成:前端构建 → 把 dist 内嵌进后端 jar(拷进 src/main/resources/static)→ mvn clean package → 清空 static(保持仓库干净)→ 组装部署目录 → 压缩 zip。
产物:
- 目录 deploy/traffic-audit-deploy-YYYYMMDD/
- 压缩包 traffic-audit-deploy-YYYYMMDD.zip(本次 69.9 MB / 246 个文件)
部署目录结构(沿用 8 月版包习惯):
backend/ jar(含内嵌前端)+ docs/ + application.yml + start-backend.cmd/.sh
frontend/ dist/ + nginx.conf.example (仅 nginx 方案需要)
db/ init.sql + sql_city_bus.sql + sql_city_taxi.sql + sql_wyc.sql
README-部署说明.txt
JwtUtils:jwt.secret 非法 Base64 导致登录 500(根因修复)application.yml 的 jwt.secret 填成中文串后,登录接口返回 500 {"code":500,"message":"20018"}。DatatypeConverter.guessLength 抛ArrayIndexOutOfBoundsException,被 GlobalExceptionHandler 兜成 500。部署同事第一次改配置很容易踩。JwtUtils 新增 secretKeyBytes()——能按 Base64 解码就用解码结果(与历史 token 兼容),否则按 UTF-8 字节处理并打 WARN。docs/打包部署说明.md)旧版(2026-08-27)有三处与现状不符,本次一并改正:
| 旧说明 | 实际情况 | 处理 |
|---|---|---|
| 「Redis 后端启动必需」 | pom 无 redis 依赖,代码也未使用 | 改为「不需要 Redis」,并去掉外置配置里的 redis 段 |
| 「jar 已内置前端静态托管(file:./web/)」 | jar 内无任何静态文件,也没有 static-locations 配置;旧包实际靠 nginx |
改为「打包时把 dist 内嵌进 jar」,访问 8090 即可,无需 nginx |
| 数据库只提 3 个脚本 | 现网还有 wyc_total_monthly / wyc_order_monthly 两张表 |
补 db/sql_wyc.sql,并补充 summary.template-dir 等配置与时序 |
start-dev.ps1,重启后 PID 变化)、前端 8080、MySQL 127.0.0.1:3308。deploy/traffic-audit-deploy-20260913/backend 拷到 _deploytest/,用 8091 端口独立启动package-deploy.py、_deploy_smoke.py(结果 _deploytest_result.txt、_deploytest_backend.log)。| 编号 | 输入 | 预期 | 实际 | 结论 |
|---|---|---|---|---|
| TC-1 | python package-deploy.py --no-build |
产出 zip,且无报错 | traffic-audit-deploy-20260913.zip 69.9 MB / 246 文件 |
通过 |
| TC-2 | 检查 jar 内容 | 含内嵌前端 | jar 55.5 MB,BOOT-INF/classes/static/ 下 42 个文件,static/index.html 存在 |
通过 |
| TC-3 | 检查部署目录结构 | backend/frontend/db/README 齐全 | backend(jar + docs + application.yml + 2 个启动脚本)、frontend/dist、db 4 个 sql、README | 通过 |
| TC-4 | 打包后源码目录 | src/main/resources/static 应被清空 |
打包结束自动清空(仓库无污染) | 通过 |
| TC-5 | 从部署包独立启动(8091) | 启动成功 | 日志出现 Started TrafficAuditApplication、Tomcat started on port(s): 8091 |
通过 |
| TC-6 | GET / |
200 且返回页面 | 200,1828 字节,含 <div id="app"> |
通过 |
| TC-7 | GET /js/app.*.js |
200(静态资源可访问) | 200,6253 字节 | 通过 |
| TC-8 | POST /api/auth/login(admin/123456) |
200 且返回 token | 200,{"code":200,"message":"success","data":{...,"token":"eyJ..."}} |
通过 |
| TC-9 | 带 token 导出《道路运输量汇总表》2026-08 | 200 且是合法 xlsx | 200,950,783 字节,头部 PK(与开发机导出件同尺寸) |
通过 |
| 编号 | 输入 | 预期 | 实际 | 结论 |
|---|---|---|---|---|
| TC-N1 | 修复前:--jwt.secret=这是一个中文密钥 |
暴露问题 | 登录 500,日志 ArrayIndexOutOfBoundsException: 20018 at JwtUtils.generateToken |
已复现(问题成立) |
| TC-N2 | 修复后:同样传中文 secret | 不因密钥格式崩,登录仍可用 | 登录 200 拿到 token;导出同样 200 / 950,783 字节;日志出现「jwt.secret 不是合法的 Base64 字符串…」WARN | 通过 |
| TC-N3 | 外置 application.yml 保留占位密码,用命令行 --spring.datasource.password 覆盖 |
命令行覆盖生效 | 启动成功并正常查库(登录 200) | 通过 |
| TC-N4 | 打包脚本重复执行(旧 staging 目录含只读文件) | 能清理并重建 | 首轮报 PermissionError WinError 5,加 rmtree_force(先清只读属性)后正常 |
已修复 |
| 编号 | 输入 | 预期 | 实际 | 结论 |
|---|---|---|---|---|
| TC-R1 | 开发机重启后端(载入 JwtUtils 改动) | 功能不回归 | 登录 200;导出 2026-08 → HTTP 200 / 950,783 字节(与部署包产物、9-13 归档件同尺寸) | 通过 |
| TC-R2 | 前端生产构建 | 构建成功 | npm run build 成功,dist/index.html + css/js/fonts 齐全 |
通过 |
jwt.secret 非法 Base64 → 登录 500(烟测抓到):已按根因修复(Base64 优先、否则 UTF-8 兜底 + WARN),并在打包配置里给出可直接使用的 32 位十六进制示例值、在说明文档里写明取值规则。docs 里有只读 xlsx,copy2 会把只读属性带过去,导致 Windows 下 rmtree 报「拒绝访问」→ 新增 rmtree_force() 先 chmod 再删。"token"),原始响应与后续带 token 的导出请求均成功(HTTP 200),程序本身无问题。jwt.secret 的部署陷阱已根因修复,部署同事即使填了不规范的值也不会再出现「登录 500」而无从下手。