修复Mysql

This commit is contained in:
biss committed 2026-07-26 10:19:46 +08:00
1 parent 634ef69a27
commit 72297aa23f
31 files changed
+725 -275

No files matched your search

@@ -178,13 +178,25 @@ public sealed class AttendanceController(
{ {
Sheet = new Sheet = new
{ {
sheet.Id, sheet.TeachingTaskId, sheet.Name, sheet.AttendanceDate, sheet.Id,
sheet.Status, sheet.Notes, sheet.SubmittedAt, sheet.TeachingTaskId,
sheet.TaskNumber, sheet.TaskName, sheet.CourseCode, sheet.CourseName, sheet.Name,
sheet.AttendanceDate,
sheet.Status,
sheet.Notes,
sheet.SubmittedAt,
sheet.TaskNumber,
sheet.TaskName,
sheet.CourseCode,
sheet.CourseName,
Records = sheet.Records.Select(r => new Records = sheet.Records.Select(r => new
{ {
r.StudentId, r.StudentNumber, r.Name, r.ClassName, r.StudentId,
r.Status, r.Notes, r.StudentNumber,
r.Name,
r.ClassName,
r.Status,
r.Notes,
IsExempt = exemptStudentIds.Contains(r.StudentId), IsExempt = exemptStudentIds.Contains(r.StudentId),
IsDeferred = deferredStudentIds.Contains(r.StudentId) IsDeferred = deferredStudentIds.Contains(r.StudentId)
}) })
@@ -504,8 +516,8 @@ public sealed class AttendanceController(
var source = db.AttendanceRecords.AsNoTracking() var source = db.AttendanceRecords.AsNoTracking()
.Where(r => .Where(r =>
r.AttendanceSheet!.Status == AttendanceSheetStatus.Submitted && r.AttendanceSheet!.Status == AttendanceSheetStatus.Submitted)
targetClassIds.Contains(r.Student!.AdministrativeClassId)); .WhereIn(targetClassIds, r => r.Student!.AdministrativeClassId);
if (academicTermId.HasValue) if (academicTermId.HasValue)
source = source.Where(r => source = source.Where(r =>
r.AttendanceSheet!.TeachingTask!.AcademicTermId == academicTermId); r.AttendanceSheet!.TeachingTask!.AcademicTermId == academicTermId);
@@ -77,20 +77,30 @@ public sealed class BaseDataExcelController(AppDbContext db) : ControllerBase
} }
if (rows.Count == 0) return ValidationProblem("Excel 中没有可导入的数据。"); if (rows.Count == 0) return ValidationProblem("Excel 中没有可导入的数据。");
return await db.ExecuteInRetriableTransactionAsync<
ActionResult<ExcelImportResult>>(
async transaction =>
{
var errors = new List<string>(); var errors = new List<string>();
ExcelImportResult result; ExcelImportResult result;
await using var transaction = await db.Database.BeginTransactionAsync(cancellationToken);
try try
{ {
result = kind.ToLowerInvariant() switch result = kind.ToLowerInvariant() switch
{ {
"campuses" => await ImportCampusesAsync(rows, errors, cancellationToken), "campuses" => await ImportCampusesAsync(
"colleges" => await ImportCollegesAsync(rows, errors, cancellationToken), rows, errors, cancellationToken),
"majors" => await ImportMajorsAsync(rows, errors, cancellationToken), "colleges" => await ImportCollegesAsync(
"classes" => await ImportClassesAsync(rows, errors, cancellationToken), rows, errors, cancellationToken),
"terms" => await ImportTermsAsync(rows, errors, cancellationToken), "majors" => await ImportMajorsAsync(
"buildings" => await ImportBuildingsAsync(rows, errors, cancellationToken), rows, errors, cancellationToken),
"classrooms" => await ImportClassroomsAsync(rows, errors, cancellationToken), "classes" => await ImportClassesAsync(
rows, errors, cancellationToken),
"terms" => await ImportTermsAsync(
rows, errors, cancellationToken),
"buildings" => await ImportBuildingsAsync(
rows, errors, cancellationToken),
"classrooms" => await ImportClassroomsAsync(
rows, errors, cancellationToken),
"course-categories" => await ImportCourseCategoriesAsync( "course-categories" => await ImportCourseCategoriesAsync(
rows, errors, cancellationToken), rows, errors, cancellationToken),
_ => throw new InvalidOperationException() _ => throw new InvalidOperationException()
@@ -116,6 +126,8 @@ public sealed class BaseDataExcelController(AppDbContext db) : ControllerBase
Status = StatusCodes.Status409Conflict Status = StatusCodes.Status409Conflict
}); });
} }
},
cancellationToken);
} }
private async Task<IReadOnlyList<IReadOnlyList<object?>>> GetExportRowsAsync( private async Task<IReadOnlyList<IReadOnlyList<object?>>> GetExportRowsAsync(
@@ -256,7 +268,9 @@ public sealed class BaseDataExcelController(AppDbContext db) : ControllerBase
{ {
entity = new Major entity = new Major
{ {
Code = code, Name = name, CollegeId = college.Id, Code = code,
Name = name,
CollegeId = college.Id,
DegreeType = degreeType DegreeType = degreeType
}; };
db.Majors.Add(entity); db.Majors.Add(entity);
@@ -316,7 +330,10 @@ public sealed class BaseDataExcelController(AppDbContext db) : ControllerBase
{ {
entity = new AdministrativeClass entity = new AdministrativeClass
{ {
Code = code, Name = name, MajorId = major.Id, Grade = grade.Value Code = code,
Name = name,
MajorId = major.Id,
Grade = grade.Value
}; };
db.AdministrativeClasses.Add(entity); db.AdministrativeClasses.Add(entity);
existing[code] = entity; existing[code] = entity;
@@ -362,8 +379,11 @@ public sealed class BaseDataExcelController(AppDbContext db) : ControllerBase
{ {
entity = new AcademicTerm entity = new AcademicTerm
{ {
Code = code, Name = name, AcademicYear = academicYear, Code = code,
Season = season.Value, StartDate = startDate.Value, Name = name,
AcademicYear = academicYear,
Season = season.Value,
StartDate = startDate.Value,
EndDate = endDate.Value EndDate = endDate.Value
}; };
db.AcademicTerms.Add(entity); db.AcademicTerms.Add(entity);
@@ -461,7 +481,9 @@ public sealed class BaseDataExcelController(AppDbContext db) : ControllerBase
{ {
entity = new Classroom entity = new Classroom
{ {
Code = code, Name = name, BuildingId = building.Id, Code = code,
Name = name,
BuildingId = building.Id,
RoomType = roomType RoomType = roomType
}; };
db.Classrooms.Add(entity); db.Classrooms.Add(entity);
@@ -369,12 +369,12 @@ public sealed class CourseSelectionsController(
var source = db.Students.AsNoTracking() var source = db.Students.AsNoTracking()
.Where(x => .Where(x =>
x.Status == StudentStatus.Active && x.Status == StudentStatus.Active &&
(offering.IsOpenToAll ||
offering.ClassIds.Contains(x.AdministrativeClassId)) &&
!db.CourseEnrollments.Any(enrollment => !db.CourseEnrollments.Any(enrollment =>
enrollment.CourseSelectionOfferingId == id && enrollment.CourseSelectionOfferingId == id &&
enrollment.StudentId == x.Id && enrollment.StudentId == x.Id &&
enrollment.Status == CourseEnrollmentStatus.Enrolled)); enrollment.Status == CourseEnrollmentStatus.Enrolled));
if (!offering.IsOpenToAll)
source = source.WhereIn(offering.ClassIds, x => x.AdministrativeClassId);
if (!string.IsNullOrWhiteSpace(keyword)) if (!string.IsNullOrWhiteSpace(keyword))
{ {
keyword = keyword.Trim(); keyword = keyword.Trim();
@@ -415,9 +415,10 @@ public sealed class CourseSelectionsController(
if (studentIds.Length > 100) if (studentIds.Length > 100)
return ValidationProblem("单次最多可为 100 名学生代选。"); return ValidationProblem("单次最多可为 100 名学生代选。");
await using var transaction = await db.Database.BeginTransactionAsync( return await db.ExecuteInRetriableTransactionAsync<ActionResult>(
IsolationLevel.Serializable, async transaction =>
cancellationToken); {
db.ChangeTracker.Clear();
var offering = await db.CourseSelectionOfferings var offering = await db.CourseSelectionOfferings
.Include(x => x.CourseSelectionRound) .Include(x => x.CourseSelectionRound)
.Include(x => x.TeachingTask) .Include(x => x.TeachingTask)
@@ -437,7 +438,7 @@ public sealed class CourseSelectionsController(
var students = await db.Students var students = await db.Students
.Include(x => x.AdministrativeClass) .Include(x => x.AdministrativeClass)
.Where(x => studentIds.Contains(x.Id)) .WhereIn(studentIds, x => x.Id)
.OrderBy(x => x.StudentNumber) .OrderBy(x => x.StudentNumber)
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
if (students.Count != studentIds.Length) if (students.Count != studentIds.Length)
@@ -456,9 +457,8 @@ public sealed class CourseSelectionsController(
} }
var existingEnrollments = await db.CourseEnrollments var existingEnrollments = await db.CourseEnrollments
.Where(x => .Where(x => x.CourseSelectionOfferingId == id)
x.CourseSelectionOfferingId == id && .WhereIn(studentIds, x => x.StudentId)
studentIds.Contains(x.StudentId))
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
var alreadyEnrolled = existingEnrollments.FirstOrDefault(x => var alreadyEnrolled = existingEnrollments.FirstOrDefault(x =>
x.Status == CourseEnrollmentStatus.Enrolled); x.Status == CourseEnrollmentStatus.Enrolled);
@@ -482,11 +482,11 @@ public sealed class CourseSelectionsController(
var duplicateStudentIds = await db.CourseEnrollments.AsNoTracking() var duplicateStudentIds = await db.CourseEnrollments.AsNoTracking()
.Where(x => .Where(x =>
studentIds.Contains(x.StudentId) &&
x.Status == CourseEnrollmentStatus.Enrolled && x.Status == CourseEnrollmentStatus.Enrolled &&
x.CourseSelectionOffering!.TeachingTask!.CourseId == task.CourseId && x.CourseSelectionOffering!.TeachingTask!.CourseId == task.CourseId &&
x.CourseSelectionOffering.CourseSelectionRound!.AcademicTermId == x.CourseSelectionOffering.CourseSelectionRound!.AcademicTermId ==
round.AcademicTermId) round.AcademicTermId)
.WhereIn(studentIds, x => x.StudentId)
.Select(x => x.StudentId) .Select(x => x.StudentId)
.Distinct() .Distinct()
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
@@ -566,6 +566,9 @@ public sealed class CourseSelectionsController(
await db.SaveChangesAsync(cancellationToken); await db.SaveChangesAsync(cancellationToken);
await transaction.CommitAsync(cancellationToken); await transaction.CommitAsync(cancellationToken);
return Ok(new { EnrolledCount = students.Count }); return Ok(new { EnrolledCount = students.Count });
},
cancellationToken,
IsolationLevel.Serializable);
} }
[HttpPost("offerings/{offeringId:guid}/force-enroll")] [HttpPost("offerings/{offeringId:guid}/force-enroll")]
@@ -581,8 +584,10 @@ public sealed class CourseSelectionsController(
if (studentIds.Length > 100) if (studentIds.Length > 100)
return ValidationProblem("单次最多可为 100 名学生强制选课。"); return ValidationProblem("单次最多可为 100 名学生强制选课。");
await using var transaction = await db.Database.BeginTransactionAsync( return await db.ExecuteInRetriableTransactionAsync<ActionResult>(
IsolationLevel.Serializable, cancellationToken); async transaction =>
{
db.ChangeTracker.Clear();
var offering = await db.CourseSelectionOfferings var offering = await db.CourseSelectionOfferings
.Include(x => x.CourseSelectionRound) .Include(x => x.CourseSelectionRound)
.Include(x => x.TeachingTask) .Include(x => x.TeachingTask)
@@ -599,16 +604,15 @@ public sealed class CourseSelectionsController(
var students = await db.Students var students = await db.Students
.Include(x => x.AdministrativeClass) .Include(x => x.AdministrativeClass)
.Where(x => studentIds.Contains(x.Id)) .WhereIn(studentIds, x => x.Id)
.OrderBy(x => x.StudentNumber) .OrderBy(x => x.StudentNumber)
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
if (students.Count != studentIds.Length) if (students.Count != studentIds.Length)
return ValidationProblem("存在无效的学生档案。"); return ValidationProblem("存在无效的学生档案。");
var existingEnrollments = await db.CourseEnrollments var existingEnrollments = await db.CourseEnrollments
.Where(x => .Where(x => x.CourseSelectionOfferingId == offeringId)
x.CourseSelectionOfferingId == offeringId && .WhereIn(studentIds, x => x.StudentId)
studentIds.Contains(x.StudentId))
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
var alreadyEnrolled = existingEnrollments var alreadyEnrolled = existingEnrollments
.FirstOrDefault(x => x.Status == CourseEnrollmentStatus.Enrolled); .FirstOrDefault(x => x.Status == CourseEnrollmentStatus.Enrolled);
@@ -648,6 +652,9 @@ public sealed class CourseSelectionsController(
await db.SaveChangesAsync(cancellationToken); await db.SaveChangesAsync(cancellationToken);
await transaction.CommitAsync(cancellationToken); await transaction.CommitAsync(cancellationToken);
return Ok(new { EnrolledCount = enrolled }); return Ok(new { EnrolledCount = enrolled });
},
cancellationToken,
IsolationLevel.Serializable);
} }
[HttpDelete("offerings/{offeringId:guid}/admin-enrollments/{enrollmentId:guid}")] [HttpDelete("offerings/{offeringId:guid}/admin-enrollments/{enrollmentId:guid}")]
@@ -824,14 +831,15 @@ public sealed class CourseSelectionsController(
StudentEnrollmentRequest request, StudentEnrollmentRequest request,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
return await db.ExecuteInRetriableTransactionAsync<ActionResult>(
async transaction =>
{
db.ChangeTracker.Clear();
var student = await CurrentStudentAsync(cancellationToken); var student = await CurrentStudentAsync(cancellationToken);
if (student is null) return ProfileNotFound(); if (student is null) return ProfileNotFound();
if (student.Status != StudentStatus.Active) if (student.Status != StudentStatus.Active)
return ConflictProblem("只有在籍学生可以选课。"); return ConflictProblem("只有在籍学生可以选课。");
await using var transaction = await db.Database.BeginTransactionAsync(
IsolationLevel.Serializable,
cancellationToken);
var offering = await db.CourseSelectionOfferings var offering = await db.CourseSelectionOfferings
.Include(x => x.CourseSelectionRound) .Include(x => x.CourseSelectionRound)
.Include(x => x.TeachingTask) .Include(x => x.TeachingTask)
@@ -963,6 +971,9 @@ public sealed class CourseSelectionsController(
await db.SaveChangesAsync(cancellationToken); await db.SaveChangesAsync(cancellationToken);
await transaction.CommitAsync(cancellationToken); await transaction.CommitAsync(cancellationToken);
return Created(string.Empty, new { existing.Id, IsRetake = isRetake }); return Created(string.Empty, new { existing.Id, IsRetake = isRetake });
},
cancellationToken,
IsolationLevel.Serializable);
} }
[HttpDelete("student/enrollments/{id:guid}")] [HttpDelete("student/enrollments/{id:guid}")]
@@ -1159,9 +1170,9 @@ public sealed class CourseSelectionsController(
if (taskIds.Count == 0) return []; if (taskIds.Count == 0) return [];
return await db.ScheduleEntries.AsNoTracking() return await db.ScheduleEntries.AsNoTracking()
.Where(x => .Where(x =>
taskIds.Contains(x.TeachingTaskId) &&
x.SchedulePlan!.AcademicTermId == academicTermId && x.SchedulePlan!.AcademicTermId == academicTermId &&
x.SchedulePlan.Status == SchedulePlanStatus.Published) x.SchedulePlan.Status == SchedulePlanStatus.Published)
.WhereIn(taskIds, x => x.TeachingTaskId)
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
} }
Loaded 3 of 31 files, more files were not shown because too many files have changed in this diff. Show more