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 ? (
) : (