PUT /api/candidate/profile

行政区划、学校班级、特长类别校验
证件号码唯一性
自动创建资料审核工作流
资料和用户显示名称原子更新

POST /api/candidate/registrations
资料审核状态、报名时间和科目校验
重复报名防护
自动选择负载最低的审批管理员
报名、科目和审批流原子写入
This commit is contained in:
biss committed 2026-07-22 19:55:19 +08:00
1 parent 07efa6813b
commit aecb0a04e7
16 files changed
+987 -28

No files matched your search

+1 -1
View File
@@ -47,7 +47,7 @@ $env:AUTH_NATIVE_ENABLED = 'true'
开发环境未配置 Redis 时可以使用进程内状态独立验证原生认证。生产环境以及仍需访问 Node 受保护接口的联调环境必须配置 `REDIS_URL` 或 `REDIS_SESSION_URL`;两个运行时会复用相同逻辑库和 `exam-information:auth` 键前缀,从而共享登录会话。`GET /health/migration` 会报告 `authentication.nativeEnabled`、状态后端和跨运行时会话共享能力。
考生域第一批只读端点(首页、通知、个人资料、可报名考试、我的报名)可通过以下开关原生运行;该开关必须与原生认证及共享 Redis 同时启用。资料更新、报名提交、成绩、准考证、复议和志愿填报仍会继续转发给 Node:
考生域核心端点(首页、通知、个人资料读取与提交、可报名考试、我的报名读取与提交)可通过以下开关原生运行;该开关必须与原生认证及共享 Redis 同时启用。成绩、准考证、复议和志愿填报仍会继续转发给 Node:
```powershell
$env:AUTH_NATIVE_ENABLED = 'true'
+103
View File
@@ -440,6 +440,108 @@ try {
throw 'Native candidate profile route was not available during onboarding'
}
$invalidSpecialtyBody = @{
name = '原生迁移注册考生'
gender = '女'
idNumber = 'SMOKE-INVALID-SPECIALTY'
phone = '13800000000'
email = 'candidate@example.test'
nativePlace = '江苏连云港'
address = '迁移测试路 1 号'
schoolId = $registrationSchool.id
classId = $registrationClass.id
provinceCode = '320000'
cityCode = '320700'
districtCode = '320706'
specialtyCategory = 'arts'
specialtyType = 'track_field'
} | ConvertTo-Json -Compress
$invalidSpecialty = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/candidate/profile" -Method Put -ContentType 'application/json' -Body $invalidSpecialtyBody -WebSession $registeredSession -SkipHttpErrorCheck
if ($invalidSpecialty.StatusCode -ne 400) {
throw 'Native candidate profile API accepted a mismatched specialty category and type'
}
$profileIdNumber = "SMOKE-NATIVE-$([guid]::NewGuid().ToString('N'))"
$profileBody = @{
name = '原生迁移注册考生'
gender = '女'
idNumber = $profileIdNumber
phone = '13800000000'
email = 'candidate@example.test'
address = '迁移测试路 1 号'
emergencyContact = '测试联系人'
emergencyPhone = '13900000000'
nativePlace = '江苏连云港'
birthDate = '2010-01-02'
ethnicity = '汉族'
postalCode = '222000'
guardianName = '测试监护人'
guardianPhone = '13700000000'
schoolId = $registrationSchool.id
classId = $registrationClass.id
provinceCode = '320000'
cityCode = '320700'
districtCode = '320706'
specialtyCategory = ''
specialtyType = ''
specialtyCertificate = ''
policyEligibility = ''
} | ConvertTo-Json -Compress
$profileUpdateResponse = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/candidate/profile" -Method Put -ContentType 'application/json' -Body $profileBody -WebSession $registeredSession
if ($profileUpdateResponse.Headers['X-EIS-Implementation'] -ne 'aspnet-core') {
throw 'Candidate profile update did not use the native ASP.NET Core endpoint'
}
$profileUpdate = $profileUpdateResponse.Content | ConvertFrom-Json
if ($profileUpdate.profile.profileCompleted -ne $true -or $profileUpdate.profile.districtName -ne '海州区' -or $profileUpdate.profile.status -ne 'pending') {
throw 'Native candidate profile update did not resolve the region or persist onboarding state'
}
$prematureRegistrationBody = @{ examId = 'not-approved-yet'; subjectIds = @('none') } | ConvertTo-Json -Compress
$prematureRegistration = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/candidate/registrations" -Method Post -ContentType 'application/json' -Body $prematureRegistrationBody -WebSession $registeredSession -SkipHttpErrorCheck
if ($prematureRegistration.StatusCode -ne 403) {
throw 'Native registration API did not require an approved candidate profile'
}
$candidateWriteSetup = Start-TestProcess -FileName $nodeExecutable -ArgumentList @(
'tests/helpers/prepare-candidate-write-smoke.mjs', $smokeDatabasePath, $registration.registrationNumber
) -Environment $nodeEnvironment
if (-not $candidateWriteSetup.WaitForExit(10000)) {
$candidateWriteSetup.Kill($true)
throw 'Timed out while preparing the native candidate write smoke test'
}
$candidateWriteSetupOutput = $candidateWriteSetup.StandardOutput.ReadToEnd().Trim()
$candidateWriteSetupError = $candidateWriteSetup.StandardError.ReadToEnd()
if ($candidateWriteSetup.ExitCode -ne 0) {
throw "Could not prepare the native candidate write smoke test`n$candidateWriteSetupError"
}
$candidateWriteSetup.Dispose()
$candidateWriteTarget = $candidateWriteSetupOutput | ConvertFrom-Json
$nativeRegistrationBody = @{
examId = $candidateWriteTarget.examId
subjectIds = @($candidateWriteTarget.subjectId)
} | ConvertTo-Json -Compress
$nativeRegistrationResponse = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/candidate/registrations" -Method Post -ContentType 'application/json' -Body $nativeRegistrationBody -WebSession $registeredSession
if ($nativeRegistrationResponse.StatusCode -ne 201 -or $nativeRegistrationResponse.Headers['X-EIS-Implementation'] -ne 'aspnet-core') {
throw 'Exam registration submission did not use the native ASP.NET Core endpoint'
}
$nativeRegistration = $nativeRegistrationResponse.Content | ConvertFrom-Json
if ($nativeRegistration.registration.status -ne 'pending' -or $nativeRegistration.registration.subjectIds.Count -ne 1) {
throw 'Native exam registration did not persist the selected subject and pending state'
}
$duplicateRegistration = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/candidate/registrations" -Method Post -ContentType 'application/json' -Body $nativeRegistrationBody -WebSession $registeredSession -SkipHttpErrorCheck
if ($duplicateRegistration.StatusCode -ne 409) {
throw 'Native exam registration did not reject a duplicate submission'
}
$legacyRegisteredSession = [Microsoft.PowerShell.Commands.WebRequestSession]::new()
Invoke-RestMethod -Uri "$legacyBaseUrl/api/auth/login" -Method Post -ContentType 'application/json' -Body $registeredLoginBody -WebSession $legacyRegisteredSession | Out-Null
foreach ($candidateWriteReadRoute in @('profile', 'registrations')) {
$legacyWriteRead = Invoke-WebRequest -Uri "$legacyBaseUrl/api/candidate/$candidateWriteReadRoute" -WebSession $legacyRegisteredSession
$nativeWriteRead = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/candidate/$candidateWriteReadRoute" -WebSession $registeredSession
Assert-JsonEquivalent -Expected $legacyWriteRead.Content -Actual $nativeWriteRead.Content -Label "Candidate write follow-up '$candidateWriteReadRoute'"
}
$nativeSession = [Microsoft.PowerShell.Commands.WebRequestSession]::new()
$nativeLoginResponse = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/auth/login" -Method Post -ContentType 'application/json' -Body $loginBody -WebSession $nativeSession
if ($nativeLoginResponse.Headers['X-EIS-Implementation'] -ne 'aspnet-core') {
@@ -528,6 +630,7 @@ try {
DocumentCodes = 'passed'
NativeAuthentication = 'passed'
NativeCandidateReads = 'passed'
NativeCandidateWrites = 'passed'
} | Format-List
}
finally {
@@ -4,7 +4,7 @@ namespace Eis.Application.Candidate;
public sealed record CandidateEndpointResult(int StatusCode, JsonObject Body);
public interface ICandidateQueryService
public interface ICandidateService
{
Task<CandidateEndpointResult> GetDashboardAsync(string sessionToken, CancellationToken cancellationToken);
@@ -15,4 +15,14 @@ public interface ICandidateQueryService
Task<CandidateEndpointResult> GetExamsAsync(string sessionToken, CancellationToken cancellationToken);
Task<CandidateEndpointResult> GetRegistrationsAsync(string sessionToken, CancellationToken cancellationToken);
Task<CandidateEndpointResult> UpdateProfileAsync(
string sessionToken,
JsonObject body,
CancellationToken cancellationToken);
Task<CandidateEndpointResult> CreateRegistrationAsync(
string sessionToken,
JsonObject body,
CancellationToken cancellationToken);
}
Loaded 3 of 16 files, more files were not shown because too many files have changed in this diff. Show more