31 KiB
31 KiB
互動式單字查詢功能設計規格書
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 句子分析快取表
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 使用統計表
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 組件
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 組件
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 組件
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 組件
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 顏色系統
/* 主色彩 */
--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 互動效果
/* 可點擊單字基礎樣式 */
.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 彈窗動畫
/* 彈窗進入動畫 */
@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 設計
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 快取策略
// 本地快取實現
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 句子分析服務
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 設計
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 組件懶加載
// 懶加載重型組件
const WordInfoPopup = lazy(() => import('./WordInfoPopup'));
const ExampleImageViewer = lazy(() => import('./ExampleImageViewer'));
// 使用 Suspense 包裝
<Suspense fallback={<LoadingSpinner />}>
<WordInfoPopup {...props} />
</Suspense>
6.1.2 虛擬化長文本
// 對於長句子使用虛擬化渲染
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 批量分析優化
// 批量處理多個句子
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 前端組件測試
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 後端服務測試
[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 測試流程
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 導師指導詞彙學習