选课批次:时间窗口、退课截止、学分上限、开放/关闭。

教学班投放:容量、班级范围、全校开放。
学生选退课:容量、重复课程、学分上限、课表冲突校验。
教学班实时名单。
校级、学院级、学生角色分级操作。
SQLite 本地增量升级及演示数据。
MySQL 正式 EF Core 迁移。
Vue 已编译进 wwwroot,单服务运行无需 npm run dev。
学生端和管理端均完成桌面、手机响应式检查。
This commit is contained in:
2026-07-24 15:17:26 +08:00 Unverified
parent bcc4d33bd5
commit 151a22554f
19 changed files with 3972 additions and 6 deletions
@@ -11,6 +11,7 @@ public sealed class DevelopmentSqliteMigrator(
private const string TeachingTasksMigration = "20260724_03_teaching_tasks";
private const string SchedulesMigration = "20260724_04_schedules";
private const string ClassCounselorMigration = "20260724_05_class_counselor";
private const string CourseSelectionMigration = "20260724_06_course_selection";
public async Task MigrateAsync(CancellationToken cancellationToken = default)
{
@@ -58,6 +59,10 @@ public sealed class DevelopmentSqliteMigrator(
? ClassCounselorStatements.Skip(1)
: ClassCounselorStatements,
cancellationToken);
await ApplyMigrationAsync(
CourseSelectionMigration,
CourseSelectionStatements,
cancellationToken);
}
private async Task ApplyMigrationAsync(
@@ -426,4 +431,79 @@ public sealed class DevelopmentSqliteMigrator(
ON "AdministrativeClasses" ("CounselorUserId");
"""
];
private static readonly string[] CourseSelectionStatements =
[
"""
CREATE TABLE IF NOT EXISTS "CourseSelectionRounds" (
"Id" TEXT NOT NULL CONSTRAINT "PK_CourseSelectionRounds" PRIMARY KEY,
"AcademicTermId" TEXT NOT NULL,
"Name" TEXT NOT NULL,
"StartsAt" TEXT NOT NULL,
"EndsAt" TEXT NOT NULL,
"WithdrawalEndsAt" TEXT NOT NULL,
"MaxCredits" TEXT NOT NULL,
"Status" INTEGER NOT NULL,
"Notes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
CONSTRAINT "FK_CourseSelectionRounds_AcademicTerms_AcademicTermId"
FOREIGN KEY ("AcademicTermId") REFERENCES "AcademicTerms" ("Id") ON DELETE RESTRICT
);
""",
"""
CREATE INDEX IF NOT EXISTS "IX_CourseSelectionRounds_AcademicTermId_Status"
ON "CourseSelectionRounds" ("AcademicTermId", "Status");
""",
"""
CREATE TABLE IF NOT EXISTS "CourseSelectionOfferings" (
"Id" TEXT NOT NULL CONSTRAINT "PK_CourseSelectionOfferings" PRIMARY KEY,
"CourseSelectionRoundId" TEXT NOT NULL,
"TeachingTaskId" TEXT NOT NULL,
"Capacity" INTEGER NOT NULL,
"IsOpenToAll" INTEGER NOT NULL,
"Notes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
CONSTRAINT "FK_CourseSelectionOfferings_CourseSelectionRounds_CourseSelectionRoundId"
FOREIGN KEY ("CourseSelectionRoundId") REFERENCES "CourseSelectionRounds" ("Id")
ON DELETE CASCADE,
CONSTRAINT "FK_CourseSelectionOfferings_TeachingTasks_TeachingTaskId"
FOREIGN KEY ("TeachingTaskId") REFERENCES "TeachingTasks" ("Id") ON DELETE RESTRICT
);
""",
"""
CREATE UNIQUE INDEX IF NOT EXISTS "IX_CourseSelectionOfferings_CourseSelectionRoundId_TeachingTaskId"
ON "CourseSelectionOfferings" ("CourseSelectionRoundId", "TeachingTaskId");
""",
"""
CREATE INDEX IF NOT EXISTS "IX_CourseSelectionOfferings_TeachingTaskId"
ON "CourseSelectionOfferings" ("TeachingTaskId");
""",
"""
CREATE TABLE IF NOT EXISTS "CourseEnrollments" (
"Id" TEXT NOT NULL CONSTRAINT "PK_CourseEnrollments" PRIMARY KEY,
"CourseSelectionOfferingId" TEXT NOT NULL,
"StudentId" TEXT NOT NULL,
"Status" INTEGER NOT NULL,
"EnrolledAt" TEXT NOT NULL,
"WithdrawnAt" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
CONSTRAINT "FK_CourseEnrollments_CourseSelectionOfferings_CourseSelectionOfferingId"
FOREIGN KEY ("CourseSelectionOfferingId") REFERENCES "CourseSelectionOfferings" ("Id")
ON DELETE CASCADE,
CONSTRAINT "FK_CourseEnrollments_Students_StudentId"
FOREIGN KEY ("StudentId") REFERENCES "Students" ("Id") ON DELETE RESTRICT
);
""",
"""
CREATE UNIQUE INDEX IF NOT EXISTS "IX_CourseEnrollments_CourseSelectionOfferingId_StudentId"
ON "CourseEnrollments" ("CourseSelectionOfferingId", "StudentId");
""",
"""
CREATE INDEX IF NOT EXISTS "IX_CourseEnrollments_StudentId_Status"
ON "CourseEnrollments" ("StudentId", "Status");
"""
];
}