# ============================================================================ # MilliSim 后端部署脚本(IIS 独立后端 + 反向代理架构) # 在部署服务器上以管理员身份运行 PowerShell 执行此脚本 # 用法:.\deploy-to-iis.ps1 -DeployDir "C:\MilliSim\Backend" -ZipPath "C:\Deploy\MilliSim.Api.publish.clean.zip" # ============================================================================ param( [Parameter(Mandatory=$true)] [string]$DeployDir, [Parameter(Mandatory=$true)] [string]$ZipPath, [string]$ServiceName = "MilliSimApi" ) $ErrorActionPreference = "Stop" Write-Host "================================================" -ForegroundColor Cyan Write-Host " MilliSim 后端部署脚本" -ForegroundColor Cyan Write-Host "================================================" -ForegroundColor Cyan Write-Host "部署目录: $DeployDir" Write-Host "发布包: $ZipPath" Write-Host "服务名: $ServiceName" Write-Host "" # 1. 前置检查 Write-Host "[1/7] 前置检查..." -ForegroundColor Yellow if (-not (Test-Path $ZipPath)) { throw "发布包不存在: $ZipPath" } if (-not (Test-Path $DeployDir)) { throw "部署目录不存在: $DeployDir" } if (-not (Test-Path "$DeployDir\MilliSim.Api.dll")) { throw "部署目录下未找到 MilliSim.Api.dll,确认是否正确的部署目录" } Write-Host " [OK] 检查通过" -ForegroundColor Green # 2. 备份当前部署目录 $timestamp = Get-Date -Format "yyyyMMdd_HHmmss" $backupDir = "$DeployDir.bak_$timestamp" Write-Host "[2/7] 备份当前部署目录到: $backupDir" -ForegroundColor Yellow Copy-Item -Path $DeployDir -Destination $backupDir -Recurse -Force Write-Host " [OK] 备份完成" -ForegroundColor Green # 3. 停止后端服务 Write-Host "[3/7] 停止后端服务..." -ForegroundColor Yellow $serviceStopped = $false try { $svc = Get-Service -Name $ServiceName -ErrorAction Stop if ($svc.Status -eq "Running") { Stop-Service -Name $ServiceName -Force Start-Sleep -Seconds 2 Write-Host " [OK] Windows 服务 '$ServiceName' 已停止" -ForegroundColor Green } else { Write-Host " [SKIP] 服务已处于停止状态" -ForegroundColor Gray } $serviceStopped = $true } catch { Write-Host " [INFO] 未找到 Windows 服务 '$ServiceName',尝试查找 dotnet 进程..." -ForegroundColor Gray $procs = Get-Process -Name "dotnet","MilliSim.Api" -ErrorAction SilentlyContinue | Where-Object { $_.Path -like "*$DeployDir*" } if ($procs) { $procs | Stop-Process -Force Start-Sleep -Seconds 2 Write-Host " [OK] 已终止 dotnet 进程: $($procs.Id -join ', ')" -ForegroundColor Green } else { Write-Host " [INFO] 未找到运行中的后端进程" -ForegroundColor Gray } } # 4. 备份当前 appsettings.json(保留服务器配置) $appSettingsPath = "$DeployDir\appsettings.json" $appSettingsBackup = "$DeployDir\appsettings.json.server_backup" if (Test-Path $appSettingsPath) { Copy-Item -Path $appSettingsPath -Destination $appSettingsBackup -Force Write-Host "[4/7] 已备份服务器 appsettings.json 到: $appSettingsBackup" -ForegroundColor Green } else { Write-Host "[4/7] 部署目录无 appsettings.json,跳过备份" -ForegroundColor Gray } # 5. 解压新代码(不删除 wwwroot/uploads 和 wwwroot/backups 的文件) Write-Host "[5/7] 解压新代码到部署目录..." -ForegroundColor Yellow Add-Type -AssemblyName System.IO.Compression.FileSystem $zip = [System.IO.Compression.ZipFile]::OpenRead($ZipPath) try { foreach ($entry in $zip.Entries) { # 跳过 wwwroot/uploads 和 wwwroot/backups 下的所有文件(保留服务器上的上传文件) if ($entry.FullName -match "^wwwroot/uploads/" -and $entry.Length -gt 0) { continue } if ($entry.FullName -match "^wwwroot/backups/" -and $entry.Length -gt 0) { continue } $targetPath = Join-Path $DeployDir $entry.FullName $targetDir = Split-Path $targetPath -Parent if (-not (Test-Path $targetDir)) { New-Item -ItemType Directory -Path $targetDir -Force | Out-Null } if ($entry.Length -gt 0) { [System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, $targetPath, $true) } elseif (-not (Test-Path $targetPath)) { New-Item -ItemType Directory -Path $targetPath -Force | Out-Null } } Write-Host " [OK] 解压完成(已保留 wwwroot/uploads 和 wwwroot/backups 中的文件)" -ForegroundColor Green } finally { $zip.Dispose() } # 6. 恢复服务器 appsettings.json(如果存在备份) if (Test-Path $appSettingsBackup) { Copy-Item -Path $appSettingsBackup -Destination $appSettingsPath -Force Write-Host "[6/7] 已恢复服务器 appsettings.json(保留端口/连接字符串配置)" -ForegroundColor Green } else { Write-Host "[6/7] 未恢复 appsettings.json(使用发布包默认配置)" -ForegroundColor Gray } # 7. 启动后端服务 Write-Host "[7/7] 启动后端服务..." -ForegroundColor Yellow if ($serviceStopped) { try { Start-Service -Name $ServiceName -ErrorAction Stop Start-Sleep -Seconds 3 $svc = Get-Service -Name $ServiceName if ($svc.Status -eq "Running") { Write-Host " [OK] Windows 服务 '$ServiceName' 已启动" -ForegroundColor Green } else { Write-Host " [WARN] 服务启动状态: $($svc.Status)" -ForegroundColor Yellow } } catch { Write-Host " [ERROR] 服务启动失败: $_" -ForegroundColor Red Write-Host " 请手动启动服务或检查日志" -ForegroundColor Yellow } } else { Write-Host " [INFO] 之前未通过 Windows 服务停止,请用原启动方式重启后端" -ForegroundColor Gray Write-Host " 例如: cd $DeployDir; dotnet MilliSim.Api.dll --urls http://localhost:30001" -ForegroundColor Gray } # 验证 Write-Host "" Write-Host "================================================" -ForegroundColor Cyan Write-Host " 部署完成,开始验证" -ForegroundColor Cyan Write-Host "================================================" -ForegroundColor Cyan Start-Sleep -Seconds 5 Write-Host "[验证 1] 检查后端端口 30001 是否监听..." -ForegroundColor Yellow $conn = Test-NetConnection -ComputerName localhost -Port 30001 -WarningAction SilentlyContinue if ($conn.TcpTestSucceeded) { Write-Host " [OK] 端口 30001 监听正常" -ForegroundColor Green } else { Write-Host " [FAIL] 端口 30001 未监听,请检查后端启动日志" -ForegroundColor Red } Write-Host "[验证 2] 调用 /api/system/public-settings ..." -ForegroundColor Yellow try { $resp = Invoke-WebRequest -Uri "http://localhost:30001/api/system/public-settings" -UseBasicParsing -TimeoutSec 10 Write-Host " [OK] HTTP $($resp.StatusCode)" -ForegroundColor Green } catch { Write-Host " [FAIL] 请求失败: $_" -ForegroundColor Red } Write-Host "[验证 3] 检查 /uploads/ 路径是否由后端服务 ..." -ForegroundColor Yellow try { $resp = Invoke-WebRequest -Uri "http://localhost:30001/uploads/" -UseBasicParsing -TimeoutSec 10 Write-Host " [OK] HTTP $($resp.StatusCode)" -ForegroundColor Green } catch { $code = $_.Exception.Response.StatusCode.value__ if ($code -eq 404) { Write-Host " [OK] HTTP 404(目录浏览禁用,符合预期)" -ForegroundColor Green } else { Write-Host " [INFO] HTTP $code" -ForegroundColor Gray } } Write-Host "" Write-Host "================================================" -ForegroundColor Cyan Write-Host " 部署脚本结束" -ForegroundColor Cyan Write-Host "================================================" -ForegroundColor Cyan Write-Host "" Write-Host "下一步:在浏览器访问前端页面,验证以下 4 个问题是否全部解决:" -ForegroundColor White Write-Host " 1. 员工管理页面头像加载(应返回 /uploads/avatars/xxx.png,不再请求 COS)" Write-Host " 2. 轮播图列表页图片加载" Write-Host " 3. 项目封面/视频加载" Write-Host " 4. WebGL 资源加载(.unityweb 文件应返回 200 + application/octet-stream)" Write-Host "" Write-Host "如需回滚:删除 $DeployDir,重命名 $backupDir 为 $DeployDir,重启服务" -ForegroundColor Gray Write-Host "" Write-Host "如服务器上缺少 COS 模式时上传的旧文件,可调用迁移端点补齐:" Write-Host " Invoke-WebRequest -Uri 'http://localhost:30001/api/system/migrate-cos-to-local' -Method POST -Headers @{Authorization='Bearer '}"