Files
EIS-dotnet/src/Eis.Infrastructure/Administration/AdminCenterSnapshot.cs
T
biss 518c922773 本轮完成“考点与考场变更审批”原生迁移:
GET/POST/PATCH /api/admin/centers
PATCH /api/admin/center-change-requests/{id}
校级数据范围、班级管理员禁用
省市区县及结构化考场校验
正式档案和待审申请代码去重
多级工作流分配、通过与拒绝
终审时原子更新流程、审计、考点及考场
新增开关 ADMIN_NATIVE_CENTERS_ENABLED=false
2026-07-23 09:17:04 +08:00

189 lines
7.8 KiB
C#

using System.Data.Common;
using System.Globalization;
using Eis.Infrastructure.Data;
namespace Eis.Infrastructure.Administration;
internal sealed record AdminCenterSnapshot(
IReadOnlyList<AdminCenter> Centers,
IReadOnlyList<AdminCenterRoom> Rooms,
IReadOnlyList<AdminCenterChange> Changes,
IReadOnlyList<AdminCenterChangeRoom> ChangeRooms);
internal sealed record AdminCenter(
string Id,
string SchoolId,
string Code,
string Name,
string ProvinceCode,
string ProvinceName,
string CityCode,
string CityName,
string DistrictCode,
string DistrictName,
string Address,
string Contact,
string ManagerName,
string ManagerPhone,
string EmergencyPhone,
string GateOpenTime,
string Transport,
string Status,
string Notes,
string Rooms,
string UpdatedAt);
internal sealed record AdminCenterRoom(
string Id,
string CenterId,
string Code,
string Name,
string Building,
string Floor,
int Capacity,
string SeatPlan,
int SeatStart,
int SeatEnd,
string RoomType,
string Status,
string Notes);
internal sealed record AdminCenterChange(
string Id,
string? CenterId,
string SchoolId,
string RequestType,
string Code,
string Name,
string ProvinceCode,
string ProvinceName,
string CityCode,
string CityName,
string DistrictCode,
string DistrictName,
string Address,
string Contact,
string ManagerName,
string ManagerPhone,
string EmergencyPhone,
string GateOpenTime,
string Transport,
string CenterStatus,
string Notes,
string Status,
string ReviewNote,
string? RequestedBy,
string CreatedAt,
string? ReviewedAt);
internal sealed record AdminCenterChangeRoom(
string Id,
string RequestId,
string? RoomId,
string Code,
string Name,
string Building,
string Floor,
int Capacity,
string SeatPlan,
int SeatStart,
int SeatEnd,
string RoomType,
string Status,
string Notes);
internal sealed class AdminCenterSnapshotLoader(IRelationalConnectionFactory connectionFactory)
{
public async Task<AdminCenterSnapshot> LoadAsync(CancellationToken cancellationToken)
{
await using var connection = await connectionFactory.OpenAsync(cancellationToken);
var centers = await QueryAsync(connection,
"""
SELECT id, school_id, code, name, province_code, province_name, city_code, city_name,
district_code, district_name, address, contact, manager_name, manager_phone, emergency_phone,
gate_open_time, transport, status, notes, rooms, updated_at
FROM test_centers ORDER BY school_id, name, id
""",
ReadCenter,
cancellationToken);
var rooms = await QueryAsync(connection,
"""
SELECT id, center_id, code, name, building, floor, capacity, seat_plan, seat_start, seat_end,
room_type, status, notes FROM test_rooms ORDER BY center_id, code, id
""",
ReadRoom,
cancellationToken);
var changes = await QueryAsync(connection,
"""
SELECT id, center_id, school_id, request_type, code, name, province_code, province_name,
city_code, city_name, district_code, district_name, address, contact, manager_name,
manager_phone, emergency_phone, gate_open_time, transport, center_status, notes, status,
review_note, requested_by, created_at, reviewed_at
FROM center_change_requests ORDER BY created_at DESC, id
""",
ReadChange,
cancellationToken);
var changeRooms = await QueryAsync(connection,
"""
SELECT id, request_id, room_id, code, name, building, floor, capacity, seat_plan, seat_start,
seat_end, room_type, status, notes FROM center_change_rooms ORDER BY request_id, code, id
""",
ReadChangeRoom,
cancellationToken);
return new(centers, rooms, changes, changeRooms);
}
private static AdminCenter ReadCenter(DbDataReader reader) => new(
Text(reader, "id"), Text(reader, "school_id"), Text(reader, "code"), Text(reader, "name"),
Text(reader, "province_code"), Text(reader, "province_name"), Text(reader, "city_code"), Text(reader, "city_name"),
Text(reader, "district_code"), Text(reader, "district_name"), Text(reader, "address"), Optional(reader, "contact") ?? "",
Optional(reader, "manager_name") ?? "", Optional(reader, "manager_phone") ?? "", Optional(reader, "emergency_phone") ?? "",
Optional(reader, "gate_open_time") ?? "", Optional(reader, "transport") ?? "", Text(reader, "status"),
Optional(reader, "notes") ?? "", Text(reader, "rooms"), Text(reader, "updated_at"));
private static AdminCenterRoom ReadRoom(DbDataReader reader) => new(
Text(reader, "id"), Text(reader, "center_id"), Text(reader, "code"), Text(reader, "name"), Text(reader, "building"),
Optional(reader, "floor") ?? "", Integer(reader, "capacity"), Optional(reader, "seat_plan") ?? "",
Integer(reader, "seat_start"), Integer(reader, "seat_end"), Text(reader, "room_type"), Text(reader, "status"), Optional(reader, "notes") ?? "");
private static AdminCenterChange ReadChange(DbDataReader reader) => new(
Text(reader, "id"), Optional(reader, "center_id"), Text(reader, "school_id"), Text(reader, "request_type"),
Text(reader, "code"), Text(reader, "name"), Text(reader, "province_code"), Text(reader, "province_name"),
Text(reader, "city_code"), Text(reader, "city_name"), Text(reader, "district_code"), Text(reader, "district_name"),
Text(reader, "address"), Optional(reader, "contact") ?? "", Optional(reader, "manager_name") ?? "",
Optional(reader, "manager_phone") ?? "", Optional(reader, "emergency_phone") ?? "", Optional(reader, "gate_open_time") ?? "",
Optional(reader, "transport") ?? "", Text(reader, "center_status"), Optional(reader, "notes") ?? "", Text(reader, "status"),
Optional(reader, "review_note") ?? "", Optional(reader, "requested_by"), Text(reader, "created_at"), Optional(reader, "reviewed_at"));
private static AdminCenterChangeRoom ReadChangeRoom(DbDataReader reader) => new(
Text(reader, "id"), Text(reader, "request_id"), Optional(reader, "room_id"), Text(reader, "code"), Text(reader, "name"),
Text(reader, "building"), Optional(reader, "floor") ?? "", Integer(reader, "capacity"), Optional(reader, "seat_plan") ?? "",
Integer(reader, "seat_start"), Integer(reader, "seat_end"), Text(reader, "room_type"), Text(reader, "status"), Optional(reader, "notes") ?? "");
private static async Task<IReadOnlyList<T>> QueryAsync<T>(
DbConnection connection,
string sql,
Func<DbDataReader, T> map,
CancellationToken cancellationToken)
{
await using var command = connection.CreateCommand();
command.CommandText = sql;
await using var reader = await command.ExecuteReaderAsync(cancellationToken);
var output = new List<T>();
while (await reader.ReadAsync(cancellationToken)) output.Add(map(reader));
return output;
}
private static string Text(DbDataReader reader, string name) =>
Convert.ToString(reader.GetValue(reader.GetOrdinal(name)), CultureInfo.InvariantCulture) ?? "";
private static string? Optional(DbDataReader reader, string name)
{
var ordinal = reader.GetOrdinal(name);
return reader.IsDBNull(ordinal) ? null : Convert.ToString(reader.GetValue(ordinal), CultureInfo.InvariantCulture);
}
private static int Integer(DbDataReader reader, string name) =>
Convert.ToInt32(reader.GetValue(reader.GetOrdinal(name)), CultureInfo.InvariantCulture);
}