using AgriculturalPlatform.Api.Models; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace AgriculturalPlatform.Api.Data; public class AppDbContext(DbContextOptions options) : DbContext(options) { public DbSet Users => Set(); public DbSet Organizations => Set(); public DbSet Farmers => Set(); public DbSet Regions => Set(); public DbSet Products => Set(); public DbSet PurchaseOrders => Set(); public DbSet PurchaseWeighs => Set(); public DbSet PurchaseTransfers => Set(); public DbSet PaymentRecords => Set(); public DbSet Invoices => Set(); // 系统管理(RBAC) public DbSet SysDicts => Set(); public DbSet SysDictItems => Set(); public DbSet SysMenus => Set(); public DbSet SysRoles => Set(); public DbSet SysRoleMenus => Set(); // 通知公告 public DbSet Announcements => Set(); public DbSet AnnouncementReads => Set(); protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // 所有枚举属性以字符串形式存储,便于阅读 foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { foreach (var property in entityType.GetProperties()) { if (property.ClrType.IsEnum) { var converterType = typeof(EnumToStringConverter<>).MakeGenericType(property.ClrType); var converter = (ValueConverter)Activator.CreateInstance(converterType)!; property.SetValueConverter(converter); } } } // 用户 modelBuilder.Entity() .Property(u => u.Username).HasMaxLength(50); modelBuilder.Entity() .HasIndex(u => u.Username).IsUnique(); modelBuilder.Entity() .HasOne(u => u.Org).WithMany().HasForeignKey(u => u.OrgId) .OnDelete(DeleteBehavior.SetNull); modelBuilder.Entity() .HasOne(u => u.SysRole).WithMany().HasForeignKey(u => u.RoleId) .OnDelete(DeleteBehavior.SetNull); // 组织 modelBuilder.Entity() .Property(o => o.Type).HasMaxLength(32); modelBuilder.Entity() .Property(o => o.Name).HasMaxLength(200); modelBuilder.Entity() .HasIndex(o => new { o.Type, o.Name }); modelBuilder.Entity() .HasOne(o => o.Parent).WithMany().HasForeignKey(o => o.ParentId) .OnDelete(DeleteBehavior.Restrict); // 农户:身份证唯一 modelBuilder.Entity() .Property(f => f.IdCard).HasMaxLength(18); modelBuilder.Entity() .HasIndex(f => f.IdCard).IsUnique(); modelBuilder.Entity() .Property(f => f.Phone).HasMaxLength(20); modelBuilder.Entity() .HasIndex(f => f.Phone); modelBuilder.Entity() .Property(f => f.FarmerType).HasMaxLength(20); modelBuilder.Entity() .Property(f => f.IdCardFrontUrl).HasMaxLength(500); modelBuilder.Entity() .Property(f => f.IdCardBackUrl).HasMaxLength(500); modelBuilder.Entity() .Property(f => f.Province).HasMaxLength(50); modelBuilder.Entity() .Property(f => f.County).HasMaxLength(50); modelBuilder.Entity() .Property(f => f.Township).HasMaxLength(50); modelBuilder.Entity() .Property(f => f.Village).HasMaxLength(100); modelBuilder.Entity() .Property(f => f.GroupName).HasMaxLength(50); modelBuilder.Entity() .HasIndex(f => new { f.Province, f.County, f.Township }); // 行政区划(AreaCity 全国数据,表由导入脚本/启动建表创建) modelBuilder.Entity() .HasKey(r => r.Id); modelBuilder.Entity() .Property(r => r.Name).HasMaxLength(100); modelBuilder.Entity() .Property(r => r.ExtName).HasMaxLength(100); modelBuilder.Entity() .HasIndex(r => r.Pid); modelBuilder.Entity() .HasIndex(r => r.Deep); // 品种 modelBuilder.Entity() .Property(p => p.Name).HasMaxLength(100); modelBuilder.Entity() .HasIndex(p => p.Name).IsUnique(); // 收购单 modelBuilder.Entity() .Property(p => p.OrderNo).HasMaxLength(32); modelBuilder.Entity() .HasIndex(p => p.OrderNo).IsUnique(); modelBuilder.Entity() .HasIndex(p => new { p.Status, p.CreatedAt }); modelBuilder.Entity() .HasOne(p => p.Farmer).WithMany().HasForeignKey(p => p.FarmerId) .OnDelete(DeleteBehavior.Restrict); modelBuilder.Entity() .HasOne(p => p.Product).WithMany().HasForeignKey(p => p.ProductId) .OnDelete(DeleteBehavior.Restrict); modelBuilder.Entity() .HasOne(p => p.PurchaserOrg).WithMany().HasForeignKey(p => p.PurchaserOrgId) .OnDelete(DeleteBehavior.Restrict); modelBuilder.Entity() .HasOne(p => p.Operator).WithMany().HasForeignKey(p => p.OperatorId) .OnDelete(DeleteBehavior.SetNull); // 收购单-磅次 modelBuilder.Entity() .HasOne(w => w.PurchaseOrder).WithMany(p => p.Weighs) .HasForeignKey(w => w.PurchaseOrderId) .OnDelete(DeleteBehavior.Cascade); // 票据过户 modelBuilder.Entity() .HasOne(t => t.PurchaseOrder).WithMany(p => p.Transfers) .HasForeignKey(t => t.PurchaseOrderId) .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() .HasOne(t => t.Operator).WithMany().HasForeignKey(t => t.OperatorId) .OnDelete(DeleteBehavior.SetNull); // 数据字典 modelBuilder.Entity() .Property(d => d.Code).HasMaxLength(64); modelBuilder.Entity() .HasIndex(d => d.Code).IsUnique(); modelBuilder.Entity() .Property(d => d.Name).HasMaxLength(100); modelBuilder.Entity() .Property(i => i.Label).HasMaxLength(100); modelBuilder.Entity() .Property(i => i.Value).HasMaxLength(64); modelBuilder.Entity() .HasIndex(i => new { i.DictId, i.Value }).IsUnique(); modelBuilder.Entity() .HasMany(d => d.Items).WithOne().HasForeignKey(i => i.DictId) .OnDelete(DeleteBehavior.Cascade); // 菜单(自引用父子关系显式指定外键,避免 EF 推断额外列) modelBuilder.Entity() .Property(m => m.Name).HasMaxLength(100); modelBuilder.Entity() .Property(m => m.Path).HasMaxLength(200); modelBuilder.Entity() .Property(m => m.Permission).HasMaxLength(100); modelBuilder.Entity() .HasIndex(m => m.Permission); modelBuilder.Entity() .HasMany(m => m.Children).WithOne().HasForeignKey(m => m.ParentId) .OnDelete(DeleteBehavior.Restrict); // 角色 modelBuilder.Entity() .Property(r => r.Name).HasMaxLength(100); modelBuilder.Entity() .HasIndex(r => r.Code).IsUnique(); modelBuilder.Entity() .HasKey(rm => new { rm.RoleId, rm.MenuId }); // 公告 modelBuilder.Entity() .Property(a => a.Title).HasMaxLength(200); modelBuilder.Entity() .HasIndex(a => new { a.Type, a.PublishedAt }); modelBuilder.Entity() .HasOne(a => a.Publisher).WithMany().HasForeignKey(a => a.PublisherId) .OnDelete(DeleteBehavior.SetNull); modelBuilder.Entity() .HasIndex(r => new { r.AnnouncementId, r.UserId }).IsUnique(); // 支付 modelBuilder.Entity() .Property(p => p.PayNo).HasMaxLength(32); modelBuilder.Entity() .HasIndex(p => p.PayNo).IsUnique(); modelBuilder.Entity() .HasIndex(p => new { p.Status, p.CreatedAt }); modelBuilder.Entity() .HasOne(p => p.Farmer).WithMany().HasForeignKey(p => p.FarmerId) .OnDelete(DeleteBehavior.Restrict); modelBuilder.Entity() .HasOne(p => p.PurchaseOrder).WithMany().HasForeignKey(p => p.PurchaseOrderId) .OnDelete(DeleteBehavior.SetNull); modelBuilder.Entity() .HasOne(p => p.PurchaserOrg).WithMany().HasForeignKey(p => p.PurchaserOrgId) .OnDelete(DeleteBehavior.Restrict); modelBuilder.Entity() .HasOne(p => p.Operator).WithMany().HasForeignKey(p => p.OperatorId) .OnDelete(DeleteBehavior.SetNull); // 发票 modelBuilder.Entity() .Property(i => i.InvoiceNo).HasMaxLength(32); modelBuilder.Entity() .HasIndex(i => i.InvoiceNo).IsUnique(); modelBuilder.Entity() .Property(i => i.BatchNo).HasMaxLength(32); modelBuilder.Entity() .HasIndex(i => i.BatchNo); modelBuilder.Entity() .HasIndex(i => new { i.Status, i.IssueDate }); modelBuilder.Entity() .HasOne(i => i.Farmer).WithMany().HasForeignKey(i => i.FarmerId) .OnDelete(DeleteBehavior.Restrict); modelBuilder.Entity() .HasOne(i => i.PurchaserOrg).WithMany().HasForeignKey(i => i.PurchaserOrgId) .OnDelete(DeleteBehavior.Restrict); modelBuilder.Entity() .HasOne(i => i.Product).WithMany().HasForeignKey(i => i.ProductId) .OnDelete(DeleteBehavior.Restrict); modelBuilder.Entity() .HasOne(i => i.PurchaseOrder).WithMany().HasForeignKey(i => i.PurchaseOrderId) .OnDelete(DeleteBehavior.SetNull); modelBuilder.Entity() .HasOne(i => i.Operator).WithMany().HasForeignKey(i => i.OperatorId) .OnDelete(DeleteBehavior.SetNull); // 金额/重量精度 foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { foreach (var property in entityType.GetProperties()) { if (property.ClrType == typeof(decimal) || property.ClrType == typeof(decimal?)) { property.SetPrecision(18); property.SetScale(2); } } } } }