From 1fb7cadd52d6b0970cb1b06cffe016670ea71c72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=84=AD=E6=B2=9B=E8=BB=92?= Date: Thu, 18 Sep 2025 14:44:48 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=AE=8C=E6=95=B4?= =?UTF-8?q?=E7=9A=84=E7=B3=BB=E7=B5=B1=E5=88=86=E6=9E=90=E6=96=87=E6=AA=94?= =?UTF-8?q?=E5=92=8C=E5=89=8D=E7=AB=AF=E5=BF=AB=E5=8F=96=E7=8B=80=E6=85=8B?= =?UTF-8?q?=E9=A1=AF=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 前端改善: - 新增快取狀態視覺化標籤 (💾 快取結果 / 🤖 AI 分析) - 完善ClickableTextV2組件的大小寫屬性相容性 - 修復互動式單字查詢功能在快取場景下的顯示問題 - 改善載入狀態提示,增加時間預期說明 - 新增getWordProperty輔助函數,統一處理屬性讀取 系統文檔: - 新增完整的功能規格文檔 (User Flow + 測試案例) - 生成快取機制分析報告 (前端+後端) - 建立使用限制功能實現報告 - 記錄所有檢查方法和問題解決過程 清理: - 移除過時的環境設定文檔 - 整理專案結構 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- frontend/app/generate/page.tsx | 82 +- frontend/components/ClickableTextV2.tsx | 64 +- .../ENV_SETUP_SECURE.md | 0 .../SUPABASE_SETUP_GUIDE.md | 0 note/cache-analysis-report.md | 268 ++++++ note/frontend-cache-analysis-report.md | 275 ++++++ note/interactive-word-query-system-spec.md | 795 ++++++++++++++++++ note/usage-limit-implementation-report.md | 456 ++++++++++ 8 files changed, 1884 insertions(+), 56 deletions(-) rename ENV_SETUP_SECURE.md => note/ENV_SETUP_SECURE.md (100%) rename SUPABASE_SETUP_GUIDE.md => note/SUPABASE_SETUP_GUIDE.md (100%) create mode 100644 note/cache-analysis-report.md create mode 100644 note/frontend-cache-analysis-report.md create mode 100644 note/interactive-word-query-system-spec.md create mode 100644 note/usage-limit-implementation-report.md diff --git a/frontend/app/generate/page.tsx b/frontend/app/generate/page.tsx index c68e062..60c97ca 100644 --- a/frontend/app/generate/page.tsx +++ b/frontend/app/generate/page.tsx @@ -22,6 +22,12 @@ function GenerateContent() { const [finalText, setFinalText] = useState('') const [usageCount, setUsageCount] = useState(0) const [isPremium] = useState(false) + const [cacheStatus, setCacheStatus] = useState<{ + isCached: boolean + cacheHit: boolean + usingAI: boolean + message: string + } | null>(null) // 處理句子分析 - 使用真實AI API const handleAnalyzeSentence = async () => { @@ -66,17 +72,26 @@ function GenerateContent() { console.log('📦 完整API響應:', result) if (result.success) { - // 使用真實AI的回應資料 - setSentenceAnalysis(result.data.wordAnalysis || {}) + // 設定快取狀態 + setCacheStatus({ + isCached: result.cached || false, + cacheHit: result.cacheHit || false, + usingAI: result.usingAI || false, + message: result.message || '分析完成' + }) + + // 使用真實AI的回應資料 - 支援兩種key格式 (小寫/大寫) + setSentenceAnalysis(result.data.wordAnalysis || result.data.WordAnalysis || {}) + + // 安全處理 sentenceMeaning - 支援兩種key格式 (小寫/大寫) + const sentenceMeaning = result.data.sentenceMeaning || result.data.SentenceMeaning || {} + const translation = sentenceMeaning.Translation || sentenceMeaning.translation || '翻譯處理中...' + const explanation = sentenceMeaning.Explanation || sentenceMeaning.explanation || '解釋處理中...' - // 安全處理 sentenceMeaning - const sentenceMeaning = result.data.sentenceMeaning || {} - const translation = sentenceMeaning.translation || '翻譯處理中...' - const explanation = sentenceMeaning.explanation || '解釋處理中...' setSentenceMeaning(translation + ' ' + explanation) - setGrammarCorrection(result.data.grammarCorrection || { hasErrors: false }) - setFinalText(result.data.finalAnalysisText || textInput) + setGrammarCorrection(result.data.grammarCorrection || result.data.GrammarCorrection || { hasErrors: false }) + setFinalText(result.data.finalAnalysisText || result.data.FinalAnalysisText || textInput) setShowAnalysisView(true) setUsageCount(prev => prev + 1) } else { @@ -226,7 +241,7 @@ function GenerateContent() { {/* Extraction Type Selection */} -
+ {/*

萃取方式

-
+ */} {/* Action Buttons */}
@@ -270,33 +285,13 @@ function GenerateContent() { - 正在分析句子並標記高價值詞彙... + 正在分析句子... (AI 分析約需 3-5 秒) ) : ( - '🔍 分析句子(點擊查詢單字)' + '🔍 分析句子' )} - {/* 生成詞卡按鈕 */} - {/* 使用次數顯示 */}
@@ -335,7 +330,28 @@ function GenerateContent() { {/* 原始句子顯示 */}
-

句子分析

+
+

句子分析

+ {cacheStatus && ( +
+ {cacheStatus.isCached ? ( + <> + 💾 + 快取結果 + + ) : ( + <> + 🤖 + AI 分析 + + )} +
+ )} +

📝 用戶輸入

diff --git a/frontend/components/ClickableTextV2.tsx b/frontend/components/ClickableTextV2.tsx index 1a00297..337e2ab 100644 --- a/frontend/components/ClickableTextV2.tsx +++ b/frontend/components/ClickableTextV2.tsx @@ -55,6 +55,13 @@ export function ClickableTextV2({ position: { x: number, y: number } } | null>(null) + // 輔助函數:兼容大小寫屬性名稱 + const getWordProperty = (wordData: any, propName: string) => { + const lowerProp = propName.toLowerCase() + const upperProp = propName.charAt(0).toUpperCase() + propName.slice(1) + return wordData?.[lowerProp] || wordData?.[upperProp] + } + // 將文字分割成單字,保留空格 const words = text.split(/(\s+|[.,!?;:])/g) @@ -69,8 +76,9 @@ export function ClickableTextV2({ y: rect.top - 10 } - // 檢查是否為高價值詞彙(免費) - if (wordAnalysis.isHighValue) { + // 檢查是否為高價值詞彙(免費)- 支援兩種屬性格式 + const isHighValue = getWordProperty(wordAnalysis, 'isHighValue') + if (isHighValue) { setPopupPosition(position) setSelectedWord(cleanWord) onWordClick?.(cleanWord, wordAnalysis) @@ -146,13 +154,17 @@ export function ClickableTextV2({ const baseClass = "cursor-pointer transition-all duration-200 rounded relative mx-0.5 px-1 py-0.5" + // 支援兩種屬性名稱格式 + const isHighValue = getWordProperty(wordAnalysis, 'isHighValue') + const isPhrase = getWordProperty(wordAnalysis, 'isPhrase') + // 高價值片語(黃色系) - if (wordAnalysis.isHighValue && wordAnalysis.isPhrase) { + if (isHighValue && isPhrase) { return `${baseClass} bg-yellow-100 border-2 border-yellow-400 hover:bg-yellow-200 hover:shadow-sm transform hover:-translate-y-0.5` } // 高價值單字(綠色系) - if (wordAnalysis.isHighValue && !wordAnalysis.isPhrase) { + if (isHighValue && !isPhrase) { return `${baseClass} bg-green-100 border-2 border-green-400 hover:bg-green-200 hover:shadow-sm transform hover:-translate-y-0.5` } @@ -182,7 +194,7 @@ export function ClickableTextV2({ const className = getWordClass(word) const cleanWord = word.toLowerCase().replace(/[.,!?;:]/g, '') const wordAnalysis = analysis?.[cleanWord] - const isHighValue = wordAnalysis?.isHighValue + const isHighValue = wordAnalysis?.isHighValue || wordAnalysis?.IsHighValue return (

- {analysis[selectedWord].word} + {getWordProperty(analysis[selectedWord], 'word')}

@@ -265,10 +277,10 @@ export function ClickableTextV2({ {/* 詞性和發音 */}
- {analysis[selectedWord].partOfSpeech} + {getWordProperty(analysis[selectedWord], 'partOfSpeech')} - {analysis[selectedWord].pronunciation} + {getWordProperty(analysis[selectedWord], 'pronunciation')}