| New file |
| | |
| | | <# |
| | | 一键打包部署包(含"去密自检")—— 2026-09-27 新增 |
| | | 用法: |
| | | powershell -NoProfile -ExecutionPolicy Bypass -File pack-deploy.ps1 |
| | | powershell -NoProfile -ExecutionPolicy Bypass -File pack-deploy.ps1 -OutRoot "E:\试验" -SkipFrontend |
| | | 做四件事: |
| | | 1) mvn clean package 构建后端 jar |
| | | 2) 解压 jar 检查内置 application.yml:必须全是占位符(禁止 sk- 密钥 / 明文口令),不合格直接中止 |
| | | 3) npm run build 构建前端 dist(可 -SkipFrontend) |
| | | 4) 组装 deploy 目录结构(backend / frontend / db / README-部署说明.txt),输出到 <OutRoot>\traffic-audit-deploy-<yyyyMMdd> |
| | | #> |
| | | param( |
| | | [string]$OutRoot = 'E:\试验', |
| | | [switch]$SkipFrontend |
| | | ) |
| | | |
| | | $ErrorActionPreference = 'Stop' |
| | | $root = Split-Path -Parent $MyInvocation.MyCommand.Path |
| | | $tplDir = Join-Path $root 'deploy-templates' # 打包模板件(已入库,2026-09-27 起) |
| | | $jarRel = 'traffic-audit-server\target\traffic-audit-server-1.0.0-SNAPSHOT.jar' |
| | | $jar = Join-Path $root $jarRel |
| | | |
| | | Write-Host '[1/5] 构建后端(mvn clean package -DskipTests)...' |
| | | Push-Location $root |
| | | cmd /c "mvn.cmd -q clean package -DskipTests -f traffic-audit-server\pom.xml > pack-build.log 2>&1" |
| | | $rc = $LASTEXITCODE |
| | | Pop-Location |
| | | if ($rc -ne 0) { throw '后端构建失败,详见 pack-build.log' } |
| | | if (-not (Test-Path $jar)) { throw "未找到构建产物:$jar" } |
| | | Write-Host (" 产物:{0:N1} MB" -f ((Get-Item $jar).Length / 1MB)) |
| | | |
| | | Write-Host '[2/5] 去密自检:解压 jar 检查 BOOT-INF/classes/application.yml ...' |
| | | Add-Type -AssemblyName System.IO.Compression.FileSystem |
| | | $zip = [System.IO.Compression.ZipFile]::OpenRead($jar) |
| | | try { |
| | | $entry = $zip.Entries | Where-Object { $_.FullName -eq 'BOOT-INF/classes/application.yml' } |
| | | if (-not $entry) { throw 'jar 内缺少 BOOT-INF/classes/application.yml' } |
| | | $sr = New-Object System.IO.StreamReader($entry.Open()) |
| | | $text = $sr.ReadToEnd() |
| | | $sr.Close() |
| | | } finally { $zip.Dispose() } |
| | | |
| | | $bad = @() |
| | | if ($text -match 'sk-[A-Za-z0-9]{16,}') { $bad += '疑似明文 API key(sk-…)' } |
| | | foreach ($line in ($text -split "`n")) { |
| | | if ($line -match '^\s*(password|api-key|secret)\s*:\s*(.+)$') { |
| | | $v = $matches[2].Trim().Trim('"') |
| | | if ($v -and -not $v.StartsWith('${')) { $bad += ("$($matches[1]) 不是占位符") } |
| | | } |
| | | } |
| | | if ($bad.Count -gt 0) { |
| | | throw ('去密自检未通过:' + ($bad -join ';') + '。请检查 traffic-audit-server\src\main\resources\application.yml') |
| | | } |
| | | Write-Host ' 通过:jar 内置配置全部为占位符,无明文密钥/口令。' |
| | | |
| | | Write-Host '[3/5] 构建前端(npm run build,可 -SkipFrontend 跳过)...' |
| | | if (-not $SkipFrontend) { |
| | | Push-Location (Join-Path $root 'traffic-audit-web') |
| | | cmd /c "npm.cmd run build > ..\pack-front.log 2>&1" |
| | | $rc2 = $LASTEXITCODE |
| | | Pop-Location |
| | | if ($rc2 -ne 0) { throw '前端构建失败,详见 pack-front.log' } |
| | | Write-Host ' 前端 dist 已重建' |
| | | } |
| | | |
| | | Write-Host '[4/5] 组装部署目录...' |
| | | $stamp = Get-Date -Format 'yyyyMMdd' |
| | | $dest = Join-Path $OutRoot ("traffic-audit-deploy-" + $stamp) |
| | | $n = 2 |
| | | while (Test-Path $dest) { $dest = Join-Path $OutRoot ("traffic-audit-deploy-" + $stamp + "-" + $n); $n++ } |
| | | New-Item -ItemType Directory -Force -Path (Join-Path $dest 'backend'), (Join-Path $dest 'frontend'), (Join-Path $dest 'db') | Out-Null |
| | | |
| | | Copy-Item $jar (Join-Path $dest 'backend') -Force |
| | | Copy-Item (Join-Path $tplDir 'backend\application.yml') (Join-Path $dest 'backend') -Force |
| | | Copy-Item (Join-Path $tplDir 'backend\start-backend.cmd') (Join-Path $dest 'backend') -Force |
| | | Copy-Item (Join-Path $tplDir 'backend\start-backend.sh') (Join-Path $dest 'backend') -Force |
| | | Copy-Item (Join-Path $root 'traffic-audit-web\dist') (Join-Path $dest 'frontend') -Recurse -Force |
| | | Copy-Item (Join-Path $tplDir 'frontend\nginx.conf.example') (Join-Path $dest 'frontend') -Force |
| | | Copy-Item (Join-Path $tplDir 'db\*.sql') (Join-Path $dest 'db') -Force |
| | | |
| | | # docs:运行时模板 + 用户文档;排除开发过程文档与缓存/备份(减小体积、避免把内部清单带出去) |
| | | $docsDest = Join-Path $dest 'backend\docs' |
| | | New-Item -ItemType Directory -Force -Path $docsDest | Out-Null |
| | | cmd /c "robocopy `"$root\docs`" `"$docsDest`" /E /NFL /NDL /NJH /NJS /NP /XD _cache 工作日结 功能测试报告 问题汇总 /XF 生成_*.xlsx _bak_* >nul" |
| | | if ($LASTEXITCODE -ge 8) { throw "docs 复制失败(robocopy 返回 $LASTEXITCODE)" } |
| | | # db 脚本也放一份到 docs(与旧包一致:docs/init.sql 等) |
| | | Copy-Item (Join-Path $tplDir 'db\init.sql'), (Join-Path $tplDir 'db\sql_city_bus.sql'), (Join-Path $tplDir 'db\sql_city_taxi.sql'), (Join-Path $tplDir 'db\sql_wyc.sql'), (Join-Path $tplDir 'db\sql_holiday.sql'), (Join-Path $tplDir 'db\sql_holiday_decimal_migration.sql'), (Join-Path $tplDir 'db\sql_holiday_comparison_base_migration.sql') $docsDest -Force -ErrorAction SilentlyContinue |
| | | |
| | | # README:用旧包模板并替换日期占位 |
| | | $readme = Get-Content (Join-Path $tplDir 'README-部署说明.txt') -Raw -Encoding UTF8 |
| | | $readme = $readme.Replace('{stamp}', $stamp) |
| | | Set-Content -LiteralPath (Join-Path $dest 'README-部署说明.txt') -Value $readme -Encoding UTF8 |
| | | |
| | | Write-Host '[5/5] 汇总...' |
| | | $files = Get-ChildItem $dest -Recurse -File |
| | | Write-Host (" 输出目录:{0}" -f $dest) |
| | | Write-Host (" 文件数:{0} 合计:{1:N2} MB" -f $files.Count, (($files | Measure-Object Length -Sum).Sum / 1MB)) |
| | | Write-Host ' 提醒:把包拷到部署机后,只需改 backend\application.yml 里的数据库三处 + 需要时填 deepseek.api-key。' |