按 examId 延迟加载成绩工作区,未选择考试时不执行排名计算

单条及批量成绩草稿、发布
特长分单条及批量维护
成绩复议多级审批、终审改分、归档锁定
排名、等级、单科达线和考试汇总
成绩台账 Excel 导出
管理范围、超级管理员写权限及事务审计
独立开关 ADMIN_NATIVE_RESULTS_ENABLED
This commit is contained in:
biss committed 2026-07-23 12:16:01 +08:00
1 parent 8f83adbc1e
commit d47c614b53
15 files changed
+1523 -15

No files matched your search

@@ -0,0 +1,15 @@
using System.Text.Json.Nodes;
namespace Eis.Application.Administration;
public interface IAdminResultService
{
Task<AdminEndpointResult> GetAsync(string sessionToken, string examId, CancellationToken cancellationToken);
Task<AdminEndpointResult> SaveAsync(string sessionToken, JsonObject body, CancellationToken cancellationToken);
Task<AdminEndpointResult> SaveBulkAsync(string sessionToken, JsonObject body, CancellationToken cancellationToken);
Task<AdminEndpointResult> SaveFeatureBulkAsync(string sessionToken, JsonObject body, CancellationToken cancellationToken);
Task<AdminEndpointResult> SaveFeatureAsync(string sessionToken, string registrationId, JsonObject body, CancellationToken cancellationToken);
Task<AdminEndpointResult> ReviewAppealAsync(string sessionToken, string resultId, JsonObject body, CancellationToken cancellationToken);
Task<AdminEndpointResult> RefreshCacheAsync(string sessionToken, CancellationToken cancellationToken);
Task<AdminDocumentResult> ExportAsync(string sessionToken, string examId, CancellationToken cancellationToken);
}
@@ -12,7 +12,8 @@ public sealed record AdminMigrationOptions(
bool NativeRegistrationPaymentWritesEnabled = false,
bool NativeWorkflowOperationsEnabled = false,
bool NativeExamManagementEnabled = false,
bool NativeArrangementsEnabled = false)
bool NativeArrangementsEnabled = false,
bool NativeResultsEnabled = false)
{
public static AdminMigrationOptions FromEnvironment(
bool configuredNativeReadsEnabled,
@@ -28,7 +29,8 @@ public sealed record AdminMigrationOptions(
bool configuredNativeRegistrationPaymentWritesEnabled = false,
bool configuredNativeWorkflowOperationsEnabled = false,
bool configuredNativeExamManagementEnabled = false,
bool configuredNativeArrangementsEnabled = false)
bool configuredNativeArrangementsEnabled = false,
bool configuredNativeResultsEnabled = false)
{
var readsEnabled = ParseBoolean(
Environment.GetEnvironmentVariable("ADMIN_NATIVE_READS_ENABLED"),
@@ -67,17 +69,20 @@ public sealed record AdminMigrationOptions(
var arrangementsEnabled = ParseBoolean(
Environment.GetEnvironmentVariable("ADMIN_NATIVE_ARRANGEMENTS_ENABLED"),
configuredNativeArrangementsEnabled);
if ((organizationWritesEnabled || accountBatchesEnabled || configurationEnabled || noticeManagementEnabled || centersEnabled || operationalReadsEnabled || candidateManagementEnabled || registrationPaymentWritesEnabled || workflowOperationsEnabled || examManagementEnabled || arrangementsEnabled) && !readsEnabled)
var resultsEnabled = ParseBoolean(
Environment.GetEnvironmentVariable("ADMIN_NATIVE_RESULTS_ENABLED"),
configuredNativeResultsEnabled);
if ((organizationWritesEnabled || accountBatchesEnabled || configurationEnabled || noticeManagementEnabled || centersEnabled || operationalReadsEnabled || candidateManagementEnabled || registrationPaymentWritesEnabled || workflowOperationsEnabled || examManagementEnabled || arrangementsEnabled || resultsEnabled) && !readsEnabled)
{
throw new InvalidOperationException(
"启用原生组织维护接口前必须同时设置 ADMIN_NATIVE_READS_ENABLED=true");
}
if ((candidateManagementEnabled || registrationPaymentWritesEnabled) && !operationalReadsEnabled)
if ((candidateManagementEnabled || registrationPaymentWritesEnabled || resultsEnabled) && !operationalReadsEnabled)
{
throw new InvalidOperationException(
"启用原生考生、报名或缴费写接口前必须同时设置 ADMIN_NATIVE_OPERATIONAL_READS_ENABLED=true");
}
var anyNativeAdminEndpointEnabled = readsEnabled || organizationWritesEnabled || accountBatchesEnabled || configurationEnabled || noticeManagementEnabled || centersEnabled || operationalReadsEnabled || candidateManagementEnabled || registrationPaymentWritesEnabled || workflowOperationsEnabled || examManagementEnabled || arrangementsEnabled;
var anyNativeAdminEndpointEnabled = readsEnabled || organizationWritesEnabled || accountBatchesEnabled || configurationEnabled || noticeManagementEnabled || centersEnabled || operationalReadsEnabled || candidateManagementEnabled || registrationPaymentWritesEnabled || workflowOperationsEnabled || examManagementEnabled || arrangementsEnabled || resultsEnabled;
if (anyNativeAdminEndpointEnabled && !authenticationNativeEnabled)
{
throw new InvalidOperationException(
@@ -93,7 +98,7 @@ public sealed record AdminMigrationOptions(
"管理端仍有接口需要转发给 Node;启用原生管理端接口必须配置共享 Redis 会话");
}
return new AdminMigrationOptions(readsEnabled, organizationWritesEnabled, accountBatchesEnabled, configurationEnabled, noticeManagementEnabled, centersEnabled, operationalReadsEnabled, candidateManagementEnabled, registrationPaymentWritesEnabled, workflowOperationsEnabled, examManagementEnabled, arrangementsEnabled);
return new AdminMigrationOptions(readsEnabled, organizationWritesEnabled, accountBatchesEnabled, configurationEnabled, noticeManagementEnabled, centersEnabled, operationalReadsEnabled, candidateManagementEnabled, registrationPaymentWritesEnabled, workflowOperationsEnabled, examManagementEnabled, arrangementsEnabled, resultsEnabled);
}
private static bool ParseBoolean(string? value, bool fallback) => value?.Trim().ToLowerInvariant() switch
@@ -0,0 +1,80 @@
using ClosedXML.Excel;
namespace Eis.Infrastructure.Administration;
internal sealed record AdminResultExportRow(
string CandidateNumber,
string CardNumber,
string CandidateName,
string SchoolName,
string ClassName,
string ExamCode,
string ExamName,
string SubjectName,
object FullScore,
string PassRule,
object PassScore,
object Score,
object Rank,
object RankPercent,
string Qualified,
string Grade,
string Published,
string UpdatedAt);
internal static class AdminResultExporter
{
private static readonly string[] Headers =
[
"报名号*", "准考证号(只读参考)", "姓名(只读参考)", "学校(只读参考)", "班级(只读参考)",
"考试代码*", "考试名称(只读参考)", "科目*", "科目满分(只读参考)", "单科及格规则(只读参考)",
"实际及格分(只读参考)", "成绩*", "本科排名(导出计算)", "排名百分位(导出计算)",
"单科达线(导出计算)", "排名等级(自动计算)", "发布状态*", "更新时间(只读参考)"
];
public static byte[] Build(IReadOnlyList<AdminResultExportRow> rows)
{
using var workbook = new XLWorkbook();
var sheet = workbook.Worksheets.Add("成绩");
sheet.Cell(1, 1).Value = "考试成绩台账";
sheet.Range(1, 1, 1, Headers.Length).Merge();
sheet.Range(1, 1, 1, Headers.Length).Style
.Font.SetBold().Font.SetFontSize(16).Font.SetFontColor(XLColor.White)
.Fill.SetBackgroundColor(XLColor.FromHtml("#182552"));
for (var index = 0; index < Headers.Length; index++)
{
sheet.Cell(2, index + 1).Value = Headers[index];
}
sheet.Range(2, 1, 2, Headers.Length).Style
.Font.SetBold().Font.SetFontColor(XLColor.White)
.Fill.SetBackgroundColor(XLColor.FromHtml("#245365"));
for (var rowIndex = 0; rowIndex < rows.Count; rowIndex++)
{
var item = rows[rowIndex];
object[] values =
[
item.CandidateNumber, item.CardNumber, item.CandidateName, item.SchoolName, item.ClassName,
item.ExamCode, item.ExamName, item.SubjectName, item.FullScore, item.PassRule, item.PassScore,
item.Score, item.Rank, item.RankPercent, item.Qualified, item.Grade, item.Published, item.UpdatedAt
];
for (var column = 0; column < values.Length; column++)
{
sheet.Cell(rowIndex + 3, column + 1).Value = XLCellValue.FromObject(values[column]);
}
}
sheet.SheetView.FreezeRows(2);
sheet.RangeUsed()?.SetAutoFilter();
double[] widths =
[
24, 22, 14, 22, 16, 18, 26, 14, 16, 22, 18, 12, 16, 18, 16, 18, 14, 22
];
for (var index = 0; index < widths.Length; index++)
{
sheet.Column(index + 1).Width = widths[index];
}
sheet.Columns(9, 14).Style.NumberFormat.Format = "0.00";
using var stream = new MemoryStream();
workbook.SaveAs(stream);
return stream.ToArray();
}
}
Loaded 3 of 15 files, more files were not shown because too many files have changed in this diff. Show more