本轮迁移已完成,生产运行链路现已切换为纯 ASP.NET Core 10。

主要完成:
补齐招生学校计划、投档审核、报到、扫码、补录等原生接口。
新增 SQLite/MySQL 空库初始化及默认审批流、号码规则。
删除 Node 兼容代理,未知 API 直接返回原生 404。
Docker、Compose、Gitea CI 全部切换到 Eis.Web.dll。
CKEditor 已固化到 Web 发布资源,不再依赖 node_modules。
新增纯 .NET 冒烟脚本:[smoke-dotnet-native.ps1 (line 1)](C:/Users/BI/Documents/EIS-dotnet/scripts/smoke-dotnet-native.ps1:1)。
迁移状态已更新:[MIGRATION.md (line 14)](C:/Users/BI/Documents/EIS-dotnet/MIGRATION.md:14)。
容器入口见 [Dockerfile (line 31)](C:/Users/BI/Documents/EIS-dotnet/Dockerfile:31)。
This commit is contained in:
2026-07-23 15:14:42 +08:00 Unverified
parent fea91a19af
commit e3c04dad8a
36 changed files with 1449 additions and 491 deletions
+17 -1
View File
@@ -1134,13 +1134,29 @@ try {
}
}
$admissionSchoolSession = [Microsoft.PowerShell.Commands.WebRequestSession]::new()
$legacyAdmissionSchoolSession = [Microsoft.PowerShell.Commands.WebRequestSession]::new()
$admissionSchoolLoginBody = @{ username = 'admission_1_admin'; password = '12345678' } | ConvertTo-Json -Compress
Invoke-RestMethod -Uri "$nativeAuthBaseUrl/api/auth/login" -Method Post -ContentType 'application/json' -Body $admissionSchoolLoginBody -WebSession $admissionSchoolSession | Out-Null
foreach ($admissionSchoolRoute in @('notice-template', 'reporting', 'placements')) {
Invoke-RestMethod -Uri "$legacyBaseUrl/api/auth/login" -Method Post -ContentType 'application/json' -Body $admissionSchoolLoginBody -WebSession $legacyAdmissionSchoolSession | Out-Null
foreach ($admissionSchoolRoute in @('context', 'plans', 'notice-template', 'reporting', 'placements')) {
$legacyAdmissionSchoolResponse = Invoke-WebRequest -Uri "$legacyBaseUrl/api/admission/$admissionSchoolRoute" -WebSession $legacyAdmissionSchoolSession
$admissionSchoolResponse = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admission/$admissionSchoolRoute" -WebSession $admissionSchoolSession
if ($admissionSchoolResponse.Headers['X-EIS-Implementation'] -ne 'aspnet-core') {
throw "Native admission-school route '$admissionSchoolRoute' did not use ASP.NET Core"
}
Assert-JsonEquivalent -Expected $legacyAdmissionSchoolResponse.Content -Actual $admissionSchoolResponse.Content -Label "Admission-school route '$admissionSchoolRoute'"
}
$invalidAdmissionSchoolPlan = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admission/plans" -Method Post -ContentType 'application/json' -Body '{"examId":"missing","categories":[]}' -WebSession $admissionSchoolSession -SkipHttpErrorCheck
if ($invalidAdmissionSchoolPlan.StatusCode -ne 404 -or $invalidAdmissionSchoolPlan.Headers['X-EIS-Implementation'] -ne 'aspnet-core') {
throw 'Native admission-school plan endpoint did not reject a missing exam'
}
$emptyAdmissionPlacementBulk = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admission/placements/bulk" -Method Post -ContentType 'application/json' -Body '{"ids":[],"decision":"accept"}' -WebSession $admissionSchoolSession -SkipHttpErrorCheck
if ($emptyAdmissionPlacementBulk.StatusCode -ne 400 -or $emptyAdmissionPlacementBulk.Headers['X-EIS-Implementation'] -ne 'aspnet-core') {
throw 'Native admission-school placement bulk endpoint accepted an empty selection'
}
$invalidAdmissionScan = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admission/reporting/scan-preview" -Method Post -ContentType 'application/json' -Body '{"code":"invalid"}' -WebSession $admissionSchoolSession -SkipHttpErrorCheck
if ($invalidAdmissionScan.StatusCode -ne 400 -or $invalidAdmissionScan.Headers['X-EIS-Implementation'] -ne 'aspnet-core') {
throw 'Native admission-school scan preview accepted an invalid verification code'
}
$legacyResultsAfterWrites = Invoke-WebRequest -Uri "$legacyBaseUrl$resultsUri" -WebSession $session
$nativeResultsAfterWrites = Invoke-WebRequest -Uri "$nativeAuthBaseUrl$resultsUri" -WebSession $nativeSession
+140
View File
@@ -0,0 +1,140 @@
[CmdletBinding()]
param(
[string]$PublishDirectory = (Join-Path $PSScriptRoot '..\artifacts\native-smoke-publish')
)
$ErrorActionPreference = 'Stop'
$repositoryRoot = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..')).Path
$publishPath = [IO.Path]::GetFullPath(
$(if ([IO.Path]::IsPathRooted($PublishDirectory)) {
$PublishDirectory
}
else {
Join-Path $repositoryRoot $PublishDirectory
}))
dotnet publish (Join-Path $repositoryRoot 'src\Eis.Web\Eis.Web.csproj') `
--configuration Release `
--output $publishPath `
--no-restore
if ($LASTEXITCODE -ne 0) {
throw 'ASP.NET Core 发布失败。'
}
$tempBase = [IO.Path]::GetFullPath([IO.Path]::GetTempPath())
$testRoot = Join-Path $tempBase ('eis-native-smoke-' + [Guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $testRoot | Out-Null
$listener = [Net.Sockets.TcpListener]::new([Net.IPAddress]::Loopback, 0)
$listener.Start()
$port = ([Net.IPEndPoint]$listener.LocalEndpoint).Port
$listener.Stop()
$baseUrl = "http://127.0.0.1:$port"
$stdoutPath = Join-Path $testRoot 'stdout.log'
$stderrPath = Join-Path $testRoot 'stderr.log'
$databasePath = Join-Path $testRoot 'eis.sqlite'
$process = $null
try {
$process = Start-Process `
-FilePath 'dotnet' `
-ArgumentList (Join-Path $publishPath 'Eis.Web.dll') `
-WorkingDirectory $publishPath `
-Environment @{
ASPNETCORE_ENVIRONMENT = 'Development'
ASPNETCORE_URLS = $baseUrl
DATABASE_CLIENT = 'sqlite'
SQLITE_PATH = $databasePath
INITIAL_ADMIN_USERNAME = 'admin'
INITIAL_ADMIN_PASSWORD = 'NativeSmoke123456'
INITIAL_ADMIN_DISPLAY_NAME = '原生冒烟管理员'
REDIS_URL = ''
REDIS_SESSION_URL = ''
} `
-RedirectStandardOutput $stdoutPath `
-RedirectStandardError $stderrPath `
-WindowStyle Hidden `
-PassThru
$ready = $false
for ($attempt = 0; $attempt -lt 60; $attempt++) {
if ($process.HasExited) {
break
}
try {
$live = Invoke-RestMethod -Uri "$baseUrl/health/live" -TimeoutSec 2
$ready = $true
break
}
catch {
Start-Sleep -Milliseconds 250
}
}
if (-not $ready) {
$stdout = Get-Content -Raw $stdoutPath -ErrorAction SilentlyContinue
$stderr = Get-Content -Raw $stderrPath -ErrorAction SilentlyContinue
throw "ASP.NET Core 发布产物未就绪。`nstdout: $stdout`nstderr: $stderr"
}
$migration = Invoke-RestMethod -Uri "$baseUrl/health/migration" -TimeoutSec 5
$homeResponse = Invoke-WebRequest -Uri "$baseUrl/" -TimeoutSec 5
$ckeditor = Invoke-WebRequest -Uri "$baseUrl/vendor/ckeditor5/ckeditor5.js" -TimeoutSec 10
$session = [Microsoft.PowerShell.Commands.WebRequestSession]::new()
$loginBody = @{
username = 'admin'
password = 'NativeSmoke123456'
} | ConvertTo-Json -Compress
$login = Invoke-RestMethod `
-Uri "$baseUrl/api/auth/login" `
-Method Post `
-ContentType 'application/json' `
-Body $loginBody `
-WebSession $session `
-TimeoutSec 5
$currentUser = Invoke-RestMethod `
-Uri "$baseUrl/api/auth/me" `
-WebSession $session `
-TimeoutSec 5
$missing = Invoke-WebRequest `
-Uri "$baseUrl/api/removed-node-route" `
-SkipHttpErrorCheck `
-TimeoutSec 5
if ($live.status -ne 'healthy' -or
$migration.legacyApiRemoved -ne $true -or
$homeResponse.StatusCode -ne 200 -or
$ckeditor.RawContentLength -lt 100000 -or
$login.user.role -ne 'admin' -or
$login.user.adminLevel -ne 'super' -or
$currentUser.user.username -ne 'admin' -or
$missing.StatusCode -ne 404) {
throw '原生宿主冒烟断言失败。'
}
[pscustomobject]@{
Process = $process.ProcessName
Live = $live.status
LegacyRemoved = $migration.legacyApiRemoved
HomeStatus = $homeResponse.StatusCode
CkeditorBytes = $ckeditor.RawContentLength
LoginRole = $login.user.role
CurrentUser = $currentUser.user.username
UnknownApiStatus = $missing.StatusCode
DatabaseTarget = $databasePath
} | Format-List
}
finally {
if ($null -ne $process -and -not $process.HasExited) {
Stop-Process -Id $process.Id -Force
$process.WaitForExit()
}
$resolvedTestRoot = [IO.Path]::GetFullPath($testRoot)
if ($resolvedTestRoot.StartsWith($tempBase, [StringComparison]::OrdinalIgnoreCase) -and
(Test-Path -LiteralPath $resolvedTestRoot)) {
Remove-Item -LiteralPath $resolvedTestRoot -Recurse -Force
}
}