1027 lines
27 KiB
Markdown
1027 lines
27 KiB
Markdown
# 詞彙生成系統待完成功能規格
|
||
|
||
## 文件概述
|
||
|
||
本規格文件詳細說明DramaLing詞彙學習系統中詞彙生成功能的**未完成項目**和**改進需求**,為後續開發提供明確的技術指導和優先級規劃。
|
||
|
||
---
|
||
|
||
## 1. 未完成核心功能
|
||
### 1.2 詞卡儲存整合機制
|
||
|
||
#### 功能描述
|
||
- **問題**: 生成的詞卡只能預覽,無法保存到資料庫
|
||
- **目標**: 實現批量詞卡保存、重複檢測、用戶回饋
|
||
|
||
#### 技術規格
|
||
|
||
##### 1.2.1 批量保存API
|
||
```http
|
||
POST /api/ai/save-generated-cards
|
||
Authorization: Bearer {token}
|
||
|
||
Request:
|
||
{
|
||
"cardSetId": "guid?", // 可選,未指定則使用預設卡組
|
||
"selectedCards": [
|
||
{
|
||
"word": "string",
|
||
"translation": "string",
|
||
"definition": "string",
|
||
"partOfSpeech": "string?",
|
||
"pronunciation": "string?",
|
||
"example": "string?",
|
||
"exampleTranslation": "string?",
|
||
"difficultyLevel": "string?",
|
||
"isSelected": boolean // 用戶是否選擇保存
|
||
}
|
||
],
|
||
"handleDuplicates": "merge" | "skip" | "replace"
|
||
}
|
||
|
||
Response:
|
||
{
|
||
"success": boolean,
|
||
"data": {
|
||
"savedCount": number,
|
||
"skippedCount": number,
|
||
"duplicateCount": number,
|
||
"savedCards": ["guid"],
|
||
"duplicateCards": [
|
||
{
|
||
"word": "string",
|
||
"existingCardId": "guid",
|
||
"action": "merged" | "skipped"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
##### 1.2.2 重複詞卡檢測邏輯
|
||
```csharp
|
||
public class DuplicateDetectionService
|
||
{
|
||
// 檢測策略
|
||
public enum DuplicateStrategy
|
||
{
|
||
ExactMatch, // 完全相同的word
|
||
SimilarMatch, // 相似度80%以上
|
||
DefinitionMatch // 定義相似
|
||
}
|
||
|
||
public async Task<List<DuplicateCard>> DetectDuplicatesAsync(
|
||
Guid userId,
|
||
List<GeneratedCard> newCards)
|
||
{
|
||
// 1. 查詢用戶現有詞卡
|
||
// 2. 逐一比對相似度
|
||
// 3. 返回重複檢測結果
|
||
}
|
||
}
|
||
```
|
||
|
||
##### 1.2.3 前端保存流程改進
|
||
```typescript
|
||
// 替換現有的保存按鈕邏輯
|
||
const handleSaveCards = async () => {
|
||
// 1. 彈出詞卡選擇對話框
|
||
// 2. 處理重複詞卡確認
|
||
// 3. 顯示保存進度
|
||
// 4. 成功後跳轉到詞卡列表
|
||
// 5. 失敗後提供重試選項
|
||
}
|
||
|
||
// 新增組件: CardSelectionDialog
|
||
// 新增組件: DuplicateHandlingDialog
|
||
// 新增組件: SaveProgressIndicator
|
||
```
|
||
|
||
#### 開發估時
|
||
- **後端API實現**: 5-7個工作天
|
||
- **重複檢測邏輯**: 3-5個工作天
|
||
- **前端流程改進**: 5-7個工作天
|
||
- **總計**: 13-19個工作天
|
||
|
||
---
|
||
|
||
### 1.3 智能萃取模式實現
|
||
|
||
#### 功能描述
|
||
- **當前問題**: 前端有選項但被註解掉,後端缺乏差異化邏輯
|
||
- **目標**: 實現片語、俚語、上下文相關詞彙的智能識別
|
||
|
||
#### 技術規格
|
||
|
||
##### 1.3.1 智能萃取算法
|
||
```csharp
|
||
public class SmartExtractionService
|
||
{
|
||
// 片語識別
|
||
public async Task<List<Phrase>> ExtractPhrasesAsync(string text)
|
||
{
|
||
// 1. 使用NLP模型識別片語
|
||
// 2. 檢查片語字典
|
||
// 3. 評估片語重要性
|
||
}
|
||
|
||
// 俚語識別
|
||
public async Task<List<Slang>> ExtractSlangAsync(string text)
|
||
{
|
||
// 1. 俚語詞典比對
|
||
// 2. 上下文相關性分析
|
||
// 3. 地域性俚語識別
|
||
}
|
||
|
||
// 上下文詞彙萃取
|
||
public async Task<List<ContextualWord>> ExtractContextualWordsAsync(
|
||
string text, string userLevel)
|
||
{
|
||
// 1. 基於用戶程度的詞彙篩選
|
||
// 2. 語義重要性分析
|
||
// 3. 學習價值評估
|
||
}
|
||
}
|
||
```
|
||
|
||
##### 1.3.2 萃取模式差異化
|
||
```json
|
||
// vocabulary模式回應
|
||
{
|
||
"extractionType": "vocabulary",
|
||
"focus": "individual_words",
|
||
"cards": [
|
||
{
|
||
"type": "word",
|
||
"word": "elaborate",
|
||
"cefrLevel": "C1",
|
||
"frequency": "medium"
|
||
}
|
||
]
|
||
}
|
||
|
||
// smart模式回應
|
||
{
|
||
"extractionType": "smart",
|
||
"focus": "phrases_and_context",
|
||
"cards": [
|
||
{
|
||
"type": "phrase",
|
||
"phrase": "break the ice",
|
||
"meaning": "開始對話",
|
||
"category": "idiom"
|
||
},
|
||
{
|
||
"type": "contextual",
|
||
"word": "literally",
|
||
"contextMeaning": "在此語境下表示強調",
|
||
"commonMisuse": true
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
##### 1.3.3 前端模式切換
|
||
```typescript
|
||
// 重新啟用萃取模式選擇
|
||
const [extractionType, setExtractionType] = useState<'vocabulary' | 'smart'>('vocabulary');
|
||
|
||
// 根據模式調整UI提示
|
||
const getModeDescription = (mode: string) => {
|
||
return {
|
||
vocabulary: "基於字典的標準詞彙萃取,適合系統性學習",
|
||
smart: "AI智能分析片語、俚語和語境詞彙,適合實用性學習"
|
||
}[mode];
|
||
}
|
||
```
|
||
|
||
#### 開發估時
|
||
- **智能萃取算法**: 7-10個工作天
|
||
- **後端模式差異化**: 3-5個工作天
|
||
- **前端模式切換**: 2-3個工作天
|
||
- **總計**: 12-18個工作天
|
||
|
||
---
|
||
|
||
## 2. 功能改進規格
|
||
|
||
### 2.1 詞卡品質驗證系統
|
||
|
||
#### 功能描述
|
||
- **問題**: AI生成的詞卡缺乏品質控制機制
|
||
- **目標**: 建立多層次的品質檢查和用戶回饋系統
|
||
|
||
#### 技術規格
|
||
|
||
##### 2.1.1 AI生成品質評估
|
||
```csharp
|
||
public class CardQualityValidator
|
||
{
|
||
public async Task<QualityScore> ValidateCardAsync(GeneratedCard card)
|
||
{
|
||
var score = new QualityScore();
|
||
|
||
// 1. 翻譯準確性檢查
|
||
score.TranslationAccuracy = await ValidateTranslationAsync(card.Word, card.Translation);
|
||
|
||
// 2. 定義完整性檢查
|
||
score.DefinitionCompleteness = ValidateDefinition(card.Definition);
|
||
|
||
// 3. 例句相關性檢查
|
||
score.ExampleRelevance = await ValidateExampleAsync(card.Word, card.Example);
|
||
|
||
// 4. CEFR等級準確性
|
||
score.CEFRAccuracy = await ValidateCEFRLevelAsync(card.Word, card.DifficultyLevel);
|
||
|
||
return score;
|
||
}
|
||
}
|
||
|
||
public class QualityScore
|
||
{
|
||
public float TranslationAccuracy { get; set; } // 0-1
|
||
public float DefinitionCompleteness { get; set; } // 0-1
|
||
public float ExampleRelevance { get; set; } // 0-1
|
||
public float CEFRAccuracy { get; set; } // 0-1
|
||
public float OverallScore => (TranslationAccuracy + DefinitionCompleteness + ExampleRelevance + CEFRAccuracy) / 4;
|
||
}
|
||
```
|
||
|
||
##### 2.1.2 用戶回饋整合
|
||
```http
|
||
POST /api/flashcards/{id}/quality-feedback
|
||
Authorization: Bearer {token}
|
||
|
||
Request:
|
||
{
|
||
"feedbackType": "translation_error" | "definition_unclear" | "example_irrelevant" | "difficulty_wrong",
|
||
"description": "string",
|
||
"suggestedCorrection": "string?",
|
||
"severity": "low" | "medium" | "high"
|
||
}
|
||
```
|
||
|
||
##### 2.1.3 自動品質改進
|
||
```csharp
|
||
public class CardQualityImprover
|
||
{
|
||
// 基於用戶回饋自動修正
|
||
public async Task<GeneratedCard> ImproveCardAsync(GeneratedCard card, List<QualityFeedback> feedbacks)
|
||
{
|
||
// 1. 分析回饋模式
|
||
// 2. 重新調用AI進行修正
|
||
// 3. 驗證修正結果
|
||
return improvedCard;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 開發估時
|
||
- **品質驗證算法**: 5-7個工作天
|
||
- **回饋系統**: 3-5個工作天
|
||
- **自動改進機制**: 5-7個工作天
|
||
- **總計**: 13-19個工作天
|
||
|
||
---
|
||
|
||
### 2.2 進度追蹤和狀態管理
|
||
|
||
#### 功能描述
|
||
- **問題**: 詞卡生成過程缺乏進度回饋,用戶體驗差
|
||
- **目標**: 實現實時進度追蹤、失敗重試、生成歷史
|
||
|
||
#### 技術規格
|
||
|
||
##### 2.2.1 任務狀態管理
|
||
```csharp
|
||
public enum GenerationStatus
|
||
{
|
||
Queued, // 排隊中
|
||
Processing, // 處理中
|
||
Analyzing, // AI分析中
|
||
Generating, // 生成詞卡中
|
||
Validating, // 品質驗證中
|
||
Completed, // 完成
|
||
Failed, // 失敗
|
||
Cancelled // 取消
|
||
}
|
||
|
||
public class GenerationTask
|
||
{
|
||
public Guid Id { get; set; }
|
||
public Guid UserId { get; set; }
|
||
public string InputText { get; set; }
|
||
public GenerationStatus Status { get; set; }
|
||
public int Progress { get; set; } // 0-100
|
||
public string? ErrorMessage { get; set; }
|
||
public DateTime CreatedAt { get; set; }
|
||
public DateTime? CompletedAt { get; set; }
|
||
public List<GeneratedCard> Results { get; set; } = new();
|
||
}
|
||
```
|
||
|
||
##### 2.2.2 WebSocket實時更新
|
||
```typescript
|
||
// 前端WebSocket連接
|
||
class GenerationProgressTracker {
|
||
private ws: WebSocket;
|
||
|
||
trackGeneration(taskId: string) {
|
||
this.ws = new WebSocket(`ws://localhost:5000/ws/generation/${taskId}`);
|
||
|
||
this.ws.onmessage = (event) => {
|
||
const update = JSON.parse(event.data);
|
||
this.handleProgressUpdate(update);
|
||
};
|
||
}
|
||
|
||
private handleProgressUpdate(update: GenerationProgressUpdate) {
|
||
// 更新進度條
|
||
// 顯示當前步驟
|
||
// 處理錯誤狀態
|
||
}
|
||
}
|
||
```
|
||
|
||
##### 2.2.3 失敗重試機制
|
||
```http
|
||
POST /api/ai/retry-generation/{taskId}
|
||
Authorization: Bearer {token}
|
||
|
||
Request:
|
||
{
|
||
"retryCount": number,
|
||
"adjustments": {
|
||
"extractionType": "string?",
|
||
"cardCount": number?,
|
||
"userLevel": "string?"
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 開發估時
|
||
- **任務狀態管理**: 3-5個工作天
|
||
- **WebSocket實現**: 3-5個工作天
|
||
- **重試機制**: 2-3個工作天
|
||
- **前端進度UI**: 3-5個工作天
|
||
- **總計**: 11-18個工作天
|
||
|
||
---
|
||
|
||
### 2.3 個人化推薦算法
|
||
|
||
#### 功能描述
|
||
- **當前限制**: 只基於用戶英語程度,缺乏深度個人化
|
||
- **目標**: 基於學習歷史、困難詞彙、目標導向的智能推薦
|
||
|
||
#### 技術規格
|
||
|
||
##### 2.3.1 學習歷史分析
|
||
```csharp
|
||
public class LearningHistoryAnalyzer
|
||
{
|
||
public async Task<LearningProfile> AnalyzeUserLearningAsync(Guid userId)
|
||
{
|
||
// 1. 分析學習過的詞彙類型
|
||
var studiedWords = await GetStudiedWordsAsync(userId);
|
||
|
||
// 2. 識別困難詞彙模式
|
||
var difficultyPatterns = AnalyzeDifficultyPatterns(studiedWords);
|
||
|
||
// 3. 計算學習偏好
|
||
var preferences = CalculatePreferences(studiedWords);
|
||
|
||
return new LearningProfile
|
||
{
|
||
WeakAreas = difficultyPatterns.WeakAreas,
|
||
PreferredPartOfSpeech = preferences.PartOfSpeech,
|
||
OptimalDifficultyRange = preferences.DifficultyRange,
|
||
LearningVelocity = preferences.Velocity
|
||
};
|
||
}
|
||
}
|
||
```
|
||
|
||
##### 2.3.2 智能詞彙篩選
|
||
```csharp
|
||
public class PersonalizedVocabSelector
|
||
{
|
||
public async Task<List<GeneratedCard>> SelectOptimalCardsAsync(
|
||
List<GeneratedCard> candidates,
|
||
LearningProfile profile)
|
||
{
|
||
var scoredCards = candidates.Select(card => new
|
||
{
|
||
Card = card,
|
||
Score = CalculateRelevanceScore(card, profile)
|
||
});
|
||
|
||
return scoredCards
|
||
.OrderByDescending(x => x.Score)
|
||
.Take(profile.OptimalCardCount)
|
||
.Select(x => x.Card)
|
||
.ToList();
|
||
}
|
||
|
||
private float CalculateRelevanceScore(GeneratedCard card, LearningProfile profile)
|
||
{
|
||
float score = 0;
|
||
|
||
// 1. 困難度適配性 (30%)
|
||
score += CalculateDifficultyScore(card.DifficultyLevel, profile.OptimalDifficultyRange) * 0.3f;
|
||
|
||
// 2. 詞性偏好 (20%)
|
||
score += CalculatePartOfSpeechScore(card.PartOfSpeech, profile.PreferredPartOfSpeech) * 0.2f;
|
||
|
||
// 3. 弱項改進 (25%)
|
||
score += CalculateWeakAreaScore(card, profile.WeakAreas) * 0.25f;
|
||
|
||
// 4. 新穎性 (25%)
|
||
score += CalculateNoveltyScore(card, profile.StudiedWords) * 0.25f;
|
||
|
||
return score;
|
||
}
|
||
}
|
||
```
|
||
|
||
##### 2.3.3 目標導向推薦
|
||
```csharp
|
||
public enum LearningGoal
|
||
{
|
||
ExamPreparation, // 考試準備
|
||
BusinessEnglish, // 商務英語
|
||
DailyConversation,// 日常對話
|
||
AcademicWriting, // 學術寫作
|
||
MediaConsumption // 影劇理解
|
||
}
|
||
|
||
public class GoalOrientedRecommender
|
||
{
|
||
public async Task<List<GeneratedCard>> RecommendByGoalAsync(
|
||
string inputText,
|
||
LearningGoal goal,
|
||
string userLevel)
|
||
{
|
||
var extractor = GetGoalSpecificExtractor(goal);
|
||
return await extractor.ExtractRelevantVocabAsync(inputText, userLevel);
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 開發估時
|
||
- **學習歷史分析**: 5-7個工作天
|
||
- **個人化算法**: 7-10個工作天
|
||
- **目標導向推薦**: 5-7個工作天
|
||
- **前端個人化設定**: 3-5個工作天
|
||
- **總計**: 20-29個工作天
|
||
|
||
---
|
||
|
||
## 3. API整合規格
|
||
|
||
### 3.1 詞彙查詢API完整實現
|
||
|
||
#### 功能描述
|
||
- **問題**: `POST /api/ai/query-word`端點存在但前端未使用
|
||
- **目標**: 完整實現點擊詞彙查詢功能
|
||
|
||
#### 技術規格
|
||
|
||
##### 3.1.1 前端點擊事件整合
|
||
```typescript
|
||
// 修改 ClickableTextV2 組件
|
||
const handleWordClick = async (word: string, context: string) => {
|
||
setIsLoading(true);
|
||
|
||
try {
|
||
const response = await fetch('/api/ai/query-word', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'Authorization': `Bearer ${token}`
|
||
},
|
||
body: JSON.stringify({
|
||
word,
|
||
sentence: context,
|
||
analysisId: currentAnalysisId
|
||
})
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
showWordDetailsModal(result.data);
|
||
trackWordQuery(word, result.data.isHighValue);
|
||
}
|
||
} catch (error) {
|
||
handleQueryError(error);
|
||
} finally {
|
||
setIsLoading(false);
|
||
}
|
||
};
|
||
```
|
||
|
||
##### 3.1.2 詞彙詳情展示組件
|
||
```typescript
|
||
interface WordDetailsModalProps {
|
||
word: string;
|
||
analysis: WordAnalysisResult;
|
||
onClose: () => void;
|
||
onAddToCards: (word: WordAnalysisResult) => void;
|
||
}
|
||
|
||
const WordDetailsModal: React.FC<WordDetailsModalProps> = ({
|
||
word, analysis, onClose, onAddToCards
|
||
}) => {
|
||
return (
|
||
<Modal isOpen onClose={onClose}>
|
||
<div className="space-y-4">
|
||
{/* 詞彙基本資訊 */}
|
||
<div className="border-b pb-4">
|
||
<h2 className="text-2xl font-bold">{analysis.word}</h2>
|
||
<p className="text-gray-600">{analysis.pronunciation}</p>
|
||
<span className="inline-block px-2 py-1 bg-blue-100 text-blue-800 text-sm rounded">
|
||
{analysis.partOfSpeech}
|
||
</span>
|
||
</div>
|
||
|
||
{/* 翻譯和定義 */}
|
||
<div className="space-y-2">
|
||
<div>
|
||
<h3 className="font-semibold">翻譯</h3>
|
||
<p>{analysis.translation}</p>
|
||
</div>
|
||
<div>
|
||
<h3 className="font-semibold">定義</h3>
|
||
<p>{analysis.definition}</p>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 操作按鈕 */}
|
||
<div className="flex gap-2 pt-4 border-t">
|
||
<button
|
||
onClick={() => onAddToCards(analysis)}
|
||
className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700"
|
||
>
|
||
加入詞卡
|
||
</button>
|
||
<button
|
||
onClick={onClose}
|
||
className="bg-gray-200 text-gray-700 px-4 py-2 rounded hover:bg-gray-300"
|
||
>
|
||
關閉
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</Modal>
|
||
);
|
||
};
|
||
```
|
||
|
||
##### 3.1.3 使用量計費前端實現
|
||
```typescript
|
||
const trackWordQuery = async (word: string, isHighValue: boolean) => {
|
||
// 更新本地使用量計數
|
||
const currentUsage = getLocalUsageCount();
|
||
|
||
if (!isHighValue && !isPremiumUser) {
|
||
if (currentUsage >= FREE_TIER_LIMIT) {
|
||
showUpgradePrompt();
|
||
return false;
|
||
}
|
||
|
||
updateLocalUsageCount(currentUsage + 1);
|
||
}
|
||
|
||
// 發送使用量統計到後端
|
||
await fetch('/api/usage/track-word-query', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'Authorization': `Bearer ${token}`
|
||
},
|
||
body: JSON.stringify({
|
||
word,
|
||
isHighValue,
|
||
timestamp: new Date().toISOString()
|
||
})
|
||
});
|
||
|
||
return true;
|
||
};
|
||
```
|
||
|
||
#### 開發估時
|
||
- **前端整合**: 3-5個工作天
|
||
- **詞彙詳情組件**: 2-3個工作天
|
||
- **使用量計費**: 2-3個工作天
|
||
- **總計**: 7-11個工作天
|
||
|
||
---
|
||
|
||
### 3.2 批量詞卡操作功能
|
||
|
||
#### 功能描述
|
||
- **目標**: 支援批量編輯、選擇性保存、詞卡預覽編輯
|
||
|
||
#### 技術規格
|
||
|
||
##### 3.2.1 批量編輯API
|
||
```http
|
||
PUT /api/ai/batch-edit-cards
|
||
Authorization: Bearer {token}
|
||
|
||
Request:
|
||
{
|
||
"operations": [
|
||
{
|
||
"cardIndex": number,
|
||
"field": "translation" | "definition" | "example" | "partOfSpeech",
|
||
"newValue": "string"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
##### 3.2.2 選擇性保存介面
|
||
```typescript
|
||
interface CardSelectionState {
|
||
[cardIndex: number]: {
|
||
isSelected: boolean;
|
||
hasChanges: boolean;
|
||
originalCard: GeneratedCard;
|
||
editedCard: GeneratedCard;
|
||
};
|
||
}
|
||
|
||
const BatchCardEditor: React.FC = () => {
|
||
const [selectionState, setSelectionState] = useState<CardSelectionState>({});
|
||
const [editMode, setEditMode] = useState<'view' | 'edit'>('view');
|
||
|
||
const handleSelectAll = () => {
|
||
// 全選/取消全選邏輯
|
||
};
|
||
|
||
const handleBatchSave = async () => {
|
||
const selectedCards = Object.entries(selectionState)
|
||
.filter(([_, state]) => state.isSelected)
|
||
.map(([index, state]) => state.editedCard);
|
||
|
||
await saveSelectedCards(selectedCards);
|
||
};
|
||
|
||
return (
|
||
<div className="space-y-4">
|
||
{/* 批量操作工具列 */}
|
||
<div className="flex justify-between items-center">
|
||
<div className="flex gap-2">
|
||
<button onClick={handleSelectAll}>全選</button>
|
||
<button onClick={() => setEditMode('edit')}>編輯模式</button>
|
||
</div>
|
||
<div className="flex gap-2">
|
||
<span>已選擇: {getSelectedCount()}</span>
|
||
<button
|
||
onClick={handleBatchSave}
|
||
disabled={getSelectedCount() === 0}
|
||
className="bg-blue-600 text-white px-4 py-2 rounded disabled:opacity-50"
|
||
>
|
||
保存選中詞卡
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 詞卡列表 */}
|
||
<div className="grid gap-4">
|
||
{generatedCards.map((card, index) => (
|
||
<EditableCardPreview
|
||
key={index}
|
||
card={card}
|
||
isSelected={selectionState[index]?.isSelected || false}
|
||
isEditable={editMode === 'edit'}
|
||
onSelectionChange={(selected) => handleSelectionChange(index, selected)}
|
||
onCardEdit={(editedCard) => handleCardEdit(index, editedCard)}
|
||
/>
|
||
))}
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
```
|
||
|
||
##### 3.2.3 內聯編輯組件
|
||
```typescript
|
||
const EditableCardPreview: React.FC<{
|
||
card: GeneratedCard;
|
||
isSelected: boolean;
|
||
isEditable: boolean;
|
||
onSelectionChange: (selected: boolean) => void;
|
||
onCardEdit: (card: GeneratedCard) => void;
|
||
}> = ({ card, isSelected, isEditable, onSelectionChange, onCardEdit }) => {
|
||
const [editingField, setEditingField] = useState<string | null>(null);
|
||
const [localCard, setLocalCard] = useState(card);
|
||
|
||
const handleFieldEdit = (field: string, value: string) => {
|
||
const updatedCard = { ...localCard, [field]: value };
|
||
setLocalCard(updatedCard);
|
||
onCardEdit(updatedCard);
|
||
};
|
||
|
||
return (
|
||
<div className={`border rounded-lg p-4 ${isSelected ? 'border-blue-500 bg-blue-50' : 'border-gray-200'}`}>
|
||
{/* 選擇框 */}
|
||
<div className="flex items-start gap-3">
|
||
<input
|
||
type="checkbox"
|
||
checked={isSelected}
|
||
onChange={(e) => onSelectionChange(e.target.checked)}
|
||
className="mt-1"
|
||
/>
|
||
|
||
<div className="flex-1 space-y-2">
|
||
{/* 可編輯欄位 */}
|
||
<EditableField
|
||
label="單字"
|
||
value={localCard.word}
|
||
isEditable={isEditable}
|
||
onChange={(value) => handleFieldEdit('word', value)}
|
||
/>
|
||
<EditableField
|
||
label="翻譯"
|
||
value={localCard.translation}
|
||
isEditable={isEditable}
|
||
onChange={(value) => handleFieldEdit('translation', value)}
|
||
/>
|
||
{/* 其他欄位... */}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
```
|
||
|
||
#### 開發估時
|
||
- **批量編輯API**: 2-3個工作天
|
||
- **選擇性保存介面**: 5-7個工作天
|
||
- **內聯編輯組件**: 3-5個工作天
|
||
- **總計**: 10-15個工作天
|
||
|
||
---
|
||
|
||
## 4. 技術實現細節
|
||
|
||
### 4.1 資料庫Schema變更需求
|
||
|
||
#### 4.1.1 新增表格
|
||
```sql
|
||
-- 生成任務追蹤表
|
||
CREATE TABLE generation_tasks (
|
||
id UUID PRIMARY KEY,
|
||
user_id UUID NOT NULL REFERENCES user_profiles(id),
|
||
input_text TEXT NOT NULL,
|
||
extraction_type VARCHAR(20) NOT NULL,
|
||
status VARCHAR(20) NOT NULL,
|
||
progress INTEGER DEFAULT 0,
|
||
error_message TEXT,
|
||
created_at TIMESTAMP DEFAULT NOW(),
|
||
completed_at TIMESTAMP,
|
||
FOREIGN KEY (user_id) REFERENCES user_profiles(id) ON DELETE CASCADE
|
||
);
|
||
|
||
-- 品質回饋表
|
||
CREATE TABLE card_quality_feedback (
|
||
id UUID PRIMARY KEY,
|
||
flashcard_id UUID NOT NULL,
|
||
user_id UUID NOT NULL,
|
||
feedback_type VARCHAR(50) NOT NULL,
|
||
description TEXT,
|
||
suggested_correction TEXT,
|
||
severity VARCHAR(10) NOT NULL,
|
||
created_at TIMESTAMP DEFAULT NOW(),
|
||
FOREIGN KEY (flashcard_id) REFERENCES flashcards(id) ON DELETE CASCADE,
|
||
FOREIGN KEY (user_id) REFERENCES user_profiles(id) ON DELETE CASCADE
|
||
);
|
||
|
||
-- 學習目標表
|
||
CREATE TABLE user_learning_goals (
|
||
id UUID PRIMARY KEY,
|
||
user_id UUID NOT NULL,
|
||
goal_type VARCHAR(50) NOT NULL,
|
||
target_level VARCHAR(10),
|
||
priority INTEGER DEFAULT 1,
|
||
is_active BOOLEAN DEFAULT true,
|
||
created_at TIMESTAMP DEFAULT NOW(),
|
||
FOREIGN KEY (user_id) REFERENCES user_profiles(id) ON DELETE CASCADE
|
||
);
|
||
```
|
||
|
||
#### 4.1.2 現有表格擴展
|
||
```sql
|
||
-- 擴展詞卡表,增加品質分數
|
||
ALTER TABLE flashcards ADD COLUMN quality_score FLOAT DEFAULT 0.8;
|
||
ALTER TABLE flashcards ADD COLUMN generation_source VARCHAR(50) DEFAULT 'manual';
|
||
ALTER TABLE flashcards ADD COLUMN ai_confidence FLOAT DEFAULT 1.0;
|
||
|
||
-- 擴展用戶表,增加學習偏好
|
||
ALTER TABLE user_profiles ADD COLUMN learning_preferences JSON;
|
||
ALTER TABLE user_profiles ADD COLUMN optimal_difficulty_range VARCHAR(20) DEFAULT 'auto';
|
||
|
||
-- 擴展使用統計表
|
||
ALTER TABLE WordQueryUsageStats ADD COLUMN context_queries INTEGER DEFAULT 0;
|
||
ALTER TABLE WordQueryUsageStats ADD COLUMN failed_queries INTEGER DEFAULT 0;
|
||
```
|
||
|
||
### 4.2 效能優化需求
|
||
|
||
#### 4.2.1 AI API調用優化
|
||
```csharp
|
||
public class AIApiOptimizer
|
||
{
|
||
// 並行處理多個詞卡生成
|
||
public async Task<List<GeneratedCard>> GenerateCardsParallelAsync(
|
||
string inputText,
|
||
int cardCount)
|
||
{
|
||
var chunks = SplitTextIntoChunks(inputText, maxChunkSize: 200);
|
||
var tasks = chunks.Select(chunk => GenerateCardsForChunkAsync(chunk));
|
||
var results = await Task.WhenAll(tasks);
|
||
|
||
return results.SelectMany(r => r).Take(cardCount).ToList();
|
||
}
|
||
|
||
// 請求去重和快取
|
||
public async Task<T> CachedAICallAsync<T>(string cacheKey, Func<Task<T>> apiCall)
|
||
{
|
||
var cached = await _cache.GetAsync<T>(cacheKey);
|
||
if (cached != null) return cached;
|
||
|
||
var result = await apiCall();
|
||
await _cache.SetAsync(cacheKey, result, TimeSpan.FromHours(24));
|
||
|
||
return result;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 4.2.2 前端效能優化
|
||
```typescript
|
||
// 虛擬化長列表
|
||
import { FixedSizeList as List } from 'react-window';
|
||
|
||
const VirtualizedCardList: React.FC<{cards: GeneratedCard[]}> = ({ cards }) => {
|
||
return (
|
||
<List
|
||
height={600}
|
||
itemCount={cards.length}
|
||
itemSize={200}
|
||
width="100%"
|
||
>
|
||
{({ index, style }) => (
|
||
<div style={style}>
|
||
<CardPreview card={cards[index]} />
|
||
</div>
|
||
)}
|
||
</List>
|
||
);
|
||
};
|
||
|
||
// 圖片懶加載
|
||
const LazyImage: React.FC<{src: string}> = ({ src }) => {
|
||
const [isLoaded, setIsLoaded] = useState(false);
|
||
const imgRef = useRef<HTMLImageElement>(null);
|
||
|
||
useEffect(() => {
|
||
const observer = new IntersectionObserver(
|
||
([entry]) => {
|
||
if (entry.isIntersecting) {
|
||
setIsLoaded(true);
|
||
observer.disconnect();
|
||
}
|
||
}
|
||
);
|
||
|
||
if (imgRef.current) observer.observe(imgRef.current);
|
||
|
||
return () => observer.disconnect();
|
||
}, []);
|
||
|
||
return (
|
||
<img
|
||
ref={imgRef}
|
||
src={isLoaded ? src : '/placeholder.png'}
|
||
alt=""
|
||
/>
|
||
);
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 開發優先級和路線圖
|
||
|
||
### 5.1 優先級分類
|
||
|
||
#### 🔴 **高優先級** (立即開始)
|
||
1. **詞卡儲存整合機制** - 核心功能缺失
|
||
- 影響用戶基本使用流程
|
||
- 估時: 13-19個工作天
|
||
|
||
2. **詞彙查詢API整合** - 現有功能完善
|
||
- 提升用戶互動體驗
|
||
- 估時: 7-11個工作天
|
||
|
||
3. **進度追蹤和狀態管理** - 用戶體驗關鍵
|
||
- 解決生成過程黑盒問題
|
||
- 估時: 11-18個工作天
|
||
|
||
#### 🟡 **中優先級** (第二階段)
|
||
4. **智能萃取模式實現** - 功能差異化
|
||
- 提供更專業的詞彙萃取
|
||
- 估時: 12-18個工作天
|
||
|
||
5. **詞卡品質驗證系統** - 內容品質保證
|
||
- 建立信任和專業度
|
||
- 估時: 13-19個工作天
|
||
|
||
6. **批量詞卡操作功能** - 效率提升
|
||
- 改善大量詞卡管理體驗
|
||
- 估時: 10-15個工作天
|
||
|
||
#### 🟢 **低優先級** (後續迭代)
|
||
7. **個人化推薦算法** - 智能化提升
|
||
- 長期用戶價值
|
||
- 估時: 20-29個工作天
|
||
|
||
8. **影劇截圖識別功能** - 創新功能
|
||
- 差異化競爭優勢
|
||
- 估時: 16-24個工作天
|
||
|
||
### 5.2 開發時程規劃
|
||
|
||
#### **第一階段** (6-8週)
|
||
```
|
||
週次 1-2: 詞卡儲存整合機制
|
||
週次 3-4: 詞彙查詢API整合
|
||
週次 5-6: 進度追蹤和狀態管理
|
||
週次 7-8: 測試整合和bug修復
|
||
```
|
||
|
||
#### **第二階段** (8-10週)
|
||
```
|
||
週次 1-3: 智能萃取模式實現
|
||
週次 4-6: 詞卡品質驗證系統
|
||
週次 7-8: 批量詞卡操作功能
|
||
週次 9-10: 測試和優化
|
||
```
|
||
|
||
#### **第三階段** (10-12週)
|
||
```
|
||
週次 1-5: 個人化推薦算法
|
||
週次 6-10: 影劇截圖識別功能
|
||
週次 11-12: 整體優化和發布準備
|
||
```
|
||
|
||
### 5.3 技術風險評估
|
||
|
||
#### **高風險項目**
|
||
- **影劇截圖識別**: OCR技術整合複雜度高
|
||
- **個人化推薦算法**: 機器學習模型訓練需求
|
||
|
||
#### **中風險項目**
|
||
- **智能萃取模式**: NLP技術要求較高
|
||
- **詞卡品質驗證**: AI模型準確性依賴
|
||
|
||
#### **低風險項目**
|
||
- **詞卡儲存整合**: 標準CRUD操作
|
||
- **進度追蹤**: 成熟的WebSocket技術
|
||
|
||
---
|
||
|
||
## 6. 成功指標
|
||
|
||
### 6.1 功能完成度指標
|
||
- [ ] 詞卡保存成功率 > 99%
|
||
- [ ] 詞彙查詢回應時間 < 2秒
|
||
- [ ] 生成進度追蹤準確度 > 95%
|
||
- [ ] 智能萃取模式差異化明顯
|
||
- [ ] 品質驗證誤報率 < 10%
|
||
|
||
### 6.2 用戶體驗指標
|
||
- [ ] 詞卡生成完成率提升30%
|
||
- [ ] 用戶詞彙查詢頻率提升50%
|
||
- [ ] 生成過程中斷率降低80%
|
||
- [ ] 詞卡編輯使用率 > 40%
|
||
- [ ] 整體用戶滿意度 > 4.5/5
|
||
|
||
### 6.3 技術效能指標
|
||
- [ ] API回應時間平均 < 3秒
|
||
- [ ] 系統並發支援 > 100用戶
|
||
- [ ] 資料庫查詢優化 > 50%
|
||
- [ ] 快取命中率 > 80%
|
||
- [ ] 錯誤率 < 1%
|
||
|
||
---
|
||
|
||
**文件版本**: 1.0
|
||
**建立日期**: 2025-09-20
|
||
**維護者**: DramaLing開發團隊
|
||
**預計完成**: 2025年第二季度 |