2025-12-18 19:07:21 +08:00
import { v4 as uuidv4 } from 'uuid' ;
import { query , run , get } from '../database' ;
import { Question } from './question' ;
export interface QuizRecord {
id : string ;
userId : string ;
totalScore : number ;
correctCount : number ;
totalCount : number ;
createdAt : string ;
}
export interface QuizAnswer {
id : string ;
recordId : string ;
questionId : string ;
userAnswer : string | string [ ] ;
score : number ;
isCorrect : boolean ;
createdAt : string ;
questionContent? : string ;
questionType? : string ;
correctAnswer? : string | string [ ] ;
questionScore? : number ;
}
export interface SubmitAnswerData {
questionId : string ;
userAnswer : string | string [ ] ;
score : number ;
isCorrect : boolean ;
}
export interface SubmitQuizData {
userId : string ;
answers : SubmitAnswerData [ ] ;
}
export class QuizModel {
// 创建答题记录
static async createRecord ( data : { userId : string ; totalScore : number ; correctCount : number ; totalCount : number } ) : Promise < QuizRecord > {
const id = uuidv4 ( ) ;
const sql = `
INSERT INTO quiz_records ( id , user_id , total_score , correct_count , total_count )
VALUES ( ? , ? , ? , ? , ? )
` ;
await run ( sql , [ id , data . userId , data . totalScore , data . correctCount , data . totalCount ] ) ;
return this . findRecordById ( id ) as Promise < QuizRecord > ;
}
// 创建答题答案
static async createAnswer ( data : Omit < QuizAnswer , 'id' | 'createdAt' > ) : Promise < QuizAnswer > {
const id = uuidv4 ( ) ;
const userAnswerStr = Array . isArray ( data . userAnswer ) ? JSON . stringify ( data . userAnswer ) : data . userAnswer ;
const sql = `
INSERT INTO quiz_answers ( id , record_id , question_id , user_answer , score , is_correct )
VALUES ( ? , ? , ? , ? , ? , ? )
` ;
await run ( sql , [ id , data . recordId , data . questionId , userAnswerStr , data . score , data . isCorrect ] ) ;
return {
id ,
recordId : data.recordId ,
questionId : data.questionId ,
userAnswer : data.userAnswer ,
score : data.score ,
isCorrect : data.isCorrect ,
createdAt : new Date ( ) . toISOString ( )
} ;
}
// 批量创建答题答案
static async createAnswers ( recordId : string , answers : SubmitAnswerData [ ] ) : Promise < QuizAnswer [ ] > {
const createdAnswers : QuizAnswer [ ] = [ ] ;
for ( const answer of answers ) {
const createdAnswer = await this . createAnswer ( {
recordId ,
questionId : answer.questionId ,
userAnswer : answer.userAnswer ,
score : answer.score ,
isCorrect : answer.isCorrect
} ) ;
createdAnswers . push ( createdAnswer ) ;
}
return createdAnswers ;
}
// 提交答题
static async submitQuiz ( data : SubmitQuizData ) : Promise < { record : QuizRecord ; answers : QuizAnswer [ ] } > {
const totalScore = data . answers . reduce ( ( sum , answer ) = > sum + answer . score , 0 ) ;
const correctCount = data . answers . filter ( answer = > answer . isCorrect ) . length ;
const totalCount = data . answers . length ;
// 创建答题记录
const record = await this . createRecord ( {
userId : data.userId ,
totalScore ,
correctCount ,
totalCount
} ) ;
// 创建答题答案
const answers = await this . createAnswers ( record . id , data . answers ) ;
return { record , answers } ;
}
// 根据ID查找答题记录
static async findRecordById ( id : string ) : Promise < QuizRecord | null > {
const sql = ` SELECT id, user_id as userId, total_score as totalScore, correct_count as correctCount, total_count as totalCount, created_at as createdAt FROM quiz_records WHERE id = ? ` ;
const record = await get ( sql , [ id ] ) ;
return record || null ;
}
// 获取用户的答题记录
static async findRecordsByUserId ( userId : string , limit = 10 , offset = 0 ) : Promise < { records : QuizRecord [ ] ; total : number } > {
const recordsSql = `
SELECT id , user_id as userId , total_score as totalScore , correct_count as correctCount , total_count as totalCount , created_at as createdAt
FROM quiz_records
WHERE user_id = ?
ORDER BY created_at DESC
LIMIT ? OFFSET ?
` ;
const countSql = ` SELECT COUNT(*) as total FROM quiz_records WHERE user_id = ? ` ;
const [ records , countResult ] = await Promise . all ( [
query ( recordsSql , [ userId , limit , offset ] ) ,
get ( countSql , [ userId ] )
] ) ;
return {
records ,
total : countResult.total
} ;
}
// 获取所有答题记录(管理员用)
2025-12-19 00:58:58 +08:00
static async findAllRecords ( limit = 10 , offset = 0 ) : Promise < { records : any [ ] ; total : number } > {
2025-12-18 19:07:21 +08:00
const recordsSql = `
SELECT r . id , r . user_id as userId , u . name as userName , u . phone as userPhone ,
r . total_score as totalScore , r . correct_count as correctCount , r . total_count as totalCount ,
2025-12-19 00:58:58 +08:00
r . created_at as createdAt , r . subject_id as subjectId , s . name as subjectName ,
r . task_id as taskId
2025-12-18 19:07:21 +08:00
FROM quiz_records r
JOIN users u ON r . user_id = u . id
2025-12-19 00:58:58 +08:00
LEFT JOIN exam_subjects s ON r . subject_id = s . id
2025-12-18 19:07:21 +08:00
ORDER BY r . created_at DESC
LIMIT ? OFFSET ?
` ;
const countSql = ` SELECT COUNT(*) as total FROM quiz_records ` ;
const [ records , countResult ] = await Promise . all ( [
query ( recordsSql , [ limit , offset ] ) ,
get ( countSql )
] ) ;
2025-12-19 00:58:58 +08:00
// 对于每条记录,计算该考试任务的参与人数
const processedRecords = await Promise . all ( records . map ( async ( record ) = > {
let examCount = 0 ;
if ( record . taskId ) {
// 统计该任务的参与人数
const taskCountSql = ` SELECT COUNT(DISTINCT user_id) as count FROM quiz_records WHERE task_id = ? ` ;
const taskCountResult = await get ( taskCountSql , [ record . taskId ] ) ;
examCount = taskCountResult . count || 0 ;
} else if ( record . subjectId ) {
// 统计该科目的参与人数
const subjectCountSql = ` SELECT COUNT(DISTINCT user_id) as count FROM quiz_records WHERE subject_id = ? ` ;
const subjectCountResult = await get ( subjectCountSql , [ record . subjectId ] ) ;
examCount = subjectCountResult . count || 0 ;
}
return {
. . . record ,
examCount
} ;
} ) ) ;
2025-12-18 19:07:21 +08:00
return {
2025-12-19 00:58:58 +08:00
records : processedRecords ,
2025-12-18 19:07:21 +08:00
total : countResult.total
} ;
}
// 获取答题答案详情
static async findAnswersByRecordId ( recordId : string ) : Promise < QuizAnswer [ ] > {
const sql = `
SELECT a . id , a . record_id as recordId , a . question_id as questionId ,
a . user_answer as userAnswer , a . score , a . is_correct as isCorrect ,
a . created_at as createdAt ,
q . content as questionContent , q . type as questionType , q . answer as correctAnswer , q . score as questionScore
FROM quiz_answers a
JOIN questions q ON a . question_id = q . id
WHERE a . record_id = ?
ORDER BY a . created_at ASC
` ;
const answers = await query ( sql , [ recordId ] ) ;
return answers . map ( row = > ( {
id : row.id ,
recordId : row.recordId ,
questionId : row.questionId ,
userAnswer : this.parseAnswer ( row . userAnswer , row . questionType ) ,
score : row.score ,
isCorrect : Boolean ( row . isCorrect ) ,
createdAt : row.createdAt ,
questionContent : row.questionContent ,
questionType : row.questionType ,
correctAnswer : this.parseAnswer ( row . correctAnswer , row . questionType ) ,
questionScore : row.questionScore
} ) ) ;
}
// 解析答案
private static parseAnswer ( answer : string , type : string ) : string | string [ ] {
if ( type === 'multiple' || type === 'checkbox' ) {
try {
return JSON . parse ( answer ) ;
} catch ( e ) {
return answer ;
}
}
return answer ;
}
// 获取统计数据
static async getStatistics ( ) : Promise < {
totalUsers : number ;
totalRecords : number ;
averageScore : number ;
typeStats : Array < { type : string ; total : number ; correct : number ; correctRate : number } > ;
} > {
2025-12-19 00:58:58 +08:00
const totalUsersSql = ` SELECT COUNT(*) as total FROM users ` ;
2025-12-18 19:07:21 +08:00
const totalRecordsSql = ` SELECT COUNT(*) as total FROM quiz_records ` ;
const averageScoreSql = ` SELECT AVG(total_score) as average FROM quiz_records ` ;
const typeStatsSql = `
SELECT q . type ,
COUNT ( * ) as total ,
SUM ( CASE WHEN qa . is_correct = 1 THEN 1 ELSE 0 END ) as correct ,
ROUND ( SUM ( CASE WHEN qa . is_correct = 1 THEN 1.0 ELSE 0.0 END ) * 100 / COUNT ( * ) , 2 ) as correctRate
FROM quiz_answers qa
JOIN questions q ON qa . question_id = q . id
GROUP BY q . type
` ;
const [ totalUsers , totalRecords , averageScore , typeStats ] = await Promise . all ( [
get ( totalUsersSql ) ,
get ( totalRecordsSql ) ,
get ( averageScoreSql ) ,
query ( typeStatsSql )
] ) ;
return {
totalUsers : totalUsers.total ,
totalRecords : totalRecords.total ,
averageScore : Math.round ( averageScore . average * 100 ) / 100 ,
typeStats
} ;
}
}