# DMS 一体化构建脚本 # 用法:在项目根目录执行 .\build\build.ps1 [CmdletBinding()] param( [switch]$SkipFrontend, [switch]$SkipPyInstallerInstall ) $ErrorActionPreference = "Stop" $ProjectRoot = Split-Path $PSScriptRoot -Parent $Backend = Join-Path $ProjectRoot "backend" $Frontend = Join-Path $ProjectRoot "frontend" $Dist = Join-Path $ProjectRoot "dist-package" $BuildCache = Join-Path $PSScriptRoot "__pycache_build" Write-Host "========== DMS 一体化构建 ==========" -ForegroundColor Cyan Write-Host "项目根目录: $ProjectRoot" # ---------- 1. 前端构建 ---------- if (-not $SkipFrontend) { Write-Host "`n[1/5] 构建前端..." -ForegroundColor Yellow Push-Location $Frontend try { if (-not (Test-Path "node_modules")) { Write-Host " npm install..." -ForegroundColor DarkGray npm install if ($LASTEXITCODE -ne 0) { throw "npm install 失败" } } npm run build if ($LASTEXITCODE -ne 0) { throw "前端构建失败" } } finally { Pop-Location } } else { Write-Host "`n[1/5] 跳过前端构建 (-SkipFrontend)" -ForegroundColor DarkGray } $indexHtml = Join-Path $Frontend "dist\index.html" if (-not (Test-Path $indexHtml)) { throw "前端产物不存在: $indexHtml - 请先执行 npm run build" } # ---------- 2. 检查 PyInstaller ---------- Write-Host "`n[2/5] 检查 PyInstaller..." -ForegroundColor Yellow Push-Location $Backend try { $pyinstallerVersion = & python -m PyInstaller --version 2>$null if ($LASTEXITCODE -ne 0 -or -not $pyinstallerVersion) { if ($SkipPyInstallerInstall) { throw "PyInstaller 未安装且 -SkipPyInstallerInstall 已指定" } Write-Host " 安装 PyInstaller..." -ForegroundColor DarkGray python -m pip install pyinstaller if ($LASTEXITCODE -ne 0) { throw "PyInstaller 安装失败" } } else { Write-Host " PyInstaller 版本: $pyinstallerVersion" -ForegroundColor DarkGray } } finally { Pop-Location } # ---------- 3. PyInstaller 打包 ---------- Write-Host "`n[3/5] PyInstaller 打包..." -ForegroundColor Yellow $ReleaseDir = Join-Path $Dist "dms-server" # 暂存现有 .env:删除发行目录前移出,重打包后自动还原,避免用户配置每次丢失 $KeepEnv = Join-Path $Dist ".env.keep" if (Test-Path (Join-Path $ReleaseDir ".env")) { Copy-Item (Join-Path $ReleaseDir ".env") $KeepEnv -Force } if (Test-Path $BuildCache) { Remove-Item $BuildCache -Recurse -Force } if (Test-Path $ReleaseDir) { try { Remove-Item $ReleaseDir -Recurse -Force -ErrorAction Stop } catch { # 目录被其他进程占用(如命令行窗口停留在该目录内):保留目录节点,清空内容 Write-Warning "发行目录被其他进程占用,改为清空目录内容后继续" Get-ChildItem $ReleaseDir -Force | Remove-Item -Recurse -Force } } Push-Location $Backend try { python -m PyInstaller ` --noconfirm ` --clean ` --distpath $Dist ` --workpath $BuildCache ` (Join-Path $PSScriptRoot "dms-server.spec") if ($LASTEXITCODE -ne 0) { throw "PyInstaller 打包失败" } } finally { Pop-Location } # ---------- 4. 整理发行包 ---------- Write-Host "`n[4/5] 整理发行包..." -ForegroundColor Yellow if (-not (Test-Path $ReleaseDir)) { throw "发行目录未找到: $ReleaseDir" } # 复制配置模板(不含真实 .env,保护凭据) Copy-Item (Join-Path $Backend ".env.example") $ReleaseDir -Force # 尝试调用项目自带的导出工具,把示例数据库导出为 SQL 文件 # 工具三参数均必填:--env-file 提供库连接、--storage 提供文件根、--output 为输出目录 # (产物含 full_demo_data.sql 与 storage 文件副本),仅取 SQL 进发行包,临时目录用完即删 $ExportTool = Join-Path $ProjectRoot "tools\export_full_demo_data.py" $ExportedSql = Join-Path $ReleaseDir "database-init.sql" if (Test-Path $ExportTool) { # 运行时 .env 不在 backend 目录:按优先级取最新的现有配置(暂存 > 备份 > backend) $ExportEnv = @( $KeepEnv, (Join-Path $ProjectRoot "dist-package-bak\dms-server\.env"), (Join-Path $Backend ".env") ) | Where-Object { Test-Path $_ } | Select-Object -First 1 $ExportStorage = Join-Path $Backend "dms-storage" if (-not $ExportEnv) { Write-Warning "未找到可用的 .env(暂存 .env.keep / 备份目录 / backend\.env),跳过自动导出" } elseif (-not (Test-Path $ExportStorage)) { Write-Warning "未找到存储目录 $ExportStorage,跳过自动导出" } else { Write-Host " 导出示例数据库 SQL..." -ForegroundColor DarkGray $ExportTmp = Join-Path ([System.IO.Path]::GetTempPath()) "dms-demo-export" if (Test-Path $ExportTmp) { Remove-Item $ExportTmp -Recurse -Force } try { # 通过当前 Python 环境运行导出工具 python $ExportTool --env-file $ExportEnv --storage $ExportStorage --output $ExportTmp 2>&1 | Out-Host $ExportedSource = Join-Path $ExportTmp "full_demo_data.sql" if (Test-Path $ExportedSource) { Copy-Item $ExportedSource $ExportedSql -Force Write-Host " 已生成 database-init.sql" -ForegroundColor DarkGray } else { Write-Warning "导出工具未生成 full_demo_data.sql,请手动准备 SQL 文件" } } catch { Write-Warning "导出工具执行失败:$_" } finally { if (Test-Path $ExportTmp) { Remove-Item $ExportTmp -Recurse -Force } } } } else { Write-Warning "未找到 tools\export_full_demo_data.py,跳过自动导出" } # 复制说明文档 Copy-Item (Join-Path $PSScriptRoot "README.txt") $ReleaseDir -Force # DB upgrade script for existing deployments (dms-server.exe upgrade-db) Copy-Item (Join-Path $PSScriptRoot "upgrade-db.bat") $ReleaseDir -Force # ---------- 5. 从备份恢复 .env 和 dms-storage ---------- Write-Host "`n[5/5] 从备份恢复 .env 和 dms-storage..." -ForegroundColor Yellow $BackupDir = Join-Path $ProjectRoot "dist-package-bak\dms-server" if (-not (Test-Path $BackupDir)) { Write-Warning "备份目录不存在: $BackupDir - 跳过恢复" } else { $BackupEnv = Join-Path $BackupDir ".env" if (Test-Path $BackupEnv) { Copy-Item $BackupEnv $ReleaseDir -Force Write-Host " 已恢复 .env" -ForegroundColor DarkGray } else { Write-Warning "备份中没有 .env,跳过" } $BackupStorage = Join-Path $BackupDir "dms-storage" if (Test-Path $BackupStorage) { $TargetStorage = Join-Path $ReleaseDir "dms-storage" if (Test-Path $TargetStorage) { Remove-Item $TargetStorage -Recurse -Force } Copy-Item $BackupStorage $TargetStorage -Recurse -Force Write-Host " 已恢复 dms-storage" -ForegroundColor DarkGray } else { Write-Warning "备份中没有 dms-storage,跳过" } } # 还原打包前暂存的 .env(优先级高于备份目录,始终保留用户最新配置) if (Test-Path $KeepEnv) { Copy-Item $KeepEnv (Join-Path $ReleaseDir ".env") -Force Remove-Item $KeepEnv -Force Write-Host " 已保留重打包前的 .env" -ForegroundColor DarkGray } Write-Host "`n========== 构建完成 ==========" -ForegroundColor Green Write-Host "发行包目录: $ReleaseDir" -ForegroundColor Green $sizeMB = [math]::Round((Get-ChildItem $ReleaseDir -Recurse | Measure-Object -Property Length -Sum).Sum / 1MB, 1) Write-Host "包大小: ${sizeMB} MB" -ForegroundColor Green Write-Host "将该目录整体压缩即可分发给最终用户" -ForegroundColor Green