From f6610013ef278bc5894e74b647fc272319fae71e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B3=E4=BD=A9=E5=BA=AD?= Date: Mon, 29 Sep 2025 17:24:31 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=A6=E4=BD=9C=E7=B6=9C=E5=90=88=E9=A1=8C?= =?UTF-8?q?=E5=9E=8B=E7=B5=90=E6=9E=9C=E8=88=87=E8=B3=87=E6=96=99=E5=BA=AB?= =?UTF-8?q?=E6=95=B4=E5=90=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/test-results/combined/route.ts | 108 ++++++++++++ app/api/user/test-results/route.ts | 104 +++++++----- app/results/combined/page.tsx | 49 +++++- app/tests/combined/page.tsx | 149 ++++++++++++----- app/tests/creative/page.tsx | 2 +- app/tests/logic/page.tsx | 2 +- lib/database/init.ts | 4 + lib/database/models/combined_test_result.ts | 166 +++++++++++++++++++ scripts/check-all-combined-times.js | 67 ++++++++ scripts/check-all-test-times.js | 73 ++++++++ scripts/check-combined-test-results-table.js | 42 +++++ scripts/check-combined-test-results-table.ts | 42 +++++ scripts/check-combined-time.js | 64 +++++++ scripts/check-db-time-format.js | 78 +++++++++ scripts/check-raw-test-data.js | 72 ++++++++ scripts/check-timezone-issue.js | 68 ++++++++ scripts/fix-existing-times.js | 61 +++++++ scripts/test-combined-db-integration.js | 114 +++++++++++++ scripts/test-new-time-format.js | 49 ++++++ scripts/test-time-fix.js | 63 +++++++ scripts/test-user-results-with-combined.js | 56 +++++++ 21 files changed, 1341 insertions(+), 92 deletions(-) create mode 100644 app/api/test-results/combined/route.ts create mode 100644 lib/database/models/combined_test_result.ts create mode 100644 scripts/check-all-combined-times.js create mode 100644 scripts/check-all-test-times.js create mode 100644 scripts/check-combined-test-results-table.js create mode 100644 scripts/check-combined-test-results-table.ts create mode 100644 scripts/check-combined-time.js create mode 100644 scripts/check-db-time-format.js create mode 100644 scripts/check-raw-test-data.js create mode 100644 scripts/check-timezone-issue.js create mode 100644 scripts/fix-existing-times.js create mode 100644 scripts/test-combined-db-integration.js create mode 100644 scripts/test-new-time-format.js create mode 100644 scripts/test-time-fix.js create mode 100644 scripts/test-user-results-with-combined.js diff --git a/app/api/test-results/combined/route.ts b/app/api/test-results/combined/route.ts new file mode 100644 index 0000000..7b3f33c --- /dev/null +++ b/app/api/test-results/combined/route.ts @@ -0,0 +1,108 @@ +import { NextRequest, NextResponse } from 'next/server' +import { createCombinedTestResult } from '@/lib/database/models/combined_test_result' + +export async function POST(request: NextRequest) { + let body: any + try { + body = await request.json() + const { + userId, + logicScore, + creativityScore, + overallScore, + level, + description, + logicBreakdown, + creativityBreakdown, + balanceScore, + completedAt + } = body + + // 驗證必要欄位 + if (!userId || logicScore === undefined || creativityScore === undefined || + overallScore === undefined || !level || !completedAt) { + return NextResponse.json( + { success: false, error: '缺少必要欄位' }, + { status: 400 } + ) + } + + console.log('🔄 開始建立綜合測試結果...') + console.log('用戶ID:', userId) + console.log('邏輯分數:', logicScore) + console.log('創意分數:', creativityScore) + console.log('總分:', overallScore) + console.log('等級:', level) + + // 建立綜合測試結果 + const testResult = await createCombinedTestResult({ + user_id: userId, + logic_score: logicScore, + creativity_score: creativityScore, + overall_score: overallScore, + level: level, + description: description || null, + logic_breakdown: logicBreakdown || null, + creativity_breakdown: creativityBreakdown || null, + balance_score: balanceScore || 0, + completed_at: completedAt + }) + + if (!testResult) { + return NextResponse.json( + { success: false, error: '建立綜合測試結果失敗' }, + { status: 500 } + ) + } + + console.log('✅ 綜合測試結果建立成功,ID:', testResult.id) + + return NextResponse.json({ + success: true, + data: { + testResult + } + }) + + } catch (error) { + console.error('上傳綜合測試結果失敗:', error) + return NextResponse.json( + { + success: false, + error: '伺服器錯誤', + details: error instanceof Error ? error.message : '未知錯誤' + }, + { status: 500 } + ) + } +} + +export async function GET(request: NextRequest) { + try { + const { searchParams } = new URL(request.url) + const userId = searchParams.get('userId') + + if (!userId) { + return NextResponse.json( + { success: false, error: '缺少用戶ID' }, + { status: 400 } + ) + } + + // 獲取用戶的綜合測試結果 + const { getCombinedTestResultsByUserId } = await import('@/lib/database/models/combined_test_result') + const results = await getCombinedTestResultsByUserId(userId) + + return NextResponse.json({ + success: true, + data: results + }) + + } catch (error) { + console.error('獲取綜合測試結果失敗:', error) + return NextResponse.json( + { success: false, error: '伺服器錯誤' }, + { status: 500 } + ) + } +} diff --git a/app/api/user/test-results/route.ts b/app/api/user/test-results/route.ts index 3bf86e9..194681b 100644 --- a/app/api/user/test-results/route.ts +++ b/app/api/user/test-results/route.ts @@ -1,5 +1,7 @@ import { NextRequest, NextResponse } from 'next/server' -import { getTestResultsByUserId } from '@/lib/database/models/test_result' +import { getTestResultsByUserId, TestResult as DBTestResult } from '@/lib/database/models/test_result' +import { getCombinedTestResultsByUserId, CombinedTestResult } from '@/lib/database/models/combined_test_result' +import { findUserById } from '@/lib/database/models/user' export async function GET(request: NextRequest) { try { @@ -13,38 +15,21 @@ export async function GET(request: NextRequest) { ) } - // 獲取用戶的所有測試結果 - const allResults = await getTestResultsByUserId(userId) + // 獲取邏輯和創意測試結果 + const testResults = await getTestResultsByUserId(userId) - if (allResults.length === 0) { - return NextResponse.json({ - success: true, - data: { - results: [], - stats: { - totalTests: 0, - averageScore: 0, - bestScore: 0, - lastTestDate: null, - testCounts: { - logic: 0, - creative: 0, - combined: 0 - } - } - } - }) - } + // 獲取綜合測試結果 + const combinedResults = await getCombinedTestResultsByUserId(userId) // 按測試類型分組,只保留每種類型的最新結果 const latestResults = { - logic: allResults.filter(r => r.test_type === 'logic').sort((a, b) => + logic: testResults.filter(r => r.test_type === 'logic').sort((a, b) => new Date(b.completed_at).getTime() - new Date(a.completed_at).getTime() )[0], - creative: allResults.filter(r => r.test_type === 'creative').sort((a, b) => + creative: testResults.filter(r => r.test_type === 'creative').sort((a, b) => new Date(b.completed_at).getTime() - new Date(a.completed_at).getTime() )[0], - combined: allResults.filter(r => r.test_type === 'combined').sort((a, b) => + combined: combinedResults.sort((a, b) => new Date(b.completed_at).getTime() - new Date(a.completed_at).getTime() )[0] } @@ -59,7 +44,10 @@ export async function GET(request: NextRequest) { // 平均分數:基於每種類型測試的最新分數計算 const latestScores = Object.values(latestResults) .filter(result => result !== undefined) - .map(result => result.score) + .map(result => { + // 綜合測試結果使用 overall_score,其他使用 score + return 'overall_score' in result ? result.overall_score : result.score + }) const averageScore = latestScores.length > 0 ? Math.round(latestScores.reduce((sum, score) => sum + score, 0) / latestScores.length) @@ -69,32 +57,58 @@ export async function GET(request: NextRequest) { const bestScore = latestScores.length > 0 ? Math.max(...latestScores) : 0 // 最近測試日期:基於所有測試結果 - const lastTestDate = allResults.length > 0 - ? allResults.sort((a, b) => - new Date(b.completed_at).getTime() - new Date(a.completed_at).getTime() - )[0]?.completed_at || null - : null + const allTestDates = [ + ...testResults.map(r => r.completed_at), + ...combinedResults.map(r => r.completed_at) + ].sort((a, b) => new Date(b).getTime() - new Date(a).getTime()) + + const lastTestDate = allTestDates.length > 0 ? allTestDates[0] : null // 計算各類型測試次數 const testCounts = { - logic: allResults.filter(r => r.test_type === 'logic').length, - creative: allResults.filter(r => r.test_type === 'creative').length, - combined: allResults.filter(r => r.test_type === 'combined').length + logic: testResults.filter(r => r.test_type === 'logic').length, + creative: testResults.filter(r => r.test_type === 'creative').length, + combined: combinedResults.length } // 轉換為前端需要的格式 - const formattedResults = results.map(result => ({ - type: result.test_type, - score: result.score, - completedAt: result.completed_at, - testCount: testCounts[result.test_type as keyof typeof testCounts], - details: { - id: result.id, - total_questions: result.total_questions, - correct_answers: result.correct_answers, - created_at: result.created_at + const formattedResults = results.map(result => { + // 檢查是否為綜合測試結果 + if ('overall_score' in result) { + // 綜合測試結果 + const combinedResult = result as CombinedTestResult + return { + type: 'combined' as const, + score: combinedResult.overall_score, + completedAt: combinedResult.completed_at, + testCount: testCounts.combined, + details: { + id: combinedResult.id, + logic_score: combinedResult.logic_score, + creativity_score: combinedResult.creativity_score, + level: combinedResult.level, + description: combinedResult.description, + balance_score: combinedResult.balance_score, + created_at: combinedResult.created_at + } + } + } else { + // 邏輯或創意測試結果 + const testResult = result as DBTestResult + return { + type: testResult.test_type, + score: testResult.score, + completedAt: testResult.completed_at, + testCount: testCounts[testResult.test_type as keyof typeof testCounts], + details: { + id: testResult.id, + total_questions: testResult.total_questions, + correct_answers: testResult.correct_answers, + created_at: testResult.created_at + } + } } - })) + }) // 按完成時間排序(最新的在前) formattedResults.sort((a, b) => new Date(b.completedAt).getTime() - new Date(a.completedAt).getTime()) diff --git a/app/results/combined/page.tsx b/app/results/combined/page.tsx index c3f70f2..0d231f7 100644 --- a/app/results/combined/page.tsx +++ b/app/results/combined/page.tsx @@ -8,6 +8,7 @@ import { Progress } from "@/components/ui/progress" import { Brain, Lightbulb, BarChart3, Home, RotateCcw, TrendingUp, Target, Award } from "lucide-react" import Link from "next/link" import { getRecommendations } from "@/lib/utils/score-calculator" +import { useAuth } from "@/lib/hooks/use-auth" interface LogicQuestion { id: number @@ -51,6 +52,7 @@ interface CombinedTestResults { } export default function CombinedResultsPage() { + const { user } = useAuth() const [results, setResults] = useState(null) const [logicQuestions, setLogicQuestions] = useState([]) const [creativeQuestions, setCreativeQuestions] = useState([]) @@ -58,11 +60,43 @@ export default function CombinedResultsPage() { useEffect(() => { const loadData = async () => { + if (!user) return + try { - // Load test results - const savedResults = localStorage.getItem("combinedTestResults") - if (savedResults) { - setResults(JSON.parse(savedResults)) + // 從資料庫獲取最新的綜合測試結果 + const response = await fetch(`/api/test-results/combined?userId=${user.id}`) + const data = await response.json() + + if (data.success && data.data.length > 0) { + // 取最新的結果 + const latestResult = data.data[0] + + // 轉換為前端需要的格式 + setResults({ + type: "combined", + logicScore: latestResult.logic_score, + creativityScore: latestResult.creativity_score, + overallScore: latestResult.overall_score, + level: latestResult.level, + description: latestResult.description || "", + breakdown: { + logic: latestResult.logic_score, + creativity: latestResult.creativity_score, + balance: latestResult.balance_score + }, + logicAnswers: latestResult.logic_breakdown?.answers || {}, + creativeAnswers: latestResult.creativity_breakdown?.answers || {}, + logicCorrect: latestResult.logic_breakdown?.correct || 0, + creativityTotal: latestResult.creativity_breakdown?.total || 0, + creativityMaxScore: latestResult.creativity_breakdown?.maxScore || 0, + completedAt: latestResult.completed_at + }) + } else { + // 如果沒有資料庫結果,回退到 localStorage + const savedResults = localStorage.getItem("combinedTestResults") + if (savedResults) { + setResults(JSON.parse(savedResults)) + } } // Load questions from database @@ -80,13 +114,18 @@ export default function CombinedResultsPage() { } } catch (error) { console.error('Error loading data:', error) + // 回退到 localStorage + const savedResults = localStorage.getItem("combinedTestResults") + if (savedResults) { + setResults(JSON.parse(savedResults)) + } } finally { setIsLoading(false) } } loadData() - }, []) + }, [user]) if (isLoading) { return ( diff --git a/app/tests/combined/page.tsx b/app/tests/combined/page.tsx index 626a3f7..c290a7e 100644 --- a/app/tests/combined/page.tsx +++ b/app/tests/combined/page.tsx @@ -9,6 +9,7 @@ import { Label } from "@/components/ui/label" import { Progress } from "@/components/ui/progress" import { useRouter } from "next/navigation" import { calculateCombinedScore } from "@/lib/utils/score-calculator" +import { useAuth } from "@/lib/hooks/use-auth" interface LogicQuestion { id: number @@ -35,6 +36,7 @@ type TestPhase = "logic" | "creative" | "completed" export default function CombinedTestPage() { const router = useRouter() + const { user } = useAuth() const [phase, setPhase] = useState("logic") const [currentQuestion, setCurrentQuestion] = useState(0) const [logicAnswers, setLogicAnswers] = useState>({}) @@ -43,6 +45,7 @@ export default function CombinedTestPage() { const [logicQuestions, setLogicQuestions] = useState([]) const [creativeQuestions, setCreativeQuestions] = useState([]) const [isLoading, setIsLoading] = useState(true) + const [isSubmitting, setIsSubmitting] = useState(false) // Load questions from database useEffect(() => { @@ -148,47 +151,113 @@ export default function CombinedTestPage() { } } - const handleSubmit = () => { - // Calculate logic score - let logicCorrect = 0 - logicQuestions.forEach((question, index) => { - if (logicAnswers[index] === question.correct_answer) { - logicCorrect++ - } - }) - const logicScore = Math.round((logicCorrect / logicQuestions.length) * 100) + const handleSubmit = async () => { + console.log('🔍 開始提交綜合測試...') + console.log('用戶狀態:', user) - // Calculate creativity score - let creativityTotal = 0 - creativeQuestions.forEach((question, index) => { - const answer = creativeAnswers[index] || 1 - creativityTotal += question.is_reverse ? 6 - answer : answer - }) - const creativityMaxScore = creativeQuestions.length * 5 - const creativityScore = Math.round((creativityTotal / creativityMaxScore) * 100) - - // Calculate combined score - const combinedResult = calculateCombinedScore(logicScore, creativityScore) - - // Store results - const results = { - type: "combined", - logicScore, - creativityScore, - overallScore: combinedResult.overallScore, - level: combinedResult.level, - description: combinedResult.description, - breakdown: combinedResult.breakdown, - logicAnswers, - creativeAnswers, - logicCorrect, - creativityTotal, - creativityMaxScore, - completedAt: new Date().toISOString(), + if (!user) { + console.log('❌ 用戶未登入') + alert('請先登入') + return } - localStorage.setItem("combinedTestResults", JSON.stringify(results)) - router.push("/results/combined") + console.log('✅ 用戶已登入,用戶ID:', user.id) + setIsSubmitting(true) + + try { + // Calculate logic score + let logicCorrect = 0 + logicQuestions.forEach((question, index) => { + if (logicAnswers[index] === question.correct_answer) { + logicCorrect++ + } + }) + const logicScore = Math.round((logicCorrect / logicQuestions.length) * 100) + + // Calculate creativity score + let creativityTotal = 0 + creativeQuestions.forEach((question, index) => { + const answer = creativeAnswers[index] || 1 + creativityTotal += question.is_reverse ? 6 - answer : answer + }) + const creativityMaxScore = creativeQuestions.length * 5 + const creativityScore = Math.round((creativityTotal / creativityMaxScore) * 100) + + // Calculate combined score + const combinedResult = calculateCombinedScore(logicScore, creativityScore) + + // Store results in localStorage (for backward compatibility) + const results = { + type: "combined", + logicScore, + creativityScore, + overallScore: combinedResult.overallScore, + level: combinedResult.level, + description: combinedResult.description, + breakdown: combinedResult.breakdown, + logicAnswers, + creativeAnswers, + logicCorrect, + creativityTotal, + creativityMaxScore, + completedAt: new Date().toISOString(), + } + + localStorage.setItem("combinedTestResults", JSON.stringify(results)) + console.log('✅ 結果已儲存到 localStorage') + + // Upload to database + console.log('🔄 開始上傳到資料庫...') + const uploadData = { + userId: user.id, + logicScore, + creativityScore, + overallScore: combinedResult.overallScore, + level: combinedResult.level, + description: combinedResult.description, + logicBreakdown: { + correct: logicCorrect, + total: logicQuestions.length, + answers: logicAnswers + }, + creativityBreakdown: { + total: creativityTotal, + maxScore: creativityMaxScore, + answers: creativeAnswers + }, + balanceScore: combinedResult.breakdown.balance, + completedAt: new Date().toISOString() + } + console.log('上傳數據:', uploadData) + + const uploadResponse = await fetch('/api/test-results/combined', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(uploadData) + }) + + console.log('📡 API 響應狀態:', uploadResponse.status) + const uploadResult = await uploadResponse.json() + console.log('📡 API 響應內容:', uploadResult) + + if (uploadResult.success) { + console.log('✅ 綜合測試結果已上傳到資料庫') + console.log('測試結果ID:', uploadResult.data.testResult.id) + } else { + console.error('❌ 上傳到資料庫失敗:', uploadResult.error) + // 即使上傳失敗,也繼續顯示結果 + } + + router.push("/results/combined") + + } catch (error) { + console.error('❌ 提交測驗失敗:', error) + alert('提交測驗失敗,請重試') + } finally { + setIsSubmitting(false) + } } const currentQuestions = getCurrentQuestions() @@ -389,8 +458,8 @@ export default function CombinedTestPage() { {isLastQuestion ? ( - ) : (