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
}