本轮完成“考点与考场变更审批”原生迁移:

GET/POST/PATCH /api/admin/centers
PATCH /api/admin/center-change-requests/{id}
校级数据范围、班级管理员禁用
省市区县及结构化考场校验
正式档案和待审申请代码去重
多级工作流分配、通过与拒绝
终审时原子更新流程、审计、考点及考场
新增开关 ADMIN_NATIVE_CENTERS_ENABLED=false
This commit is contained in:
2026-07-23 09:17:04 +08:00 Unverified
parent e845ba8d3a
commit 518c922773
13 changed files with 1123 additions and 21 deletions
+127
View File
@@ -441,6 +441,7 @@ try {
ADMIN_NATIVE_ACCOUNT_BATCHES_ENABLED = 'true'
ADMIN_NATIVE_CONFIGURATION_ENABLED = 'true'
ADMIN_NATIVE_NOTICE_MANAGEMENT_ENABLED = 'true'
ADMIN_NATIVE_CENTERS_ENABLED = 'true'
ADMIN_NATIVE_ALLOW_MEMORY = 'true'
LegacyNode__Enabled = 'true'
LegacyNode__BaseUrl = $legacyBaseUrl
@@ -801,6 +802,131 @@ try {
throw 'Native admins route did not preserve the class-admin boundary'
}
$legacyCenters = Invoke-WebRequest -Uri "$legacyBaseUrl/api/admin/centers" -WebSession $session
$nativeCenters = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/centers" -WebSession $nativeSession
if ($nativeCenters.Headers['X-EIS-Implementation'] -ne 'aspnet-core') {
throw 'Native center management list did not use ASP.NET Core'
}
Assert-JsonEquivalent -Expected $legacyCenters.Content -Actual $nativeCenters.Content -Label 'Super-admin center management'
$legacySchoolCenters = Invoke-WebRequest -Uri "$legacyBaseUrl/api/admin/centers" -WebSession $legacySchoolSession
$nativeSchoolCenters = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/centers" -WebSession $nativeSchoolSession
Assert-JsonEquivalent -Expected $legacySchoolCenters.Content -Actual $nativeSchoolCenters.Content -Label 'School-admin center management'
$classCentersForbidden = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/centers" -WebSession $nativeClassSession -SkipHttpErrorCheck
if ($classCentersForbidden.StatusCode -ne 403 -or $classCentersForbidden.Headers['X-EIS-Implementation'] -ne 'aspnet-core') {
throw 'Native center management did not preserve the class-admin boundary'
}
$schoolCenterState = $nativeSchoolCenters.Content | ConvertFrom-Json
$templateCenter = @($schoolCenterState.centers)[0]
if ($null -eq $templateCenter) {
throw 'Seed data did not provide a school-scoped center for center-management smoke testing'
}
$invalidCenterBody = @{
code = 'NATIVE_INVALID_CENTER'
name = '无考场测试考点'
provinceCode = $templateCenter.provinceCode
cityCode = $templateCenter.cityCode
districtCode = $templateCenter.districtCode
address = '迁移测试路 10 号'
rooms = @()
} | ConvertTo-Json -Depth 5 -Compress
$invalidCenter = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/centers" -Method Post -ContentType 'application/json' -Body $invalidCenterBody -WebSession $nativeSchoolSession -SkipHttpErrorCheck
if ($invalidCenter.StatusCode -ne 400) {
throw 'Native center submission accepted a center without structured rooms'
}
$centerCreateBody = @{
code = 'NATIVE_CENTER'
name = '原生迁移考点'
provinceCode = $templateCenter.provinceCode
cityCode = $templateCenter.cityCode
districtCode = $templateCenter.districtCode
address = '迁移测试路 11 号'
contact = '迁移联系人'
managerName = '考务负责人'
managerPhone = '13800001111'
emergencyPhone = '13800002222'
gateOpenTime = '07:00'
transport = '地铁迁移测试站'
status = 'active'
notes = 'ASP.NET Core 原生考点'
rooms = @(@{
code = 'NATIVE_ROOM_01'
name = '迁移第一考场'
building = '迁移楼'
floor = '1层'
capacity = 32
seatPlan = '4x8'
roomType = 'standard'
status = 'active'
notes = '原生创建'
})
} | ConvertTo-Json -Depth 6 -Compress
$centerCreateResponse = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/centers" -Method Post -ContentType 'application/json' -Body $centerCreateBody -WebSession $nativeSchoolSession
$createdCenterChange = ($centerCreateResponse.Content | ConvertFrom-Json).changeRequest
if ($centerCreateResponse.StatusCode -ne 202 -or $centerCreateResponse.Headers['X-EIS-Implementation'] -ne 'aspnet-core' -or $createdCenterChange.status -ne 'pending' -or $createdCenterChange.workflow.assignee.adminLevel -ne 'super') {
throw 'Native center submission did not create the expected super-admin workflow'
}
$duplicateCenterSubmission = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/centers" -Method Post -ContentType 'application/json' -Body $centerCreateBody -WebSession $nativeSchoolSession -SkipHttpErrorCheck
if ($duplicateCenterSubmission.StatusCode -ne 409) {
throw 'Native center submission did not reserve pending center codes'
}
$centerApprovalBody = @{ status = 'approved'; reviewNote = '原生考点终审通过' } | ConvertTo-Json -Compress
$centerApprovalResponse = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/center-change-requests/$($createdCenterChange.id)" -Method Patch -ContentType 'application/json' -Body $centerApprovalBody -WebSession $nativeSession
if ($centerApprovalResponse.Headers['X-EIS-Implementation'] -ne 'aspnet-core' -or ($centerApprovalResponse.Content | ConvertFrom-Json).changeRequest.status -ne 'approved') {
throw 'Native center approval did not complete the workflow'
}
$centersAfterCreate = Invoke-RestMethod -Uri "$nativeAuthBaseUrl/api/admin/centers" -WebSession $nativeSchoolSession
$createdCenter = @($centersAfterCreate.centers | Where-Object { $_.code -eq 'NATIVE_CENTER' })[0]
if ($null -eq $createdCenter -or $createdCenter.totalCapacity -ne 32 -or $createdCenter.rooms.Count -ne 1) {
throw 'Native center approval did not materialize the center and room atomically'
}
$centerUpdateBody = @{
code = $createdCenter.code
name = '原生迁移考点(更新)'
provinceCode = $createdCenter.provinceCode
cityCode = $createdCenter.cityCode
districtCode = $createdCenter.districtCode
address = '迁移测试路 12 号'
contact = $createdCenter.contact
managerName = $createdCenter.managerName
managerPhone = $createdCenter.managerPhone
emergencyPhone = $createdCenter.emergencyPhone
gateOpenTime = $createdCenter.gateOpenTime
transport = $createdCenter.transport
status = 'active'
notes = 'ASP.NET Core 原生考点已更新'
rooms = @($createdCenter.rooms | ForEach-Object {
@{
id = $_.id
code = $_.code
name = '迁移第一考场(更新)'
building = $_.building
floor = $_.floor
capacity = 36
seatPlan = $_.seatPlan
roomType = $_.roomType
status = $_.status
notes = '原生更新'
}
})
} | ConvertTo-Json -Depth 6 -Compress
$centerUpdateResponse = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/centers/$($createdCenter.id)" -Method Patch -ContentType 'application/json' -Body $centerUpdateBody -WebSession $nativeSchoolSession
$updatedCenterChange = ($centerUpdateResponse.Content | ConvertFrom-Json).changeRequest
if ($centerUpdateResponse.StatusCode -ne 202 -or $updatedCenterChange.requestType -ne 'update') {
throw 'Native center update did not create a change workflow'
}
$duplicateCenterUpdate = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/centers/$($createdCenter.id)" -Method Patch -ContentType 'application/json' -Body $centerUpdateBody -WebSession $nativeSchoolSession -SkipHttpErrorCheck
if ($duplicateCenterUpdate.StatusCode -ne 409) {
throw 'Native center update allowed a second pending change for the same center'
}
Invoke-RestMethod -Uri "$nativeAuthBaseUrl/api/admin/center-change-requests/$($updatedCenterChange.id)" -Method Patch -ContentType 'application/json' -Body $centerApprovalBody -WebSession $nativeSession | Out-Null
$centersAfterUpdate = Invoke-RestMethod -Uri "$nativeAuthBaseUrl/api/admin/centers" -WebSession $nativeSchoolSession
$updatedCenter = @($centersAfterUpdate.centers | Where-Object { $_.id -eq $createdCenter.id })[0]
if ($updatedCenter.name -ne '原生迁移考点(更新)' -or $updatedCenter.address -ne '迁移测试路 12 号' -or $updatedCenter.totalCapacity -ne 36 -or $updatedCenter.pendingChange -ne $false) {
throw 'Native center update approval did not replace the formal center archive'
}
foreach ($configurationRoute in @('number-rules', 'workflows')) {
$legacyConfiguration = Invoke-WebRequest -Uri "$legacyBaseUrl/api/admin/$configurationRoute" -WebSession $session
$nativeConfiguration = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/$configurationRoute" -WebSession $nativeSession
@@ -1143,6 +1269,7 @@ try {
NativeAdminAccountBatches = 'passed'
NativeAdminConfiguration = 'passed'
NativeAdminNoticeManagement = 'passed'
NativeAdminCenters = 'passed'
} | Format-List
}
finally {