Files
tiku-backend.net/Tiku.Infrastructure/Persistence/Configurations/ContentV2QuestionConfigurations.cs
xiong 3f5957d744
Some checks failed
ci / release-gate (push) Has been cancelled
feat(content): establish v2 learning delivery foundation
2026-08-06 09:52:18 +08:00

160 lines
8.1 KiB
C#

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Tiku.Domain.Content;
namespace Tiku.Infrastructure.Persistence.Configurations;
internal sealed class QuestionAssetConfiguration : IEntityTypeConfiguration<QuestionAsset>
{
public void Configure(EntityTypeBuilder<QuestionAsset> 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<QuestionFamily>().WithMany()
.HasForeignKey(entity => new { entity.TenantId, Id = entity.FamilyId })
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
.OnDelete(DeleteBehavior.Restrict);
builder.HasOne<QuestionRevision>().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<QuestionRevision>
{
public void Configure(EntityTypeBuilder<QuestionRevision> 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<QuestionAsset>().WithMany()
.HasForeignKey(entity => new { entity.TenantId, Id = entity.QuestionAssetId })
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
.OnDelete(DeleteBehavior.Cascade);
}
}
internal sealed class QuestionFamilyConfiguration : IEntityTypeConfiguration<QuestionFamily>
{
public void Configure(EntityTypeBuilder<QuestionFamily> 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<QuestionPlacement>
{
public void Configure(EntityTypeBuilder<QuestionPlacement> 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<QuestionAsset>().WithMany()
.HasForeignKey(entity => new { TenantId = entity.QuestionAssetOwnerTenantId, Id = entity.QuestionAssetId })
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
.OnDelete(DeleteBehavior.Restrict);
builder.HasOne<CurriculumVersion>().WithMany()
.HasForeignKey(entity => new { entity.TenantId, Id = entity.CurriculumVersionId })
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
.OnDelete(DeleteBehavior.Restrict);
builder.HasOne<CurriculumNode>().WithMany()
.HasForeignKey(entity => new { entity.TenantId, Id = entity.CurriculumNodeId })
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
.OnDelete(DeleteBehavior.Restrict);
builder.HasOne<AssessmentPolicyVersion>().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<QuestionPlacementConcept>
{
public void Configure(EntityTypeBuilder<QuestionPlacementConcept> builder)
{
builder.ConfigureTenantOwned("question_placement_concepts");
builder.HasIndex(entity => new { entity.TenantId, entity.PlacementId, entity.KnowledgeConceptId }).IsUnique();
builder.HasOne<QuestionPlacement>().WithMany()
.HasForeignKey(entity => new { entity.TenantId, Id = entity.PlacementId })
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
.OnDelete(DeleteBehavior.Cascade);
builder.HasOne<KnowledgeConcept>().WithMany()
.HasForeignKey(entity => new { entity.TenantId, Id = entity.KnowledgeConceptId })
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
.OnDelete(DeleteBehavior.Restrict);
}
}
internal sealed class QuestionPlacementRuleGroupConfiguration : IEntityTypeConfiguration<QuestionPlacementRuleGroup>
{
public void Configure(EntityTypeBuilder<QuestionPlacementRuleGroup> builder)
{
builder.ConfigureTenantOwned("question_placement_rule_groups");
builder.HasIndex(entity => new { entity.TenantId, entity.PlacementId, entity.GroupOrder }).IsUnique();
builder.HasOne<QuestionPlacement>().WithMany()
.HasForeignKey(entity => new { entity.TenantId, Id = entity.PlacementId })
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
.OnDelete(DeleteBehavior.Cascade);
}
}
internal sealed class QuestionPlacementRuleConditionConfiguration :
IEntityTypeConfiguration<QuestionPlacementRuleCondition>
{
public void Configure(EntityTypeBuilder<QuestionPlacementRuleCondition> 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<QuestionPlacementRuleGroup>().WithMany()
.HasForeignKey(entity => new { entity.TenantId, Id = entity.RuleGroupId })
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
.OnDelete(DeleteBehavior.Cascade);
builder.HasOne<TargetDimensionDefinition>().WithMany()
.HasForeignKey(entity => entity.TargetDimensionDefinitionId)
.OnDelete(DeleteBehavior.Restrict);
builder.HasOne<TargetNode>().WithMany()
.HasForeignKey(entity => entity.TargetNodeId)
.OnDelete(DeleteBehavior.Restrict);
}
}