337 lines
15 KiB
C#
337 lines
15 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
|
using Tiku.Domain.Catalog;
|
|
using Tiku.Domain.Content;
|
|
using Tiku.Domain.Identity;
|
|
|
|
namespace Tiku.Infrastructure.Persistence.Configurations;
|
|
|
|
internal sealed class VocabularyUnitConfiguration : IEntityTypeConfiguration<VocabularyUnit>
|
|
{
|
|
public void Configure(EntityTypeBuilder<VocabularyUnit> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("vocabulary_units");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.LegacyId).HasMaxLength(64);
|
|
builder.Property(entity => entity.Name).HasMaxLength(300);
|
|
builder.Property(entity => entity.Description).HasMaxLength(2000);
|
|
builder.Property(entity => entity.SourceHash).HasMaxLength(128);
|
|
builder.Property(entity => entity.Metadata).IsJson("{}");
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
|
|
builder.HasIndex(entity => new
|
|
{
|
|
entity.TenantId,
|
|
entity.EntryId,
|
|
entity.ContentNodeId,
|
|
entity.RegionId,
|
|
entity.IsActive,
|
|
entity.SortOrder
|
|
});
|
|
|
|
builder.HasOne<Region>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.RegionId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
builder.HasOne<ContentEntry>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.EntryId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
builder.HasOne<ContentNode>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.ContentNodeId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
}
|
|
}
|
|
|
|
internal sealed class VocabularyWordConfiguration : IEntityTypeConfiguration<VocabularyWord>
|
|
{
|
|
public void Configure(EntityTypeBuilder<VocabularyWord> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("vocabulary_words");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.LegacyId).HasMaxLength(64);
|
|
builder.Property(entity => entity.Word).HasMaxLength(300);
|
|
builder.Property(entity => entity.Phonetic).HasMaxLength(200);
|
|
builder.Property(entity => entity.Meaning).HasMaxLength(2000);
|
|
builder.Property(entity => entity.Example).HasMaxLength(4000);
|
|
builder.Property(entity => entity.ExampleTranslation).HasMaxLength(4000);
|
|
builder.Property(entity => entity.SourceHash).HasMaxLength(128);
|
|
builder.Property(entity => entity.Tags).IsJson("[]");
|
|
builder.Property(entity => entity.Metadata).IsJson("{}");
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
|
|
builder.HasIndex(entity => new
|
|
{
|
|
entity.TenantId,
|
|
entity.EntryId,
|
|
entity.ContentNodeId,
|
|
entity.UnitId,
|
|
entity.IsActive,
|
|
entity.SortOrder
|
|
});
|
|
builder.HasIndex(entity => new
|
|
{
|
|
entity.TenantId,
|
|
entity.UnitId,
|
|
entity.IsActive,
|
|
entity.SortOrder,
|
|
entity.CreatedAt
|
|
});
|
|
|
|
builder.HasOne<VocabularyUnit>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.UnitId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
builder.HasOne<ContentEntry>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.EntryId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
builder.HasOne<ContentNode>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.ContentNodeId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
}
|
|
}
|
|
|
|
internal sealed class UserWordProgressConfiguration : IEntityTypeConfiguration<UserWordProgress>
|
|
{
|
|
public void Configure(EntityTypeBuilder<UserWordProgress> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("user_word_progress");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.LegacyId).HasMaxLength(64);
|
|
builder.Property(entity => entity.LegacyUserId).HasMaxLength(64);
|
|
builder.Property(entity => entity.LegacyWordId).HasMaxLength(64);
|
|
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.EaseFactor).HasPrecision(4, 2).HasDefaultValue(2.50m);
|
|
builder.Property(entity => entity.LastResult).HasNullableSnakeCaseEnum();
|
|
builder.Property(entity => entity.DueLevel).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.Metadata).IsJson("{}");
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.WordId }).IsUnique();
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.Status });
|
|
builder.HasIndex(entity => new
|
|
{
|
|
entity.TenantId,
|
|
entity.UserId,
|
|
entity.NextReviewAt,
|
|
entity.Status,
|
|
entity.DueLevel
|
|
});
|
|
|
|
builder.ToTable(table =>
|
|
{
|
|
table.HasCheckConstraint("ck_user_word_progress_review_count", "review_count >= 0");
|
|
table.HasCheckConstraint("ck_user_word_progress_correct_streak", "correct_streak >= 0");
|
|
table.HasCheckConstraint("ck_user_word_progress_ease_factor", "ease_factor >= 1.30 and ease_factor <= 3.00");
|
|
});
|
|
|
|
builder.HasOne<User>().WithMany()
|
|
.HasForeignKey(entity => entity.UserId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
builder.HasOne<VocabularyWord>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.WordId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
}
|
|
}
|
|
|
|
internal sealed class UserWordFavoriteConfiguration : IEntityTypeConfiguration<UserWordFavorite>
|
|
{
|
|
public void Configure(EntityTypeBuilder<UserWordFavorite> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("user_word_favorites");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.LegacyId).HasMaxLength(64);
|
|
builder.Property(entity => entity.LegacyUserId).HasMaxLength(64);
|
|
builder.Property(entity => entity.LegacyWordId).HasMaxLength(64);
|
|
builder.Property(entity => entity.Note).HasMaxLength(2000);
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.WordId }).IsUnique();
|
|
|
|
builder.HasOne<User>().WithMany()
|
|
.HasForeignKey(entity => entity.UserId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
builder.HasOne<VocabularyWord>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.WordId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
}
|
|
}
|
|
|
|
internal sealed class HandbookSubjectConfiguration : IEntityTypeConfiguration<HandbookSubject>
|
|
{
|
|
public void Configure(EntityTypeBuilder<HandbookSubject> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("handbook_subjects");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.LegacyId).HasMaxLength(64);
|
|
builder.Property(entity => entity.Name).HasMaxLength(300);
|
|
builder.Property(entity => entity.Type).HasNullableSnakeCaseEnum();
|
|
builder.Property(entity => entity.Icon).HasMaxLength(2048);
|
|
builder.Property(entity => entity.Color).HasMaxLength(50);
|
|
builder.Property(entity => entity.Description).HasMaxLength(2000);
|
|
builder.Property(entity => entity.MajorLegacyIds).IsJson("[]");
|
|
builder.Property(entity => entity.SourceHash).HasMaxLength(128);
|
|
builder.Property(entity => entity.Metadata).IsJson("{}");
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
|
|
builder.HasIndex(entity => new
|
|
{
|
|
entity.TenantId,
|
|
entity.EntryId,
|
|
entity.ContentNodeId,
|
|
entity.RegionId,
|
|
entity.IsActive,
|
|
entity.SortOrder
|
|
});
|
|
|
|
builder.HasOne<Region>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.RegionId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
builder.HasOne<School>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.SchoolId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
builder.HasOne<Major>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.MajorId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
builder.HasOne<ContentEntry>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.EntryId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
builder.HasOne<ContentNode>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.ContentNodeId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
}
|
|
}
|
|
|
|
internal sealed class HandbookChapterConfiguration : IEntityTypeConfiguration<HandbookChapter>
|
|
{
|
|
public void Configure(EntityTypeBuilder<HandbookChapter> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("handbook_chapters");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.LegacyId).HasMaxLength(64);
|
|
builder.Property(entity => entity.Name).HasMaxLength(300);
|
|
builder.Property(entity => entity.Description).HasMaxLength(2000);
|
|
builder.Property(entity => entity.SourceHash).HasMaxLength(128);
|
|
builder.Property(entity => entity.Metadata).IsJson("{}");
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
|
|
builder.HasIndex(entity => new
|
|
{
|
|
entity.TenantId,
|
|
entity.EntryId,
|
|
entity.ContentNodeId,
|
|
entity.SubjectId,
|
|
entity.IsActive,
|
|
entity.SortOrder
|
|
});
|
|
|
|
builder.HasOne<HandbookSubject>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.SubjectId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
builder.HasOne<ContentEntry>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.EntryId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
builder.HasOne<ContentNode>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.ContentNodeId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
}
|
|
}
|
|
|
|
internal sealed class HandbookEntryConfiguration : IEntityTypeConfiguration<HandbookEntry>
|
|
{
|
|
public void Configure(EntityTypeBuilder<HandbookEntry> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("handbook_entries");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.LegacyId).HasMaxLength(64);
|
|
builder.Property(entity => entity.Title).HasMaxLength(300);
|
|
builder.Property(entity => entity.Summary).HasMaxLength(2000);
|
|
builder.Property(entity => entity.Tags).IsJson("[]");
|
|
builder.Property(entity => entity.SourceHash).HasMaxLength(128);
|
|
builder.Property(entity => entity.Metadata).IsJson("{}");
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
|
|
builder.HasIndex(entity => new
|
|
{
|
|
entity.TenantId,
|
|
entity.EntryId,
|
|
entity.ContentNodeId,
|
|
entity.ChapterId,
|
|
entity.IsActive,
|
|
entity.SortOrder
|
|
});
|
|
|
|
builder.HasOne<HandbookChapter>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.ChapterId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
builder.HasOne<ContentEntry>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.EntryId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
builder.HasOne<ContentNode>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.ContentNodeId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
}
|
|
}
|
|
|
|
internal sealed class QuestionTypeGroupConfiguration : IEntityTypeConfiguration<QuestionTypeGroup>
|
|
{
|
|
public void Configure(EntityTypeBuilder<QuestionTypeGroup> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("question_type_groups");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.LegacyId).HasMaxLength(64);
|
|
builder.Property(entity => entity.DisplayName).HasMaxLength(300);
|
|
builder.Property(entity => entity.Types).IsJson("[]");
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
|
|
builder.HasIndex(entity => new
|
|
{
|
|
entity.TenantId,
|
|
entity.SubjectId,
|
|
entity.IsActive,
|
|
entity.SortOrder
|
|
});
|
|
|
|
builder.HasOne<Subject>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.SubjectId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
}
|
|
}
|
|
|
|
internal sealed class SubjectShareConfiguration : IEntityTypeConfiguration<SubjectShare>
|
|
{
|
|
public void Configure(EntityTypeBuilder<SubjectShare> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("subject_shares");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.LegacyId).HasMaxLength(64);
|
|
builder.Property(entity => entity.LegacySourceSubjectId).HasMaxLength(64);
|
|
builder.Property(entity => entity.LegacyTargetSubjectId).HasMaxLength(64);
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
|
|
builder.HasIndex(entity => new
|
|
{
|
|
entity.TenantId,
|
|
entity.SourceSubjectId,
|
|
entity.TargetSubjectId
|
|
}).IsUnique();
|
|
|
|
builder.HasOne<Subject>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.SourceSubjectId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
builder.HasOne<Subject>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.TargetSubjectId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
}
|
|
}
|