Files
jiapuapp/tests/interface-page-mapping-contract.ps1
T
2026-07-22 17:31:38 +08:00

209 lines
8.5 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
$ErrorActionPreference = 'Stop'
$root = Split-Path -Parent $PSScriptRoot
$pagesPath = Join-Path $root 'pages.json'
$mappingPath = Join-Path $root 'docs/接口与页面映射总表.md'
$planPath = Join-Path $root 'docs/家谱项目全量治理实施计划.md'
if (-not (Test-Path -LiteralPath $mappingPath -PathType Leaf)) {
throw '缺少页面与接口关系的唯一总表:docs/接口与页面映射总表.md'
}
$pagesDocument = Get-Content -Raw -Encoding UTF8 -LiteralPath $pagesPath | ConvertFrom-Json
$registeredRoutes = @($pagesDocument.pages | ForEach-Object { $_.path })
$mappingLines = @(Get-Content -Encoding UTF8 -LiteralPath $mappingPath)
$mappingText = $mappingLines -join "`n"
$planLines = @(Get-Content -Encoding UTF8 -LiteralPath $planPath)
$planText = $planLines -join "`n"
# 第一步:只解析以正式页面编号开头的表格行,避免把正文中的示例路由误算成页面映射。
$routeRows = @(
foreach ($line in $mappingLines) {
if ($line -match '^\|\s*(?<code>[AGTFRNM]\d{2})\s*\|\s*[^|]+\|\s*`(?<route>pages/[^`]+)`\s*\|') {
$columns = @($line.Trim('|').Split('|') | ForEach-Object { $_.Trim() })
[pscustomobject]@{
Code = $Matches.code
Route = $Matches.route
Line = $line
Columns = $columns
}
}
}
)
if ($routeRows.Count -ne $registeredRoutes.Count) {
throw "页面映射行数与 pages.json 不一致:映射=$($routeRows.Count),注册=$($registeredRoutes.Count)"
}
# 第二步:每个活动页面必须完整填写业务目标、入口、流程结果、状态和接口业务域;只列编号和路由不算完成迁移。
foreach ($row in $routeRows) {
if ($row.Columns.Count -ne 9) {
throw "页面映射列数错误:$($row.Code) 应为 9 列,实际为 $($row.Columns.Count)"
}
foreach ($columnIndex in 1..7) {
if ([string]::IsNullOrWhiteSpace($row.Columns[$columnIndex])) {
throw "页面映射存在空业务列:$($row.Code)$($columnIndex + 1) 列"
}
}
}
# 第三步:页面编号和路由都必须唯一;重复行会制造两个互相冲突的当前合同。
$duplicateCodes = @($routeRows | Group-Object Code | Where-Object Count -gt 1)
if ($duplicateCodes.Count -gt 0) {
throw "页面映射存在重复编号:$($duplicateCodes.Name -join ', ')"
}
$duplicateRoutes = @($routeRows | Group-Object Route | Where-Object Count -gt 1)
if ($duplicateRoutes.Count -gt 0) {
throw "页面映射存在重复路由:$($duplicateRoutes.Name -join ', ')"
}
# 第四步:总表顺序和路由集合必须与 pages.json 精确一致,新增或删除页面时必须同轮更新唯一总表。
$mappedRoutes = @($routeRows | ForEach-Object Route)
$missingRoutes = @($registeredRoutes | Where-Object { $_ -notin $mappedRoutes })
$staleRoutes = @($mappedRoutes | Where-Object { $_ -notin $registeredRoutes })
if ($missingRoutes.Count -gt 0 -or $staleRoutes.Count -gt 0) {
throw "页面映射与 pages.json 不相等:缺失=[$($missingRoutes -join ', ')];陈旧=[$($staleRoutes -join ', ')]"
}
for ($index = 0; $index -lt $registeredRoutes.Count; $index += 1) {
if ($mappedRoutes[$index] -ne $registeredRoutes[$index]) {
throw "页面映射顺序错误:第 $($index + 1) 行应为 $($registeredRoutes[$index]),实际为 $($mappedRoutes[$index])"
}
}
# 第五步:只允许 A04 和 T01 使用已经形成证据的专项结论;其余页面不能提前冒充完成 OpenAPI 审查。
$prematureRows = @(
$routeRows | Where-Object {
if ($_.Code -eq 'A04') { return $_.Line -notmatch '已核对注册成功响应为 `LoginResult`' -or $_.Line -notmatch '成功建立登录态并进入 G01' }
if ($_.Code -eq 'T01') { return $_.Line -notmatch '专项已核对:现有递归 `LineagePersonTreeView` 不满足' }
return $_.Line -notmatch '待对应业务阶段 OpenAPI 审查'
}
)
if ($prematureRows.Count -gt 0) {
throw "存在没有准确表达接口审查状态的活动页面:$($prematureRows.Code -join ', ')"
}
foreach ($heading in @(
'## 一、权威边界',
'## 二、全局业务与交互合同',
'## 三、52 个活动页面映射',
'## 四、封存与已移除页面',
'## 五、后续接口审查填充规则'
)) {
if (-not $mappingText.Contains($heading)) {
throw "接口与页面映射总表缺少章节:$heading"
}
}
# 第六步:锁定旧资料中仍然有效的关键业务事实,避免未来把表格保留却悄悄清空真正的产品合同。
foreach ($requiredFact in @(
'832',
'A01 在本地校验通过后、登录请求前触发',
'A05 在请求发送短信验证码前触发',
'A04 当前临时在完整注册表单提交后触发',
'聚焦密码框',
'审核中记录只展示进度和“撤回申请”',
'被拒绝记录展示原因和“修改后重新提交”',
'仅成员可见',
'公开可申请',
'LineageGraphWindow',
'EMPTY/POPULATED',
'entryPersonIds',
'genealogyRootPersonIds',
'redactedGenealogyRootCount',
'rootVisibility',
'pathCompleteness',
'ancestorPathSegments',
'anchorPersonId',
'schemaVersion',
'returnedNodeCount',
'partnerRelationship',
'relationshipKind',
'parentRole',
'LINEAGE_QUERY_INVALID',
'LINEAGE_FOCUS_NOT_AVAILABLE',
'RELATIONSHIP_PATCH_EMPTY',
'sceneVersion',
'/genealogy/app/v2/',
'API-T01-001',
'单个原生页面实例',
'注册成功响应为 `LoginResult`',
'aria-describedby',
'邀请码直接加入且不生成审核记录',
'appApi',
'500',
'20',
'100ms',
'44dp',
'导航栈语义统一',
'跨页面领域数据持久化',
'全局文字层级和无障碍第二轮',
'A02 账号登录',
'A06 登录状态',
'G02',
'G04',
'G07',
'T02'
)) {
if (-not $mappingText.Contains($requiredFact)) {
throw "接口与页面映射总表缺少关键业务事实:$requiredFact"
}
}
# 第七步:在可执行注册表落地前解析实施计划中的 52 行路由表,防止计划与页面总表各自正确却互相漂移。
$plannedRouteRows = @(
foreach ($line in $planLines) {
if ($line -match '^\|\s*(?<code>[AGTFRNM]\d{2})\s*\|\s*(?<kind>认证根页|根页|普通页|流程页|单实例页)\s*\|') {
$columns = @($line.Trim('|').Split('|') | ForEach-Object { $_.Trim() })
[pscustomobject]@{
Code = $Matches.code
Kind = $Matches.kind
Columns = $columns
Line = $line
}
}
}
)
if ($plannedRouteRows.Count -ne 52) {
throw "实施计划路由表不是 52 行:实际=$($plannedRouteRows.Count)"
}
$duplicatePlannedCodes = @($plannedRouteRows | Group-Object Code | Where-Object Count -gt 1)
if ($duplicatePlannedCodes.Count -gt 0) {
throw "实施计划路由表存在重复编号:$($duplicatePlannedCodes.Name -join ', ')"
}
$mappedCodes = @($routeRows | ForEach-Object Code)
$plannedCodes = @($plannedRouteRows | ForEach-Object Code)
$missingPlannedCodes = @($mappedCodes | Where-Object { $_ -notin $plannedCodes })
$stalePlannedCodes = @($plannedCodes | Where-Object { $_ -notin $mappedCodes })
if ($missingPlannedCodes.Count -gt 0 -or $stalePlannedCodes.Count -gt 0) {
throw "实施计划路由表与页面映射编号不相等:缺失=[$($missingPlannedCodes -join ', ')];陈旧=[$($stalePlannedCodes -join ', ')]"
}
$plannedRootCodes = @($plannedRouteRows | Where-Object { $_.Kind -in @('认证根页', '根页') } | ForEach-Object Code)
if (($plannedRootCodes -join ',') -ne 'A01,G01,F01,M01') {
throw "实施计划四个根语义错误:$($plannedRootCodes -join ',')"
}
$plannedBusinessRoots = @($plannedRouteRows | Where-Object Kind -eq '根页' | ForEach-Object Code)
if (($plannedBusinessRoots -join ',') -ne 'G01,F01,M01') {
throw "实施计划业务根页必须仅为 G01/F01/M01$($plannedBusinessRoots -join ',')"
}
if ($planText -notmatch 'T03: defineRoute\([^\r\n]*parentParamMap: \{ selectedId: "personId" \}') {
throw '实施计划缺少 T03 直接进入时 personId 到 selectedId 的父页参数映射'
}
if ($planText -notmatch 'openPage\("N02", \{ id \}, "N01"\)') {
throw '实施计划没有把 N01 打开 N02 的真实来源固定为 N01'
}
$plannedTableText = ($plannedRouteRows | ForEach-Object Line) -join "`n"
foreach ($forbiddenPlannedParam in @('genealogyId,step', 'genealogyName')) {
if ($plannedTableText.Contains($forbiddenPlannedParam)) {
throw "实施计划路由表仍含已删除参数:$forbiddenPlannedParam"
}
}
if ($mappingText -match '汤正华堂侄') {
throw '当前产品文档不得使用具体姓名示例,应改为“某某某堂侄”等通用占位表达'
}
Write-Output "INTERFACE-PAGE-MAPPING-CONTRACT PASS ROUTES=$($routeRows.Count)"