# 複習系統技術實作規格書 **版本**: 1.0 **對應PRD**: 產品需求規格.md **目標讀者**: 開發者、技術主管 **最後更新**: 2025-10-03 --- ## 📊 **核心算法和公式** ### **進度條計算公式** ```typescript 進度百分比 = (今日完成) / (今日完成 + 今日到期) * 100 ``` **實作範例**: ```typescript const progressPercentage = completedToday / (completedToday + dueToday) * 100 ``` ### **複習間隔算法** ```typescript 下一次複習時間 = 當前時間 + (2^成功複習次數) 天 ``` **實作範例**: ```typescript const nextReviewDate = new Date() nextReviewDate.setDate(nextReviewDate.getDate() + Math.pow(2, successCount)) ``` --- ## 🏷️ **卡片狀態管理機制** ### **延遲註記系統** **新增延遲註記的情況**: 1. 用戶點選 "跳過" → 卡片標記 `isDelayed: true` 2. 用戶答錯題目 → 卡片標記 `isDelayed: true` **延遲註記的影響**: ```typescript // 撈取下一個複習卡片時排除延遲卡片 const nextCards = allCards.filter(card => !card.isDelayed) ``` **消除延遲註記**: ```typescript // 用戶答對題目時 if (isCorrect) { card.isDelayed = false card.successCount++ card.nextReviewDate = calculateNextReviewDate(card.successCount) } ``` --- ## 🎯 **測驗模式實作** ### **階段1: 翻卡記憶 (已實作)** **數據結構**: ```typescript interface FlashcardData { id: string word: string definition: string example: string exampleTranslation: string pronunciation: string synonyms: string[] cefr: string } ``` **狀態管理**: ```typescript const [isFlipped, setIsFlipped] = useState(false) const [selectedConfidence, setSelectedConfidence] = useState(null) const [cardHeight, setCardHeight] = useState(400) ``` **信心度評分映射**: ```typescript const confidenceToScore = { 1: 0, // 完全不懂 → 答錯 2: 0, // 模糊 → 答錯 3: 1, // 一般 → 答對 4: 1, // 熟悉 → 答對 5: 1 // 非常熟悉 → 答對 } ``` ### **階段2: 詞彙選擇題 (已設計完成)** **組件架構** (基於您的設計): ```typescript interface VocabChoiceTestProps { cardData: FlashcardData options: string[] // 4選1選項 onAnswer: (answer: string) => void onReportError: () => void disabled?: boolean } // 組件分區設計 const VocabChoiceTest = () => { const questionArea = // 問題顯示區域 const optionsArea = // 選項網格區域 const resultArea = // 結果顯示區域 return ( ) } ``` **問題顯示區域設計**: ```typescript const questionArea = (

定義

{cardData.definition}

請選擇符合上述定義的英文詞彙:

) ``` **狀態管理設計**: ```typescript const [selectedAnswer, setSelectedAnswer] = useState(null) const [showResult, setShowResult] = useState(false) // 答案驗證邏輯 const isCorrect = useMemo(() => selectedAnswer === cardData.word, [selectedAnswer, cardData.word] ) // 選擇處理 const handleAnswerSelect = useCallback((answer: string) => { if (disabled || showResult) return setSelectedAnswer(answer) setShowResult(true) onAnswer(answer) }, [disabled, showResult, onAnswer]) ``` **選項生成算法**: ```typescript // MVP階段: 固定選項 const generateSimpleOptions = (correctWord: string): string[] => { const fixedDistractors = ['apple', 'orange', 'banana'] return shuffle([correctWord, ...fixedDistractors]) } // 階段3升級方案: 智能干擾項 const generateSmartOptions = (correctWord: Flashcard, allWords: Flashcard[]): string[] => { const samePOS = allWords.filter(w => w.partOfSpeech === correctWord.partOfSpeech) const sameCEFR = allWords.filter(w => w.cefr === correctWord.cefr) const distractors = selectRandom([...samePOS, ...sameCEFR], 3) return shuffle([correctWord.word, ...distractors.map(w => w.word)]) } ``` **ChoiceGrid組件設計** (您的完整設計): ```typescript interface ChoiceGridProps { options: string[] // 4個選項 selectedOption?: string | null correctAnswer?: string showResult?: boolean // 控制結果顯示 onSelect: (option: string) => void disabled?: boolean className?: string } // 響應式網格布局
{options.map((option, index) => ( ))}
``` **ChoiceOption樣式規格** (您的設計): ```typescript // 選項按鈕狀態樣式 const getOptionStyles = (isSelected, isCorrect, isIncorrect, showResult) => { if (showResult) { if (isCorrect) return 'border-green-500 bg-green-50 text-green-700' if (isIncorrect && isSelected) return 'border-red-500 bg-red-50 text-red-700' return 'border-gray-200 bg-gray-50 text-gray-500' } if (isSelected) return 'border-blue-500 bg-blue-50 text-blue-700' return 'border-gray-200 hover:border-blue-300 hover:bg-blue-50' } // 按鈕基礎樣式 className={`p-4 text-center rounded-lg border-2 transition-all ${getOptionStyles()}`} ``` **UI組件層次設計**: ```typescript // 您設計的完整組件結構 // 外層容器和錯誤處理 // 問題顯示區域

定義

{definition}

請選擇符合上述定義的英文詞彙:

// 選項區域 // 結果顯示區域 {showResult && ( )}
``` --- ## 🎨 **UI/UX實作規格** ### **翻卡動畫參數** ```css /* 3D翻卡動畫 - 已調校的完美參數 */ .flip-card { perspective: 1000px; transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1); } .flip-card.flipped { transform: rotateY(180deg); } ``` ### **響應式高度計算** ```typescript const calculateCardHeight = () => { const backHeight = backRef.current?.scrollHeight || 0 const minHeight = window.innerWidth <= 480 ? 300 : window.innerWidth <= 768 ? 350 : 400 return Math.max(minHeight, backHeight) } ``` ### **信心度按鈕配色** ```typescript const confidenceColors = { 1: 'bg-red-100 text-red-700 border-red-200', // 完全不懂 2: 'bg-orange-100 text-orange-700 border-orange-200', // 模糊 3: 'bg-yellow-100 text-yellow-700 border-yellow-200', // 一般 4: 'bg-blue-100 text-blue-700 border-blue-200', // 熟悉 5: 'bg-green-100 text-green-700 border-green-200' // 非常熟悉 } ``` --- ## 📱 **API設計規格** (階段3實作) ### **獲取複習卡片API** ```typescript GET /api/flashcards/due?limit=10 Response: { "success": true, "data": { "flashcards": FlashcardData[], "count": number }, "timestamp": string } ``` ### **記錄複習結果API** ```typescript POST /api/flashcards/{id}/review Request: { "confidence": number, // 1-5 "isCorrect": boolean, // 基於confidence >= 3判斷 "reviewType": "flip-memory", "responseTimeMs": number } Response: { "success": boolean, "nextReviewDate": string, "newSuccessCount": number } ``` --- ## 🗄️ **數據存儲設計** ### **階段1: 內存狀態 (當前)** ```typescript // 純React狀態,會話結束即消失 const [currentIndex, setCurrentIndex] = useState(0) const [score, setScore] = useState({ correct: 0, total: 0 }) ``` ### **階段2: 本地存儲 (計劃)** ```typescript // localStorage持久化 const saveProgress = (progress: ReviewProgress) => { localStorage.setItem('review-progress', JSON.stringify({ currentIndex, score, timestamp: Date.now() })) } ``` ### **階段3: 後端同步 (遠期)** ```typescript // 與後端API同步 const syncProgress = async (progress: ReviewProgress) => { await api.post('/api/user/review-progress', progress) } ``` --- ## 🔒 **業務邏輯約束** ### **狀態轉換規則** ```typescript // 卡片狀態轉換 enum CardState { PENDING = 'pending', // 未開始 COMPLETED = 'completed', // 已完成 DELAYED = 'delayed' // 延遲 (跳過或答錯) } // 狀態轉換邏輯 const handleAnswer = (confidence: number) => { if (confidence >= 3) { // 答對: 移除延遲標記,標記完成 card.state = CardState.COMPLETED card.isDelayed = false card.successCount++ } else { // 答錯: 添加延遲標記 card.isDelayed = true card.state = CardState.DELAYED } } ``` ### **隊列管理邏輯** ```typescript // 下一張卡片選擇邏輯 const getNextCard = (cards: FlashCard[]) => { // 1. 優先選擇未延遲的卡片 const normalCards = cards.filter(c => !c.isDelayed && c.state === CardState.PENDING) if (normalCards.length > 0) { return normalCards[0] } // 2. 如果沒有正常卡片,選擇延遲卡片 const delayedCards = cards.filter(c => c.isDelayed) return delayedCards[0] || null } ``` --- ## ⚡ **性能和優化規格** ### **載入性能要求** ```typescript // 性能指標 const PERFORMANCE_TARGETS = { INITIAL_LOAD: 2000, // 初始載入 < 2秒 CARD_FLIP: 500, // 翻卡響應 < 500ms CONFIDENCE_SELECT: 200, // 按鈕響應 < 200ms NAVIGATION: 300 // 頁面切換 < 300ms } ``` ### **記憶體管理** ```typescript // 避免記憶體洩漏 useEffect(() => { const timer = setTimeout(updateHeight, 100) const resizeHandler = () => updateHeight() window.addEventListener('resize', resizeHandler) return () => { clearTimeout(timer) window.removeEventListener('resize', resizeHandler) } }, [dependencies]) ``` --- ## 🧪 **測試規格** (TDD Implementation) ### **測試覆蓋要求** - **核心邏輯**: 100% 測試覆蓋 - **UI組件**: 關鍵交互測試 - **API調用**: Mock測試 - **狀態管理**: 狀態轉換測試 ### **測試案例範例** ```typescript // 翻卡邏輯測試 describe('FlipCard Component', () => { test('應該在點擊時切換翻轉狀態', () => { const { result } = renderHook(() => useFlipCard()) act(() => result.current.flip()) expect(result.current.isFlipped).toBe(true) }) test('應該在選擇信心度時觸發提交', () => { const onSubmit = jest.fn() const card = render() card.selectConfidence(4) expect(onSubmit).toHaveBeenCalledWith(4) }) }) ``` --- ## 📋 **實作檢查清單** ### **每個功能完成時檢查** - [ ] 功能符合產品需求規格 - [ ] 遵循技術約束和算法 - [ ] 通過所有相關測試 - [ ] 性能指標達標 - [ ] 無記憶體洩漏 - [ ] 錯誤處理完善 ### **代碼品質標準** - [ ] TypeScript 無錯誤 - [ ] ESLint 無警告 - [ ] 組件 < 200行 - [ ] 函數 < 20行 - [ ] 嵌套層次 < 3層 --- *技術實作規格維護者: 開發團隊* *版本控制: 與產品需求規格同步更新* *目的: 確保實作準確性,避免開發失控*