支持五级混编、稳定种子、容量预检、固定考点、逐科考场/座位及四种准考证号规则。

正式编排会事务性替换整场计划、准考证、座位和审计记录。
支持三级管理员范围读取、批量 HTML 准考证、准考证信息 Excel,以及校级考点桌贴、门贴和签名单。
核心实现见 [AdminArrangementPlanner.cs (line 7)](C:/Users/BI/Documents/EIS-dotnet/src/Eis.Infrastructure/Administration/AdminArrangementPlanner.cs:7)、[AdminArrangementService.cs (line 11)](C:/Users/BI/Documents/EIS-dotnet/src/Eis.Infrastructure/Administration/AdminArrangementService.cs:11) 和 [AdminArrangementExporter.cs (line 5)](C:/Users/BI/Documents/EIS-dotnet/src/Eis.Infrastructure/Administration/AdminArrangementExporter.cs:5)。
新增开关 ADMIN_NATIVE_ARRANGEMENTS_ENABLED=true,说明已更新到 [MIGRATION.md (line 57)](C:/Users/BI/Documents/EIS-dotnet/MIGRATION.md:57)。
Excel 由 .NET 直接生成,使用 ClosedXML 0.105.0;该库支持服务端生成 Excel 2007+ 工作簿。ClosedXML NuGet 页面
This commit is contained in:
2026-07-23 11:25:09 +08:00 Unverified
parent 25501651e1
commit 8f83adbc1e
18 changed files with 2138 additions and 11 deletions
@@ -139,6 +139,31 @@ public static class NativeAdminReadEndpoints
endpoints.MapPost("/api/admin/exams/{examId}/archive", (HttpContext context, string examId, IAdminExamManagementService service, CancellationToken cancellationToken) =>
Execute(context, service.ArchiveAsync(Token(context), examId, cancellationToken)));
}
if (options.NativeArrangementsEnabled)
{
endpoints.MapGet("/api/admin/admission-arrangements",
(HttpContext context, IAdminArrangementService service, CancellationToken cancellationToken) =>
Execute(context, service.GetAsync(Token(context), cancellationToken)));
endpoints.MapPost("/api/admin/exams/{examId}/admission-arrangement/preview",
(HttpContext context, string examId, JsonObject body, IAdminArrangementService service, CancellationToken cancellationToken) =>
Execute(context, service.PreviewAsync(Token(context), examId, body, cancellationToken)));
endpoints.MapPost("/api/admin/exams/{examId}/admission-arrangement",
(HttpContext context, string examId, JsonObject body, IAdminArrangementService service, CancellationToken cancellationToken) =>
Execute(context, service.ApplyAsync(Token(context), examId, body, cancellationToken)));
endpoints.MapGet("/api/admin/admission-exports/{type:regex(^(admit-cards|info|center-materials)$)}",
(HttpContext context, string type, IAdminArrangementService service, CancellationToken cancellationToken) =>
ExecuteDocument(
context,
service.ExportAsync(
Token(context),
type,
context.Request.Query["examId"].ToString(),
cancellationToken)));
endpoints.MapPost("/api/admin/registrations/{registrationId}/admit-card",
(HttpContext context, string registrationId, IAdminArrangementService service, CancellationToken cancellationToken) =>
Execute(context, service.RetiredSingleAsync(Token(context), cancellationToken)));
}
return endpoints;
}
@@ -150,6 +175,25 @@ public static class NativeAdminReadEndpoints
return Results.Json(result.Body, statusCode: result.StatusCode);
}
private static async Task<IResult> ExecuteDocument(
HttpContext context,
Task<AdminDocumentResult> operation)
{
var result = await operation;
context.Response.Headers.CacheControl = "no-store";
context.Response.Headers["X-EIS-Implementation"] = "aspnet-core";
if (result.Error is not null)
{
return Results.Json(result.Error, statusCode: result.StatusCode);
}
return Results.File(
result.Content ?? [],
result.ContentType ?? "application/octet-stream",
result.FileName,
enableRangeProcessing: false);
}
private static string Token(HttpContext context) =>
context.Request.Cookies.TryGetValue("hz_session", out var token) ? token : string.Empty;
}
+10 -1
View File
@@ -55,7 +55,8 @@ var adminMigrationOptions = AdminMigrationOptions.FromEnvironment(
builder.Configuration.GetValue<bool>("AdminMigration:NativeCandidateManagementEnabled"),
builder.Configuration.GetValue<bool>("AdminMigration:NativeRegistrationPaymentWritesEnabled"),
builder.Configuration.GetValue<bool>("AdminMigration:NativeWorkflowOperationsEnabled"),
builder.Configuration.GetValue<bool>("AdminMigration:NativeExamManagementEnabled"));
builder.Configuration.GetValue<bool>("AdminMigration:NativeExamManagementEnabled"),
builder.Configuration.GetValue<bool>("AdminMigration:NativeArrangementsEnabled"));
builder.Services.AddEisInfrastructure(
DatabaseOptions.FromEnvironment(applicationRoot, builder.Environment.IsProduction()),
DocumentVerificationOptions.FromEnvironment(builder.Environment.IsProduction()),
@@ -120,6 +121,7 @@ app.MapGet("/health/migration", async (LegacyApiProxy proxy, CancellationToken c
nativeRegistrationPaymentWritesEnabled = adminMigrationOptions.NativeRegistrationPaymentWritesEnabled,
nativeWorkflowOperationsEnabled = adminMigrationOptions.NativeWorkflowOperationsEnabled,
nativeExamManagementEnabled = adminMigrationOptions.NativeExamManagementEnabled,
nativeArrangementsEnabled = adminMigrationOptions.NativeArrangementsEnabled,
nativeRoutes = (adminMigrationOptions.NativeReadsEnabled
? new[] { "GET context", "GET dashboard", "GET schools", "GET school-organization", "GET admins", "GET exams" }
: [])
@@ -157,6 +159,13 @@ app.MapGet("/health/migration", async (LegacyApiProxy proxy, CancellationToken c
.Concat(adminMigrationOptions.NativeExamManagementEnabled
? new[] { "POST/PATCH exams", "POST exam archive" }
: [])
.Concat(adminMigrationOptions.NativeArrangementsEnabled
? new[]
{
"GET admission-arrangements", "POST arrangement preview/apply",
"GET admission exports"
}
: [])
.ToArray()
},
features = MigrationFeatureCatalog.Current(authenticationOptions.NativeEnabled, candidateMigrationOptions.NativeEnabled)
+2 -1
View File
@@ -20,7 +20,8 @@
"NativeCandidateManagementEnabled": false,
"NativeRegistrationPaymentWritesEnabled": false,
"NativeWorkflowOperationsEnabled": false,
"NativeExamManagementEnabled": false
"NativeExamManagementEnabled": false,
"NativeArrangementsEnabled": false
},
"Logging": {
"LogLevel": {