本轮完成“考点与考场变更审批”原生迁移:
GET/POST/PATCH /api/admin/centers
PATCH /api/admin/center-change-requests/{id}
校级数据范围、班级管理员禁用
省市区县及结构化考场校验
正式档案和待审申请代码去重
多级工作流分配、通过与拒绝
终审时原子更新流程、审计、考点及考场
新增开关 ADMIN_NATIVE_CENTERS_ENABLED=false
This commit is contained in:
1 parent
e845ba8d3a
commit
518c922773
13 files changed
+1114
-12
No files matched your search
@@ -35,6 +35,8 @@ ADMIN_NATIVE_ACCOUNT_BATCHES_ENABLED=false
|
||||
ADMIN_NATIVE_CONFIGURATION_ENABLED=false
|
||||
# 通知公告列表、手工公告创建修改及系统公示显隐控制;必须同时启用管理端只读接口。
|
||||
ADMIN_NATIVE_NOTICE_MANAGEMENT_ENABLED=false
|
||||
# 考点考场读取、变更提交及审批;必须同时启用管理端只读接口。
|
||||
ADMIN_NATIVE_CENTERS_ENABLED=false
|
||||
|
||||
# 仅在首次创建空数据库时使用。部署前务必修改初始密码。
|
||||
INITIAL_ADMIN_USERNAME=admin
|
||||
|
||||
+4
-3
@@ -12,7 +12,7 @@
|
||||
- [x] 招生公示与 HMAC 文书验真公开接口
|
||||
- [x] 登录、自主注册、Session 与 TOTP(兼容开关默认关闭)
|
||||
- [x] 考生业务
|
||||
- [ ] 管理后台、审批流和考务编排(管理端读取、组织维护、批量报名号审批、流程配置及通知公告管理已原生化)
|
||||
- [ ] 管理后台、审批流和考务编排(管理端读取、组织维护、批量报名号审批、流程配置、通知公告及考点变更审批已原生化)
|
||||
- [x] 考生志愿填报与招生录取查询
|
||||
- [ ] Excel、文书和缓存
|
||||
- [ ] 容器入口切换及 Node.js 后端移除
|
||||
@@ -54,7 +54,7 @@ $env:AUTH_NATIVE_ENABLED = 'true'
|
||||
$env:CANDIDATE_NATIVE_ENABLED = 'true'
|
||||
```
|
||||
|
||||
管理后台第一批只读接口(管理上下文、仪表盘、学校、学校组织、管理员和考试列表)已经原生化,并保留超级、校级、班级管理员的权限与数据作用域。第二批覆盖学校、班级和管理员的创建与维护、管理员密码重置及自主注册开关;更新操作与审计日志在同一事务中提交,停用或重置管理员会同步失效其会话。第三批覆盖批量报名号申领的读取、提交和审批,终审会按照当前号码规则原子生成考生账号、初始密码和待补录资料。第四批覆盖报名号规则及审批流程定义的读取与维护,并保留流程层级和批量申领终审约束。第五批覆盖完整通知公告管理:超级管理员可读取手工公告与五类系统公示、创建和编辑手工公告,并控制系统公示是否公开显示;HTML 净化、显隐更新和审计日志均由 ASP.NET Core 原生处理。
|
||||
管理后台第一批只读接口(管理上下文、仪表盘、学校、学校组织、管理员和考试列表)已经原生化,并保留超级、校级、班级管理员的权限与数据作用域。第二批覆盖学校、班级和管理员的创建与维护、管理员密码重置及自主注册开关;更新操作与审计日志在同一事务中提交,停用或重置管理员会同步失效其会话。第三批覆盖批量报名号申领的读取、提交和审批,终审会按照当前号码规则原子生成考生账号、初始密码和待补录资料。第四批覆盖报名号规则及审批流程定义的读取与维护,并保留流程层级和批量申领终审约束。第五批覆盖完整通知公告管理:超级管理员可读取手工公告与五类系统公示、创建和编辑手工公告,并控制系统公示是否公开显示;HTML 净化、显隐更新和审计日志均由 ASP.NET Core 原生处理。第六批覆盖考点考场读取、新增与修改申请、重复待审拦截及多级审批;终审会在同一事务中更新流程、审计日志和正式考点考场档案。
|
||||
|
||||
其余审批流和考务编排接口仍转发给 Node,因此管理端开关都要求原生认证和共享 Redis;各写入子功能还必须与只读开关一起启用:
|
||||
|
||||
@@ -65,9 +65,10 @@ $env:ADMIN_NATIVE_ORGANIZATION_WRITES_ENABLED = 'true'
|
||||
$env:ADMIN_NATIVE_ACCOUNT_BATCHES_ENABLED = 'true'
|
||||
$env:ADMIN_NATIVE_CONFIGURATION_ENABLED = 'true'
|
||||
$env:ADMIN_NATIVE_NOTICE_MANAGEMENT_ENABLED = 'true'
|
||||
$env:ADMIN_NATIVE_CENTERS_ENABLED = 'true'
|
||||
```
|
||||
|
||||
旧版 `ADMIN_NATIVE_NOTICE_WRITES_ENABLED` 仍可作为兼容别名使用。`GET /health/migration` 的 `administration.nativeReadsEnabled`、`administration.nativeOrganizationWritesEnabled`、`administration.nativeAccountBatchesEnabled`、`administration.nativeConfigurationEnabled`、`administration.nativeNoticeManagementEnabled` 和 `administration.nativeRoutes` 会报告这些端点是否已切换。
|
||||
旧版 `ADMIN_NATIVE_NOTICE_WRITES_ENABLED` 仍可作为兼容别名使用。`GET /health/migration` 的 `administration.nativeReadsEnabled`、`administration.nativeOrganizationWritesEnabled`、`administration.nativeAccountBatchesEnabled`、`administration.nativeConfigurationEnabled`、`administration.nativeNoticeManagementEnabled`、`administration.nativeCentersEnabled` 和 `administration.nativeRoutes` 会报告这些端点是否已切换。
|
||||
|
||||
完整的宿主、静态资源、JSON 转发和 Session Cookie 冒烟测试:
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Loaded 3 of 13 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user