using Jiaowu.Api.Domain.Academic; using Jiaowu.Api.Domain.Common; using Jiaowu.Api.Domain.Identity; using Jiaowu.Api.Domain.System; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; namespace Jiaowu.Api.Infrastructure.Persistence; public sealed class AppDbContext(DbContextOptions options) : IdentityDbContext(options) { public DbSet Campuses => Set(); public DbSet Colleges => Set(); public DbSet Majors => Set(); public DbSet AdministrativeClasses => Set(); public DbSet Buildings => Set(); public DbSet Classrooms => Set(); public DbSet AcademicTerms => Set(); public DbSet Teachers => Set(); public DbSet Students => Set(); public DbSet CourseCategories => Set(); public DbSet Courses => Set(); public DbSet CurriculumPlans => Set(); public DbSet CurriculumModules => Set(); public DbSet CurriculumCourses => Set(); public DbSet TeachingTasks => Set(); public DbSet TeachingTaskTeachers => Set(); public DbSet TeachingTaskClasses => Set(); public DbSet TeacherCourseApplications => Set(); public DbSet SchedulePlans => Set(); public DbSet ScheduleEntries => Set(); public DbSet ScheduleTimeSlots => Set(); public DbSet TeachingTaskScheduleConstraints => Set(); public DbSet TeachingTaskAllowedClassrooms => Set(); public DbSet AutomaticScheduleJobs => Set(); public DbSet SchedulePublishJobs => Set(); public DbSet CourseSelectionRounds => Set(); public DbSet CourseSelectionOfferings => Set(); public DbSet CourseEnrollments => Set(); public DbSet GradeSheets => Set(); public DbSet GradeRecords => Set(); public DbSet GradeItems => Set(); public DbSet GradeItemScores => Set(); public DbSet AttendanceSheets => Set(); public DbSet AttendanceRecords => Set(); public DbSet ExamPlans => Set(); public DbSet ExamSessions => Set(); public DbSet ExamSessionInvigilators => Set(); public DbSet MakeupExamPlans => Set(); public DbSet MakeupExamSessions => Set(); public DbSet MakeupExamSessionInvigilators => Set(); public DbSet MakeupExamEnrollments => Set(); public DbSet MakeupExamAutoJobs => Set(); public DbSet CourseAdjustments => Set(); public DbSet CourseExemptions => Set(); public DbSet DeferredExams => Set(); public DbSet GradeModifications => Set(); public DbSet CourseSubstitutions => Set(); public DbSet WarningRules => Set(); public DbSet WarningRecords => Set(); public DbSet EvaluationSetups => Set(); public DbSet EvaluationDimensions => Set(); public DbSet EvaluationRecords => Set(); public DbSet EvaluationScores => Set(); public DbSet Notifications => Set(); public DbSet StudentStatusChanges => Set(); public DbSet GraduationAuditBatches => Set(); public DbSet GraduationAuditResults => Set(); public DbSet DegreeAwardBatches => Set(); public DbSet DegreeAwardResults => Set(); public DbSet GraduationClearanceBatches => Set(); public DbSet GraduationClearanceItems => Set(); public DbSet GraduationClearanceRecords => Set(); public DbSet AuditLogs => Set(); protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); builder.Entity(entity => { entity.Property(x => x.DisplayName).HasMaxLength(50); entity.Property(x => x.StaffNumber).HasMaxLength(30); entity.HasIndex(x => x.StaffNumber); }); builder.Entity(entity => { entity.Property(x => x.Description).HasMaxLength(100); }); ConfigureCatalog(builder); ConfigureCatalog(builder); ConfigureCatalog(builder); ConfigureCatalog(builder); ConfigureCatalog(builder); ConfigureCatalog(builder); ConfigureCatalog(builder); ConfigureCatalog(builder); ConfigureCatalog(builder); builder.Entity() .HasOne(x => x.Campus) .WithMany() .HasForeignKey(x => x.CampusId) .OnDelete(DeleteBehavior.Restrict); builder.Entity() .HasOne(x => x.College) .WithMany() .HasForeignKey(x => x.CollegeId) .OnDelete(DeleteBehavior.Restrict); builder.Entity() .HasOne(x => x.Major) .WithMany() .HasForeignKey(x => x.MajorId) .OnDelete(DeleteBehavior.Restrict); builder.Entity(entity => { entity.HasIndex(x => x.CounselorUserId); entity.HasOne(x => x.CounselorUser) .WithMany() .HasForeignKey(x => x.CounselorUserId) .OnDelete(DeleteBehavior.SetNull); }); builder.Entity() .HasOne(x => x.Campus) .WithMany() .HasForeignKey(x => x.CampusId) .OnDelete(DeleteBehavior.Restrict); builder.Entity() .HasOne(x => x.Building) .WithMany() .HasForeignKey(x => x.BuildingId) .OnDelete(DeleteBehavior.Restrict); builder.Entity() .HasIndex(x => x.IsCurrent); builder.Entity(entity => { entity.Property(x => x.TeacherNumber).HasMaxLength(30); entity.Property(x => x.Name).HasMaxLength(50); entity.Property(x => x.Title).HasMaxLength(30); entity.Property(x => x.Phone).HasMaxLength(30); entity.Property(x => x.Email).HasMaxLength(100); entity.Property(x => x.Notes).HasMaxLength(500); entity.HasIndex(x => x.TeacherNumber).IsUnique(); entity.HasIndex(x => new { x.CollegeId, x.Status }); entity.HasOne(x => x.College) .WithMany() .HasForeignKey(x => x.CollegeId) .OnDelete(DeleteBehavior.Restrict); entity.HasOne() .WithMany() .HasForeignKey(x => x.UserId) .OnDelete(DeleteBehavior.SetNull); }); builder.Entity(entity => { entity.Property(x => x.StudentNumber).HasMaxLength(30); entity.Property(x => x.Name).HasMaxLength(50); entity.Property(x => x.Phone).HasMaxLength(30); entity.Property(x => x.Email).HasMaxLength(100); entity.Property(x => x.Notes).HasMaxLength(500); entity.HasIndex(x => x.StudentNumber).IsUnique(); entity.HasIndex(x => new { x.AdministrativeClassId, x.Status }); entity.HasIndex(x => x.EnrollmentYear); entity.HasOne(x => x.AdministrativeClass) .WithMany(x => x.Students) .HasForeignKey(x => x.AdministrativeClassId) .OnDelete(DeleteBehavior.Restrict); entity.HasOne() .WithMany() .HasForeignKey(x => x.UserId) .OnDelete(DeleteBehavior.SetNull); }); builder.Entity(entity => { entity.Property(x => x.EnglishName).HasMaxLength(150); entity.Property(x => x.Credits).HasPrecision(5, 2); entity.Property(x => x.Description).HasMaxLength(1000); entity.HasIndex(x => new { x.CollegeId, x.Nature }); entity.HasIndex(x => x.CourseCategoryId); entity.HasOne(x => x.College) .WithMany() .HasForeignKey(x => x.CollegeId) .OnDelete(DeleteBehavior.Restrict); entity.HasOne(x => x.CourseCategory) .WithMany() .HasForeignKey(x => x.CourseCategoryId) .OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.Property(x => x.Name).HasMaxLength(120); entity.Property(x => x.Version).HasMaxLength(30); entity.Property(x => x.TotalCredits).HasPrecision(6, 2); entity.Property(x => x.Description).HasMaxLength(1000); entity.HasIndex(x => new { x.MajorId, x.EffectiveGrade, x.Version }).IsUnique(); entity.HasIndex(x => new { x.Status, x.EffectiveGrade }); entity.HasOne(x => x.Major) .WithMany() .HasForeignKey(x => x.MajorId) .OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.Property(x => x.Code).HasMaxLength(30); entity.Property(x => x.Name).HasMaxLength(100); entity.Property(x => x.RequiredCredits).HasPrecision(6, 2); entity.HasIndex(x => new { x.CurriculumPlanId, x.Code }).IsUnique(); entity.HasOne(x => x.CurriculumPlan) .WithMany(x => x.Modules) .HasForeignKey(x => x.CurriculumPlanId) .OnDelete(DeleteBehavior.Cascade); }); builder.Entity(entity => { entity.Property(x => x.Notes).HasMaxLength(500); entity.HasIndex(x => new { x.CurriculumModuleId, x.CourseId }).IsUnique(); entity.HasOne(x => x.CurriculumModule) .WithMany(x => x.Courses) .HasForeignKey(x => x.CurriculumModuleId) .OnDelete(DeleteBehavior.Cascade); entity.HasOne(x => x.Course) .WithMany() .HasForeignKey(x => x.CourseId) .OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.Property(x => x.TaskNumber).HasMaxLength(40); entity.Property(x => x.Name).HasMaxLength(120); entity.Property(x => x.GenerationBatchCode).HasMaxLength(40); entity.Property(x => x.Notes).HasMaxLength(500); entity.HasIndex(x => x.TaskNumber).IsUnique(); entity.HasIndex(x => new { x.AcademicTermId, x.Status }); entity.HasOne(x => x.AcademicTerm) .WithMany() .HasForeignKey(x => x.AcademicTermId) .OnDelete(DeleteBehavior.Restrict); entity.HasOne(x => x.Course) .WithMany() .HasForeignKey(x => x.CourseId) .OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.HasKey(x => new { x.TeachingTaskId, x.TeacherId }); entity.HasOne(x => x.TeachingTask) .WithMany(x => x.Teachers) .HasForeignKey(x => x.TeachingTaskId) .OnDelete(DeleteBehavior.Cascade); entity.HasOne(x => x.Teacher) .WithMany() .HasForeignKey(x => x.TeacherId) .OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.HasKey(x => new { x.TeachingTaskId, x.AdministrativeClassId }); entity.HasOne(x => x.TeachingTask) .WithMany(x => x.Classes) .HasForeignKey(x => x.TeachingTaskId) .OnDelete(DeleteBehavior.Cascade); entity.HasOne(x => x.AdministrativeClass) .WithMany() .HasForeignKey(x => x.AdministrativeClassId) .OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.Property(x => x.Statement).HasMaxLength(500); entity.Property(x => x.ReviewComment).HasMaxLength(500); entity.HasIndex(x => new { x.AcademicTermId, x.TeacherId, x.CourseId }).IsUnique(); entity.HasIndex(x => new { x.Status, x.AcademicTermId }); entity.HasOne(x => x.AcademicTerm) .WithMany() .HasForeignKey(x => x.AcademicTermId) .OnDelete(DeleteBehavior.Restrict); entity.HasOne(x => x.Teacher) .WithMany() .HasForeignKey(x => x.TeacherId) .OnDelete(DeleteBehavior.Cascade); entity.HasOne(x => x.Course) .WithMany() .HasForeignKey(x => x.CourseId) .OnDelete(DeleteBehavior.Restrict); entity.HasOne() .WithMany() .HasForeignKey(x => x.ReviewedByUserId) .OnDelete(DeleteBehavior.SetNull); }); builder.Entity(entity => { entity.Property(x => x.Name).HasMaxLength(120); entity.Property(x => x.Version).HasMaxLength(30); entity.Property(x => x.Notes).HasMaxLength(500); entity.HasIndex(x => new { x.AcademicTermId, x.Version }).IsUnique(); entity.HasIndex(x => new { x.AcademicTermId, x.Status }); entity.HasOne(x => x.AcademicTerm) .WithMany() .HasForeignKey(x => x.AcademicTermId) .OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.Property(x => x.Notes).HasMaxLength(500); entity.HasIndex(x => new { x.SchedulePlanId, x.DayOfWeek, x.StartPeriod }); entity.HasOne(x => x.SchedulePlan) .WithMany(x => x.Entries) .HasForeignKey(x => x.SchedulePlanId) .OnDelete(DeleteBehavior.Cascade); entity.HasOne(x => x.TeachingTask) .WithMany() .HasForeignKey(x => x.TeachingTaskId) .OnDelete(DeleteBehavior.Restrict); entity.HasOne(x => x.Classroom) .WithMany() .HasForeignKey(x => x.ClassroomId) .OnDelete(DeleteBehavior.SetNull); }); builder.Entity(entity => { entity.Property(x => x.Name).HasMaxLength(40); entity.HasIndex(x => new { x.AcademicTermId, x.PeriodNumber }).IsUnique(); entity.HasOne(x => x.AcademicTerm) .WithMany() .HasForeignKey(x => x.AcademicTermId) .OnDelete(DeleteBehavior.Cascade); }); builder.Entity(entity => { entity.Property(x => x.AllowedDayOfWeeks).HasMaxLength(20); entity.HasIndex(x => x.TeachingTaskId).IsUnique(); entity.HasOne(x => x.TeachingTask) .WithMany() .HasForeignKey(x => x.TeachingTaskId) .OnDelete(DeleteBehavior.Cascade); entity.HasOne(x => x.RequiredCampus) .WithMany() .HasForeignKey(x => x.RequiredCampusId) .OnDelete(DeleteBehavior.Restrict); entity.HasOne(x => x.RequiredBuilding) .WithMany() .HasForeignKey(x => x.RequiredBuildingId) .OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.HasKey(x => new { x.TeachingTaskScheduleConstraintId, x.ClassroomId }); entity.HasOne(x => x.TeachingTaskScheduleConstraint) .WithMany(x => x.AllowedClassrooms) .HasForeignKey(x => x.TeachingTaskScheduleConstraintId) .OnDelete(DeleteBehavior.Cascade); entity.HasOne(x => x.Classroom) .WithMany() .HasForeignKey(x => x.ClassroomId) .OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.Property(x => x.ErrorMessage).HasMaxLength(2000); entity.HasIndex(x => x.ActiveSchedulePlanId).IsUnique(); entity.HasIndex(x => new { x.SchedulePlanId, x.CreatedAt }); entity.HasIndex(x => new { x.Status, x.CreatedAt }); entity.HasIndex(x => x.RequestedByUserId); entity.HasOne(x => x.SchedulePlan) .WithMany() .HasForeignKey(x => x.SchedulePlanId) .OnDelete(DeleteBehavior.Cascade); entity.HasOne() .WithMany() .HasForeignKey(x => x.RequestedByUserId) .OnDelete(DeleteBehavior.SetNull); }); builder.Entity(entity => { entity.Property(x => x.CurrentStep).HasMaxLength(200); entity.Property(x => x.ErrorMessage).HasMaxLength(2000); entity.HasIndex(x => x.ActiveAcademicTermId).IsUnique(); entity.HasIndex(x => new { x.SchedulePlanId, x.CreatedAt }); entity.HasIndex(x => new { x.Status, x.CreatedAt }); entity.HasIndex(x => x.RequestedByUserId); entity.HasOne(x => x.SchedulePlan) .WithMany() .HasForeignKey(x => x.SchedulePlanId) .OnDelete(DeleteBehavior.Cascade); entity.HasOne() .WithMany() .HasForeignKey(x => x.RequestedByUserId) .OnDelete(DeleteBehavior.SetNull); }); builder.Entity(entity => { entity.Property(x => x.Name).HasMaxLength(120); entity.Property(x => x.MaxCredits).HasPrecision(6, 1); entity.Property(x => x.Notes).HasMaxLength(500); entity.HasIndex(x => new { x.AcademicTermId, x.Status }); entity.HasOne(x => x.AcademicTerm) .WithMany() .HasForeignKey(x => x.AcademicTermId) .OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.Property(x => x.Notes).HasMaxLength(500); entity.HasIndex(x => new { x.CourseSelectionRoundId, x.TeachingTaskId }).IsUnique(); entity.HasOne(x => x.CourseSelectionRound) .WithMany(x => x.Offerings) .HasForeignKey(x => x.CourseSelectionRoundId) .OnDelete(DeleteBehavior.Cascade); entity.HasOne(x => x.TeachingTask) .WithMany() .HasForeignKey(x => x.TeachingTaskId) .OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.HasIndex(x => new { x.CourseSelectionOfferingId, x.StudentId }) .IsUnique(); entity.HasIndex(x => new { x.StudentId, x.Status }); entity.HasOne(x => x.CourseSelectionOffering) .WithMany(x => x.Enrollments) .HasForeignKey(x => x.CourseSelectionOfferingId) .OnDelete(DeleteBehavior.Cascade); entity.HasOne(x => x.Student) .WithMany() .HasForeignKey(x => x.StudentId) .OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.Property(x => x.RegularWeight).HasPrecision(5, 1); entity.Property(x => x.FinalWeight).HasPrecision(5, 1); entity.Property(x => x.ReviewComment).HasMaxLength(500); entity.HasIndex(x => x.TeachingTaskId).IsUnique(); entity.HasIndex(x => x.Status); entity.HasOne(x => x.TeachingTask) .WithMany() .HasForeignKey(x => x.TeachingTaskId) .OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.Property(x => x.Name).HasMaxLength(60); entity.Property(x => x.Weight).HasPrecision(5, 1); entity.HasIndex(x => new { x.GradeSheetId, x.SortOrder }); entity.HasOne(x => x.GradeSheet) .WithMany(x => x.Items) .HasForeignKey(x => x.GradeSheetId) .OnDelete(DeleteBehavior.Cascade); }); builder.Entity(entity => { entity.Property(x => x.RegularScore).HasPrecision(5, 1); entity.Property(x => x.FinalScore).HasPrecision(5, 1); entity.Property(x => x.TotalScore).HasPrecision(5, 1); entity.Property(x => x.GradePoint).HasPrecision(3, 1); entity.Property(x => x.Notes).HasMaxLength(300); entity.HasIndex(x => new { x.GradeSheetId, x.StudentId }).IsUnique(); entity.HasIndex(x => new { x.StudentId, x.TotalScore }); entity.HasOne(x => x.GradeSheet) .WithMany(x => x.Records) .HasForeignKey(x => x.GradeSheetId) .OnDelete(DeleteBehavior.Cascade); entity.HasOne(x => x.Student) .WithMany() .HasForeignKey(x => x.StudentId) .OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.HasKey(x => new { x.GradeRecordId, x.GradeItemId }); entity.Property(x => x.Score).HasPrecision(5, 1); entity.HasOne(x => x.GradeRecord) .WithMany(x => x.ItemScores) .HasForeignKey(x => x.GradeRecordId) .OnDelete(DeleteBehavior.Cascade); entity.HasOne(x => x.GradeItem) .WithMany(x => x.Scores) .HasForeignKey(x => x.GradeItemId) .OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.Property(x => x.Name).HasMaxLength(120); entity.Property(x => x.Notes).HasMaxLength(500); entity.HasIndex(x => new { x.TeachingTaskId, x.AttendanceDate }); entity.HasOne(x => x.TeachingTask) .WithMany() .HasForeignKey(x => x.TeachingTaskId) .OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.HasKey(x => new { x.AttendanceSheetId, x.StudentId }); entity.Property(x => x.Notes).HasMaxLength(300); entity.Property(x => x.AppealReason).HasMaxLength(500); entity.Property(x => x.AppealReviewComment).HasMaxLength(300); entity.HasIndex(x => x.AppealStatus); entity.HasOne(x => x.AttendanceSheet) .WithMany(x => x.Records) .HasForeignKey(x => x.AttendanceSheetId) .OnDelete(DeleteBehavior.Cascade); entity.HasOne(x => x.Student) .WithMany() .HasForeignKey(x => x.StudentId) .OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.Property(x => x.Name).HasMaxLength(120); entity.Property(x => x.Notes).HasMaxLength(500); entity.HasIndex(x => new { x.AcademicTermId, x.Status }); entity.HasOne(x => x.AcademicTerm).WithMany() .HasForeignKey(x => x.AcademicTermId).OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.Property(x => x.Notes).HasMaxLength(500); entity.HasIndex(x => new { x.ExamPlanId, x.StartsAt }); entity.HasIndex(x => x.TeachingTaskId); entity.HasIndex(x => new { x.ExamPlanId, x.ExamDate }); entity.HasIndex(x => x.RequiredBuildingId); entity.HasOne(x => x.ExamPlan).WithMany(x => x.Sessions) .HasForeignKey(x => x.ExamPlanId).OnDelete(DeleteBehavior.Cascade); entity.HasOne(x => x.TeachingTask).WithMany() .HasForeignKey(x => x.TeachingTaskId).OnDelete(DeleteBehavior.Restrict); entity.HasOne(x => x.Classroom).WithMany() .HasForeignKey(x => x.ClassroomId).OnDelete(DeleteBehavior.SetNull); entity.HasOne(x => x.RequiredBuilding).WithMany() .HasForeignKey(x => x.RequiredBuildingId).OnDelete(DeleteBehavior.SetNull); }); builder.Entity(entity => { entity.HasKey(x => new { x.ExamSessionId, x.TeacherId }); entity.HasOne(x => x.ExamSession).WithMany(x => x.Invigilators) .HasForeignKey(x => x.ExamSessionId).OnDelete(DeleteBehavior.Cascade); entity.HasOne(x => x.Teacher).WithMany() .HasForeignKey(x => x.TeacherId).OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.Property(x => x.Name).HasMaxLength(120); entity.Property(x => x.Notes).HasMaxLength(500); entity.HasIndex(x => new { x.AcademicTermId, x.Status }); entity.HasOne(x => x.AcademicTerm).WithMany() .HasForeignKey(x => x.AcademicTermId).OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.Property(x => x.Notes).HasMaxLength(500); entity.HasIndex(x => new { x.MakeupExamPlanId, x.StartsAt }); entity.HasIndex(x => x.TeachingTaskId); entity.HasIndex(x => new { x.MakeupExamPlanId, x.ExamDate }); entity.HasIndex(x => x.RequiredBuildingId); entity.HasOne(x => x.MakeupExamPlan).WithMany(x => x.Sessions) .HasForeignKey(x => x.MakeupExamPlanId).OnDelete(DeleteBehavior.Cascade); entity.HasOne(x => x.TeachingTask).WithMany() .HasForeignKey(x => x.TeachingTaskId).OnDelete(DeleteBehavior.Restrict); entity.HasOne(x => x.Classroom).WithMany() .HasForeignKey(x => x.ClassroomId).OnDelete(DeleteBehavior.SetNull); entity.HasOne(x => x.RequiredBuilding).WithMany() .HasForeignKey(x => x.RequiredBuildingId).OnDelete(DeleteBehavior.SetNull); }); builder.Entity(entity => { entity.HasKey(x => new { x.MakeupExamSessionId, x.TeacherId }); entity.HasOne(x => x.MakeupExamSession).WithMany(x => x.Invigilators) .HasForeignKey(x => x.MakeupExamSessionId).OnDelete(DeleteBehavior.Cascade); entity.HasOne(x => x.Teacher).WithMany() .HasForeignKey(x => x.TeacherId).OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.HasKey(x => new { x.MakeupExamSessionId, x.StudentId }); entity.HasIndex(x => new { x.StudentId, x.MakeupExamSessionId }); entity.Property(x => x.MakeupScore).HasPrecision(5, 1); entity.HasOne(x => x.MakeupExamSession).WithMany(x => x.Enrollments) .HasForeignKey(x => x.MakeupExamSessionId).OnDelete(DeleteBehavior.Cascade); entity.HasOne(x => x.Student).WithMany() .HasForeignKey(x => x.StudentId).OnDelete(DeleteBehavior.Restrict); entity.HasOne(x => x.SourceGradeRecord).WithMany() .HasForeignKey(x => x.SourceGradeRecordId).OnDelete(DeleteBehavior.SetNull); entity.HasOne(x => x.SourceDeferredExam).WithMany() .HasForeignKey(x => x.SourceDeferredExamId).OnDelete(DeleteBehavior.SetNull); }); builder.Entity(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(entity => { entity.Property(x => x.Reason).HasMaxLength(1000); entity.Property(x => x.ReviewComment).HasMaxLength(500); entity.HasIndex(x => new { x.StudentId, x.State }); entity.HasOne(x => x.Student).WithMany() .HasForeignKey(x => x.StudentId).OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.Property(x => x.Name).HasMaxLength(120); entity.Property(x => x.Notes).HasMaxLength(500); entity.HasIndex(x => new { x.GraduationYear, x.EnrollmentYear }); entity.HasIndex(x => x.Status); }); builder.Entity(entity => { entity.Property(x => x.RequiredCredits).HasPrecision(6, 2); entity.Property(x => x.EarnedCredits).HasPrecision(6, 2); entity.Property(x => x.MissingCourseNames).HasMaxLength(2000); entity.Property(x => x.ReviewComment).HasMaxLength(500); entity.HasIndex(x => new { x.GraduationAuditBatchId, x.StudentId }) .IsUnique(); entity.HasIndex(x => new { x.Conclusion, x.IsOverridden }); entity.HasOne(x => x.GraduationAuditBatch).WithMany(x => x.Results) .HasForeignKey(x => x.GraduationAuditBatchId) .OnDelete(DeleteBehavior.Cascade); entity.HasOne(x => x.Student).WithMany() .HasForeignKey(x => x.StudentId).OnDelete(DeleteBehavior.Restrict); entity.HasOne(x => x.CurriculumPlan).WithMany() .HasForeignKey(x => x.CurriculumPlanId).OnDelete(DeleteBehavior.SetNull); }); builder.Entity(entity => { entity.Property(x => x.Name).HasMaxLength(120); entity.Property(x => x.DegreeName).HasMaxLength(80); entity.Property(x => x.MinimumGradePoint).HasPrecision(3, 2); entity.Property(x => x.Notes).HasMaxLength(500); entity.HasIndex(x => new { x.GraduationYear, x.Status }); }); builder.Entity(entity => { entity.Property(x => x.AverageGradePoint).HasPrecision(4, 2); entity.Property(x => x.ExceptionReason).HasMaxLength(500); entity.Property(x => x.ReviewComment).HasMaxLength(500); entity.HasIndex(x => new { x.DegreeAwardBatchId, x.StudentId }).IsUnique(); entity.HasIndex(x => new { x.Conclusion, x.IsOverridden }); entity.HasOne(x => x.DegreeAwardBatch).WithMany(x => x.Results) .HasForeignKey(x => x.DegreeAwardBatchId).OnDelete(DeleteBehavior.Cascade); entity.HasOne(x => x.Student).WithMany() .HasForeignKey(x => x.StudentId).OnDelete(DeleteBehavior.Restrict); entity.HasOne(x => x.GraduationAuditResult).WithMany() .HasForeignKey(x => x.GraduationAuditResultId) .OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.Property(x => x.Name).HasMaxLength(120); entity.Property(x => x.Notes).HasMaxLength(500); entity.HasIndex(x => new { x.GraduationYear, x.Status }); }); builder.Entity(entity => { entity.Property(x => x.Code).HasMaxLength(30); entity.Property(x => x.Name).HasMaxLength(100); entity.Property(x => x.ResponsibleUnit).HasMaxLength(100); entity.Property(x => x.ResponsibleRole).HasMaxLength(30); entity.HasIndex(x => new { x.GraduationClearanceBatchId, x.Code }).IsUnique(); entity.HasOne(x => x.GraduationClearanceBatch).WithMany(x => x.Items) .HasForeignKey(x => x.GraduationClearanceBatchId) .OnDelete(DeleteBehavior.Cascade); }); builder.Entity(entity => { entity.Property(x => x.Notes).HasMaxLength(500); entity.HasIndex(x => new { x.GraduationClearanceItemId, x.StudentId }) .IsUnique(); entity.HasIndex(x => new { x.StudentId, x.Status }); entity.HasOne(x => x.GraduationClearanceItem).WithMany(x => x.Records) .HasForeignKey(x => x.GraduationClearanceItemId) .OnDelete(DeleteBehavior.Cascade); entity.HasOne(x => x.Student).WithMany() .HasForeignKey(x => x.StudentId).OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.Property(x => x.Reason).HasMaxLength(500); entity.Property(x => x.ReviewComment).HasMaxLength(500); entity.HasIndex(x => new { x.Status, x.CreatedAt }); entity.HasIndex(x => new { x.StudentId, x.TeachingTaskId }).IsUnique(); entity.HasOne(x => x.Student).WithMany() .HasForeignKey(x => x.StudentId).OnDelete(DeleteBehavior.Restrict); entity.HasOne(x => x.TeachingTask).WithMany() .HasForeignKey(x => x.TeachingTaskId).OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.Property(x => x.Reason).HasMaxLength(500); entity.Property(x => x.ReviewComment).HasMaxLength(500); entity.HasIndex(x => new { x.Status, x.CreatedAt }); entity.HasIndex(x => new { x.StudentId, x.TeachingTaskId }).IsUnique(); entity.HasOne(x => x.Student).WithMany() .HasForeignKey(x => x.StudentId).OnDelete(DeleteBehavior.Restrict); entity.HasOne(x => x.TeachingTask).WithMany() .HasForeignKey(x => x.TeachingTaskId).OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.Property(x => x.CurrentScore).HasPrecision(5, 1); entity.Property(x => x.RequestedScore).HasPrecision(5, 1); entity.Property(x => x.Reason).HasMaxLength(500); entity.Property(x => x.ReviewComment).HasMaxLength(500); entity.HasIndex(x => new { x.Status, x.CreatedAt }); entity.HasOne(x => x.GradeRecord).WithMany() .HasForeignKey(x => x.GradeRecordId).OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.Property(x => x.Name).HasMaxLength(100); entity.Property(x => x.Threshold).HasPrecision(7, 2); entity.Property(x => x.Description).HasMaxLength(300); entity.HasIndex(x => new { x.AcademicTermId, x.Type }).IsUnique(); entity.HasOne(x => x.AcademicTerm).WithMany() .HasForeignKey(x => x.AcademicTermId).OnDelete(DeleteBehavior.Cascade); }); builder.Entity(entity => { entity.Property(x => x.TriggerValue).HasPrecision(7, 2); entity.Property(x => x.Detail).HasMaxLength(1000); entity.Property(x => x.AcknowledgeComment).HasMaxLength(300); entity.HasIndex(x => new { x.StudentId, x.AcademicTermId, x.Type }).IsUnique(); entity.HasIndex(x => x.Status); entity.HasOne(x => x.Student).WithMany() .HasForeignKey(x => x.StudentId).OnDelete(DeleteBehavior.Restrict); }); // ── Evaluation ── builder.Entity(entity => { entity.Property(x => x.Name).HasMaxLength(120); entity.HasIndex(x => new { x.AcademicTermId, x.Status }); entity.HasOne(x => x.AcademicTerm).WithMany() .HasForeignKey(x => x.AcademicTermId).OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.Property(x => x.Name).HasMaxLength(60); entity.HasIndex(x => new { x.EvaluationSetupId, x.SortOrder }); entity.HasOne(x => x.EvaluationSetup).WithMany(x => x.Dimensions) .HasForeignKey(x => x.EvaluationSetupId).OnDelete(DeleteBehavior.Cascade); }); builder.Entity(entity => { entity.HasIndex(x => new { x.EvaluationSetupId, x.StudentId, x.TeachingTaskId }).IsUnique(); entity.HasOne(x => x.EvaluationSetup).WithMany(x => x.Records) .HasForeignKey(x => x.EvaluationSetupId).OnDelete(DeleteBehavior.Restrict); entity.HasOne(x => x.Student).WithMany() .HasForeignKey(x => x.StudentId).OnDelete(DeleteBehavior.Restrict); entity.HasOne(x => x.TeachingTask).WithMany() .HasForeignKey(x => x.TeachingTaskId).OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.HasKey(x => new { x.EvaluationRecordId, x.EvaluationDimensionId }); entity.HasOne(x => x.EvaluationRecord).WithMany(x => x.Scores) .HasForeignKey(x => x.EvaluationRecordId).OnDelete(DeleteBehavior.Cascade); entity.HasOne(x => x.EvaluationDimension).WithMany(x => x.Scores) .HasForeignKey(x => x.EvaluationDimensionId).OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.Property(x => x.Reason).HasMaxLength(500); entity.Property(x => x.ReviewComment).HasMaxLength(500); entity.HasIndex(x => new { x.Status, x.CreatedAt }); entity.HasIndex(x => new { x.StudentId, x.OriginalCourseId }).IsUnique(); entity.HasOne(x => x.Student).WithMany() .HasForeignKey(x => x.StudentId).OnDelete(DeleteBehavior.Restrict); entity.HasOne(x => x.OriginalCourse).WithMany() .HasForeignKey(x => x.OriginalCourseId).OnDelete(DeleteBehavior.Restrict); entity.HasOne(x => x.SubstituteCourse).WithMany() .HasForeignKey(x => x.SubstituteCourseId).OnDelete(DeleteBehavior.Restrict); }); builder.Entity(entity => { entity.Property(x => x.Reason).HasMaxLength(500); entity.Property(x => x.ReviewComment).HasMaxLength(500); entity.HasIndex(x => new { x.TeachingTaskId, x.Status }); entity.HasIndex(x => x.ApplicantUserId); entity.HasIndex(x => new { x.Status, x.CreatedAt }); entity.HasOne(x => x.TeachingTask).WithMany() .HasForeignKey(x => x.TeachingTaskId).OnDelete(DeleteBehavior.Restrict); entity.HasOne(x => x.Classroom).WithMany() .HasForeignKey(x => x.ClassroomId).OnDelete(DeleteBehavior.SetNull); entity.HasOne(x => x.SubstituteTeacher).WithMany() .HasForeignKey(x => x.SubstituteTeacherId).OnDelete(DeleteBehavior.SetNull); }); builder.Entity(entity => { entity.Property(x => x.Title).HasMaxLength(200); entity.Property(x => x.Content).HasMaxLength(1000); entity.Property(x => x.LinkUrl).HasMaxLength(300); entity.HasIndex(x => new { x.UserId, x.IsRead }); entity.HasIndex(x => x.CreatedAt); }); builder.Entity(entity => { entity.Property(x => x.Method).HasMaxLength(10); entity.Property(x => x.Path).HasMaxLength(300); entity.Property(x => x.UserName).HasMaxLength(100); entity.Property(x => x.IpAddress).HasMaxLength(64); entity.HasIndex(x => x.CreatedAt); }); } public override Task SaveChangesAsync(CancellationToken cancellationToken = default) { foreach (var entry in ChangeTracker.Entries() .Where(x => x.State == EntityState.Modified)) { entry.Entity.UpdatedAt = DateTime.UtcNow; } return base.SaveChangesAsync(cancellationToken); } private static void ConfigureCatalog(ModelBuilder builder) where TEntity : CatalogEntity { builder.Entity(entity => { entity.Property(x => x.Code).HasMaxLength(40); entity.Property(x => x.Name).HasMaxLength(100); entity.HasIndex(x => x.Code).IsUnique(); entity.HasIndex(x => new { x.IsEnabled, x.SortOrder }); }); } }