# 例句圖生成前後端完整整合計劃 ## 📋 **項目概覽** **目標**: 將已實現的例句圖生成後端 API 完整整合到前端詞卡管理介面 **預估時間**: 6-9 小時 **複雜度**: 中等 (需要前後端協調) --- ## 🎯 **當前狀況評估** ### ✅ **已完成功能** - **後端 API**: 完整的兩階段圖片生成系統 (Gemini + Replicate) - **圖片壓縮**: 自動壓縮 1024x1024 → 512x512 - **資料庫設計**: 完整的圖片關聯表格和追蹤系統 - **API 測試**: 至少 1 次成功生成驗證 - **Git 安全**: wwwroot 已被忽略,API Keys 安全存儲 ### ❌ **缺失功能** - **後端資料整合**: FlashcardsController 未返回圖片資訊 - **前端 API 整合**: 所有圖片生成功能都未實現 - **前端狀態管理**: 沒有生成進度追蹤 - **用戶體驗**: 仍使用硬編碼圖片映射 --- ## 🚀 **Phase 1: 後端資料整合 (1-2 小時)** ### 🎯 **目標**: 讓 flashcards API 返回圖片資訊 #### **1.1 修改 FlashcardsController (30分鐘)** ```csharp // 當前查詢 var flashcards = await _context.Flashcards .Where(f => f.UserId == userId) .ToListAsync(); // 改為包含圖片關聯 var flashcards = await _context.Flashcards .Include(f => f.FlashcardExampleImages) .ThenInclude(fei => fei.ExampleImage) .Where(f => f.UserId == userId) .ToListAsync(); ``` #### **1.2 擴展 FlashcardDto (30分鐘)** ```csharp public class FlashcardDto { // 現有欄位... // 新增圖片相關欄位 public List ExampleImages { get; set; } = new(); public bool HasExampleImage => ExampleImages.Any(); public string? PrimaryImageUrl => ExampleImages.FirstOrDefault(img => img.IsPrimary)?.ImageUrl; } public class ExampleImageDto { public string Id { get; set; } public string ImageUrl { get; set; } public bool IsPrimary { get; set; } public decimal? QualityScore { get; set; } } ``` #### **1.3 添加圖片 URL 生成邏輯 (30分鐘)** ```csharp private async Task> MapExampleImages(List flashcardImages) { var result = new List(); foreach (var item in flashcardImages) { var imageUrl = await _imageStorageService.GetImageUrlAsync(item.ExampleImage.RelativePath); result.Add(new ExampleImageDto { Id = item.ExampleImage.Id.ToString(), ImageUrl = imageUrl, IsPrimary = item.IsPrimary, QualityScore = item.ExampleImage.QualityScore }); } return result; } ``` #### **1.4 測試後端更新 (30分鐘)** - 驗證 API 回應包含圖片資訊 - 確認圖片 URL 正確生成 - 測試有圖片和無圖片的詞卡 --- ## 🎨 **Phase 2: 前端 API 服務整合 (2-3 小時)** ### 🎯 **目標**: 創建完整的前端圖片生成服務 #### **2.1 創建圖片生成 API 服務 (1小時)** **檔案**: `/frontend/lib/services/imageGeneration.ts` ```typescript export interface ImageGenerationRequest { style: 'cartoon' | 'realistic' | 'minimal'; priority: 'normal' | 'high' | 'low'; replicateModel: string; options: { useGeminiCache: boolean; useImageCache: boolean; maxRetries: number; learnerLevel: string; scenario: string; }; } export interface GenerationStatus { requestId: string; overallStatus: string; stages: { gemini: StageStatus; replicate: StageStatus; }; result?: { imageUrl: string; imageId: string; }; } export class ImageGenerationService { async generateImage(flashcardId: string, request: ImageGenerationRequest): Promise<{requestId: string}> { // 調用 POST /api/imagegeneration/flashcards/{flashcardId}/generate } async getGenerationStatus(requestId: string): Promise { // 調用 GET /api/imagegeneration/requests/{requestId}/status } async pollUntilComplete(requestId: string, onProgress?: (status: GenerationStatus) => void): Promise { // 輪詢直到完成 } } ``` #### **2.2 創建 React Hook (1小時)** **檔案**: `/frontend/hooks/useImageGeneration.ts` ```typescript export const useImageGeneration = () => { const [generationStates, setGenerationStates] = useState>({}); const generateImage = async (flashcardId: string) => { // 啟動生成流程 // 更新狀態為 generating // 開始輪詢進度 }; const getGenerationState = (flashcardId: string) => { return generationStates[flashcardId] || { status: 'idle' }; }; return { generateImage, getGenerationState }; }; ``` #### **2.3 更新 flashcards 服務 (30分鐘)** **檔案**: `/frontend/lib/services/flashcards.ts` ```typescript export interface Flashcard { // 現有欄位... // 新增圖片欄位 exampleImages: ExampleImage[]; hasExampleImage: boolean; primaryImageUrl?: string; } export interface ExampleImage { id: string; imageUrl: string; isPrimary: boolean; qualityScore?: number; } ``` --- ## 🎮 **Phase 3: 前端 UI 整合 (2-3 小時)** ### 🎯 **目標**: 完整的用戶介面功能 #### **3.1 修改圖片顯示邏輯 (1小時)** **檔案**: `/frontend/app/flashcards/page.tsx` ```typescript // 移除硬編碼映射 const getExampleImage = (card: Flashcard): string | null => { return card.primaryImageUrl || null; }; const hasExampleImage = (card: Flashcard): boolean => { return card.hasExampleImage; }; ``` #### **3.2 實現圖片生成功能 (1小時)** ```typescript const { generateImage, getGenerationState } = useImageGeneration(); const handleGenerateExampleImage = async (card: Flashcard) => { try { setGeneratingCards(prev => new Set([...prev, card.id])); await generateImage(card.id); // 生成完成後刷新詞卡列表 await searchActions.refresh(); toast.success(`「${card.word}」的例句圖片生成完成!`); } catch (error) { toast.error(`圖片生成失敗: ${error.message}`); } finally { setGeneratingCards(prev => { const newSet = new Set(prev); newSet.delete(card.id); return newSet; }); } }; ``` #### **3.3 添加生成進度 UI (30分鐘)** ```typescript const GenerationProgress = ({ flashcardId }: { flashcardId: string }) => { const generationState = getGenerationState(flashcardId); if (generationState.status === 'generating') { return (
{generationState.currentStage === 'description_generation' ? '生成描述中...' : '生成圖片中...'}
); } return null; }; ``` #### **3.4 錯誤處理和重試 (30分鐘)** ```typescript const RetryButton = ({ flashcardId, onRetry }: RetryButtonProps) => { return ( ); }; ``` --- ## 🧪 **Phase 4: 測試與部署 (1 小時)** ### **4.1 功能測試 (30分鐘)** - 完整的圖片生成流程測試 - 多詞卡並發生成測試 - 錯誤情境測試 (網路中斷、API 失敗等) ### **4.2 用戶體驗優化 (20分鐘)** - 載入動畫調整 - 成功/失敗訊息優化 - 響應式顯示調整 ### **4.3 文檔更新 (10分鐘)** - 更新使用說明 - 記錄整合完成狀態 --- ## 📊 **成功指標** ### **功能指標** - ✅ 點擊"新增例句圖"按鈕能啟動實際生成 - ✅ 能看到即時的生成進度 (描述生成 → 圖片生成) - ✅ 生成完成後圖片立即顯示在詞卡中 - ✅ 錯誤處理優雅,用戶體驗流暢 ### **技術指標** - ✅ 前端完全不依賴硬編碼圖片映射 - ✅ 所有圖片資訊從後端 API 動態載入 - ✅ 支援多張圖片的詞卡 - ✅ 完整的狀態管理和錯誤處理 ### **用戶體驗指標** - ✅ 生成進度清楚可見 (預計 2-3 分鐘) - ✅ 可以並發生成多個詞卡的圖片 - ✅ 響應式設計在各裝置正常顯示 --- ## 🎛️ **實施建議** ### **建議順序** 1. **先完成後端整合** - 確保資料正確返回 2. **再進行前端整合** - 逐步替換硬編碼邏輯 3. **最後優化體驗** - 完善 UI 和錯誤處理 ### **風險控制** - **漸進式替換**: 保留硬編碼映射作為 fallback - **功能開關**: 可以暫時關閉圖片生成功能 - **測試優先**: 每個階段都要充分測試 --- **文檔版本**: v1.0 **建立日期**: 2025-09-24 **預估完成**: 2025-09-25 **負責團隊**: 全端開發團隊