實作創意題與資料庫整合
This commit is contained in:
31
app/api/creative-test-answers/route.ts
Normal file
31
app/api/creative-test-answers/route.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { getCreativeTestAnswersByTestResultId } from '@/lib/database/models/creative_test_answer'
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const testResultId = searchParams.get('testResultId')
|
||||
|
||||
if (!testResultId) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: '缺少測試結果ID' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// 獲取創意測驗答案
|
||||
const answers = await getCreativeTestAnswersByTestResultId(testResultId)
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: answers
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('獲取創意測驗答案失敗:', error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: '伺服器錯誤' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
155
app/api/test-results/creative/route.ts
Normal file
155
app/api/test-results/creative/route.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { createTestResult, getTestResultsByUserId } from '@/lib/database/models/test_result'
|
||||
import { createCreativeTestAnswers } from '@/lib/database/models/creative_test_answer'
|
||||
import { getAllCreativeQuestions } from '@/lib/database/models/creative_question'
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
let body: any
|
||||
try {
|
||||
body = await request.json()
|
||||
const {
|
||||
userId,
|
||||
answers,
|
||||
completedAt
|
||||
} = body
|
||||
|
||||
// 驗證必要欄位
|
||||
if (!userId || !answers || !completedAt) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: '缺少必要欄位' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// 獲取創意題目
|
||||
const questions = await getAllCreativeQuestions()
|
||||
if (questions.length === 0) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: '無法獲取題目' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
|
||||
// 計算分數(包含反向題處理)
|
||||
let totalScore = 0
|
||||
const answerRecords = []
|
||||
|
||||
for (let i = 0; i < questions.length; i++) {
|
||||
const question = questions[i]
|
||||
const userAnswer = answers[i] || 1 // 預設為1
|
||||
|
||||
// 處理反向題:如果是反向題,分數要反轉
|
||||
let score = userAnswer
|
||||
if (question.is_reverse) {
|
||||
score = 6 - userAnswer // 5->1, 4->2, 3->3, 2->4, 1->5
|
||||
}
|
||||
|
||||
totalScore += score
|
||||
|
||||
answerRecords.push({
|
||||
test_result_id: '', // 稍後填入
|
||||
question_id: question.id,
|
||||
user_answer: userAnswer,
|
||||
score: score
|
||||
})
|
||||
}
|
||||
|
||||
// 計算百分比分數
|
||||
const maxPossibleScore = questions.length * 5 // 每題最高5分
|
||||
const scorePercentage = Math.round((totalScore / maxPossibleScore) * 100)
|
||||
|
||||
// 建立測試結果
|
||||
console.log('🔄 開始建立創意測驗結果...')
|
||||
console.log('測試結果數據:', {
|
||||
user_id: userId,
|
||||
test_type: 'creative',
|
||||
score: scorePercentage,
|
||||
total_questions: questions.length,
|
||||
correct_answers: totalScore, // 創意測驗用總分數代替正確答案數
|
||||
completed_at: completedAt
|
||||
})
|
||||
|
||||
const testResult = await createTestResult({
|
||||
user_id: userId,
|
||||
test_type: 'creative',
|
||||
score: scorePercentage,
|
||||
total_questions: questions.length,
|
||||
correct_answers: totalScore,
|
||||
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 createCreativeTestAnswers(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 creativeResults = results.filter(r => r.test_type === 'creative')
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: creativeResults
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('獲取創意測驗結果失敗:', error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: '伺服器錯誤' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
@@ -8,6 +8,7 @@ import { Progress } from "@/components/ui/progress"
|
||||
import { Lightbulb, Home, RotateCcw, TrendingUp } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import { creativeQuestions } from "@/lib/questions/creative-questions"
|
||||
import { useAuth } from "@/lib/hooks/use-auth"
|
||||
|
||||
interface CreativeTestResults {
|
||||
type: string
|
||||
@@ -16,17 +17,139 @@ interface CreativeTestResults {
|
||||
maxScore: number
|
||||
answers: Record<number, number>
|
||||
completedAt: string
|
||||
dimensionScores?: {
|
||||
innovation: { percentage: number, rawScore: number, maxScore: number }
|
||||
imagination: { percentage: number, rawScore: number, maxScore: number }
|
||||
flexibility: { percentage: number, rawScore: number, maxScore: number }
|
||||
originality: { percentage: number, rawScore: number, maxScore: number }
|
||||
}
|
||||
}
|
||||
|
||||
export default function CreativeResultsPage() {
|
||||
const { user } = useAuth()
|
||||
const [results, setResults] = useState<CreativeTestResults | null>(null)
|
||||
const [questions, setQuestions] = useState<any[]>([])
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
if (!user) return
|
||||
|
||||
try {
|
||||
// 從資料庫獲取最新的創意測驗結果
|
||||
const response = await fetch(`/api/test-results/creative?userId=${user.id}`)
|
||||
const data = await response.json()
|
||||
|
||||
if (data.success && data.data.length > 0) {
|
||||
// 取最新的結果
|
||||
const latestResult = data.data[0]
|
||||
|
||||
// 獲取題目資料來計算各維度分數
|
||||
const questionsResponse = await fetch('/api/creative-questions')
|
||||
const questionsData = await questionsResponse.json()
|
||||
|
||||
if (questionsData.success) {
|
||||
setQuestions(questionsData.questions)
|
||||
|
||||
// 計算各維度分數
|
||||
const dimensionScores = await calculateDimensionScores(latestResult, questionsData.questions)
|
||||
|
||||
setResults({
|
||||
type: "creative",
|
||||
score: latestResult.score,
|
||||
totalScore: latestResult.correct_answers,
|
||||
maxScore: latestResult.total_questions * 5,
|
||||
answers: {}, // 從資料庫結果中獲取
|
||||
completedAt: latestResult.completed_at,
|
||||
dimensionScores: dimensionScores
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// 如果沒有資料庫結果,回退到 localStorage
|
||||
const savedResults = localStorage.getItem("creativeTestResults")
|
||||
if (savedResults) {
|
||||
setResults(JSON.parse(savedResults))
|
||||
}
|
||||
}, [])
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading creative test results:', error)
|
||||
// 回退到 localStorage
|
||||
const savedResults = localStorage.getItem("creativeTestResults")
|
||||
if (savedResults) {
|
||||
setResults(JSON.parse(savedResults))
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
loadData()
|
||||
}, [user])
|
||||
|
||||
// 計算各維度分數
|
||||
const calculateDimensionScores = async (testResult: any, questions: any[]) => {
|
||||
try {
|
||||
// 獲取詳細答案
|
||||
const answersResponse = await fetch(`/api/creative-test-answers?testResultId=${testResult.id}`)
|
||||
const answersData = await answersResponse.json()
|
||||
|
||||
if (!answersData.success) {
|
||||
return {
|
||||
innovation: { percentage: 0, rawScore: 0, maxScore: 0 },
|
||||
imagination: { percentage: 0, rawScore: 0, maxScore: 0 },
|
||||
flexibility: { percentage: 0, rawScore: 0, maxScore: 0 },
|
||||
originality: { percentage: 0, rawScore: 0, maxScore: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
const answers = answersData.data
|
||||
const dimensionScores: Record<string, { total: number, count: number }> = {
|
||||
innovation: { total: 0, count: 0 },
|
||||
imagination: { total: 0, count: 0 },
|
||||
flexibility: { total: 0, count: 0 },
|
||||
originality: { total: 0, count: 0 }
|
||||
}
|
||||
|
||||
// 計算各維度分數
|
||||
answers.forEach((answer: any) => {
|
||||
const question = questions.find(q => q.id === answer.question_id)
|
||||
if (question && dimensionScores[question.category]) {
|
||||
dimensionScores[question.category].total += answer.score
|
||||
dimensionScores[question.category].count += 1
|
||||
}
|
||||
})
|
||||
|
||||
// 計算百分比分數和原始分數
|
||||
const result = {
|
||||
innovation: { percentage: 0, rawScore: 0, maxScore: 0 },
|
||||
imagination: { percentage: 0, rawScore: 0, maxScore: 0 },
|
||||
flexibility: { percentage: 0, rawScore: 0, maxScore: 0 },
|
||||
originality: { percentage: 0, rawScore: 0, maxScore: 0 }
|
||||
}
|
||||
|
||||
Object.keys(dimensionScores).forEach(category => {
|
||||
const { total, count } = dimensionScores[category]
|
||||
const maxScore = count * 5
|
||||
const percentage = count > 0 ? Math.round((total / maxScore) * 100) : 0
|
||||
|
||||
result[category as keyof typeof result] = {
|
||||
percentage: percentage,
|
||||
rawScore: total,
|
||||
maxScore: maxScore
|
||||
}
|
||||
})
|
||||
|
||||
return result
|
||||
} catch (error) {
|
||||
console.error('計算維度分數失敗:', error)
|
||||
return {
|
||||
innovation: { percentage: 0, rawScore: 0, maxScore: 0 },
|
||||
imagination: { percentage: 0, rawScore: 0, maxScore: 0 },
|
||||
flexibility: { percentage: 0, rawScore: 0, maxScore: 0 },
|
||||
originality: { percentage: 0, rawScore: 0, maxScore: 0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!results) {
|
||||
return (
|
||||
@@ -78,7 +201,33 @@ export default function CreativeResultsPage() {
|
||||
|
||||
const creativityLevel = getCreativityLevel(results.score)
|
||||
|
||||
// Calculate category scores
|
||||
// Calculate category scores - prioritize database data if available
|
||||
let categoryResults: Array<{
|
||||
category: string
|
||||
name: string
|
||||
score: number
|
||||
rawScore: number
|
||||
maxRawScore: number
|
||||
}> = []
|
||||
|
||||
if (results.dimensionScores) {
|
||||
// Use database-calculated dimension scores
|
||||
const dimensionNames = {
|
||||
innovation: '創新能力',
|
||||
imagination: '想像力',
|
||||
flexibility: '靈活性',
|
||||
originality: '原創性'
|
||||
}
|
||||
|
||||
categoryResults = Object.entries(results.dimensionScores).map(([key, data]) => ({
|
||||
category: key,
|
||||
name: dimensionNames[key as keyof typeof dimensionNames],
|
||||
score: data.percentage,
|
||||
rawScore: data.rawScore,
|
||||
maxRawScore: data.maxScore
|
||||
}))
|
||||
} else {
|
||||
// Fallback to localStorage calculation
|
||||
const categoryScores = {
|
||||
innovation: { total: 0, count: 0, name: "創新能力" },
|
||||
imagination: { total: 0, count: 0, name: "想像力" },
|
||||
@@ -93,13 +242,14 @@ export default function CreativeResultsPage() {
|
||||
categoryScores[question.category].count += 1
|
||||
})
|
||||
|
||||
const categoryResults = Object.entries(categoryScores).map(([key, data]) => ({
|
||||
categoryResults = Object.entries(categoryScores).map(([key, data]) => ({
|
||||
category: key,
|
||||
name: data.name,
|
||||
score: data.count > 0 ? Math.round((data.total / (data.count * 5)) * 100) : 0,
|
||||
rawScore: data.total,
|
||||
maxRawScore: data.count * 5,
|
||||
}))
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
|
@@ -7,6 +7,7 @@ import { Button } from "@/components/ui/button"
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { useAuth } from "@/lib/hooks/use-auth"
|
||||
|
||||
interface CreativeQuestion {
|
||||
id: number
|
||||
@@ -18,11 +19,13 @@ interface CreativeQuestion {
|
||||
|
||||
export default function CreativeTestPage() {
|
||||
const router = useRouter()
|
||||
const { user } = useAuth()
|
||||
const [questions, setQuestions] = useState<CreativeQuestion[]>([])
|
||||
const [currentQuestion, setCurrentQuestion] = useState(0)
|
||||
const [answers, setAnswers] = useState<Record<number, number>>({})
|
||||
const [timeRemaining, setTimeRemaining] = useState(30 * 60) // 30 minutes in seconds
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||
|
||||
// Load questions from database
|
||||
useEffect(() => {
|
||||
@@ -88,7 +91,20 @@ export default function CreativeTestPage() {
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = () => {
|
||||
const handleSubmit = async () => {
|
||||
console.log('🔍 開始提交創意測驗...')
|
||||
console.log('用戶狀態:', user)
|
||||
|
||||
if (!user) {
|
||||
console.log('❌ 用戶未登入')
|
||||
alert('請先登入')
|
||||
return
|
||||
}
|
||||
|
||||
console.log('✅ 用戶已登入,用戶ID:', user.id)
|
||||
setIsSubmitting(true)
|
||||
|
||||
try {
|
||||
// Calculate score based on creativity scoring
|
||||
let totalScore = 0
|
||||
questions.forEach((question, index) => {
|
||||
@@ -101,7 +117,7 @@ export default function CreativeTestPage() {
|
||||
const maxScore = questions.length * 5
|
||||
const score = Math.round((totalScore / maxScore) * 100)
|
||||
|
||||
// Store results in localStorage
|
||||
// Store results in localStorage (for backward compatibility)
|
||||
const results = {
|
||||
type: "creative",
|
||||
score,
|
||||
@@ -112,7 +128,46 @@ export default function CreativeTestPage() {
|
||||
}
|
||||
|
||||
localStorage.setItem("creativeTestResults", JSON.stringify(results))
|
||||
console.log('✅ 結果已儲存到 localStorage')
|
||||
|
||||
// Upload to database
|
||||
console.log('🔄 開始上傳到資料庫...')
|
||||
const uploadData = {
|
||||
userId: user.id,
|
||||
answers: Object.values(answers),
|
||||
completedAt: new Date().toISOString().replace('Z', '').replace('T', ' ')
|
||||
}
|
||||
console.log('上傳數據:', uploadData)
|
||||
|
||||
const uploadResponse = await fetch('/api/test-results/creative', {
|
||||
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/creative")
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 提交測驗失敗:', error)
|
||||
alert('提交測驗失敗,請重試')
|
||||
} finally {
|
||||
setIsSubmitting(false)
|
||||
}
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
@@ -223,8 +278,8 @@ export default function CreativeTestPage() {
|
||||
</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}>
|
||||
|
@@ -4,6 +4,7 @@ import { createLogicQuestionsTable } from './models/logic_question'
|
||||
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'
|
||||
|
||||
// 初始化資料庫
|
||||
export async function initializeDatabase(): Promise<boolean> {
|
||||
@@ -32,6 +33,9 @@ export async function initializeDatabase(): Promise<boolean> {
|
||||
// 建立邏輯測驗答案表
|
||||
await createLogicTestAnswersTable()
|
||||
|
||||
// 建立創意測驗答案表
|
||||
await createCreativeTestAnswersTable()
|
||||
|
||||
console.log('✅ 資料庫初始化完成')
|
||||
return true
|
||||
} catch (error) {
|
||||
|
@@ -60,7 +60,7 @@ export async function findCreativeQuestionById(id: number): Promise<CreativeQues
|
||||
// 獲取所有題目
|
||||
export async function getAllCreativeQuestions(): Promise<CreativeQuestion[]> {
|
||||
const query = 'SELECT * FROM creative_questions ORDER BY created_at'
|
||||
return await executeQuery<CreativeQuestion[]>(query)
|
||||
return await executeQuery<CreativeQuestion>(query)
|
||||
}
|
||||
|
||||
// 清空所有題目
|
||||
|
91
lib/database/models/creative_test_answer.ts
Normal file
91
lib/database/models/creative_test_answer.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import { executeQuery } from '../connection'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
export interface CreativeTestAnswer {
|
||||
id: string
|
||||
test_result_id: string
|
||||
question_id: number
|
||||
user_answer: number // 1-5 Likert scale
|
||||
score: number // 計算後的分數(考慮反向題)
|
||||
created_at?: string
|
||||
}
|
||||
|
||||
export interface CreateCreativeTestAnswerData {
|
||||
test_result_id: string
|
||||
question_id: number
|
||||
user_answer: number
|
||||
score: number
|
||||
}
|
||||
|
||||
// 建立創意測驗答案表(如果不存在)
|
||||
export async function createCreativeTestAnswersTable(): Promise<void> {
|
||||
const createTableQuery = `
|
||||
CREATE TABLE IF NOT EXISTS creative_test_answers (
|
||||
id VARCHAR(50) PRIMARY KEY,
|
||||
test_result_id VARCHAR(50) NOT NULL,
|
||||
question_id INT NOT NULL,
|
||||
user_answer TINYINT NOT NULL,
|
||||
score INT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
INDEX idx_test_result_id (test_result_id),
|
||||
INDEX idx_question_id (question_id),
|
||||
FOREIGN KEY (test_result_id) REFERENCES test_results(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (question_id) REFERENCES creative_questions(id) ON DELETE CASCADE
|
||||
)
|
||||
`
|
||||
|
||||
await executeQuery(createTableQuery)
|
||||
console.log('✅ 創意測驗答案表建立成功')
|
||||
}
|
||||
|
||||
// 批量建立創意測驗答案
|
||||
export async function createCreativeTestAnswers(answers: CreateCreativeTestAnswerData[]): Promise<CreativeTestAnswer[]> {
|
||||
if (answers.length === 0) {
|
||||
return []
|
||||
}
|
||||
|
||||
const placeholders = answers.map(() => '(?, ?, ?, ?, ?)').join(', ')
|
||||
const values: any[] = []
|
||||
|
||||
answers.forEach(answer => {
|
||||
values.push(
|
||||
`answer_${Date.now()}_${uuidv4().substring(0, 9)}`, // Generate unique ID for each answer
|
||||
answer.test_result_id,
|
||||
answer.question_id,
|
||||
answer.user_answer,
|
||||
answer.score
|
||||
)
|
||||
})
|
||||
|
||||
const insertQuery = `
|
||||
INSERT INTO creative_test_answers (id, test_result_id, question_id, user_answer, score)
|
||||
VALUES ${placeholders}
|
||||
`
|
||||
|
||||
try {
|
||||
await executeQuery(insertQuery, values)
|
||||
// For simplicity, we're not fetching each inserted answer.
|
||||
// In a real app, you might fetch them or return a success status.
|
||||
return answers.map(ans => ({ ...ans, id: `answer_${Date.now()}_${uuidv4().substring(0, 9)}` })) // Dummy IDs for return
|
||||
} catch (error) {
|
||||
console.error('建立創意測驗答案失敗:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 根據測試結果ID獲取所有答案
|
||||
export async function getCreativeTestAnswersByTestResultId(testResultId: string): Promise<CreativeTestAnswer[]> {
|
||||
try {
|
||||
const query = 'SELECT * FROM creative_test_answers WHERE test_result_id = ? ORDER BY created_at ASC'
|
||||
return await executeQuery<CreativeTestAnswer>(query, [testResultId])
|
||||
} catch (error) {
|
||||
console.error('獲取創意測驗答案失敗:', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
// 清空所有創意測驗答案
|
||||
export async function clearCreativeTestAnswers(): Promise<void> {
|
||||
await executeQuery('DELETE FROM creative_test_answers')
|
||||
console.log('✅ 已清空所有創意測驗答案')
|
||||
}
|
21
package-lock.json
generated
21
package-lock.json
generated
@@ -36,6 +36,7 @@
|
||||
"@radix-ui/react-toggle": "1.1.1",
|
||||
"@radix-ui/react-toggle-group": "1.1.1",
|
||||
"@radix-ui/react-tooltip": "1.1.6",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@vercel/analytics": "1.3.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"bcryptjs": "^3.0.2",
|
||||
@@ -61,6 +62,7 @@
|
||||
"sonner": "^1.7.4",
|
||||
"tailwind-merge": "^2.5.5",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"uuid": "^13.0.0",
|
||||
"vaul": "^0.9.9",
|
||||
"xlsx": "latest",
|
||||
"zod": "3.25.67"
|
||||
@@ -2373,6 +2375,12 @@
|
||||
"@types/react": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/uuid": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
|
||||
"integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vercel/analytics": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@vercel/analytics/-/analytics-1.3.1.tgz",
|
||||
@@ -4254,6 +4262,19 @@
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "13.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz",
|
||||
"integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist-node/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/vaul": {
|
||||
"version": "0.9.9",
|
||||
"resolved": "https://registry.npmjs.org/vaul/-/vaul-0.9.9.tgz",
|
||||
|
@@ -78,6 +78,7 @@
|
||||
"@radix-ui/react-toggle": "1.1.1",
|
||||
"@radix-ui/react-toggle-group": "1.1.1",
|
||||
"@radix-ui/react-tooltip": "1.1.6",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@vercel/analytics": "1.3.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"bcryptjs": "^3.0.2",
|
||||
@@ -103,6 +104,7 @@
|
||||
"sonner": "^1.7.4",
|
||||
"tailwind-merge": "^2.5.5",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"uuid": "^13.0.0",
|
||||
"vaul": "^0.9.9",
|
||||
"xlsx": "latest",
|
||||
"zod": "3.25.67"
|
||||
|
49
scripts/check-creative-questions-table.js
Normal file
49
scripts/check-creative-questions-table.js
Normal file
@@ -0,0 +1,49 @@
|
||||
const mysql = require('mysql2/promise')
|
||||
|
||||
async function checkCreativeQuestionsTable() {
|
||||
const config = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'hr_assessment',
|
||||
password: process.env.DB_PASSWORD || 'QFOts8FlibiI',
|
||||
database: process.env.DB_NAME || 'db_hr_assessment',
|
||||
}
|
||||
|
||||
console.log('🔍 檢查 creative_questions 表結構...')
|
||||
|
||||
let connection
|
||||
try {
|
||||
connection = await mysql.createConnection(config)
|
||||
|
||||
// 檢查表結構
|
||||
const [columns] = await connection.execute("DESCRIBE creative_questions")
|
||||
console.log('\n📊 creative_questions 表結構:')
|
||||
columns.forEach(col => {
|
||||
console.log(`- ${col.Field}: ${col.Type} ${col.Null === 'NO' ? 'NOT NULL' : 'NULL'} ${col.Key ? `(${col.Key})` : ''}`)
|
||||
})
|
||||
|
||||
// 檢查表內容
|
||||
const [rows] = await connection.execute("SELECT * FROM creative_questions LIMIT 3")
|
||||
console.log('\n📝 creative_questions 表內容 (前3筆):')
|
||||
if (rows.length > 0) {
|
||||
rows.forEach((row, index) => {
|
||||
console.log(`${index + 1}. ID: ${row.id}`)
|
||||
console.log(` 題目: ${row.statement}`)
|
||||
console.log(` 類別: ${row.category}`)
|
||||
console.log(` 是否反向: ${row.is_reverse}`)
|
||||
console.log('')
|
||||
})
|
||||
} else {
|
||||
console.log('❌ 沒有找到創意題目數據')
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 檢查失敗:', error.message)
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkCreativeQuestionsTable()
|
41
scripts/check-creative-test-answers-table.js
Normal file
41
scripts/check-creative-test-answers-table.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const mysql = require('mysql2/promise')
|
||||
|
||||
async function checkCreativeTestAnswersTable() {
|
||||
const config = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'hr_assessment',
|
||||
password: process.env.DB_PASSWORD || 'QFOts8FlibiI',
|
||||
database: process.env.DB_NAME || 'db_hr_assessment',
|
||||
}
|
||||
|
||||
console.log('🔍 檢查 creative_test_answers 表結構...')
|
||||
|
||||
let connection
|
||||
try {
|
||||
connection = await mysql.createConnection(config)
|
||||
|
||||
// 檢查表是否存在
|
||||
const [tables] = await connection.execute("SHOW TABLES LIKE 'creative_test_answers'")
|
||||
if (tables.length === 0) {
|
||||
console.log('❌ creative_test_answers 表不存在')
|
||||
return
|
||||
}
|
||||
|
||||
// 檢查表結構
|
||||
const [columns] = await connection.execute("DESCRIBE creative_test_answers")
|
||||
console.log('\n📊 creative_test_answers 表結構:')
|
||||
columns.forEach(col => {
|
||||
console.log(`- ${col.Field}: ${col.Type} ${col.Null === 'NO' ? 'NOT NULL' : 'NULL'} ${col.Key ? `(${col.Key})` : ''}`)
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 檢查失敗:', error.message)
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkCreativeTestAnswersTable()
|
84
scripts/check-creative-test-results.js
Normal file
84
scripts/check-creative-test-results.js
Normal file
@@ -0,0 +1,84 @@
|
||||
const mysql = require('mysql2/promise')
|
||||
|
||||
async function checkCreativeTestResults() {
|
||||
const config = {
|
||||
host: process.env.DB_HOST || 'mysql.theaken.com',
|
||||
port: parseInt(process.env.DB_PORT || '33306'),
|
||||
user: process.env.DB_USER || 'hr_assessment',
|
||||
password: process.env.DB_PASSWORD || 'QFOts8FlibiI',
|
||||
database: process.env.DB_NAME || 'db_hr_assessment',
|
||||
}
|
||||
|
||||
console.log('🔍 檢查創意測驗結果...')
|
||||
|
||||
let connection
|
||||
try {
|
||||
connection = await mysql.createConnection(config)
|
||||
|
||||
// 檢查 test_results 表中的創意測驗結果
|
||||
const [creativeResults] = await connection.execute(
|
||||
`SELECT tr.*, u.name as user_name
|
||||
FROM test_results tr
|
||||
JOIN users u ON tr.user_id = u.id
|
||||
WHERE tr.test_type = 'creative'
|
||||
ORDER BY tr.completed_at DESC`
|
||||
)
|
||||
|
||||
console.log('\n📊 test_results 表中的創意測驗結果:')
|
||||
if (creativeResults.length > 0) {
|
||||
console.log(`找到 ${creativeResults.length} 筆創意測驗結果:`)
|
||||
creativeResults.forEach((result, index) => {
|
||||
console.log(`${index + 1}. ID: ${result.id}`)
|
||||
console.log(` 用戶ID: ${result.user_id}`)
|
||||
console.log(` 用戶名稱: ${result.user_name}`)
|
||||
console.log(` 分數: ${result.score}`)
|
||||
console.log(` 總分數: ${result.correct_answers}`)
|
||||
console.log(` 總題數: ${result.total_questions}`)
|
||||
console.log(` 完成時間: ${new Date(result.completed_at)}`)
|
||||
console.log(` 建立時間: ${new Date(result.created_at)}`)
|
||||
console.log('')
|
||||
})
|
||||
} else {
|
||||
console.log('❌ 沒有找到創意測驗結果')
|
||||
}
|
||||
|
||||
// 檢查 creative_test_answers 表中的答案記錄
|
||||
const [answerRecords] = await connection.execute(
|
||||
`SELECT cta.*, u.id as user_id, cq.statement as question_text, cq.is_reverse
|
||||
FROM creative_test_answers cta
|
||||
JOIN test_results tr ON cta.test_result_id = tr.id
|
||||
JOIN users u ON tr.user_id = u.id
|
||||
JOIN creative_questions cq ON cta.question_id = cq.id
|
||||
ORDER BY cta.created_at ASC
|
||||
LIMIT 10`
|
||||
)
|
||||
|
||||
console.log('\n📝 creative_test_answers 表中的答案記錄 (前10筆):')
|
||||
if (answerRecords.length > 0) {
|
||||
console.log(`找到 ${answerRecords.length} 筆答案記錄:`)
|
||||
answerRecords.forEach((record, index) => {
|
||||
console.log(`${index + 1}. 答案ID: ${record.id}`)
|
||||
console.log(` 測試結果ID: ${record.test_result_id}`)
|
||||
console.log(` 用戶ID: ${record.user_id}`)
|
||||
console.log(` 題目ID: ${record.question_id}`)
|
||||
console.log(` 題目: ${record.question_text}`)
|
||||
console.log(` 用戶答案: ${record.user_answer}`)
|
||||
console.log(` 計算分數: ${record.score}`)
|
||||
console.log(` 是否反向題: ${record.is_reverse ? '是' : '否'}`)
|
||||
console.log(` 建立時間: ${new Date(record.created_at)}`)
|
||||
console.log('')
|
||||
})
|
||||
} else {
|
||||
console.log('❌ 沒有找到答案記錄')
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 檢查失敗:', error.message)
|
||||
} finally {
|
||||
if (connection) {
|
||||
await connection.end()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkCreativeTestResults()
|
132
scripts/debug-dimension-scoring.js
Normal file
132
scripts/debug-dimension-scoring.js
Normal file
@@ -0,0 +1,132 @@
|
||||
const https = require('https')
|
||||
const http = require('http')
|
||||
|
||||
const debugDimensionScoring = async () => {
|
||||
console.log('🔍 調試維度分數計算')
|
||||
console.log('=' .repeat(50))
|
||||
|
||||
const testResultId = 'test_1759086508812_xv2pof6lk' // 使用最新的測試結果ID
|
||||
|
||||
try {
|
||||
// 1. 獲取測試結果
|
||||
console.log('\n📊 1. 獲取測試結果...')
|
||||
const resultResponse = await new Promise((resolve, reject) => {
|
||||
const req = http.get(`http://localhost:3000/api/test-results/creative?userId=user-1759073326705-m06y3wacd`, (res) => {
|
||||
let data = ''
|
||||
res.on('data', chunk => data += chunk)
|
||||
res.on('end', () => resolve({ status: res.statusCode, data }))
|
||||
})
|
||||
req.on('error', reject)
|
||||
})
|
||||
|
||||
if (resultResponse.status === 200) {
|
||||
const resultData = JSON.parse(resultResponse.data)
|
||||
if (resultData.success && resultData.data.length > 0) {
|
||||
const testResult = resultData.data[0]
|
||||
console.log('測試結果:', {
|
||||
id: testResult.id,
|
||||
score: testResult.score,
|
||||
total_questions: testResult.total_questions,
|
||||
correct_answers: testResult.correct_answers
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 獲取題目資料
|
||||
console.log('\n📊 2. 獲取題目資料...')
|
||||
const questionsResponse = await new Promise((resolve, reject) => {
|
||||
const req = http.get('http://localhost:3000/api/creative-questions', (res) => {
|
||||
let data = ''
|
||||
res.on('data', chunk => data += chunk)
|
||||
res.on('end', () => resolve({ status: res.statusCode, data }))
|
||||
})
|
||||
req.on('error', reject)
|
||||
})
|
||||
|
||||
let questions = []
|
||||
if (questionsResponse.status === 200) {
|
||||
const questionsData = JSON.parse(questionsResponse.data)
|
||||
if (questionsData.success) {
|
||||
questions = questionsData.questions
|
||||
console.log(`獲取到 ${questions.length} 個題目`)
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 獲取詳細答案
|
||||
console.log('\n📊 3. 獲取詳細答案...')
|
||||
const answersResponse = await new Promise((resolve, reject) => {
|
||||
const req = http.get(`http://localhost:3000/api/creative-test-answers?testResultId=${testResultId}`, (res) => {
|
||||
let data = ''
|
||||
res.on('data', chunk => data += chunk)
|
||||
res.on('end', () => resolve({ status: res.statusCode, data }))
|
||||
})
|
||||
req.on('error', reject)
|
||||
})
|
||||
|
||||
if (answersResponse.status === 200) {
|
||||
const answersData = JSON.parse(answersResponse.data)
|
||||
if (answersData.success) {
|
||||
const answers = answersData.data
|
||||
console.log(`獲取到 ${answers.length} 個答案`)
|
||||
|
||||
// 4. 按維度分組計算
|
||||
console.log('\n📊 4. 按維度分組計算...')
|
||||
const dimensionScores = {
|
||||
innovation: { total: 0, count: 0, answers: [] },
|
||||
imagination: { total: 0, count: 0, answers: [] },
|
||||
flexibility: { total: 0, count: 0, answers: [] },
|
||||
originality: { total: 0, count: 0, answers: [] }
|
||||
}
|
||||
|
||||
answers.forEach((answer) => {
|
||||
const question = questions.find(q => q.id === answer.question_id)
|
||||
if (question && dimensionScores[question.category]) {
|
||||
dimensionScores[question.category].total += answer.score
|
||||
dimensionScores[question.category].count += 1
|
||||
dimensionScores[question.category].answers.push({
|
||||
question_id: answer.question_id,
|
||||
user_answer: answer.user_answer,
|
||||
score: answer.score,
|
||||
is_reverse: question.is_reverse
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 5. 顯示詳細計算過程
|
||||
console.log('\n📊 5. 詳細計算過程:')
|
||||
Object.keys(dimensionScores).forEach(category => {
|
||||
const { total, count, answers } = dimensionScores[category]
|
||||
const maxPossible = count * 5
|
||||
const percentage = count > 0 ? Math.round((total / maxPossible) * 100) : 0
|
||||
|
||||
console.log(`\n${category}:`)
|
||||
console.log(` 題目數量: ${count}`)
|
||||
console.log(` 總分數: ${total}`)
|
||||
console.log(` 最大可能分數: ${maxPossible}`)
|
||||
console.log(` 百分比: ${percentage}%`)
|
||||
console.log(` 答案詳情:`)
|
||||
answers.forEach((ans, index) => {
|
||||
console.log(` ${index + 1}. 題目${ans.question_id}: 用戶答案${ans.user_answer} → 計算分數${ans.score} (反向題: ${ans.is_reverse ? '是' : '否'})`)
|
||||
})
|
||||
})
|
||||
|
||||
// 6. 總分驗證
|
||||
console.log('\n📊 6. 總分驗證:')
|
||||
const totalScore = answers.reduce((sum, answer) => sum + answer.score, 0)
|
||||
const maxTotalScore = answers.length * 5
|
||||
const totalPercentage = Math.round((totalScore / maxTotalScore) * 100)
|
||||
console.log(`所有答案總分數: ${totalScore}`)
|
||||
console.log(`最大可能總分數: ${maxTotalScore}`)
|
||||
console.log(`總百分比: ${totalPercentage}%`)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 調試失敗:', error.message)
|
||||
} finally {
|
||||
console.log('\n✅ 維度分數計算調試完成')
|
||||
}
|
||||
}
|
||||
|
||||
debugDimensionScoring()
|
61
scripts/test-creative-answers-api.js
Normal file
61
scripts/test-creative-answers-api.js
Normal file
@@ -0,0 +1,61 @@
|
||||
const https = require('https')
|
||||
const http = require('http')
|
||||
|
||||
const testCreativeAnswersAPI = async () => {
|
||||
console.log('🧪 測試創意測驗答案 API')
|
||||
console.log('=' .repeat(50))
|
||||
|
||||
const testResultId = 'test_1759086508812_xv2pof6lk' // 使用最新的測試結果ID
|
||||
const url = `http://localhost:3000/api/creative-test-answers?testResultId=${testResultId}`
|
||||
|
||||
try {
|
||||
console.log(`\n📊 測試結果ID: ${testResultId}`)
|
||||
console.log(`🔗 API URL: ${url}`)
|
||||
|
||||
const response = await new Promise((resolve, reject) => {
|
||||
const req = http.get(url, (res) => {
|
||||
let data = ''
|
||||
res.on('data', chunk => data += chunk)
|
||||
res.on('end', () => resolve({ status: res.statusCode, data }))
|
||||
})
|
||||
req.on('error', reject)
|
||||
})
|
||||
|
||||
console.log('📊 響應狀態:', response.status)
|
||||
|
||||
if (response.status === 200) {
|
||||
const result = JSON.parse(response.data)
|
||||
console.log('\n✅ API 測試成功!')
|
||||
console.log('📡 響應內容:', JSON.stringify(result, null, 2))
|
||||
|
||||
if (result.success) {
|
||||
console.log(`\n📈 答案記錄詳情:`)
|
||||
console.log(`- 答案數量: ${result.data.length}`)
|
||||
|
||||
// 按維度分組統計
|
||||
const dimensionStats = {
|
||||
innovation: { total: 0, count: 0 },
|
||||
imagination: { total: 0, count: 0 },
|
||||
flexibility: { total: 0, count: 0 },
|
||||
originality: { total: 0, count: 0 }
|
||||
}
|
||||
|
||||
result.data.forEach((answer, index) => {
|
||||
console.log(`${index + 1}. 題目ID: ${answer.question_id}, 用戶答案: ${answer.user_answer}, 計算分數: ${answer.score}`)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
console.log('❌ API 響應失敗,狀態碼:', response.status)
|
||||
console.log('響應內容:', response.data)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n❌ 請求失敗:')
|
||||
console.error('錯誤類型:', error.name)
|
||||
console.error('錯誤訊息:', error.message)
|
||||
} finally {
|
||||
console.log('\n✅ 創意測驗答案 API 測試完成')
|
||||
}
|
||||
}
|
||||
|
||||
testCreativeAnswersAPI()
|
79
scripts/test-creative-db-upload.js
Normal file
79
scripts/test-creative-db-upload.js
Normal file
@@ -0,0 +1,79 @@
|
||||
const https = require('https')
|
||||
const http = require('http')
|
||||
|
||||
const testCreativeDBUpload = async () => {
|
||||
console.log('🧪 測試創意測驗資料庫上傳功能')
|
||||
console.log('=' .repeat(50))
|
||||
|
||||
const userId = 'user-1759073326705-m06y3wacd' // 使用現有用戶ID
|
||||
const url = 'http://localhost:3000/api/test-results/creative'
|
||||
|
||||
// 模擬創意測驗答案(1-5分,包含反向題)
|
||||
const testAnswers = [
|
||||
5, 4, 3, 2, 1, // 前5題正常題
|
||||
1, 2, 3, 4, 5, // 中間5題反向題(會自動反轉)
|
||||
5, 4, 3, 2, 1, // 後8題正常題
|
||||
1, 2, 3, 4, 5
|
||||
]
|
||||
|
||||
const testData = {
|
||||
userId: userId,
|
||||
answers: testAnswers,
|
||||
completedAt: new Date().toISOString().replace('Z', '').replace('T', ' ')
|
||||
}
|
||||
|
||||
console.log('\n📝 測試數據:')
|
||||
console.log('用戶ID:', testData.userId)
|
||||
console.log('答案數量:', testData.answers.length)
|
||||
console.log('答案內容:', testData.answers)
|
||||
console.log('完成時間:', testData.completedAt)
|
||||
|
||||
try {
|
||||
console.log('\n🔄 測試創意測驗 API...')
|
||||
|
||||
const response = await new Promise((resolve, reject) => {
|
||||
const req = http.request(url, {
|
||||
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('📊 響應狀態:', response.status)
|
||||
|
||||
if (response.status === 200) {
|
||||
const result = JSON.parse(response.data)
|
||||
console.log('\n✅ API 測試成功!')
|
||||
console.log('📡 響應內容:', JSON.stringify(result, null, 2))
|
||||
|
||||
if (result.success) {
|
||||
console.log('\n📈 測試結果詳情:')
|
||||
console.log('- 測試結果ID:', result.data.testResult.id)
|
||||
console.log('- 分數:', result.data.testResult.score)
|
||||
console.log('- 總題數:', result.data.testResult.total_questions)
|
||||
console.log('- 總分數:', result.data.testResult.correct_answers)
|
||||
console.log('- 答案記錄數量:', result.data.answerCount)
|
||||
}
|
||||
} else {
|
||||
console.log('❌ API 響應失敗,狀態碼:', response.status)
|
||||
console.log('響應內容:', response.data)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n❌ 請求失敗:')
|
||||
console.error('錯誤類型:', error.name)
|
||||
console.error('錯誤訊息:', error.message)
|
||||
} finally {
|
||||
console.log('\n✅ 創意測驗資料庫上傳測試完成')
|
||||
}
|
||||
}
|
||||
|
||||
testCreativeDBUpload()
|
67
scripts/test-creative-results-page.js
Normal file
67
scripts/test-creative-results-page.js
Normal file
@@ -0,0 +1,67 @@
|
||||
const https = require('https')
|
||||
const http = require('http')
|
||||
|
||||
const testCreativeResultsPage = async () => {
|
||||
console.log('🧪 測試創意測驗結果頁面數據獲取')
|
||||
console.log('=' .repeat(50))
|
||||
|
||||
const userId = 'user-1759073326705-m06y3wacd'
|
||||
|
||||
try {
|
||||
// 1. 測試用戶測試結果 API
|
||||
console.log('\n📊 1. 測試用戶測試結果 API...')
|
||||
const userResultsResponse = 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 (userResultsResponse.status === 200) {
|
||||
const userResultsData = JSON.parse(userResultsResponse.data)
|
||||
console.log('用戶測試結果:', JSON.stringify(userResultsData, null, 2))
|
||||
}
|
||||
|
||||
// 2. 測試創意測驗結果 API
|
||||
console.log('\n📊 2. 測試創意測驗結果 API...')
|
||||
const creativeResultsResponse = 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 (creativeResultsResponse.status === 200) {
|
||||
const creativeResultsData = JSON.parse(creativeResultsResponse.data)
|
||||
console.log('創意測驗結果:', JSON.stringify(creativeResultsData, null, 2))
|
||||
}
|
||||
|
||||
// 3. 測試創意測驗答案 API
|
||||
console.log('\n📊 3. 測試創意測驗答案 API...')
|
||||
const testResultId = 'test_1759086508812_xv2pof6lk'
|
||||
const answersResponse = await new Promise((resolve, reject) => {
|
||||
const req = http.get(`http://localhost:3000/api/creative-test-answers?testResultId=${testResultId}`, (res) => {
|
||||
let data = ''
|
||||
res.on('data', chunk => data += chunk)
|
||||
res.on('end', () => resolve({ status: res.statusCode, data }))
|
||||
})
|
||||
req.on('error', reject)
|
||||
})
|
||||
|
||||
if (answersResponse.status === 200) {
|
||||
const answersData = JSON.parse(answersResponse.data)
|
||||
console.log('創意測驗答案數量:', answersData.data.length)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error.message)
|
||||
} finally {
|
||||
console.log('\n✅ 創意測驗結果頁面數據獲取測試完成')
|
||||
}
|
||||
}
|
||||
|
||||
testCreativeResultsPage()
|
156
scripts/test-fixed-dimension-display.js
Normal file
156
scripts/test-fixed-dimension-display.js
Normal file
@@ -0,0 +1,156 @@
|
||||
const https = require('https')
|
||||
const http = require('http')
|
||||
|
||||
const testFixedDimensionDisplay = async () => {
|
||||
console.log('🧪 測試修正後的維度分數顯示')
|
||||
console.log('=' .repeat(50))
|
||||
|
||||
const userId = 'user-1759073326705-m06y3wacd'
|
||||
const testResultId = 'test_1759086508812_xv2pof6lk'
|
||||
|
||||
try {
|
||||
// 1. 獲取測試結果
|
||||
console.log('\n📊 1. 獲取測試結果...')
|
||||
const resultResponse = 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)
|
||||
})
|
||||
|
||||
let testResult = null
|
||||
if (resultResponse.status === 200) {
|
||||
const resultData = JSON.parse(resultResponse.data)
|
||||
if (resultData.success && resultData.data.length > 0) {
|
||||
testResult = resultData.data[0]
|
||||
console.log('測試結果:', {
|
||||
id: testResult.id,
|
||||
score: testResult.score,
|
||||
total_questions: testResult.total_questions,
|
||||
correct_answers: testResult.correct_answers
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 獲取題目資料
|
||||
console.log('\n📊 2. 獲取題目資料...')
|
||||
const questionsResponse = await new Promise((resolve, reject) => {
|
||||
const req = http.get('http://localhost:3000/api/creative-questions', (res) => {
|
||||
let data = ''
|
||||
res.on('data', chunk => data += chunk)
|
||||
res.on('end', () => resolve({ status: res.statusCode, data }))
|
||||
})
|
||||
req.on('error', reject)
|
||||
})
|
||||
|
||||
let questions = []
|
||||
if (questionsResponse.status === 200) {
|
||||
const questionsData = JSON.parse(questionsResponse.data)
|
||||
if (questionsData.success) {
|
||||
questions = questionsData.questions
|
||||
console.log(`獲取到 ${questions.length} 個題目`)
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 獲取詳細答案
|
||||
console.log('\n📊 3. 獲取詳細答案...')
|
||||
const answersResponse = await new Promise((resolve, reject) => {
|
||||
const req = http.get(`http://localhost:3000/api/creative-test-answers?testResultId=${testResultId}`, (res) => {
|
||||
let data = ''
|
||||
res.on('data', chunk => data += chunk)
|
||||
res.on('end', () => resolve({ status: res.statusCode, data }))
|
||||
})
|
||||
req.on('error', reject)
|
||||
})
|
||||
|
||||
if (answersResponse.status === 200) {
|
||||
const answersData = JSON.parse(answersResponse.data)
|
||||
if (answersData.success) {
|
||||
const answers = answersData.data
|
||||
console.log(`獲取到 ${answers.length} 個答案`)
|
||||
|
||||
// 4. 模擬新的維度分數計算
|
||||
console.log('\n📊 4. 模擬新的維度分數計算...')
|
||||
|
||||
const dimensionScores = {
|
||||
innovation: { total: 0, count: 0 },
|
||||
imagination: { total: 0, count: 0 },
|
||||
flexibility: { total: 0, count: 0 },
|
||||
originality: { total: 0, count: 0 }
|
||||
}
|
||||
|
||||
answers.forEach((answer) => {
|
||||
const question = questions.find(q => q.id === answer.question_id)
|
||||
if (question && dimensionScores[question.category]) {
|
||||
dimensionScores[question.category].total += answer.score
|
||||
dimensionScores[question.category].count += 1
|
||||
}
|
||||
})
|
||||
|
||||
// 計算新的維度分數結構
|
||||
const calculatedDimensionScores = {
|
||||
innovation: { percentage: 0, rawScore: 0, maxScore: 0 },
|
||||
imagination: { percentage: 0, rawScore: 0, maxScore: 0 },
|
||||
flexibility: { percentage: 0, rawScore: 0, maxScore: 0 },
|
||||
originality: { percentage: 0, rawScore: 0, maxScore: 0 }
|
||||
}
|
||||
|
||||
Object.keys(dimensionScores).forEach(category => {
|
||||
const { total, count } = dimensionScores[category]
|
||||
const maxScore = count * 5
|
||||
const percentage = count > 0 ? Math.round((total / maxScore) * 100) : 0
|
||||
|
||||
calculatedDimensionScores[category] = {
|
||||
percentage: percentage,
|
||||
rawScore: total,
|
||||
maxScore: maxScore
|
||||
}
|
||||
})
|
||||
|
||||
console.log('\n📈 新的維度分數結構:')
|
||||
Object.entries(calculatedDimensionScores).forEach(([category, data]) => {
|
||||
console.log(`${category}:`)
|
||||
console.log(` 百分比: ${data.percentage}%`)
|
||||
console.log(` 原始分數: ${data.rawScore}`)
|
||||
console.log(` 滿分: ${data.maxScore}`)
|
||||
console.log(` 顯示: ${data.rawScore}/${data.maxScore} 分`)
|
||||
})
|
||||
|
||||
// 5. 模擬 categoryResults 計算
|
||||
console.log('\n📊 5. 模擬 categoryResults 計算...')
|
||||
|
||||
const dimensionNames = {
|
||||
innovation: '創新能力',
|
||||
imagination: '想像力',
|
||||
flexibility: '靈活性',
|
||||
originality: '原創性'
|
||||
}
|
||||
|
||||
const categoryResults = Object.entries(calculatedDimensionScores).map(([key, data]) => ({
|
||||
category: key,
|
||||
name: dimensionNames[key],
|
||||
score: data.percentage,
|
||||
rawScore: data.rawScore,
|
||||
maxRawScore: data.maxScore
|
||||
}))
|
||||
|
||||
console.log('\n📈 categoryResults:')
|
||||
categoryResults.forEach(category => {
|
||||
console.log(`${category.name}:`)
|
||||
console.log(` 分數: ${category.score}分`)
|
||||
console.log(` 顯示: ${category.rawScore}/${category.maxRawScore} 分`)
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error.message)
|
||||
} finally {
|
||||
console.log('\n✅ 修正後的維度分數顯示測試完成')
|
||||
}
|
||||
}
|
||||
|
||||
testFixedDimensionDisplay()
|
158
scripts/test-fixed-dimension-scoring.js
Normal file
158
scripts/test-fixed-dimension-scoring.js
Normal file
@@ -0,0 +1,158 @@
|
||||
const https = require('https')
|
||||
const http = require('http')
|
||||
|
||||
const testFixedDimensionScoring = async () => {
|
||||
console.log('🧪 測試修正後的維度分數計算')
|
||||
console.log('=' .repeat(50))
|
||||
|
||||
const userId = 'user-1759073326705-m06y3wacd'
|
||||
const testResultId = 'test_1759086508812_xv2pof6lk'
|
||||
|
||||
try {
|
||||
// 1. 獲取測試結果
|
||||
console.log('\n📊 1. 獲取測試結果...')
|
||||
const resultResponse = 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)
|
||||
})
|
||||
|
||||
let testResult = null
|
||||
if (resultResponse.status === 200) {
|
||||
const resultData = JSON.parse(resultResponse.data)
|
||||
if (resultData.success && resultData.data.length > 0) {
|
||||
testResult = resultData.data[0]
|
||||
console.log('測試結果:', {
|
||||
id: testResult.id,
|
||||
score: testResult.score,
|
||||
total_questions: testResult.total_questions,
|
||||
correct_answers: testResult.correct_answers
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 獲取題目資料
|
||||
console.log('\n📊 2. 獲取題目資料...')
|
||||
const questionsResponse = await new Promise((resolve, reject) => {
|
||||
const req = http.get('http://localhost:3000/api/creative-questions', (res) => {
|
||||
let data = ''
|
||||
res.on('data', chunk => data += chunk)
|
||||
res.on('end', () => resolve({ status: res.statusCode, data }))
|
||||
})
|
||||
req.on('error', reject)
|
||||
})
|
||||
|
||||
let questions = []
|
||||
if (questionsResponse.status === 200) {
|
||||
const questionsData = JSON.parse(questionsResponse.data)
|
||||
if (questionsData.success) {
|
||||
questions = questionsData.questions
|
||||
console.log(`獲取到 ${questions.length} 個題目`)
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 獲取詳細答案
|
||||
console.log('\n📊 3. 獲取詳細答案...')
|
||||
const answersResponse = await new Promise((resolve, reject) => {
|
||||
const req = http.get(`http://localhost:3000/api/creative-test-answers?testResultId=${testResultId}`, (res) => {
|
||||
let data = ''
|
||||
res.on('data', chunk => data += chunk)
|
||||
res.on('end', () => resolve({ status: res.statusCode, data }))
|
||||
})
|
||||
req.on('error', reject)
|
||||
})
|
||||
|
||||
if (answersResponse.status === 200) {
|
||||
const answersData = JSON.parse(answersResponse.data)
|
||||
if (answersData.success) {
|
||||
const answers = answersData.data
|
||||
console.log(`獲取到 ${answers.length} 個答案`)
|
||||
|
||||
// 4. 模擬結果頁面的維度分數計算
|
||||
console.log('\n📊 4. 模擬結果頁面的維度分數計算...')
|
||||
|
||||
// 計算各維度分數(模擬資料庫計算)
|
||||
const dimensionScores = {
|
||||
innovation: { total: 0, count: 0 },
|
||||
imagination: { total: 0, count: 0 },
|
||||
flexibility: { total: 0, count: 0 },
|
||||
originality: { total: 0, count: 0 }
|
||||
}
|
||||
|
||||
answers.forEach((answer) => {
|
||||
const question = questions.find(q => q.id === answer.question_id)
|
||||
if (question && dimensionScores[question.category]) {
|
||||
dimensionScores[question.category].total += answer.score
|
||||
dimensionScores[question.category].count += 1
|
||||
}
|
||||
})
|
||||
|
||||
// 計算百分比分數
|
||||
const calculatedDimensionScores = {
|
||||
innovation: 0,
|
||||
imagination: 0,
|
||||
flexibility: 0,
|
||||
originality: 0
|
||||
}
|
||||
|
||||
Object.keys(dimensionScores).forEach(category => {
|
||||
const { total, count } = dimensionScores[category]
|
||||
calculatedDimensionScores[category] =
|
||||
count > 0 ? Math.round((total / (count * 5)) * 100) : 0
|
||||
})
|
||||
|
||||
console.log('\n📈 計算結果:')
|
||||
console.log('創新能力:', calculatedDimensionScores.innovation + '%')
|
||||
console.log('想像力:', calculatedDimensionScores.imagination + '%')
|
||||
console.log('靈活性:', calculatedDimensionScores.flexibility + '%')
|
||||
console.log('原創性:', calculatedDimensionScores.originality + '%')
|
||||
|
||||
// 5. 模擬結果頁面的 categoryResults 計算
|
||||
console.log('\n📊 5. 模擬結果頁面的 categoryResults 計算...')
|
||||
|
||||
const dimensionNames = {
|
||||
innovation: '創新能力',
|
||||
imagination: '想像力',
|
||||
flexibility: '靈活性',
|
||||
originality: '原創性'
|
||||
}
|
||||
|
||||
const categoryResults = Object.entries(calculatedDimensionScores).map(([key, score]) => ({
|
||||
category: key,
|
||||
name: dimensionNames[key],
|
||||
score: score,
|
||||
rawScore: 0,
|
||||
maxRawScore: 0
|
||||
}))
|
||||
|
||||
console.log('\n📈 categoryResults:')
|
||||
categoryResults.forEach(category => {
|
||||
console.log(`${category.name}: ${category.score}分`)
|
||||
})
|
||||
|
||||
// 6. 驗證總分一致性
|
||||
console.log('\n📊 6. 驗證總分一致性...')
|
||||
const totalScore = answers.reduce((sum, answer) => sum + answer.score, 0)
|
||||
const maxTotalScore = answers.length * 5
|
||||
const totalPercentage = Math.round((totalScore / maxTotalScore) * 100)
|
||||
|
||||
console.log(`資料庫總分數: ${testResult.correct_answers}`)
|
||||
console.log(`計算總分數: ${totalScore}`)
|
||||
console.log(`資料庫百分比: ${testResult.score}%`)
|
||||
console.log(`計算百分比: ${totalPercentage}%`)
|
||||
console.log(`一致性: ${testResult.correct_answers === totalScore && testResult.score === totalPercentage ? '✅ 一致' : '❌ 不一致'}`)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 測試失敗:', error.message)
|
||||
} finally {
|
||||
console.log('\n✅ 修正後的維度分數計算測試完成')
|
||||
}
|
||||
}
|
||||
|
||||
testFixedDimensionScoring()
|
Reference in New Issue
Block a user