原生志愿、录取、计划余量、指标资格和通知查询:[CandidateService.Admissions.cs (line 29)](C:/Users/BI/Documents/EIS-dotnet/src/Eis.Infrastructure/Candidate/CandidateService.Admissions.cs:29)

志愿提交、资格校验、补录限制、次数上限与自动锁定:[CandidateService.Admissions.cs (line 152)](C:/Users/BI/Documents/EIS-dotnet/src/Eis.Infrastructure/Candidate/CandidateService.Admissions.cs:152)
通用招生记录读取及事务写入:[CandidateAdmissionRepository.cs (line 25)](C:/Users/BI/Documents/EIS-dotnet/src/Eis.Infrastructure/Candidate/CandidateAdmissionRepository.cs:25)
原生路由已接入:[NativeCandidateEndpoints.cs (line 70)](C:/Users/BI/Documents/EIS-dotnet/src/Eis.Web/Candidate/NativeCandidateEndpoints.cs:70)
迁移状态现在会把 Candidate 标记为原生:[MigrationFeatureCatalog.cs (line 12)](C:/Users/BI/Documents/EIS-dotnet/src/Eis.Infrastructure/Migration/MigrationFeatureCatalog.cs:12)
This commit is contained in:
biss committed 2026-07-22 20:22:46 +08:00
1 parent 4417d6d674
commit 4b0dae6d71
13 files changed
+859 -7

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_URL` 或 `REDIS_SESSION_URL`;两个运行时会复用相同逻辑库和 `exam-information:auth` 键前缀,从而共享登录会话。`GET /health/migration` 会报告 `authentication.nativeEnabled`、状态后端和跨运行时会话共享能力。
考生域核心端点(首页、通知、个人资料读取与提交、可报名考试、我的报名读取与提交、成绩与总分排名、成绩单防伪码、成绩复议、准考证下载)可通过以下开关原生运行;该开关必须与原生认证及共享 Redis 同时启用。志愿填报与录取查询仍会继续转发给 Node: 考生域全部端点(首页、通知、个人资料读取与提交、可报名考试、我的报名读取与提交、成绩与总分排名、成绩单防伪码、成绩复议、准考证下载、志愿填报与录取查询)可通过以下开关原生运行;该开关必须与原生认证及共享 Redis 同时启用:
```powershell ```powershell
$env:AUTH_NATIVE_ENABLED = 'true' $env:AUTH_NATIVE_ENABLED = 'true'
+75
View File
@@ -126,6 +126,28 @@ function Assert-CandidateResultsEquivalent {
} }
} }
function Assert-CandidateAdmissionsEquivalent {
param(
[Parameter(Mandatory)]
[string] $Expected,
[Parameter(Mandatory)]
[string] $Actual
)
$expectedNode = [System.Text.Json.Nodes.JsonNode]::Parse($Expected)
$actualNode = [System.Text.Json.Nodes.JsonNode]::Parse($Actual)
foreach ($node in @($expectedNode, $actualNode)) {
foreach ($admission in $node['admissions'].AsArray()) {
$admission.AsObject().Remove('noticeVerificationQr') | Out-Null
}
}
if (-not [System.Text.Json.Nodes.JsonNode]::DeepEquals($expectedNode, $actualNode)) {
$difference = Find-JsonDifference -Expected $expectedNode -Actual $actualNode -Path '$'
throw "Candidate route 'admissions' JSON payload differs from the legacy API at $difference"
}
}
function Find-JsonDifference { function Find-JsonDifference {
param( param(
[AllowNull()] [AllowNull()]
@@ -439,6 +461,58 @@ try {
if (@($nativeCandidateResults.summaries | Where-Object { $_.verificationQr -match '^data:image/png;base64,' }).Count -eq 0) { if (@($nativeCandidateResults.summaries | Where-Object { $_.verificationQr -match '^data:image/png;base64,' }).Count -eq 0) {
throw 'Native candidate results did not include a local PNG verification QR code' throw 'Native candidate results did not include a local PNG verification QR code'
} }
$legacyCandidateAdmissionsResponse = Invoke-WebRequest -Uri "$legacyBaseUrl/api/candidate/admissions" -WebSession $candidateSession
$nativeCandidateAdmissionsResponse = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/candidate/admissions" -WebSession $nativeCandidateSession
if ($nativeCandidateAdmissionsResponse.Headers['X-EIS-Implementation'] -ne 'aspnet-core') {
throw "Candidate route 'admissions' did not use the native ASP.NET Core endpoint"
}
Assert-CandidateAdmissionsEquivalent -Expected $legacyCandidateAdmissionsResponse.Content -Actual $nativeCandidateAdmissionsResponse.Content
$admissionWriteProcess = Start-TestProcess -FileName $nodeExecutable -ArgumentList @(
'tests/helpers/prepare-candidate-admission-write-smoke.mjs', $smokeDatabasePath
) -Environment $nodeEnvironment
if (-not $admissionWriteProcess.WaitForExit(10000)) {
$admissionWriteProcess.Kill($true)
throw 'Timed out while preparing the candidate admission preference smoke data'
}
$admissionWriteError = $admissionWriteProcess.StandardError.ReadToEnd()
if ($admissionWriteProcess.ExitCode -ne 0) {
throw "Could not prepare the candidate admission preference smoke data`n$admissionWriteError"
}
$admissionWriteProcess.Dispose()
$admissionContext = Invoke-RestMethod -Uri "$nativeAuthBaseUrl/api/candidate/admissions" -WebSession $nativeCandidateSession
$admissionSetting = @($admissionContext.admissions | Where-Object { $_.status -eq 'filling' } | Select-Object -First 1)[0]
$admissionPlan = @($admissionSetting.plans | Select-Object -First 1)[0]
$admissionCategory = @($admissionPlan.categories | Where-Object { $_.preferenceTypes -contains 'general' } | Select-Object -First 1)[0]
if ($null -eq $admissionSetting -or $null -eq $admissionPlan -or $null -eq $admissionCategory) {
throw 'Native candidate admissions did not expose an eligible plan for preference submission'
}
$emptyPreference = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/candidate/admissions/$($admissionSetting.examId)/preferences" -Method Put -ContentType 'application/json' -Body '{"choices":[]}' -WebSession $nativeCandidateSession -SkipHttpErrorCheck
if ($emptyPreference.StatusCode -ne 400) {
throw 'Native admission preference API accepted an empty preference list'
}
$preferenceBody = @{
choices = @(@{
schoolId = $admissionPlan.schoolId
categoryCode = $admissionCategory.code
preferenceType = 'general'
})
} | ConvertTo-Json -Depth 5 -Compress
$firstPreferenceResponse = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/candidate/admissions/$($admissionSetting.examId)/preferences" -Method Put -ContentType 'application/json' -Body $preferenceBody -WebSession $nativeCandidateSession
$firstPreference = $firstPreferenceResponse.Content | ConvertFrom-Json
if ($firstPreferenceResponse.Headers['X-EIS-Implementation'] -ne 'aspnet-core' -or $firstPreference.remainingSubmissions -ne 1 -or $firstPreference.locked -ne $false) {
throw 'Native admission preference API did not persist the first submission count'
}
$secondPreference = Invoke-RestMethod -Uri "$nativeAuthBaseUrl/api/candidate/admissions/$($admissionSetting.examId)/preferences" -Method Put -ContentType 'application/json' -Body $preferenceBody -WebSession $nativeCandidateSession
if ($secondPreference.remainingSubmissions -ne 0 -or $secondPreference.locked -ne $true) {
throw 'Native admission preference API did not lock at the configured submission limit'
}
$lockedPreference = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/candidate/admissions/$($admissionSetting.examId)/preferences" -Method Put -ContentType 'application/json' -Body $preferenceBody -WebSession $nativeCandidateSession -SkipHttpErrorCheck
if ($lockedPreference.StatusCode -ne 409) {
throw 'Native admission preference API accepted a submission after automatic locking'
}
$legacyAdmissionsAfterWrite = Invoke-WebRequest -Uri "$legacyBaseUrl/api/candidate/admissions" -WebSession $candidateSession
$nativeAdmissionsAfterWrite = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/candidate/admissions" -WebSession $nativeCandidateSession
Assert-CandidateAdmissionsEquivalent -Expected $legacyAdmissionsAfterWrite.Content -Actual $nativeAdmissionsAfterWrite.Content
$appealableResult = @($nativeCandidateResults.results | Where-Object { $null -eq $_.appeal } | Select-Object -First 1)[0] $appealableResult = @($nativeCandidateResults.results | Where-Object { $null -eq $_.appeal } | Select-Object -First 1)[0]
if ($null -ne $appealableResult) { if ($null -ne $appealableResult) {
$shortAppeal = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/candidate/results/$($appealableResult.id)/appeals" -Method Post -ContentType 'application/json' -Body '{"reason":"短"}' -WebSession $nativeCandidateSession -SkipHttpErrorCheck $shortAppeal = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/candidate/results/$($appealableResult.id)/appeals" -Method Post -ContentType 'application/json' -Body '{"reason":"短"}' -WebSession $nativeCandidateSession -SkipHttpErrorCheck
@@ -699,6 +773,7 @@ try {
NativeCandidateReads = 'passed' NativeCandidateReads = 'passed'
NativeCandidateWrites = 'passed' NativeCandidateWrites = 'passed'
NativeCandidateDocuments = 'passed' NativeCandidateDocuments = 'passed'
NativeCandidateAdmissions = 'passed'
} | Format-List } | Format-List
} }
finally { finally {
@@ -27,6 +27,11 @@ public interface ICandidateService
string verificationBaseUrl, string verificationBaseUrl,
CancellationToken cancellationToken); CancellationToken cancellationToken);
Task<CandidateEndpointResult> GetAdmissionsAsync(
string sessionToken,
string verificationBaseUrl,
CancellationToken cancellationToken);
Task<CandidateEndpointResult> UpdateProfileAsync( Task<CandidateEndpointResult> UpdateProfileAsync(
string sessionToken, string sessionToken,
JsonObject body, JsonObject body,
@@ -47,4 +52,10 @@ public interface ICandidateService
string sessionToken, string sessionToken,
string registrationId, string registrationId,
CancellationToken cancellationToken); CancellationToken cancellationToken);
Task<CandidateEndpointResult> UpdateAdmissionPreferencesAsync(
string sessionToken,
string examId,
JsonObject body,
CancellationToken cancellationToken);
} }
Loaded 3 of 13 files, more files were not shown because too many files have changed in this diff. Show more