using DramaLing.Api.Services; namespace DramaLing.Api.Services.Domain.Learning; /// /// 間隔重複學習服務介面 /// public interface ISpacedRepetitionService { /// /// 計算下次複習時間 /// Task CalculateNextReviewAsync(ReviewInput input); /// /// 更新學習進度 /// Task UpdateStudyProgressAsync(Guid flashcardId, int qualityRating, Guid userId); /// /// 取得今日應複習的詞卡 /// Task> GetDueCardsAsync(Guid userId, int limit = 20); /// /// 取得學習統計 /// Task GetLearningAnalyticsAsync(Guid userId); /// /// 優化學習序列 /// Task GenerateStudyPlanAsync(Guid userId, int targetMinutes); } /// /// 複習輸入參數 /// public class ReviewInput { public Guid FlashcardId { get; set; } public Guid UserId { get; set; } public int QualityRating { get; set; } // 1-5 (SM2 標準) public int CurrentRepetitions { get; set; } public float CurrentEasinessFactor { get; set; } public int CurrentIntervalDays { get; set; } public DateTime LastReviewDate { get; set; } } /// /// 複習排程結果 /// public class ReviewSchedule { public DateTime NextReviewDate { get; set; } public int NewIntervalDays { get; set; } public float NewEasinessFactor { get; set; } public int NewRepetitions { get; set; } public int NewMasteryLevel { get; set; } public string RecommendedAction { get; set; } = string.Empty; } /// /// 學習進度 /// public class StudyProgress { public Guid FlashcardId { get; set; } public bool IsImproved { get; set; } public int PreviousMasteryLevel { get; set; } public int NewMasteryLevel { get; set; } public DateTime NextReviewDate { get; set; } public string ProgressMessage { get; set; } = string.Empty; } /// /// 複習卡片 /// public class ReviewCard { public Guid Id { get; set; } public string Word { get; set; } = string.Empty; public string Translation { get; set; } = string.Empty; public string DifficultyLevel { get; set; } = string.Empty; public int MasteryLevel { get; set; } public DateTime NextReviewDate { get; set; } public int DaysSinceLastReview { get; set; } public int ReviewPriority { get; set; } // 1-5 (5 最高) } /// /// 學習分析 /// public class LearningAnalytics { public int TotalCards { get; set; } public int DueCards { get; set; } public int OverdueCards { get; set; } public int MasteredCards { get; set; } public double RetentionRate { get; set; } public TimeSpan AverageStudyInterval { get; set; } public Dictionary DifficultyDistribution { get; set; } = new(); public List RecentPerformance { get; set; } = new(); } /// /// 每日學習統計 /// public class DailyStudyStats { public DateOnly Date { get; set; } public int CardsReviewed { get; set; } public int CorrectAnswers { get; set; } public double AccuracyRate => CardsReviewed > 0 ? (double)CorrectAnswers / CardsReviewed : 0; public TimeSpan StudyDuration { get; set; } } /// /// 優化學習計劃 /// public class OptimizedStudyPlan { public IEnumerable RecommendedCards { get; set; } = new List(); public int EstimatedMinutes { get; set; } public string StudyFocus { get; set; } = string.Empty; // "複習", "新學習", "加強練習" public Dictionary LevelBreakdown { get; set; } = new(); public string RecommendationReason { get; set; } = string.Empty; } /// /// 間隔重複學習服務實作 /// public class SpacedRepetitionService : ISpacedRepetitionService { private readonly ILogger _logger; public SpacedRepetitionService(ILogger logger) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public Task CalculateNextReviewAsync(ReviewInput input) { try { // 使用現有的 SM2Algorithm var sm2Input = new SM2Input( input.QualityRating, input.CurrentEasinessFactor, input.CurrentRepetitions, input.CurrentIntervalDays ); var sm2Result = SM2Algorithm.Calculate(sm2Input); var schedule = new ReviewSchedule { NextReviewDate = sm2Result.NextReviewDate, NewIntervalDays = sm2Result.IntervalDays, NewEasinessFactor = sm2Result.EasinessFactor, NewRepetitions = sm2Result.Repetitions, NewMasteryLevel = CalculateMasteryLevel(sm2Result.EasinessFactor, sm2Result.Repetitions), RecommendedAction = GetRecommendedAction(input.QualityRating) }; return Task.FromResult(schedule); } catch (Exception ex) { _logger.LogError(ex, "Error calculating next review for flashcard {FlashcardId}", input.FlashcardId); throw; } } public Task UpdateStudyProgressAsync(Guid flashcardId, int qualityRating, Guid userId) { // 這裡應該整合 Repository 來獲取和更新詞卡數據 // 暫時返回模擬結果 var progress = new StudyProgress { FlashcardId = flashcardId, IsImproved = qualityRating >= 3, ProgressMessage = GetProgressMessage(qualityRating) }; return Task.FromResult(progress); } public Task> GetDueCardsAsync(Guid userId, int limit = 20) { // 需要整合 Repository 來實作 var cards = new List(); return Task.FromResult>(cards); } public Task GetLearningAnalyticsAsync(Guid userId) { // 需要整合 Repository 來實作 var analytics = new LearningAnalytics(); return Task.FromResult(analytics); } public Task GenerateStudyPlanAsync(Guid userId, int targetMinutes) { // 需要整合 Repository 和 AI 服務來實作 var plan = new OptimizedStudyPlan { EstimatedMinutes = targetMinutes, StudyFocus = "複習", RecommendationReason = "基於間隔重複算法的個人化推薦" }; return Task.FromResult(plan); } #region 私有方法 private int CalculateMasteryLevel(float easinessFactor, int repetitions) { // 根據難度係數和重複次數計算掌握程度 if (repetitions >= 5 && easinessFactor >= 2.3f) return 5; // 完全掌握 if (repetitions >= 3 && easinessFactor >= 2.0f) return 4; // 熟練 if (repetitions >= 2 && easinessFactor >= 1.8f) return 3; // 理解 if (repetitions >= 1) return 2; // 認識 return 1; // 新學習 } private string GetRecommendedAction(int qualityRating) { return qualityRating switch { 1 => "建議重新學習此詞彙", 2 => "需要額外練習", 3 => "繼續複習", 4 => "掌握良好", 5 => "完全掌握", _ => "繼續學習" }; } private string GetProgressMessage(int qualityRating) { return qualityRating switch { 1 or 2 => "需要加強練習,別氣餒!", 3 => "不錯的進步!", 4 => "很好!掌握得不錯", 5 => "太棒了!完全掌握", _ => "繼續努力學習!" }; } #endregion }