[CmdletBinding()] param( [switch]$Restart ) $ErrorActionPreference = "Stop" $ProjectRoot = $PSScriptRoot $BackendDir = Join-Path $ProjectRoot "backend" $EnvFile = Join-Path $ProjectRoot ".env" function Get-MatchingProcesses { param([string]$Pattern) return @(Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -and $_.CommandLine -match $Pattern }) } function Stop-MatchingProcesses { param([string]$Name, [string]$Pattern) $processes = Get-MatchingProcesses -Pattern $Pattern if (-not $processes) { return } $hasStaleEnvironment = @($processes | Where-Object { $_.CreationDate -lt $script:EnvLastWriteTime }).Count -gt 0 if (-not $Restart -and -not $hasStaleEnvironment) { Write-Host "[skip] $Name 已运行" -ForegroundColor Yellow return } foreach ($process in $processes) { Stop-Process -Id $process.ProcessId -Force -ErrorAction SilentlyContinue } $reason = if ($Restart) { "显式重启" } else { ".env 晚于进程启动时间" } Write-Host "[restart] 已停止旧 $Name 进程($reason)" -ForegroundColor Yellow } function Start-PythonProcess { param( [string]$Name, [string]$Pattern, [string]$Command ) if (Get-MatchingProcesses -Pattern $Pattern) { return } $escapedDirectory = $BackendDir.Replace("'", "''") $windowCommand = "Set-Location -LiteralPath '$escapedDirectory'; `$Host.UI.RawUI.WindowTitle = '$Name'; $Command" Start-Process powershell.exe -ArgumentList @("-NoExit", "-Command", $windowCommand) Write-Host "[start] $Name" -ForegroundColor Green } if (-not (Test-Path -LiteralPath $EnvFile)) { throw "未找到 $EnvFile,请先复制 .env.example 并完成配置。" } if (-not (Get-Command python -ErrorAction SilentlyContinue)) { throw "未找到 python,请先安装 Python 3.12 并加入 PATH。" } $script:EnvLastWriteTime = (Get-Item -LiteralPath $EnvFile).LastWriteTime Push-Location $ProjectRoot try { python -c "from cryptography.fernet import Fernet; from dotenv import dotenv_values; key=dotenv_values('.env').get('AI_CREDENTIAL_ENCRYPTION_KEY',''); Fernet(key.encode()); print('[ok] AI credential encryption key is valid')" if ($LASTEXITCODE -ne 0) { throw ".env 中的 AI_CREDENTIAL_ENCRYPTION_KEY 缺失或不是有效 Fernet 密钥。" } Stop-MatchingProcesses -Name "Django API" -Pattern "manage\.py runserver 0\.0\.0\.0:8000" Stop-MatchingProcesses -Name "Celery Worker" -Pattern "celery.*-A config\.celery worker" Stop-MatchingProcesses -Name "Celery Beat" -Pattern "celery.*-A config\.celery beat" Start-PythonProcess -Name "AI Ticket - Django API" ` -Pattern "manage\.py runserver 0\.0\.0\.0:8000" ` -Command "python manage.py runserver 0.0.0.0:8000" Start-PythonProcess -Name "AI Ticket - Celery Worker" ` -Pattern "celery.*-A config\.celery worker" ` -Command "python -m celery -A config.celery worker -l info -P solo" Start-PythonProcess -Name "AI Ticket - Celery Beat" ` -Pattern "celery.*-A config\.celery beat" ` -Command "python -m celery -A config.celery beat -l info" Write-Host "" Write-Host "Python 后端程序已启动:" -ForegroundColor Cyan Write-Host " Django API http://localhost:8000/api/docs" Write-Host " Celery Worker 已启动" Write-Host " Celery Beat 已启动" Write-Host "" Write-Host "修改 .env 后可执行:.\start-backend.ps1 -Restart" -ForegroundColor Yellow } finally { Pop-Location }