using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Tiku.Domain.Content; namespace Tiku.Infrastructure.Persistence.Configurations; internal sealed class QuestionAssetConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.ConfigureTenantEntity("question_assets"); builder.ConfigureTimestamps(); builder.Property(entity => entity.DeliveryFingerprint).HasMaxLength(64); builder.Property(entity => entity.Status).HasSnakeCaseEnum(); builder.HasIndex(entity => new { entity.TenantId, entity.DeliveryFingerprint }).IsUnique(); builder.HasIndex(entity => new { entity.TenantId, entity.FamilyId }); builder.HasOne().WithMany() .HasForeignKey(entity => new { entity.TenantId, Id = entity.FamilyId }) .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) .OnDelete(DeleteBehavior.Restrict); builder.HasOne().WithMany() .HasForeignKey(entity => new { entity.TenantId, QuestionAssetId = entity.Id, Id = entity.CurrentRevisionId }) .HasPrincipalKey(entity => new { entity.TenantId, entity.QuestionAssetId, entity.Id }) .OnDelete(DeleteBehavior.Restrict); } } internal sealed class QuestionRevisionConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.ConfigureTenantOwned("question_revisions"); builder.HasAlternateKey(entity => new { entity.TenantId, entity.QuestionAssetId, entity.Id }); builder.Property(entity => entity.QuestionType).HasMaxLength(50); builder.Property(entity => entity.TypeLabel).HasMaxLength(100); builder.Property(entity => entity.NormalizedContent).HasColumnType("text"); builder.Property(entity => entity.Options).IsJson("[]"); builder.Property(entity => entity.CorrectOptionIndices).IsJson("[]"); builder.Property(entity => entity.SubQuestions).IsJson("[]"); builder.Property(entity => entity.CodeLang).HasMaxLength(50); builder.Property(entity => entity.DeliveryFingerprint).HasMaxLength(64); builder.Property(entity => entity.CreatedAt).HasDefaultValueSql("now()"); builder.HasIndex(entity => new { entity.TenantId, entity.QuestionAssetId, entity.RevisionNo }).IsUnique(); builder.HasIndex(entity => new { entity.TenantId, entity.DeliveryFingerprint }); builder.HasIndex(entity => entity.NormalizedContent).HasMethod("gin") .HasOperators("gin_trgm_ops") .HasDatabaseName("ix_question_revisions_content_trgm"); builder.HasOne().WithMany() .HasForeignKey(entity => new { entity.TenantId, Id = entity.QuestionAssetId }) .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) .OnDelete(DeleteBehavior.Cascade); } } internal sealed class QuestionFamilyConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.ConfigureTenantEntity("question_families"); builder.ConfigureTimestamps(); builder.Property(entity => entity.Name).HasMaxLength(300); builder.Property(entity => entity.Description).HasMaxLength(2000); } } internal sealed class QuestionPlacementConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.ConfigureTenantEntity("question_placements"); builder.ConfigureTimestamps(); builder.Property(entity => entity.Status).HasSnakeCaseEnum(); builder.HasIndex(entity => new { entity.TenantId, entity.QuestionAssetOwnerTenantId, entity.QuestionAssetId, entity.CurriculumVersionId, entity.CurriculumNodeId, entity.AssessmentPolicyVersionId }).IsUnique(); builder.HasOne().WithMany() .HasForeignKey(entity => new { TenantId = entity.QuestionAssetOwnerTenantId, Id = entity.QuestionAssetId }) .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) .OnDelete(DeleteBehavior.Restrict); builder.HasOne().WithMany() .HasForeignKey(entity => new { entity.TenantId, Id = entity.CurriculumVersionId }) .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) .OnDelete(DeleteBehavior.Restrict); builder.HasOne().WithMany() .HasForeignKey(entity => new { entity.TenantId, Id = entity.CurriculumNodeId }) .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) .OnDelete(DeleteBehavior.Restrict); builder.HasOne().WithMany() .HasForeignKey(entity => new { entity.TenantId, Id = entity.AssessmentPolicyVersionId }) .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) .OnDelete(DeleteBehavior.Restrict); builder.ToTable(table => table.HasCheckConstraint( "ck_question_placements_valid_period", "valid_to is null or valid_from is null or valid_to > valid_from")); } } internal sealed class QuestionPlacementConceptConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.ConfigureTenantOwned("question_placement_concepts"); builder.HasIndex(entity => new { entity.TenantId, entity.PlacementId, entity.KnowledgeConceptId }).IsUnique(); builder.HasOne().WithMany() .HasForeignKey(entity => new { entity.TenantId, Id = entity.PlacementId }) .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) .OnDelete(DeleteBehavior.Cascade); builder.HasOne().WithMany() .HasForeignKey(entity => new { entity.TenantId, Id = entity.KnowledgeConceptId }) .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) .OnDelete(DeleteBehavior.Restrict); } } internal sealed class QuestionPlacementRuleGroupConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.ConfigureTenantOwned("question_placement_rule_groups"); builder.HasIndex(entity => new { entity.TenantId, entity.PlacementId, entity.GroupOrder }).IsUnique(); builder.HasOne().WithMany() .HasForeignKey(entity => new { entity.TenantId, Id = entity.PlacementId }) .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) .OnDelete(DeleteBehavior.Cascade); } } internal sealed class QuestionPlacementRuleConditionConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.ConfigureTenantOwned("question_placement_rule_conditions"); builder.Property(entity => entity.Operator).HasSnakeCaseEnum(); builder.HasIndex(entity => new { entity.TenantId, entity.RuleGroupId, entity.TargetDimensionDefinitionId, entity.TargetNodeId, entity.Operator }).IsUnique(); builder.HasOne().WithMany() .HasForeignKey(entity => new { entity.TenantId, Id = entity.RuleGroupId }) .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) .OnDelete(DeleteBehavior.Cascade); builder.HasOne().WithMany() .HasForeignKey(entity => entity.TargetDimensionDefinitionId) .OnDelete(DeleteBehavior.Restrict); builder.HasOne().WithMany() .HasForeignKey(entity => entity.TargetNodeId) .OnDelete(DeleteBehavior.Restrict); } }