461 lines
19 KiB
PowerShell
461 lines
19 KiB
PowerShell
param(
|
||
[string]$TaskPackagePath = "",
|
||
[string]$ArtifactRoot = "",
|
||
[switch]$FailOnBlocked
|
||
)
|
||
|
||
$ErrorActionPreference = "Stop"
|
||
|
||
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
|
||
$timestamp = Get-Date -Format "yyyy-MM-ddTHH-mm-ss-fff"
|
||
|
||
function Resolve-LatestArtifactDir {
|
||
param([string]$RootRelativePath)
|
||
$root = Join-Path $repoRoot $RootRelativePath
|
||
$latest = Get-ChildItem -LiteralPath $root -Directory -ErrorAction SilentlyContinue |
|
||
Sort-Object Name -Descending |
|
||
Select-Object -First 1
|
||
if ($latest) {
|
||
return $latest.FullName
|
||
}
|
||
return ""
|
||
}
|
||
|
||
if ([string]::IsNullOrWhiteSpace($TaskPackagePath)) {
|
||
$TaskPackagePath = Resolve-LatestArtifactDir -RootRelativePath "project-docs\qa-artifacts\phase13-review-task-package-gate"
|
||
}
|
||
if (-not [string]::IsNullOrWhiteSpace($TaskPackagePath) -and -not [System.IO.Path]::IsPathRooted($TaskPackagePath)) {
|
||
$TaskPackagePath = Join-Path $repoRoot $TaskPackagePath
|
||
}
|
||
|
||
if ([string]::IsNullOrWhiteSpace($ArtifactRoot)) {
|
||
$ArtifactRoot = Join-Path $repoRoot "project-docs\qa-artifacts\phase13-reviewer-kit-export"
|
||
}
|
||
if (-not [System.IO.Path]::IsPathRooted($ArtifactRoot)) {
|
||
$ArtifactRoot = Join-Path $repoRoot $ArtifactRoot
|
||
}
|
||
|
||
$artifactDir = Join-Path $ArtifactRoot $timestamp
|
||
$kitRoot = Join-Path $artifactDir "reviewer-kits"
|
||
$legalKitDir = Join-Path $kitRoot "legal"
|
||
$businessKitDir = Join-Path $kitRoot "business"
|
||
$ackKitDir = Join-Path $kitRoot "ack"
|
||
$returnDir = Join-Path $kitRoot "return-templates"
|
||
$auditDir = Join-Path $kitRoot "audit"
|
||
New-Item -ItemType Directory -Force -Path $legalKitDir, $businessKitDir, $ackKitDir, $returnDir, $auditDir | Out-Null
|
||
|
||
$summaryJson = Join-Path $artifactDir "phase13-reviewer-kit-export-summary.json"
|
||
$summaryMd = Join-Path $artifactDir "phase13-reviewer-kit-export-summary.md"
|
||
$manifestCsv = Join-Path $artifactDir "reviewer-kit-manifest.csv"
|
||
$hashManifestCsv = Join-Path $artifactDir "reviewer-kit-sha256-manifest.csv"
|
||
$returnChecklistMd = Join-Path $artifactDir "reviewer-kit-return-checklist.md"
|
||
$gitStatusPath = Join-Path $artifactDir "git-status.txt"
|
||
|
||
$checks = New-Object System.Collections.Generic.List[object]
|
||
$manifestRows = New-Object System.Collections.Generic.List[object]
|
||
$hashRows = New-Object System.Collections.Generic.List[object]
|
||
|
||
function Add-Check {
|
||
param(
|
||
[string]$Key,
|
||
[string]$Label,
|
||
[ValidateSet("pass", "fail", "warn", "skip", "blocked")]
|
||
[string]$Status,
|
||
[string]$Detail,
|
||
[bool]$Required = $true,
|
||
[hashtable]$Meta = @{}
|
||
)
|
||
|
||
$checks.Add([ordered]@{
|
||
key = $Key
|
||
label = $Label
|
||
status = $Status
|
||
required = $Required
|
||
detail = $Detail
|
||
meta = $Meta
|
||
}) | Out-Null
|
||
}
|
||
|
||
function Save-Json {
|
||
param([string]$Path, [object]$Value)
|
||
$Value | ConvertTo-Json -Depth 50 | Set-Content -Encoding UTF8 -Path $Path
|
||
}
|
||
|
||
function Read-JsonFile {
|
||
param([string]$Path)
|
||
if ([string]::IsNullOrWhiteSpace($Path) -or -not (Test-Path -LiteralPath $Path -PathType Leaf)) {
|
||
return $null
|
||
}
|
||
return Get-Content -LiteralPath $Path -Raw -Encoding UTF8 | ConvertFrom-Json
|
||
}
|
||
|
||
function Import-CsvRows {
|
||
param([string]$Path)
|
||
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
|
||
return @()
|
||
}
|
||
return @(Import-Csv -LiteralPath $Path)
|
||
}
|
||
|
||
function Get-MetricValue {
|
||
param([object]$Summary, [string]$Name, [object]$Default = 0)
|
||
if ($null -eq $Summary -or $null -eq $Summary.metrics) {
|
||
return $Default
|
||
}
|
||
$property = $Summary.metrics.PSObject.Properties[$Name]
|
||
if ($null -eq $property -or $null -eq $property.Value) {
|
||
return $Default
|
||
}
|
||
return $property.Value
|
||
}
|
||
|
||
function Convert-OverallStatusText {
|
||
param([string]$Status)
|
||
switch ($Status) {
|
||
"PASS" { return "通过" }
|
||
"WARN" { return "通过但有警告" }
|
||
"FAIL" { return "未通过" }
|
||
"BLOCKED" { return "阻塞" }
|
||
default { return $Status }
|
||
}
|
||
}
|
||
|
||
function Convert-CheckStatusText {
|
||
param([string]$Status)
|
||
switch ($Status) {
|
||
"pass" { return "通过" }
|
||
"fail" { return "失败" }
|
||
"warn" { return "警告" }
|
||
"skip" { return "跳过" }
|
||
"blocked" { return "阻塞" }
|
||
default { return $Status }
|
||
}
|
||
}
|
||
|
||
function Add-Material {
|
||
param(
|
||
[string]$SourcePath,
|
||
[string]$TargetPath,
|
||
[string]$Kind,
|
||
[bool]$Required = $true
|
||
)
|
||
|
||
if (Test-Path -LiteralPath $SourcePath -PathType Leaf) {
|
||
Copy-Item -LiteralPath $SourcePath -Destination $TargetPath -Force
|
||
$manifestRows.Add([pscustomobject][ordered]@{
|
||
kind = $Kind
|
||
source = $SourcePath
|
||
target = $TargetPath
|
||
required = $Required
|
||
status = "included"
|
||
}) | Out-Null
|
||
return $true
|
||
}
|
||
|
||
$manifestRows.Add([pscustomobject][ordered]@{
|
||
kind = $Kind
|
||
source = $SourcePath
|
||
target = $TargetPath
|
||
required = $Required
|
||
status = "missing"
|
||
}) | Out-Null
|
||
return $false
|
||
}
|
||
|
||
function Write-ReturnTemplate {
|
||
param(
|
||
[object[]]$Rows,
|
||
[string]$Path,
|
||
[string]$Role
|
||
)
|
||
|
||
$Rows | ForEach-Object {
|
||
[pscustomobject][ordered]@{
|
||
review_id = $_.review_id
|
||
asset_type = $_.asset_type
|
||
asset_id = $_.asset_id
|
||
asset_name = $_.asset_name
|
||
reviewer_role = $Role
|
||
reviewer = $_.assignee
|
||
review_date = ""
|
||
review_result = "PENDING"
|
||
required_changes = ""
|
||
evidence_path = $_.evidence_form_path
|
||
allow_review_import = "false"
|
||
allow_production_release = "false"
|
||
notes = "由真实复核人填写;未填写前不得改为 APPROVED"
|
||
}
|
||
} | Export-Csv -LiteralPath $Path -NoTypeInformation -Encoding UTF8
|
||
}
|
||
|
||
function Write-AckReturnTemplate {
|
||
param([object[]]$Rows, [string]$Path)
|
||
|
||
$Rows | ForEach-Object {
|
||
[pscustomobject][ordered]@{
|
||
work_order_id = $_.work_order_id
|
||
recipient_role = $_.recipient_role
|
||
recipient = $_.recipient
|
||
handoff_owner = $_.handoff_owner
|
||
acknowledged = "false"
|
||
ack_date = ""
|
||
package_path = $_.package_path
|
||
missing_materials = ""
|
||
notes = "由接收人填写;未签收前不得视为真实复核启动"
|
||
}
|
||
} | Export-Csv -LiteralPath $Path -NoTypeInformation -Encoding UTF8
|
||
}
|
||
|
||
if (-not (Test-Path "$HOME\.Codex\skills\gstack\bin")) {
|
||
Add-Check -Key "gstack" -Label "gstack 前置检查" -Status "fail" -Detail "gstack 缺失"
|
||
} else {
|
||
Add-Check -Key "gstack" -Label "gstack 前置检查" -Status "pass" -Detail "GSTACK_OK"
|
||
}
|
||
|
||
$branch = (git -C $repoRoot rev-parse --abbrev-ref HEAD).Trim()
|
||
$commit = (git -C $repoRoot rev-parse HEAD).Trim()
|
||
$remoteUrl = (git -C $repoRoot remote get-url origin).Trim()
|
||
$gitStatus = git -C $repoRoot status --short
|
||
($gitStatus -join [Environment]::NewLine) | Set-Content -Encoding UTF8 -Path $gitStatusPath
|
||
|
||
if ($remoteUrl -match "192\.168\.3\.90") {
|
||
Add-Check -Key "origin_remote" -Label "origin 远端仓库" -Status "pass" -Detail "origin 指向 192.168.3.90" -Meta @{ branch = $branch; commit = $commit; remote = $remoteUrl }
|
||
} else {
|
||
Add-Check -Key "origin_remote" -Label "origin 远端仓库" -Status "fail" -Detail "origin 未指向 192.168.3.90" -Meta @{ branch = $branch; commit = $commit; remote = $remoteUrl }
|
||
}
|
||
|
||
if ($gitStatus) {
|
||
Add-Check -Key "working_tree" -Label "工作区状态" -Status "warn" -Required $false -Detail "工作区存在未提交改动;派发包门禁按证据记录" -Meta @{ artifact = $gitStatusPath }
|
||
} else {
|
||
Add-Check -Key "working_tree" -Label "工作区状态" -Status "pass" -Detail "工作区干净" -Meta @{ artifact = $gitStatusPath }
|
||
}
|
||
|
||
$taskSummaryPath = if ([string]::IsNullOrWhiteSpace($TaskPackagePath)) { "" } else { Join-Path $TaskPackagePath "phase13-review-task-package-gate-summary.json" }
|
||
$taskSummary = Read-JsonFile -Path $taskSummaryPath
|
||
if ($null -ne $taskSummary) {
|
||
Add-Check -Key "task_package_source" -Label "任务包来源" -Status "pass" -Detail "读取到任务包摘要,状态=$($taskSummary.status)" -Meta @{ path = $taskSummaryPath }
|
||
} else {
|
||
Add-Check -Key "task_package_source" -Label "任务包来源" -Status "fail" -Detail "缺少任务包摘要" -Meta @{ path = $taskSummaryPath }
|
||
}
|
||
|
||
$legalWorkOrdersPath = Join-Path $TaskPackagePath "legal-review-work-orders.csv"
|
||
$businessWorkOrdersPath = Join-Path $TaskPackagePath "business-review-work-orders.csv"
|
||
$ackWorkOrdersPath = Join-Path $TaskPackagePath "handoff-ack-work-orders.csv"
|
||
$acceptanceBoardPath = Join-Path $TaskPackagePath "review-acceptance-tracking-board.csv"
|
||
$dispatchManifestPath = Join-Path $TaskPackagePath "review-dispatch-manifest.csv"
|
||
$sourceWorkOrdersDir = Join-Path $TaskPackagePath "work-orders"
|
||
|
||
$legalRows = Import-CsvRows -Path $legalWorkOrdersPath
|
||
$businessRows = Import-CsvRows -Path $businessWorkOrdersPath
|
||
$ackRows = Import-CsvRows -Path $ackWorkOrdersPath
|
||
|
||
$materialsOk = $true
|
||
$materialsOk = (Add-Material -SourcePath $legalWorkOrdersPath -TargetPath (Join-Path $legalKitDir "legal-review-work-orders.csv") -Kind "legal_work_orders") -and $materialsOk
|
||
$materialsOk = (Add-Material -SourcePath $businessWorkOrdersPath -TargetPath (Join-Path $businessKitDir "business-review-work-orders.csv") -Kind "business_work_orders") -and $materialsOk
|
||
$materialsOk = (Add-Material -SourcePath $ackWorkOrdersPath -TargetPath (Join-Path $ackKitDir "handoff-ack-work-orders.csv") -Kind "ack_work_orders") -and $materialsOk
|
||
$materialsOk = (Add-Material -SourcePath $acceptanceBoardPath -TargetPath (Join-Path $auditDir "review-acceptance-tracking-board.csv") -Kind "acceptance_board") -and $materialsOk
|
||
$materialsOk = (Add-Material -SourcePath $dispatchManifestPath -TargetPath (Join-Path $auditDir "review-dispatch-manifest.csv") -Kind "dispatch_manifest") -and $materialsOk
|
||
|
||
foreach ($subdir in @("legal", "business", "ack")) {
|
||
$sourceDir = Join-Path $sourceWorkOrdersDir $subdir
|
||
$targetDir = Join-Path $kitRoot $subdir
|
||
if (Test-Path -LiteralPath $sourceDir -PathType Container) {
|
||
$sourceFiles = @(Get-ChildItem -LiteralPath $sourceDir -File -ErrorAction SilentlyContinue)
|
||
foreach ($sourceFile in $sourceFiles) {
|
||
Copy-Item -LiteralPath $sourceFile.FullName -Destination $targetDir -Force
|
||
}
|
||
$manifestRows.Add([pscustomobject][ordered]@{
|
||
kind = "$subdir`_markdown_work_orders"
|
||
source = $sourceDir
|
||
target = $targetDir
|
||
required = $true
|
||
status = if ($sourceFiles.Count -gt 0) { "included:$($sourceFiles.Count)" } else { "missing" }
|
||
}) | Out-Null
|
||
if ($sourceFiles.Count -eq 0) {
|
||
$materialsOk = $false
|
||
}
|
||
} else {
|
||
$materialsOk = $false
|
||
$manifestRows.Add([pscustomobject][ordered]@{
|
||
kind = "$subdir`_markdown_work_orders"
|
||
source = $sourceDir
|
||
target = $targetDir
|
||
required = $true
|
||
status = "missing"
|
||
}) | Out-Null
|
||
}
|
||
}
|
||
|
||
$legalReturnTemplate = Join-Path $returnDir "legal-review-result-return.csv"
|
||
$businessReturnTemplate = Join-Path $returnDir "business-review-result-return.csv"
|
||
$ackReturnTemplate = Join-Path $returnDir "handoff-ack-return.csv"
|
||
Write-ReturnTemplate -Rows $legalRows -Path $legalReturnTemplate -Role "法务"
|
||
Write-ReturnTemplate -Rows $businessRows -Path $businessReturnTemplate -Role "业务"
|
||
Write-AckReturnTemplate -Rows $ackRows -Path $ackReturnTemplate
|
||
|
||
$manifestRows.Add([pscustomobject][ordered]@{ kind = "legal_return_template"; source = ""; target = $legalReturnTemplate; required = $true; status = "generated" }) | Out-Null
|
||
$manifestRows.Add([pscustomobject][ordered]@{ kind = "business_return_template"; source = ""; target = $businessReturnTemplate; required = $true; status = "generated" }) | Out-Null
|
||
$manifestRows.Add([pscustomobject][ordered]@{ kind = "ack_return_template"; source = ""; target = $ackReturnTemplate; required = $true; status = "generated" }) | Out-Null
|
||
|
||
$checklist = New-Object System.Collections.Generic.List[string]
|
||
$checklist.Add("# 阶段 13 复核派发包回收清单") | Out-Null
|
||
$checklist.Add("") | Out-Null
|
||
$checklist.Add("## 派发前") | Out-Null
|
||
$checklist.Add("") | Out-Null
|
||
$checklist.Add("1. 确认真实复核输入接入门禁通过。") | Out-Null
|
||
$checklist.Add("2. 确认任务包门禁为 PASS 或 WARN,且可派发工作项不为 0。") | Out-Null
|
||
$checklist.Add("3. 将 legal、business、ack 三个目录分别交给对应角色。") | Out-Null
|
||
$checklist.Add("4. 保存本目录哈希清单,作为派发前证据。") | Out-Null
|
||
$checklist.Add("") | Out-Null
|
||
$checklist.Add("## 回收后") | Out-Null
|
||
$checklist.Add("") | Out-Null
|
||
$checklist.Add("1. 回收 `return-templates` 中的结果文件。") | Out-Null
|
||
$checklist.Add("2. 运行 `apply-phase13-review-results.ps1` 做 DryRun 校验。") | Out-Null
|
||
$checklist.Add("3. 运行进度、审计归档、总控和生产发布门禁。") | Out-Null
|
||
$checklist.Add("4. 未通过门禁前不得把任何资产改为生产可发布。") | Out-Null
|
||
$checklist | Set-Content -Encoding UTF8 -Path $returnChecklistMd
|
||
$manifestRows.Add([pscustomobject][ordered]@{ kind = "return_checklist"; source = ""; target = $returnChecklistMd; required = $true; status = "generated" }) | Out-Null
|
||
|
||
$manifestRows | Export-Csv -LiteralPath $manifestCsv -NoTypeInformation -Encoding UTF8
|
||
|
||
$kitFiles = @(Get-ChildItem -LiteralPath $kitRoot -Recurse -File -ErrorAction SilentlyContinue)
|
||
foreach ($file in $kitFiles) {
|
||
$hash = Get-FileHash -LiteralPath $file.FullName -Algorithm SHA256
|
||
$relative = $file.FullName.Substring($kitRoot.Length).TrimStart("\")
|
||
$hashRows.Add([pscustomobject][ordered]@{
|
||
relative_path = $relative
|
||
length = $file.Length
|
||
sha256 = $hash.Hash
|
||
}) | Out-Null
|
||
}
|
||
$hashRows | Export-Csv -LiteralPath $hashManifestCsv -NoTypeInformation -Encoding UTF8
|
||
|
||
if ($materialsOk) {
|
||
Add-Check -Key "kit_materials" -Label "派发包材料" -Status "pass" -Detail "必需派发材料齐套"
|
||
} else {
|
||
Add-Check -Key "kit_materials" -Label "派发包材料" -Status "fail" -Detail "必需派发材料缺失"
|
||
}
|
||
|
||
if ($kitFiles.Count -gt 0 -and $hashRows.Count -eq $kitFiles.Count) {
|
||
Add-Check -Key "hash_manifest" -Label "派发包哈希清单" -Status "pass" -Detail "已生成 $($hashRows.Count) 条 SHA256 留痕"
|
||
} else {
|
||
Add-Check -Key "hash_manifest" -Label "派发包哈希清单" -Status "fail" -Detail "哈希清单数量异常"
|
||
}
|
||
|
||
$taskReadyOrders = [int](Get-MetricValue -Summary $taskSummary -Name "ready_to_dispatch_orders")
|
||
$taskAcceptanceRows = [int](Get-MetricValue -Summary $taskSummary -Name "acceptance_tracking_rows")
|
||
if ($taskAcceptanceRows -gt 0 -and $taskReadyOrders -eq $taskAcceptanceRows -and @("PASS", "WARN") -contains [string]$taskSummary.status) {
|
||
Add-Check -Key "dispatch_readiness" -Label "真实派发条件" -Status "pass" -Detail "任务包已具备真实派发条件"
|
||
} else {
|
||
Add-Check -Key "dispatch_readiness" -Label "真实派发条件" -Status "blocked" -Detail "任务包尚未具备真实派发条件:ready=$taskReadyOrders / acceptance=$taskAcceptanceRows / status=$($taskSummary.status)"
|
||
}
|
||
|
||
$approvalLeaks = @($kitFiles | Select-String -Pattern "APPROVED_FOR_PRODUCTION|APPROVED_FOR_REVIEW_IMPORT" -SimpleMatch -List -ErrorAction SilentlyContinue)
|
||
if ($approvalLeaks.Count -eq 0) {
|
||
Add-Check -Key "no_approval_in_export" -Label "未在派发包中预置批准结论" -Status "pass" -Detail "派发包未预置批准结论"
|
||
} else {
|
||
Add-Check -Key "no_approval_in_export" -Label "未在派发包中预置批准结论" -Status "fail" -Detail "发现疑似预置批准结论:$($approvalLeaks.Count)"
|
||
}
|
||
|
||
$failedRequired = @($checks | Where-Object { $_.required -and $_.status -eq "fail" }).Count
|
||
$blockedRequired = @($checks | Where-Object { $_.required -and $_.status -eq "blocked" }).Count
|
||
$warnings = @($checks | Where-Object { $_.status -eq "warn" }).Count
|
||
|
||
$overallStatus = if ($failedRequired -gt 0) {
|
||
"FAIL"
|
||
} elseif ($blockedRequired -gt 0) {
|
||
"BLOCKED"
|
||
} elseif ($warnings -gt 0) {
|
||
"WARN"
|
||
} else {
|
||
"PASS"
|
||
}
|
||
|
||
$metrics = [ordered]@{
|
||
legal_work_orders = $legalRows.Count
|
||
business_work_orders = $businessRows.Count
|
||
ack_work_orders = $ackRows.Count
|
||
kit_files = $kitFiles.Count
|
||
hash_manifest_rows = $hashRows.Count
|
||
manifest_rows = $manifestRows.Count
|
||
task_ready_to_dispatch_orders = $taskReadyOrders
|
||
task_acceptance_rows = $taskAcceptanceRows
|
||
}
|
||
|
||
$summary = [ordered]@{
|
||
name = "phase13-reviewer-kit-export"
|
||
status = $overallStatus
|
||
generated_at = (Get-Date).ToUniversalTime().ToString("o")
|
||
branch = $branch
|
||
commit = $commit
|
||
remote = $remoteUrl
|
||
task_package_path = $TaskPackagePath
|
||
checks = $checks.ToArray()
|
||
metrics = $metrics
|
||
artifacts = [ordered]@{
|
||
summary_json = $summaryJson
|
||
summary_md = $summaryMd
|
||
kit_root = $kitRoot
|
||
manifest = $manifestCsv
|
||
hash_manifest = $hashManifestCsv
|
||
return_checklist = $returnChecklistMd
|
||
git_status = $gitStatusPath
|
||
}
|
||
totals = [ordered]@{
|
||
total_checks = $checks.Count
|
||
failed_required = $failedRequired
|
||
blocked_required = $blockedRequired
|
||
warnings = $warnings
|
||
}
|
||
}
|
||
Save-Json -Path $summaryJson -Value $summary
|
||
|
||
$md = New-Object System.Collections.Generic.List[string]
|
||
$md.Add("# 阶段 13 复核派发包导出门禁") | Out-Null
|
||
$md.Add("") | Out-Null
|
||
$md.Add("- 门禁状态:$(Convert-OverallStatusText -Status $overallStatus)($overallStatus)") | Out-Null
|
||
$md.Add("- 生成时间:$($summary.generated_at)") | Out-Null
|
||
$md.Add("- 分支:$branch") | Out-Null
|
||
$md.Add("- 提交:$commit") | Out-Null
|
||
$md.Add("- 任务包来源:$TaskPackagePath") | Out-Null
|
||
$md.Add("- 派发包目录:$kitRoot") | Out-Null
|
||
$md.Add("") | Out-Null
|
||
$md.Add("## 统计") | Out-Null
|
||
$md.Add("") | Out-Null
|
||
$md.Add("| 指标 | 数量 |") | Out-Null
|
||
$md.Add("|---|---:|") | Out-Null
|
||
$md.Add("| 法务工作单 | $($metrics.legal_work_orders) |") | Out-Null
|
||
$md.Add("| 业务工作单 | $($metrics.business_work_orders) |") | Out-Null
|
||
$md.Add("| 签收工作单 | $($metrics.ack_work_orders) |") | Out-Null
|
||
$md.Add("| 派发包文件 | $($metrics.kit_files) |") | Out-Null
|
||
$md.Add("| 哈希清单 | $($metrics.hash_manifest_rows) |") | Out-Null
|
||
$md.Add("| 可派发工作项 | $($metrics.task_ready_to_dispatch_orders) / $($metrics.task_acceptance_rows) |") | Out-Null
|
||
$md.Add("") | Out-Null
|
||
$md.Add("## 检查项") | Out-Null
|
||
$md.Add("") | Out-Null
|
||
$md.Add("| 检查项 | 必需 | 状态 | 说明 |") | Out-Null
|
||
$md.Add("|---|---:|---|---|") | Out-Null
|
||
foreach ($check in $checks) {
|
||
$requiredText = if ($check.required) { "是" } else { "否" }
|
||
$md.Add("| $($check.label) | $requiredText | $(Convert-CheckStatusText -Status $check.status) | $($check.detail) |") | Out-Null
|
||
}
|
||
$md.Add("") | Out-Null
|
||
$md.Add("## 当前结论") | Out-Null
|
||
$md.Add("") | Out-Null
|
||
if ($overallStatus -eq "BLOCKED") {
|
||
$md.Add("派发包已导出并留痕,但由于真实复核输入和任务包派发条件未齐套,当前不能作为真实已派发证据。") | Out-Null
|
||
} elseif ($overallStatus -eq "PASS") {
|
||
$md.Add("派发包已具备真实派发条件,可按角色交付并进入签收回收。") | Out-Null
|
||
} else {
|
||
$md.Add("派发包门禁未完全通过,请查看检查项和清单。") | Out-Null
|
||
}
|
||
$md | Set-Content -Encoding UTF8 -Path $summaryMd
|
||
|
||
Write-Host "PHASE13_REVIEWER_KIT_EXPORT_$overallStatus $artifactDir"
|
||
if ($overallStatus -eq "FAIL") {
|
||
throw "阶段 13 复核派发包导出门禁未通过。查看:$summaryMd"
|
||
}
|
||
if ($overallStatus -eq "BLOCKED" -and $FailOnBlocked) {
|
||
throw "阶段 13 复核派发包导出门禁被阻塞。查看:$summaryMd"
|
||
}
|