生产环境
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
|
||||
namespace Jiaowu.Api.Infrastructure.Persistence;
|
||||
|
||||
public sealed class AppDbContextFactory : IDesignTimeDbContextFactory<AppDbContext>
|
||||
{
|
||||
public AppDbContext CreateDbContext(string[] args)
|
||||
{
|
||||
var connectionString =
|
||||
Environment.GetEnvironmentVariable("ConnectionStrings__MySql")
|
||||
?? "Server=localhost;Port=3306;Database=jiaowu;User=__design_time__;Password=__not_used__;";
|
||||
|
||||
var options = new DbContextOptionsBuilder<AppDbContext>()
|
||||
.UseMySQL(connectionString)
|
||||
.Options;
|
||||
|
||||
return new AppDbContext(options);
|
||||
}
|
||||
}
|
||||
@@ -10,31 +10,49 @@ public sealed class DatabaseInitializer(
|
||||
RoleManager<ApplicationRole> roleManager,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
DevelopmentSqliteMigrator sqliteMigrator,
|
||||
DevelopmentDemoDataSeeder developmentDemoDataSeeder,
|
||||
DatabaseOptions databaseOptions,
|
||||
IConfiguration configuration,
|
||||
IHostEnvironment environment,
|
||||
ILogger<DatabaseInitializer> logger)
|
||||
{
|
||||
public async Task InitializeAsync()
|
||||
public async Task InitializeAsync(bool migrateOnly = false)
|
||||
{
|
||||
if (environment.IsDevelopment())
|
||||
{
|
||||
if (migrateOnly)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"--migrate-only 仅用于非 Development 环境的 MySQL 数据库。");
|
||||
}
|
||||
|
||||
await db.Database.EnsureCreatedAsync();
|
||||
await sqliteMigrator.MigrateAsync();
|
||||
}
|
||||
else
|
||||
else if (migrateOnly || databaseOptions.ApplyMigrationsOnStartup)
|
||||
{
|
||||
await db.Database.MigrateAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
var pendingMigrations = (await db.Database.GetPendingMigrationsAsync()).ToArray();
|
||||
if (pendingMigrations.Length > 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"数据库还有 {pendingMigrations.Length} 个待执行迁移。请在发布前运行 " +
|
||||
"'Jiaowu.Api --migrate-only',或显式配置 " +
|
||||
"Database:ApplyMigrationsOnStartup=true。");
|
||||
}
|
||||
}
|
||||
|
||||
if (migrateOnly)
|
||||
{
|
||||
logger.LogInformation("数据库迁移已完成。");
|
||||
return;
|
||||
}
|
||||
|
||||
await SeedRolesAsync();
|
||||
await SeedAdministratorAsync();
|
||||
await SeedCourseCategoriesAsync();
|
||||
|
||||
if (environment.IsDevelopment())
|
||||
{
|
||||
await SeedDevelopmentDataAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SeedRolesAsync()
|
||||
@@ -131,7 +149,11 @@ public sealed class DatabaseInitializer(
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var (code, name, sortOrder) in defaults)
|
||||
{
|
||||
if (existingCodes.Contains(code)) continue;
|
||||
if (existingCodes.Contains(code))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
db.CourseCategories.Add(new CourseCategory
|
||||
{
|
||||
Code = code,
|
||||
@@ -159,523 +181,6 @@ public sealed class DatabaseInitializer(
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private async Task SeedDevelopmentDataAsync()
|
||||
{
|
||||
if (!await db.Campuses.AnyAsync())
|
||||
{
|
||||
var campus = new Campus
|
||||
{
|
||||
Code = "MAIN",
|
||||
Name = "主校区",
|
||||
Address = "大学路 1 号"
|
||||
};
|
||||
var college = new College
|
||||
{
|
||||
Code = "CS",
|
||||
Name = "计算机学院",
|
||||
ShortName = "计算机学院",
|
||||
CampusId = campus.Id
|
||||
};
|
||||
var major = new Major
|
||||
{
|
||||
Code = "080901",
|
||||
Name = "计算机科学与技术",
|
||||
CollegeId = college.Id,
|
||||
DegreeType = "工学学士",
|
||||
SchoolingYears = 4
|
||||
};
|
||||
var building = new Building
|
||||
{
|
||||
Code = "J1",
|
||||
Name = "第一教学楼",
|
||||
CampusId = campus.Id
|
||||
};
|
||||
|
||||
db.AddRange(
|
||||
campus,
|
||||
college,
|
||||
major,
|
||||
new AdministrativeClass
|
||||
{
|
||||
Code = "CS2026-01",
|
||||
Name = "计科 2026-1 班",
|
||||
MajorId = major.Id,
|
||||
Grade = 2026,
|
||||
CounselorName = "陈老师"
|
||||
},
|
||||
building,
|
||||
new Classroom
|
||||
{
|
||||
Code = "J1-201",
|
||||
Name = "J1-201",
|
||||
BuildingId = building.Id,
|
||||
Capacity = 60,
|
||||
RoomType = "多媒体教室",
|
||||
Equipment = "投影、扩声、录播"
|
||||
},
|
||||
new AcademicTerm
|
||||
{
|
||||
Code = "2026-2027-1",
|
||||
Name = "2026—2027 学年第一学期",
|
||||
AcademicYear = "2026-2027",
|
||||
Season = TermSeason.Autumn,
|
||||
StartDate = new DateOnly(2026, 9, 7),
|
||||
EndDate = new DateOnly(2027, 1, 17),
|
||||
IsCurrent = true
|
||||
});
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
var computerCollege = await db.Colleges.SingleAsync(x => x.Code == "CS");
|
||||
var computerClass = await db.AdministrativeClasses
|
||||
.SingleAsync(x => x.Code == "CS2026-01");
|
||||
|
||||
if (!await db.Teachers.AnyAsync())
|
||||
{
|
||||
db.Teachers.AddRange(
|
||||
new Teacher
|
||||
{
|
||||
TeacherNumber = "T2026001",
|
||||
Name = "陈明远",
|
||||
Gender = Gender.Male,
|
||||
CollegeId = computerCollege.Id,
|
||||
Title = "副教授",
|
||||
Status = TeacherStatus.Active,
|
||||
HireDate = new DateOnly(2018, 7, 1),
|
||||
Email = "chenmy@example.edu.cn"
|
||||
},
|
||||
new Teacher
|
||||
{
|
||||
TeacherNumber = "T2026002",
|
||||
Name = "林书雅",
|
||||
Gender = Gender.Female,
|
||||
CollegeId = computerCollege.Id,
|
||||
Title = "讲师",
|
||||
Status = TeacherStatus.Active,
|
||||
HireDate = new DateOnly(2022, 9, 1),
|
||||
Email = "linsy@example.edu.cn"
|
||||
});
|
||||
}
|
||||
|
||||
if (!await db.Students.AnyAsync())
|
||||
{
|
||||
db.Students.AddRange(
|
||||
new Student
|
||||
{
|
||||
StudentNumber = "202601001",
|
||||
Name = "周启航",
|
||||
Gender = Gender.Male,
|
||||
AdministrativeClassId = computerClass.Id,
|
||||
EnrollmentYear = 2026,
|
||||
EnrollmentDate = new DateOnly(2026, 9, 7),
|
||||
Status = StudentStatus.Active
|
||||
},
|
||||
new Student
|
||||
{
|
||||
StudentNumber = "202601002",
|
||||
Name = "许知夏",
|
||||
Gender = Gender.Female,
|
||||
AdministrativeClassId = computerClass.Id,
|
||||
EnrollmentYear = 2026,
|
||||
EnrollmentDate = new DateOnly(2026, 9, 7),
|
||||
Status = StudentStatus.Active
|
||||
},
|
||||
new Student
|
||||
{
|
||||
StudentNumber = "202601003",
|
||||
Name = "方嘉树",
|
||||
Gender = Gender.Male,
|
||||
AdministrativeClassId = computerClass.Id,
|
||||
EnrollmentYear = 2026,
|
||||
EnrollmentDate = new DateOnly(2026, 9, 7),
|
||||
Status = StudentStatus.Active
|
||||
});
|
||||
}
|
||||
|
||||
var courseCategories = await db.CourseCategories
|
||||
.ToDictionaryAsync(x => x.Code, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
if (!await db.Courses.AnyAsync())
|
||||
{
|
||||
db.Courses.AddRange(
|
||||
new Course
|
||||
{
|
||||
Code = "CS101",
|
||||
Name = "程序设计基础",
|
||||
EnglishName = "Fundamentals of Programming",
|
||||
CollegeId = computerCollege.Id,
|
||||
CourseCategoryId = courseCategories["BASIC"].Id,
|
||||
Credits = 4,
|
||||
TotalHours = 64,
|
||||
LectureHours = 40,
|
||||
PracticeHours = 24,
|
||||
Nature = CourseNature.MajorRequired,
|
||||
AssessmentMethod = AssessmentMethod.Examination,
|
||||
Description = "面向一年级学生的程序设计入门课程。"
|
||||
},
|
||||
new Course
|
||||
{
|
||||
Code = "CS201",
|
||||
Name = "数据结构",
|
||||
EnglishName = "Data Structures",
|
||||
CollegeId = computerCollege.Id,
|
||||
CourseCategoryId = courseCategories["BASIC"].Id,
|
||||
Credits = 3.5m,
|
||||
TotalHours = 56,
|
||||
LectureHours = 40,
|
||||
PracticeHours = 16,
|
||||
Nature = CourseNature.MajorRequired,
|
||||
AssessmentMethod = AssessmentMethod.Examination
|
||||
},
|
||||
new Course
|
||||
{
|
||||
Code = "CS305",
|
||||
Name = "软件工程实践",
|
||||
EnglishName = "Software Engineering Practice",
|
||||
CollegeId = computerCollege.Id,
|
||||
CourseCategoryId = courseCategories["PRACTICE"].Id,
|
||||
Credits = 2,
|
||||
TotalHours = 48,
|
||||
LectureHours = 8,
|
||||
PracticeHours = 40,
|
||||
Nature = CourseNature.Practice,
|
||||
AssessmentMethod = AssessmentMethod.Assessment
|
||||
});
|
||||
}
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
if (!await db.CurriculumPlans.AnyAsync())
|
||||
{
|
||||
var seededCourses = await db.Courses
|
||||
.Where(x => x.Code == "CS101" || x.Code == "CS201" || x.Code == "CS305")
|
||||
.ToDictionaryAsync(x => x.Code);
|
||||
if (seededCourses.Count == 3)
|
||||
{
|
||||
db.CurriculumPlans.Add(new CurriculumPlan
|
||||
{
|
||||
MajorId = (await db.Majors.SingleAsync(x => x.Code == "080901")).Id,
|
||||
Name = "计算机科学与技术专业培养方案",
|
||||
Version = "2026版",
|
||||
EffectiveGrade = 2026,
|
||||
TotalCredits = 9.5m,
|
||||
Status = CurriculumPlanStatus.Published,
|
||||
PublishedAt = DateTime.UtcNow,
|
||||
Description = "用于本地开发的精简培养方案示例。",
|
||||
Modules =
|
||||
[
|
||||
new CurriculumModule
|
||||
{
|
||||
Code = "BASIC",
|
||||
Name = "专业基础课程",
|
||||
RequiredCredits = 7.5m,
|
||||
SortOrder = 10,
|
||||
Courses =
|
||||
[
|
||||
new CurriculumCourse
|
||||
{
|
||||
CourseId = seededCourses["CS101"].Id,
|
||||
RecommendedSemester = 1,
|
||||
Type = CurriculumCourseType.Required
|
||||
},
|
||||
new CurriculumCourse
|
||||
{
|
||||
CourseId = seededCourses["CS201"].Id,
|
||||
RecommendedSemester = 3,
|
||||
Type = CurriculumCourseType.Required
|
||||
}
|
||||
]
|
||||
},
|
||||
new CurriculumModule
|
||||
{
|
||||
Code = "PRACTICE",
|
||||
Name = "实践教学",
|
||||
RequiredCredits = 2m,
|
||||
SortOrder = 20,
|
||||
Courses =
|
||||
[
|
||||
new CurriculumCourse
|
||||
{
|
||||
CourseId = seededCourses["CS305"].Id,
|
||||
RecommendedSemester = 5,
|
||||
Type = CurriculumCourseType.Required
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
if (!await db.TeachingTasks.AnyAsync())
|
||||
{
|
||||
var term = await db.AcademicTerms.SingleAsync(x => x.IsCurrent);
|
||||
var course = await db.Courses.SingleAsync(x => x.Code == "CS101");
|
||||
var teacher = await db.Teachers.SingleAsync(x => x.TeacherNumber == "T2026001");
|
||||
var administrativeClass = await db.AdministrativeClasses
|
||||
.SingleAsync(x => x.Code == "CS2026-01");
|
||||
db.TeachingTasks.Add(new TeachingTask
|
||||
{
|
||||
TaskNumber = "2026-1-CS101-01",
|
||||
Name = "程序设计基础教学班 01",
|
||||
AcademicTermId = term.Id,
|
||||
CourseId = course.Id,
|
||||
Capacity = 60,
|
||||
StartWeek = 1,
|
||||
EndWeek = 16,
|
||||
WeeklyHours = 4,
|
||||
Status = TeachingTaskStatus.Published,
|
||||
PublishedAt = DateTime.UtcNow,
|
||||
Teachers =
|
||||
[
|
||||
new TeachingTaskTeacher
|
||||
{
|
||||
TeacherId = teacher.Id,
|
||||
IsPrimary = true
|
||||
}
|
||||
],
|
||||
Classes =
|
||||
[
|
||||
new TeachingTaskClass
|
||||
{
|
||||
AdministrativeClassId = administrativeClass.Id
|
||||
}
|
||||
]
|
||||
});
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
if (!await db.SchedulePlans.AnyAsync())
|
||||
{
|
||||
var term = await db.AcademicTerms.SingleAsync(x => x.IsCurrent);
|
||||
var task = await db.TeachingTasks.SingleAsync(x => x.TaskNumber == "2026-1-CS101-01");
|
||||
var classroom = await db.Classrooms.SingleAsync(x => x.Code == "J1-201");
|
||||
db.SchedulePlans.Add(new SchedulePlan
|
||||
{
|
||||
AcademicTermId = term.Id,
|
||||
Name = "2026—2027 学年第一学期正式课表",
|
||||
Version = "V1",
|
||||
Status = SchedulePlanStatus.Published,
|
||||
PublishedAt = DateTime.UtcNow,
|
||||
Entries =
|
||||
[
|
||||
new ScheduleEntry
|
||||
{
|
||||
TeachingTaskId = task.Id,
|
||||
ClassroomId = classroom.Id,
|
||||
DayOfWeek = 1,
|
||||
StartPeriod = 1,
|
||||
PeriodCount = 2,
|
||||
StartWeek = 1,
|
||||
EndWeek = 16,
|
||||
WeekPattern = WeekPattern.All
|
||||
}
|
||||
]
|
||||
});
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
await SeedDevelopmentUsersAsync(computerCollege.Id);
|
||||
await SeedDevelopmentCourseSelectionAsync();
|
||||
await SeedDevelopmentGradesAsync();
|
||||
await SeedDevelopmentExamsAsync();
|
||||
await developmentDemoDataSeeder.SeedAsync();
|
||||
}
|
||||
|
||||
private async Task SeedDevelopmentCourseSelectionAsync()
|
||||
{
|
||||
if (!await db.CourseSelectionRounds.AnyAsync())
|
||||
{
|
||||
var term = await db.AcademicTerms.SingleAsync(x => x.IsCurrent);
|
||||
var task = await db.TeachingTasks.SingleAsync(
|
||||
x => x.TaskNumber == "2026-1-CS101-01");
|
||||
var now = DateTime.UtcNow;
|
||||
db.CourseSelectionRounds.Add(new CourseSelectionRound
|
||||
{
|
||||
AcademicTermId = term.Id,
|
||||
Name = "2026—2027 学年第一学期第一轮选课",
|
||||
StartsAt = now.AddDays(-2),
|
||||
EndsAt = now.AddDays(14),
|
||||
WithdrawalEndsAt = now.AddDays(21),
|
||||
MaxCredits = 30,
|
||||
Status = CourseSelectionRoundStatus.Open,
|
||||
Notes = "本地开发演示轮次,可用于验证选课、退课和教学班名单。",
|
||||
Offerings =
|
||||
[
|
||||
new CourseSelectionOffering
|
||||
{
|
||||
TeachingTaskId = task.Id,
|
||||
Capacity = 60,
|
||||
IsOpenToAll = false,
|
||||
Notes = "面向计科 2026-1 班开放。"
|
||||
}
|
||||
]
|
||||
});
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
var seededTask = await db.TeachingTasks.SingleAsync(
|
||||
x => x.TaskNumber == "2026-1-CS101-01");
|
||||
var offering = await db.CourseSelectionOfferings.FirstOrDefaultAsync(
|
||||
x => x.TeachingTaskId == seededTask.Id);
|
||||
var student = await db.Students.SingleAsync(x =>
|
||||
x.StudentNumber == "202601001");
|
||||
if (offering is not null &&
|
||||
!await db.CourseEnrollments.AnyAsync(x =>
|
||||
x.CourseSelectionOfferingId == offering.Id &&
|
||||
x.StudentId == student.Id))
|
||||
{
|
||||
db.CourseEnrollments.Add(new CourseEnrollment
|
||||
{
|
||||
CourseSelectionOfferingId = offering.Id,
|
||||
StudentId = student.Id
|
||||
});
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SeedDevelopmentGradesAsync()
|
||||
{
|
||||
if (await db.GradeSheets.AnyAsync()) return;
|
||||
var task = await db.TeachingTasks.SingleAsync(
|
||||
x => x.TaskNumber == "2026-1-CS101-01");
|
||||
var student = await db.Students.SingleAsync(
|
||||
x => x.StudentNumber == "202601001");
|
||||
db.GradeSheets.Add(new GradeSheet
|
||||
{
|
||||
TeachingTaskId = task.Id,
|
||||
RegularWeight = 30,
|
||||
FinalWeight = 70,
|
||||
Status = GradeSheetStatus.Draft,
|
||||
Records =
|
||||
[
|
||||
new GradeRecord
|
||||
{
|
||||
StudentId = student.Id,
|
||||
RegularScore = 88,
|
||||
FinalScore = 92,
|
||||
TotalScore = 90.8m,
|
||||
GradePoint = 4.0m
|
||||
}
|
||||
]
|
||||
});
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private async Task SeedDevelopmentExamsAsync()
|
||||
{
|
||||
if (await db.ExamPlans.AnyAsync()) return;
|
||||
var term = await db.AcademicTerms.SingleAsync(x => x.IsCurrent);
|
||||
var task = await db.TeachingTasks.SingleAsync(x => x.TaskNumber == "2026-1-CS101-01");
|
||||
var room = await db.Classrooms.SingleAsync(x => x.Code == "J1-201");
|
||||
var teacher = await db.Teachers.SingleAsync(x => x.TeacherNumber == "T2026001");
|
||||
db.ExamPlans.Add(new ExamPlan
|
||||
{
|
||||
AcademicTermId = term.Id,
|
||||
Name = "2026—2027 学年第一学期期末考试",
|
||||
Status = ExamPlanStatus.Published,
|
||||
PublishedAt = DateTime.UtcNow,
|
||||
Notes = "本地开发演示考试计划。",
|
||||
Sessions =
|
||||
[
|
||||
new ExamSession
|
||||
{
|
||||
TeachingTaskId = task.Id,
|
||||
ClassroomId = room.Id,
|
||||
ExamDate = new DateOnly(2027, 1, 8),
|
||||
StartPeriod = 1,
|
||||
PeriodCount = 2,
|
||||
StartsAt = new DateTime(2027, 1, 8, 9, 0, 0, DateTimeKind.Utc),
|
||||
EndsAt = new DateTime(2027, 1, 8, 11, 0, 0, DateTimeKind.Utc),
|
||||
RequiredInvigilatorCount = 2,
|
||||
Invigilators =
|
||||
[
|
||||
new ExamSessionInvigilator { TeacherId = teacher.Id }
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private async Task SeedDevelopmentUsersAsync(Guid collegeId)
|
||||
{
|
||||
var definitions = new[]
|
||||
{
|
||||
new DevelopmentUser(
|
||||
"academic", "校级教务员", "Academic@123456",
|
||||
null, null, SystemRoles.AcademicAdmin),
|
||||
new DevelopmentUser(
|
||||
"college", "计算机学院教务员", "College@123456",
|
||||
"A2026001", collegeId, SystemRoles.CollegeAdmin),
|
||||
new DevelopmentUser(
|
||||
"counselor", "陈老师", "Counselor@123456",
|
||||
"C2026001", collegeId, SystemRoles.Counselor),
|
||||
new DevelopmentUser(
|
||||
"teacher", "陈明远", "Teacher@123456",
|
||||
"T2026001", collegeId, SystemRoles.Teacher),
|
||||
new DevelopmentUser(
|
||||
"student", "周启航", "Student@123456",
|
||||
"202601001", collegeId, SystemRoles.Student),
|
||||
new DevelopmentUser(
|
||||
"leader", "教学分管领导", "Leader@123456",
|
||||
null, null, SystemRoles.Leader)
|
||||
};
|
||||
|
||||
foreach (var definition in definitions)
|
||||
{
|
||||
var user = await userManager.FindByNameAsync(definition.UserName);
|
||||
if (user is null)
|
||||
{
|
||||
user = new ApplicationUser
|
||||
{
|
||||
UserName = definition.UserName,
|
||||
DisplayName = definition.DisplayName,
|
||||
StaffNumber = definition.StaffNumber,
|
||||
CollegeId = definition.CollegeId,
|
||||
LockoutEnabled = true,
|
||||
IsEnabled = true
|
||||
};
|
||||
EnsureSucceeded(
|
||||
await userManager.CreateAsync(user, definition.Password),
|
||||
$"创建开发账号 {definition.UserName}");
|
||||
}
|
||||
|
||||
if (!await userManager.IsInRoleAsync(user, definition.Role))
|
||||
{
|
||||
EnsureSucceeded(
|
||||
await userManager.AddToRoleAsync(user, definition.Role),
|
||||
$"授予开发账号 {definition.UserName} 角色");
|
||||
}
|
||||
|
||||
if (definition.Role == SystemRoles.Teacher)
|
||||
{
|
||||
var teacher = await db.Teachers.SingleAsync(
|
||||
x => x.TeacherNumber == definition.StaffNumber);
|
||||
if (!teacher.UserId.HasValue) teacher.UserId = user.Id;
|
||||
}
|
||||
if (definition.Role == SystemRoles.Student)
|
||||
{
|
||||
var student = await db.Students.SingleAsync(
|
||||
x => x.StudentNumber == definition.StaffNumber);
|
||||
if (!student.UserId.HasValue) student.UserId = user.Id;
|
||||
}
|
||||
if (definition.Role == SystemRoles.Counselor)
|
||||
{
|
||||
var classes = await db.AdministrativeClasses
|
||||
.Where(x =>
|
||||
x.CounselorUserId == null &&
|
||||
x.CounselorName == definition.DisplayName)
|
||||
.ToListAsync();
|
||||
foreach (var administrativeClass in classes)
|
||||
administrativeClass.CounselorUserId = user.Id;
|
||||
}
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private static void EnsureSucceeded(IdentityResult result, string action)
|
||||
{
|
||||
if (result.Succeeded)
|
||||
@@ -686,12 +191,4 @@ public sealed class DatabaseInitializer(
|
||||
throw new InvalidOperationException(
|
||||
$"{action}失败:{string.Join(";", result.Errors.Select(x => x.Description))}");
|
||||
}
|
||||
|
||||
private sealed record DevelopmentUser(
|
||||
string UserName,
|
||||
string DisplayName,
|
||||
string Password,
|
||||
string? StaffNumber,
|
||||
Guid? CollegeId,
|
||||
string Role);
|
||||
}
|
||||
|
||||
@@ -4,4 +4,6 @@ public sealed class DatabaseOptions
|
||||
{
|
||||
public const string SectionName = "Database";
|
||||
public string Provider { get; set; } = "MySql";
|
||||
public bool ApplyMigrationsOnStartup { get; set; }
|
||||
public int CommandTimeoutSeconds { get; set; } = 30;
|
||||
}
|
||||
|
||||
@@ -1,620 +0,0 @@
|
||||
using Jiaowu.Api.Domain.Academic;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Jiaowu.Api.Infrastructure.Persistence;
|
||||
|
||||
public sealed class DevelopmentDemoDataSeeder(
|
||||
AppDbContext db,
|
||||
ILogger<DevelopmentDemoDataSeeder> logger)
|
||||
{
|
||||
private const int Grade = 2026;
|
||||
private const int ClassesPerMajor = 2;
|
||||
private const int StudentsPerClass = 35;
|
||||
private const int TeachersPerCollege = 8;
|
||||
|
||||
public async Task SeedAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var campus = await db.Campuses
|
||||
.OrderBy(x => x.Code == "MAIN" ? 0 : 1)
|
||||
.ThenBy(x => x.Code)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (campus is null)
|
||||
{
|
||||
campus = new Campus
|
||||
{
|
||||
Code = "MAIN",
|
||||
Name = "主校区",
|
||||
Address = "大学路 1 号"
|
||||
};
|
||||
db.Campuses.Add(campus);
|
||||
await db.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
await SeedCollegesAsync(campus.Id, cancellationToken);
|
||||
await SeedMajorsAsync(cancellationToken);
|
||||
await SeedClassesAsync(cancellationToken);
|
||||
await SeedTeachersAsync(cancellationToken);
|
||||
await SeedStudentsAsync(cancellationToken);
|
||||
await SeedCoursesAsync(cancellationToken);
|
||||
await SeedTeacherCourseApplicationsAsync(cancellationToken);
|
||||
await LogSummaryAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private async Task SeedCollegesAsync(Guid campusId, CancellationToken cancellationToken)
|
||||
{
|
||||
var existingCodes = (await db.Colleges
|
||||
.Select(x => x.Code)
|
||||
.ToListAsync(cancellationToken))
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
var additions = CollegeDefinitions
|
||||
.Where(x => !existingCodes.Contains(x.Code))
|
||||
.Select((x, index) => new College
|
||||
{
|
||||
Code = x.Code,
|
||||
Name = x.Name,
|
||||
ShortName = x.ShortName,
|
||||
CampusId = campusId,
|
||||
SortOrder = (index + 1) * 10
|
||||
})
|
||||
.ToList();
|
||||
if (additions.Count == 0) return;
|
||||
db.Colleges.AddRange(additions);
|
||||
await db.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private async Task SeedMajorsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var colleges = await db.Colleges
|
||||
.ToDictionaryAsync(x => x.Code, StringComparer.OrdinalIgnoreCase, cancellationToken);
|
||||
var existingCodes = (await db.Majors
|
||||
.Select(x => x.Code)
|
||||
.ToListAsync(cancellationToken))
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
var additions = new List<Major>();
|
||||
foreach (var collegeDefinition in CollegeDefinitions)
|
||||
{
|
||||
if (!colleges.TryGetValue(collegeDefinition.Code, out var college)) continue;
|
||||
for (var index = 0; index < collegeDefinition.Majors.Count; index++)
|
||||
{
|
||||
var definition = collegeDefinition.Majors[index];
|
||||
if (existingCodes.Contains(definition.Code)) continue;
|
||||
additions.Add(new Major
|
||||
{
|
||||
Code = definition.Code,
|
||||
Name = definition.Name,
|
||||
CollegeId = college.Id,
|
||||
DegreeType = definition.DegreeType,
|
||||
SchoolingYears = definition.SchoolingYears,
|
||||
SortOrder = (index + 1) * 10
|
||||
});
|
||||
}
|
||||
}
|
||||
if (additions.Count == 0) return;
|
||||
db.Majors.AddRange(additions);
|
||||
await db.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private async Task SeedClassesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var targetMajorCodes = CollegeDefinitions
|
||||
.SelectMany(x => x.Majors)
|
||||
.Select(x => x.Code)
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
var majors = await db.Majors
|
||||
.Where(x => targetMajorCodes.Contains(x.Code))
|
||||
.OrderBy(x => x.Code)
|
||||
.ToListAsync(cancellationToken);
|
||||
var existingClasses = await db.AdministrativeClasses
|
||||
.Where(x => x.Grade == Grade && targetMajorCodes.Contains(x.Major!.Code))
|
||||
.OrderBy(x => x.Code)
|
||||
.ToListAsync(cancellationToken);
|
||||
var existingCodes = (await db.AdministrativeClasses
|
||||
.Select(x => x.Code)
|
||||
.ToListAsync(cancellationToken))
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
var additions = new List<AdministrativeClass>();
|
||||
foreach (var major in majors)
|
||||
{
|
||||
var currentCount = existingClasses.Count(x => x.MajorId == major.Id);
|
||||
for (var section = currentCount + 1; section <= ClassesPerMajor; section++)
|
||||
{
|
||||
var code = CreateAvailableCode(
|
||||
$"{major.Code}-{Grade}-{section:D2}",
|
||||
existingCodes);
|
||||
additions.Add(new AdministrativeClass
|
||||
{
|
||||
Code = code,
|
||||
Name = $"{major.Name}{Grade}级{section}班",
|
||||
MajorId = major.Id,
|
||||
Grade = Grade,
|
||||
CounselorName = $"{CounselorSurnames[(section + major.Code.Length) % CounselorSurnames.Length]}老师",
|
||||
SortOrder = section * 10
|
||||
});
|
||||
}
|
||||
}
|
||||
if (additions.Count == 0) return;
|
||||
db.AdministrativeClasses.AddRange(additions);
|
||||
await db.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private async Task SeedTeachersAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var collegeCodes = CollegeDefinitions
|
||||
.Select(x => x.Code)
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
var colleges = await db.Colleges
|
||||
.Where(x => collegeCodes.Contains(x.Code))
|
||||
.OrderBy(x => x.Code)
|
||||
.ToListAsync(cancellationToken);
|
||||
var existingTeachers = await db.Teachers
|
||||
.Where(x => colleges.Select(c => c.Id).Contains(x.CollegeId))
|
||||
.ToListAsync(cancellationToken);
|
||||
var existingNumbers = (await db.Teachers
|
||||
.Select(x => x.TeacherNumber)
|
||||
.ToListAsync(cancellationToken))
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
var additions = new List<Teacher>();
|
||||
for (var collegeIndex = 0; collegeIndex < colleges.Count; collegeIndex++)
|
||||
{
|
||||
var college = colleges[collegeIndex];
|
||||
var currentCount = existingTeachers.Count(x => x.CollegeId == college.Id);
|
||||
for (var position = currentCount + 1; position <= TeachersPerCollege; position++)
|
||||
{
|
||||
var teacherNumber = CreateAvailableCode(
|
||||
$"T26{collegeIndex + 1:D2}{position:D3}",
|
||||
existingNumbers);
|
||||
additions.Add(new Teacher
|
||||
{
|
||||
TeacherNumber = teacherNumber,
|
||||
Name = BuildPersonName(collegeIndex, position),
|
||||
Gender = position % 2 == 0 ? Gender.Female : Gender.Male,
|
||||
CollegeId = college.Id,
|
||||
Title = TeacherTitles[(position - 1) % TeacherTitles.Length],
|
||||
Status = TeacherStatus.Active,
|
||||
HireDate = new DateOnly(2012 + position, 7, 1),
|
||||
Email = $"{teacherNumber.ToLowerInvariant()}@example.edu.cn",
|
||||
Notes = "Development 环境批量测试教师。"
|
||||
});
|
||||
}
|
||||
}
|
||||
if (additions.Count == 0) return;
|
||||
db.Teachers.AddRange(additions);
|
||||
await db.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private async Task SeedStudentsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var targetMajorCodes = CollegeDefinitions
|
||||
.SelectMany(x => x.Majors)
|
||||
.Select(x => x.Code)
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
var classes = await db.AdministrativeClasses
|
||||
.Where(x => x.Grade == Grade && targetMajorCodes.Contains(x.Major!.Code))
|
||||
.OrderBy(x => x.Code)
|
||||
.ToListAsync(cancellationToken);
|
||||
var classIds = classes.Select(x => x.Id).ToHashSet();
|
||||
var existingStudents = await db.Students
|
||||
.Where(x => classIds.Contains(x.AdministrativeClassId))
|
||||
.ToListAsync(cancellationToken);
|
||||
var existingNumbers = (await db.Students
|
||||
.Select(x => x.StudentNumber)
|
||||
.ToListAsync(cancellationToken))
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
var additions = new List<Student>();
|
||||
for (var classIndex = 0; classIndex < classes.Count; classIndex++)
|
||||
{
|
||||
var administrativeClass = classes[classIndex];
|
||||
var existingCount = existingStudents.Count(x =>
|
||||
x.AdministrativeClassId == administrativeClass.Id);
|
||||
var candidate = 1;
|
||||
while (existingCount + additions.Count(x =>
|
||||
x.AdministrativeClassId == administrativeClass.Id) < StudentsPerClass)
|
||||
{
|
||||
var studentNumber = $"{Grade}{classIndex + 1:D3}{candidate:D3}";
|
||||
candidate++;
|
||||
if (!existingNumbers.Add(studentNumber)) continue;
|
||||
additions.Add(new Student
|
||||
{
|
||||
StudentNumber = studentNumber,
|
||||
Name = BuildPersonName(classIndex, candidate + 5),
|
||||
Gender = candidate % 2 == 0 ? Gender.Male : Gender.Female,
|
||||
AdministrativeClassId = administrativeClass.Id,
|
||||
EnrollmentYear = Grade,
|
||||
EnrollmentDate = new DateOnly(Grade, 9, 7),
|
||||
DateOfBirth = new DateOnly(2007 + candidate % 2, candidate % 12 + 1,
|
||||
candidate % 27 + 1),
|
||||
Status = StudentStatus.Active,
|
||||
Email = $"{studentNumber}@student.example.edu.cn",
|
||||
Notes = "Development 环境批量测试学生。"
|
||||
});
|
||||
}
|
||||
}
|
||||
if (additions.Count == 0) return;
|
||||
db.Students.AddRange(additions);
|
||||
await db.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private async Task SeedCoursesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var colleges = await db.Colleges
|
||||
.ToDictionaryAsync(x => x.Code, StringComparer.OrdinalIgnoreCase, cancellationToken);
|
||||
var categories = await db.CourseCategories
|
||||
.ToDictionaryAsync(x => x.Code, StringComparer.OrdinalIgnoreCase, cancellationToken);
|
||||
var existingCodes = (await db.Courses
|
||||
.Select(x => x.Code)
|
||||
.ToListAsync(cancellationToken))
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
var additions = new List<Course>();
|
||||
|
||||
foreach (var definition in PublicCourseDefinitions)
|
||||
{
|
||||
if (existingCodes.Contains(definition.Code) ||
|
||||
!colleges.TryGetValue(definition.CollegeCode, out var college) ||
|
||||
!categories.TryGetValue(definition.CategoryCode, out var category))
|
||||
continue;
|
||||
additions.Add(new Course
|
||||
{
|
||||
Code = definition.Code,
|
||||
Name = definition.Name,
|
||||
CollegeId = college.Id,
|
||||
CourseCategoryId = category.Id,
|
||||
Credits = definition.Credits,
|
||||
TotalHours = definition.TotalHours,
|
||||
LectureHours = definition.LectureHours,
|
||||
PracticeHours = definition.TotalHours - definition.LectureHours,
|
||||
Nature = definition.Nature,
|
||||
AssessmentMethod = definition.AssessmentMethod,
|
||||
Description = "Development 环境公共课程测试数据。"
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var collegeDefinition in CollegeDefinitions)
|
||||
{
|
||||
if (!colleges.TryGetValue(collegeDefinition.Code, out var college)) continue;
|
||||
for (var index = 0; index < collegeDefinition.ProfessionalCourses.Count; index++)
|
||||
{
|
||||
var code = $"{collegeDefinition.Code}D{index + 1:D2}";
|
||||
if (existingCodes.Contains(code)) continue;
|
||||
var nature = index switch
|
||||
{
|
||||
< 4 => CourseNature.MajorRequired,
|
||||
< 6 => CourseNature.MajorElective,
|
||||
_ => CourseNature.Practice
|
||||
};
|
||||
var categoryCode = nature == CourseNature.Practice ? "PRACTICE" : "MAJOR";
|
||||
additions.Add(new Course
|
||||
{
|
||||
Code = code,
|
||||
Name = collegeDefinition.ProfessionalCourses[index],
|
||||
CollegeId = college.Id,
|
||||
CourseCategoryId = categories[categoryCode].Id,
|
||||
Credits = nature switch
|
||||
{
|
||||
CourseNature.MajorRequired => index % 2 == 0 ? 3m : 3.5m,
|
||||
_ => 2m
|
||||
},
|
||||
TotalHours = nature switch
|
||||
{
|
||||
CourseNature.MajorRequired => index % 2 == 0 ? 48 : 56,
|
||||
CourseNature.MajorElective => 32,
|
||||
_ => 48
|
||||
},
|
||||
LectureHours = nature == CourseNature.Practice ? 8 :
|
||||
nature == CourseNature.MajorElective ? 24 :
|
||||
index % 2 == 0 ? 40 : 48,
|
||||
PracticeHours = nature == CourseNature.Practice ? 40 :
|
||||
nature == CourseNature.MajorElective ? 8 : 8,
|
||||
Nature = nature,
|
||||
AssessmentMethod = nature == CourseNature.Practice
|
||||
? AssessmentMethod.Assessment
|
||||
: AssessmentMethod.Examination,
|
||||
Description = $"由{collegeDefinition.Name}开设的专业课程测试数据。"
|
||||
});
|
||||
}
|
||||
}
|
||||
if (additions.Count == 0) return;
|
||||
db.Courses.AddRange(additions);
|
||||
await db.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private async Task SeedTeacherCourseApplicationsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var term = await db.AcademicTerms
|
||||
.OrderByDescending(x => x.IsCurrent)
|
||||
.ThenByDescending(x => x.StartDate)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (term is null) return;
|
||||
|
||||
var collegeCodes = CollegeDefinitions
|
||||
.Select(x => x.Code)
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
var colleges = await db.Colleges
|
||||
.Where(x => collegeCodes.Contains(x.Code))
|
||||
.OrderBy(x => x.Code)
|
||||
.ToListAsync(cancellationToken);
|
||||
var collegeIds = colleges.Select(x => x.Id).ToHashSet();
|
||||
var teachers = await db.Teachers
|
||||
.Where(x => collegeIds.Contains(x.CollegeId) && x.Status == TeacherStatus.Active)
|
||||
.OrderBy(x => x.TeacherNumber)
|
||||
.ToListAsync(cancellationToken);
|
||||
var courses = await db.Courses
|
||||
.Where(x => collegeIds.Contains(x.CollegeId))
|
||||
.OrderBy(x => x.Code)
|
||||
.ToListAsync(cancellationToken);
|
||||
var publicCourses = courses
|
||||
.Where(x => x.Nature is CourseNature.GeneralRequired or CourseNature.GeneralElective)
|
||||
.ToList();
|
||||
if (publicCourses.Count == 0) return;
|
||||
|
||||
var existing = (await db.TeacherCourseApplications
|
||||
.Where(x => x.AcademicTermId == term.Id)
|
||||
.Select(x => new { x.TeacherId, x.CourseId })
|
||||
.ToListAsync(cancellationToken))
|
||||
.Select(x => (x.TeacherId, x.CourseId))
|
||||
.ToHashSet();
|
||||
var additions = new List<TeacherCourseApplication>();
|
||||
for (var teacherIndex = 0; teacherIndex < teachers.Count; teacherIndex++)
|
||||
{
|
||||
var teacher = teachers[teacherIndex];
|
||||
var professionalCourses = courses
|
||||
.Where(x => x.CollegeId == teacher.CollegeId &&
|
||||
x.Nature is CourseNature.MajorRequired or
|
||||
CourseNature.MajorElective or CourseNature.Practice)
|
||||
.Take(4);
|
||||
var selectedPublicCourses = Enumerable.Range(0, 2)
|
||||
.Select(offset => publicCourses[(teacherIndex + offset) % publicCourses.Count]);
|
||||
foreach (var course in professionalCourses
|
||||
.Concat(selectedPublicCourses)
|
||||
.DistinctBy(x => x.Id))
|
||||
{
|
||||
if (!existing.Add((teacher.Id, course.Id))) continue;
|
||||
additions.Add(new TeacherCourseApplication
|
||||
{
|
||||
AcademicTermId = term.Id,
|
||||
TeacherId = teacher.Id,
|
||||
CourseId = course.Id,
|
||||
Status = TeacherCourseApplicationStatus.Approved,
|
||||
Statement = "Development 环境批量生成的授课意向。",
|
||||
ReviewComment = "测试数据自动审核通过。",
|
||||
SubmittedAt = DateTime.UtcNow.AddDays(-7),
|
||||
ReviewedAt = DateTime.UtcNow.AddDays(-6)
|
||||
});
|
||||
}
|
||||
}
|
||||
if (additions.Count == 0) return;
|
||||
db.TeacherCourseApplications.AddRange(additions);
|
||||
await db.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private async Task LogSummaryAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var courseCounts = await db.Courses
|
||||
.GroupBy(x => x.Nature)
|
||||
.Select(x => new { Nature = x.Key, Count = x.Count() })
|
||||
.ToDictionaryAsync(x => x.Nature, x => x.Count, cancellationToken);
|
||||
logger.LogInformation(
|
||||
"Development 测试数据已就绪:学院/教学单位 {CollegeCount},专业 {MajorCount}," +
|
||||
"行政班 {ClassCount},教师 {TeacherCount},学生 {StudentCount},课程 {CourseCount};" +
|
||||
"公共必修 {GeneralRequiredCount},公共选修 {GeneralElectiveCount}," +
|
||||
"专业必修 {MajorRequiredCount},专业选修 {MajorElectiveCount},实践课程 {PracticeCount};" +
|
||||
"已审核授课资格 {ApplicationCount}。",
|
||||
await db.Colleges.CountAsync(cancellationToken),
|
||||
await db.Majors.CountAsync(cancellationToken),
|
||||
await db.AdministrativeClasses.CountAsync(cancellationToken),
|
||||
await db.Teachers.CountAsync(cancellationToken),
|
||||
await db.Students.CountAsync(cancellationToken),
|
||||
await db.Courses.CountAsync(cancellationToken),
|
||||
courseCounts.GetValueOrDefault(CourseNature.GeneralRequired),
|
||||
courseCounts.GetValueOrDefault(CourseNature.GeneralElective),
|
||||
courseCounts.GetValueOrDefault(CourseNature.MajorRequired),
|
||||
courseCounts.GetValueOrDefault(CourseNature.MajorElective),
|
||||
courseCounts.GetValueOrDefault(CourseNature.Practice),
|
||||
await db.TeacherCourseApplications
|
||||
.CountAsync(x => x.Status == TeacherCourseApplicationStatus.Approved,
|
||||
cancellationToken));
|
||||
}
|
||||
|
||||
private static string CreateAvailableCode(string preferredCode, ISet<string> existingCodes)
|
||||
{
|
||||
var candidate = preferredCode;
|
||||
var suffix = 1;
|
||||
while (!existingCodes.Add(candidate))
|
||||
{
|
||||
candidate = $"{preferredCode}-{suffix++}";
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
|
||||
private static string BuildPersonName(int groupIndex, int position)
|
||||
{
|
||||
var surname = Surnames[(groupIndex + position) % Surnames.Length];
|
||||
var givenName = GivenNames[
|
||||
(groupIndex * 7 + position * 3) % GivenNames.Length];
|
||||
return surname + givenName;
|
||||
}
|
||||
|
||||
private static readonly string[] Surnames =
|
||||
["王", "李", "张", "刘", "陈", "杨", "黄", "赵", "吴", "周", "徐", "孙", "马", "朱", "胡", "郭", "何", "高", "林", "罗"];
|
||||
|
||||
private static readonly string[] GivenNames =
|
||||
["明远", "知夏", "嘉树", "雨桐", "思源", "若溪", "景行", "书雅", "子涵", "浩然", "清越", "语晨", "承宇", "欣怡", "博文", "婉宁", "俊逸", "安然", "泽楷", "可心"];
|
||||
|
||||
private static readonly string[] CounselorSurnames =
|
||||
["陈", "林", "周", "王", "李", "张", "刘", "赵"];
|
||||
|
||||
private static readonly string[] TeacherTitles =
|
||||
["教授", "副教授", "讲师", "讲师", "副教授", "实验师", "讲师", "教授"];
|
||||
|
||||
private static readonly CollegeSeed[] CollegeDefinitions =
|
||||
[
|
||||
new("CS", "计算机学院", "计算机学院",
|
||||
[
|
||||
new("080901", "计算机科学与技术", "工学学士"),
|
||||
new("080902", "软件工程", "工学学士"),
|
||||
new("080903", "网络工程", "工学学士"),
|
||||
new("080910T", "数据科学与大数据技术", "工学学士"),
|
||||
new("080717T", "人工智能", "工学学士")
|
||||
],
|
||||
["程序设计基础", "离散数学", "数据结构", "计算机组成原理", "操作系统", "数据库系统原理", "软件工程课程设计", "人工智能项目实践"]),
|
||||
new("EIA", "电子信息与自动化学院", "电子信息学院",
|
||||
[
|
||||
new("080701", "电子信息工程", "工学学士"),
|
||||
new("080703", "通信工程", "工学学士"),
|
||||
new("080801", "自动化", "工学学士"),
|
||||
new("080803T", "机器人工程", "工学学士")
|
||||
],
|
||||
["电路分析", "模拟电子技术", "数字电子技术", "信号与系统", "通信原理", "嵌入式系统", "电子系统设计", "综合电子实训"]),
|
||||
new("ME", "机械与车辆工程学院", "机械学院",
|
||||
[
|
||||
new("080202", "机械设计制造及其自动化", "工学学士"),
|
||||
new("080207", "车辆工程", "工学学士"),
|
||||
new("080205", "工业设计", "工学学士"),
|
||||
new("080213T", "智能制造工程", "工学学士")
|
||||
],
|
||||
["工程制图", "理论力学", "材料力学", "机械原理", "机械设计", "智能制造技术", "机械创新设计", "工程训练"]),
|
||||
new("EE", "电气工程学院", "电气学院",
|
||||
[
|
||||
new("080601", "电气工程及其自动化", "工学学士"),
|
||||
new("080604T", "电气工程与智能控制", "工学学士"),
|
||||
new("080605T", "电机电器智能化", "工学学士")
|
||||
],
|
||||
["电路原理", "电机学", "电力电子技术", "自动控制原理", "电力系统分析", "继电保护", "电气控制实训", "电力系统综合设计"]),
|
||||
new("CIVIL", "土木建筑工程学院", "土建学院",
|
||||
[
|
||||
new("081001", "土木工程", "工学学士"),
|
||||
new("082801", "建筑学", "建筑学学士", 5),
|
||||
new("120103", "工程管理", "管理学学士"),
|
||||
new("081006T", "道路桥梁与渡河工程", "工学学士")
|
||||
],
|
||||
["工程制图与识图", "工程力学", "结构力学", "混凝土结构", "土力学与地基基础", "工程项目管理", "建筑设计基础", "工程测量实习"]),
|
||||
new("ECON", "经济与管理学院", "经管学院",
|
||||
[
|
||||
new("120201K", "工商管理", "管理学学士"),
|
||||
new("120203K", "会计学", "管理学学士"),
|
||||
new("020301K", "金融学", "经济学学士"),
|
||||
new("020401", "国际经济与贸易", "经济学学士"),
|
||||
new("120202", "市场营销", "管理学学士")
|
||||
],
|
||||
["微观经济学", "宏观经济学", "管理学原理", "会计学原理", "统计学", "财务管理", "企业经营沙盘", "商务数据分析实践"]),
|
||||
new("FOREIGN", "外国语学院", "外国语学院",
|
||||
[
|
||||
new("050201", "英语", "文学学士"),
|
||||
new("050207", "日语", "文学学士"),
|
||||
new("050262", "商务英语", "文学学士")
|
||||
],
|
||||
["综合英语", "英语听力", "英语口语", "英语写作", "翻译理论与实践", "跨文化交际", "商务英语实训", "口译实践"]),
|
||||
new("MATH", "数学与统计学院", "数统学院",
|
||||
[
|
||||
new("070101", "数学与应用数学", "理学学士"),
|
||||
new("071201", "统计学", "理学学士"),
|
||||
new("020102", "经济统计学", "经济学学士")
|
||||
],
|
||||
["数学分析", "高等代数", "解析几何", "常微分方程", "实变函数", "数值分析", "数学建模", "统计软件实践"]),
|
||||
new("PHYSICS", "物理与光电工程学院", "物电学院",
|
||||
[
|
||||
new("070201", "物理学", "理学学士"),
|
||||
new("070202", "应用物理学", "理学学士"),
|
||||
new("080705", "光电信息科学与工程", "工学学士")
|
||||
],
|
||||
["力学", "热学", "电磁学", "光学", "量子力学", "固体物理", "近代物理实验", "光电技术综合实验"]),
|
||||
new("CHEM", "化学与环境工程学院", "化环学院",
|
||||
[
|
||||
new("070301", "化学", "理学学士"),
|
||||
new("070302", "应用化学", "理学学士"),
|
||||
new("081301", "化学工程与工艺", "工学学士"),
|
||||
new("082502", "环境工程", "工学学士")
|
||||
],
|
||||
["无机化学", "有机化学", "分析化学", "物理化学", "化工原理", "仪器分析", "基础化学实验", "化工设计实践"]),
|
||||
new("HUMANITIES", "人文与法学院", "人文法学院",
|
||||
[
|
||||
new("050101", "汉语言文学", "文学学士"),
|
||||
new("030101K", "法学", "法学学士"),
|
||||
new("120402", "行政管理", "管理学学士")
|
||||
],
|
||||
["中国古代文学", "中国现当代文学", "现代汉语", "古代汉语", "文学概论", "行政管理学", "新闻写作实训", "社会调查实践"]),
|
||||
new("EDU", "教育科学学院", "教育学院",
|
||||
[
|
||||
new("040101", "教育学", "教育学学士"),
|
||||
new("040107", "小学教育", "教育学学士"),
|
||||
new("040106", "学前教育", "教育学学士")
|
||||
],
|
||||
["教育学原理", "普通心理学", "教育心理学", "课程与教学论", "教育研究方法", "班级管理", "微格教学", "教育见习"]),
|
||||
new("ART", "艺术设计学院", "艺术学院",
|
||||
[
|
||||
new("130502", "视觉传达设计", "艺术学学士"),
|
||||
new("130503", "环境设计", "艺术学学士"),
|
||||
new("130202", "音乐学", "艺术学学士")
|
||||
],
|
||||
["设计素描", "色彩基础", "构成基础", "艺术概论", "数字媒体设计", "品牌视觉设计", "专业采风", "毕业创作实践"]),
|
||||
new("PE", "体育学院", "体育学院",
|
||||
[
|
||||
new("040201", "体育教育", "教育学学士"),
|
||||
new("040203", "社会体育指导与管理", "教育学学士")
|
||||
],
|
||||
["运动解剖学", "运动生理学", "学校体育学", "体育心理学", "运动训练学", "体育社会学", "田径专项训练", "球类专项训练"]),
|
||||
new("LIFE", "生命科学与食品工程学院", "生食学院",
|
||||
[
|
||||
new("071001", "生物科学", "理学学士"),
|
||||
new("071002", "生物技术", "理学学士"),
|
||||
new("082701", "食品科学与工程", "工学学士")
|
||||
],
|
||||
["普通生物学", "生物化学", "细胞生物学", "遗传学", "微生物学", "食品化学", "分子生物学实验", "生物工程综合实践"]),
|
||||
new("MARXISM", "马克思主义学院", "马克思主义学院", [], [])
|
||||
];
|
||||
|
||||
private static readonly PublicCourseSeed[] PublicCourseDefinitions =
|
||||
[
|
||||
new("PUB001", "思想道德与法治", "MARXISM", "MORAL", 3, 48, 48, CourseNature.GeneralRequired),
|
||||
new("PUB002", "中国近现代史纲要", "MARXISM", "MORAL", 3, 48, 48, CourseNature.GeneralRequired),
|
||||
new("PUB003", "马克思主义基本原理", "MARXISM", "MORAL", 3, 48, 48, CourseNature.GeneralRequired),
|
||||
new("PUB004", "毛泽东思想和中国特色社会主义理论体系概论", "MARXISM", "MORAL", 5, 80, 64, CourseNature.GeneralRequired),
|
||||
new("PUB005", "习近平新时代中国特色社会主义思想概论", "MARXISM", "MORAL", 3, 48, 48, CourseNature.GeneralRequired),
|
||||
new("PUB006", "形势与政策", "MARXISM", "MORAL", 2, 32, 32, CourseNature.GeneralRequired, AssessmentMethod.Assessment),
|
||||
new("PUB007", "大学英语 I", "FOREIGN", "ENGLISH", 4, 64, 48, CourseNature.GeneralRequired),
|
||||
new("PUB008", "大学英语 II", "FOREIGN", "ENGLISH", 4, 64, 48, CourseNature.GeneralRequired),
|
||||
new("PUB009", "高等数学 A(上)", "MATH", "BASIC", 5, 80, 80, CourseNature.GeneralRequired),
|
||||
new("PUB010", "高等数学 A(下)", "MATH", "BASIC", 5, 80, 80, CourseNature.GeneralRequired),
|
||||
new("PUB011", "线性代数", "MATH", "BASIC", 3, 48, 48, CourseNature.GeneralRequired),
|
||||
new("PUB012", "概率论与数理统计", "MATH", "BASIC", 3, 48, 48, CourseNature.GeneralRequired),
|
||||
new("PUB013", "大学计算机基础", "CS", "BASIC", 2, 32, 16, CourseNature.GeneralRequired),
|
||||
new("PUB014", "Python 程序设计", "CS", "BASIC", 3, 48, 24, CourseNature.GeneralRequired),
|
||||
new("PUB015", "大学体育 I", "PE", "SPORTS", 1, 32, 4, CourseNature.GeneralRequired, AssessmentMethod.Assessment),
|
||||
new("PUB016", "大学体育 II", "PE", "SPORTS", 1, 32, 4, CourseNature.GeneralRequired, AssessmentMethod.Assessment),
|
||||
new("PUB017", "军事理论", "HUMANITIES", "MILITARY", 2, 36, 32, CourseNature.GeneralRequired, AssessmentMethod.Assessment),
|
||||
new("PUB018", "大学生心理健康教育", "EDU", "BASIC", 2, 32, 24, CourseNature.GeneralRequired, AssessmentMethod.Assessment),
|
||||
new("PUB019", "大学生职业发展与就业指导", "EDU", "BASIC", 2, 32, 24, CourseNature.GeneralRequired, AssessmentMethod.Assessment),
|
||||
new("PUB020", "创新创业基础", "ECON", "INNOVATION", 2, 32, 20, CourseNature.GeneralRequired, AssessmentMethod.Assessment),
|
||||
new("PUB021", "劳动教育", "EDU", "LABOR", 1, 32, 8, CourseNature.GeneralRequired, AssessmentMethod.Assessment),
|
||||
new("PUB022", "国家安全教育", "HUMANITIES", "MORAL", 1, 16, 16, CourseNature.GeneralRequired, AssessmentMethod.Assessment),
|
||||
new("PUB023", "文献检索与学术规范", "HUMANITIES", "BASIC", 1, 16, 12, CourseNature.GeneralRequired, AssessmentMethod.Assessment),
|
||||
new("PUB024", "艺术鉴赏", "ART", "AESTHETIC", 2, 32, 24, CourseNature.GeneralElective, AssessmentMethod.Assessment),
|
||||
new("PUB025", "中国传统文化", "HUMANITIES", "AESTHETIC", 2, 32, 32, CourseNature.GeneralElective, AssessmentMethod.Assessment),
|
||||
new("PUB026", "生态文明导论", "LIFE", "BASIC", 2, 32, 24, CourseNature.GeneralElective, AssessmentMethod.Assessment),
|
||||
new("PUB027", "人工智能导论", "CS", "INNOVATION", 2, 32, 20, CourseNature.GeneralElective, AssessmentMethod.Assessment),
|
||||
new("PUB028", "经济学通识", "ECON", "BASIC", 2, 32, 32, CourseNature.GeneralElective, AssessmentMethod.Assessment)
|
||||
];
|
||||
|
||||
private sealed record CollegeSeed(
|
||||
string Code,
|
||||
string Name,
|
||||
string ShortName,
|
||||
IReadOnlyList<MajorSeed> Majors,
|
||||
IReadOnlyList<string> ProfessionalCourses);
|
||||
|
||||
private sealed record MajorSeed(
|
||||
string Code,
|
||||
string Name,
|
||||
string DegreeType,
|
||||
int SchoolingYears = 4);
|
||||
|
||||
private sealed record PublicCourseSeed(
|
||||
string Code,
|
||||
string Name,
|
||||
string CollegeCode,
|
||||
string CategoryCode,
|
||||
decimal Credits,
|
||||
int TotalHours,
|
||||
int LectureHours,
|
||||
CourseNature Nature,
|
||||
AssessmentMethod AssessmentMethod = AssessmentMethod.Examination);
|
||||
}
|
||||
-184
@@ -1,184 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class FlexibleGradesAndAttendance : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "GradeItems",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
GradeSheetId = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
Name = table.Column<string>(type: "varchar(60)", maxLength: 60, nullable: false),
|
||||
Weight = table.Column<decimal>(type: "decimal(5,1)", precision: 5, scale: 1, nullable: false),
|
||||
SortOrder = table.Column<int>(type: "int", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_GradeItems", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_GradeItems_GradeSheets_GradeSheetId",
|
||||
column: x => x.GradeSheetId,
|
||||
principalTable: "GradeSheets",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
})
|
||||
.Annotation("MySQL:Charset", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "GradeItemScores",
|
||||
columns: table => new
|
||||
{
|
||||
GradeRecordId = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
GradeItemId = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
Score = table.Column<decimal>(type: "decimal(5,1)", precision: 5, scale: 1, nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_GradeItemScores", x => new { x.GradeRecordId, x.GradeItemId });
|
||||
table.ForeignKey(
|
||||
name: "FK_GradeItemScores_GradeItems_GradeItemId",
|
||||
column: x => x.GradeItemId,
|
||||
principalTable: "GradeItems",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "FK_GradeItemScores_GradeRecords_GradeRecordId",
|
||||
column: x => x.GradeRecordId,
|
||||
principalTable: "GradeRecords",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
})
|
||||
.Annotation("MySQL:Charset", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_GradeItems_GradeSheetId_SortOrder",
|
||||
table: "GradeItems",
|
||||
columns: new[] { "GradeSheetId", "SortOrder" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_GradeItemScores_GradeRecordId_GradeItemId",
|
||||
table: "GradeItemScores",
|
||||
columns: new[] { "GradeRecordId", "GradeItemId" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_GradeItemScores_GradeItemId",
|
||||
table: "GradeItemScores",
|
||||
column: "GradeItemId");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MidtermWeight",
|
||||
table: "GradeSheets");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MidtermScore",
|
||||
table: "GradeRecords");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AttendanceSheets",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
TeachingTaskId = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
Name = table.Column<string>(type: "varchar(120)", maxLength: 120, nullable: false),
|
||||
AttendanceDate = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
Status = table.Column<int>(type: "int", nullable: false),
|
||||
Notes = table.Column<string>(type: "varchar(500)", maxLength: 500, nullable: true),
|
||||
SubmittedAt = table.Column<DateTime>(type: "datetime(6)", nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AttendanceSheets", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AttendanceSheets_TeachingTasks_TeachingTaskId",
|
||||
column: x => x.TeachingTaskId,
|
||||
principalTable: "TeachingTasks",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
})
|
||||
.Annotation("MySQL:Charset", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AttendanceRecords",
|
||||
columns: table => new
|
||||
{
|
||||
AttendanceSheetId = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
StudentId = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
Status = table.Column<int>(type: "int", nullable: false),
|
||||
Notes = table.Column<string>(type: "varchar(300)", maxLength: 300, nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AttendanceRecords", x => new { x.AttendanceSheetId, x.StudentId });
|
||||
table.ForeignKey(
|
||||
name: "FK_AttendanceRecords_AttendanceSheets_AttendanceSheetId",
|
||||
column: x => x.AttendanceSheetId,
|
||||
principalTable: "AttendanceSheets",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_AttendanceRecords_Students_StudentId",
|
||||
column: x => x.StudentId,
|
||||
principalTable: "Students",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
})
|
||||
.Annotation("MySQL:Charset", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AttendanceSheets_TeachingTaskId_AttendanceDate",
|
||||
table: "AttendanceSheets",
|
||||
columns: new[] { "TeachingTaskId", "AttendanceDate" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AttendanceRecords_AttendanceSheetId_StudentId",
|
||||
table: "AttendanceRecords",
|
||||
columns: new[] { "AttendanceSheetId", "StudentId" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AttendanceRecords_StudentId",
|
||||
table: "AttendanceRecords",
|
||||
column: "StudentId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(name: "AttendanceRecords");
|
||||
migrationBuilder.DropTable(name: "AttendanceSheets");
|
||||
migrationBuilder.DropTable(name: "GradeItemScores");
|
||||
migrationBuilder.DropTable(name: "GradeItems");
|
||||
|
||||
migrationBuilder.AddColumn<decimal>(
|
||||
name: "MidtermWeight",
|
||||
table: "GradeSheets",
|
||||
type: "decimal(5,1)",
|
||||
precision: 5,
|
||||
scale: 1,
|
||||
nullable: false,
|
||||
defaultValue: 0m);
|
||||
|
||||
migrationBuilder.AddColumn<decimal>(
|
||||
name: "MidtermScore",
|
||||
table: "GradeRecords",
|
||||
type: "decimal(5,1)",
|
||||
precision: 5,
|
||||
scale: 1,
|
||||
nullable: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
-158
@@ -1,158 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ExamSchedulingOptimization : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// Drop old FK/index on ClassroomId
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_ExamSessions_Classrooms_ClassroomId",
|
||||
table: "ExamSessions");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_ExamSessions_ClassroomId",
|
||||
table: "ExamSessions");
|
||||
|
||||
// Make ClassroomId nullable
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "ClassroomId",
|
||||
table: "ExamSessions",
|
||||
type: "char(36)",
|
||||
nullable: true,
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "char(36)");
|
||||
|
||||
// Add new columns
|
||||
migrationBuilder.AddColumn<DateOnly>(
|
||||
name: "ExamDate",
|
||||
table: "ExamSessions",
|
||||
type: "date",
|
||||
nullable: false,
|
||||
defaultValue: new DateOnly(2027, 1, 1));
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "StartPeriod",
|
||||
table: "ExamSessions",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 1);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "PeriodCount",
|
||||
table: "ExamSessions",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 2);
|
||||
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "RequiredBuildingId",
|
||||
table: "ExamSessions",
|
||||
type: "char(36)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "RequiredInvigilatorCount",
|
||||
table: "ExamSessions",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 2);
|
||||
|
||||
// Re-add FK/index on ClassroomId (nullable, SetNull)
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ExamSessions_ClassroomId",
|
||||
table: "ExamSessions",
|
||||
column: "ClassroomId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_ExamSessions_Classrooms_ClassroomId",
|
||||
table: "ExamSessions",
|
||||
column: "ClassroomId",
|
||||
principalTable: "Classrooms",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
|
||||
// New indices
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ExamSessions_ExamPlanId_ExamDate",
|
||||
table: "ExamSessions",
|
||||
columns: new[] { "ExamPlanId", "ExamDate" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ExamSessions_RequiredBuildingId",
|
||||
table: "ExamSessions",
|
||||
column: "RequiredBuildingId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_ExamSessions_Buildings_RequiredBuildingId",
|
||||
table: "ExamSessions",
|
||||
column: "RequiredBuildingId",
|
||||
principalTable: "Buildings",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// Remove new FK/index
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_ExamSessions_Buildings_RequiredBuildingId",
|
||||
table: "ExamSessions");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_ExamSessions_Classrooms_ClassroomId",
|
||||
table: "ExamSessions");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_ExamSessions_ClassroomId",
|
||||
table: "ExamSessions");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_ExamSessions_ExamPlanId_ExamDate",
|
||||
table: "ExamSessions");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_ExamSessions_RequiredBuildingId",
|
||||
table: "ExamSessions");
|
||||
|
||||
// Drop new columns
|
||||
migrationBuilder.DropColumn(name: "RequiredInvigilatorCount", table: "ExamSessions");
|
||||
migrationBuilder.DropColumn(name: "RequiredBuildingId", table: "ExamSessions");
|
||||
migrationBuilder.DropColumn(name: "PeriodCount", table: "ExamSessions");
|
||||
migrationBuilder.DropColumn(name: "StartPeriod", table: "ExamSessions");
|
||||
migrationBuilder.DropColumn(name: "ExamDate", table: "ExamSessions");
|
||||
|
||||
// Restore ClassroomId to non-nullable
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "ClassroomId",
|
||||
table: "ExamSessions",
|
||||
type: "char(36)",
|
||||
nullable: false,
|
||||
defaultValue: Guid.Empty,
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "char(36)",
|
||||
oldNullable: true);
|
||||
|
||||
// Restore original FK/index
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ExamSessions_ClassroomId",
|
||||
table: "ExamSessions",
|
||||
column: "ClassroomId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_ExamSessions_Classrooms_ClassroomId",
|
||||
table: "ExamSessions",
|
||||
column: "ClassroomId",
|
||||
principalTable: "Classrooms",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
}
|
||||
}
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class TeachingEvaluation : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+953
-635
File diff suppressed because it is too large
Load Diff
+1173
File diff suppressed because it is too large
Load Diff
-29
@@ -1,29 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class RetakeEnrollment : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "EnrollmentType",
|
||||
table: "CourseEnrollments",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 1);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "EnrollmentType",
|
||||
table: "CourseEnrollments");
|
||||
}
|
||||
}
|
||||
}
|
||||
-125
@@ -1,125 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class CourseAdjustments : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "CourseAdjustments",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
TeachingTaskId = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
Type = table.Column<int>(type: "int", nullable: false),
|
||||
Status = table.Column<int>(type: "int", nullable: false),
|
||||
ApplicantUserId = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
TargetDate = table.Column<DateOnly>(type: "date", nullable: true),
|
||||
DayOfWeek = table.Column<int>(type: "int", nullable: true),
|
||||
StartPeriod = table.Column<int>(type: "int", nullable: true),
|
||||
PeriodCount = table.Column<int>(type: "int", nullable: true),
|
||||
ClassroomId = table.Column<Guid>(type: "char(36)", nullable: true),
|
||||
SubstituteTeacherId = table.Column<Guid>(type: "char(36)", nullable: true),
|
||||
CancelWeek = table.Column<int>(type: "int", nullable: true),
|
||||
CancelDate = table.Column<DateOnly>(type: "date", nullable: true),
|
||||
Reason = table.Column<string>(type: "varchar(500)", maxLength: 500, nullable: false),
|
||||
ReviewComment = table.Column<string>(type: "varchar(500)", maxLength: 500, nullable: true),
|
||||
SubmittedAt = table.Column<DateTime>(type: "datetime(6)", nullable: true),
|
||||
ReviewedAt = table.Column<DateTime>(type: "datetime(6)", nullable: true),
|
||||
ReviewedByUserId = table.Column<Guid>(type: "char(36)", nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_CourseAdjustments", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_CourseAdjustments_Classrooms_ClassroomId",
|
||||
column: x => x.ClassroomId,
|
||||
principalTable: "Classrooms",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "FK_CourseAdjustments_Teachers_SubstituteTeacherId",
|
||||
column: x => x.SubstituteTeacherId,
|
||||
principalTable: "Teachers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "FK_CourseAdjustments_TeachingTasks_TeachingTaskId",
|
||||
column: x => x.TeachingTaskId,
|
||||
principalTable: "TeachingTasks",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
})
|
||||
.Annotation("MySQL:Charset", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Notifications",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
UserId = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
Title = table.Column<string>(type: "varchar(200)", maxLength: 200, nullable: false),
|
||||
Content = table.Column<string>(type: "varchar(1000)", maxLength: 1000, nullable: false),
|
||||
IsRead = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||
LinkUrl = table.Column<string>(type: "varchar(300)", maxLength: 300, nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Notifications", x => x.Id);
|
||||
})
|
||||
.Annotation("MySQL:Charset", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_CourseAdjustments_ApplicantUserId",
|
||||
table: "CourseAdjustments",
|
||||
column: "ApplicantUserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_CourseAdjustments_ClassroomId",
|
||||
table: "CourseAdjustments",
|
||||
column: "ClassroomId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_CourseAdjustments_Status_CreatedAt",
|
||||
table: "CourseAdjustments",
|
||||
columns: new[] { "Status", "CreatedAt" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_CourseAdjustments_SubstituteTeacherId",
|
||||
table: "CourseAdjustments",
|
||||
column: "SubstituteTeacherId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_CourseAdjustments_TeachingTaskId_Status",
|
||||
table: "CourseAdjustments",
|
||||
columns: new[] { "TeachingTaskId", "Status" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Notifications_CreatedAt",
|
||||
table: "Notifications",
|
||||
column: "CreatedAt");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Notifications_UserId_IsRead",
|
||||
table: "Notifications",
|
||||
columns: new[] { "UserId", "IsRead" });
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(name: "Notifications");
|
||||
migrationBuilder.DropTable(name: "CourseAdjustments");
|
||||
}
|
||||
}
|
||||
}
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AttendanceAppeal : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "AppealStatus",
|
||||
table: "AttendanceRecords",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "AppealReason",
|
||||
table: "AttendanceRecords",
|
||||
type: "varchar(500)",
|
||||
maxLength: 500,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "AppealSubmittedAt",
|
||||
table: "AttendanceRecords",
|
||||
type: "datetime(6)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "AppealReviewComment",
|
||||
table: "AttendanceRecords",
|
||||
type: "varchar(300)",
|
||||
maxLength: 300,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "AppealReviewedAt",
|
||||
table: "AttendanceRecords",
|
||||
type: "datetime(6)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AttendanceRecords_AppealStatus",
|
||||
table: "AttendanceRecords",
|
||||
column: "AppealStatus");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_AttendanceRecords_AppealStatus",
|
||||
table: "AttendanceRecords");
|
||||
|
||||
migrationBuilder.DropColumn(name: "AppealReviewedAt", table: "AttendanceRecords");
|
||||
migrationBuilder.DropColumn(name: "AppealReviewComment", table: "AttendanceRecords");
|
||||
migrationBuilder.DropColumn(name: "AppealSubmittedAt", table: "AttendanceRecords");
|
||||
migrationBuilder.DropColumn(name: "AppealReason", table: "AttendanceRecords");
|
||||
migrationBuilder.DropColumn(name: "AppealStatus", table: "AttendanceRecords");
|
||||
}
|
||||
}
|
||||
}
|
||||
-117
@@ -1,117 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
{
|
||||
public partial class ApprovalRequests : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// CourseExemption
|
||||
migrationBuilder.CreateTable("CourseExemptions", table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
StudentId = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
TeachingTaskId = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
Reason = table.Column<string>(type: "varchar(500)", maxLength: 500, nullable: false),
|
||||
Status = table.Column<int>(type: "int", nullable: false),
|
||||
ReviewComment = table.Column<string>(type: "varchar(500)", maxLength: 500, nullable: true),
|
||||
SubmittedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
ReviewedAt = table.Column<DateTime>(type: "datetime(6)", nullable: true),
|
||||
ReviewedByUserId = table.Column<Guid>(type: "char(36)", nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
}, constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_CourseExemptions", x => x.Id);
|
||||
table.ForeignKey("FK_CourseExemptions_Students", x => x.StudentId, "Students", "Id", onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey("FK_CourseExemptions_TeachingTasks", x => x.TeachingTaskId, "TeachingTasks", "Id", onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
migrationBuilder.CreateIndex("IX_CourseExemptions_Status_CreatedAt", "CourseExemptions", new[] { "Status", "CreatedAt" });
|
||||
migrationBuilder.CreateIndex("IX_CourseExemptions_StudentId_TeachingTaskId", "CourseExemptions", new[] { "StudentId", "TeachingTaskId" }, unique: true);
|
||||
|
||||
// DeferredExam
|
||||
migrationBuilder.CreateTable("DeferredExams", table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
StudentId = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
TeachingTaskId = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
Reason = table.Column<string>(type: "varchar(500)", maxLength: 500, nullable: false),
|
||||
Status = table.Column<int>(type: "int", nullable: false),
|
||||
ReviewComment = table.Column<string>(type: "varchar(500)", maxLength: 500, nullable: true),
|
||||
SubmittedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
ReviewedAt = table.Column<DateTime>(type: "datetime(6)", nullable: true),
|
||||
ReviewedByUserId = table.Column<Guid>(type: "char(36)", nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
}, constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_DeferredExams", x => x.Id);
|
||||
table.ForeignKey("FK_DeferredExams_Students", x => x.StudentId, "Students", "Id", onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey("FK_DeferredExams_TeachingTasks", x => x.TeachingTaskId, "TeachingTasks", "Id", onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
migrationBuilder.CreateIndex("IX_DeferredExams_Status_CreatedAt", "DeferredExams", new[] { "Status", "CreatedAt" });
|
||||
migrationBuilder.CreateIndex("IX_DeferredExams_StudentId_TeachingTaskId", "DeferredExams", new[] { "StudentId", "TeachingTaskId" }, unique: true);
|
||||
|
||||
// GradeModification
|
||||
migrationBuilder.CreateTable("GradeModifications", table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
GradeRecordId = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
CurrentScore = table.Column<decimal>(type: "decimal(5,1)", precision: 5, scale: 1, nullable: false),
|
||||
RequestedScore = table.Column<decimal>(type: "decimal(5,1)", precision: 5, scale: 1, nullable: false),
|
||||
Reason = table.Column<string>(type: "varchar(500)", maxLength: 500, nullable: false),
|
||||
Status = table.Column<int>(type: "int", nullable: false),
|
||||
ReviewComment = table.Column<string>(type: "varchar(500)", maxLength: 500, nullable: true),
|
||||
ApplicantUserId = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
SubmittedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
CollegeReviewedAt = table.Column<DateTime>(type: "datetime(6)", nullable: true),
|
||||
CollegeReviewedByUserId = table.Column<Guid>(type: "char(36)", nullable: true),
|
||||
FinalReviewedAt = table.Column<DateTime>(type: "datetime(6)", nullable: true),
|
||||
FinalReviewedByUserId = table.Column<Guid>(type: "char(36)", nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
}, constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_GradeModifications", x => x.Id);
|
||||
table.ForeignKey("FK_GradeModifications_GradeRecords", x => x.GradeRecordId, "GradeRecords", "Id", onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
migrationBuilder.CreateIndex("IX_GradeModifications_Status_CreatedAt", "GradeModifications", new[] { "Status", "CreatedAt" });
|
||||
|
||||
// CourseSubstitution
|
||||
migrationBuilder.CreateTable("CourseSubstitutions", table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
StudentId = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
OriginalCourseId = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
SubstituteCourseId = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
Reason = table.Column<string>(type: "varchar(500)", maxLength: 500, nullable: false),
|
||||
Status = table.Column<int>(type: "int", nullable: false),
|
||||
ReviewComment = table.Column<string>(type: "varchar(500)", maxLength: 500, nullable: true),
|
||||
SubmittedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
ReviewedAt = table.Column<DateTime>(type: "datetime(6)", nullable: true),
|
||||
ReviewedByUserId = table.Column<Guid>(type: "char(36)", nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
}, constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_CourseSubstitutions", x => x.Id);
|
||||
table.ForeignKey("FK_CourseSubstitutions_Students", x => x.StudentId, "Students", "Id", onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey("FK_CourseSubstitutions_OriginalCourse", x => x.OriginalCourseId, "Courses", "Id", onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey("FK_CourseSubstitutions_SubstituteCourse", x => x.SubstituteCourseId, "Courses", "Id", onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
migrationBuilder.CreateIndex("IX_CourseSubstitutions_Status_CreatedAt", "CourseSubstitutions", new[] { "Status", "CreatedAt" });
|
||||
migrationBuilder.CreateIndex("IX_CourseSubstitutions_StudentId_OriginalCourseId", "CourseSubstitutions", new[] { "StudentId", "OriginalCourseId" }, unique: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable("CourseSubstitutions");
|
||||
migrationBuilder.DropTable("GradeModifications");
|
||||
migrationBuilder.DropTable("DeferredExams");
|
||||
migrationBuilder.DropTable("CourseExemptions");
|
||||
}
|
||||
}
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
{
|
||||
public partial class AcademicWarnings : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable("WarningRules", table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
Type = table.Column<int>(type: "int", nullable: false),
|
||||
Name = table.Column<string>(type: "varchar(100)", maxLength: 100, nullable: false),
|
||||
Threshold = table.Column<decimal>(type: "decimal(7,2)", precision: 7, scale: 2, nullable: false),
|
||||
IsEnabled = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||
NotifyStudent = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||
NotifyCounselor = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||
AcademicTermId = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
Description = table.Column<string>(type: "varchar(300)", maxLength: 300, nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
}, constraints: t => { t.PrimaryKey("PK_WarningRules", x => x.Id); t.ForeignKey("FK_WarningRules_AcademicTerms", x => x.AcademicTermId, "AcademicTerms", "Id", onDelete: ReferentialAction.Cascade); });
|
||||
migrationBuilder.CreateIndex("IX_WarningRules_AcademicTermId_Type", "WarningRules", new[] { "AcademicTermId", "Type" }, unique: true);
|
||||
|
||||
migrationBuilder.CreateTable("WarningRecords", table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
StudentId = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
Type = table.Column<int>(type: "int", nullable: false),
|
||||
Status = table.Column<int>(type: "int", nullable: false),
|
||||
TriggerValue = table.Column<decimal>(type: "decimal(7,2)", precision: 7, scale: 2, nullable: false),
|
||||
Detail = table.Column<string>(type: "varchar(1000)", maxLength: 1000, nullable: false),
|
||||
AcknowledgedAt = table.Column<DateTime>(type: "datetime(6)", nullable: true),
|
||||
AcknowledgeComment = table.Column<string>(type: "varchar(300)", maxLength: 300, nullable: true),
|
||||
AcademicTermId = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
}, constraints: t => { t.PrimaryKey("PK_WarningRecords", x => x.Id); t.ForeignKey("FK_WarningRecords_Students", x => x.StudentId, "Students", "Id", onDelete: ReferentialAction.Restrict); });
|
||||
migrationBuilder.CreateIndex("IX_WarningRecords_StudentId_AcademicTermId_Type", "WarningRecords", new[] { "StudentId", "AcademicTermId", "Type" }, unique: true);
|
||||
migrationBuilder.CreateIndex("IX_WarningRecords_Status", "WarningRecords", "Status");
|
||||
}
|
||||
protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable("WarningRecords"); migrationBuilder.DropTable("WarningRules"); }
|
||||
}
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
{
|
||||
public partial class WarningAutoCheck : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable("WarningSchedules");
|
||||
migrationBuilder.AddColumn<bool>("AutoCheckEnabled", "WarningRules", type: "tinyint(1)", nullable: false, defaultValue: false);
|
||||
migrationBuilder.AddColumn<int?>("CheckDayOfWeek", "WarningRules", type: "int", nullable: true);
|
||||
migrationBuilder.AddColumn<int>("CheckHour", "WarningRules", type: "int", nullable: false, defaultValue: 8);
|
||||
migrationBuilder.AddColumn<int>("CheckMinute", "WarningRules", type: "int", nullable: false, defaultValue: 0);
|
||||
migrationBuilder.AddColumn<DateTime?>("LastCheckAt", "WarningRules", type: "datetime(6)", nullable: true);
|
||||
}
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn("LastCheckAt", "WarningRules");
|
||||
migrationBuilder.DropColumn("CheckMinute", "WarningRules");
|
||||
migrationBuilder.DropColumn("CheckHour", "WarningRules");
|
||||
migrationBuilder.DropColumn("CheckDayOfWeek", "WarningRules");
|
||||
migrationBuilder.DropColumn("AutoCheckEnabled", "WarningRules");
|
||||
// WarningSchedules table recreation omitted for brevity
|
||||
}
|
||||
}
|
||||
}
|
||||
+951
-633
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user