已完成本轮迁移:管理后台、审批流和考务编排的业务 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)
This commit is contained in:
1 parent
d47c614b53
commit
4df6303153
14 files changed
+1810
-13
No files matched your search
@@ -0,0 +1,20 @@
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace Eis.Application.Administration;
|
||||
|
||||
public interface IAdminAdmissionService
|
||||
{
|
||||
Task<AdminEndpointResult> GetAsync(string sessionToken, CancellationToken cancellationToken);
|
||||
Task<AdminEndpointResult> GetQualificationsAsync(string sessionToken, CancellationToken cancellationToken);
|
||||
Task<AdminEndpointResult> SaveQualificationAsync(string sessionToken, string examId, string userId, JsonObject body, CancellationToken cancellationToken);
|
||||
Task<AdminEndpointResult> SaveQualificationsAsync(string sessionToken, string examId, JsonObject body, CancellationToken cancellationToken);
|
||||
Task<AdminEndpointResult> CreateAccountAsync(string sessionToken, JsonObject body, CancellationToken cancellationToken);
|
||||
Task<AdminEndpointResult> UpdateAccountAsync(string sessionToken, string accountId, JsonObject body, CancellationToken cancellationToken);
|
||||
Task<AdminEndpointResult> ResetAccountPasswordAsync(string sessionToken, string accountId, CancellationToken cancellationToken);
|
||||
Task<AdminEndpointResult> SaveSettingAsync(string sessionToken, string examId, JsonObject body, CancellationToken cancellationToken);
|
||||
Task<AdminEndpointResult> SavePlanAsync(string sessionToken, JsonObject body, CancellationToken cancellationToken);
|
||||
Task<AdminEndpointResult> ReviewPlanAsync(string sessionToken, string planId, JsonObject body, CancellationToken cancellationToken);
|
||||
Task<AdminEndpointResult> RunActionAsync(string sessionToken, string examId, string action, JsonObject body, CancellationToken cancellationToken);
|
||||
Task<AdminEndpointResult> ReviewReportingAsync(string sessionToken, string recordId, JsonObject body, CancellationToken cancellationToken);
|
||||
Task<AdminEndpointResult> ReviewWithdrawalAsync(string sessionToken, string placementId, JsonObject body, CancellationToken cancellationToken);
|
||||
}
|
||||
@@ -7,6 +7,7 @@ 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> ImportAsync(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);
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
using System.Data.Common;
|
||||
using Eis.Infrastructure.Candidate;
|
||||
using Eis.Infrastructure.Data;
|
||||
|
||||
namespace Eis.Infrastructure.Administration;
|
||||
|
||||
internal sealed class AdminAdmissionRepository(IRelationalConnectionFactory connectionFactory)
|
||||
{
|
||||
public Task SaveRecordsAsync(
|
||||
IReadOnlyList<CandidateAdmissionRecord> records,
|
||||
AdminAuditEntry audit,
|
||||
CancellationToken cancellationToken) =>
|
||||
TransactionAsync(async (connection, transaction) =>
|
||||
{
|
||||
foreach (var record in records)
|
||||
{
|
||||
await ExecuteAsync(connection, transaction, "DELETE FROM admission_records WHERE id = @id",
|
||||
[new("@id", record.Id)], cancellationToken);
|
||||
await ExecuteAsync(connection, transaction,
|
||||
"""
|
||||
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
|
||||
)
|
||||
""",
|
||||
[
|
||||
new("@id", record.Id), new("@kind", record.Kind), new("@examId", record.ExamId),
|
||||
new("@userId", record.UserId), new("@schoolId", record.SchoolId),
|
||||
new("@status", record.Status), new("@payload", record.Payload.ToJsonString()),
|
||||
new("@createdAt", record.CreatedAt), new("@updatedAt", record.UpdatedAt)
|
||||
], cancellationToken);
|
||||
}
|
||||
await InsertAuditAsync(connection, transaction, audit, cancellationToken);
|
||||
}, cancellationToken);
|
||||
|
||||
public Task CreateAccountAsync(
|
||||
AdminUser account,
|
||||
string passwordHash,
|
||||
AdminAuditEntry audit,
|
||||
CancellationToken cancellationToken) =>
|
||||
TransactionAsync(async (connection, transaction) =>
|
||||
{
|
||||
await ExecuteAsync(connection, transaction,
|
||||
"""
|
||||
INSERT INTO users (
|
||||
id, username, password_hash, role, admin_level, school_id, class_id,
|
||||
display_name, active, must_change_password, created_at
|
||||
) VALUES (
|
||||
@id, @username, @passwordHash, 'admission_school', NULL, @schoolId, NULL,
|
||||
@displayName, 1, 0, @createdAt
|
||||
)
|
||||
""",
|
||||
[
|
||||
new("@id", account.Id), new("@username", account.Username), new("@passwordHash", passwordHash),
|
||||
new("@schoolId", account.SchoolId), new("@displayName", account.DisplayName),
|
||||
new("@createdAt", account.CreatedAt)
|
||||
], cancellationToken);
|
||||
await InsertAuditAsync(connection, transaction, audit, cancellationToken);
|
||||
}, cancellationToken);
|
||||
|
||||
public Task UpdateAccountAsync(
|
||||
string accountId,
|
||||
string displayName,
|
||||
bool active,
|
||||
string? passwordHash,
|
||||
AdminAuditEntry audit,
|
||||
CancellationToken cancellationToken) =>
|
||||
TransactionAsync(async (connection, transaction) =>
|
||||
{
|
||||
await ExecuteAsync(connection, transaction,
|
||||
passwordHash is null
|
||||
? "UPDATE users SET display_name = @displayName, active = @active WHERE id = @id"
|
||||
: "UPDATE users SET display_name = @displayName, active = @active, password_hash = @passwordHash WHERE id = @id",
|
||||
[
|
||||
new("@displayName", displayName), new("@active", active ? 1 : 0),
|
||||
new("@passwordHash", passwordHash), new("@id", accountId)
|
||||
], cancellationToken);
|
||||
await InsertAuditAsync(connection, transaction, audit, cancellationToken);
|
||||
}, cancellationToken);
|
||||
|
||||
private async Task TransactionAsync(
|
||||
Func<DbConnection, DbTransaction, Task> operation,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
await using var connection = await connectionFactory.OpenAsync(cancellationToken);
|
||||
await using var transaction = await connection.BeginTransactionAsync(cancellationToken);
|
||||
try
|
||||
{
|
||||
await operation(connection, transaction);
|
||||
await transaction.CommitAsync(cancellationToken);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await transaction.RollbackAsync(cancellationToken);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static Task InsertAuditAsync(
|
||||
DbConnection connection,
|
||||
DbTransaction transaction,
|
||||
AdminAuditEntry audit,
|
||||
CancellationToken cancellationToken) =>
|
||||
ExecuteAsync(connection, transaction,
|
||||
"INSERT INTO audit_logs (id, actor_id, action, detail, created_at) VALUES (@id, @actorId, @action, @detail, @createdAt)",
|
||||
[
|
||||
new("@id", audit.Id), new("@actorId", audit.ActorId), new("@action", audit.Action),
|
||||
new("@detail", audit.Detail), new("@createdAt", audit.CreatedAt)
|
||||
], cancellationToken);
|
||||
|
||||
private static async Task ExecuteAsync(
|
||||
DbConnection connection,
|
||||
DbTransaction transaction,
|
||||
string sql,
|
||||
IReadOnlyList<SqlValue> values,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
await using var command = connection.CreateCommand();
|
||||
command.Transaction = transaction;
|
||||
command.CommandText = sql;
|
||||
foreach (var value in values)
|
||||
{
|
||||
var parameter = command.CreateParameter();
|
||||
parameter.ParameterName = value.Name;
|
||||
parameter.Value = value.Value ?? DBNull.Value;
|
||||
command.Parameters.Add(parameter);
|
||||
}
|
||||
await command.ExecuteNonQueryAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private sealed record SqlValue(string Name, object? Value);
|
||||
}
|
||||
Loaded 3 of 14 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user