dramaling-vocab-learning/同義詞功能實作計劃.md

8.1 KiB
Raw Blame History

同義詞功能實作計劃

問題分析

現狀

  • 前端有同義詞顯示功能: FlipMemoryTest、詞卡詳情頁都有同義詞區塊
  • 測試資料有同義詞: example-data.json 包含同義詞資料
  • AI 已生成同義詞: GeminiService 的 AnalyzeSentenceAsync 已包含同義詞提示詞和處理邏輯
  • 後端實體缺少同義詞欄位: Flashcard 模型沒有 Synonyms 屬性
  • 同義詞未儲存: AI 生成的同義詞沒有從分析結果提取並儲存到詞卡
  • API 不回傳同義詞: GetFlashcards 不包含同義詞欄位

關鍵發現

GeminiService.ConvertVocabularyAnalysis (第202行):

Synonyms = aiWord.Synonyms ?? new List<string>(),

AI 分析已經包含同義詞,但這些資料沒有被儲存到 Flashcard 實體中

影響範圍

  • AI 生成的同義詞被浪費,沒有儲存
  • 詞卡詳情頁同義詞區塊永遠為空
  • FlipMemoryTest 組件同義詞區塊不顯示
  • 學習體驗不完整

實作計劃

Phase 1: 後端資料模型擴展 (預計 0.5 天)

1.1 更新 Flashcard 實體

檔案: backend/DramaLing.Api/Models/Entities/Flashcard.cs

// 在 FilledQuestionText 欄位後添加
[MaxLength(1000)]
public string? Synonyms { get; set; }  // JSON格式存儲同義詞陣列

1.2 創建資料庫 Migration

dotnet ef migrations add AddSynonymsField
dotnet ef database update

1.3 更新 DbContext 欄位映射

檔案: backend/DramaLing.Api/Data/DramaLingDbContext.cs

private void ConfigureFlashcardEntity(ModelBuilder modelBuilder)
{
    var flashcardEntity = modelBuilder.Entity<Flashcard>();
    // ... 現有映射
    flashcardEntity.Property(f => f.Synonyms).HasColumnName("synonyms");
}

Phase 2: API 功能擴展 (預計 1 天)

2.1 更新創建詞卡 DTO

檔案: backend/DramaLing.Api/Models/DTOs/CreateFlashcardRequest.cs

public class CreateFlashcardRequest
{
    // ... 現有屬性
    public List<string>? Synonyms { get; set; }
}

2.2 修改 FlashcardsController

檔案: backend/DramaLing.Api/Controllers/FlashcardsController.cs

CreateFlashcard 方法
var flashcard = new Flashcard
{
    // ... 現有欄位
    Synonyms = request.Synonyms?.Any() == true
        ? JsonSerializer.Serialize(request.Synonyms)
        : null,
};
GetFlashcards 方法 (查詢回應)
flashcardDtos.Add(new
{
    // ... 現有欄位
    Synonyms = !string.IsNullOrEmpty(flashcard.Synonyms)
        ? JsonSerializer.Deserialize<List<string>>(flashcard.Synonyms) ?? new List<string>()
        : new List<string>(),
});

2.3 利用現有 AI 分析的同義詞

關鍵發現: GeminiService 的句子分析 已經包含同義詞生成

現有邏輯 (GeminiService.cs 第70行):

"synonyms": ["synonym1", "synonym2"],

現有處理 (第202行):

Synonyms = aiWord.Synonyms ?? new List<string>(),

問題: 這些同義詞只存在於 AI 分析結果中,沒有儲存到 Flashcard 實體

解決方案: 修改詞卡創建流程,從 AI 分析結果提取同義詞並儲存

Phase 3: 創建詞卡時自動生成同義詞 (預計 0.5 天)

3.1 修改創建流程 - 從現有 AI 分析提取同義詞

檔案: backend/DramaLing.Api/Controllers/FlashcardsController.cs

在 CreateFlashcard 方法中AI 分析後:

// 現有的 AI 分析邏輯
var analysisResult = await _analysisService.AnalyzeSentenceAsync(request.Example, options);

// 🆕 從 AI 分析結果提取目標詞彙的同義詞
var targetWordAnalysis = analysisResult.VocabularyAnalysis
    .FirstOrDefault(kvp => kvp.Key.Equals(request.Word, StringComparison.OrdinalIgnoreCase));

List<string> synonyms = new();
if (targetWordAnalysis.Value != null && targetWordAnalysis.Value.Synonyms.Any())
{
    synonyms = targetWordAnalysis.Value.Synonyms.Take(4).ToList();
    _logger.LogInformation("Extracted {Count} synonyms for word: {Word}",
        synonyms.Count, request.Word);
}

// 儲存同義詞到 Flashcard
flashcard.Synonyms = synonyms.Any()
    ? JsonSerializer.Serialize(synonyms)
    : null;

3.2 Alternative: 直接調用 AI 生成同義詞

如果上述方法提取失敗,備用方案:

// 備用:直接為單詞生成同義詞
if (string.IsNullOrEmpty(flashcard.Synonyms))
{
    var singleWordAnalysis = await _analysisService.AnalyzeSentenceAsync(
        request.Word, new AnalysisOptions());

    var wordSynonyms = singleWordAnalysis.VocabularyAnalysis
        .FirstOrDefault().Value?.Synonyms ?? new List<string>();

    flashcard.Synonyms = wordSynonyms.Any()
        ? JsonSerializer.Serialize(wordSynonyms)
        : null;
}

Phase 4: 前端創建表單支援 (預計 1 天)

4.1 詞卡創建表單擴展

檔案: frontend/app/generate/page.tsx 或詞卡創建相關組件

// 添加同義詞輸入區塊
const [synonyms, setSynonyms] = useState<string[]>([]);

// 同義詞管理功能
const addSynonym = (synonym: string) => {
    if (synonym.trim() && !synonyms.includes(synonym.trim())) {
        setSynonyms([...synonyms, synonym.trim()]);
    }
};

const removeSynonym = (index: number) => {
    setSynonyms(synonyms.filter((_, i) => i !== index));
};

4.2 同義詞輸入 UI 組件

<div className="space-y-2">
  <label className="block text-sm font-medium text-gray-700">
    同義詞 (可選)
  </label>

  {/* 已添加的同義詞 */}
  <div className="flex flex-wrap gap-2 mb-2">
    {synonyms.map((synonym, index) => (
      <span key={index} className="bg-purple-100 text-purple-700 px-2 py-1 rounded-full text-sm">
        {synonym}
        <button onClick={() => removeSynonym(index)} className="ml-1 text-purple-500">×</button>
      </span>
    ))}
  </div>

  {/* 添加新同義詞 */}
  <input
    type="text"
    placeholder="輸入同義詞並按 Enter"
    onKeyDown={(e) => {
      if (e.key === 'Enter') {
        addSynonym(e.currentTarget.value);
        e.currentTarget.value = '';
      }
    }}
    className="w-full px-3 py-2 border border-gray-300 rounded-md"
  />
</div>

Phase 5: 測試與優化 (預計 0.5 天)

5.1 功能驗證

  • 新創建的詞卡包含同義詞
  • 詞卡詳情頁正確顯示同義詞
  • FlipMemoryTest 組件顯示同義詞
  • 同義詞 CRUD 功能正常

5.2 資料遷移

  • 為現有詞卡補充同義詞資料
  • 批次生成常見詞彙的同義詞
  • 驗證資料完整性

實施順序

Week 1

  • Day 1: Phase 1 (資料模型) + Phase 2 (API 功能)
  • Day 2: Phase 3 (自動生成) + Phase 4 (前端表單)

Week 2

  • Day 1: Phase 5 (測試優化) + 資料遷移
  • Day 2: 部署和監控

技術考量

資料存儲策略

  • JSON 格式: 使用 JSON 字串存儲同義詞陣列
  • 欄位長度: MaxLength(1000) 支援多個同義詞
  • 索引考量: 暫不建立同義詞搜尋索引

效能優化

  • 快取常見同義詞: 避免重複 AI 調用
  • 批次處理: 新詞卡創建時批次生成同義詞
  • 前端優化: 使用 useMemo 處理同義詞資料

錯誤處理

  • AI 生成失敗: 使用常見同義詞對應表降級
  • 資料格式錯誤: JSON 反序列化異常處理
  • 前端容錯: 空同義詞時隱藏區塊

預期效果

用戶體驗

  • 完整的詞彙學習: 同義詞豐富詞彙理解
  • 自動化生成: 無需手動輸入同義詞
  • 彈性管理: 支援手動編輯和添加

系統優勢

  • 🚀 學習深度: 同義詞擴展詞彙掌握範圍
  • 🤖 智能輔助: AI 生成 + 人工精選
  • 📊 資料完整: 統一的同義詞資料管理

維護性

  • 📦 模組化設計: 同義詞服務獨立管理
  • 🔧 易於擴展: 支援多語言同義詞
  • 📈 可監控: 同義詞生成成功率追蹤