using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Haoliang.Data.Entities; using Haoliang.Models.System; namespace Haoliang.Data { public class CNCBusinessDbContext : DbContext { public CNCBusinessDbContext(DbContextOptions options) : base(options) { } // Device Management public DbSet Devices { get; set; } public DbSet DeviceCurrentStatus { get; set; } public DbSet TagData { get; set; } // DeviceStatus as string property (since it's an enum) public string DeviceStatus { get; set; } = null!; // Template Management public DbSet CNCTemplates { get; set; } public DbSet TagMappings { get; set; } // Production Management public DbSet ProductionRecords { get; set; } public DbSet ProgramProductionSummary { get; set; } public DbSet ProductionSummaries { get; set; } // User Management public DbSet Users { get; set; } public DbSet Roles { get; set; } public DbSet Employees { get; set; } public DbSet UserPermissions { get; set; } public DbSet RolePermissions { get; set; } public DbSet UserSessions { get; set; } public DbSet PasswordResets { get; set; } // System Management public DbSet Alarms { get; set; } public DbSet AlarmRules { get; set; } public DbSet AlarmNotifications { get; set; } public DbSet SystemConfigs { get; set; } public DbSet LogEntries { get; set; } public DbSet StatisticRules { get; set; } public DbSet StatisticResults { get; set; } // Data Collection public DbSet CollectionTasks { get; set; } public DbSet CollectionResults { get; set; } public DbSet CollectionLogs { get; set; } public DbSet CollectionConfigs { get; set; } // Scheduled Tasks public DbSet ScheduledTasks { get; set; } public DbSet TaskExecutionResults { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // Configure MySQL-specific settings foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { // Set default charset and collation if (typeof(BaseEntity).IsAssignableFrom(entityType.ClrType)) { modelBuilder.Entity(entityType.ClrType) .ToTable(entityType.GetTableName() ?? "", t => t .charset("utf8mb4") .collation("utf8mb4_unicode_ci")); } } // Configure relationships ConfigureDeviceRelationships(modelBuilder); ConfigureUserRelationships(modelBuilder); ConfigureAlarmRelationships(modelBuilder); ConfigureCollectionRelationships(modelBuilder); ConfigureProductionRelationships(modelBuilder); ConfigureTemplateRelationships(modelBuilder); // Configure indexes ConfigureIndexes(modelBuilder); // Configure constraints ConfigureConstraints(modelBuilder); // Configure data conversions ConfigureDataConversions(modelBuilder); } private void ConfigureDeviceRelationships(ModelBuilder modelBuilder) { modelBuilder.Entity() .HasMany(d => d.DeviceStatus) .WithOne() .HasForeignKey(ds => ds.DeviceId) .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() .HasMany(d => d.CollectionResults) .WithOne() .HasForeignKey(cr => cr.DeviceId) .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() .HasMany(d => d.CollectionLogs) .WithOne() .HasForeignKey(cl => cl.DeviceId) .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() .HasMany(d => d.ProductionRecords) .WithOne() .HasForeignKey(pr => pr.DeviceId) .OnDelete(DeleteBehavior.Cascade); } private void ConfigureUserRelationships(ModelBuilder modelBuilder) { modelBuilder.Entity() .HasOne(u => u.Role) .WithMany() .HasForeignKey(u => u.RoleId) .OnDelete(DeleteBehavior.Restrict); modelBuilder.Entity() .HasMany(u => u.UserPermissions) .WithOne(up => up.User) .HasForeignKey(up => up.UserId) .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() .HasMany(u => u.UserSessions) .WithOne(us => us.User) .HasForeignKey(us => us.UserId) .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() .HasMany(u => u.PasswordResets) .WithOne(pr => pr.User) .HasForeignKey(pr => pr.UserId) .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() .HasMany(r => r.RolePermissions) .WithOne(rp => rp.Role) .HasForeignKey(rp => rp.RoleId) .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() .HasMany(e => e.DeviceAssignments) .WithOne(da => da.Employee) .HasForeignKey(da => da.EmployeeId) .OnDelete(DeleteBehavior.Cascade); } private void ConfigureAlarmRelationships(ModelBuilder modelBuilder) { modelBuilder.Entity() .HasMany(a => a.AlarmNotifications) .WithOne(an => an.Alarm) .HasForeignKey(an => an.AlarmId) .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() .HasMany(ar => ar.Alarms) .WithOne(a => a.AlarmRule) .HasForeignKey(a => a.AlarmRuleId) .OnDelete(DeleteBehavior.Restrict); } private void ConfigureCollectionRelationships(ModelBuilder modelBuilder) { modelBuilder.Entity() .HasMany(ct => ct.CollectionResults) .WithOne(cr => cr.CollectionTask) .HasForeignKey(cr => cr.TaskId) .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() .HasMany(cr => cr.CollectionLogs) .WithOne(cl => cl.CollectionResult) .HasForeignKey(cl => cl.ResultId) .OnDelete(DeleteBehavior.Cascade); } private void ConfigureProductionRelationships(ModelBuilder modelBuilder) { modelBuilder.Entity() .HasMany(pr => pr.ProgramSummaries) .WithOne(pps => pps.ProductionRecord) .HasForeignKey(pps => pps.RecordId) .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() .HasMany(d => d.ProductionSummaries) .WithOne(ps => ps.Device) .HasForeignKey(ps => ps.DeviceId) .OnDelete(DeleteBehavior.Cascade); } private void ConfigureTemplateRelationships(ModelBuilder modelBuilder) { modelBuilder.Entity() .HasMany(t => t.TagMappings) .WithOne(tm => tm.Template) .HasForeignKey(tm => tm.TemplateId) .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() .HasMany(t => t.Devices) .WithOne(d => d.Template) .HasForeignKey(d => d.TemplateId) .OnDelete(DeleteBehavior.Restrict); } private void ConfigureIndexes(ModelBuilder modelBuilder) { // Device indexes modelBuilder.Entity() .HasIndex(d => d.DeviceCode) .IsUnique(); modelBuilder.Entity() .HasIndex(d => d.IPAddress); modelBuilder.Entity() .HasIndex(d => d.IsOnline); modelBuilder.Entity() .HasIndex(d => d.IsAvailable); // User indexes modelBuilder.Entity() .HasIndex(u => u.Username) .IsUnique(); modelBuilder.Entity() .HasIndex(u => u.Email) .IsUnique(); modelBuilder.Entity() .HasIndex(u => u.IsActive); // Alarm indexes modelBuilder.Entity() .HasIndex(a => a.AlarmStatus); modelBuilder.Entity() .HasIndex(a => a.IsActive); modelBuilder.Entity() .HasIndex(a => a.CreateTime); // Collection indexes modelBuilder.Entity() .HasIndex(cr => cr.DeviceId); modelBuilder.Entity() .HasIndex(cr => cr.CollectionTime); modelBuilder.Entity() .HasIndex(cr => cr.IsSuccess); // Production indexes modelBuilder.Entity() .HasIndex(pr => pr.DeviceId); modelBuilder.Entity() .HasIndex(pr => pr.ProductionDate); modelBuilder.Entity() .HasIndex(pr => pr.IsCompleted); // Template indexes modelBuilder.Entity() .HasIndex(t => t.BrandName); modelBuilder.Entity() .HasIndex(t => t.IsActive); // System config indexes modelBuilder.Entity() .HasIndex(sc => sc.ConfigKey) .IsUnique(); modelBuilder.Entity() .HasIndex(sc => sc.Category); modelBuilder.Entity() .HasIndex(sc => sc.IsActive); } private void ConfigureConstraints(ModelBuilder modelBuilder) { // Device constraints modelBuilder.Entity() .Property(d => d.DeviceCode) .IsRequired() .HasMaxLength(50); modelBuilder.Entity() .Property(d => d.DeviceName) .IsRequired() .HasMaxLength(100); modelBuilder.Entity() .Property(d => d.IPAddress) .IsRequired() .HasMaxLength(15); modelBuilder.Entity() .Property(d => d.HttpUrl) .IsRequired() .HasMaxLength(255); modelBuilder.Entity() .Property(d => d.CollectionInterval) .IsRequired(); // User constraints modelBuilder.Entity() .Property(u => u.Username) .IsRequired() .HasMaxLength(50); modelBuilder.Entity() .Property(u => u.Email) .IsRequired() .HasMaxLength(100); modelBuilder.Entity() .Property(u => u.PasswordHash) .IsRequired() .HasMaxLength(255); modelBuilder.Entity() .Property(u => u.FirstName) .IsRequired() .HasMaxLength(50); modelBuilder.Entity() .Property(u => u.LastName) .IsRequired() .HasMaxLength(50); // Alarm constraints modelBuilder.Entity() .Property(a => a.AlarmType) .IsRequired() .HasMaxLength(50); modelBuilder.Entity() .Property(a => a.Title) .IsRequired() .HasMaxLength(255); modelBuilder.Entity() .Property(a => a.AlarmStatus) .IsRequired(); // System config constraints modelBuilder.Entity() .Property(sc => sc.ConfigKey) .IsRequired() .HasMaxLength(100); modelBuilder.Entity() .Property(sc => sc.ConfigValue) .IsRequired(); modelBuilder.Entity() .Property(sc => sc.Category) .IsRequired() .HasMaxLength(50); } private void ConfigureDataConversions(ModelBuilder modelBuilder) { // Configure decimal properties with appropriate precision modelBuilder.Entity() .Property(ps => ps.QualityRate) .HasColumnType("decimal(5,2)"); modelBuilder.Entity() .Property(sc => sc.ConfigValue) .HasColumnType("text"); modelBuilder.Entity() .Property(cl => cl.LogData) .HasColumnType("text"); modelBuilder.Entity() .Property(cr => cr.RawData) .HasColumnType("text"); modelBuilder.Entity() .Property(cr => cr.ParsedData) .HasColumnType("text"); modelBuilder.Entity() .Property(t => t.TagsJson) .HasColumnType("json"); modelBuilder.Entity() .Property(t => t.DataProcessingRulesJson) .HasColumnType("json"); modelBuilder.Entity() .Property(sr => sr.ResultData) .HasColumnType("json"); } public static void ConfigureDatabaseServices(IServiceCollection services, IConfiguration configuration) { var connectionString = configuration.GetConnectionString("CNCBusinessDb"); services.AddDbContext(options => { options.UseMySql(connectionString, mysqlOptions => { mysqlOptions.EnableRetryOnFailure( maxRetryCount: 3, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); mysqlOptions.EnableSensitiveDataLogging(true); }); // Enable lazy loading for development if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development") { options.EnableSensitiveDataLogging(); options.EnableDetailedErrors(); } }); } } }