补齐源快照、资料预演、正式导入及工资核算的依赖链路。 新增版本化输入、逐段 FIFO、生成锁和结构化当月调整, 并提供默认关闭的周期采集、有限重试及 Windows 托管。 原始客户资料、私有配置及首页改动不纳入本次提交。 BREAKING CHANGE: 升级前需应用新增迁移,并通过 PAYROLL_MAKER_USER_ID 绑定实际工资制作人,否则业务只读。 旧单批次核算默认关闭;历史兼容需显式启用 PAYROLL_ALLOW_LEGACY_CALCULATION。生产启用和真实金额待验收。
67 lines
4.0 KiB
PowerShell
67 lines
4.0 KiB
PowerShell
[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
|
||
}
|