[CmdletBinding(SupportsShouldProcess = $true)] param( [Parameter(Mandatory = $true)][string]$ProjectRoot, [Parameter(Mandatory = $true)][string]$PythonPath, [Parameter(Mandatory = $true)][string]$RuntimeDirectory, [Parameter(Mandatory = $true)][string]$ConnectionFile, [string]$TaskPath = '\PayrollManagementSystem\', [string]$ServiceAccount, [int]$ApiPort = 8000, [switch]$Apply, [switch]$TestMode ) . (Join-Path $PSScriptRoot 'TaskHost.Common.ps1') $plan = Get-PayrollTaskPlan -ProjectRoot $ProjectRoot -PythonPath $PythonPath -RuntimeDirectory $RuntimeDirectory ` -ConnectionFile $ConnectionFile -TaskPath $TaskPath -ApiPort $ApiPort if ($TestMode -and $TaskPath -notmatch '-Test-[0-9a-f]{8}\\$') { throw '测试模式必须使用独立测试任务目录。' } if (-not $Apply -or -not $PSCmdlet.ShouldProcess($TaskPath, '注册默认禁用的三个项目托管任务')) { $plan | ConvertTo-Json -Depth 5 return } foreach ($task in $plan.Tasks) { if (Get-ScheduledTask -TaskPath $TaskPath -TaskName $task.Name -ErrorAction SilentlyContinue) { throw '任务已存在,拒绝覆盖;请先核对当前安装。' } } if ($TestMode) { $account = [Security.Principal.WindowsIdentity]::GetCurrent().Name $principal = New-ScheduledTaskPrincipal -UserId $account -LogonType Interactive -RunLevel Limited $credential = $null } else { Assert-TaskValue $ServiceAccount if ($ServiceAccount -match '^(SYSTEM|LocalSystem|NT AUTHORITY\\SYSTEM)$') { throw '请使用已有受限服务账号,不使用SYSTEM。' } $credential = Get-Credential -UserName $ServiceAccount -Message '请输入已有受限服务账号的凭据(不会写入命令行或清单)' if ($null -eq $credential) { throw '未提供服务账号凭据,未注册任务。' } $account = $credential.UserName $principal = New-ScheduledTaskPrincipal -UserId $account -LogonType Password -RunLevel Limited } $serviceSid = Resolve-PayrollAccountSid $account if ($serviceSid -eq 'S-1-5-18') { throw '请使用已有受限服务账号,不使用SYSTEM。' } $plan | Add-Member -NotePropertyName ServiceSid -NotePropertyValue $serviceSid $plan | Add-Member -NotePropertyName LogonType -NotePropertyValue ([string]$principal.LogonType) Assert-PayrollHostAccess -Plan $plan -ServiceSid $serviceSid if (Test-Path -LiteralPath (Join-Path $plan.RuntimeDirectory 'task-manifest.json')) { throw '运行目录已有安装清单,拒绝覆盖。' } '任务已安装但未启用' | Set-Content -LiteralPath $plan.StopFile -Encoding UTF8 $registered = @() try { foreach ($task in $plan.Tasks) { $action = New-ScheduledTaskAction -Execute $task.Execute -Argument $task.Arguments -WorkingDirectory $task.WorkingDirectory $triggers = @(New-ScheduledTaskTrigger -AtStartup) if ($task.Role -eq 'maintenance') { $triggers += New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(1) -RepetitionInterval (New-TimeSpan -Minutes 1) } $definition = New-ScheduledTask -Action $action -Trigger $triggers -Settings (Get-PayrollTaskSettings) -Principal $principal if ($TestMode) { Register-ScheduledTask -TaskPath $TaskPath -TaskName $task.Name -InputObject $definition | Out-Null } else { $secretPassword = $credential.GetNetworkCredential().Password try { Register-ScheduledTask -TaskPath $TaskPath -TaskName $task.Name -InputObject $definition -User $account -Password $secretPassword | Out-Null } finally { $secretPassword = $null } } $registered += $task.Name } $plan | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $plan.RuntimeDirectory 'task-manifest.json') -Encoding UTF8 Write-Output '三个任务已注册且默认禁用;检查配置后显式执行Start。自动采集开关仍由应用配置控制。' } catch { Write-Error ('安装未完成,已注册的任务保持禁用:' + ($registered -join ', ') + '。请核对项目任务目录。') throw }