Files
EIS-dotnet/src/Eis.Infrastructure/Administration/AdminArrangementExporter.cs
T
biss 8f83adbc1e 支持五级混编、稳定种子、容量预检、固定考点、逐科考场/座位及四种准考证号规则。
正式编排会事务性替换整场计划、准考证、座位和审计记录。
支持三级管理员范围读取、批量 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 页面
2026-07-23 11:25:09 +08:00

245 lines
9.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using ClosedXML.Excel;
namespace Eis.Infrastructure.Administration;
internal static class AdminArrangementExporter
{
private static readonly string[] InformationHeaders =
[
"考生学校", "班级", "报名号", "姓名", "证件号码", "考试代码", "考试名称", "准考证号",
"考点代码", "考点名称", "考点详细地址", "科目", "日期", "时间", "考试考场序号",
"考场通用名称", "物理场地代码", "楼栋", "楼层", "座位号"
];
public static byte[] AdmitCardInformation(
IReadOnlyList<ArrangementExportRow> rows,
string subtitle)
{
using var workbook = new XLWorkbook();
var sheet = workbook.Worksheets.Add("准考证信息");
ConfigureTable(
sheet,
$"准考证信息台账|{subtitle}",
InformationHeaders,
rows.Select(InformationValues).ToArray());
return Save(workbook);
}
public static byte[] CenterMaterials(
IReadOnlyList<ArrangementExportRow> rows,
string subtitle)
{
using var workbook = new XLWorkbook();
var sorted = rows
.OrderBy(item => item.CenterCode, StringComparer.Ordinal)
.ThenBy(item => item.SubjectDate, StringComparer.Ordinal)
.ThenBy(item => item.SubjectTime, StringComparer.Ordinal)
.ThenBy(item => item.ExamRoomCode, StringComparer.Ordinal)
.ThenBy(item => item.Seat, StringComparer.Ordinal)
.ToArray();
AddDeskStickers(workbook.Worksheets.Add("桌贴"), sorted);
var doors = sorted
.GroupBy(
item => new
{
item.ExamCode,
item.SubjectName,
item.CenterCode,
item.ExamRoomCode,
item.RoomCode
})
.Select(group => (Row: group.First(), Count: group.Count()))
.ToArray();
ConfigureTable(
workbook.Worksheets.Add("门贴"),
$"考场门贴|{subtitle}",
[
"考试", "科目", "日期", "时间", "考点", "考试考场序号",
"考场通用名称", "场地代码", "楼栋", "楼层", "人数"
],
doors.Select(item => new object[]
{
item.Row.ExamName,
item.Row.SubjectName,
item.Row.SubjectDate,
item.Row.SubjectTime,
item.Row.CenterName,
item.Row.ExamRoomCode,
item.Row.RoomName,
item.Row.RoomCode,
item.Row.Building,
item.Row.Floor,
item.Count
}).ToArray());
ConfigureTable(
workbook.Worksheets.Add("考场签名单"),
$"考场考生签名单|{subtitle}",
[
"考试", "科目", "日期", "时间", "考试考场序号", "考场通用名称",
"楼栋", "楼层", "座位号", "准考证号", "姓名", "考生学校", "班级", "考生签名"
],
sorted.Select(item => new object[]
{
item.ExamName,
item.SubjectName,
item.SubjectDate,
item.SubjectTime,
item.ExamRoomCode,
item.RoomName,
item.Building,
item.Floor,
item.Seat,
item.CardNumber,
item.CandidateName,
item.SchoolName,
item.ClassName,
""
}).ToArray());
return Save(workbook);
}
private static void AddDeskStickers(
IXLWorksheet sheet,
IReadOnlyList<ArrangementExportRow> rows)
{
sheet.ShowGridLines = false;
for (var column = 1; column <= 7; column++)
sheet.Column(column).Width = column == 4 ? 3 : 14.5;
for (var index = 0; index < rows.Count; index++)
{
var item = rows[index];
var slot = index % 2;
var group = index / 2;
var startRow = group * 9 + 1;
var startColumn = slot == 0 ? 1 : 5;
var values = new[]
{
item.ExamName,
$"{item.SubjectName}{item.SubjectDate} {item.SubjectTime}",
"考试考场序号",
item.ExamRoomCode,
$"座位号 {item.Seat}",
$"{item.RoomName}(场地 {item.RoomCode})|{item.Building} {item.Floor}",
$"{item.CandidateName}{item.SchoolName} {item.ClassName}",
$"{item.CenterName}|准考证号 {item.CardNumber}"
};
for (var offset = 0; offset < values.Length; offset++)
{
var range = sheet.Range(
startRow + offset,
startColumn,
startRow + offset,
startColumn + 2);
range.Merge();
range.Value = values[offset];
range.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
range.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center;
range.Style.Alignment.WrapText = true;
range.Style.Border.OutsideBorder = XLBorderStyleValues.Medium;
range.Style.Border.InsideBorder = XLBorderStyleValues.Hair;
sheet.Row(startRow + offset).Height = 28;
}
var title = sheet.Range(startRow, startColumn, startRow, startColumn + 2);
title.Style.Fill.BackgroundColor = XLColor.FromHtml("#173F60");
title.Style.Font.FontColor = XLColor.White;
title.Style.Font.Bold = true;
var room = sheet.Range(startRow + 3, startColumn, startRow + 3, startColumn + 2);
room.Style.Font.FontSize = 24;
room.Style.Font.Bold = true;
var seat = sheet.Range(startRow + 4, startColumn, startRow + 4, startColumn + 2);
seat.Style.Fill.BackgroundColor = XLColor.FromHtml("#FFF1C7");
seat.Style.Font.FontColor = XLColor.FromHtml("#8A4B08");
seat.Style.Font.FontSize = 18;
seat.Style.Font.Bold = true;
}
sheet.PageSetup.PageOrientation = XLPageOrientation.Portrait;
sheet.PageSetup.FitToPages(1, 0);
sheet.PageSetup.Margins.SetLeft(0.25);
sheet.PageSetup.Margins.SetRight(0.25);
sheet.PageSetup.Margins.SetTop(0.25);
sheet.PageSetup.Margins.SetBottom(0.25);
}
private static void ConfigureTable(
IXLWorksheet sheet,
string title,
IReadOnlyList<string> headers,
IReadOnlyList<object[]> rows)
{
sheet.ShowGridLines = false;
var lastColumn = headers.Count;
var titleRange = sheet.Range(1, 1, 1, lastColumn);
titleRange.Merge();
titleRange.Value = title;
titleRange.Style.Fill.BackgroundColor = XLColor.FromHtml("#173F60");
titleRange.Style.Font.FontColor = XLColor.White;
titleRange.Style.Font.FontSize = 16;
titleRange.Style.Font.Bold = true;
titleRange.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center;
sheet.Row(1).Height = 28;
for (var column = 0; column < headers.Count; column++)
{
var cell = sheet.Cell(2, column + 1);
cell.Value = headers[column];
cell.Style.Fill.BackgroundColor = XLColor.FromHtml("#2C7080");
cell.Style.Font.FontColor = XLColor.White;
cell.Style.Font.Bold = true;
cell.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
cell.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center;
cell.Style.Alignment.WrapText = true;
}
for (var rowIndex = 0; rowIndex < rows.Count; rowIndex++)
{
for (var column = 0; column < headers.Count; column++)
{
var value = column < rows[rowIndex].Length ? rows[rowIndex][column] : "";
var cell = sheet.Cell(rowIndex + 3, column + 1);
if (value is int integer) cell.Value = integer;
else if (value is double number) cell.Value = number;
else cell.Value = Convert.ToString(value, System.Globalization.CultureInfo.InvariantCulture) ?? "";
cell.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center;
cell.Style.Alignment.WrapText = true;
cell.Style.Border.BottomBorder = XLBorderStyleValues.Hair;
cell.Style.Border.BottomBorderColor = XLColor.FromHtml("#D8E2E7");
}
}
sheet.SheetView.FreezeRows(2);
if (rows.Count > 0) sheet.Range(2, 1, rows.Count + 2, lastColumn).SetAutoFilter();
sheet.Columns().AdjustToContents(8, 40);
sheet.PageSetup.PageOrientation = XLPageOrientation.Landscape;
sheet.PageSetup.FitToPages(1, 0);
sheet.PageSetup.SetRowsToRepeatAtTop(1, 2);
}
private static object[] InformationValues(ArrangementExportRow item) =>
[
item.SchoolName,
item.ClassName,
item.CandidateNumber,
item.CandidateName,
item.IdNumber,
item.ExamCode,
item.ExamName,
item.CardNumber,
item.CenterCode,
item.CenterName,
item.CenterAddress,
item.SubjectName,
item.SubjectDate,
item.SubjectTime,
item.ExamRoomCode,
item.RoomName,
item.RoomCode,
item.Building,
item.Floor,
item.Seat
];
private static byte[] Save(XLWorkbook workbook)
{
using var stream = new MemoryStream();
workbook.SaveAs(stream);
return stream.ToArray();
}
}