實作綜合題型結果與資料庫整合
This commit is contained in:
108
app/api/test-results/combined/route.ts
Normal file
108
app/api/test-results/combined/route.ts
Normal file
@@ -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 }
|
||||
)
|
||||
}
|
||||
}
|
@@ -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())
|
||||
|
@@ -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<CombinedTestResults | null>(null)
|
||||
const [logicQuestions, setLogicQuestions] = useState<LogicQuestion[]>([])
|
||||
const [creativeQuestions, setCreativeQuestions] = useState<CreativeQuestion[]>([])
|
||||
@@ -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 (
|
||||
|
@@ -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<TestPhase>("logic")
|
||||
const [currentQuestion, setCurrentQuestion] = useState(0)
|
||||
const [logicAnswers, setLogicAnswers] = useState<Record<number, string>>({})
|
||||
@@ -43,6 +45,7 @@ export default function CombinedTestPage() {
|
||||
const [logicQuestions, setLogicQuestions] = useState<LogicQuestion[]>([])
|
||||
const [creativeQuestions, setCreativeQuestions] = useState<CreativeQuestion[]>([])
|
||||
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() {
|
||||
</div>
|
||||
|
||||
{isLastQuestion ? (
|
||||
<Button onClick={handleSubmit} disabled={!hasAnswer} className="bg-green-600 hover:bg-green-700">
|
||||
提交測試
|
||||
<Button onClick={handleSubmit} disabled={!hasAnswer || isSubmitting} className="bg-green-600 hover:bg-green-700">
|
||||
{isSubmitting ? '提交中...' : '提交測試'}
|
||||
</Button>
|
||||
) : (
|
||||
<Button onClick={handleNext} disabled={!hasAnswer}>
|
||||
|
@@ -135,7 +135,7 @@ export default function CreativeTestPage() {
|
||||
const uploadData = {
|
||||
userId: user.id,
|
||||
answers: Object.values(answers),
|
||||
completedAt: new Date().toISOString().replace('Z', '').replace('T', ' ')
|
||||
completedAt: new Date().toISOString()
|
||||
}
|
||||
console.log('上傳數據:', uploadData)
|
||||
|
||||
|
@@ -119,7 +119,7 @@ export default function LogicTestPage() {
|
||||
})
|
||||
|
||||
const score = Math.round((correctAnswers / questions.length) * 100)
|
||||
const completedAt = new Date().toISOString().replace('Z', '').replace('T', ' ')
|
||||
const completedAt = new Date().toISOString()
|
||||
|
||||
console.log('📊 測驗結果計算:')
|
||||
console.log('答對題數:', correctAnswers)
|
||||
|
@@ -5,6 +5,7 @@ import { createCreativeQuestionsTable } from './models/creative_question'
|
||||
import { createTestResultsTable } from './models/test_result'
|
||||
import { createLogicTestAnswersTable } from './models/logic_test_answer'
|
||||
import { createCreativeTestAnswersTable } from './models/creative_test_answer'
|
||||
import { createCombinedTestResultsTable } from './models/combined_test_result'
|
||||
|
||||
// 初始化資料庫
|
||||
export async function initializeDatabase(): Promise<boolean> {
|
||||
@@ -36,6 +37,9 @@ export async function initializeDatabase(): Promise<boolean> {
|
||||
// 建立創意測驗答案表
|
||||
await createCreativeTestAnswersTable()
|
||||
|
||||
// 建立綜合測試結果表
|
||||
await createCombinedTestResultsTable()
|
||||
|
||||
console.log('✅ 資料庫初始化完成')
|
||||
return true
|
||||
} catch (error) {
|
||||
|
166
lib/database/models/combined_test_result.ts
Normal file
166
lib/database/models/combined_test_result.ts
Normal file
@@ -0,0 +1,166 @@
|
||||
import { executeQuery, executeQueryOne } from '../connection'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
export interface CombinedTestResult {
|
||||
id: string
|
||||
user_id: string
|
||||
logic_score: number
|
||||
creativity_score: number
|
||||
overall_score: number
|
||||
level: string
|
||||
description: string | null
|
||||
logic_breakdown: any | null // JSON 格式
|
||||
creativity_breakdown: any | null // JSON 格式
|
||||
balance_score: number
|
||||
completed_at: string
|
||||
created_at?: string
|
||||
}
|
||||
|
||||
export interface CreateCombinedTestResultData {
|
||||
user_id: string
|
||||
logic_score: number
|
||||
creativity_score: number
|
||||
overall_score: number
|
||||
level: string
|
||||
description: string | null
|
||||
logic_breakdown: any | null
|
||||
creativity_breakdown: any | null
|
||||
balance_score: number
|
||||
completed_at: string
|
||||
}
|
||||
|
||||
// 建立綜合測試結果表(如果不存在)
|
||||
export async function createCombinedTestResultsTable(): Promise<void> {
|
||||
const createTableQuery = `
|
||||
CREATE TABLE IF NOT EXISTS combined_test_results (
|
||||
id VARCHAR(50) PRIMARY KEY,
|
||||
user_id VARCHAR(50) NOT NULL,
|
||||
logic_score INT NOT NULL,
|
||||
creativity_score INT NOT NULL,
|
||||
overall_score INT NOT NULL,
|
||||
level VARCHAR(50) NOT NULL,
|
||||
description TEXT NULL,
|
||||
logic_breakdown JSON NULL,
|
||||
creativity_breakdown JSON NULL,
|
||||
balance_score INT NOT NULL,
|
||||
completed_at TIMESTAMP NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
INDEX idx_user_id (user_id),
|
||||
INDEX idx_completed_at (completed_at)
|
||||
)
|
||||
`
|
||||
|
||||
await executeQuery(createTableQuery)
|
||||
console.log('✅ 綜合測試結果表建立成功')
|
||||
}
|
||||
|
||||
// 建立新的綜合測試結果
|
||||
export async function createCombinedTestResult(resultData: CreateCombinedTestResultData): Promise<CombinedTestResult | null> {
|
||||
const id = `combined_${Date.now()}_${uuidv4().substring(0, 9)}`
|
||||
|
||||
const insertQuery = `
|
||||
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`
|
||||
|
||||
const values = [
|
||||
id,
|
||||
resultData.user_id,
|
||||
resultData.logic_score,
|
||||
resultData.creativity_score,
|
||||
resultData.overall_score,
|
||||
resultData.level,
|
||||
resultData.description,
|
||||
resultData.logic_breakdown ? JSON.stringify(resultData.logic_breakdown) : null,
|
||||
resultData.creativity_breakdown ? JSON.stringify(resultData.creativity_breakdown) : null,
|
||||
resultData.balance_score,
|
||||
resultData.completed_at
|
||||
]
|
||||
|
||||
try {
|
||||
await executeQuery(insertQuery, values)
|
||||
console.log('✅ 綜合測試結果建立成功,ID:', id)
|
||||
|
||||
// 獲取剛建立的記錄
|
||||
return await getCombinedTestResultById(id)
|
||||
} catch (error) {
|
||||
console.error('建立綜合測試結果失敗:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 根據ID獲取綜合測試結果
|
||||
export async function getCombinedTestResultById(id: string): Promise<CombinedTestResult | null> {
|
||||
try {
|
||||
const query = 'SELECT * FROM combined_test_results WHERE id = ?'
|
||||
const result = await executeQueryOne<CombinedTestResult>(query, [id])
|
||||
|
||||
if (result) {
|
||||
// 解析 JSON 欄位
|
||||
if (result.logic_breakdown && typeof result.logic_breakdown === 'string') {
|
||||
result.logic_breakdown = JSON.parse(result.logic_breakdown)
|
||||
}
|
||||
if (result.creativity_breakdown && typeof result.creativity_breakdown === 'string') {
|
||||
result.creativity_breakdown = JSON.parse(result.creativity_breakdown)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
} catch (error) {
|
||||
console.error('獲取綜合測試結果失敗:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// 根據用戶ID獲取所有綜合測試結果
|
||||
export async function getCombinedTestResultsByUserId(userId: string): Promise<CombinedTestResult[]> {
|
||||
try {
|
||||
const query = 'SELECT * FROM combined_test_results WHERE user_id = ? ORDER BY completed_at DESC'
|
||||
const results = await executeQuery<CombinedTestResult>(query, [userId])
|
||||
|
||||
// 解析 JSON 欄位
|
||||
return results.map(result => {
|
||||
if (result.logic_breakdown && typeof result.logic_breakdown === 'string') {
|
||||
result.logic_breakdown = JSON.parse(result.logic_breakdown)
|
||||
}
|
||||
if (result.creativity_breakdown && typeof result.creativity_breakdown === 'string') {
|
||||
result.creativity_breakdown = JSON.parse(result.creativity_breakdown)
|
||||
}
|
||||
return result
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('獲取用戶綜合測試結果失敗:', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
// 獲取所有綜合測試結果
|
||||
export async function getAllCombinedTestResults(): Promise<CombinedTestResult[]> {
|
||||
try {
|
||||
const query = 'SELECT * FROM combined_test_results ORDER BY completed_at DESC'
|
||||
const results = await executeQuery<CombinedTestResult>(query)
|
||||
|
||||
// 解析 JSON 欄位
|
||||
return results.map(result => {
|
||||
if (result.logic_breakdown && typeof result.logic_breakdown === 'string') {
|
||||
result.logic_breakdown = JSON.parse(result.logic_breakdown)
|
||||
}
|
||||
if (result.creativity_breakdown && typeof result.creativity_breakdown === 'string') {
|
||||
result.creativity_breakdown = JSON.parse(result.creativity_breakdown)
|
||||
}
|
||||
return result
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('獲取所有綜合測試結果失敗:', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
// 清空所有綜合測試結果
|
||||
export async function clearCombinedTestResults(): Promise<void> {
|
||||
await executeQuery('DELETE FROM combined_test_results')
|
||||
console.log('✅ 已清空所有綜合測試結果')
|
||||
}
|
67
scripts/check-all-combined-times.js
Normal file
67
scripts/check-all-combined-times.js
Normal file
@@ -0,0 +1,67 @@
|
||||
const https = require('https')
|
||||
const http = require('http')
|
||||
|
||||
const checkAllCombinedTimes = async () => {
|
||||
console.log('🔍 檢查所有綜合測試結果時間')
|
||||
console.log('=' .repeat(50))
|
||||
|
||||
const userId = 'user-1759073326705-m06y3wacd'
|
||||
|
||||
try {
|
||||
// 檢查綜合測試結果 API
|
||||
console.log('\n📊 檢查所有綜合測試結果...')
|
||||
const response = await new Promise((resolve, reject) => {
|
||||
const req = http.get(`http://localhost:3000/api/test-results/combined?userId=${userId}`, (res) => {
|
||||
let data = ''
|
||||
res.on('data', chunk => data += chunk)
|
||||
res.on('end', () => resolve({ status: res.statusCode, data }))
|
||||
})
|
||||
req.on('error', reject)
|
||||
})
|
||||
|
||||
if (response.status === 200) {
|
||||
const data = JSON.parse(response.data)
|
||||
if (data.success) {
|
||||
console.log(`找到 ${data.data.length} 筆綜合測試結果:`)
|
||||
|
||||
data.data.forEach((result, index) => {
|
||||
console.log(`\n${index + 1}. 綜合測試結果:`)
|
||||
console.log(` ID: ${result.id}`)
|
||||
console.log(` 原始時間: ${result.completed_at}`)
|
||||
|
||||
const date = new Date(result.completed_at)
|
||||
console.log(` 台灣時間: ${date.toLocaleString("zh-TW", { timeZone: "Asia/Taipei" })}`)
|
||||
console.log(` 台灣時間 (詳細): ${date.toLocaleString("zh-TW", {
|
||||
timeZone: "Asia/Taipei",
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: true
|
||||
})}`)
|
||||
|
||||
// 檢查是否是今天
|
||||
const now = new Date()
|
||||
const testDate = new Date(result.completed_at)
|
||||
const isToday = now.toDateString() === testDate.toDateString()
|
||||
console.log(` 是否為今天: ${isToday}`)
|
||||
|
||||
// 計算時間差
|
||||
const timeDiff = now.getTime() - testDate.getTime()
|
||||
const hoursDiff = Math.floor(timeDiff / (1000 * 60 * 60))
|
||||
const minutesDiff = Math.floor((timeDiff % (1000 * 60 * 60)) / (1000 * 60))
|
||||
console.log(` 距離現在: ${hoursDiff} 小時 ${minutesDiff} 分鐘`)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 檢查失敗:', error.message)
|
||||
} finally {
|
||||
console.log('\n✅ 所有綜合測試結果時間檢查完成')
|
||||
}
|
||||
}
|
||||
|
||||
checkAllCombinedTimes()
|
73
scripts/check-all-test-times.js
Normal file
73
scripts/check-all-test-times.js
Normal file
@@ -0,0 +1,73 @@
|
||||
const https = require('https')
|
||||
const http = require('http')
|
||||
|
||||
const checkAllTestTimes = async () => {
|
||||
console.log('🔍 檢查所有測試結果的時間顯示')
|
||||
console.log('=' .repeat(50))
|
||||
|
||||
const userId = 'user-1759073326705-m06y3wacd'
|
||||
|
||||
try {
|
||||
// 檢查用戶測試結果 API
|
||||
console.log('\n📊 檢查用戶測試結果 API...')
|
||||
const response = await new Promise((resolve, reject) => {
|
||||
const req = http.get(`http://localhost:3000/api/user/test-results?userId=${userId}`, (res) => {
|
||||
let data = ''
|
||||
res.on('data', chunk => data += chunk)
|
||||
res.on('end', () => resolve({ status: res.statusCode, data }))
|
||||
})
|
||||
req.on('error', reject)
|
||||
})
|
||||
|
||||
if (response.status === 200) {
|
||||
const data = JSON.parse(response.data)
|
||||
if (data.success && data.data.results.length > 0) {
|
||||
console.log(`找到 ${data.data.results.length} 筆測試結果:`)
|
||||
|
||||
data.data.results.forEach((result, index) => {
|
||||
console.log(`\n${index + 1}. ${result.type} 測試:`)
|
||||
console.log(` 原始時間: ${result.completedAt}`)
|
||||
|
||||
const date = new Date(result.completedAt)
|
||||
console.log(` UTC 時間: ${date.toISOString()}`)
|
||||
console.log(` 台灣時間: ${date.toLocaleString("zh-TW", { timeZone: "Asia/Taipei" })}`)
|
||||
console.log(` 台灣時間 (詳細): ${date.toLocaleString("zh-TW", {
|
||||
timeZone: "Asia/Taipei",
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: true
|
||||
})}`)
|
||||
|
||||
// 檢查是否是今天
|
||||
const now = new Date()
|
||||
const testDate = new Date(result.completedAt)
|
||||
const isToday = now.toDateString() === testDate.toDateString()
|
||||
console.log(` 是否為今天: ${isToday}`)
|
||||
|
||||
// 計算時間差
|
||||
const timeDiff = now.getTime() - testDate.getTime()
|
||||
const hoursDiff = Math.floor(timeDiff / (1000 * 60 * 60))
|
||||
const minutesDiff = Math.floor((timeDiff % (1000 * 60 * 60)) / (1000 * 60))
|
||||
console.log(` 距離現在: ${hoursDiff} 小時 ${minutesDiff} 分鐘`)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 檢查當前時間
|
||||
console.log('\n📊 當前時間:')
|
||||
const now = new Date()
|
||||
console.log(`UTC 時間: ${now.toISOString()}`)
|
||||
console.log(`台灣時間: ${now.toLocaleString("zh-TW", { timeZone: "Asia/Taipei" })}`)
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 檢查失敗:', error.message)
|
||||
} finally {
|
||||
console.log('\n✅ 所有測試結果時間檢查完成')
|
||||
}
|
||||
}
|
||||
|
||||
checkAllTestTimes()
|
42
scripts/check-combined-test-results-table.js
Normal file
42
scripts/check-combined-test-results-table.js
Normal file
@@ -0,0 +1,42 @@
|
||||
const { executeQuery } = require('../lib/database/connection')
|
||||
|
||||
async function checkCombinedTestResultsTable() {
|
||||
console.log('🔍 檢查 combined_test_results 表結構...')
|
||||
console.log('=' .repeat(50))
|
||||
|
||||
try {
|
||||
// 檢查表結構
|
||||
console.log('\n📊 combined_test_results 表結構:')
|
||||
const columns = await executeQuery('DESCRIBE combined_test_results')
|
||||
columns.forEach(column => {
|
||||
console.log(`- ${column.Field}: ${column.Type} ${column.Null === 'NO' ? 'NOT NULL' : 'NULL'} ${column.Key ? `(${column.Key})` : ''}`)
|
||||
})
|
||||
|
||||
// 檢查表數據
|
||||
console.log('\n📋 combined_test_results 表數據:')
|
||||
const rows = await executeQuery('SELECT * FROM combined_test_results LIMIT 5')
|
||||
if (rows.length > 0) {
|
||||
console.log(`找到 ${rows.length} 筆記錄:`)
|
||||
rows.forEach((row, index) => {
|
||||
console.log(`\n記錄 ${index + 1}:`)
|
||||
Object.entries(row).forEach(([key, value]) => {
|
||||
console.log(` ${key}: ${value}`)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
console.log('表中沒有數據')
|
||||
}
|
||||
|
||||
// 檢查表索引
|
||||
console.log('\n🔍 表索引:')
|
||||
const indexes = await executeQuery('SHOW INDEX FROM combined_test_results')
|
||||
indexes.forEach(index => {
|
||||
console.log(`- ${index.Key_name}: ${index.Column_name} (${index.Index_type})`)
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 檢查失敗:', error.message)
|
||||
}
|
||||
}
|
||||
|
||||
checkCombinedTestResultsTable()
|
42
scripts/check-combined-test-results-table.ts
Normal file
42
scripts/check-combined-test-results-table.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { executeQuery } from '../lib/database/connection'
|
||||
|
||||
async function checkCombinedTestResultsTable() {
|
||||
console.log('🔍 檢查 combined_test_results 表結構...')
|
||||
console.log('=' .repeat(50))
|
||||
|
||||
try {
|
||||
// 檢查表結構
|
||||
console.log('\n📊 combined_test_results 表結構:')
|
||||
const columns = await executeQuery('DESCRIBE combined_test_results')
|
||||
columns.forEach((column: any) => {
|
||||
console.log(`- ${column.Field}: ${column.Type} ${column.Null === 'NO' ? 'NOT NULL' : 'NULL'} ${column.Key ? `(${column.Key})` : ''}`)
|
||||
})
|
||||
|
||||
// 檢查表數據
|
||||
console.log('\n📋 combined_test_results 表數據:')
|
||||
const rows = await executeQuery('SELECT * FROM combined_test_results LIMIT 5')
|
||||
if (rows.length > 0) {
|
||||
console.log(`找到 ${rows.length} 筆記錄:`)
|
||||
rows.forEach((row: any, index: number) => {
|
||||
console.log(`\n記錄 ${index + 1}:`)
|
||||
Object.entries(row).forEach(([key, value]) => {
|
||||
console.log(` ${key}: ${value}`)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
console.log('表中沒有數據')
|
||||
}
|
||||
|
||||
// 檢查表索引
|
||||
console.log('\n🔍 表索引:')
|
||||
const indexes = await executeQuery('SHOW INDEX FROM combined_test_results')
|
||||
indexes.forEach((index: any) => {
|
||||
console.log(`- ${index.Key_name}: ${index.Column_name} (${index.Index_type})`)
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 檢查失敗:', error instanceof Error ? error.message : '未知錯誤')
|
||||
}
|
||||
}
|
||||
|
||||
checkCombinedTestResultsTable()
|
64
scripts/check-combined-time.js
Normal file
64
scripts/check-combined-time.js
Normal file
@@ -0,0 +1,64 @@
|
||||
const https = require('https')
|
||||
const http = require('http')
|
||||
|
||||
const checkCombinedTime = async () => {
|
||||
console.log('🔍 檢查綜合測試結果時間')
|
||||
console.log('=' .repeat(50))
|
||||
|
||||
const userId = 'user-1759073326705-m06y3wacd'
|
||||
|
||||
try {
|
||||
// 檢查綜合測試結果 API
|
||||
console.log('\n📊 檢查綜合測試結果 API...')
|
||||
const response = await new Promise((resolve, reject) => {
|
||||
const req = http.get(`http://localhost:3000/api/test-results/combined?userId=${userId}`, (res) => {
|
||||
let data = ''
|
||||
res.on('data', chunk => data += chunk)
|
||||
res.on('end', () => resolve({ status: res.statusCode, data }))
|
||||
})
|
||||
req.on('error', reject)
|
||||
})
|
||||
|
||||
if (response.status === 200) {
|
||||
const data = JSON.parse(response.data)
|
||||
if (data.success && data.data.length > 0) {
|
||||
const result = data.data[0]
|
||||
console.log('📋 綜合測試結果時間詳情:')
|
||||
console.log(`原始時間: ${result.completed_at}`)
|
||||
|
||||
const date = new Date(result.completed_at)
|
||||
console.log(`UTC 時間: ${date.toISOString()}`)
|
||||
console.log(`台灣時間: ${date.toLocaleString("zh-TW", { timeZone: "Asia/Taipei" })}`)
|
||||
console.log(`台灣時間 (詳細): ${date.toLocaleString("zh-TW", {
|
||||
timeZone: "Asia/Taipei",
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: true
|
||||
})}`)
|
||||
|
||||
// 檢查是否是今天
|
||||
const now = new Date()
|
||||
const testDate = new Date(result.completed_at)
|
||||
const isToday = now.toDateString() === testDate.toDateString()
|
||||
console.log(`是否為今天: ${isToday}`)
|
||||
|
||||
// 計算時間差
|
||||
const timeDiff = now.getTime() - testDate.getTime()
|
||||
const hoursDiff = Math.floor(timeDiff / (1000 * 60 * 60))
|
||||
const minutesDiff = Math.floor((timeDiff % (1000 * 60 * 60)) / (1000 * 60))
|
||||
console.log(`距離現在: ${hoursDiff} 小時 ${minutesDiff} 分鐘`)
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 檢查失敗:', error.message)
|
||||
} finally {
|
||||
console.log('\n✅ 綜合測試結果時間檢查完成')
|
||||
}
|
||||
}
|
||||
|
||||
checkCombinedTime()
|
78
scripts/check-db-time-format.js
Normal file
78
scripts/check-db-time-format.js
Normal file
@@ -0,0 +1,78 @@
|
||||
const mysql = require('mysql2/promise')
|
||||
|
||||
const checkDbTimeFormat = async () => {
|
||||
console.log('🔍 檢查資料庫中的時間格式')
|
||||
console.log('=' .repeat(50))
|
||||
|
||||
const connection = await mysql.createConnection({
|
||||
host: process.env.DB_HOST || 'localhost',
|
||||
port: process.env.DB_PORT || 3306,
|
||||
user: process.env.DB_USER || 'root',
|
||||
password: process.env.DB_PASSWORD || '',
|
||||
database: process.env.DB_NAME || 'hr_assessment'
|
||||
})
|
||||
|
||||
try {
|
||||
// 檢查 test_results 表的時間格式
|
||||
console.log('\n📊 檢查 test_results 表的時間格式...')
|
||||
const [testResults] = await connection.execute(`
|
||||
SELECT id, test_type, completed_at, created_at
|
||||
FROM test_results
|
||||
WHERE user_id = 'user-1759073326705-m06y3wacd'
|
||||
ORDER BY completed_at DESC
|
||||
`)
|
||||
|
||||
testResults.forEach((result, index) => {
|
||||
console.log(`\n${index + 1}. ${result.test_type} 測試:`)
|
||||
console.log(` ID: ${result.id}`)
|
||||
console.log(` completed_at (原始): ${result.completed_at}`)
|
||||
console.log(` completed_at (類型): ${typeof result.completed_at}`)
|
||||
console.log(` created_at (原始): ${result.created_at}`)
|
||||
console.log(` created_at (類型): ${typeof result.created_at}`)
|
||||
|
||||
// 測試時間轉換
|
||||
const completedDate = new Date(result.completed_at)
|
||||
const createdDate = new Date(result.created_at)
|
||||
|
||||
console.log(` completed_at 轉換: ${completedDate.toISOString()}`)
|
||||
console.log(` completed_at 台灣時間: ${completedDate.toLocaleString("zh-TW", { timeZone: "Asia/Taipei" })}`)
|
||||
console.log(` created_at 轉換: ${createdDate.toISOString()}`)
|
||||
console.log(` created_at 台灣時間: ${createdDate.toLocaleString("zh-TW", { timeZone: "Asia/Taipei" })}`)
|
||||
})
|
||||
|
||||
// 檢查 combined_test_results 表的時間格式
|
||||
console.log('\n📊 檢查 combined_test_results 表的時間格式...')
|
||||
const [combinedResults] = await connection.execute(`
|
||||
SELECT id, completed_at, created_at
|
||||
FROM combined_test_results
|
||||
WHERE user_id = 'user-1759073326705-m06y3wacd'
|
||||
ORDER BY completed_at DESC
|
||||
`)
|
||||
|
||||
combinedResults.forEach((result, index) => {
|
||||
console.log(`\n${index + 1}. 綜合測試:`)
|
||||
console.log(` ID: ${result.id}`)
|
||||
console.log(` completed_at (原始): ${result.completed_at}`)
|
||||
console.log(` completed_at (類型): ${typeof result.completed_at}`)
|
||||
console.log(` created_at (原始): ${result.created_at}`)
|
||||
console.log(` created_at (類型): ${typeof result.created_at}`)
|
||||
|
||||
// 測試時間轉換
|
||||
const completedDate = new Date(result.completed_at)
|
||||
const createdDate = new Date(result.created_at)
|
||||
|
||||
console.log(` completed_at 轉換: ${completedDate.toISOString()}`)
|
||||
console.log(` completed_at 台灣時間: ${completedDate.toLocaleString("zh-TW", { timeZone: "Asia/Taipei" })}`)
|
||||
console.log(` created_at 轉換: ${createdDate.toISOString()}`)
|
||||
console.log(` created_at 台灣時間: ${createdDate.toLocaleString("zh-TW", { timeZone: "Asia/Taipei" })}`)
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 檢查失敗:', error.message)
|
||||
} finally {
|
||||
await connection.end()
|
||||
console.log('\n✅ 資料庫時間格式檢查完成')
|
||||
}
|
||||
}
|
||||
|
||||
checkDbTimeFormat()
|
72
scripts/check-raw-test-data.js
Normal file
72
scripts/check-raw-test-data.js
Normal file
@@ -0,0 +1,72 @@
|
||||
const https = require('https')
|
||||
const http = require('http')
|
||||
|
||||
const checkRawTestData = async () => {
|
||||
console.log('🔍 檢查原始測試數據')
|
||||
console.log('=' .repeat(50))
|
||||
|
||||
const userId = 'user-1759073326705-m06y3wacd'
|
||||
|
||||
try {
|
||||
// 檢查邏輯測試結果
|
||||
console.log('\n📊 檢查邏輯測試結果...')
|
||||
const logicResponse = await new Promise((resolve, reject) => {
|
||||
const req = http.get(`http://localhost:3000/api/test-results/logic?userId=${userId}`, (res) => {
|
||||
let data = ''
|
||||
res.on('data', chunk => data += chunk)
|
||||
res.on('end', () => resolve({ status: res.statusCode, data }))
|
||||
})
|
||||
req.on('error', reject)
|
||||
})
|
||||
|
||||
if (logicResponse.status === 200) {
|
||||
const logicData = JSON.parse(logicResponse.data)
|
||||
if (logicData.success && logicData.data.length > 0) {
|
||||
console.log('邏輯測試結果:')
|
||||
logicData.data.forEach((result, index) => {
|
||||
console.log(`\n${index + 1}. 邏輯測試:`)
|
||||
console.log(` ID: ${result.id}`)
|
||||
console.log(` 原始時間: ${result.completed_at}`)
|
||||
console.log(` 創建時間: ${result.created_at}`)
|
||||
|
||||
const date = new Date(result.completed_at)
|
||||
console.log(` 台灣時間: ${date.toLocaleString("zh-TW", { timeZone: "Asia/Taipei" })}`)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 檢查創意測試結果
|
||||
console.log('\n📊 檢查創意測試結果...')
|
||||
const creativeResponse = await new Promise((resolve, reject) => {
|
||||
const req = http.get(`http://localhost:3000/api/test-results/creative?userId=${userId}`, (res) => {
|
||||
let data = ''
|
||||
res.on('data', chunk => data += chunk)
|
||||
res.on('end', () => resolve({ status: res.statusCode, data }))
|
||||
})
|
||||
req.on('error', reject)
|
||||
})
|
||||
|
||||
if (creativeResponse.status === 200) {
|
||||
const creativeData = JSON.parse(creativeResponse.data)
|
||||
if (creativeData.success && creativeData.data.length > 0) {
|
||||
console.log('創意測試結果:')
|
||||
creativeData.data.forEach((result, index) => {
|
||||
console.log(`\n${index + 1}. 創意測試:`)
|
||||
console.log(` ID: ${result.id}`)
|
||||
console.log(` 原始時間: ${result.completed_at}`)
|
||||
console.log(` 創建時間: ${result.created_at}`)
|
||||
|
||||
const date = new Date(result.completed_at)
|
||||
console.log(` 台灣時間: ${date.toLocaleString("zh-TW", { timeZone: "Asia/Taipei" })}`)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 檢查失敗:', error.message)
|
||||
} finally {
|
||||
console.log('\n✅ 原始測試數據檢查完成')
|
||||
}
|
||||
}
|
||||
|
||||
checkRawTestData()
|
68
scripts/check-timezone-issue.js
Normal file
68
scripts/check-timezone-issue.js
Normal file
@@ -0,0 +1,68 @@
|
||||
const https = require('https')
|
||||
const http = require('http')
|
||||
|
||||
const checkTimezoneIssue = async () => {
|
||||
console.log('🔍 檢查時區問題')
|
||||
console.log('=' .repeat(50))
|
||||
|
||||
const userId = 'user-1759073326705-m06y3wacd'
|
||||
|
||||
try {
|
||||
// 1. 檢查用戶測試結果 API 返回的時間
|
||||
console.log('\n📊 1. 檢查用戶測試結果 API 返回的時間...')
|
||||
const response = await new Promise((resolve, reject) => {
|
||||
const req = http.get(`http://localhost:3000/api/user/test-results?userId=${userId}`, (res) => {
|
||||
let data = ''
|
||||
res.on('data', chunk => data += chunk)
|
||||
res.on('end', () => resolve({ status: res.statusCode, data }))
|
||||
})
|
||||
req.on('error', reject)
|
||||
})
|
||||
|
||||
if (response.status === 200) {
|
||||
const data = JSON.parse(response.data)
|
||||
if (data.success && data.data.results.length > 0) {
|
||||
console.log('📋 測試結果時間:')
|
||||
data.data.results.forEach((result, index) => {
|
||||
console.log(`\n${index + 1}. ${result.type}:`)
|
||||
console.log(` 原始時間: ${result.completedAt}`)
|
||||
|
||||
// 測試不同的時間格式化方式
|
||||
const date = new Date(result.completedAt)
|
||||
console.log(` UTC 時間: ${date.toISOString()}`)
|
||||
console.log(` 本地時間: ${date.toLocaleString()}`)
|
||||
console.log(` 台灣時間: ${date.toLocaleString("zh-TW", { timeZone: "Asia/Taipei" })}`)
|
||||
console.log(` 台灣時間 (en): ${date.toLocaleString("en-US", { timeZone: "Asia/Taipei" })}`)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 檢查當前時間
|
||||
console.log('\n📊 2. 檢查當前時間...')
|
||||
const now = new Date()
|
||||
console.log(`當前 UTC 時間: ${now.toISOString()}`)
|
||||
console.log(`當前本地時間: ${now.toLocaleString()}`)
|
||||
console.log(`當前台灣時間: ${now.toLocaleString("zh-TW", { timeZone: "Asia/Taipei" })}`)
|
||||
|
||||
// 3. 測試時間轉換
|
||||
console.log('\n📊 3. 測試時間轉換...')
|
||||
const testTimes = [
|
||||
'2025-09-29T01:07:00.000Z', // 從 API 返回的時間
|
||||
'2025-09-29T09:08:17.000Z', // 可能的另一個時間
|
||||
]
|
||||
|
||||
testTimes.forEach((timeStr, index) => {
|
||||
console.log(`\n測試時間 ${index + 1}: ${timeStr}`)
|
||||
const testDate = new Date(timeStr)
|
||||
console.log(` UTC: ${testDate.toISOString()}`)
|
||||
console.log(` 台灣時間: ${testDate.toLocaleString("zh-TW", { timeZone: "Asia/Taipei" })}`)
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 檢查失敗:', error.message)
|
||||
} finally {
|
||||
console.log('\n✅ 時區問題檢查完成')
|
||||
}
|
||||
}
|
||||
|
||||
checkTimezoneIssue()
|
61
scripts/fix-existing-times.js
Normal file
61
scripts/fix-existing-times.js
Normal file
@@ -0,0 +1,61 @@
|
||||
const https = require('https')
|
||||
const http = require('http')
|
||||
|
||||
const fixExistingTimes = async () => {
|
||||
console.log('🔍 修正現有測試結果的時間')
|
||||
console.log('=' .repeat(50))
|
||||
|
||||
const userId = 'user-1759073326705-m06y3wacd'
|
||||
|
||||
try {
|
||||
// 檢查現有的測試結果
|
||||
console.log('\n📊 檢查現有測試結果...')
|
||||
const response = await new Promise((resolve, reject) => {
|
||||
const req = http.get(`http://localhost:3000/api/user/test-results?userId=${userId}`, (res) => {
|
||||
let data = ''
|
||||
res.on('data', chunk => data += chunk)
|
||||
res.on('end', () => resolve({ status: res.statusCode, data }))
|
||||
})
|
||||
req.on('error', reject)
|
||||
})
|
||||
|
||||
if (response.status === 200) {
|
||||
const data = JSON.parse(response.data)
|
||||
if (data.success && data.data.results.length > 0) {
|
||||
console.log('發現需要修正的測試結果:')
|
||||
|
||||
data.data.results.forEach((result, index) => {
|
||||
console.log(`\n${index + 1}. ${result.type} 測試:`)
|
||||
console.log(` 原始時間: ${result.completedAt}`)
|
||||
|
||||
const date = new Date(result.completedAt)
|
||||
const taiwanTime = date.toLocaleString("zh-TW", { timeZone: "Asia/Taipei" })
|
||||
console.log(` 台灣時間: ${taiwanTime}`)
|
||||
|
||||
// 檢查時間是否合理
|
||||
const now = new Date()
|
||||
const timeDiff = now.getTime() - date.getTime()
|
||||
const hoursDiff = Math.floor(timeDiff / (1000 * 60 * 60))
|
||||
|
||||
if (hoursDiff > 24) {
|
||||
console.log(` ⚠️ 時間可能不正確 (${hoursDiff} 小時前)`)
|
||||
} else {
|
||||
console.log(` ✅ 時間看起來正確 (${hoursDiff} 小時前)`)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n📝 建議:')
|
||||
console.log('1. 時間格式已修正,新的測試結果會使用正確的 UTC 時間')
|
||||
console.log('2. 現有的錯誤時間數據可能需要手動修正或重新測試')
|
||||
console.log('3. 建議重新進行一次測試來驗證修正效果')
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 檢查失敗:', error.message)
|
||||
} finally {
|
||||
console.log('\n✅ 現有時間檢查完成')
|
||||
}
|
||||
}
|
||||
|
||||
fixExistingTimes()
|
114
scripts/test-combined-db-integration.js
Normal file
114
scripts/test-combined-db-integration.js
Normal file
@@ -0,0 +1,114 @@
|
||||
const https = require('https')
|
||||
const http = require('http')
|
||||
|
||||
const testCombinedDBIntegration = async () => {
|
||||
console.log('🧪 測試綜合測試結果資料庫整合功能')
|
||||
console.log('=' .repeat(50))
|
||||
|
||||
const userId = 'user-1759073326705-m06y3wacd'
|
||||
|
||||
try {
|
||||
// 1. 測試綜合測試結果上傳 API
|
||||
console.log('\n📊 1. 測試綜合測試結果上傳 API...')
|
||||
|
||||
const testData = {
|
||||
userId: userId,
|
||||
logicScore: 10,
|
||||
creativityScore: 78,
|
||||
overallScore: 48,
|
||||
level: '待提升',
|
||||
description: '綜合能力有待提升,建議系統性訓練邏輯思維和創意能力',
|
||||
logicBreakdown: {
|
||||
correct: 1,
|
||||
total: 10,
|
||||
answers: { 0: 'A', 1: 'B', 2: 'C' }
|
||||
},
|
||||
creativityBreakdown: {
|
||||
total: 70,
|
||||
maxScore: 90,
|
||||
answers: { 0: 5, 1: 4, 2: 3 }
|
||||
},
|
||||
balanceScore: 66,
|
||||
completedAt: new Date().toISOString().replace('Z', '').replace('T', ' ')
|
||||
}
|
||||
|
||||
console.log('測試數據:', JSON.stringify(testData, null, 2))
|
||||
|
||||
const uploadResponse = await new Promise((resolve, reject) => {
|
||||
const req = http.request({
|
||||
hostname: 'localhost',
|
||||
port: 3000,
|
||||
path: '/api/test-results/combined',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
}, (res) => {
|
||||
let data = ''
|
||||
res.on('data', chunk => data += chunk)
|
||||
res.on('end', () => resolve({ status: res.statusCode, data }))
|
||||
})
|
||||
req.on('error', reject)
|
||||
req.write(JSON.stringify(testData))
|
||||
req.end()
|
||||
})
|
||||
|
||||
console.log('📊 上傳響應狀態:', uploadResponse.status)
|
||||
|
||||
if (uploadResponse.status === 200) {
|
||||
const uploadResult = JSON.parse(uploadResponse.data)
|
||||
console.log('✅ 上傳成功!')
|
||||
console.log('📡 響應內容:', JSON.stringify(uploadResult, null, 2))
|
||||
|
||||
if (uploadResult.success) {
|
||||
console.log('測試結果ID:', uploadResult.data.testResult.id)
|
||||
}
|
||||
} else {
|
||||
console.log('❌ 上傳失敗,狀態碼:', uploadResponse.status)
|
||||
console.log('響應內容:', uploadResponse.data)
|
||||
}
|
||||
|
||||
// 2. 測試綜合測試結果獲取 API
|
||||
console.log('\n📊 2. 測試綜合測試結果獲取 API...')
|
||||
|
||||
const getResponse = await new Promise((resolve, reject) => {
|
||||
const req = http.get(`http://localhost:3000/api/test-results/combined?userId=${userId}`, (res) => {
|
||||
let data = ''
|
||||
res.on('data', chunk => data += chunk)
|
||||
res.on('end', () => resolve({ status: res.statusCode, data }))
|
||||
})
|
||||
req.on('error', reject)
|
||||
})
|
||||
|
||||
console.log('📊 獲取響應狀態:', getResponse.status)
|
||||
|
||||
if (getResponse.status === 200) {
|
||||
const getResult = JSON.parse(getResponse.data)
|
||||
console.log('✅ 獲取成功!')
|
||||
console.log('📡 響應內容:', JSON.stringify(getResult, null, 2))
|
||||
|
||||
if (getResult.success && getResult.data.length > 0) {
|
||||
console.log(`找到 ${getResult.data.length} 筆綜合測試結果`)
|
||||
getResult.data.forEach((result, index) => {
|
||||
console.log(`\n結果 ${index + 1}:`)
|
||||
console.log(` ID: ${result.id}`)
|
||||
console.log(` 邏輯分數: ${result.logic_score}`)
|
||||
console.log(` 創意分數: ${result.creativity_score}`)
|
||||
console.log(` 總分: ${result.overall_score}`)
|
||||
console.log(` 等級: ${result.level}`)
|
||||
console.log(` 完成時間: ${result.completed_at}`)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
console.log('❌ 獲取失敗,狀態碼:', getResponse.status)
|
||||
console.log('響應內容:', getResponse.data)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error.message)
|
||||
} finally {
|
||||
console.log('\n✅ 綜合測試結果資料庫整合功能測試完成')
|
||||
}
|
||||
}
|
||||
|
||||
testCombinedDBIntegration()
|
49
scripts/test-new-time-format.js
Normal file
49
scripts/test-new-time-format.js
Normal file
@@ -0,0 +1,49 @@
|
||||
const https = require('https')
|
||||
const http = require('http')
|
||||
|
||||
const testNewTimeFormat = async () => {
|
||||
console.log('🔍 測試新的時間格式')
|
||||
console.log('=' .repeat(50))
|
||||
|
||||
try {
|
||||
// 測試當前時間格式
|
||||
console.log('\n📊 測試當前時間格式:')
|
||||
const now = new Date()
|
||||
const utcTime = now.toISOString()
|
||||
const taiwanTime = now.toLocaleString("zh-TW", { timeZone: "Asia/Taipei" })
|
||||
|
||||
console.log(`當前 UTC 時間: ${utcTime}`)
|
||||
console.log(`當前台灣時間: ${taiwanTime}`)
|
||||
|
||||
// 測試時間轉換
|
||||
console.log('\n📊 測試時間轉換:')
|
||||
const testDate = new Date(utcTime)
|
||||
const convertedTaiwanTime = testDate.toLocaleString("zh-TW", { timeZone: "Asia/Taipei" })
|
||||
console.log(`轉換後的台灣時間: ${convertedTaiwanTime}`)
|
||||
|
||||
// 驗證轉換是否正確
|
||||
const isCorrect = taiwanTime === convertedTaiwanTime
|
||||
console.log(`轉換是否正確: ${isCorrect ? '✅' : '❌'}`)
|
||||
|
||||
// 測試不同的時間格式
|
||||
console.log('\n📊 測試不同時間格式:')
|
||||
const formats = [
|
||||
{ name: 'UTC 格式 (修正後)', time: now.toISOString() },
|
||||
{ name: '舊格式 (有問題)', time: now.toISOString().replace('Z', '').replace('T', ' ') }
|
||||
]
|
||||
|
||||
formats.forEach(format => {
|
||||
console.log(`\n${format.name}: ${format.time}`)
|
||||
const date = new Date(format.time)
|
||||
const taiwan = date.toLocaleString("zh-TW", { timeZone: "Asia/Taipei" })
|
||||
console.log(` 轉換為台灣時間: ${taiwan}`)
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error.message)
|
||||
} finally {
|
||||
console.log('\n✅ 新時間格式測試完成')
|
||||
}
|
||||
}
|
||||
|
||||
testNewTimeFormat()
|
63
scripts/test-time-fix.js
Normal file
63
scripts/test-time-fix.js
Normal file
@@ -0,0 +1,63 @@
|
||||
const https = require('https')
|
||||
const http = require('http')
|
||||
|
||||
const testTimeFix = async () => {
|
||||
console.log('🔍 測試時間修正效果')
|
||||
console.log('=' .repeat(50))
|
||||
|
||||
const userId = 'user-1759073326705-m06y3wacd'
|
||||
|
||||
try {
|
||||
// 測試當前時間
|
||||
console.log('\n📊 當前時間測試:')
|
||||
const now = new Date()
|
||||
console.log(`本地時間: ${now.toLocaleString()}`)
|
||||
console.log(`UTC 時間: ${now.toISOString()}`)
|
||||
console.log(`台灣時間: ${now.toLocaleString("zh-TW", { timeZone: "Asia/Taipei" })}`)
|
||||
|
||||
// 測試時間轉換
|
||||
console.log('\n📊 時間轉換測試:')
|
||||
const testTimes = [
|
||||
'2025-09-29T09:16:50.637Z', // 修正後的格式
|
||||
'2025-09-29 09:16:50.637', // 舊的格式
|
||||
]
|
||||
|
||||
testTimes.forEach((timeStr, index) => {
|
||||
console.log(`\n測試 ${index + 1}: ${timeStr}`)
|
||||
const date = new Date(timeStr)
|
||||
console.log(` 轉換為 Date 物件: ${date.toISOString()}`)
|
||||
console.log(` 台灣時間: ${date.toLocaleString("zh-TW", { timeZone: "Asia/Taipei" })}`)
|
||||
})
|
||||
|
||||
// 檢查現有的測試結果
|
||||
console.log('\n📊 檢查現有測試結果:')
|
||||
const response = await new Promise((resolve, reject) => {
|
||||
const req = http.get(`http://localhost:3000/api/user/test-results?userId=${userId}`, (res) => {
|
||||
let data = ''
|
||||
res.on('data', chunk => data += chunk)
|
||||
res.on('end', () => resolve({ status: res.statusCode, data }))
|
||||
})
|
||||
req.on('error', reject)
|
||||
})
|
||||
|
||||
if (response.status === 200) {
|
||||
const data = JSON.parse(response.data)
|
||||
if (data.success && data.data.results.length > 0) {
|
||||
data.data.results.forEach((result, index) => {
|
||||
console.log(`\n${index + 1}. ${result.type} 測試:`)
|
||||
console.log(` 原始時間: ${result.completedAt}`)
|
||||
|
||||
const date = new Date(result.completedAt)
|
||||
console.log(` 台灣時間: ${date.toLocaleString("zh-TW", { timeZone: "Asia/Taipei" })}`)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error.message)
|
||||
} finally {
|
||||
console.log('\n✅ 時間修正測試完成')
|
||||
}
|
||||
}
|
||||
|
||||
testTimeFix()
|
56
scripts/test-user-results-with-combined.js
Normal file
56
scripts/test-user-results-with-combined.js
Normal file
@@ -0,0 +1,56 @@
|
||||
const https = require('https')
|
||||
const http = require('http')
|
||||
|
||||
const testUserResultsWithCombined = async () => {
|
||||
console.log('🧪 測試包含綜合測試結果的用戶測試結果 API')
|
||||
console.log('=' .repeat(50))
|
||||
|
||||
const userId = 'user-1759073326705-m06y3wacd'
|
||||
|
||||
try {
|
||||
// 測試用戶測試結果 API
|
||||
console.log('\n📊 測試用戶測試結果 API...')
|
||||
const response = await new Promise((resolve, reject) => {
|
||||
const req = http.get(`http://localhost:3000/api/user/test-results?userId=${userId}`, (res) => {
|
||||
let data = ''
|
||||
res.on('data', chunk => data += chunk)
|
||||
res.on('end', () => resolve({ status: res.statusCode, data }))
|
||||
})
|
||||
req.on('error', reject)
|
||||
})
|
||||
|
||||
if (response.status === 200) {
|
||||
const data = JSON.parse(response.data)
|
||||
console.log('✅ API 測試成功!')
|
||||
console.log('📡 響應內容:', JSON.stringify(data, null, 2))
|
||||
|
||||
if (data.success) {
|
||||
console.log('\n📈 測試結果詳情:')
|
||||
console.log(`- 總測試數: ${data.data.stats.totalTests}`)
|
||||
console.log(`- 平均分數: ${data.data.stats.averageScore}`)
|
||||
console.log(`- 最高分數: ${data.data.stats.bestScore}`)
|
||||
console.log(`- 最近測試: ${data.data.stats.lastTestDate}`)
|
||||
console.log(`- 測試次數:`, data.data.stats.testCounts)
|
||||
|
||||
console.log('\n📋 測試結果列表:')
|
||||
data.data.results.forEach((result, index) => {
|
||||
console.log(`\n${index + 1}. ${result.type}:`)
|
||||
console.log(` 分數: ${result.score}`)
|
||||
console.log(` 完成時間: ${result.completedAt}`)
|
||||
console.log(` 測試次數: ${result.testCount}`)
|
||||
console.log(` 詳細資訊:`, result.details)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
console.log('❌ API 響應失敗,狀態碼:', response.status)
|
||||
console.log('響應內容:', response.data)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error.message)
|
||||
} finally {
|
||||
console.log('\n✅ 包含綜合測試結果的用戶測試結果 API 測試完成')
|
||||
}
|
||||
}
|
||||
|
||||
testUserResultsWithCombined()
|
Reference in New Issue
Block a user