8.7 KiB
8.7 KiB
例句圖生成前後端完整整合計劃
📋 項目概覽
目標: 將已實現的例句圖生成後端 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分鐘)
// 當前查詢
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分鐘)
public class FlashcardDto
{
// 現有欄位...
// 新增圖片相關欄位
public List<ExampleImageDto> 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分鐘)
private async Task<List<ExampleImageDto>> MapExampleImages(List<FlashcardExampleImage> flashcardImages)
{
var result = new List<ExampleImageDto>();
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
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<GenerationStatus> {
// 調用 GET /api/imagegeneration/requests/{requestId}/status
}
async pollUntilComplete(requestId: string, onProgress?: (status: GenerationStatus) => void): Promise<GenerationStatus> {
// 輪詢直到完成
}
}
2.2 創建 React Hook (1小時)
檔案: /frontend/hooks/useImageGeneration.ts
export const useImageGeneration = () => {
const [generationStates, setGenerationStates] = useState<Record<string, GenerationState>>({});
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
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
// 移除硬編碼映射
const getExampleImage = (card: Flashcard): string | null => {
return card.primaryImageUrl || null;
};
const hasExampleImage = (card: Flashcard): boolean => {
return card.hasExampleImage;
};
3.2 實現圖片生成功能 (1小時)
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分鐘)
const GenerationProgress = ({ flashcardId }: { flashcardId: string }) => {
const generationState = getGenerationState(flashcardId);
if (generationState.status === 'generating') {
return (
<div className="flex items-center gap-2 text-blue-600">
<Spinner className="w-4 h-4" />
<span className="text-xs">
{generationState.currentStage === 'description_generation' ? '生成描述中...' : '生成圖片中...'}
</span>
</div>
);
}
return null;
};
3.4 錯誤處理和重試 (30分鐘)
const RetryButton = ({ flashcardId, onRetry }: RetryButtonProps) => {
return (
<button
onClick={() => onRetry(flashcardId)}
className="text-xs text-red-600 hover:text-red-800"
>
重試生成
</button>
);
};
🧪 Phase 4: 測試與部署 (1 小時)
4.1 功能測試 (30分鐘)
- 完整的圖片生成流程測試
- 多詞卡並發生成測試
- 錯誤情境測試 (網路中斷、API 失敗等)
4.2 用戶體驗優化 (20分鐘)
- 載入動畫調整
- 成功/失敗訊息優化
- 響應式顯示調整
4.3 文檔更新 (10分鐘)
- 更新使用說明
- 記錄整合完成狀態
📊 成功指標
功能指標
- ✅ 點擊"新增例句圖"按鈕能啟動實際生成
- ✅ 能看到即時的生成進度 (描述生成 → 圖片生成)
- ✅ 生成完成後圖片立即顯示在詞卡中
- ✅ 錯誤處理優雅,用戶體驗流暢
技術指標
- ✅ 前端完全不依賴硬編碼圖片映射
- ✅ 所有圖片資訊從後端 API 動態載入
- ✅ 支援多張圖片的詞卡
- ✅ 完整的狀態管理和錯誤處理
用戶體驗指標
- ✅ 生成進度清楚可見 (預計 2-3 分鐘)
- ✅ 可以並發生成多個詞卡的圖片
- ✅ 響應式設計在各裝置正常顯示
🎛️ 實施建議
建議順序
- 先完成後端整合 - 確保資料正確返回
- 再進行前端整合 - 逐步替換硬編碼邏輯
- 最後優化體驗 - 完善 UI 和錯誤處理
風險控制
- 漸進式替換: 保留硬編碼映射作為 fallback
- 功能開關: 可以暫時關閉圖片生成功能
- 測試優先: 每個階段都要充分測試
文檔版本: v1.0 建立日期: 2025-09-24 預估完成: 2025-09-25 負責團隊: 全端開發團隊