forked from xiongyuxing/tiku-backend.net
300 lines
15 KiB
C#
300 lines
15 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
|
using Tiku.Domain.Catalog;
|
|
using Tiku.Domain.Content;
|
|
using Tiku.Domain.Identity;
|
|
using Tiku.Domain.QuestionBanks;
|
|
|
|
namespace Tiku.Infrastructure.Persistence.Configurations;
|
|
|
|
internal sealed class ContentAssetConfiguration : IEntityTypeConfiguration<ContentAsset>
|
|
{
|
|
public void Configure(EntityTypeBuilder<ContentAsset> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("content_assets");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.LegacyId).HasMaxLength(64);
|
|
builder.Property(entity => entity.AssetKey).HasMaxLength(200);
|
|
builder.Property(entity => entity.Title).HasMaxLength(300);
|
|
builder.Property(entity => entity.Category).HasMaxLength(100);
|
|
builder.Property(entity => entity.FileName).HasMaxLength(500);
|
|
builder.Property(entity => entity.CdnUrl).HasMaxLength(2048);
|
|
builder.Property(entity => entity.AssetType).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.StorageProvider).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.Bucket).HasMaxLength(200);
|
|
builder.Property(entity => entity.ObjectKey).HasMaxLength(1000);
|
|
builder.Property(entity => entity.MimeType).HasMaxLength(200);
|
|
builder.Property(entity => entity.ChecksumSha256).HasMaxLength(64);
|
|
builder.Property(entity => entity.Visibility).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.PreviewUrl).HasMaxLength(2048);
|
|
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.AccessRules).IsJson("{}");
|
|
builder.Property(entity => entity.Source).HasMaxLength(50);
|
|
builder.Property(entity => entity.UploadStatus).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.VerifiedChecksumSha256).HasMaxLength(64);
|
|
builder.Property(entity => entity.VerificationDetails).IsJson("{}");
|
|
builder.Property(entity => entity.PreviewObjectKey).HasMaxLength(1000);
|
|
builder.Property(entity => entity.PreviewStatus).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.SecurityFlags).IsJson("{}");
|
|
builder.Property(entity => entity.Metadata).IsJson("{}");
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
|
|
builder.HasIndex(entity => new
|
|
{
|
|
entity.TenantId,
|
|
entity.Status,
|
|
entity.Visibility,
|
|
entity.AssetType,
|
|
entity.RegionId,
|
|
entity.SubjectId,
|
|
entity.CategoryId,
|
|
entity.SortOrder
|
|
});
|
|
builder.HasIndex(entity => new
|
|
{
|
|
entity.TenantId,
|
|
entity.StorageProvider,
|
|
entity.Bucket,
|
|
entity.ObjectKey
|
|
});
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.ChecksumSha256 })
|
|
.HasFilter("checksum_sha256 is not null");
|
|
builder.HasIndex(entity => new
|
|
{
|
|
entity.TenantId,
|
|
entity.UploadStatus,
|
|
entity.Status,
|
|
entity.UpdatedAt
|
|
});
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.PreviewStatus })
|
|
.HasFilter("preview_status <> 'none'");
|
|
builder.ToTable(table =>
|
|
{
|
|
table.HasCheckConstraint("ck_content_assets_file_size", "file_size_bytes is null or file_size_bytes >= 0");
|
|
table.HasCheckConstraint("ck_content_assets_download_count", "download_count >= 0");
|
|
table.HasCheckConstraint("ck_content_assets_verified_size", "verified_size_bytes is null or verified_size_bytes >= 0");
|
|
table.HasCheckConstraint(
|
|
"ck_content_assets_verified_checksum",
|
|
"verified_checksum_sha256 is null or verified_checksum_sha256 ~ '^[a-f0-9]{64}$'");
|
|
});
|
|
|
|
builder.HasOne<Region>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.RegionId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
builder.HasOne<Subject>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.SubjectId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
builder.HasOne<Category>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.CategoryId })
|
|
.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);
|
|
builder.HasOne<User>().WithMany()
|
|
.HasForeignKey(entity => entity.CreatedBy)
|
|
.OnDelete(DeleteBehavior.SetNull);
|
|
builder.HasOne<User>().WithMany()
|
|
.HasForeignKey(entity => entity.UpdatedBy)
|
|
.OnDelete(DeleteBehavior.SetNull);
|
|
builder.HasOne<User>().WithMany()
|
|
.HasForeignKey(entity => entity.VerifiedBy)
|
|
.OnDelete(DeleteBehavior.SetNull);
|
|
}
|
|
}
|
|
|
|
internal sealed class ContentImportJobConfiguration : IEntityTypeConfiguration<ContentImportJob>
|
|
{
|
|
public void Configure(EntityTypeBuilder<ContentImportJob> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("content_import_jobs");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.ImportType).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.SourceFormat).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.SourceName).HasMaxLength(500);
|
|
builder.Property(entity => entity.SourceHash).HasMaxLength(128);
|
|
builder.Property(entity => entity.Summary).IsJson("{}");
|
|
builder.Property(entity => entity.RawPayload).IsJson("[]");
|
|
builder.Property(entity => entity.NormalizedPayload).IsJson("[]");
|
|
builder.HasIndex(entity => new
|
|
{
|
|
entity.TenantId,
|
|
entity.ImportType,
|
|
entity.Status,
|
|
entity.CreatedAt
|
|
});
|
|
builder.ToTable(table =>
|
|
{
|
|
table.HasCheckConstraint("ck_content_import_jobs_counts", "total_count >= 0 and valid_count >= 0 and error_count >= 0 and warning_count >= 0 and inserted_count >= 0 and updated_count >= 0 and skipped_count >= 0");
|
|
});
|
|
|
|
builder.HasOne<User>().WithMany()
|
|
.HasForeignKey(entity => entity.CreatedBy)
|
|
.OnDelete(DeleteBehavior.SetNull);
|
|
builder.HasOne<Region>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.TargetRegionId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
builder.HasOne<Subject>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.TargetSubjectId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
builder.HasOne<Category>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.TargetCategoryId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
builder.HasOne<ContentNode>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.TargetContentNodeId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
builder.HasOne<QuestionBank>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.TargetQuestionBankId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
}
|
|
}
|
|
|
|
internal sealed class ContentImportItemConfiguration : IEntityTypeConfiguration<ContentImportItem>
|
|
{
|
|
public void Configure(EntityTypeBuilder<ContentImportItem> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("content_import_items");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.ExternalId).HasMaxLength(200);
|
|
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.TargetType).HasMaxLength(100);
|
|
builder.Property(entity => entity.SourcePayload).IsJson("{}");
|
|
builder.Property(entity => entity.NormalizedPayload).IsJson("{}");
|
|
builder.Property(entity => entity.ContentHash).HasMaxLength(128);
|
|
builder.HasIndex(entity => new { entity.JobId, entity.RowNo }).IsUnique();
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.JobId, entity.Status, entity.RowNo });
|
|
builder.ToTable(table =>
|
|
{
|
|
table.HasCheckConstraint("ck_content_import_items_row_no", "row_no >= 0");
|
|
table.HasCheckConstraint("ck_content_import_items_issues_count", "issues_count >= 0");
|
|
});
|
|
|
|
builder.HasOne<ContentImportJob>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.JobId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
}
|
|
}
|
|
|
|
internal sealed class ContentImportIssueConfiguration : IEntityTypeConfiguration<ContentImportIssue>
|
|
{
|
|
public void Configure(EntityTypeBuilder<ContentImportIssue> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("content_import_issues");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.Severity).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.Code).HasMaxLength(100);
|
|
builder.Property(entity => entity.FieldPath).HasMaxLength(500);
|
|
builder.Property(entity => entity.Message).HasMaxLength(2000);
|
|
builder.Property(entity => entity.Details).IsJson("{}");
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.JobId, entity.Severity, entity.RowNo });
|
|
|
|
builder.HasOne<ContentImportJob>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.JobId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
builder.HasOne<ContentImportItem>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.ItemId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
}
|
|
}
|
|
|
|
internal sealed class ImageAssetConfiguration : IEntityTypeConfiguration<ImageAsset>
|
|
{
|
|
public void Configure(EntityTypeBuilder<ImageAsset> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("images");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.LegacyId).HasMaxLength(64);
|
|
builder.Property(entity => entity.Title).HasMaxLength(300);
|
|
builder.Property(entity => entity.Category).HasMaxLength(100);
|
|
builder.Property(entity => entity.FileName).HasMaxLength(500);
|
|
builder.Property(entity => entity.CdnUrl).HasMaxLength(2048);
|
|
builder.Property(entity => entity.Metadata).IsJson("{}");
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.Category, entity.IsPublic });
|
|
}
|
|
}
|
|
|
|
internal sealed class AppAssetConfiguration : IEntityTypeConfiguration<AppAsset>
|
|
{
|
|
public void Configure(EntityTypeBuilder<AppAsset> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("app_assets");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.LegacyId).HasMaxLength(64);
|
|
builder.Property(entity => entity.AssetKey).HasMaxLength(200);
|
|
builder.Property(entity => entity.FileName).HasMaxLength(500);
|
|
builder.Property(entity => entity.Description).HasMaxLength(1000);
|
|
builder.Property(entity => entity.Metadata).IsJson("{}");
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.AssetKey }).IsUnique();
|
|
}
|
|
}
|
|
|
|
internal sealed class VideoExplanationConfiguration : IEntityTypeConfiguration<VideoExplanation>
|
|
{
|
|
public void Configure(EntityTypeBuilder<VideoExplanation> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("video_explanations");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.LegacyId).HasMaxLength(64);
|
|
builder.Property(entity => entity.LegacySubjectId).HasMaxLength(64);
|
|
builder.Property(entity => entity.Title).HasMaxLength(300);
|
|
builder.Property(entity => entity.VideoUrl).HasMaxLength(2048);
|
|
builder.Property(entity => entity.ThumbnailUrl).HasMaxLength(2048);
|
|
builder.Property(entity => entity.KnowledgeTags).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.SourceHash })
|
|
.HasFilter("source_hash is not null");
|
|
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.Restrict);
|
|
}
|
|
}
|
|
|
|
internal sealed class QuestionVideoConfiguration : IEntityTypeConfiguration<QuestionVideo>
|
|
{
|
|
public void Configure(EntityTypeBuilder<QuestionVideo> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("question_videos");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.LegacyId).HasMaxLength(64);
|
|
builder.Property(entity => entity.LegacyQuestionId).HasMaxLength(64);
|
|
builder.Property(entity => entity.LegacyVideoId).HasMaxLength(64);
|
|
builder.Property(entity => entity.VideoType).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.Metadata).IsJson("{}");
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
|
|
builder.HasIndex(entity => new
|
|
{
|
|
entity.TenantId,
|
|
entity.QuestionId,
|
|
entity.VideoId,
|
|
entity.VideoType
|
|
}).IsUnique();
|
|
|
|
builder.HasOne<Question>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.QuestionId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
builder.HasOne<VideoExplanation>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.VideoId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
}
|
|
}
|