254 lines
8.1 KiB
C#
254 lines
8.1 KiB
C#
using DramaLing.Api.Services;
|
|
|
|
namespace DramaLing.Api.Services.Domain.Learning;
|
|
|
|
/// <summary>
|
|
/// 間隔重複學習服務介面
|
|
/// </summary>
|
|
public interface ISpacedRepetitionService
|
|
{
|
|
/// <summary>
|
|
/// 計算下次複習時間
|
|
/// </summary>
|
|
Task<ReviewSchedule> CalculateNextReviewAsync(ReviewInput input);
|
|
|
|
/// <summary>
|
|
/// 更新學習進度
|
|
/// </summary>
|
|
Task<StudyProgress> UpdateStudyProgressAsync(Guid flashcardId, int qualityRating, Guid userId);
|
|
|
|
/// <summary>
|
|
/// 取得今日應複習的詞卡
|
|
/// </summary>
|
|
Task<IEnumerable<ReviewCard>> GetDueCardsAsync(Guid userId, int limit = 20);
|
|
|
|
/// <summary>
|
|
/// 取得學習統計
|
|
/// </summary>
|
|
Task<LearningAnalytics> GetLearningAnalyticsAsync(Guid userId);
|
|
|
|
/// <summary>
|
|
/// 優化學習序列
|
|
/// </summary>
|
|
Task<OptimizedStudyPlan> GenerateStudyPlanAsync(Guid userId, int targetMinutes);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 複習輸入參數
|
|
/// </summary>
|
|
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; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// 複習排程結果
|
|
/// </summary>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 學習進度
|
|
/// </summary>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 複習卡片
|
|
/// </summary>
|
|
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 最高)
|
|
}
|
|
|
|
/// <summary>
|
|
/// 學習分析
|
|
/// </summary>
|
|
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<string, int> DifficultyDistribution { get; set; } = new();
|
|
public List<DailyStudyStats> RecentPerformance { get; set; } = new();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 每日學習統計
|
|
/// </summary>
|
|
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; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// 優化學習計劃
|
|
/// </summary>
|
|
public class OptimizedStudyPlan
|
|
{
|
|
public IEnumerable<ReviewCard> RecommendedCards { get; set; } = new List<ReviewCard>();
|
|
public int EstimatedMinutes { get; set; }
|
|
public string StudyFocus { get; set; } = string.Empty; // "複習", "新學習", "加強練習"
|
|
public Dictionary<string, int> LevelBreakdown { get; set; } = new();
|
|
public string RecommendationReason { get; set; } = string.Empty;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 間隔重複學習服務實作
|
|
/// </summary>
|
|
public class SpacedRepetitionService : ISpacedRepetitionService
|
|
{
|
|
private readonly ILogger<SpacedRepetitionService> _logger;
|
|
|
|
public SpacedRepetitionService(ILogger<SpacedRepetitionService> logger)
|
|
{
|
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
}
|
|
|
|
public Task<ReviewSchedule> 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<StudyProgress> 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<IEnumerable<ReviewCard>> GetDueCardsAsync(Guid userId, int limit = 20)
|
|
{
|
|
// 需要整合 Repository 來實作
|
|
var cards = new List<ReviewCard>();
|
|
return Task.FromResult<IEnumerable<ReviewCard>>(cards);
|
|
}
|
|
|
|
public Task<LearningAnalytics> GetLearningAnalyticsAsync(Guid userId)
|
|
{
|
|
// 需要整合 Repository 來實作
|
|
var analytics = new LearningAnalytics();
|
|
return Task.FromResult(analytics);
|
|
}
|
|
|
|
public Task<OptimizedStudyPlan> 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
|
|
} |