完成50%

This commit is contained in:
2026-07-23 08:23:59 +08:00
parent 9b0ad62df4
commit f1edc6b533
218 changed files with 24318 additions and 5514 deletions
+118 -60
View File
@@ -3,7 +3,6 @@
$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'
@@ -13,8 +12,6 @@ $pagesDocument = Get-Content -Raw -Encoding UTF8 -LiteralPath $pagesPath | Conve
$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 = @(
@@ -58,6 +55,16 @@ if ($duplicateRoutes.Count -gt 0) {
throw "页面映射存在重复路由:$($duplicateRoutes.Name -join ', ')"
}
# 页面编号必须由实际文件名语义得出,不能把正确路径和另一个页面编号错误拼接。
foreach ($row in $routeRows) {
if ($row.Route -notmatch '/(?<routeCode>[a-z]\d{2})-[^/]+$') {
throw "页面映射路由缺少文件名语义编号:$($row.Route)"
}
if ($Matches.routeCode.ToUpperInvariant() -ne $row.Code) {
throw "页面映射编号与路由不一致:编号=$($row.Code),路由=$($row.Route)"
}
}
# 第四步:总表顺序和路由集合必须与 pages.json 精确一致,新增或删除页面时必须同轮更新唯一总表。
$mappedRoutes = @($routeRows | ForEach-Object Route)
$missingRoutes = @($registeredRoutes | Where-Object { $_ -notin $mappedRoutes })
@@ -72,11 +79,67 @@ for ($index = 0; $index -lt $registeredRoutes.Count; $index += 1) {
}
}
# 第五步:只允许 A04 和 T01 使用已经形成证据的专项结论;其余页面不能提前冒充完成 OpenAPI 审查。
# 第五步:A04、G 系列第一轮、F 系列当前写边界和 T01 允许登记已有专项证据;
# 其余页面不能提前冒充完成 OpenAPI 审查。F 系列每页必须同时写出已核对事实和仍未启用的边界。
$authSeriesReviewPatterns = @{
A01 = '`APP_SMS_LOGIN` 客户端链已落地.*`API-AUTH-TAC-001` 硬关闭.*见 2\.10'
A04 = '`APP_REGISTER`、4 位码、`RAppLoginVo → AppLoginVo\.access_token` 客户端链已落地.*真实 challenge.*红灯.*见 2\.10'
A05 = '`APP_FORGOT_PASSWORD` 客户端链已落地.*真实后端、Android 可访问替代与 MuMu 仍红灯.*见 2\.10'
}
$gSeriesWorkspacePatterns = @{
G01 = 'Task26 工作区红灯已建立.*`/mine`.*尚未接远端.*Task29 unread-count 红灯.*M01.*见 2\.13/5\.6'
G03 = 'Task35 已建立 atomic bootstrap、无 PII operation-status、统一 accessPreset、APP 可信地区与词法 ID 的后端/客户端双红灯.*见 2\.8/5\.12'
G05 = 'Task26 选定 `/overview` 为唯一 owner.*对象级授权和错误语义未关闭.*见 2\.8/5\.3'
}
$gSeriesFirstReviewCodes = @('G06', 'G08', 'G09', 'G10', 'G11', 'G12')
$fSeriesReviewPatterns = @{
F01 = '任务 7 已核对.*线上有动态列表/写入能力.*当前静态批次只读'
F02 = '线上写请求要求 `feedContent`.*minLength=1.*`feedType/mediaOssIds/sortOrder/status`.*`WRITE_UNAVAILABLE`'
F03 = '线上评论写请求要求 `commentContent`.*minLength=0/maxLength=1000.*不宣称发送成功'
F04 = '任务 7 已核对线上列表及写接口.*唯一只读 owner'
F05 = '线上未证明收藏合同.*`articleTitle/articleContent`.*minLength=1'
F06 = '线上 POST/PUT 已存在.*`categoryId/coverOssId` 为 int64.*真实调用未'
F07 = '线上创建要求 `albumName`.*不伪增列表或计数'
F08 = '任务 7 已核对.*复合身份.*跨谱'
F09 = '旧 `/files/upload` 已下线.*resumable init/chunk/complete.*complete 返回.*`ossId` 为 string.*照片创建要求 int64.*后端消除类型冲突前不得转换或提交'
F10 = '任务 7 未发现.*保持关闭'
}
$rSeriesReviewPatterns = @{
R01 = '任务 8 已核对.*lineage/persons/page.*词法字符串'
R02 = '任务 8 已核对.*AppLineagePersonBody.*`name` 必填.*真实写入未启用'
R03 = '任务 8 已核对.*relative-records.*不是.*ceremony gifts.*方向语义'
R04 = '任务 8 已核对.*`relativeId`.*`relativeName` 必填.*本地预览'
R05 = '任务 8 已核对.*ceremonies.*`ceremonyTitle/ceremonyType` 必填.*无正式枚举'
R06 = '任务 8 已核对.*invitations.*成员选项.*参与者'
R07 = '任务 8 已核对.*`ceremonyId`.*真实写入未启用'
R08 = '任务 8 已核对.*growth-records.*`lineagePersonId`.*`recordType` 无枚举'
R09 = '任务 8 已核对.*没有独立人生事件接口.*硬关闭'
R10 = '任务 8 已核对.*memos.*`memoTitle` 必填.*`completed` 无枚举'
R11 = '任务 8 已核对.*merit-records.*`donorName/meritTitle` 必填.*无枚举'
}
$nmSeriesReviewPatterns = @{
N01 = 'Task29 读取红灯已建立.*完整活动集合.*纯文本.*计数同域.*见 2\.13/5\.6.*Task30 前不得本地伪写'
N02 = 'Task29 选定 list-owned snapshot.*删除目标 CTA.*Task30.*私有字符串 ID.*见 2\.13/5\.6/5\.7'
M01 = 'Task28 profile GET 红灯.*身份失败不锁菜单.*见 2\.13/5\.5.*Task29 unread-count 红灯.*fixture 伪数.*见 2\.13/5\.6'
M02 = 'Task28 读取红灯仍是前置.*Task31 已建立 PUT merge.*If-Match.*typed response.*409 红灯.*头像不混入.*见 2\.13/5\.5/5\.8'
M03 = 'Task28 已建立 profile GET 红灯.*普通读取失败不得阻断密码入口.*设备状态仍无合同.*见 2\.13/5\.5'
M04 = 'Task33 已建立 raw writeOnly、ALL 会话撤销、typed 错误与 session marker 红灯.*四条密码 wire 必须同批迁移.*见 2\.13/5\.10'
M05 = 'Task34 已建立专用 SaToken 发码、全活动六位码、ALL 会话撤销、outbox 与 credential marker 红灯.*依赖 M04 raw wire.*见 2\.13/5\.11'
M06 = 'Task27 选定列表唯一 owner 并建立红灯.*专用 required、纯文本、仅发布、顺序和认证语义待后端关闭.*见 2\.13/5\.4'
M07 = 'Task25 已接 `POST /genealogy/app/feedback` 严格客户端.*remote 实测与 MuMu 待认证门禁关闭.*见 2\.13'
M08 = '线上无邀请码签发/校验/直入合同.*删除码值、复制和海报伪能力.*见 2\.13'
M09 = '套餐/订单端点存在但支付闭环未定义.*删除查询参数演示订单.*见 2\.13'
M10 = 'Task9 已完成本机清理基线.*Task32 已建立当前凭证族、幂等 200、logoutCoordinator、required RVoid 与部署复用红灯.*见 2\.13/5\.9'
}
$prematureRows = @(
$routeRows | Where-Object {
if ($_.Code -eq 'A04') { return $_.Line -notmatch '已核对注册成功响应为 `LoginResult`' -or $_.Line -notmatch '成功建立登录态并进入 G01' }
if ($authSeriesReviewPatterns.ContainsKey($_.Code)) { return $_.Line -notmatch $authSeriesReviewPatterns[$_.Code] }
if ($_.Code -eq 'T01') { return $_.Line -notmatch '专项已核对:现有递归 `LineagePersonTreeView` 不满足' }
if ($gSeriesWorkspacePatterns.ContainsKey($_.Code)) { return $_.Line -notmatch $gSeriesWorkspacePatterns[$_.Code] }
if ($_.Code -in $gSeriesFirstReviewCodes) { return $_.Line -notmatch '第一轮已核对' -or $_.Line -notmatch '见 2\.8' }
if ($fSeriesReviewPatterns.ContainsKey($_.Code)) { return $_.Line -notmatch $fSeriesReviewPatterns[$_.Code] }
if ($rSeriesReviewPatterns.ContainsKey($_.Code)) { return $_.Line -notmatch $rSeriesReviewPatterns[$_.Code] }
if ($nmSeriesReviewPatterns.ContainsKey($_.Code)) { return $_.Line -notmatch $nmSeriesReviewPatterns[$_.Code] }
return $_.Line -notmatch '待对应业务阶段 OpenAPI 审查'
}
)
@@ -98,10 +161,32 @@ foreach ($heading in @(
# 第六步:锁定旧资料中仍然有效的关键业务事实,避免未来把表格保留却悄悄清空真正的产品合同。
foreach ($requiredFact in @(
'832',
'A01 在本地校验通过后、登录请求前触发',
'A05 在请求发送短信验证码前触发',
'A04 当前临时在完整注册表单提交后触发',
'15—64 Unicode code point',
'无组成规则',
'SmsCodeSecret',
'恰好 6 位 ASCII 数字',
'/genealogy/app/auth/phone/sms/code',
'PHONE-CHANGE-OPENAPI-CONTRACT BLOCKED',
'API-PHONE-001',
'STEP_UP_UNAVAILABLE',
'AppGenealogyBootstrapBody',
'GenealogyBootstrapOperationKey',
'GenealogyAccessPreset',
'GenealogyRegionCode',
'FAILED_NO_COMMIT',
'G03-BOOTSTRAP-OPENAPI-CONTRACT BLOCKED',
'G03-BOOTSTRAP-CLIENT-RELEASE BLOCKED',
'/genealogy/app/region/search',
'GET 必须纯读',
'receipt→mine cache→context→G05',
'API-G03-001',
'API-G03-005',
'A01、A04、A05 已接入同一个 `TacVerification`',
'`APP_SMS_LOGIN`',
'`APP_REGISTER`',
'`APP_FORGOT_PASSWORD`',
'密码登录入口保持不可用',
'不能把本地滑动成功冒充服务端验证',
'聚焦密码框',
'审核中记录只展示进度和“撤回申请”',
'被拒绝记录展示原因和“修改后重新提交”',
@@ -128,7 +213,8 @@ foreach ($requiredFact in @(
'/genealogy/app/v2/',
'API-T01-001',
'单个原生页面实例',
'注册成功响应为 `LoginResult`',
'注册成功响应为 `RAppLoginVo`',
'`AppLoginVo.access_token`',
'aria-describedby',
'邀请码直接加入且不生成审核记录',
'appApi',
@@ -151,58 +237,30 @@ foreach ($requiredFact in @(
}
}
# 第七步:在可执行注册表落地前解析实施计划中的 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 '当前产品文档不得使用具体姓名示例,应改为“某某某堂侄”等通用占位表达'
}
$localRouteBoundary = [regex]::Match(
$mappingText,
'(?s)### 2\.6 当前本地数据与路由参数边界.*?(?=### 2\.7)'
).Value
$localRouteTable = [regex]::Match($localRouteBoundary, '(?s)\| 页面 \|.*?\n\n').Value
foreach ($retiredRParameter in @('`count`', '`saveResult`', '`giftId`', '`ritualId`', '`personName`')) {
if ($localRouteTable.Contains($retiredRParameter)) {
throw "R 系列本地路由边界仍登记已退役参数:$retiredRParameter"
}
}
foreach ($currentRContract in @(
'| R01 | `genealogyId`、`state` |',
'| R04 | `genealogyId`、`mode`、`relativeId`、`state` |',
'| R07 | `genealogyId`、`mode`、`ceremonyId`、`state` |',
'| R08、R09 | `genealogyId`、`personId`、`state` |',
'| R10、R11 | `genealogyId`、`state` |'
)) {
if (-not $localRouteTable.Contains($currentRContract)) {
throw "R 系列本地路由边界缺少当前复合身份合同:$currentRContract"
}
}
Write-Output "INTERFACE-PAGE-MAPPING-CONTRACT PASS ROUTES=$($routeRows.Count)"