本批“报名审核与缴费更新”迁移完成:

PATCH /api/admin/registrations/{registrationId}
PATCH /api/admin/payments/{registrationId}
实现了多级报名审核、终审绑定报名号与号码规则、缴费确认/撤销、状态防重、考试归档锁定及事务审计
This commit is contained in:
biss committed 2026-07-23 10:00:26 +08:00
1 parent 3f555929fd
commit 7fd20caf3e
12 files changed
+542 -13

No files matched your search

+2
View File
@@ -41,6 +41,8 @@ ADMIN_NATIVE_CENTERS_ENABLED=false
ADMIN_NATIVE_OPERATIONAL_READS_ENABLED=false ADMIN_NATIVE_OPERATIONAL_READS_ENABLED=false
# 考生账户归档、密码重置及资料流程审核;必须同时启用考生/报名/缴费读取。 # 考生账户归档、密码重置及资料流程审核;必须同时启用考生/报名/缴费读取。
ADMIN_NATIVE_CANDIDATE_MANAGEMENT_ENABLED=false ADMIN_NATIVE_CANDIDATE_MANAGEMENT_ENABLED=false
# 报名审核与缴费状态更新;必须同时启用考生/报名/缴费读取。
ADMIN_NATIVE_REGISTRATION_PAYMENT_WRITES_ENABLED=false
# 仅在首次创建空数据库时使用。部署前务必修改初始密码。 # 仅在首次创建空数据库时使用。部署前务必修改初始密码。
INITIAL_ADMIN_USERNAME=admin INITIAL_ADMIN_USERNAME=admin
+4 -3
View File
@@ -12,7 +12,7 @@
- [x] 招生公示与 HMAC 文书验真公开接口 - [x] 招生公示与 HMAC 文书验真公开接口
- [x] 登录、自主注册、Session 与 TOTP(兼容开关默认关闭) - [x] 登录、自主注册、Session 与 TOTP(兼容开关默认关闭)
- [x] 考生业务 - [x] 考生业务
- [ ] 管理后台、审批流和考务编排(管理端基础读取、组织维护、批量报名号审批、流程配置、通知公告、考点变更审批、考生/报名/缴费读取及考生账号维护已原生化) - [ ] 管理后台、审批流和考务编排(管理端基础读取、组织维护、批量报名号审批、流程配置、通知公告、考点变更审批、考生/报名/缴费读取、考生账号维护及报名缴费写入已原生化)
- [x] 考生志愿填报与招生录取查询 - [x] 考生志愿填报与招生录取查询
- [ ] Excel、文书和缓存 - [ ] Excel、文书和缓存
- [ ] 容器入口切换及 Node.js 后端移除 - [ ] 容器入口切换及 Node.js 后端移除
@@ -54,7 +54,7 @@ $env:AUTH_NATIVE_ENABLED = 'true'
$env:CANDIDATE_NATIVE_ENABLED = 'true' $env:CANDIDATE_NATIVE_ENABLED = 'true'
``` ```
管理后台第一批只读接口(管理上下文、仪表盘、学校、学校组织、管理员和考试列表)已经原生化,并保留超级、校级、班级管理员的权限与数据作用域。第二批覆盖学校、班级和管理员的创建与维护、管理员密码重置及自主注册开关;更新操作与审计日志在同一事务中提交,停用或重置管理员会同步失效其会话。第三批覆盖批量报名号申领的读取、提交和审批,终审会按照当前号码规则原子生成考生账号、初始密码和待补录资料。第四批覆盖报名号规则及审批流程定义的读取与维护,并保留流程层级和批量申领终审约束。第五批覆盖完整通知公告管理:超级管理员可读取手工公告与五类系统公示、创建和编辑手工公告,并控制系统公示是否公开显示;HTML 净化、显隐更新和审计日志均由 ASP.NET Core 原生处理。第六批覆盖考点考场读取、新增与修改申请、重复待审拦截及多级审批;终审会在同一事务中更新流程、审计日志和正式考点考场档案。第七批覆盖考生档案、报名记录和已审核报名的缴费记录读取,并保持三级管理员的数据作用域、证件号脱敏、考试科目及审批流投影与 Node 一致。第八批覆盖校级按班级或年级归档及恢复考生账户、超级管理员重置考生密码、三级管理员按配置流程审核考生资料;账号与流程更新、审计日志在同一事务中提交,归档和密码重置会同步失效相关 ASP.NET Core 会话。 管理后台第一批只读接口(管理上下文、仪表盘、学校、学校组织、管理员和考试列表)已经原生化,并保留超级、校级、班级管理员的权限与数据作用域。第二批覆盖学校、班级和管理员的创建与维护、管理员密码重置及自主注册开关;更新操作与审计日志在同一事务中提交,停用或重置管理员会同步失效其会话。第三批覆盖批量报名号申领的读取、提交和审批,终审会按照当前号码规则原子生成考生账号、初始密码和待补录资料。第四批覆盖报名号规则及审批流程定义的读取与维护,并保留流程层级和批量申领终审约束。第五批覆盖完整通知公告管理:超级管理员可读取手工公告与五类系统公示、创建和编辑手工公告,并控制系统公示是否公开显示;HTML 净化、显隐更新和审计日志均由 ASP.NET Core 原生处理。第六批覆盖考点考场读取、新增与修改申请、重复待审拦截及多级审批;终审会在同一事务中更新流程、审计日志和正式考点考场档案。第七批覆盖考生档案、报名记录和已审核报名的缴费记录读取,并保持三级管理员的数据作用域、证件号脱敏、考试科目及审批流投影与 Node 一致。第八批覆盖校级按班级或年级归档及恢复考生账户、超级管理员重置考生密码、三级管理员按配置流程审核考生资料;账号与流程更新、审计日志在同一事务中提交,归档和密码重置会同步失效相关 ASP.NET Core 会话。第九批覆盖报名审核流程及缴费状态更新:多步报名终审会绑定考生报名号和当前号码规则,缴费确认及撤销会维护确认时间和操作人,所有流程、业务状态与审计日志均在同一事务中提交。
其余审批流和考务编排接口仍转发给 Node,因此管理端开关都要求原生认证和共享 Redis;各写入子功能还必须与只读开关一起启用: 其余审批流和考务编排接口仍转发给 Node,因此管理端开关都要求原生认证和共享 Redis;各写入子功能还必须与只读开关一起启用:
@@ -68,9 +68,10 @@ $env:ADMIN_NATIVE_NOTICE_MANAGEMENT_ENABLED = 'true'
$env:ADMIN_NATIVE_CENTERS_ENABLED = 'true' $env:ADMIN_NATIVE_CENTERS_ENABLED = 'true'
$env:ADMIN_NATIVE_OPERATIONAL_READS_ENABLED = 'true' $env:ADMIN_NATIVE_OPERATIONAL_READS_ENABLED = 'true'
$env:ADMIN_NATIVE_CANDIDATE_MANAGEMENT_ENABLED = 'true' $env:ADMIN_NATIVE_CANDIDATE_MANAGEMENT_ENABLED = 'true'
$env:ADMIN_NATIVE_REGISTRATION_PAYMENT_WRITES_ENABLED = 'true'
``` ```
旧版 `ADMIN_NATIVE_NOTICE_WRITES_ENABLED` 仍可作为兼容别名使用。`GET /health/migration` 的 `administration.nativeReadsEnabled`、`administration.nativeOrganizationWritesEnabled`、`administration.nativeAccountBatchesEnabled`、`administration.nativeConfigurationEnabled`、`administration.nativeNoticeManagementEnabled`、`administration.nativeCentersEnabled`、`administration.nativeOperationalReadsEnabled`、`administration.nativeCandidateManagementEnabled` 和 `administration.nativeRoutes` 会报告这些端点是否已切换。 旧版 `ADMIN_NATIVE_NOTICE_WRITES_ENABLED` 仍可作为兼容别名使用。`GET /health/migration` 的 `administration.nativeReadsEnabled`、`administration.nativeOrganizationWritesEnabled`、`administration.nativeAccountBatchesEnabled`、`administration.nativeConfigurationEnabled`、`administration.nativeNoticeManagementEnabled`、`administration.nativeCentersEnabled`、`administration.nativeOperationalReadsEnabled`、`administration.nativeCandidateManagementEnabled`、`administration.nativeRegistrationPaymentWritesEnabled` 和 `administration.nativeRoutes` 会报告这些端点是否已切换。
完整的宿主、静态资源、JSON 转发和 Session Cookie 冒烟测试: 完整的宿主、静态资源、JSON 转发和 Session Cookie 冒烟测试:
+60
View File
@@ -444,6 +444,7 @@ try {
ADMIN_NATIVE_CENTERS_ENABLED = 'true' ADMIN_NATIVE_CENTERS_ENABLED = 'true'
ADMIN_NATIVE_OPERATIONAL_READS_ENABLED = 'true' ADMIN_NATIVE_OPERATIONAL_READS_ENABLED = 'true'
ADMIN_NATIVE_CANDIDATE_MANAGEMENT_ENABLED = 'true' ADMIN_NATIVE_CANDIDATE_MANAGEMENT_ENABLED = 'true'
ADMIN_NATIVE_REGISTRATION_PAYMENT_WRITES_ENABLED = 'true'
ADMIN_NATIVE_ALLOW_MEMORY = 'true' ADMIN_NATIVE_ALLOW_MEMORY = 'true'
LegacyNode__Enabled = 'true' LegacyNode__Enabled = 'true'
LegacyNode__BaseUrl = $legacyBaseUrl LegacyNode__BaseUrl = $legacyBaseUrl
@@ -906,6 +907,64 @@ try {
$nativeCandidatesAfterManagement = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/candidates" -WebSession $nativeSession $nativeCandidatesAfterManagement = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/candidates" -WebSession $nativeSession
Assert-JsonEquivalent -Expected $legacyCandidatesAfterManagement.Content -Actual $nativeCandidatesAfterManagement.Content -Label 'Candidate management follow-up' Assert-JsonEquivalent -Expected $legacyCandidatesAfterManagement.Content -Actual $nativeCandidatesAfterManagement.Content -Label 'Candidate management follow-up'
$prematurePayment = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/payments/$($nativeRegistration.registration.id)" -Method Patch -ContentType 'application/json' -Body '{}' -WebSession $nativeSession -SkipHttpErrorCheck
if ($prematurePayment.StatusCode -ne 409 -or $prematurePayment.Headers['X-EIS-Implementation'] -ne 'aspnet-core') {
throw 'Native payment update did not require an approved registration'
}
$invalidRegistrationReview = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/registrations/$($nativeRegistration.registration.id)" -Method Patch -ContentType 'application/json' -Body '{"status":"invalid"}' -WebSession $nativeSession -SkipHttpErrorCheck
if ($invalidRegistrationReview.StatusCode -ne 400 -or $invalidRegistrationReview.Headers['X-EIS-Implementation'] -ne 'aspnet-core') {
throw 'Native registration review accepted an invalid status'
}
$registrationReviewIterations = 0
do {
$registrationReviewIterations += 1
if ($registrationReviewIterations -gt 10) {
throw 'Native registration workflow did not reach a terminal state'
}
$registrationReviewBody = @{ status = 'approved'; reviewNote = "原生报名审核第 $registrationReviewIterations 步" } | ConvertTo-Json -Compress
$registrationReviewResponse = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/registrations/$($nativeRegistration.registration.id)" -Method Patch -ContentType 'application/json' -Body $registrationReviewBody -WebSession $nativeSession
if ($registrationReviewResponse.Headers['X-EIS-Implementation'] -ne 'aspnet-core') {
throw 'Registration review did not use ASP.NET Core'
}
$registrationReview = $registrationReviewResponse.Content | ConvertFrom-Json
} while ($registrationReview.workflow.status -eq 'pending')
if ($registrationReview.registration.status -ne 'approved' -or $registrationReview.workflow.status -ne 'approved' -or $registrationReview.registration.registrationNumber -ne $registration.registrationNumber) {
throw 'Native registration review did not complete the configured workflow or bind the candidate number'
}
$duplicateRegistrationReview = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/registrations/$($nativeRegistration.registration.id)" -Method Patch -ContentType 'application/json' -Body $registrationReviewBody -WebSession $nativeSession -SkipHttpErrorCheck
if ($duplicateRegistrationReview.StatusCode -ne 409) {
throw 'Native registration review did not protect a completed workflow'
}
$legacyRegistrationsAfterReview = Invoke-WebRequest -Uri "$legacyBaseUrl/api/admin/registrations" -WebSession $session
$nativeRegistrationsAfterReview = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/registrations" -WebSession $nativeSession
Assert-JsonEquivalent -Expected $legacyRegistrationsAfterReview.Content -Actual $nativeRegistrationsAfterReview.Content -Label 'Registration review follow-up'
$invalidPayment = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/payments/$($nativeRegistration.registration.id)" -Method Patch -ContentType 'application/json' -Body '{"status":"settled"}' -WebSession $nativeSession -SkipHttpErrorCheck
if ($invalidPayment.StatusCode -ne 400 -or $invalidPayment.Headers['X-EIS-Implementation'] -ne 'aspnet-core') {
throw 'Native payment update accepted an invalid status'
}
$paidResponse = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/payments/$($nativeRegistration.registration.id)" -Method Patch -ContentType 'application/json' -Body '{}' -WebSession $nativeSession
$paidResult = ($paidResponse.Content | ConvertFrom-Json).payment
if ($paidResponse.Headers['X-EIS-Implementation'] -ne 'aspnet-core' -or $paidResult.status -ne 'paid' -or -not $paidResult.paidAt -or $paidResult.paidByName -ne $nativeLogin.user.displayName) {
throw 'Native payment update did not record the payment actor and timestamp'
}
$duplicatePayment = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/payments/$($nativeRegistration.registration.id)" -Method Patch -ContentType 'application/json' -Body '{}' -WebSession $nativeSession -SkipHttpErrorCheck
if ($duplicatePayment.StatusCode -ne 409) {
throw 'Native payment update did not reject an unchanged status'
}
$legacyPaymentsAfterConfirmation = Invoke-WebRequest -Uri "$legacyBaseUrl/api/admin/payments" -WebSession $session
$nativePaymentsAfterConfirmation = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/payments" -WebSession $nativeSession
Assert-JsonEquivalent -Expected $legacyPaymentsAfterConfirmation.Content -Actual $nativePaymentsAfterConfirmation.Content -Label 'Payment confirmation follow-up'
$unpaidBody = @{ status = 'unpaid' } | ConvertTo-Json -Compress
$unpaidResponse = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/payments/$($nativeRegistration.registration.id)" -Method Patch -ContentType 'application/json' -Body $unpaidBody -WebSession $nativeSession
$unpaidResult = ($unpaidResponse.Content | ConvertFrom-Json).payment
if ($unpaidResult.status -ne 'unpaid' -or $null -ne $unpaidResult.paidAt -or $null -ne $unpaidResult.paidBy -or $unpaidResult.paidByName -ne '') {
throw 'Native payment update did not clear payment metadata when reverting to unpaid'
}
$legacyPaymentsAfterReversal = Invoke-WebRequest -Uri "$legacyBaseUrl/api/admin/payments" -WebSession $session
$nativePaymentsAfterReversal = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/payments" -WebSession $nativeSession
Assert-JsonEquivalent -Expected $legacyPaymentsAfterReversal.Content -Actual $nativePaymentsAfterReversal.Content -Label 'Payment reversal follow-up'
$legacyCenters = Invoke-WebRequest -Uri "$legacyBaseUrl/api/admin/centers" -WebSession $session $legacyCenters = Invoke-WebRequest -Uri "$legacyBaseUrl/api/admin/centers" -WebSession $session
$nativeCenters = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/centers" -WebSession $nativeSession $nativeCenters = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/centers" -WebSession $nativeSession
if ($nativeCenters.Headers['X-EIS-Implementation'] -ne 'aspnet-core') { if ($nativeCenters.Headers['X-EIS-Implementation'] -ne 'aspnet-core') {
@@ -1376,6 +1435,7 @@ try {
NativeAdminCenters = 'passed' NativeAdminCenters = 'passed'
NativeAdminOperationalReads = 'passed' NativeAdminOperationalReads = 'passed'
NativeAdminCandidateManagement = 'passed' NativeAdminCandidateManagement = 'passed'
NativeAdminRegistrationPaymentWrites = 'passed'
} | Format-List } | Format-List
} }
finally { finally {
Loaded 3 of 12 files, more files were not shown because too many files have changed in this diff. Show more