本阶段迁移已完成。

Excel:班级、账号、考生、缴费、考场、成绩、志愿、录取、报到及正式录取名册均改为 ClosedXML 原生处理,支持模板、校验、筛选和导入预检。[SystemWorkbook.cs](C:\\Users\\BI\\Documents\\EIS-dotnet\\src\\Eis.Infrastructure\\Spreadsheets\\SystemWorkbook.cs)
文书:录取通知书模板、成绩单/准考证/通知书业务数据与防伪验真已脱离 Node;保留现有前端排版导出效果。[AdminAdmissionService.Documents.cs](C:\\Users\\BI\\Documents\\EIS-dotnet\\src\\Eis.Infrastructure\\Administration\\AdminAdmissionService.Documents.cs)
缓存:完成 Redis 命名空间版本缓存、有界本地回退、请求合并和并发失效保护;公开数据及成绩写入后立即失效。[ApplicationCache.cs](C:\\Users\\BI\\Documents\\EIS-dotnet\\src\\Eis.Infrastructure\\Caching\\ApplicationCache.cs)
迁移清单已更新为完成。[MIGRATION.md](C:\\Users\\BI\\Documents\\EIS-dotnet\\MIGRATION.md)
This commit is contained in:
2026-07-23 14:14:51 +08:00 Unverified
parent 4df6303153
commit fea91a19af
24 changed files with 2209 additions and 35 deletions
@@ -18,6 +18,28 @@ public static class NativeAdminReadEndpoints
Execute(context, service.GetDashboardAsync(Token(context), cancellationToken)));
endpoints.MapGet("/api/admin/schools", (HttpContext context, IAdminReadService service, CancellationToken cancellationToken) =>
Execute(context, service.GetSchoolsAsync(Token(context), cancellationToken)));
endpoints.MapGet(
"/api/admin/excel/{resource:regex(^(classes|class_admins|account_quotas|account_results|candidates|payments|centers|results)$)}",
(HttpContext context, string resource, IAdminExcelService service, CancellationToken cancellationToken) =>
ExecuteDocument(
context,
service.ExportAsync(
Token(context),
resource,
context.Request.Query.ContainsKey("template"),
context.Request.Query["examId"].ToString(),
context.Request.Query["batchId"].ToString(),
cancellationToken)));
endpoints.MapPost(
"/api/admin/excel/{resource:regex(^(classes|class_admins|account_quotas|candidates|centers|results)$)}",
async (HttpContext context, string resource, IAdminExcelService service, CancellationToken cancellationToken) =>
await Execute(
context,
service.ImportAsync(
Token(context),
resource,
await ReadWorkbookAsync(context.Request, cancellationToken),
cancellationToken)));
endpoints.MapGet("/api/admin/school-organization", (HttpContext context, IAdminReadService service, CancellationToken cancellationToken) =>
Execute(context, service.GetSchoolOrganizationAsync(Token(context), cancellationToken)));
endpoints.MapGet("/api/admin/admins", (HttpContext context, IAdminReadService service, CancellationToken cancellationToken) =>
@@ -196,21 +218,63 @@ public static class NativeAdminReadEndpoints
endpoints.MapPost("/api/admin/results/cache/refresh",
(HttpContext context, IAdminResultService service, CancellationToken cancellationToken) =>
Execute(context, service.RefreshCacheAsync(Token(context), cancellationToken)));
endpoints.MapGet("/api/admin/excel/results",
(HttpContext context, IAdminResultService service, CancellationToken cancellationToken) =>
ExecuteDocument(
context,
service.ExportAsync(
Token(context),
context.Request.Query["examId"].ToString(),
cancellationToken)));
}
if (options.NativeAdmissionsEnabled)
{
endpoints.MapGet("/api/admission/notice-template",
(HttpContext context, IAdminAdmissionService service, CancellationToken cancellationToken) =>
Execute(context, service.GetAdmissionSchoolNoticeTemplateAsync(Token(context), cancellationToken)));
endpoints.MapPut("/api/admission/notice-template",
(HttpContext context, JsonObject body, IAdminAdmissionService service, CancellationToken cancellationToken) =>
Execute(context, service.SaveAdmissionSchoolNoticeTemplateAsync(Token(context), body, cancellationToken)));
endpoints.MapGet("/api/admission/reporting",
(HttpContext context, IAdminAdmissionService service, CancellationToken cancellationToken) =>
Execute(context, service.GetAdmissionSchoolReportingAsync(Token(context), cancellationToken)));
endpoints.MapGet("/api/admission/reporting/export",
(HttpContext context, IAdminAdmissionService service, CancellationToken cancellationToken) =>
ExecuteDocument(
context,
service.ExportAdmissionSchoolReportingAsync(
Token(context),
context.Request.Query["examId"].ToString(),
cancellationToken)));
endpoints.MapPost("/api/admission/reporting/import",
async (HttpContext context, IAdminAdmissionService service, CancellationToken cancellationToken) =>
await Execute(
context,
service.ImportAdmissionSchoolReportingAsync(
Token(context),
context.Request.Query["examId"].ToString(),
await ReadWorkbookAsync(context.Request, cancellationToken),
cancellationToken)));
endpoints.MapGet("/api/admission/placements",
(HttpContext context, IAdminAdmissionService service, CancellationToken cancellationToken) =>
Execute(context, service.GetAdmissionSchoolPlacementsAsync(Token(context), cancellationToken)));
endpoints.MapGet("/api/admission/placements/export",
(HttpContext context, IAdminAdmissionService service, CancellationToken cancellationToken) =>
ExecuteDocument(
context,
service.ExportAdmissionSchoolPlacementsAsync(
Token(context),
context.Request.Query["examId"].ToString(),
cancellationToken)));
endpoints.MapGet("/api/admin/admissions",
(HttpContext context, IAdminAdmissionService service, CancellationToken cancellationToken) =>
Execute(context, service.GetAsync(Token(context), cancellationToken)));
endpoints.MapGet(
"/api/admin/admissions/{kind:regex(^(preferences|placements)$)}/export",
(HttpContext context, string kind, IAdminAdmissionService service, CancellationToken cancellationToken) =>
ExecuteDocument(
context,
service.ExportAdminLedgerAsync(
Token(context),
kind,
context.Request.Query.ToDictionary(
item => item.Key,
item => item.Value.ToString(),
StringComparer.Ordinal),
cancellationToken)));
endpoints.MapGet("/api/admin/indicator-qualifications",
(HttpContext context, IAdminAdmissionService service, CancellationToken cancellationToken) =>
Execute(context, service.GetQualificationsAsync(Token(context), cancellationToken)));
@@ -287,6 +351,19 @@ public static class NativeAdminReadEndpoints
private static string Token(HttpContext context) =>
context.Request.Cookies.TryGetValue("hz_session", out var token) ? token : string.Empty;
private static async Task<byte[]> ReadWorkbookAsync(HttpRequest request, CancellationToken cancellationToken)
{
const int maximumBytes = 20 * 1024 * 1024;
if (request.ContentLength is > maximumBytes)
{
return [];
}
await using var buffer = new MemoryStream();
await request.Body.CopyToAsync(buffer, cancellationToken);
return buffer.Length <= maximumBytes ? buffer.ToArray() : [];
}
private static IResult NativeDeleteNotAllowed(HttpContext context)
{
context.Response.Headers.CacheControl = "no-store";
+15 -2
View File
@@ -2,6 +2,7 @@ using System.Net;
using Eis.Infrastructure.Authentication;
using Eis.Infrastructure.Administration;
using Eis.Infrastructure.Candidate;
using Eis.Infrastructure.Caching;
using Eis.Application.Public;
using Eis.Infrastructure;
using Eis.Infrastructure.Data;
@@ -85,7 +86,10 @@ app.MapGet("/health/live", () => Results.Json(new
framework = ".NET 10"
}));
app.MapGet("/health/migration", async (LegacyApiProxy proxy, CancellationToken cancellationToken) =>
app.MapGet("/health/migration", async (
LegacyApiProxy proxy,
IApplicationCache cache,
CancellationToken cancellationToken) =>
{
var legacyAvailable = await proxy.IsAvailableAsync(cancellationToken);
var statusCode = legacyAvailable ? StatusCodes.Status200OK : StatusCodes.Status503ServiceUnavailable;
@@ -93,6 +97,13 @@ app.MapGet("/health/migration", async (LegacyApiProxy proxy, CancellationToken c
{
status = legacyAvailable ? "healthy" : "degraded",
legacyApiAvailable = legacyAvailable,
cache = new
{
status = cache.Status,
redisEnabled = cache.Enabled,
localFallback = true,
namespaces = new[] { "public", "results" }
},
authentication = new
{
nativeEnabled = authenticationOptions.NativeEnabled,
@@ -183,7 +194,9 @@ app.MapGet("/health/migration", async (LegacyApiProxy proxy, CancellationToken c
"GET admissions/indicator qualifications", "PUT qualification single/bulk",
"POST/PATCH/reset admission-school accounts", "PUT admission settings",
"POST/PATCH admission plans", "POST admission match/finalize/supplementary",
"PATCH admission reporting/withdrawals"
"PATCH admission reporting/withdrawals",
"GET/POST admin Excel", "GET/PUT admission notice template",
"GET/POST admission reporting Excel", "GET admission placement Excel"
}
: [])
.ToArray()