dramaling-vocab-learning/backend/DramaLing.Api/Services/Vocabulary/Options/OptionsVocabularyService.cs

191 lines
6.8 KiB
C#

using DramaLing.Api.Data;
using DramaLing.Api.Models.Configuration;
using DramaLing.Api.Models.Entities;
using DramaLing.Api.Services.Monitoring;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using System.Diagnostics;
namespace DramaLing.Api.Services;
/// <summary>
/// 選項詞彙庫服務實作
/// 提供基於 CEFR 等級、詞性和字數的智能選項生成
/// </summary>
public class OptionsVocabularyService : IOptionsVocabularyService
{
private readonly DramaLingDbContext _context;
private readonly IMemoryCache _cache;
private readonly ILogger<OptionsVocabularyService> _logger;
private readonly OptionsVocabularyOptions _options;
private readonly OptionsVocabularyMetrics _metrics;
public OptionsVocabularyService(
DramaLingDbContext context,
IMemoryCache cache,
ILogger<OptionsVocabularyService> logger,
IOptions<OptionsVocabularyOptions> options,
OptionsVocabularyMetrics metrics)
{
_context = context;
_cache = cache;
_logger = logger;
_options = options.Value;
_metrics = metrics;
}
/// <summary>
/// 生成智能干擾選項
/// </summary>
public async Task<List<string>> GenerateDistractorsAsync(
string targetWord,
string cefrLevel,
string partOfSpeech,
int count = 3)
{
var distractorsWithDetails = await GenerateDistractorsWithDetailsAsync(
targetWord, cefrLevel, partOfSpeech, count);
return distractorsWithDetails.Select(v => v.Word).ToList();
}
/// <summary>
/// 生成智能干擾選項(含詳細資訊)
/// </summary>
public async Task<List<OptionsVocabulary>> GenerateDistractorsWithDetailsAsync(
string targetWord,
string cefrLevel,
string partOfSpeech,
int count = 3)
{
var stopwatch = Stopwatch.StartNew();
try
{
// 記錄請求指標
_metrics.RecordGenerationRequest(cefrLevel, partOfSpeech, count);
_logger.LogInformation("Generating {Count} distractors for word '{Word}' (CEFR: {CEFR}, PartOfSpeech: {PartOfSpeech}) - Using fixed options",
count, targetWord, cefrLevel, partOfSpeech);
// 暫時使用固定選項,跳過複雜的詞彙篩選機制
var fixedDistractors = new List<OptionsVocabulary>
{
new OptionsVocabulary
{
Id = Guid.NewGuid(),
Word = "apple",
CEFRLevel = cefrLevel,
PartOfSpeech = partOfSpeech,
IsActive = true,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow
},
new OptionsVocabulary
{
Id = Guid.NewGuid(),
Word = "orange",
CEFRLevel = cefrLevel,
PartOfSpeech = partOfSpeech,
IsActive = true,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow
},
new OptionsVocabulary
{
Id = Guid.NewGuid(),
Word = "banana",
CEFRLevel = cefrLevel,
PartOfSpeech = partOfSpeech,
IsActive = true,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow
}
};
// 計算字數長度
foreach (var distractor in fixedDistractors)
{
distractor.CalculateWordLength();
}
// 排除目標詞彙本身(如果匹配)
var selectedDistractors = fixedDistractors
.Where(v => !string.Equals(v.Word, targetWord, StringComparison.OrdinalIgnoreCase))
.Take(count)
.ToList();
_logger.LogInformation("Successfully generated {Count} fixed distractors for '{Word}': {Distractors}",
selectedDistractors.Count, targetWord,
string.Join(", ", selectedDistractors.Select(d => d.Word)));
// 記錄生成完成指標
stopwatch.Stop();
_metrics.RecordGenerationDuration(stopwatch.Elapsed, selectedDistractors.Count);
return selectedDistractors;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error generating distractors for word '{Word}'", targetWord);
_metrics.RecordError("generation_failed", "GenerateDistractorsWithDetailsAsync");
return new List<OptionsVocabulary>();
}
}
/// <summary>
/// 檢查詞彙庫是否有足夠的詞彙支援選項生成
/// </summary>
public async Task<bool> HasSufficientVocabularyAsync(string cefrLevel, string partOfSpeech)
{
try
{
var allowedLevels = GetAllowedCEFRLevels(cefrLevel);
var count = await _context.OptionsVocabularies
.Where(v => v.IsActive &&
allowedLevels.Contains(v.CEFRLevel) &&
v.PartOfSpeech == partOfSpeech)
.CountAsync();
var hasSufficient = count >= _options.MinimumVocabularyThreshold;
_logger.LogDebug("Vocabulary count for CEFR: {CEFR}, PartOfSpeech: {PartOfSpeech}: {Count} (sufficient: {HasSufficient})",
cefrLevel, partOfSpeech, count, hasSufficient);
return hasSufficient;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error checking vocabulary sufficiency for CEFR: {CEFR}, PartOfSpeech: {PartOfSpeech}",
cefrLevel, partOfSpeech);
return false;
}
}
/// <summary>
/// 獲取允許的 CEFR 等級(包含相鄰等級)
/// </summary>
private static List<string> GetAllowedCEFRLevels(string targetLevel)
{
var levels = new[] { "A1", "A2", "B1", "B2", "C1", "C2" };
var targetIndex = Array.IndexOf(levels, targetLevel);
if (targetIndex == -1)
{
// 如果不是標準 CEFR 等級,只返回原等級
return new List<string> { targetLevel };
}
var allowed = new List<string> { targetLevel };
// 加入相鄰等級(允許難度稍有差異)
if (targetIndex > 0)
allowed.Add(levels[targetIndex - 1]);
if (targetIndex < levels.Length - 1)
allowed.Add(levels[targetIndex + 1]);
return allowed;
}
}