refactor: reorganize Infrastructure layer into folder structure with DI

This commit is contained in:
2026-06-01 18:15:58 +03:30
parent 775ae0d9b4
commit 9fe575ae07
55 changed files with 2960 additions and 3031 deletions

View File

@@ -0,0 +1,394 @@
using Microsoft.EntityFrameworkCore;
namespace GreenHome.Infrastructure.Data;
public sealed class GreenHomeDbContext : DbContext
{
public GreenHomeDbContext(DbContextOptions<GreenHomeDbContext> options) : base(options) { }
public DbSet<Domain.Device> Devices => Set<Domain.Device>();
public DbSet<Domain.TelemetryRecord> TelemetryRecords => Set<Domain.TelemetryRecord>();
public DbSet<Domain.DeviceSettings> DeviceSettings => Set<Domain.DeviceSettings>();
public DbSet<Domain.AlertCondition> AlertConditions => Set<Domain.AlertCondition>();
public DbSet<Domain.AlertRule> AlertRules => Set<Domain.AlertRule>();
public DbSet<Domain.User> Users => Set<Domain.User>();
public DbSet<Domain.VerificationCode> VerificationCodes => Set<Domain.VerificationCode>();
public DbSet<Domain.DeviceUser> DeviceUsers => Set<Domain.DeviceUser>();
public DbSet<Domain.AlertNotification> AlertNotifications => Set<Domain.AlertNotification>();
public DbSet<Domain.AlertLog> AlertLogs => Set<Domain.AlertLog>();
public DbSet<Domain.AIQuery> AIQueries => Set<Domain.AIQuery>();
public DbSet<Domain.DailyReport> DailyReports => Set<Domain.DailyReport>();
public DbSet<Domain.UserDailyReport> UserDailyReports => Set<Domain.UserDailyReport>();
public DbSet<Domain.ReportImage> ReportImages => Set<Domain.ReportImage>();
public DbSet<Domain.Checklist> Checklists => Set<Domain.Checklist>();
public DbSet<Domain.ChecklistItem> ChecklistItems => Set<Domain.ChecklistItem>();
public DbSet<Domain.ChecklistCompletion> ChecklistCompletions => Set<Domain.ChecklistCompletion>();
public DbSet<Domain.ChecklistItemCompletion> ChecklistItemCompletions => Set<Domain.ChecklistItemCompletion>();
public DbSet<Domain.DevicePost> DevicePosts => Set<Domain.DevicePost>();
public DbSet<Domain.DevicePostImage> DevicePostImages => Set<Domain.DevicePostImage>();
public DbSet<Domain.PowerOutageLog> PowerOutageLogs => Set<Domain.PowerOutageLog>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Domain.Device>(b =>
{
b.ToTable("Devices");
b.HasKey(x => x.Id);
b.Property(x => x.DeviceName).IsRequired().HasMaxLength(10);
b.Property(x => x.UserId).IsRequired();
b.Property(x => x.Location).HasMaxLength(250);
b.Property(x => x.NeshanLocation).HasMaxLength(80);
b.HasOne(x => x.User)
.WithMany()
.HasForeignKey(x => x.UserId)
.OnDelete(DeleteBehavior.Restrict);
});
modelBuilder.Entity<Domain.TelemetryRecord>(b =>
{
b.ToTable("Telemetry");
b.HasKey(x => x.Id);
b.Property(x => x.TemperatureC).HasColumnType("decimal(18,2)");
b.Property(x => x.HumidityPercent).HasColumnType("decimal(18,2)");
b.Property(x => x.SoilPercent).HasColumnType("decimal(18,2)");
b.Property(x => x.Lux).HasColumnType("decimal(18,2)");
b.Property(x => x.PersianDate).HasMaxLength(10);
b.Property(x => x.Voltage).HasColumnType("decimal(18,2)");
b.Property(x => x.BatteryVoltage).HasColumnType("decimal(18,2)");
b.Property(x => x.BatteryPercent).HasColumnType("decimal(18,2)");
b.HasIndex(x => new { x.DeviceId, x.PersianYear, x.PersianMonth });
b.HasIndex(x => new { x.DeviceId, x.TimestampUtc });
b.HasIndex(x => new { x.DeviceId, x.ServerTimestampUtc });
});
modelBuilder.Entity<Domain.DeviceSettings>(b =>
{
b.ToTable("DeviceSettings");
b.HasKey(x => x.Id);
b.Property(x => x.Province).HasMaxLength(100);
b.Property(x => x.City).HasMaxLength(100);
b.Property(x => x.Latitude).HasColumnType("decimal(9,6)");
b.Property(x => x.Longitude).HasColumnType("decimal(9,6)");
b.Property(x => x.ProductType).HasMaxLength(100);
b.Property(x => x.MinimumSmsIntervalMinutes).HasDefaultValue(15);
b.Property(x => x.MinimumCallIntervalMinutes).HasDefaultValue(60);
b.Property(x => x.AreaSquareMeters).HasColumnType("decimal(18,2)");
b.Property(x => x.SmsRecievers).HasMaxLength(120);
b.Property(x => x.RelayCount).HasDefaultValue(4);
b.HasOne(x => x.Device)
.WithMany()
.HasForeignKey(x => x.DeviceId)
.OnDelete(DeleteBehavior.Cascade);
b.HasIndex(x => x.DeviceId).IsUnique();
});
modelBuilder.Entity<Domain.AlertCondition>(b =>
{
b.ToTable("AlertConditions");
b.HasKey(x => x.Id);
b.Property(x => x.NotificationType).IsRequired().HasConversion<int>();
b.Property(x => x.TimeType).IsRequired().HasConversion<int>();
b.Property(x => x.CallCooldownMinutes).IsRequired();
b.Property(x => x.SmsCooldownMinutes).IsRequired();
b.Property(x => x.IsEnabled).IsRequired();
b.HasOne(x => x.Device)
.WithMany()
.HasForeignKey(x => x.DeviceId)
.OnDelete(DeleteBehavior.Cascade);
b.HasIndex(x => x.DeviceId);
});
modelBuilder.Entity<Domain.AlertRule>(b =>
{
b.ToTable("AlertRules");
b.HasKey(x => x.Id);
b.Property(x => x.SensorType).IsRequired().HasConversion<int>();
b.Property(x => x.ComparisonType).IsRequired().HasConversion<int>();
b.Property(x => x.Value1).IsRequired().HasColumnType("decimal(18,2)");
b.Property(x => x.Value2).HasColumnType("decimal(18,2)");
b.Property(x => x.Order).IsRequired();
b.HasOne(x => x.AlertCondition)
.WithMany(c => c.Rules)
.HasForeignKey(x => x.AlertConditionId)
.OnDelete(DeleteBehavior.Cascade);
b.HasIndex(x => x.AlertConditionId);
});
modelBuilder.Entity<Domain.User>(b =>
{
b.ToTable("Users");
b.HasKey(x => x.Id);
b.Property(x => x.Mobile).IsRequired().HasMaxLength(11);
b.Property(x => x.Name).IsRequired().HasMaxLength(100);
b.Property(x => x.Family).IsRequired().HasMaxLength(100);
b.Property(x => x.Role).IsRequired().HasConversion<int>();
b.HasIndex(x => x.Mobile).IsUnique();
});
modelBuilder.Entity<Domain.DeviceUser>(b =>
{
b.ToTable("DeviceUsers");
b.HasKey(x => new { x.DeviceId, x.UserId });
b.Property(x => x.ReceiveAlerts).IsRequired().HasDefaultValue(true);
b.HasOne(x => x.Device)
.WithMany(d => d.DeviceUsers)
.HasForeignKey(x => x.DeviceId)
.OnDelete(DeleteBehavior.Cascade);
b.HasOne(x => x.User)
.WithMany(u => u.DeviceUsers)
.HasForeignKey(x => x.UserId)
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity<Domain.VerificationCode>(b =>
{
b.ToTable("VerificationCodes");
b.HasKey(x => x.Id);
b.Property(x => x.Mobile).IsRequired().HasMaxLength(11);
b.Property(x => x.Code).IsRequired().HasMaxLength(4);
b.HasIndex(x => new { x.Mobile, x.Code, x.IsUsed });
});
modelBuilder.Entity<Domain.AlertNotification>(b =>
{
b.ToTable("AlertNotifications");
b.HasKey(x => x.Id);
b.Property(x => x.NotificationType).IsRequired().HasConversion<int>();
b.Property(x => x.Message).IsRequired().HasMaxLength(500);
b.Property(x => x.MessageOutboxIds).HasMaxLength(500);
b.Property(x => x.ErrorMessage).HasMaxLength(1000);
b.HasOne(x => x.Device)
.WithMany()
.HasForeignKey(x => x.DeviceId)
.OnDelete(DeleteBehavior.Restrict);
b.HasOne(x => x.User)
.WithMany()
.HasForeignKey(x => x.UserId)
.OnDelete(DeleteBehavior.Restrict);
b.HasOne(x => x.AlertCondition)
.WithMany()
.HasForeignKey(x => x.AlertConditionId)
.OnDelete(DeleteBehavior.Restrict);
b.HasIndex(x => new { x.DeviceId, x.UserId, x.AlertConditionId, x.SentAt });
});
modelBuilder.Entity<Domain.AIQuery>(b =>
{
b.ToTable("AIQueries");
b.HasKey(x => x.Id);
b.Property(x => x.Question).IsRequired();
b.Property(x => x.Answer).IsRequired();
b.Property(x => x.Model).HasMaxLength(100);
b.HasOne(x => x.Device)
.WithMany()
.HasForeignKey(x => x.DeviceId)
.OnDelete(DeleteBehavior.SetNull);
b.HasOne(x => x.User)
.WithMany()
.HasForeignKey(x => x.UserId)
.OnDelete(DeleteBehavior.SetNull);
b.HasIndex(x => x.DeviceId);
b.HasIndex(x => x.UserId);
b.HasIndex(x => x.CreatedAt);
});
modelBuilder.Entity<Domain.DailyReport>(b =>
{
b.ToTable("DailyReports");
b.HasKey(x => x.Id);
b.Property(x => x.PersianDate).IsRequired().HasMaxLength(10);
b.Property(x => x.Analysis).IsRequired();
b.Property(x => x.Model).HasMaxLength(100);
b.HasOne(x => x.Device)
.WithMany()
.HasForeignKey(x => x.DeviceId)
.OnDelete(DeleteBehavior.Cascade);
b.HasIndex(x => new { x.DeviceId, x.PersianDate }).IsUnique();
b.HasIndex(x => new { x.DeviceId, x.PersianYear, x.PersianMonth });
b.HasIndex(x => x.CreatedAt);
});
modelBuilder.Entity<Domain.AlertLog>(b =>
{
b.ToTable("AlertLogs");
b.HasKey(x => x.Id);
b.Property(x => x.AlertType).IsRequired().HasConversion<int>();
b.Property(x => x.NotificationType).IsRequired().HasConversion<int>();
b.Property(x => x.Message).IsRequired().HasMaxLength(1000);
b.Property(x => x.Status).IsRequired().HasConversion<int>();
b.Property(x => x.ErrorMessage).HasMaxLength(2000);
b.Property(x => x.PhoneNumber).IsRequired().HasMaxLength(20);
b.HasOne(x => x.Device)
.WithMany()
.HasForeignKey(x => x.DeviceId)
.OnDelete(DeleteBehavior.Restrict);
b.HasOne(x => x.User)
.WithMany()
.HasForeignKey(x => x.UserId)
.OnDelete(DeleteBehavior.Restrict);
b.HasOne(x => x.AlertCondition)
.WithMany()
.HasForeignKey(x => x.AlertConditionId)
.OnDelete(DeleteBehavior.SetNull);
b.HasIndex(x => new { x.DeviceId, x.SentAt });
b.HasIndex(x => new { x.UserId, x.SentAt });
b.HasIndex(x => x.AlertType);
b.HasIndex(x => x.Status);
});
modelBuilder.Entity<Domain.UserDailyReport>(b =>
{
b.ToTable("UserDailyReports");
b.HasKey(x => x.Id);
b.Property(x => x.PersianDate).IsRequired().HasMaxLength(10);
b.Property(x => x.Title).IsRequired().HasMaxLength(200);
b.Property(x => x.Observations).IsRequired();
b.Property(x => x.Operations).IsRequired();
b.Property(x => x.Notes).HasMaxLength(2000);
b.HasOne(x => x.Device)
.WithMany()
.HasForeignKey(x => x.DeviceId)
.OnDelete(DeleteBehavior.Cascade);
b.HasOne(x => x.User)
.WithMany()
.HasForeignKey(x => x.UserId)
.OnDelete(DeleteBehavior.Restrict);
b.HasIndex(x => new { x.DeviceId, x.PersianDate });
b.HasIndex(x => new { x.DeviceId, x.PersianYear, x.PersianMonth });
b.HasIndex(x => x.UserId);
});
modelBuilder.Entity<Domain.ReportImage>(b =>
{
b.ToTable("ReportImages");
b.HasKey(x => x.Id);
b.Property(x => x.FileName).IsRequired().HasMaxLength(255);
b.Property(x => x.FilePath).IsRequired().HasMaxLength(500);
b.Property(x => x.ContentType).IsRequired().HasMaxLength(100);
b.Property(x => x.Description).HasMaxLength(500);
b.HasOne(x => x.UserDailyReport)
.WithMany(r => r.Images)
.HasForeignKey(x => x.UserDailyReportId)
.OnDelete(DeleteBehavior.Cascade);
b.HasIndex(x => x.UserDailyReportId);
});
modelBuilder.Entity<Domain.Checklist>(b =>
{
b.ToTable("Checklists");
b.HasKey(x => x.Id);
b.Property(x => x.Title).IsRequired().HasMaxLength(200);
b.Property(x => x.Description).HasMaxLength(1000);
b.Property(x => x.IsActive).IsRequired();
b.HasOne(x => x.Device)
.WithMany()
.HasForeignKey(x => x.DeviceId)
.OnDelete(DeleteBehavior.Cascade);
b.HasOne(x => x.CreatedByUser)
.WithMany()
.HasForeignKey(x => x.CreatedByUserId)
.OnDelete(DeleteBehavior.Restrict);
b.HasIndex(x => new { x.DeviceId, x.IsActive });
});
modelBuilder.Entity<Domain.ChecklistItem>(b =>
{
b.ToTable("ChecklistItems");
b.HasKey(x => x.Id);
b.Property(x => x.Title).IsRequired().HasMaxLength(300);
b.Property(x => x.Description).HasMaxLength(1000);
b.Property(x => x.Order).IsRequired();
b.Property(x => x.IsRequired).IsRequired();
b.HasOne(x => x.Checklist)
.WithMany(c => c.Items)
.HasForeignKey(x => x.ChecklistId)
.OnDelete(DeleteBehavior.Cascade);
b.HasIndex(x => new { x.ChecklistId, x.Order });
});
modelBuilder.Entity<Domain.ChecklistCompletion>(b =>
{
b.ToTable("ChecklistCompletions");
b.HasKey(x => x.Id);
b.Property(x => x.PersianDate).IsRequired().HasMaxLength(10);
b.Property(x => x.Notes).HasMaxLength(2000);
b.HasOne(x => x.Checklist)
.WithMany(c => c.Completions)
.HasForeignKey(x => x.ChecklistId)
.OnDelete(DeleteBehavior.Cascade);
b.HasOne(x => x.CompletedByUser)
.WithMany()
.HasForeignKey(x => x.CompletedByUserId)
.OnDelete(DeleteBehavior.Restrict);
b.HasIndex(x => new { x.ChecklistId, x.PersianDate });
b.HasIndex(x => x.CompletedByUserId);
});
modelBuilder.Entity<Domain.ChecklistItemCompletion>(b =>
{
b.ToTable("ChecklistItemCompletions");
b.HasKey(x => x.Id);
b.Property(x => x.IsChecked).IsRequired();
b.Property(x => x.Note).HasMaxLength(500);
b.HasOne(x => x.ChecklistCompletion)
.WithMany(cc => cc.ItemCompletions)
.HasForeignKey(x => x.ChecklistCompletionId)
.OnDelete(DeleteBehavior.Cascade);
b.HasOne(x => x.ChecklistItem)
.WithMany()
.HasForeignKey(x => x.ChecklistItemId)
.OnDelete(DeleteBehavior.Restrict);
b.HasIndex(x => x.ChecklistCompletionId);
});
modelBuilder.Entity<Domain.DevicePost>(b =>
{
b.ToTable("DevicePosts");
b.HasKey(x => x.Id);
b.Property(x => x.Content).IsRequired().HasMaxLength(5000);
b.HasOne(x => x.Device)
.WithMany()
.HasForeignKey(x => x.DeviceId)
.OnDelete(DeleteBehavior.Cascade);
b.HasOne(x => x.AuthorUser)
.WithMany()
.HasForeignKey(x => x.AuthorUserId)
.OnDelete(DeleteBehavior.Restrict);
b.HasIndex(x => new { x.DeviceId, x.CreatedAt });
b.HasIndex(x => x.AuthorUserId);
});
modelBuilder.Entity<Domain.DevicePostImage>(b =>
{
b.ToTable("DevicePostImages");
b.HasKey(x => x.Id);
b.Property(x => x.FileName).IsRequired().HasMaxLength(255);
b.Property(x => x.FilePath).IsRequired().HasMaxLength(500);
b.Property(x => x.ContentType).IsRequired().HasMaxLength(100);
b.HasOne(x => x.DevicePost)
.WithMany(p => p.Images)
.HasForeignKey(x => x.DevicePostId)
.OnDelete(DeleteBehavior.Cascade);
b.HasIndex(x => x.DevicePostId);
});
modelBuilder.Entity<Domain.PowerOutageLog>(b =>
{
b.ToTable("PowerOutageLogs");
b.HasKey(x => x.Id);
b.Property(x => x.PreviousPower).IsRequired();
b.Property(x => x.CurrentPower).IsRequired();
b.Property(x => x.EventType).IsRequired().HasConversion<int>();
b.Property(x => x.PersianDate).HasMaxLength(10);
b.HasOne(x => x.Device)
.WithMany()
.HasForeignKey(x => x.DeviceId)
.OnDelete(DeleteBehavior.Cascade);
b.HasIndex(x => new { x.DeviceId, x.TimestampUtc });
b.HasIndex(x => new { x.DeviceId, x.PersianYear, x.PersianMonth });
b.HasIndex(x => x.EventType);
});
}
}