dramaling-vocab-learning/docs/02_design/interactive-word-query-spec.md

935 lines
31 KiB
Markdown
Raw Permalink 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.

# 互動式單字查詢功能設計規格書
## 1. 功能概述
### 1.1 目標
實現低成本、高效率的互動式單字查詢系統,讓用戶能夠點擊句子中的任何單字即時查看詳細意思,同時智能識別片語和俚語。
### 1.2 核心優勢
- **成本效益**:一次 API 調用,多次查詢零成本
- **即時響應**:點擊查詢無延遲
- **智能識別**:片語/俚語優先顯示和警告
- **用戶友善**:視覺化高亮和直觀操作
## 2. 系統架構設計
### 2.1 整體流程
```
用戶輸入句子 → AI 預分析 → 高價值標記 → 分析結果快取 → 互動式顯示 → 點擊查詢
↓ ↓ ↓ ↓ ↓ ↓
50字限制 一次API調用 識別學習價值 24小時快取 可點擊文字 智能計費
(扣除1次) 重要詞彙 存儲詳情 不同顏色 高價值免費
低價值收費
```
### 2.2 API 架構
#### 2.2.1 句子分析 API
```
POST /api/ai/analyze-sentence
Content-Type: application/json
Request:
{
"inputText": "He brought this thing up during our meeting and no one agreed.",
"userId": "uuid", // 用於使用次數統計
"forceRefresh": false, // 是否強制重新分析
"analysisMode": "full" // full: 完整分析並標記高價值詞彙
}
Response:
{
"success": true,
"data": {
"analysisId": "uuid",
"sentenceMeaning": {
"translation": "他在我們的會議中提出了這件事,但沒有人同意。",
"explanation": "這句話表達了在會議中有人提出某個議題或想法,但得不到其他與會者的認同。"
},
"grammarCorrection": {
"hasErrors": false, // 是否有語法錯誤
"originalText": "He brought this thing up during our meeting and no one agreed.",
"correctedText": null, // 如無錯誤則為null
"corrections": [] // 錯誤修正列表
},
"wordAnalysis": {
"brought": {
"word": "brought",
"translation": "帶來、提出",
"definition": "Past tense of bring; to take or carry something to a place",
"partOfSpeech": "verb",
"pronunciation": {
"ipa": "/brɔːt/",
"us": "/brɔːt/",
"uk": "/brɔːt/"
},
"synonyms": ["carried", "took", "delivered"],
"antonyms": ["removed", "took away"],
"isPhrase": true,
"isHighValue": true, // 高學習價值標記
"learningPriority": "high", // high, medium, low
"phraseInfo": {
"phrase": "bring up",
"meaning": "提出(話題)、養育",
"warning": "在這個句子中,\"brought up\" 是片語,意思是\"提出話題\",而不是單純的\"帶來\"",
"colorCode": "#F59E0B" // 片語顏色代碼
},
"examples": {
"original": "He brought this thing up during our meeting",
"originalTranslation": "他在會議中提出了這件事",
"generated": "She brought up an interesting point",
"generatedTranslation": "她提出了一個有趣的觀點",
"imageUrl": "/images/examples/bring_up.png",
"audioUrl": "/audio/examples/bring_up.mp3"
},
"difficultyLevel": "B1"
}
},
"finalAnalysisText": "He brought this thing up during our meeting and no one agreed.", // 最終用於學習的文本(修正後)
"highValueWords": ["brought", "up", "meeting"], // 高價值詞彙列表
"phrasesDetected": [
{
"phrase": "bring up",
"words": ["brought", "up"],
"colorCode": "#F59E0B"
}
],
"usageStatistics": {
"remainingAnalyses": 4,
"resetTime": "2025-09-17T12:48:00Z",
"costIncurred": 1 // 本次分析扣除次數
},
"cachedUntil": "2025-09-18T09:48:00Z"
},
"message": "Sentence analyzed successfully"
}
```
#### 2.2.2 單字點擊查詢 API
```
POST /api/ai/query-word
Content-Type: application/json
Request:
{
"word": "thing",
"sentence": "He brought this thing up during our meeting and no one agreed.",
"analysisId": "uuid", // 來自預分析結果
"userId": "uuid"
}
Response:
{
"success": true,
"data": {
"word": "thing",
"isHighValue": false, // 非高價值詞彙
"wasPreAnalyzed": false, // 未在預分析中
"costIncurred": 1, // 扣除1次使用次數
"analysis": {
// 完整詞彙分析資料
}
}
}
```
#### 2.2.3 快取管理 API
```
GET /api/ai/analysis-cache/{inputTextHash}
DELETE /api/ai/analysis-cache/{analysisId}
```
### 2.3 資料庫設計
#### 2.3.1 句子分析快取表
```sql
CREATE TABLE SentenceAnalysisCache (
Id UNIQUEIDENTIFIER PRIMARY KEY,
InputTextHash NVARCHAR(64) NOT NULL, -- SHA256 hash
InputText NVARCHAR(1000) NOT NULL,
CorrectedText NVARCHAR(1000), -- 修正後的文本
HasGrammarErrors BIT DEFAULT 0, -- 是否有語法錯誤
GrammarCorrections NVARCHAR(MAX), -- JSON 格式,語法修正詳情
AnalysisResult NVARCHAR(MAX) NOT NULL, -- JSON 格式
HighValueWords NVARCHAR(MAX) NOT NULL, -- JSON 格式,高價值詞彙列表
PhrasesDetected NVARCHAR(MAX), -- JSON 格式,檢測到的片語
CreatedAt DATETIME2 NOT NULL,
ExpiresAt DATETIME2 NOT NULL,
AccessCount INT DEFAULT 0,
LastAccessedAt DATETIME2
);
CREATE INDEX IX_SentenceAnalysisCache_Hash ON SentenceAnalysisCache(InputTextHash);
CREATE INDEX IX_SentenceAnalysisCache_Expires ON SentenceAnalysisCache(ExpiresAt);
```
#### 2.3.2 使用統計表
```sql
CREATE TABLE WordQueryUsageStats (
Id UNIQUEIDENTIFIER PRIMARY KEY,
UserId UNIQUEIDENTIFIER NOT NULL,
Date DATE NOT NULL,
SentenceAnalysisCount INT DEFAULT 0, -- 句子分析次數
HighValueWordClicks INT DEFAULT 0, -- 高價值詞彙點擊(免費)
LowValueWordClicks INT DEFAULT 0, -- 低價值詞彙點擊(收費)
TotalApiCalls INT DEFAULT 0, -- 總 API 調用次數
UniqueWordsQueried INT DEFAULT 0,
CreatedAt DATETIME2 NOT NULL,
UpdatedAt DATETIME2 NOT NULL
);
CREATE UNIQUE INDEX IX_WordQueryUsageStats_UserDate ON WordQueryUsageStats(UserId, Date);
```
## 3. 前端組件設計
### 3.1 主要組件架構
```
WordQueryPage
├── SentenceInputForm // 句子輸入表單
├── AnalysisLoadingState // 分析中狀態
├── GrammarCorrectionPanel // 語法修正面板
│ ├── ErrorHighlight // 錯誤標記顯示
│ ├── CorrectionSuggestion // 修正建議
│ └── UserChoiceButtons // 用戶選擇按鈕
├── InteractiveTextDisplay // 互動式文字顯示
│ ├── ClickableWord // 可點擊單字
│ └── WordInfoPopup // 單字資訊彈窗
├── UsageStatistics // 使用統計顯示
└── ActionButtons // 操作按鈕組
```
### 3.2 組件詳細設計
#### 3.2.1 GrammarCorrectionPanel 組件
```typescript
interface GrammarCorrection {
hasErrors: boolean;
originalText: string;
correctedText: string | null;
corrections: Array<{
position: { start: number; end: number };
errorType: string;
original: string;
corrected: string;
reason: string;
severity: 'high' | 'medium' | 'low';
}>;
confidenceScore: number;
}
interface GrammarCorrectionPanelProps {
correction: GrammarCorrection;
onAcceptCorrection: () => void;
onRejectCorrection: () => void;
onManualEdit: (text: string) => void;
}
const GrammarCorrectionPanel: React.FC<GrammarCorrectionPanelProps> = ({
correction,
onAcceptCorrection,
onRejectCorrection,
onManualEdit
}) => {
// 錯誤高亮顯示
// 修正建議卡片
// 修正原因說明
// 用戶選擇按鈕
};
```
#### 3.2.2 SentenceInputForm 組件
```typescript
interface SentenceInputFormProps {
maxLength: number; // 300 for manual input
onSubmit: (text: string) => void;
onModeChange: (mode: 'manual' | 'screenshot') => void;
disabled: boolean;
remainingAnalyses: number;
}
const SentenceInputForm: React.FC<SentenceInputFormProps> = ({
maxLength,
onSubmit,
onModeChange,
disabled,
remainingAnalyses
}) => {
// 即時字數統計
// 300字限制阻擋
// 模式切換UI
// 示例句子填入
// 分析按鈕狀態
};
```
#### 3.2.3 InteractiveTextDisplay 組件
```typescript
interface WordAnalysis {
word: string;
translation: string;
definition: string;
partOfSpeech: string;
pronunciation: {
ipa: string;
us: string;
uk: string;
};
synonyms: string[];
antonyms: string[];
isPhrase: boolean;
isHighValue: boolean; // 高學習價值標記
learningPriority: 'high' | 'medium' | 'low'; // 學習優先級
phraseInfo?: {
phrase: string;
meaning: string;
warning: string;
colorCode: string; // 片語顏色代碼
};
examples: {
original: string;
originalTranslation: string;
generated: string;
generatedTranslation: string;
imageUrl?: string;
audioUrl?: string;
};
difficultyLevel: string;
}
interface InteractiveTextDisplayProps {
text: string;
analysis: Record<string, WordAnalysis>;
onWordClick: (word: string, analysis: WordAnalysis) => void;
}
```
#### 3.2.4 WordInfoPopup 組件
```typescript
interface WordInfoPopupProps {
word: string;
analysis: WordAnalysis;
position: { x: number; y: number };
onClose: () => void;
onPlayAudio: (audioUrl: string) => void;
}
const WordInfoPopup: React.FC<WordInfoPopupProps> = ({
word,
analysis,
position,
onClose,
onPlayAudio
}) => {
// 片語警告顯示
// 發音播放按鈕
// 例句圖片顯示
// 同義詞/反義詞標籤
// 難度等級標示
};
```
## 4. 用戶介面設計
### 4.1 頁面佈局
#### 4.1.1 輸入階段
```
┌─────────────────────────────────────────────────────────┐
│ DramaLing │
├─────────────────────────────────────────────────────────┤
│ AI 智能生成詞卡 - 互動式單字查詢 │
│ │
│ ┌─ 原始例句類型 ──────────────────────────────────┐ │
│ │ [✍️ 手動輸入] [📷 影劇截圖] (訂閱功能) │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ ┌─ 輸入英文文本 ──────────────────────────────────┐ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ 輸入英文句子最多50字... │ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ │ 最多 50 字元 • 目前0 字元 │ │
│ │ │ │
│ │ 💡 示例句子: │ │
│ │ [點擊使用示例He brought this thing up...] │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 🔍 分析句子(點擊查詢單字) │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 免費用戶:已使用 0/5 次 (3小時內) │
└─────────────────────────────────────────────────────────┘
```
#### 4.1.2 分析結果階段
```
┌─────────────────────────────────────────────────────────┐
│ ← 返回 句子分析結果 │
├─────────────────────────────────────────────────────────┤
│ ┌─ 原始句子 ──────────────────────────────────────┐ │
│ │ He brought this thing up during our meeting. │ │
│ │ │ │
│ │ 整句意思: │ │
│ │ 他在我們的會議中提出了這件事,但沒有人同意... │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ ┌─ 點擊查詢單字意思 ──────────────────────────────┐ │
│ │ 💡 使用說明:點擊下方句子中的任何單字,可以立即 │ │
│ │ 查看詳細意思。黃色背景表示該單字屬於片語或俚語。 │ │
│ │ │ │
│ │ ╔═══════════════════════════════════════════╗ │ │
│ │ ║ He [brought] this [thing] [up] during ║ │ │
│ │ ║ our [meeting] and no one [agreed]. ║ │ │
│ │ ╚═══════════════════════════════════════════╝ │ │
│ │ // brought 和 up 有黃色背景 │ │
│ │ // 其他單字有藍色下劃線 │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ [🔄 分析新句子] [📖 生成詞卡] │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
```
#### 4.1.3 單字彈窗設計
```
┌─ brought ─────────────────── ×
│ │
│ ⚠️ 注意:這個單字屬於片語 │
│ 片語bring up │
│ 意思:提出(話題)、養育 │
│ │
│ verb | /brɔːt/ | 🔊 │
│ │
│ 翻譯:帶來、提出 │
│ │
│ 定義Past tense of bring; │
│ to take or carry something │
│ │
│ 同義詞:[carried] [took] │
│ 反義詞:[removed] │
│ │
│ 例句: │
│ • 原始He brought this... │
│ 翻譯:他提出了這件事... │
│ • 生成She brought up... │
│ 翻譯:她提出了一個... │
│ [📷 查看例句圖] [🔊 播放] │
│ │
│ 難度B1 │
└───────────────────────────────┘
```
### 4.2 視覺設計規範
#### 4.2.1 顏色系統
```css
/* 主色彩 */
--primary-blue: #3B82F6;
--primary-blue-hover: #2563EB;
--primary-blue-light: #DBEAFE;
/* 單字價值顏色系統 */
--word-high-phrase: #F59E0B; /* 高價值片語 */
--word-high-single: #10B981; /* 高價值單字 */
--word-normal: #3B82F6; /* 普通單字 */
--word-hover: #1E40AF; /* 懸停狀態 */
/* 背景顏色 */
--word-high-phrase-bg: #FEF3C7; /* 高價值片語背景 */
--word-high-single-bg: #ECFDF5; /* 高價值單字背景 */
--word-normal-bg: transparent; /* 普通單字背景 */
--word-hover-bg: #DBEAFE; /* 懸停背景 */
/* 邊框顏色 */
--border-high-phrase: #F59E0B; /* 高價值片語邊框 */
--border-high-single: #10B981; /* 高價值單字邊框 */
--border-normal: #3B82F6; /* 普通單字邊框 */
/* 狀態顏色 */
--success: #10B981;
--warning: #F59E0B;
--error: #EF4444;
--info: #3B82F6;
--premium: #8B5CF6; /* 付費功能 */
```
#### 4.2.2 互動效果
```css
/* 可點擊單字基礎樣式 */
.clickable-word {
cursor: pointer;
transition: all 0.2s ease;
padding: 2px 4px;
border-radius: 4px;
margin: 0 1px;
position: relative;
}
/* 高價值片語樣式 */
.clickable-word.high-value.phrase {
background-color: var(--word-high-phrase-bg);
border: 2px solid var(--border-high-phrase);
}
.clickable-word.high-value.phrase::after {
content: "⭐";
position: absolute;
top: -8px;
right: -8px;
font-size: 12px;
}
/* 高價值單字樣式 */
.clickable-word.high-value.single {
background-color: var(--word-high-single-bg);
border: 2px solid var(--border-high-single);
}
.clickable-word.high-value.single::after {
content: "⭐";
position: absolute;
top: -8px;
right: -8px;
font-size: 12px;
}
/* 普通單字樣式 */
.clickable-word.normal {
border-bottom: 1px solid var(--border-normal);
}
/* 懸停效果 */
.clickable-word:hover {
background-color: var(--word-hover-bg);
transform: translateY(-1px);
}
```
#### 4.2.3 彈窗動畫
```css
/* 彈窗進入動畫 */
@keyframes popup-enter {
from {
opacity: 0;
transform: translate(-50%, -100%) scale(0.9);
}
to {
opacity: 1;
transform: translate(-50%, -100%) scale(1);
}
}
.word-popup {
animation: popup-enter 0.2s ease-out;
}
```
## 5. 技術實現細節
### 5.1 前端狀態管理
#### 5.1.1 Zustand Store 設計
```typescript
interface WordQueryStore {
// 分析狀態
isAnalyzing: boolean;
analysisResult: SentenceAnalysis | null;
analysisError: string | null;
// 互動狀態
selectedWord: string | null;
popupPosition: { x: number; y: number } | null;
// 使用統計
usageStats: {
remainingAnalyses: number;
resetTime: string;
};
// 快取
analysisCache: Map<string, SentenceAnalysis>;
// Actions
analyzeSentence: (text: string) => Promise<void>;
selectWord: (word: string, position: { x: number; y: number }) => void;
closeWordPopup: () => void;
clearCache: () => void;
}
```
#### 5.1.2 快取策略
```typescript
// 本地快取實現
class AnalysisCache {
private cache = new Map<string, CacheItem>();
get(textHash: string): SentenceAnalysis | null {
const item = this.cache.get(textHash);
if (!item || this.isExpired(item)) {
this.cache.delete(textHash);
return null;
}
return item.data;
}
set(textHash: string, data: SentenceAnalysis, ttl: number): void {
this.cache.set(textHash, {
data,
expiresAt: Date.now() + ttl
});
}
private isExpired(item: CacheItem): boolean {
return Date.now() > item.expiresAt;
}
}
```
### 5.2 後端實現細節
#### 5.2.1 句子分析服務
```csharp
public class SentenceAnalysisService : ISentenceAnalysisService
{
private readonly IGeminiService _geminiService;
private readonly IAnalysisCacheService _cacheService;
private readonly IUsageTrackingService _usageService;
public async Task<SentenceAnalysisResult> AnalyzeSentenceAsync(
string inputText,
Guid userId,
bool forceRefresh = false)
{
// 1. 檢查使用限制
await _usageService.CheckUsageLimitAsync(userId);
// 2. 檢查快取
var textHash = GenerateTextHash(inputText);
if (!forceRefresh)
{
var cached = await _cacheService.GetAsync(textHash);
if (cached != null)
{
await _usageService.RecordCacheHitAsync(userId);
return cached;
}
}
// 3. AI 分析
var analysis = await _geminiService.AnalyzeSentenceAsync(inputText);
// 4. 存入快取
await _cacheService.SetAsync(textHash, analysis, TimeSpan.FromHours(24));
// 5. 記錄使用
await _usageService.RecordAnalysisAsync(userId);
return analysis;
}
}
```
#### 5.2.2 Gemini Prompt 設計
```csharp
private const string SENTENCE_ANALYSIS_PROMPT = @"
請分析以下英文句子,先檢查語法錯誤並修正,然後提供完整的單字和片語解析:
句子:{inputText}
請按照以下 JSON 格式回應:
{
""grammarCorrection"": {
""hasErrors"": true/false,
""originalText"": ""原始輸入句子"",
""correctedText"": ""修正後句子"" // 如無錯誤則與原始相同,
""corrections"": [
{
""position"": {""start"": 2, ""end"": 4},
""errorType"": ""tense_mismatch"",
""original"": ""錯誤詞彙"",
""corrected"": ""修正詞彙"",
""reason"": ""修正原因說明"",
""severity"": ""high/medium/low""
}
],
""confidenceScore"": 0.95
},
""sentenceMeaning"": {
""translation"": ""整句的繁體中文意思"",
""explanation"": ""詳細解釋""
},
""finalAnalysisText"": ""用於後續分析的最終文本(修正後)"",
""wordAnalysis"": {
""單字原形"": {
""word"": ""單字原形"",
""translation"": ""繁體中文翻譯"",
""definition"": ""英文定義(A1-A2程度)"",
""partOfSpeech"": ""詞性(noun/verb/adjective/adverb/pronoun/preposition/conjunction/interjection)"",
""pronunciation"": {
""ipa"": ""IPA音標"",
""us"": ""美式音標"",
""uk"": ""英式音標""
},
""synonyms"": [""同義詞1"", ""同義詞2"", ""同義詞3""],
""antonyms"": [""反義詞1"", ""反義詞2""],
""isPhrase"": true/false,
""phraseInfo"": {
""phrase"": ""完整片語"",
""meaning"": ""片語意思"",
""warning"": ""警告說明""
},
""examples"": {
""original"": ""來自原句的例句"",
""originalTranslation"": ""原句例句翻譯"",
""generated"": ""AI生成的新例句"",
""generatedTranslation"": ""新例句翻譯""
},
""difficultyLevel"": ""CEFR等級(A1/A2/B1/B2/C1/C2)""
}
}
}
分析要求:
1. **首要任務:語法檢查和修正**
- 檢測語法、拼寫、時態、介詞、詞序錯誤
- 提供修正建議和詳細說明
- 後續分析基於修正後的句子進行
- 保持原句意思不變
2. 識別所有有意義的單字(忽略 a, an, the 等功能詞)
3. **重點:標記高學習價值詞彙**
- 片語和俚語isHighValue: true, learningPriority: "high"
- 中級以上單字(B1+isHighValue: true, learningPriority: "high"
- 專業術語:isHighValue: true, learningPriority: "medium"
- 基礎功能詞:isHighValue: false, learningPriority: "low"
4. 特別注意片語和俚語,設定 isPhrase: true
5. 為片語提供警告說明和顏色代碼
6. 英文定義保持在 A1-A2 程度
7. 提供實用的同義詞和反義詞(如適用)
8. 例句要清楚展示單字用法
9. 準確標記 CEFR 難度等級
10. **優先處理高價值詞彙**:為高價值詞彙生成完整內容詳情
";
```
## 6. 性能優化策略
### 6.1 前端優化
#### 6.1.1 組件懶加載
```typescript
// 懶加載重型組件
const WordInfoPopup = lazy(() => import('./WordInfoPopup'));
const ExampleImageViewer = lazy(() => import('./ExampleImageViewer'));
// 使用 Suspense 包裝
<Suspense fallback={<LoadingSpinner />}>
<WordInfoPopup {...props} />
</Suspense>
```
#### 6.1.2 虛擬化長文本
```typescript
// 對於長句子使用虛擬化渲染
const VirtualizedText = ({ words, analysis, onWordClick }) => {
const [visibleRange, setVisibleRange] = useState({ start: 0, end: 50 });
return (
<div className="virtual-text-container">
{words.slice(visibleRange.start, visibleRange.end).map((word, index) => (
<ClickableWord
key={index}
word={word}
analysis={analysis[word]}
onClick={onWordClick}
/>
))}
</div>
);
};
```
### 6.2 後端優化
#### 6.2.1 快取層次設計
```
L1: 記憶體快取 (Redis) - 1小時 TTL
L2: 資料庫快取 (SQLite) - 24小時 TTL
L3: 磁碟快取 (File System) - 7天 TTL
```
#### 6.2.2 批量分析優化
```csharp
// 批量處理多個句子
public async Task<List<SentenceAnalysisResult>> AnalyzeMultipleSentencesAsync(
List<string> sentences,
Guid userId)
{
// 1. 批量檢查快取
var cacheResults = await _cacheService.GetMultipleAsync(
sentences.Select(GenerateTextHash)
);
// 2. 只分析未快取的句子
var uncachedSentences = sentences
.Where((s, i) => cacheResults[i] == null)
.ToList();
// 3. 批量調用 AI API
var newAnalyses = await _geminiService.AnalyzeMultipleSentencesAsync(
uncachedSentences
);
// 4. 合併結果
return MergeResults(cacheResults, newAnalyses);
}
```
## 7. 測試策略
### 7.1 單元測試
#### 7.1.1 前端組件測試
```typescript
describe('ClickableText Component', () => {
it('should highlight phrase words correctly', () => {
const analysis = {
'brought': { isPhrase: true, /* ... */ },
'up': { isPhrase: true, /* ... */ }
};
render(<ClickableText text="He brought this up" analysis={analysis} />);
expect(screen.getByText('brought')).toHaveClass('phrase');
expect(screen.getByText('up')).toHaveClass('phrase');
});
it('should show word popup on click', async () => {
const mockOnClick = jest.fn();
render(<ClickableText onWordClick={mockOnClick} />);
fireEvent.click(screen.getByText('brought'));
expect(mockOnClick).toHaveBeenCalledWith('brought', expect.any(Object));
});
});
```
#### 7.1.2 後端服務測試
```csharp
[Test]
public async Task AnalyzeSentence_ShouldReturnCachedResult_WhenCacheExists()
{
// Arrange
var inputText = "Test sentence";
var userId = Guid.NewGuid();
var cachedResult = new SentenceAnalysisResult();
_cacheService.Setup(c => c.GetAsync(It.IsAny<string>()))
.ReturnsAsync(cachedResult);
// Act
var result = await _analysisService.AnalyzeSentenceAsync(inputText, userId);
// Assert
Assert.AreEqual(cachedResult, result);
_geminiService.Verify(g => g.AnalyzeSentenceAsync(It.IsAny<string>()),
Times.Never);
}
```
### 7.2 整合測試
#### 7.2.1 E2E 測試流程
```typescript
describe('Word Query Flow', () => {
it('should complete full analysis and query flow', async () => {
// 1. 輸入句子
await page.fill('[data-testid=sentence-input]', 'He brought this up');
await page.click('[data-testid=analyze-button]');
// 2. 等待分析完成
await page.waitForSelector('[data-testid=interactive-text]');
// 3. 點擊單字
await page.click('[data-testid=word-brought]');
// 4. 驗證彈窗顯示
await expect(page.locator('[data-testid=word-popup]')).toBeVisible();
await expect(page.locator('[data-testid=phrase-warning]')).toBeVisible();
// 5. 播放發音
await page.click('[data-testid=play-pronunciation]');
// 6. 關閉彈窗
await page.click('[data-testid=close-popup]');
await expect(page.locator('[data-testid=word-popup]')).toBeHidden();
});
});
```
## 8. 部署和監控
### 8.1 部署策略
#### 8.1.1 前端部署
- **平台**Vercel
- **環境變量**API_BASE_URL, CACHE_TTL
- **CDN**:自動優化靜態資源
- **快取策略**:分析結果本地存儲 24 小時
#### 8.1.2 後端部署
- **平台**Azure App Service / AWS Lambda
- **資料庫**Azure SQL Database / AWS RDS
- **快取**Azure Redis Cache / AWS ElastiCache
- **檔案存儲**Azure Blob Storage / AWS S3
### 8.2 監控指標
#### 8.2.1 業務指標
- 句子分析成功率
- 平均分析響應時間
- 快取命中率
- 用戶使用次數分佈
- 單字點擊熱度排行
#### 8.2.2 技術指標
- API 響應時間 (P95 < 200ms)
- Gemini API 調用延遲
- 快取效能指標
- 錯誤率 (< 1%)
- 系統可用性 (> 99.9%)
#### 8.2.3 成本監控
- Gemini API 調用次數和費用
- 快取存儲成本
- CDN 流量費用
- 基礎設施總成本
## 9. 未來擴展計劃
### 9.1 功能增強
- **多語言支持**:支援其他語言的句子分析
- **語音輸入**:整合語音識別進行句子輸入
- **個人化推薦**:基於用戶查詢歷史推薦相關詞彙
- **社交分享**:分享有趣的句子分析結果
### 9.2 技術升級
- **AI 模型本地化**:部署本地 LLM 降低外部依賴
- **即時協作**:多用戶同時查詢同一句子
- **離線支持**PWA 實現離線查詢基礎詞彙
- **效能優化**WebAssembly 加速文本處理
### 9.3 商業化功能
- **高級分析**:更深度的語法和語義分析
- **專業詞典**:整合專業領域詞典
- **學習追蹤**:詳細的學習進度和成效分析
- **導師模式**AI 導師指導詞彙學習