diff --git a/XisongSpaceBooking_BackEnd/Configurations/SpaceBookingDbContext.cs b/XisongSpaceBooking_BackEnd/Configurations/SpaceBookingDbContext.cs new file mode 100644 index 0000000..f6297bf --- /dev/null +++ b/XisongSpaceBooking_BackEnd/Configurations/SpaceBookingDbContext.cs @@ -0,0 +1,255 @@ +using Microsoft.EntityFrameworkCore; +using Models.Entities; +using XisongSpaceBooking_BackEnd.Models.Entities; + +namespace XisongSpaceBooking_BackEnd.Configurations +{ + /// + /// 空間預約系統的資料庫內容類別 + /// + public class SpaceBookingDbContext : DbContext + { + public SpaceBookingDbContext(DbContextOptions options) : base(options) + { + } + + /// + /// 處室資料集 + /// + public DbSet Departments { get; set; } + + /// + /// 身份資料集 + /// + public DbSet Roles { get; set; } + + /// + /// 帳號資料集 + /// + public DbSet Accounts { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + // 配置 Department 實體 + ConfigureDepartment(modelBuilder); + + // 配置 Role 實體 + ConfigureRole(modelBuilder); + + // 配置 Account 實體 + ConfigureAccount(modelBuilder); + } + + /// + /// 配置 Department 實體 + /// + /// 模型建構器 + private static void ConfigureDepartment(ModelBuilder modelBuilder) + { + var entity = modelBuilder.Entity(); + + // 設定資料表名稱 + entity.ToTable("departments"); + + // 設定主鍵 + entity.HasKey(d => d.DepartmentId); + entity.Property(d => d.DepartmentId) + .HasColumnName("department_id") + .ValueGeneratedOnAdd() + .HasComment("處室 ID"); + + // 設定處室名稱 + entity.Property(d => d.DepartmentName) + .HasColumnName("department_name") + .HasMaxLength(20) + .IsRequired() + .HasComment("處室名稱"); + } + + /// + /// 配置 Role 實體 + /// + /// 模型建構器 + private static void ConfigureRole(ModelBuilder modelBuilder) + { + var entity = modelBuilder.Entity(); + + // 設定資料表名稱 + entity.ToTable("roles"); + + // 設定主鍵 + entity.HasKey(r => r.RoleId); + entity.Property(r => r.RoleId) + .HasColumnName("role_id") + .ValueGeneratedOnAdd() + .HasComment("身份 ID"); + + // 設定身份名稱 + entity.Property(r => r.RoleName) + .HasColumnName("role_name") + .HasMaxLength(20) + .IsRequired() + .HasComment("身份名稱"); + } + + /// + /// 配置 Account 實體 + /// + /// 模型建構器 + private static void ConfigureAccount(ModelBuilder modelBuilder) + { + var entity = modelBuilder.Entity(); + + // 設定資料表名稱 + entity.ToTable("accounts"); + + // 設定主鍵 + entity.HasKey(a => a.AccountId); + entity.Property(a => a.AccountId) + .HasColumnName("account_id") + .ValueGeneratedOnAdd() + .HasComment("帳號 ID"); + + // 設定使用者姓名 + entity.Property(a => a.Name) + .HasColumnName("name") + .HasMaxLength(20) + .IsRequired() + .HasComment("使用者姓名"); + + // 設定帳號名稱(唯一) + entity.Property(a => a.Username) + .HasColumnName("username") + .HasMaxLength(20) + .IsRequired() + .HasComment("帳號名稱"); + + // 設定密碼 + entity.Property(a => a.Password) + .HasColumnName("password") + .HasMaxLength(255) + .IsRequired() + .HasComment("密碼(加密)"); + + // 設定電子郵件(唯一) + entity.Property(a => a.Email) + .HasColumnName("email") + .HasMaxLength(50) + .IsRequired() + .HasComment("電子郵件"); + + // 設定處室 ID(外鍵) + entity.Property(a => a.DepartmentId) + .HasColumnName("department_id") + .IsRequired() + .HasComment("處室 ID"); + + // 設定身份 ID(外鍵) + entity.Property(a => a.RoleId) + .HasColumnName("role_id") + .IsRequired() + .HasComment("身份 ID"); + + // 設定帳號狀態(轉換為字串儲存) + entity.Property(a => a.Status) + .HasColumnName("status") + .HasConversion( + v => v.ToString().ToLower(), + v => Enum.Parse(v, true)) + .HasMaxLength(20) + .HasDefaultValue(AccountStatus.Unverified) + .IsRequired() + .HasComment("帳號狀態"); + + // 設定 BaseEntity 屬性 + entity.Property(a => a.CreatedAt) + .HasColumnName("created_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP") + .IsRequired() + .HasComment("建立時間"); + + entity.Property(a => a.UpdatedAt) + .HasColumnName("updated_at") + .ValueGeneratedOnUpdate() + .HasComment("更新時間"); + + entity.Property(a => a.ModifiedBy) + .HasColumnName("modified_by") + .IsRequired(false) + .HasComment("最後修改者帳號 ID"); + + // 設定唯一索引 + entity.HasIndex(a => a.Username) + .IsUnique() + .HasDatabaseName("IX_accounts_username"); + + entity.HasIndex(a => a.Email) + .IsUnique() + .HasDatabaseName("IX_accounts_email"); + + // 設定外鍵關聯 + entity.HasOne(a => a.Department) + .WithMany() + .HasForeignKey(a => a.DepartmentId) + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("FK_accounts_departments"); + + entity.HasOne(a => a.Role) + .WithMany() + .HasForeignKey(a => a.RoleId) + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("FK_accounts_roles"); + + // 設定自參考外鍵(ModifiedBy) + entity.HasOne() + .WithMany() + .HasForeignKey(a => a.ModifiedBy) + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_accounts_modified_by"); + } + + /// + /// 覆寫 SaveChanges 以自動更新 UpdatedAt 欄位 + /// + public override int SaveChanges() + { + UpdateTimestamps(); + return base.SaveChanges(); + } + + /// + /// 覆寫 SaveChangesAsync 以自動更新 UpdatedAt 欄位 + /// + public override async Task SaveChangesAsync(CancellationToken cancellationToken = default) + { + UpdateTimestamps(); + return await base.SaveChangesAsync(cancellationToken); + } + + /// + /// 更新時間戳記 + /// + private void UpdateTimestamps() + { + var entries = ChangeTracker.Entries(); + + foreach (var entry in entries) + { + switch (entry.State) + { + case EntityState.Added: + entry.Entity.UpdatedAt = DateTime.UtcNow; + break; + + case EntityState.Modified: + entry.Entity.UpdatedAt = DateTime.UtcNow; + // 確保 CreatedAt 不會被修改 + entry.Property(e => e.CreatedAt).IsModified = false; + break; + } + } + } + } +} \ No newline at end of file