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 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 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); 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() .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.HasOne(x => x.College) .WithMany() .HasForeignKey(x => x.CollegeId) .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.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.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 }); }); } }