Files
EIS-dotnet/src/Eis.Infrastructure/Candidate/CandidateAdmissionRepository.cs
T
biss 4b0dae6d71 原生志愿、录取、计划余量、指标资格和通知查询:[CandidateService.Admissions.cs (line 29)](C:/Users/BI/Documents/EIS-dotnet/src/Eis.Infrastructure/Candidate/CandidateService.Admissions.cs:29)
志愿提交、资格校验、补录限制、次数上限与自动锁定:[CandidateService.Admissions.cs (line 152)](C:/Users/BI/Documents/EIS-dotnet/src/Eis.Infrastructure/Candidate/CandidateService.Admissions.cs:152)
通用招生记录读取及事务写入:[CandidateAdmissionRepository.cs (line 25)](C:/Users/BI/Documents/EIS-dotnet/src/Eis.Infrastructure/Candidate/CandidateAdmissionRepository.cs:25)
原生路由已接入:[NativeCandidateEndpoints.cs (line 70)](C:/Users/BI/Documents/EIS-dotnet/src/Eis.Web/Candidate/NativeCandidateEndpoints.cs:70)
迁移状态现在会把 Candidate 标记为原生:[MigrationFeatureCatalog.cs (line 12)](C:/Users/BI/Documents/EIS-dotnet/src/Eis.Infrastructure/Migration/MigrationFeatureCatalog.cs:12)
2026-07-22 20:22:46 +08:00

133 lines
5.5 KiB
C#

using System.Data.Common;
using System.Globalization;
using System.Text.Json.Nodes;
using Eis.Infrastructure.Data;
namespace Eis.Infrastructure.Candidate;
internal sealed record CandidateAdmissionRecord(
string Id,
string Kind,
string ExamId,
string? UserId,
string? SchoolId,
string Status,
JsonObject Payload,
string CreatedAt,
string UpdatedAt);
internal sealed record CandidateAdmissionSchool(string Id, string Code, string Name);
internal sealed record CandidateAdmissionSnapshot(
IReadOnlyList<CandidateAdmissionRecord> Records,
IReadOnlyDictionary<string, CandidateAdmissionSchool> Schools);
internal sealed class CandidateAdmissionRepository(IRelationalConnectionFactory connectionFactory)
{
public async Task<CandidateAdmissionSnapshot> LoadAsync(CancellationToken cancellationToken)
{
await using var connection = await connectionFactory.OpenAsync(cancellationToken);
var records = new List<CandidateAdmissionRecord>();
await using (var command = connection.CreateCommand())
{
command.CommandText = "SELECT id, kind, exam_id, user_id, school_id, status, payload_json, created_at, updated_at FROM admission_records ORDER BY created_at, id";
await using var reader = await command.ExecuteReaderAsync(cancellationToken);
while (await reader.ReadAsync(cancellationToken))
{
records.Add(new CandidateAdmissionRecord(
ReadString(reader, "id"),
ReadString(reader, "kind"),
ReadString(reader, "exam_id"),
ReadOptionalString(reader, "user_id"),
ReadOptionalString(reader, "school_id"),
ReadString(reader, "status"),
JsonNode.Parse(ReadString(reader, "payload_json"))?.AsObject() ?? new JsonObject(),
ReadString(reader, "created_at"),
ReadString(reader, "updated_at")));
}
}
var schools = new List<CandidateAdmissionSchool>();
await using (var command = connection.CreateCommand())
{
command.CommandText = "SELECT id, code, name FROM schools ORDER BY id";
await using var reader = await command.ExecuteReaderAsync(cancellationToken);
while (await reader.ReadAsync(cancellationToken))
{
schools.Add(new CandidateAdmissionSchool(
ReadString(reader, "id"),
ReadString(reader, "code"),
ReadString(reader, "name")));
}
}
return new CandidateAdmissionSnapshot(
records,
schools.ToDictionary(item => item.Id, StringComparer.Ordinal));
}
public async Task SavePreferenceAsync(
CandidateAdmissionRecord record,
CancellationToken cancellationToken)
{
await using var connection = await connectionFactory.OpenAsync(cancellationToken);
await using var transaction = await connection.BeginTransactionAsync(cancellationToken);
try
{
await using (var delete = connection.CreateCommand())
{
delete.Transaction = transaction;
delete.CommandText = "DELETE FROM admission_records WHERE id = @id";
AddParameter(delete, "@id", record.Id);
await delete.ExecuteNonQueryAsync(cancellationToken);
}
await using (var insert = connection.CreateCommand())
{
insert.Transaction = transaction;
insert.CommandText = """
INSERT INTO admission_records (
id, kind, exam_id, user_id, school_id, status, payload_json, created_at, updated_at
) VALUES (
@id, @kind, @examId, @userId, @schoolId, @status, @payload, @createdAt, @updatedAt
)
""";
AddParameter(insert, "@id", record.Id);
AddParameter(insert, "@kind", record.Kind);
AddParameter(insert, "@examId", record.ExamId);
AddParameter(insert, "@userId", record.UserId);
AddParameter(insert, "@schoolId", record.SchoolId);
AddParameter(insert, "@status", record.Status);
AddParameter(insert, "@payload", record.Payload.ToJsonString());
AddParameter(insert, "@createdAt", record.CreatedAt);
AddParameter(insert, "@updatedAt", record.UpdatedAt);
await insert.ExecuteNonQueryAsync(cancellationToken);
}
await transaction.CommitAsync(cancellationToken);
}
catch
{
await transaction.RollbackAsync(cancellationToken);
throw;
}
}
private static void AddParameter(DbCommand command, string name, object? value)
{
var parameter = command.CreateParameter();
parameter.ParameterName = name;
parameter.Value = value ?? DBNull.Value;
command.Parameters.Add(parameter);
}
private static string ReadString(DbDataReader reader, string name) =>
Convert.ToString(reader.GetValue(reader.GetOrdinal(name)), CultureInfo.InvariantCulture) ?? string.Empty;
private static string? ReadOptionalString(DbDataReader reader, string name)
{
var ordinal = reader.GetOrdinal(name);
return reader.IsDBNull(ordinal) ? null : Convert.ToString(reader.GetValue(ordinal), CultureInfo.InvariantCulture);
}
}