补考增强

This commit is contained in:
2026-07-25 19:38:39 +08:00 Unverified
parent 44e90ae35f
commit 1da6543a38
7 changed files with 714 additions and 10 deletions
@@ -61,6 +61,7 @@ public sealed class AppDbContext(DbContextOptions<AppDbContext> options)
Set<MakeupExamSessionInvigilator>();
public DbSet<MakeupExamEnrollment> MakeupExamEnrollments =>
Set<MakeupExamEnrollment>();
public DbSet<MakeupExamAutoJob> MakeupExamAutoJobs => Set<MakeupExamAutoJob>();
public DbSet<CourseAdjustment> CourseAdjustments => Set<CourseAdjustment>();
public DbSet<CourseExemption> CourseExemptions => Set<CourseExemption>();
public DbSet<DeferredExam> DeferredExams => Set<DeferredExam>();
@@ -650,6 +651,14 @@ public sealed class AppDbContext(DbContextOptions<AppDbContext> options)
entity.HasOne(x => x.SourceDeferredExam).WithMany()
.HasForeignKey(x => x.SourceDeferredExamId).OnDelete(DeleteBehavior.SetNull);
});
builder.Entity<MakeupExamAutoJob>(entity =>
{
entity.Property(x => x.ErrorMessage).HasMaxLength(2000);
entity.HasIndex(x => new { x.MakeupExamPlanId, x.CreatedAt });
entity.HasIndex(x => new { x.Status, x.CreatedAt });
entity.HasOne(x => x.MakeupExamPlan).WithMany()
.HasForeignKey(x => x.MakeupExamPlanId).OnDelete(DeleteBehavior.Cascade);
});
builder.Entity<StudentStatusChange>(entity =>
{
entity.Property(x => x.Reason).HasMaxLength(1000);
@@ -306,6 +306,14 @@ public sealed class DevelopmentSqliteMigrator(
MakeupExamsMigration,
makeupExamsExist ? [] : MakeupExamStatements,
cancellationToken);
var makeupAutoJobsExist = await db.Database
.SqlQueryRaw<int>("SELECT COUNT(*) AS \"Value\" FROM sqlite_master WHERE type = 'table' AND name = 'MakeupExamAutoJobs'")
.AnyAsync(value => value > 0, cancellationToken);
await ApplyMigrationAsync(
MakeupExamAutoJobsMigration,
makeupAutoJobsExist ? [] : MakeupExamAutoJobStatements,
cancellationToken);
}
private async Task ApplyMigrationAsync(
@@ -1675,4 +1683,13 @@ public sealed class DevelopmentSqliteMigrator(
"""CREATE TABLE "MakeupExamEnrollments" ("MakeupExamSessionId" TEXT NOT NULL, "StudentId" TEXT NOT NULL, "Reason" INTEGER NOT NULL, "SourceGradeRecordId" TEXT NULL, "SourceDeferredExamId" TEXT NULL, "MakeupScore" TEXT NULL, CONSTRAINT "PK_MakeupExamEnrollments" PRIMARY KEY ("MakeupExamSessionId", "StudentId"), CONSTRAINT "FK_MakeupExamEnrollments_MakeupExamSessions_MakeupExamSessionId" FOREIGN KEY ("MakeupExamSessionId") REFERENCES "MakeupExamSessions" ("Id") ON DELETE CASCADE, CONSTRAINT "FK_MakeupExamEnrollments_Students_StudentId" FOREIGN KEY ("StudentId") REFERENCES "Students" ("Id") ON DELETE RESTRICT, CONSTRAINT "FK_MakeupExamEnrollments_GradeRecords_SourceGradeRecordId" FOREIGN KEY ("SourceGradeRecordId") REFERENCES "GradeRecords" ("Id") ON DELETE SET NULL, CONSTRAINT "FK_MakeupExamEnrollments_DeferredExams_SourceDeferredExamId" FOREIGN KEY ("SourceDeferredExamId") REFERENCES "DeferredExams" ("Id") ON DELETE SET NULL);""",
"""CREATE INDEX "IX_MakeupExamEnrollments_StudentId_MakeupExamSessionId" ON "MakeupExamEnrollments" ("StudentId", "MakeupExamSessionId");""",
];
private const string MakeupExamAutoJobsMigration = "MakeupExamAutoJobs";
private static readonly string[] MakeupExamAutoJobStatements =
[
"""CREATE TABLE "MakeupExamAutoJobs" ("Id" TEXT NOT NULL, "CreatedAt" TEXT NOT NULL, "UpdatedAt" TEXT NOT NULL, "MakeupExamPlanId" TEXT NOT NULL, "Status" INTEGER NOT NULL, "TotalCourses" INTEGER NOT NULL, "ProcessedCourses" INTEGER NOT NULL, "CreatedSessions" INTEGER NOT NULL, "EnrolledStudents" INTEGER NOT NULL, "MessagesJson" TEXT NULL, "ErrorMessage" TEXT NULL, "StartedAt" TEXT NULL, "CompletedAt" TEXT NULL, CONSTRAINT "PK_MakeupExamAutoJobs" PRIMARY KEY ("Id"), CONSTRAINT "FK_MakeupExamAutoJobs_MakeupExamPlans_MakeupExamPlanId" FOREIGN KEY ("MakeupExamPlanId") REFERENCES "MakeupExamPlans" ("Id") ON DELETE CASCADE);""",
"""CREATE INDEX "IX_MakeupExamAutoJobs_MakeupExamPlanId_CreatedAt" ON "MakeupExamAutoJobs" ("MakeupExamPlanId", "CreatedAt");""",
"""CREATE INDEX "IX_MakeupExamAutoJobs_Status_CreatedAt" ON "MakeupExamAutoJobs" ("Status", "CreatedAt");""",
];
}