From cf40e937a191f595e1d71e5abd6d0f7bd9d382f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B3=E4=BD=A9=E5=BA=AD?= Date: Sun, 12 Oct 2025 00:18:24 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B8=AC=E9=A9=97=E6=99=82?= =?UTF-8?q?=E9=96=93=E3=80=81=E6=96=B0=E5=A2=9E=E6=B8=AC=E9=A9=97=E6=8F=90?= =?UTF-8?q?=E9=86=92=E3=80=81=E6=96=B0=E5=A2=9E=E6=88=AA=E6=AD=A2=E6=B8=AC?= =?UTF-8?q?=E9=A9=97=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../results/detail/[testResultId]/page.tsx | 19 ++- app/api/admin/test-results/detail/route.ts | 1 + app/api/test-results/combined/route.ts | 6 +- app/api/test-results/logic/route.ts | 8 +- app/tests/combined/page.tsx | 141 +++++++++++++++++- lib/database/models/combined_test_result.ts | 10 +- lib/database/models/test_result.ts | 10 +- 7 files changed, 176 insertions(+), 19 deletions(-) diff --git a/app/admin/results/detail/[testResultId]/page.tsx b/app/admin/results/detail/[testResultId]/page.tsx index d283e2e..d3adce6 100644 --- a/app/admin/results/detail/[testResultId]/page.tsx +++ b/app/admin/results/detail/[testResultId]/page.tsx @@ -26,6 +26,7 @@ interface TestResult { type: "logic" | "creative" | "combined" score: number completedAt: string + isTimeout?: boolean details?: { logicScore?: number creativeScore?: number @@ -437,11 +438,18 @@ function AdminResultDetailContent() { > {result.score} - 邏輯測試完成! + + 邏輯測試{result.isTimeout ? '(時間到)' : ''}完成! +
{scoreLevel.level} + {result.isTimeout && ( + + 時間到強制提交 + + )}

{scoreLevel.description}

@@ -482,11 +490,18 @@ function AdminResultDetailContent() { > {result.score}
- 創意測試完成! + + 創意測試{result.isTimeout ? '(時間到)' : ''}完成! +
{scoreLevel.level} + {result.isTimeout && ( + + 時間到強制提交 + + )}

{scoreLevel.description}

diff --git a/app/api/admin/test-results/detail/route.ts b/app/api/admin/test-results/detail/route.ts index ce4ed7c..8b2aaa8 100644 --- a/app/api/admin/test-results/detail/route.ts +++ b/app/api/admin/test-results/detail/route.ts @@ -37,6 +37,7 @@ export async function GET(request: NextRequest) { type: "combined", score: combinedResult.overall_score, completedAt: combinedResult.completed_at, + isTimeout: combinedResult.is_timeout || false, details: { logicScore: combinedResult.logic_score, creativeScore: combinedResult.creativity_score, diff --git a/app/api/test-results/combined/route.ts b/app/api/test-results/combined/route.ts index eaab2a2..27f5444 100644 --- a/app/api/test-results/combined/route.ts +++ b/app/api/test-results/combined/route.ts @@ -15,7 +15,8 @@ export async function POST(request: NextRequest) { logicBreakdown, creativityBreakdown, balanceScore, - completedAt + completedAt, + isTimeout = false } = body // 驗證必要欄位 @@ -51,7 +52,8 @@ export async function POST(request: NextRequest) { logic_breakdown: logicBreakdown || null, creativity_breakdown: creativityBreakdown || null, balance_score: balanceScore || 0, - completed_at: mysqlCompletedAt + completed_at: mysqlCompletedAt, + is_timeout: isTimeout }) if (!testResult) { diff --git a/app/api/test-results/logic/route.ts b/app/api/test-results/logic/route.ts index 2e4a697..95f8029 100644 --- a/app/api/test-results/logic/route.ts +++ b/app/api/test-results/logic/route.ts @@ -6,10 +6,11 @@ import { getAllLogicQuestions } from '@/lib/database/models/logic_question' export async function POST(request: NextRequest) { try { const body = await request.json() - const { + const { userId, answers, - completedAt + completedAt, + isTimeout = false } = body // 驗證必要欄位 @@ -75,7 +76,8 @@ export async function POST(request: NextRequest) { score: score, total_questions: questions.length, correct_answers: correctAnswers, - completed_at: mysqlCompletedAt + completed_at: mysqlCompletedAt, + is_timeout: isTimeout }) console.log('測試結果建立結果:', testResult) diff --git a/app/tests/combined/page.tsx b/app/tests/combined/page.tsx index d712e7d..ad10cd0 100644 --- a/app/tests/combined/page.tsx +++ b/app/tests/combined/page.tsx @@ -41,7 +41,8 @@ export default function CombinedTestPage() { const [currentQuestion, setCurrentQuestion] = useState(0) const [logicAnswers, setLogicAnswers] = useState>({}) const [creativeAnswers, setCreativeAnswers] = useState>({}) - const [timeRemaining, setTimeRemaining] = useState(45 * 60) // 45 minutes total + const [timeRemaining, setTimeRemaining] = useState(30 * 60) // 30 minutes total + const [hasShownWarning, setHasShownWarning] = useState(false) const [logicQuestions, setLogicQuestions] = useState([]) const [creativeQuestions, setCreativeQuestions] = useState([]) const [isLoading, setIsLoading] = useState(true) @@ -81,16 +82,27 @@ export default function CombinedTestPage() { const timer = setInterval(() => { setTimeRemaining((prev) => { - if (prev <= 1) { - handleSubmit() + const newTime = prev - 1 + + // 檢查是否剩餘5分鐘(300秒)且尚未顯示警告 + if (newTime <= 300 && !hasShownWarning) { + setHasShownWarning(true) + alert('⚠️ 注意:距離測試結束還有5分鐘,請盡快完成剩餘題目!') + } + + // 時間到,強制提交 + if (newTime <= 0) { + console.log('⏰ 時間到!強制提交測驗...') + handleTimeoutSubmit() return 0 } - return prev - 1 + + return newTime }) }, 1000) return () => clearInterval(timer) - }, [logicQuestions, creativeQuestions]) + }, [logicQuestions, creativeQuestions, hasShownWarning]) const formatTime = (seconds: number) => { const mins = Math.floor(seconds / 60) @@ -151,6 +163,121 @@ export default function CombinedTestPage() { } } + const handleTimeoutSubmit = async () => { + console.log('⏰ 時間到!強制提交綜合測試...') + console.log('用戶狀態:', user) + + if (!user) { + console.log('❌ 用戶未登入') + alert('⏰ 時間到!但用戶未登入,無法提交結果。') + return + } + + 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 + const processedCreativeAnswers: Record = {} + creativeQuestions.forEach((question, index) => { + const answer = creativeAnswers[index] || 1 + const processedScore = question.is_reverse ? 6 - answer : answer + creativityTotal += processedScore + processedCreativeAnswers[index] = processedScore + }) + const creativityMaxScore = creativeQuestions.length * 5 + const creativityScore = Math.round((creativityTotal / creativityMaxScore) * 100) + + // Calculate combined score + const combinedResult = calculateCombinedScore(logicScore, creativityScore) + + // Store results in localStorage with timeout flag + const results = { + type: "combined", + logicScore, + creativityScore, + overallScore: combinedResult.overallScore, + level: combinedResult.level, + description: combinedResult.description, + breakdown: combinedResult.breakdown, + logicAnswers, + creativeAnswers: processedCreativeAnswers, + logicCorrect, + creativityTotal, + creativityMaxScore, + completedAt: new Date().toISOString(), + isTimeout: true // 標記為時間到強制提交 + } + + localStorage.setItem("combinedTestResults", JSON.stringify(results)) + console.log('✅ 強制提交結果已儲存到 localStorage') + + // Upload to database with timeout flag + 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: processedCreativeAnswers + }, + balanceScore: combinedResult.breakdown.balance, + completedAt: new Date().toISOString(), + isTimeout: true // 標記為時間到強制提交 + } + 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) + } + + // 顯示時間到提示 + alert('⏰ 測試時間已到!系統已自動提交您的答案。') + router.push("/results/combined") + + } catch (error) { + console.error('❌ 強制提交測驗失敗:', error) + alert('⏰ 時間到!但提交失敗,請聯繫管理員。') + } finally { + setIsSubmitting(false) + } + } + const handleSubmit = async () => { console.log('🔍 開始提交綜合測試...') console.log('用戶狀態:', user) @@ -204,6 +331,7 @@ export default function CombinedTestPage() { creativityTotal, creativityMaxScore, completedAt: new Date().toISOString(), + isTimeout: false // 標記為正常提交 } localStorage.setItem("combinedTestResults", JSON.stringify(results)) @@ -229,7 +357,8 @@ export default function CombinedTestPage() { answers: processedCreativeAnswers }, balanceScore: combinedResult.breakdown.balance, - completedAt: new Date().toISOString() + completedAt: new Date().toISOString(), + isTimeout: false // 標記為正常提交 } console.log('上傳數據:', uploadData) diff --git a/lib/database/models/combined_test_result.ts b/lib/database/models/combined_test_result.ts index c65b213..04a4e40 100644 --- a/lib/database/models/combined_test_result.ts +++ b/lib/database/models/combined_test_result.ts @@ -13,6 +13,7 @@ export interface CombinedTestResult { creativity_breakdown: any | null // JSON 格式 balance_score: number completed_at: string + is_timeout: boolean created_at?: string } @@ -27,6 +28,7 @@ export interface CreateCombinedTestResultData { creativity_breakdown: any | null balance_score: number completed_at: string + is_timeout: boolean } // 建立綜合測試結果表(如果不存在) @@ -44,6 +46,7 @@ export async function createCombinedTestResultsTable(): Promise { creativity_breakdown JSON NULL, balance_score INT NOT NULL, completed_at TIMESTAMP NOT NULL, + is_timeout BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX idx_user_id (user_id), INDEX idx_completed_at (completed_at) @@ -62,8 +65,8 @@ export async function createCombinedTestResult(resultData: CreateCombinedTestRes INSERT INTO combined_test_results ( id, user_id, logic_score, creativity_score, overall_score, level, description, logic_breakdown, creativity_breakdown, - balance_score, completed_at - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + balance_score, completed_at, is_timeout + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ` const values = [ @@ -77,7 +80,8 @@ export async function createCombinedTestResult(resultData: CreateCombinedTestRes resultData.logic_breakdown ? JSON.stringify(resultData.logic_breakdown) : null, resultData.creativity_breakdown ? JSON.stringify(resultData.creativity_breakdown) : null, resultData.balance_score, - resultData.completed_at + resultData.completed_at, + resultData.is_timeout ] try { diff --git a/lib/database/models/test_result.ts b/lib/database/models/test_result.ts index 6b778c3..da5b36b 100644 --- a/lib/database/models/test_result.ts +++ b/lib/database/models/test_result.ts @@ -8,6 +8,7 @@ export interface TestResult { total_questions: number correct_answers: number completed_at: string + is_timeout: boolean created_at?: string } @@ -18,6 +19,7 @@ export interface CreateTestResultData { total_questions: number correct_answers: number completed_at: string + is_timeout: boolean } // 建立測試結果表(如果不存在) @@ -31,6 +33,7 @@ export async function createTestResultsTable(): Promise { total_questions INT NOT NULL, correct_answers INT NOT NULL, completed_at TIMESTAMP NOT NULL, + is_timeout BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX idx_user_id (user_id), INDEX idx_test_type (test_type), @@ -50,8 +53,8 @@ export async function createTestResult(resultData: CreateTestResultData): Promis const insertQuery = ` INSERT INTO test_results ( id, user_id, test_type, score, total_questions, - correct_answers, completed_at - ) VALUES (?, ?, ?, ?, ?, ?, ?) + correct_answers, completed_at, is_timeout + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ` await executeQuery(insertQuery, [ @@ -61,7 +64,8 @@ export async function createTestResult(resultData: CreateTestResultData): Promis resultData.score, resultData.total_questions, resultData.correct_answers, - resultData.completed_at + resultData.completed_at, + resultData.is_timeout ]) const result = await getTestResultById(id)