using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Tiku.Domain.Identity; using Tiku.Domain.Tenancy; namespace Tiku.Infrastructure.Persistence.Configurations; internal sealed class TenantConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.ConfigureEntity("tenants"); builder.ConfigureTimestamps(); builder.Property(entity => entity.Slug).HasColumnType("citext").HasMaxLength(100); builder.Property(entity => entity.Name).HasMaxLength(200); builder.Property(entity => entity.LegalName).HasMaxLength(300); builder.Property(entity => entity.Status).HasSnakeCaseEnum(); builder.Property(entity => entity.Mode).HasSnakeCaseEnum(); builder.Property(entity => entity.BillingStatus).HasSnakeCaseEnum(); builder.Property(entity => entity.LegacyId).HasMaxLength(64); builder.Property(entity => entity.Metadata).IsJson("{}"); builder.HasIndex(entity => entity.Slug).IsUnique(); builder.HasIndex(entity => entity.LegacyId).IsUnique(); builder.HasIndex(entity => entity.Mode) .IsUnique() .HasFilter("mode = 'platform_owned'"); builder.HasOne() .WithMany() .HasForeignKey(entity => entity.OwnerUserId) .OnDelete(DeleteBehavior.SetNull); } } internal sealed class TenantMembershipConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.ConfigureTenantEntity("tenant_memberships"); builder.ConfigureTimestamps(); builder.Property(entity => entity.Role).HasSnakeCaseEnum(); builder.Property(entity => entity.Status).HasSnakeCaseEnum(); builder.Property(entity => entity.LegacyRole).HasMaxLength(50); builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.Role }).IsUnique(); builder.HasOne() .WithMany() .HasForeignKey(entity => entity.UserId) .OnDelete(DeleteBehavior.Cascade); } } internal sealed class TenantDomainConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.ConfigureTenantEntity("tenant_domains"); builder.ConfigureTimestamps(); builder.Property(entity => entity.Host).HasColumnType("citext").HasMaxLength(253); builder.Property(entity => entity.DomainType).HasSnakeCaseEnum(); builder.Property(entity => entity.Status).HasSnakeCaseEnum(); builder.Property(entity => entity.VerificationToken).HasMaxLength(256); builder.Property(entity => entity.LastFailureReason).HasMaxLength(2000); builder.HasIndex(entity => entity.Host) .IsUnique() .HasAnnotation("Tiku:GlobalUnique", true); builder.HasIndex(entity => new { entity.TenantId, entity.IsPrimary }) .IsUnique() .HasFilter("is_primary"); } } internal sealed class TenantBrandingConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.ToTable("tenant_branding"); builder.HasKey(entity => entity.TenantId); builder.ConfigureTimestamps(); builder.Property(entity => entity.BrandName).HasMaxLength(200); builder.Property(entity => entity.ShortName).HasMaxLength(100); builder.Property(entity => entity.Slogan).HasMaxLength(300); builder.Property(entity => entity.OrganizationName).HasMaxLength(300); builder.Property(entity => entity.LogoUrl).HasMaxLength(2048); builder.Property(entity => entity.FaviconUrl).HasMaxLength(2048); builder.Property(entity => entity.ServiceWechat).HasMaxLength(100); builder.Property(entity => entity.ServiceAccountName).HasMaxLength(200); builder.Property(entity => entity.Theme).IsJson("{}"); builder.Property(entity => entity.PublicAssets).IsJson("{}"); builder.HasOne().WithOne() .HasForeignKey(entity => entity.TenantId) .OnDelete(DeleteBehavior.Cascade); } } internal sealed class TenantSettingsConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.ToTable("tenant_settings"); builder.HasKey(entity => entity.TenantId); builder.ConfigureTimestamps(); builder.Property(entity => entity.FeatureFlags).IsJson("{}"); builder.Property(entity => entity.AdminFeatureFlags).IsJson("{}"); builder.Property(entity => entity.PublicConfig).IsJson("{}"); builder.HasOne().WithOne() .HasForeignKey(entity => entity.TenantId) .OnDelete(DeleteBehavior.Cascade); } } internal sealed class TenantFrontendConfigConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.ConfigureTenantEntity("tenant_frontend_configs"); builder.ConfigureTimestamps(); builder.HasIndex(entity => entity.TenantId).IsUnique(); builder.Property(entity => entity.ConfigVersion).IsConcurrencyToken(); builder.Property(entity => entity.PublishedBranding).IsJson("{}"); builder.Property(entity => entity.PublishedTheme).IsJson("{}"); builder.Property(entity => entity.PublishedFeatures).IsJson("{}"); builder.Property(entity => entity.PublishedNavigation).IsJson("[]"); builder.Property(entity => entity.PublishedHomeModules).IsJson("[]"); builder.Property(entity => entity.DraftBranding).IsJson("{}"); builder.Property(entity => entity.DraftTheme).IsJson("{}"); builder.Property(entity => entity.DraftFeatures).IsJson("{}"); builder.Property(entity => entity.DraftNavigation).IsJson("[]"); builder.Property(entity => entity.DraftHomeModules).IsJson("[]"); builder.ToTable(table => { table.HasCheckConstraint("ck_tenant_frontend_configs_schema_version", "schema_version > 0"); table.HasCheckConstraint("ck_tenant_frontend_configs_config_version", "config_version > 0"); }); } }