Files
EIS-dotnet/src/Eis.Web/Administration/NativeAdminReadEndpoints.cs
T
biss 4df6303153 已完成本轮迁移:管理后台、审批流和考务编排的业务 API 已全部由 ASP.NET Core 10 原生处理。
主要完成:
管理端招生工作台、指标资格、招生学校账号。
招生计划审核、分数优先多志愿投档、退档监督。
逐轮录取公示、报到审批、补录轮次推进、最低录取线公示。
成绩预览导入与管理员账号禁止物理删除。
新增迁移开关及健康状态:[MIGRATION.md (line 57)](C:\\Users\\BI\\Documents\\EIS-dotnet\\MIGRATION.md:57)
核心实现:[AdminAdmissionService.cs (line 10)](C:\\Users\\BI\\Documents\\EIS-dotnet\\src\\Eis.Infrastructure\\Administration\\AdminAdmissionService.cs:10)
原生路由:[NativeAdminReadEndpoints.cs (line 209)](C:\\Users\\BI\\Documents\\EIS-dotnet\\src\\Eis.Web\\Administration\\NativeAdminReadEndpoints.cs:209)
2026-07-23 13:08:17 +08:00

299 lines
24 KiB
C#

using Eis.Application.Administration;
using Eis.Infrastructure.Administration;
using System.Text.Json.Nodes;
namespace Eis.Web.Administration;
public static class NativeAdminReadEndpoints
{
public static IEndpointRouteBuilder MapNativeAdminReadEndpoints(
this IEndpointRouteBuilder endpoints,
AdminMigrationOptions options)
{
if (!options.NativeReadsEnabled) return endpoints;
endpoints.MapGet("/api/admin/context", (HttpContext context, IAdminReadService service, CancellationToken cancellationToken) =>
Execute(context, service.GetContextAsync(Token(context), cancellationToken)));
endpoints.MapGet("/api/admin/dashboard", (HttpContext context, IAdminReadService service, CancellationToken cancellationToken) =>
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/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) =>
Execute(context, service.GetAdminsAsync(Token(context), cancellationToken)));
endpoints.MapGet("/api/admin/exams", (HttpContext context, IAdminReadService service, CancellationToken cancellationToken) =>
Execute(context, service.GetExamsAsync(Token(context), cancellationToken)));
if (options.NativeOrganizationWritesEnabled)
{
endpoints.MapPost("/api/admin/schools", (HttpContext context, JsonObject body, IAdminOrganizationService service, CancellationToken cancellationToken) =>
Execute(context, service.CreateSchoolAsync(Token(context), body, cancellationToken)));
endpoints.MapPatch("/api/admin/schools/{schoolId}", (HttpContext context, string schoolId, JsonObject body, IAdminOrganizationService service, CancellationToken cancellationToken) =>
Execute(context, service.UpdateSchoolAsync(Token(context), schoolId, body, cancellationToken)));
endpoints.MapPost("/api/admin/classes", (HttpContext context, JsonObject body, IAdminOrganizationService service, CancellationToken cancellationToken) =>
Execute(context, service.CreateClassAsync(Token(context), body, cancellationToken)));
endpoints.MapPatch("/api/admin/classes/{classId}", (HttpContext context, string classId, JsonObject body, IAdminOrganizationService service, CancellationToken cancellationToken) =>
Execute(context, service.UpdateClassAsync(Token(context), classId, body, cancellationToken)));
endpoints.MapPost("/api/admin/admins", (HttpContext context, JsonObject body, IAdminOrganizationService service, CancellationToken cancellationToken) =>
Execute(context, service.CreateAdminAsync(Token(context), body, cancellationToken)));
endpoints.MapPatch("/api/admin/admins/{adminId}", (HttpContext context, string adminId, JsonObject body, IAdminOrganizationService service, CancellationToken cancellationToken) =>
Execute(context, service.UpdateAdminAsync(Token(context), adminId, body, cancellationToken)));
endpoints.MapPost("/api/admin/admins/{adminId}/reset-password", (HttpContext context, string adminId, IAdminOrganizationService service, CancellationToken cancellationToken) =>
Execute(context, service.ResetAdminPasswordAsync(Token(context), adminId, cancellationToken)));
endpoints.MapPut("/api/admin/settings/self-registration", (HttpContext context, JsonObject body, IAdminOrganizationService service, CancellationToken cancellationToken) =>
Execute(context, service.UpdateSelfRegistrationAsync(Token(context), body, cancellationToken)));
}
if (options.NativeAccountBatchesEnabled)
{
endpoints.MapGet("/api/admin/candidate-account-batches", (HttpContext context, IAdminAccountBatchService service, CancellationToken cancellationToken) =>
Execute(context, service.GetAsync(Token(context), cancellationToken)));
endpoints.MapPost("/api/admin/candidate-account-batches", (HttpContext context, JsonObject body, IAdminAccountBatchService service, CancellationToken cancellationToken) =>
Execute(context, service.SubmitAsync(Token(context), body, cancellationToken)));
endpoints.MapPatch("/api/admin/candidate-account-batches/{batchId}", (HttpContext context, string batchId, JsonObject body, IAdminAccountBatchService service, CancellationToken cancellationToken) =>
Execute(context, service.ReviewAsync(Token(context), batchId, body, cancellationToken)));
}
if (options.NativeConfigurationEnabled)
{
endpoints.MapGet("/api/admin/number-rules", (HttpContext context, IAdminConfigurationService service, CancellationToken cancellationToken) =>
Execute(context, service.GetNumberRulesAsync(Token(context), cancellationToken)));
endpoints.MapPost("/api/admin/number-rules", (HttpContext context, JsonObject body, IAdminConfigurationService service, CancellationToken cancellationToken) =>
Execute(context, service.SaveNumberRuleAsync(Token(context), body, cancellationToken)));
endpoints.MapGet("/api/admin/workflows", (HttpContext context, IAdminConfigurationService service, CancellationToken cancellationToken) =>
Execute(context, service.GetWorkflowsAsync(Token(context), cancellationToken)));
endpoints.MapPut("/api/admin/workflows/{businessType}", (HttpContext context, string businessType, JsonObject body, IAdminConfigurationService service, CancellationToken cancellationToken) =>
Execute(context, service.SaveWorkflowAsync(Token(context), businessType, body, cancellationToken)));
}
if (options.NativeNoticeManagementEnabled)
{
endpoints.MapGet("/api/admin/notices", (HttpContext context, IAdminNoticeService service, CancellationToken cancellationToken) =>
Execute(context, service.ListAsync(Token(context), cancellationToken)));
endpoints.MapPost("/api/admin/notices", (HttpContext context, JsonObject body, IAdminNoticeService service, CancellationToken cancellationToken) =>
Execute(context, service.CreateAsync(Token(context), body, cancellationToken)));
endpoints.MapPatch("/api/admin/notices/{noticeId}", (HttpContext context, string noticeId, JsonObject body, IAdminNoticeService service, CancellationToken cancellationToken) =>
Execute(context, service.UpdateAsync(Token(context), noticeId, body, cancellationToken)));
endpoints.MapPatch("/api/admin/publications/{sourceType:regex(^(plan|qualification|admission|cutoff|reporting)$)}/{publicationId}",
(HttpContext context, string sourceType, string publicationId, JsonObject body, IAdminNoticeService service, CancellationToken cancellationToken) =>
Execute(context, service.UpdatePublicationVisibilityAsync(Token(context), sourceType, publicationId, body, cancellationToken)));
}
if (options.NativeCentersEnabled)
{
endpoints.MapGet("/api/admin/centers", (HttpContext context, IAdminCenterService service, CancellationToken cancellationToken) =>
Execute(context, service.GetAsync(Token(context), cancellationToken)));
endpoints.MapPost("/api/admin/centers", (HttpContext context, JsonObject body, IAdminCenterService service, CancellationToken cancellationToken) =>
Execute(context, service.CreateAsync(Token(context), body, cancellationToken)));
endpoints.MapPatch("/api/admin/centers/{centerId}", (HttpContext context, string centerId, JsonObject body, IAdminCenterService service, CancellationToken cancellationToken) =>
Execute(context, service.UpdateAsync(Token(context), centerId, body, cancellationToken)));
endpoints.MapPatch("/api/admin/center-change-requests/{changeRequestId}", (HttpContext context, string changeRequestId, JsonObject body, IAdminCenterService service, CancellationToken cancellationToken) =>
Execute(context, service.ReviewAsync(Token(context), changeRequestId, body, cancellationToken)));
}
if (options.NativeOperationalReadsEnabled)
{
endpoints.MapGet("/api/admin/candidates", (HttpContext context, IAdminOperationalReadService service, CancellationToken cancellationToken) =>
Execute(context, service.GetCandidatesAsync(Token(context), cancellationToken)));
endpoints.MapGet("/api/admin/registrations", (HttpContext context, IAdminOperationalReadService service, CancellationToken cancellationToken) =>
Execute(context, service.GetRegistrationsAsync(Token(context), cancellationToken)));
endpoints.MapGet("/api/admin/payments", (HttpContext context, IAdminOperationalReadService service, CancellationToken cancellationToken) =>
Execute(context, service.GetPaymentsAsync(Token(context), cancellationToken)));
}
if (options.NativeCandidateManagementEnabled)
{
endpoints.MapPost("/api/admin/candidate-accounts/archive", (HttpContext context, JsonObject body, IAdminCandidateManagementService service, CancellationToken cancellationToken) =>
Execute(context, service.ArchiveAccountsAsync(Token(context), body, cancellationToken)));
endpoints.MapPost("/api/admin/candidates/{profileId}/reset-password", (HttpContext context, string profileId, IAdminCandidateManagementService service, CancellationToken cancellationToken) =>
Execute(context, service.ResetPasswordAsync(Token(context), profileId, cancellationToken)));
endpoints.MapPatch("/api/admin/candidates/{profileId}", (HttpContext context, string profileId, JsonObject body, IAdminCandidateManagementService service, CancellationToken cancellationToken) =>
Execute(context, service.ReviewProfileAsync(Token(context), profileId, body, cancellationToken)));
}
if (options.NativeRegistrationPaymentWritesEnabled)
{
endpoints.MapPatch("/api/admin/registrations/{registrationId}", (HttpContext context, string registrationId, JsonObject body, IAdminRegistrationManagementService service, CancellationToken cancellationToken) =>
Execute(context, service.ReviewAsync(Token(context), registrationId, body, cancellationToken)));
endpoints.MapPatch("/api/admin/payments/{registrationId}", (HttpContext context, string registrationId, JsonObject body, IAdminRegistrationManagementService service, CancellationToken cancellationToken) =>
Execute(context, service.UpdatePaymentAsync(Token(context), registrationId, body, cancellationToken)));
}
if (options.NativeWorkflowOperationsEnabled)
{
endpoints.MapGet("/api/admin/workflow-instances", (HttpContext context, IAdminWorkflowService service, CancellationToken cancellationToken) =>
Execute(context, service.GetAsync(Token(context), cancellationToken)));
endpoints.MapPatch("/api/admin/workflow-instances/{instanceId}/transfer", (HttpContext context, string instanceId, JsonObject body, IAdminWorkflowService service, CancellationToken cancellationToken) =>
Execute(context, service.TransferAsync(Token(context), instanceId, body, cancellationToken)));
endpoints.MapPatch("/api/admin/workflow-instances/{instanceId}/supervise", (HttpContext context, string instanceId, JsonObject body, IAdminWorkflowService service, CancellationToken cancellationToken) =>
Execute(context, service.SuperviseAsync(Token(context), instanceId, body, cancellationToken)));
}
if (options.NativeExamManagementEnabled)
{
endpoints.MapPost("/api/admin/exams", (HttpContext context, JsonObject body, IAdminExamManagementService service, CancellationToken cancellationToken) =>
Execute(context, service.CreateAsync(Token(context), body, cancellationToken)));
endpoints.MapPatch("/api/admin/exams/{examId}", (HttpContext context, string examId, JsonObject body, IAdminExamManagementService service, CancellationToken cancellationToken) =>
Execute(context, service.UpdateAsync(Token(context), examId, body, cancellationToken)));
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)));
}
if (options.NativeResultsEnabled)
{
endpoints.MapGet("/api/admin/results",
(HttpContext context, IAdminResultService service, CancellationToken cancellationToken) =>
Execute(
context,
service.GetAsync(
Token(context),
context.Request.Query["examId"].ToString(),
cancellationToken)));
endpoints.MapPost("/api/admin/results",
(HttpContext context, JsonObject body, IAdminResultService service, CancellationToken cancellationToken) =>
Execute(context, service.SaveAsync(Token(context), body, cancellationToken)));
endpoints.MapPost("/api/admin/results/bulk",
(HttpContext context, JsonObject body, IAdminResultService service, CancellationToken cancellationToken) =>
Execute(context, service.SaveBulkAsync(Token(context), body, cancellationToken)));
endpoints.MapPost("/api/admin/results/import",
(HttpContext context, JsonObject body, IAdminResultService service, CancellationToken cancellationToken) =>
Execute(context, service.ImportAsync(Token(context), body, cancellationToken)));
endpoints.MapPost("/api/admin/feature-scores/bulk",
(HttpContext context, JsonObject body, IAdminResultService service, CancellationToken cancellationToken) =>
Execute(context, service.SaveFeatureBulkAsync(Token(context), body, cancellationToken)));
endpoints.MapPatch("/api/admin/registrations/{registrationId}/feature-score",
(HttpContext context, string registrationId, JsonObject body, IAdminResultService service, CancellationToken cancellationToken) =>
Execute(context, service.SaveFeatureAsync(Token(context), registrationId, body, cancellationToken)));
endpoints.MapPatch("/api/admin/score-appeals/{resultId}",
(HttpContext context, string resultId, JsonObject body, IAdminResultService service, CancellationToken cancellationToken) =>
Execute(context, service.ReviewAppealAsync(Token(context), resultId, body, cancellationToken)));
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/admin/admissions",
(HttpContext context, IAdminAdmissionService service, CancellationToken cancellationToken) =>
Execute(context, service.GetAsync(Token(context), cancellationToken)));
endpoints.MapGet("/api/admin/indicator-qualifications",
(HttpContext context, IAdminAdmissionService service, CancellationToken cancellationToken) =>
Execute(context, service.GetQualificationsAsync(Token(context), cancellationToken)));
endpoints.MapPut("/api/admin/indicator-qualifications/{examId}/bulk",
(HttpContext context, string examId, JsonObject body, IAdminAdmissionService service, CancellationToken cancellationToken) =>
Execute(context, service.SaveQualificationsAsync(Token(context), examId, body, cancellationToken)));
endpoints.MapPut("/api/admin/indicator-qualifications/{examId}/{userId}",
(HttpContext context, string examId, string userId, JsonObject body, IAdminAdmissionService service, CancellationToken cancellationToken) =>
Execute(context, service.SaveQualificationAsync(Token(context), examId, userId, body, cancellationToken)));
endpoints.MapPost("/api/admin/admission-school-accounts",
(HttpContext context, JsonObject body, IAdminAdmissionService service, CancellationToken cancellationToken) =>
Execute(context, service.CreateAccountAsync(Token(context), body, cancellationToken)));
endpoints.MapPatch("/api/admin/admission-school-accounts/{accountId}",
(HttpContext context, string accountId, JsonObject body, IAdminAdmissionService service, CancellationToken cancellationToken) =>
Execute(context, service.UpdateAccountAsync(Token(context), accountId, body, cancellationToken)));
endpoints.MapPost("/api/admin/admission-school-accounts/{accountId}/reset-password",
(HttpContext context, string accountId, IAdminAdmissionService service, CancellationToken cancellationToken) =>
Execute(context, service.ResetAccountPasswordAsync(Token(context), accountId, cancellationToken)));
endpoints.MapPut("/api/admin/admissions/{examId}/setting",
(HttpContext context, string examId, JsonObject body, IAdminAdmissionService service, CancellationToken cancellationToken) =>
Execute(context, service.SaveSettingAsync(Token(context), examId, body, cancellationToken)));
endpoints.MapPost("/api/admin/admission-plans",
(HttpContext context, JsonObject body, IAdminAdmissionService service, CancellationToken cancellationToken) =>
Execute(context, service.SavePlanAsync(Token(context), body, cancellationToken)));
endpoints.MapPatch("/api/admin/admission-plans/{planId}",
(HttpContext context, string planId, JsonObject body, IAdminAdmissionService service, CancellationToken cancellationToken) =>
Execute(context, service.ReviewPlanAsync(Token(context), planId, body, cancellationToken)));
endpoints.MapPost("/api/admin/admissions/{examId}/{action:regex(^(match|finalize|supplementary)$)}",
(HttpContext context, string examId, string action, JsonObject body, IAdminAdmissionService service, CancellationToken cancellationToken) =>
Execute(context, service.RunActionAsync(Token(context), examId, action, body, cancellationToken)));
endpoints.MapPatch("/api/admin/admission-reporting/{recordId}",
(HttpContext context, string recordId, JsonObject body, IAdminAdmissionService service, CancellationToken cancellationToken) =>
Execute(context, service.ReviewReportingAsync(Token(context), recordId, body, cancellationToken)));
endpoints.MapPatch("/api/admin/admission-withdrawals/{placementId}",
(HttpContext context, string placementId, JsonObject body, IAdminAdmissionService service, CancellationToken cancellationToken) =>
Execute(context, service.ReviewWithdrawalAsync(Token(context), placementId, body, cancellationToken)));
}
if (options.NativeReadsEnabled)
{
endpoints.MapDelete("/api/admin/admins/{**path}", NativeDeleteNotAllowed);
endpoints.MapDelete("/api/admin/candidates/{**path}", NativeDeleteNotAllowed);
endpoints.MapDelete("/api/admin/candidate-accounts/{**path}", NativeDeleteNotAllowed);
}
return endpoints;
}
private static async Task<IResult> Execute(HttpContext context, Task<AdminEndpointResult> operation)
{
var result = await operation;
context.Response.Headers.CacheControl = "no-store";
context.Response.Headers["X-EIS-Implementation"] = "aspnet-core";
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;
private static IResult NativeDeleteNotAllowed(HttpContext context)
{
context.Response.Headers.CacheControl = "no-store";
context.Response.Headers["X-EIS-Implementation"] = "aspnet-core";
return Results.Json(
new JsonObject { ["ok"] = false, ["message"] = "账户不得删除;考生账户请由校方归档,管理员账户可停用" },
statusCode: StatusCodes.Status405MethodNotAllowed);
}
}