From aa34d2d078fa21aa1ab36e17176286df2baf861a 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 02:56:23 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=A6=E4=BD=9C=E9=82=8F=E8=BC=AF=E9=A1=8C?= =?UTF-8?q?=E7=B5=90=E6=9E=9C=E8=88=87=E8=B3=87=E6=96=99=E5=BA=AB=E6=95=B4?= =?UTF-8?q?=E5=90=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/test-db/route.ts | 27 ++++ app/api/test-results/logic/route.ts | 149 +++++++++++++++++++++++ app/api/user/test-results/route.ts | 123 +++++++++++++++++++ app/results/combined/page.tsx | 2 +- app/results/creative/page.tsx | 2 +- app/results/logic/page.tsx | 2 +- app/results/page.tsx | 84 +++++-------- app/tests/combined/page.tsx | 10 +- app/tests/logic/page.tsx | 108 ++++++++++++---- lib/database/init.ts | 8 ++ lib/database/models/logic_question.ts | 4 +- lib/database/models/logic_test_answer.ts | 140 +++++++++++++++++++++ lib/database/models/test_result.ts | 130 ++++++++++++++++++++ package.json | 12 ++ scripts/check-db-tables.js | 75 ++++++++++++ scripts/check-test-results.js | 102 ++++++++++++++++ scripts/check-user-auth.js | 43 +++++++ scripts/check-user-exists.js | 48 ++++++++ scripts/fix-logic-answers-table.js | 40 ++++++ scripts/test-api-direct.js | 73 +++++++++++ scripts/test-create-result.js | 72 +++++++++++ scripts/test-db-models.js | 73 +++++++++++ scripts/test-direct-insert.js | 101 +++++++++++++++ scripts/test-logic-api.js | 66 ++++++++++ scripts/test-logic-db-upload.js | 100 +++++++++++++++ scripts/test-multi-type-results.js | 73 +++++++++++ scripts/test-other-user-results.js | 64 ++++++++++ scripts/test-simple-insert.js | 61 ++++++++++ scripts/test-taiwan-time.js | 21 ++++ scripts/test-user-results-api.js | 57 +++++++++ scripts/test-user-results-detailed.js | 57 +++++++++ 31 files changed, 1839 insertions(+), 88 deletions(-) create mode 100644 app/api/test-db/route.ts create mode 100644 app/api/test-results/logic/route.ts create mode 100644 app/api/user/test-results/route.ts create mode 100644 lib/database/models/logic_test_answer.ts create mode 100644 lib/database/models/test_result.ts create mode 100644 scripts/check-db-tables.js create mode 100644 scripts/check-test-results.js create mode 100644 scripts/check-user-auth.js create mode 100644 scripts/check-user-exists.js create mode 100644 scripts/fix-logic-answers-table.js create mode 100644 scripts/test-api-direct.js create mode 100644 scripts/test-create-result.js create mode 100644 scripts/test-db-models.js create mode 100644 scripts/test-direct-insert.js create mode 100644 scripts/test-logic-api.js create mode 100644 scripts/test-logic-db-upload.js create mode 100644 scripts/test-multi-type-results.js create mode 100644 scripts/test-other-user-results.js create mode 100644 scripts/test-simple-insert.js create mode 100644 scripts/test-taiwan-time.js create mode 100644 scripts/test-user-results-api.js create mode 100644 scripts/test-user-results-detailed.js diff --git a/app/api/test-db/route.ts b/app/api/test-db/route.ts new file mode 100644 index 0000000..2bcc07b --- /dev/null +++ b/app/api/test-db/route.ts @@ -0,0 +1,27 @@ +import { NextResponse } from 'next/server' +import { testConnection } from '@/lib/database/connection' + +export async function GET() { + try { + const isConnected = await testConnection() + + if (isConnected) { + return NextResponse.json({ + success: true, + message: '資料庫連接正常' + }) + } else { + return NextResponse.json({ + success: false, + message: '資料庫連接失敗' + }, { status: 500 }) + } + } catch (error) { + console.error('資料庫測試失敗:', error) + return NextResponse.json({ + success: false, + message: '資料庫測試失敗', + error: error instanceof Error ? error.message : '未知錯誤' + }, { status: 500 }) + } +} diff --git a/app/api/test-results/logic/route.ts b/app/api/test-results/logic/route.ts new file mode 100644 index 0000000..4591e57 --- /dev/null +++ b/app/api/test-results/logic/route.ts @@ -0,0 +1,149 @@ +import { NextRequest, NextResponse } from 'next/server' +import { createTestResult, getTestResultsByUserId } from '@/lib/database/models/test_result' +import { createLogicTestAnswers } from '@/lib/database/models/logic_test_answer' +import { getAllLogicQuestions } from '@/lib/database/models/logic_question' + +export async function POST(request: NextRequest) { + try { + const body = await request.json() + const { + userId, + answers, + completedAt + } = body + + // 驗證必要欄位 + if (!userId || !answers || !completedAt) { + return NextResponse.json( + { success: false, error: '缺少必要欄位' }, + { status: 400 } + ) + } + + // 獲取邏輯題目 + const questions = await getAllLogicQuestions() + if (questions.length === 0) { + return NextResponse.json( + { success: false, error: '無法獲取題目' }, + { status: 500 } + ) + } + + // 計算分數 + let correctAnswers = 0 + const answerRecords = [] + + for (let i = 0; i < questions.length; i++) { + const question = questions[i] + const userAnswer = answers[i] || '' + const isCorrect = userAnswer === question.correct_answer + + if (isCorrect) { + correctAnswers++ + } + + answerRecords.push({ + test_result_id: '', // 稍後填入 + question_id: question.id, + user_answer: userAnswer as 'A' | 'B' | 'C' | 'D' | 'E', + is_correct: isCorrect + }) + } + + const score = Math.round((correctAnswers / questions.length) * 100) + + // 建立測試結果 + console.log('🔄 開始建立測試結果...') + console.log('測試結果數據:', { + user_id: userId, + test_type: 'logic', + score: score, + total_questions: questions.length, + correct_answers: correctAnswers, + completed_at: completedAt + }) + + const testResult = await createTestResult({ + user_id: userId, + test_type: 'logic', + score: score, + total_questions: questions.length, + correct_answers: correctAnswers, + completed_at: completedAt + }) + + console.log('測試結果建立結果:', testResult) + + if (!testResult) { + console.error('❌ 建立測試結果失敗') + return NextResponse.json( + { success: false, error: '建立測試結果失敗' }, + { status: 500 } + ) + } + + console.log('✅ 測試結果建立成功:', testResult.id) + + // 更新答案記錄的 test_result_id + answerRecords.forEach(record => { + record.test_result_id = testResult.id + }) + + // 建立答案記錄 + const answerResults = await createLogicTestAnswers(answerRecords) + + return NextResponse.json({ + success: true, + data: { + testResult, + answerCount: answerResults.length + } + }) + + } catch (error) { + console.error('上傳邏輯測驗結果失敗:', error) + console.error('錯誤詳情:', { + message: error instanceof Error ? error.message : '未知錯誤', + stack: error instanceof Error ? error.stack : undefined, + body: body + }) + 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 results = await getTestResultsByUserId(userId) + const logicResults = results.filter(result => result.test_type === 'logic') + + return NextResponse.json({ + success: true, + data: logicResults + }) + + } 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 new file mode 100644 index 0000000..3bf86e9 --- /dev/null +++ b/app/api/user/test-results/route.ts @@ -0,0 +1,123 @@ +import { NextRequest, NextResponse } from 'next/server' +import { getTestResultsByUserId } from '@/lib/database/models/test_result' + +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 allResults = 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 latestResults = { + logic: allResults.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) => + new Date(b.completed_at).getTime() - new Date(a.completed_at).getTime() + )[0], + combined: allResults.filter(r => r.test_type === 'combined').sort((a, b) => + new Date(b.completed_at).getTime() - new Date(a.completed_at).getTime() + )[0] + } + + // 只保留有結果的測試類型 + const results = Object.values(latestResults).filter(result => result !== undefined) + + // 計算統計數據 + // 完成測試:基於每種類型測試是否有最新結果 + const totalTests = Object.values(latestResults).filter(result => result !== undefined).length + + // 平均分數:基於每種類型測試的最新分數計算 + const latestScores = Object.values(latestResults) + .filter(result => result !== undefined) + .map(result => result.score) + + const averageScore = latestScores.length > 0 + ? Math.round(latestScores.reduce((sum, score) => sum + score, 0) / latestScores.length) + : 0 + + // 最高分數:基於每種類型測試的最新分數 + 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 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 + } + + // 轉換為前端需要的格式 + 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 + } + })) + + // 按完成時間排序(最新的在前) + formattedResults.sort((a, b) => new Date(b.completedAt).getTime() - new Date(a.completedAt).getTime()) + + return NextResponse.json({ + success: true, + data: { + results: formattedResults, + stats: { + totalTests, + averageScore, + bestScore, + lastTestDate, + testCounts + } + } + }) + + } catch (error) { + console.error('獲取用戶測試結果失敗:', error) + return NextResponse.json( + { success: false, error: '伺服器錯誤' }, + { status: 500 } + ) + } +} diff --git a/app/results/combined/page.tsx b/app/results/combined/page.tsx index f059ef9..c3f70f2 100644 --- a/app/results/combined/page.tsx +++ b/app/results/combined/page.tsx @@ -204,7 +204,7 @@ export default function CombinedResultsPage() {

綜合能力測試結果

- 完成時間:{new Date(results.completedAt).toLocaleString("zh-TW")} + 完成時間:{new Date(results.completedAt).toLocaleString("zh-TW", { timeZone: "Asia/Taipei" })}

diff --git a/app/results/creative/page.tsx b/app/results/creative/page.tsx index a2165e7..2997f0a 100644 --- a/app/results/creative/page.tsx +++ b/app/results/creative/page.tsx @@ -113,7 +113,7 @@ export default function CreativeResultsPage() {

創意能力測試結果

- 完成時間:{new Date(results.completedAt).toLocaleString("zh-TW")} + 完成時間:{new Date(results.completedAt).toLocaleString("zh-TW", { timeZone: "Asia/Taipei" })}

diff --git a/app/results/logic/page.tsx b/app/results/logic/page.tsx index 154ad49..53491ff 100644 --- a/app/results/logic/page.tsx +++ b/app/results/logic/page.tsx @@ -138,7 +138,7 @@ export default function LogicResultsPage() {

邏輯思維測試結果

- 完成時間:{new Date(results.completedAt).toLocaleString("zh-TW")} + 完成時間:{new Date(results.completedAt).toLocaleString("zh-TW", { timeZone: "Asia/Taipei" })}

diff --git a/app/results/page.tsx b/app/results/page.tsx index 21e5185..8e32ec5 100644 --- a/app/results/page.tsx +++ b/app/results/page.tsx @@ -14,6 +14,7 @@ interface TestResult { type: "logic" | "creative" | "combined" score: number completedAt: string + testCount?: number details?: any } @@ -33,66 +34,34 @@ function ResultsContent() { averageScore: 0, bestScore: 0, lastTestDate: null as string | null, + testCounts: { + logic: 0, + creative: 0, + combined: 0 + } }) useEffect(() => { - // Load all test results from localStorage - const logicResults = localStorage.getItem("logicTestResults") - const creativeResults = localStorage.getItem("creativeTestResults") - const combinedResults = localStorage.getItem("combinedTestResults") + const loadUserResults = async () => { + if (!user) return - const allResults: TestResult[] = [] + try { + const response = await fetch(`/api/user/test-results?userId=${user.id}`) + const data = await response.json() - if (logicResults) { - const data = JSON.parse(logicResults) - allResults.push({ - type: "logic", - score: data.score, - completedAt: data.completedAt, - details: data, - }) + if (data.success) { + setResults(data.data.results) + setStats(data.data.stats) + } else { + console.error('Failed to load user results:', data.error) + } + } catch (error) { + console.error('Error loading user results:', error) + } } - if (creativeResults) { - const data = JSON.parse(creativeResults) - allResults.push({ - type: "creative", - score: data.score, - completedAt: data.completedAt, - details: data, - }) - } - - if (combinedResults) { - const data = JSON.parse(combinedResults) - allResults.push({ - type: "combined", - score: data.overallScore, - completedAt: data.completedAt, - details: data, - }) - } - - // Sort by completion date (newest first) - allResults.sort((a, b) => new Date(b.completedAt).getTime() - new Date(a.completedAt).getTime()) - - setResults(allResults) - - // Calculate statistics - if (allResults.length > 0) { - const totalScore = allResults.reduce((sum, result) => sum + result.score, 0) - const averageScore = Math.round(totalScore / allResults.length) - const bestScore = Math.max(...allResults.map((r) => r.score)) - const lastTestDate = allResults[0].completedAt - - setStats({ - totalTests: allResults.length, - averageScore, - bestScore, - lastTestDate, - }) - } - }, []) + loadUserResults() + }, [user]) const getTestTypeInfo = (type: string) => { switch (type) { @@ -263,7 +232,7 @@ function ResultsContent() {
- {stats.lastTestDate ? new Date(stats.lastTestDate).toLocaleDateString("zh-TW") : "無"} + {stats.lastTestDate ? new Date(stats.lastTestDate).toLocaleDateString("zh-TW", { timeZone: "Asia/Taipei" }) : "無"}
最近測試
@@ -295,8 +264,13 @@ function ResultsContent() {

{testInfo.name}

- 完成時間:{new Date(result.completedAt).toLocaleString("zh-TW")} + 完成時間:{new Date(result.completedAt).toLocaleString("zh-TW", { timeZone: "Asia/Taipei" })}

+ {result.testCount && result.testCount > 1 && ( +

+ 已測驗 {result.testCount} 次 +

+ )}
diff --git a/app/tests/combined/page.tsx b/app/tests/combined/page.tsx index 85f6b6c..626a3f7 100644 --- a/app/tests/combined/page.tsx +++ b/app/tests/combined/page.tsx @@ -290,11 +290,11 @@ export default function CombinedTestPage() { {phase === "logic" ? // Logic question options [ - { value: 'A', text: currentQ.option_a }, - { value: 'B', text: currentQ.option_b }, - { value: 'C', text: currentQ.option_c }, - { value: 'D', text: currentQ.option_d }, - { value: 'E', text: currentQ.option_e } + { value: 'A', text: (currentQ as LogicQuestion).option_a }, + { value: 'B', text: (currentQ as LogicQuestion).option_b }, + { value: 'C', text: (currentQ as LogicQuestion).option_c }, + { value: 'D', text: (currentQ as LogicQuestion).option_d }, + { value: 'E', text: (currentQ as LogicQuestion).option_e } ].map((option, index) => (
([]) const [currentQuestion, setCurrentQuestion] = useState(0) const [answers, setAnswers] = useState>({}) const [timeRemaining, setTimeRemaining] = useState(20 * 60) // 20 minutes in seconds const [isLoading, setIsLoading] = useState(true) + const [isSubmitting, setIsSubmitting] = useState(false) // Load questions from database useEffect(() => { @@ -93,29 +96,88 @@ export default function LogicTestPage() { } } - const handleSubmit = () => { - // Calculate score - let correctAnswers = 0 - questions.forEach((question, index) => { - if (answers[index] === question.correct_answer) { - correctAnswers++ - } - }) - - const score = Math.round((correctAnswers / questions.length) * 100) - - // Store results in localStorage - const results = { - type: "logic", - score, - correctAnswers, - totalQuestions: questions.length, - answers, - completedAt: new Date().toISOString(), + const handleSubmit = async () => { + console.log('🔍 開始提交邏輯測驗...') + console.log('用戶狀態:', user) + + if (!user) { + console.log('❌ 用戶未登入') + alert('請先登入') + return } - localStorage.setItem("logicTestResults", JSON.stringify(results)) - router.push("/results/logic") + console.log('✅ 用戶已登入,用戶ID:', user.id) + setIsSubmitting(true) + + try { + // Calculate score + let correctAnswers = 0 + questions.forEach((question, index) => { + if (answers[index] === question.correct_answer) { + correctAnswers++ + } + }) + + const score = Math.round((correctAnswers / questions.length) * 100) + const completedAt = new Date().toISOString().replace('Z', '').replace('T', ' ') + + console.log('📊 測驗結果計算:') + console.log('答對題數:', correctAnswers) + console.log('總題數:', questions.length) + console.log('分數:', score) + console.log('完成時間:', completedAt) + + // Store results in localStorage (for backward compatibility) + const results = { + type: "logic", + score, + correctAnswers, + totalQuestions: questions.length, + answers, + completedAt, + } + + localStorage.setItem("logicTestResults", JSON.stringify(results)) + console.log('✅ 結果已儲存到 localStorage') + + // Upload to database + console.log('🔄 開始上傳到資料庫...') + const uploadData = { + userId: user.id, + answers: Object.values(answers), + completedAt: completedAt + } + console.log('上傳數據:', uploadData) + + const uploadResponse = await fetch('/api/test-results/logic', { + 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) + console.log('答案記錄數量:', uploadResult.data.answerCount) + } else { + console.error('❌ 上傳到資料庫失敗:', uploadResult.error) + // 即使上傳失敗,也繼續顯示結果 + } + + router.push("/results/logic") + + } catch (error) { + console.error('❌ 提交測驗失敗:', error) + alert('提交測驗失敗,請重試') + } finally { + setIsSubmitting(false) + } } if (isLoading) { @@ -204,11 +266,11 @@ export default function LogicTestPage() { {isLastQuestion ? ( ) : (