namespace DramaLing.Api.Services.Domain.Learning; /// /// CEFR 等級服務介面 /// public interface ICEFRLevelService { /// /// 取得 CEFR 等級的數字索引 /// int GetLevelIndex(string level); /// /// 判定詞彙對特定用戶是否為高價值 /// bool IsHighValueForUser(string wordLevel, string userLevel); /// /// 取得用戶的目標學習等級範圍 /// string GetTargetLevelRange(string userLevel); /// /// 取得下一個等級 /// string GetNextLevel(string currentLevel); /// /// 計算等級進度百分比 /// double CalculateLevelProgress(string currentLevel, int masteredWords, int totalWords); /// /// 根據掌握詞彙數推薦等級 /// string RecommendLevel(Dictionary masteredWordsByLevel); /// /// 驗證等級是否有效 /// bool IsValidLevel(string level); /// /// 取得所有等級列表 /// IEnumerable GetAllLevels(); } /// /// CEFR 等級服務實作 /// public class CEFRLevelService : ICEFRLevelService { private static readonly string[] Levels = { "A1", "A2", "B1", "B2", "C1", "C2" }; private readonly ILogger _logger; public CEFRLevelService(ILogger logger) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public int GetLevelIndex(string level) { if (string.IsNullOrEmpty(level)) { _logger.LogWarning("Invalid level provided: null or empty, defaulting to A2"); return 1; // 預設 A2 } var index = Array.IndexOf(Levels, level.ToUpper()); if (index == -1) { _logger.LogWarning("Unknown CEFR level: {Level}, defaulting to A2", level); return 1; } return index; } public bool IsHighValueForUser(string wordLevel, string userLevel) { var userIndex = GetLevelIndex(userLevel); var wordIndex = GetLevelIndex(wordLevel); // 無效等級處理 if (userIndex == -1 || wordIndex == -1) { _logger.LogWarning("Invalid levels for comparison: word={WordLevel}, user={UserLevel}", wordLevel, userLevel); return false; } // 高價值 = 比用戶程度高 1-2 級 var isHighValue = wordIndex >= userIndex + 1 && wordIndex <= userIndex + 2; _logger.LogDebug("High value check: word={WordLevel}({WordIndex}), user={UserLevel}({UserIndex}), result={IsHighValue}", wordLevel, wordIndex, userLevel, userIndex, isHighValue); return isHighValue; } public string GetTargetLevelRange(string userLevel) { var userIndex = GetLevelIndex(userLevel); if (userIndex == -1) return "B1-B2"; var targetMin = Levels[Math.Min(userIndex + 1, Levels.Length - 1)]; var targetMax = Levels[Math.Min(userIndex + 2, Levels.Length - 1)]; return targetMin == targetMax ? targetMin : $"{targetMin}-{targetMax}"; } public string GetNextLevel(string currentLevel) { var currentIndex = GetLevelIndex(currentLevel); if (currentIndex == -1 || currentIndex >= Levels.Length - 1) { return Levels[^1]; // 返回最高等級 } return Levels[currentIndex + 1]; } public double CalculateLevelProgress(string currentLevel, int masteredWords, int totalWords) { if (totalWords == 0) return 0; var progress = (double)masteredWords / totalWords; _logger.LogDebug("Level progress for {Level}: {MasteredWords}/{TotalWords} = {Progress:P}", currentLevel, masteredWords, totalWords, progress); return Math.Min(progress, 1.0); } public string RecommendLevel(Dictionary masteredWordsByLevel) { try { // 簡單的推薦邏輯:找到掌握詞彙最多的等級 var bestLevel = masteredWordsByLevel .Where(kvp => kvp.Value > 0) .OrderByDescending(kvp => kvp.Value) .FirstOrDefault(); if (bestLevel.Key != null && IsValidLevel(bestLevel.Key)) { return GetNextLevel(bestLevel.Key); } return "A2"; // 預設等級 } catch (Exception ex) { _logger.LogError(ex, "Error recommending level"); return "A2"; } } public bool IsValidLevel(string level) { return !string.IsNullOrEmpty(level) && Levels.Contains(level.ToUpper()); } public IEnumerable GetAllLevels() { return Levels.AsEnumerable(); } }