dramaling-vocab-learning/backend/DramaLing.Api/Data/DramaLingDbContext.cs

480 lines
26 KiB
C#

using Microsoft.EntityFrameworkCore;
using DramaLing.Api.Models.Entities;
using System.Text.Json;
namespace DramaLing.Api.Data;
public class DramaLingDbContext : DbContext
{
public DramaLingDbContext(DbContextOptions<DramaLingDbContext> options) : base(options)
{
}
// DbSets
public DbSet<User> Users { get; set; }
public DbSet<UserSettings> UserSettings { get; set; }
public DbSet<Flashcard> Flashcards { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<FlashcardTag> FlashcardTags { get; set; }
public DbSet<StudySession> StudySessions { get; set; }
public DbSet<StudyRecord> StudyRecords { get; set; }
public DbSet<StudyCard> StudyCards { get; set; }
public DbSet<TestResult> TestResults { get; set; }
public DbSet<ErrorReport> ErrorReports { get; set; }
public DbSet<DailyStats> DailyStats { get; set; }
public DbSet<SentenceAnalysisCache> SentenceAnalysisCache { get; set; }
public DbSet<WordQueryUsageStats> WordQueryUsageStats { get; set; }
public DbSet<AudioCache> AudioCaches { get; set; }
public DbSet<PronunciationAssessment> PronunciationAssessments { get; set; }
public DbSet<UserAudioPreferences> UserAudioPreferences { get; set; }
public DbSet<ExampleImage> ExampleImages { get; set; }
public DbSet<FlashcardExampleImage> FlashcardExampleImages { get; set; }
public DbSet<ImageGenerationRequest> ImageGenerationRequests { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// 設定表名稱 (與 Supabase 一致)
modelBuilder.Entity<User>().ToTable("user_profiles");
modelBuilder.Entity<UserSettings>().ToTable("user_settings");
modelBuilder.Entity<Flashcard>().ToTable("flashcards");
modelBuilder.Entity<Tag>().ToTable("tags");
modelBuilder.Entity<FlashcardTag>().ToTable("flashcard_tags");
modelBuilder.Entity<StudySession>().ToTable("study_sessions");
modelBuilder.Entity<StudyRecord>().ToTable("study_records");
modelBuilder.Entity<StudyCard>().ToTable("study_cards");
modelBuilder.Entity<TestResult>().ToTable("test_results");
modelBuilder.Entity<ErrorReport>().ToTable("error_reports");
modelBuilder.Entity<DailyStats>().ToTable("daily_stats");
modelBuilder.Entity<AudioCache>().ToTable("audio_cache");
modelBuilder.Entity<PronunciationAssessment>().ToTable("pronunciation_assessments");
modelBuilder.Entity<UserAudioPreferences>().ToTable("user_audio_preferences");
modelBuilder.Entity<ExampleImage>().ToTable("example_images");
modelBuilder.Entity<FlashcardExampleImage>().ToTable("flashcard_example_images");
modelBuilder.Entity<ImageGenerationRequest>().ToTable("image_generation_requests");
// 配置屬性名稱 (snake_case)
ConfigureUserEntity(modelBuilder);
ConfigureFlashcardEntity(modelBuilder);
ConfigureStudyEntities(modelBuilder);
ConfigureTagEntities(modelBuilder);
ConfigureErrorReportEntity(modelBuilder);
ConfigureDailyStatsEntity(modelBuilder);
ConfigureAudioEntities(modelBuilder);
ConfigureImageGenerationEntities(modelBuilder);
// 複合主鍵
modelBuilder.Entity<FlashcardTag>()
.HasKey(ft => new { ft.FlashcardId, ft.TagId });
modelBuilder.Entity<FlashcardExampleImage>()
.HasKey(fei => new { fei.FlashcardId, fei.ExampleImageId });
modelBuilder.Entity<DailyStats>()
.HasIndex(ds => new { ds.UserId, ds.Date })
.IsUnique();
// 外鍵關係
ConfigureRelationships(modelBuilder);
}
private void ConfigureUserEntity(ModelBuilder modelBuilder)
{
var userEntity = modelBuilder.Entity<User>();
userEntity.Property(u => u.Username).HasColumnName("username");
userEntity.Property(u => u.Email).HasColumnName("email");
userEntity.Property(u => u.PasswordHash).HasColumnName("password_hash");
userEntity.Property(u => u.DisplayName).HasColumnName("display_name");
userEntity.Property(u => u.AvatarUrl).HasColumnName("avatar_url");
userEntity.Property(u => u.SubscriptionType).HasColumnName("subscription_type");
userEntity.Property(u => u.Preferences)
.HasColumnName("preferences")
.HasConversion(
v => System.Text.Json.JsonSerializer.Serialize(v, (System.Text.Json.JsonSerializerOptions)null),
v => System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, object>>(v, (System.Text.Json.JsonSerializerOptions)null) ?? new Dictionary<string, object>());
// 新增個人化欄位映射
userEntity.Property(u => u.EnglishLevel).HasColumnName("english_level");
userEntity.Property(u => u.LevelUpdatedAt).HasColumnName("level_updated_at");
userEntity.Property(u => u.IsLevelVerified).HasColumnName("is_level_verified");
userEntity.Property(u => u.LevelNotes).HasColumnName("level_notes");
userEntity.Property(u => u.CreatedAt).HasColumnName("created_at");
userEntity.Property(u => u.UpdatedAt).HasColumnName("updated_at");
// Add unique indexes
userEntity.HasIndex(u => u.Email).IsUnique();
userEntity.HasIndex(u => u.Username).IsUnique();
}
private void ConfigureFlashcardEntity(ModelBuilder modelBuilder)
{
var flashcardEntity = modelBuilder.Entity<Flashcard>();
flashcardEntity.Property(f => f.UserId).HasColumnName("user_id");
flashcardEntity.Property(f => f.PartOfSpeech).HasColumnName("part_of_speech");
flashcardEntity.Property(f => f.ExampleTranslation).HasColumnName("example_translation");
flashcardEntity.Property(f => f.EasinessFactor).HasColumnName("easiness_factor");
flashcardEntity.Property(f => f.IntervalDays).HasColumnName("interval_days");
flashcardEntity.Property(f => f.NextReviewDate).HasColumnName("next_review_date");
flashcardEntity.Property(f => f.MasteryLevel).HasColumnName("mastery_level");
flashcardEntity.Property(f => f.TimesReviewed).HasColumnName("times_reviewed");
flashcardEntity.Property(f => f.TimesCorrect).HasColumnName("times_correct");
flashcardEntity.Property(f => f.LastReviewedAt).HasColumnName("last_reviewed_at");
flashcardEntity.Property(f => f.IsFavorite).HasColumnName("is_favorite");
flashcardEntity.Property(f => f.IsArchived).HasColumnName("is_archived");
flashcardEntity.Property(f => f.DifficultyLevel).HasColumnName("difficulty_level");
flashcardEntity.Property(f => f.CreatedAt).HasColumnName("created_at");
flashcardEntity.Property(f => f.UpdatedAt).HasColumnName("updated_at");
}
private void ConfigureStudyEntities(ModelBuilder modelBuilder)
{
var sessionEntity = modelBuilder.Entity<StudySession>();
sessionEntity.Property(s => s.UserId).HasColumnName("user_id");
sessionEntity.Property(s => s.SessionType).HasColumnName("session_type");
sessionEntity.Property(s => s.StartedAt).HasColumnName("started_at");
sessionEntity.Property(s => s.EndedAt).HasColumnName("ended_at");
sessionEntity.Property(s => s.TotalCards).HasColumnName("total_cards");
sessionEntity.Property(s => s.CorrectCount).HasColumnName("correct_count");
sessionEntity.Property(s => s.DurationSeconds).HasColumnName("duration_seconds");
sessionEntity.Property(s => s.AverageResponseTimeMs).HasColumnName("average_response_time_ms");
var recordEntity = modelBuilder.Entity<StudyRecord>();
recordEntity.Property(r => r.UserId).HasColumnName("user_id");
recordEntity.Property(r => r.FlashcardId).HasColumnName("flashcard_id");
recordEntity.Property(r => r.SessionId).HasColumnName("session_id");
recordEntity.Property(r => r.StudyMode).HasColumnName("study_mode");
recordEntity.Property(r => r.QualityRating).HasColumnName("quality_rating");
recordEntity.Property(r => r.ResponseTimeMs).HasColumnName("response_time_ms");
recordEntity.Property(r => r.UserAnswer).HasColumnName("user_answer");
recordEntity.Property(r => r.IsCorrect).HasColumnName("is_correct");
recordEntity.Property(r => r.StudiedAt).HasColumnName("studied_at");
// 添加複合唯一索引:防止同一用戶同一詞卡同一測驗類型重複記錄
recordEntity.HasIndex(r => new { r.UserId, r.FlashcardId, r.StudyMode })
.IsUnique()
.HasDatabaseName("IX_StudyRecord_UserCard_TestType_Unique");
}
private void ConfigureTagEntities(ModelBuilder modelBuilder)
{
var tagEntity = modelBuilder.Entity<Tag>();
tagEntity.Property(t => t.UserId).HasColumnName("user_id");
tagEntity.Property(t => t.UsageCount).HasColumnName("usage_count");
tagEntity.Property(t => t.CreatedAt).HasColumnName("created_at");
var flashcardTagEntity = modelBuilder.Entity<FlashcardTag>();
flashcardTagEntity.Property(ft => ft.FlashcardId).HasColumnName("flashcard_id");
flashcardTagEntity.Property(ft => ft.TagId).HasColumnName("tag_id");
}
private void ConfigureErrorReportEntity(ModelBuilder modelBuilder)
{
var errorEntity = modelBuilder.Entity<ErrorReport>();
errorEntity.Property(e => e.UserId).HasColumnName("user_id");
errorEntity.Property(e => e.FlashcardId).HasColumnName("flashcard_id");
errorEntity.Property(e => e.ReportType).HasColumnName("report_type");
errorEntity.Property(e => e.StudyMode).HasColumnName("study_mode");
errorEntity.Property(e => e.AdminNotes).HasColumnName("admin_notes");
errorEntity.Property(e => e.ResolvedAt).HasColumnName("resolved_at");
errorEntity.Property(e => e.ResolvedBy).HasColumnName("resolved_by");
errorEntity.Property(e => e.CreatedAt).HasColumnName("created_at");
}
private void ConfigureDailyStatsEntity(ModelBuilder modelBuilder)
{
var statsEntity = modelBuilder.Entity<DailyStats>();
statsEntity.Property(d => d.UserId).HasColumnName("user_id");
statsEntity.Property(d => d.WordsStudied).HasColumnName("words_studied");
statsEntity.Property(d => d.WordsCorrect).HasColumnName("words_correct");
statsEntity.Property(d => d.StudyTimeSeconds).HasColumnName("study_time_seconds");
statsEntity.Property(d => d.SessionCount).HasColumnName("session_count");
statsEntity.Property(d => d.CardsGenerated).HasColumnName("cards_generated");
statsEntity.Property(d => d.AiApiCalls).HasColumnName("ai_api_calls");
statsEntity.Property(d => d.CreatedAt).HasColumnName("created_at");
}
private void ConfigureRelationships(ModelBuilder modelBuilder)
{
// User relationships
modelBuilder.Entity<Flashcard>()
.HasOne(f => f.User)
.WithMany(u => u.Flashcards)
.HasForeignKey(f => f.UserId)
.OnDelete(DeleteBehavior.Cascade);
// Study relationships
modelBuilder.Entity<StudySession>()
.HasOne(ss => ss.User)
.WithMany(u => u.StudySessions)
.HasForeignKey(ss => ss.UserId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<StudyRecord>()
.HasOne(sr => sr.Flashcard)
.WithMany(f => f.StudyRecords)
.HasForeignKey(sr => sr.FlashcardId)
.OnDelete(DeleteBehavior.Cascade);
// Tag relationships
modelBuilder.Entity<FlashcardTag>()
.HasOne(ft => ft.Flashcard)
.WithMany(f => f.FlashcardTags)
.HasForeignKey(ft => ft.FlashcardId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<FlashcardTag>()
.HasOne(ft => ft.Tag)
.WithMany(t => t.FlashcardTags)
.HasForeignKey(ft => ft.TagId)
.OnDelete(DeleteBehavior.Cascade);
// Error report relationships
modelBuilder.Entity<ErrorReport>()
.HasOne(er => er.User)
.WithMany(u => u.ErrorReports)
.HasForeignKey(er => er.UserId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<ErrorReport>()
.HasOne(er => er.Flashcard)
.WithMany(f => f.ErrorReports)
.HasForeignKey(er => er.FlashcardId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<ErrorReport>()
.HasOne(er => er.ResolvedByUser)
.WithMany()
.HasForeignKey(er => er.ResolvedBy)
.OnDelete(DeleteBehavior.SetNull);
// User settings relationship
modelBuilder.Entity<UserSettings>()
.HasOne(us => us.User)
.WithOne(u => u.Settings)
.HasForeignKey<UserSettings>(us => us.UserId)
.OnDelete(DeleteBehavior.Cascade);
// Daily stats relationship
modelBuilder.Entity<DailyStats>()
.HasOne(ds => ds.User)
.WithMany(u => u.DailyStats)
.HasForeignKey(ds => ds.UserId)
.OnDelete(DeleteBehavior.Cascade);
// Sentence analysis cache configuration
modelBuilder.Entity<SentenceAnalysisCache>()
.HasIndex(sac => sac.InputTextHash)
.HasDatabaseName("IX_SentenceAnalysisCache_Hash");
modelBuilder.Entity<SentenceAnalysisCache>()
.HasIndex(sac => sac.ExpiresAt)
.HasDatabaseName("IX_SentenceAnalysisCache_Expires");
modelBuilder.Entity<SentenceAnalysisCache>()
.HasIndex(sac => new { sac.InputTextHash, sac.ExpiresAt })
.HasDatabaseName("IX_SentenceAnalysisCache_Hash_Expires");
// Word query usage stats configuration
modelBuilder.Entity<WordQueryUsageStats>()
.HasOne(wq => wq.User)
.WithMany()
.HasForeignKey(wq => wq.UserId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<WordQueryUsageStats>()
.HasIndex(wq => new { wq.UserId, wq.Date })
.IsUnique()
.HasDatabaseName("IX_WordQueryUsageStats_UserDate");
modelBuilder.Entity<WordQueryUsageStats>()
.HasIndex(wq => wq.CreatedAt)
.HasDatabaseName("IX_WordQueryUsageStats_CreatedAt");
// Audio entities relationships
ConfigureAudioRelationships(modelBuilder);
}
private void ConfigureAudioEntities(ModelBuilder modelBuilder)
{
// AudioCache configuration
var audioCacheEntity = modelBuilder.Entity<AudioCache>();
audioCacheEntity.Property(ac => ac.TextHash).HasColumnName("text_hash");
audioCacheEntity.Property(ac => ac.TextContent).HasColumnName("text_content");
audioCacheEntity.Property(ac => ac.VoiceId).HasColumnName("voice_id");
audioCacheEntity.Property(ac => ac.AudioUrl).HasColumnName("audio_url");
audioCacheEntity.Property(ac => ac.FileSize).HasColumnName("file_size");
audioCacheEntity.Property(ac => ac.DurationMs).HasColumnName("duration_ms");
audioCacheEntity.Property(ac => ac.CreatedAt).HasColumnName("created_at");
audioCacheEntity.Property(ac => ac.LastAccessed).HasColumnName("last_accessed");
audioCacheEntity.Property(ac => ac.AccessCount).HasColumnName("access_count");
audioCacheEntity.HasIndex(ac => ac.TextHash)
.IsUnique()
.HasDatabaseName("IX_AudioCache_TextHash");
audioCacheEntity.HasIndex(ac => ac.LastAccessed)
.HasDatabaseName("IX_AudioCache_LastAccessed");
// PronunciationAssessment configuration
var pronunciationEntity = modelBuilder.Entity<PronunciationAssessment>();
pronunciationEntity.Property(pa => pa.UserId).HasColumnName("user_id");
pronunciationEntity.Property(pa => pa.FlashcardId).HasColumnName("flashcard_id");
pronunciationEntity.Property(pa => pa.TargetText).HasColumnName("target_text");
pronunciationEntity.Property(pa => pa.AudioUrl).HasColumnName("audio_url");
pronunciationEntity.Property(pa => pa.OverallScore).HasColumnName("overall_score");
pronunciationEntity.Property(pa => pa.AccuracyScore).HasColumnName("accuracy_score");
pronunciationEntity.Property(pa => pa.FluencyScore).HasColumnName("fluency_score");
pronunciationEntity.Property(pa => pa.CompletenessScore).HasColumnName("completeness_score");
pronunciationEntity.Property(pa => pa.ProsodyScore).HasColumnName("prosody_score");
pronunciationEntity.Property(pa => pa.PhonemeScores).HasColumnName("phoneme_scores");
pronunciationEntity.Property(pa => pa.Suggestions).HasColumnName("suggestions");
pronunciationEntity.Property(pa => pa.StudySessionId).HasColumnName("study_session_id");
pronunciationEntity.Property(pa => pa.PracticeMode).HasColumnName("practice_mode");
pronunciationEntity.Property(pa => pa.CreatedAt).HasColumnName("created_at");
pronunciationEntity.HasIndex(pa => new { pa.UserId, pa.FlashcardId })
.HasDatabaseName("IX_PronunciationAssessment_UserFlashcard");
pronunciationEntity.HasIndex(pa => pa.StudySessionId)
.HasDatabaseName("IX_PronunciationAssessment_Session");
// UserAudioPreferences configuration
var audioPrefsEntity = modelBuilder.Entity<UserAudioPreferences>();
audioPrefsEntity.Property(uap => uap.PreferredAccent).HasColumnName("preferred_accent");
audioPrefsEntity.Property(uap => uap.PreferredVoiceMale).HasColumnName("preferred_voice_male");
audioPrefsEntity.Property(uap => uap.PreferredVoiceFemale).HasColumnName("preferred_voice_female");
audioPrefsEntity.Property(uap => uap.DefaultSpeed).HasColumnName("default_speed");
audioPrefsEntity.Property(uap => uap.AutoPlayEnabled).HasColumnName("auto_play_enabled");
audioPrefsEntity.Property(uap => uap.PronunciationDifficulty).HasColumnName("pronunciation_difficulty");
audioPrefsEntity.Property(uap => uap.TargetScoreThreshold).HasColumnName("target_score_threshold");
audioPrefsEntity.Property(uap => uap.EnableDetailedFeedback).HasColumnName("enable_detailed_feedback");
audioPrefsEntity.Property(uap => uap.UpdatedAt).HasColumnName("updated_at");
}
private void ConfigureAudioRelationships(ModelBuilder modelBuilder)
{
// PronunciationAssessment relationships
modelBuilder.Entity<PronunciationAssessment>()
.HasOne(pa => pa.User)
.WithMany()
.HasForeignKey(pa => pa.UserId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<PronunciationAssessment>()
.HasOne(pa => pa.Flashcard)
.WithMany()
.HasForeignKey(pa => pa.FlashcardId)
.OnDelete(DeleteBehavior.SetNull);
modelBuilder.Entity<PronunciationAssessment>()
.HasOne(pa => pa.StudySession)
.WithMany()
.HasForeignKey(pa => pa.StudySessionId)
.OnDelete(DeleteBehavior.SetNull);
// UserAudioPreferences relationship
modelBuilder.Entity<UserAudioPreferences>()
.HasOne(uap => uap.User)
.WithOne()
.HasForeignKey<UserAudioPreferences>(uap => uap.UserId)
.OnDelete(DeleteBehavior.Cascade);
}
private void ConfigureImageGenerationEntities(ModelBuilder modelBuilder)
{
// ExampleImage configuration
var exampleImageEntity = modelBuilder.Entity<ExampleImage>();
exampleImageEntity.Property(ei => ei.RelativePath).HasColumnName("relative_path");
exampleImageEntity.Property(ei => ei.AltText).HasColumnName("alt_text");
exampleImageEntity.Property(ei => ei.GeminiPrompt).HasColumnName("gemini_prompt");
exampleImageEntity.Property(ei => ei.GeminiDescription).HasColumnName("gemini_description");
exampleImageEntity.Property(ei => ei.ReplicatePrompt).HasColumnName("replicate_prompt");
exampleImageEntity.Property(ei => ei.ReplicateModel).HasColumnName("replicate_model");
exampleImageEntity.Property(ei => ei.ReplicateVersion).HasColumnName("replicate_version");
exampleImageEntity.Property(ei => ei.GeminiCost).HasColumnName("gemini_cost");
exampleImageEntity.Property(ei => ei.ReplicateCost).HasColumnName("replicate_cost");
exampleImageEntity.Property(ei => ei.TotalGenerationCost).HasColumnName("total_generation_cost");
exampleImageEntity.Property(ei => ei.FileSize).HasColumnName("file_size");
exampleImageEntity.Property(ei => ei.ImageWidth).HasColumnName("image_width");
exampleImageEntity.Property(ei => ei.ImageHeight).HasColumnName("image_height");
exampleImageEntity.Property(ei => ei.ContentHash).HasColumnName("content_hash");
exampleImageEntity.Property(ei => ei.QualityScore).HasColumnName("quality_score");
exampleImageEntity.Property(ei => ei.ModerationStatus).HasColumnName("moderation_status");
exampleImageEntity.Property(ei => ei.ModerationNotes).HasColumnName("moderation_notes");
exampleImageEntity.Property(ei => ei.AccessCount).HasColumnName("access_count");
exampleImageEntity.Property(ei => ei.CreatedAt).HasColumnName("created_at");
exampleImageEntity.Property(ei => ei.UpdatedAt).HasColumnName("updated_at");
exampleImageEntity.HasIndex(ei => ei.ContentHash).IsUnique();
exampleImageEntity.HasIndex(ei => ei.AccessCount);
// FlashcardExampleImage configuration
var flashcardImageEntity = modelBuilder.Entity<FlashcardExampleImage>();
flashcardImageEntity.Property(fei => fei.FlashcardId).HasColumnName("flashcard_id");
flashcardImageEntity.Property(fei => fei.ExampleImageId).HasColumnName("example_image_id");
flashcardImageEntity.Property(fei => fei.DisplayOrder).HasColumnName("display_order");
flashcardImageEntity.Property(fei => fei.IsPrimary).HasColumnName("is_primary");
flashcardImageEntity.Property(fei => fei.ContextRelevance).HasColumnName("context_relevance");
flashcardImageEntity.Property(fei => fei.CreatedAt).HasColumnName("created_at");
// ImageGenerationRequest configuration
var generationRequestEntity = modelBuilder.Entity<ImageGenerationRequest>();
generationRequestEntity.Property(igr => igr.UserId).HasColumnName("user_id");
generationRequestEntity.Property(igr => igr.FlashcardId).HasColumnName("flashcard_id");
generationRequestEntity.Property(igr => igr.OverallStatus).HasColumnName("overall_status");
generationRequestEntity.Property(igr => igr.GeminiStatus).HasColumnName("gemini_status");
generationRequestEntity.Property(igr => igr.ReplicateStatus).HasColumnName("replicate_status");
generationRequestEntity.Property(igr => igr.OriginalRequest).HasColumnName("original_request");
generationRequestEntity.Property(igr => igr.GeminiPrompt).HasColumnName("gemini_prompt");
generationRequestEntity.Property(igr => igr.GeneratedDescription).HasColumnName("generated_description");
generationRequestEntity.Property(igr => igr.FinalReplicatePrompt).HasColumnName("final_replicate_prompt");
generationRequestEntity.Property(igr => igr.GeneratedImageId).HasColumnName("generated_image_id");
generationRequestEntity.Property(igr => igr.GeminiErrorMessage).HasColumnName("gemini_error_message");
generationRequestEntity.Property(igr => igr.ReplicateErrorMessage).HasColumnName("replicate_error_message");
generationRequestEntity.Property(igr => igr.GeminiProcessingTimeMs).HasColumnName("gemini_processing_time_ms");
generationRequestEntity.Property(igr => igr.ReplicateProcessingTimeMs).HasColumnName("replicate_processing_time_ms");
generationRequestEntity.Property(igr => igr.TotalProcessingTimeMs).HasColumnName("total_processing_time_ms");
generationRequestEntity.Property(igr => igr.GeminiCost).HasColumnName("gemini_cost");
generationRequestEntity.Property(igr => igr.ReplicateCost).HasColumnName("replicate_cost");
generationRequestEntity.Property(igr => igr.TotalCost).HasColumnName("total_cost");
generationRequestEntity.Property(igr => igr.CreatedAt).HasColumnName("created_at");
generationRequestEntity.Property(igr => igr.GeminiStartedAt).HasColumnName("gemini_started_at");
generationRequestEntity.Property(igr => igr.GeminiCompletedAt).HasColumnName("gemini_completed_at");
generationRequestEntity.Property(igr => igr.ReplicateStartedAt).HasColumnName("replicate_started_at");
generationRequestEntity.Property(igr => igr.ReplicateCompletedAt).HasColumnName("replicate_completed_at");
generationRequestEntity.Property(igr => igr.CompletedAt).HasColumnName("completed_at");
// 關聯關係
flashcardImageEntity
.HasOne(fei => fei.Flashcard)
.WithMany(f => f.FlashcardExampleImages) // 指定反向導航屬性
.HasForeignKey(fei => fei.FlashcardId)
.OnDelete(DeleteBehavior.Cascade);
flashcardImageEntity
.HasOne(fei => fei.ExampleImage)
.WithMany(ei => ei.FlashcardExampleImages)
.HasForeignKey(fei => fei.ExampleImageId)
.OnDelete(DeleteBehavior.Cascade);
generationRequestEntity
.HasOne(igr => igr.User)
.WithMany()
.HasForeignKey(igr => igr.UserId)
.OnDelete(DeleteBehavior.Cascade);
generationRequestEntity
.HasOne(igr => igr.Flashcard)
.WithMany()
.HasForeignKey(igr => igr.FlashcardId)
.OnDelete(DeleteBehavior.Cascade);
generationRequestEntity
.HasOne(igr => igr.GeneratedImage)
.WithMany()
.HasForeignKey(igr => igr.GeneratedImageId)
.OnDelete(DeleteBehavior.SetNull);
}
}