dramaling-vocab-learning/EXAMPLE_IMAGE_FRONTEND_BACK...

324 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 例句圖生成前後端完整整合計劃
## 📋 **項目概覽**
**目標**: 將已實現的例句圖生成後端 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<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分鐘)**
```csharp
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`
```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<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`
```typescript
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`
```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 (
<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分鐘)**
```typescript
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 分鐘)
- ✅ 可以並發生成多個詞卡的圖片
- ✅ 響應式設計在各裝置正常顯示
---
## 🎛️ **實施建議**
### **建議順序**
1. **先完成後端整合** - 確保資料正確返回
2. **再進行前端整合** - 逐步替換硬編碼邏輯
3. **最後優化體驗** - 完善 UI 和錯誤處理
### **風險控制**
- **漸進式替換**: 保留硬編碼映射作為 fallback
- **功能開關**: 可以暫時關閉圖片生成功能
- **測試優先**: 每個階段都要充分測試
---
**文檔版本**: v1.0
**建立日期**: 2025-09-24
**預估完成**: 2025-09-25
**負責團隊**: 全端開發團隊