import { Request, Response } from 'express'; import { QuestionModel, QuizModel, SystemConfigModel } from '../models'; import { Question } from '../models/question'; export class QuizController { static async generateQuiz(req: Request, res: Response) { try { const { userId, subjectId, taskId } = req.body; if (!userId) { return res.status(400).json({ success: false, message: '用户ID不能为空' }); } if (taskId) { const { ExamTaskModel } = await import('../models/examTask'); const result = await ExamTaskModel.generateQuizQuestions(taskId, userId); res.json({ success: true, data: { questions: result.questions, totalScore: result.totalScore, timeLimit: result.timeLimitMinutes } }); return; } if (!subjectId) { return res.status(400).json({ success: false, message: 'subjectId或taskId必须提供其一' }); } const { ExamSubjectModel } = await import('../models/examSubject'); const subject = await ExamSubjectModel.findById(subjectId); if (!subject) { return res.status(404).json({ success: false, message: '考试科目不存在' }); } const result = await ExamSubjectModel.generateQuizQuestions(subject); res.json({ success: true, data: { questions: result.questions, totalScore: result.totalScore, timeLimit: result.timeLimitMinutes } }); } catch (error: any) { const message = error?.message || '生成试卷失败'; const status = message.includes('不存在') ? 404 : [ '用户ID不能为空', 'subjectId或taskId必须提供其一', '当前时间不在任务有效范围内', '用户未被分派到此任务', '考试次数已用尽', ].some((m) => message.includes(m)) ? 400 : 500; res.status(status).json({ success: false, message, }); } } static async submitQuiz(req: Request, res: Response) { try { const { userId, subjectId, taskId, answers } = req.body; if (!userId || !answers || !Array.isArray(answers)) { return res.status(400).json({ success: false, message: '参数不完整' }); } const processedAnswers = []; let totalPossibleScore = 0; for (const answer of answers) { const question = await QuestionModel.findById(answer.questionId); if (!question) { processedAnswers.push(answer); continue; } totalPossibleScore += Number(question.score) || 0; if (answer.userAnswer === undefined || answer.userAnswer === null) { answer.userAnswer = ''; } // 规则:分值为0的题目不判定正误,不要求答案,默认正确 if (Number(question.score) === 0) { answer.score = 0; answer.isCorrect = true; processedAnswers.push(answer); continue; } if (question.type === 'multiple') { const optionCount = question.options ? question.options.length : 0; const unitScore = optionCount > 0 ? question.score / optionCount : 0; let userAnsList: string[] = []; if (Array.isArray(answer.userAnswer)) { userAnsList = answer.userAnswer; } else if (typeof answer.userAnswer === 'string') { try { userAnsList = JSON.parse(answer.userAnswer); } catch (e) { userAnsList = [answer.userAnswer]; } } let correctAnsList: string[] = []; if (Array.isArray(question.answer)) { correctAnsList = question.answer; } else if (typeof question.answer === 'string') { try { const parsed = JSON.parse(question.answer); if (Array.isArray(parsed)) correctAnsList = parsed; else correctAnsList = [question.answer]; } catch { correctAnsList = [question.answer]; } } const userSet = new Set(userAnsList); const correctSet = new Set(correctAnsList); let isFullCorrect = true; if (userSet.size !== correctSet.size) { isFullCorrect = false; } else { for (const a of userSet) { if (!correctSet.has(a)) { isFullCorrect = false; break; } } } if (isFullCorrect) { answer.score = question.score; answer.isCorrect = true; } else { let tempScore = 0; for (const uAns of userAnsList) { if (correctSet.has(uAns)) { tempScore += unitScore; } else { tempScore -= unitScore; } } let finalScore = Math.max(0, tempScore); finalScore = Math.round(finalScore * 10) / 10; answer.score = finalScore; answer.isCorrect = false; } } else if (question.type === 'single' || question.type === 'judgment') { const normalizeJudgment = (raw: unknown) => { const v = String(raw ?? '').trim(); const yes = new Set(['A', 'T', 'TRUE', 'True', 'true', '1', '正确', '对', '是', 'Y', 'y', 'YES', 'yes']); const no = new Set(['B', 'F', 'FALSE', 'False', 'false', '0', '错误', '错', '否', '不是', 'N', 'n', 'NO', 'no']); if (yes.has(v)) return '正确'; if (no.has(v)) return '错误'; return v; }; const isCorrect = question.type === 'judgment' ? normalizeJudgment(answer.userAnswer) === normalizeJudgment(question.answer) : answer.userAnswer === question.answer; answer.score = isCorrect ? question.score : 0; answer.isCorrect = isCorrect; } processedAnswers.push(answer); } const result = await QuizModel.submitQuiz({ userId, answers: processedAnswers, totalPossibleScore }); if (subjectId || taskId) { let finalSubjectId: string | null = subjectId || null; const finalTaskId: string | null = taskId || null; // 任务考试场景下,如果前端未传subjectId,则从任务中反查 if (!finalSubjectId && finalTaskId) { const { ExamTaskModel } = await import('../models/examTask'); const task = await ExamTaskModel.findById(finalTaskId); if (task?.subjectId) { finalSubjectId = task.subjectId; } } const sql = ` UPDATE quiz_records SET subject_id = ?, task_id = ? WHERE id = ? `; await import('../database').then(({ run }) => run(sql, [finalSubjectId, finalTaskId, result.record.id])); } res.json({ success: true, data: { recordId: result.record.id, totalScore: result.record.totalScore, correctCount: result.record.correctCount, totalCount: result.record.totalCount } }); } catch (error: any) { res.status(500).json({ success: false, message: error.message || '提交答题失败' }); } } static async getUserRecords(req: Request, res: Response) { try { const { userId } = req.params; const page = parseInt(req.query.page as string) || 1; const limit = parseInt(req.query.limit as string) || 10; const result = await QuizModel.findRecordsByUserId(userId, limit, (page - 1) * limit); res.json({ success: true, data: result.records, pagination: { page, limit, total: result.total, pages: Math.ceil(result.total / limit) } }); } catch (error: any) { res.status(500).json({ success: false, message: error.message || '获取答题记录失败' }); } } static async getRecordDetail(req: Request, res: Response) { try { const { recordId } = req.params; const record = await QuizModel.findRecordById(recordId); if (!record) { return res.status(404).json({ success: false, message: '答题记录不存在' }); } const answers = await QuizModel.findAnswersByRecordId(recordId); res.json({ success: true, data: { record, answers } }); } catch (error: any) { res.status(500).json({ success: false, message: error.message || '获取答题记录详情失败' }); } } static async getAllRecords(req: Request, res: Response) { try { const page = parseInt(req.query.page as string) || 1; const limit = parseInt(req.query.limit as string) || 10; const result = await QuizModel.findAllRecords(limit, (page - 1) * limit); res.json({ success: true, data: result.records, pagination: { page, limit, total: result.total, pages: Math.ceil(result.total / limit) } }); } catch (error: any) { res.status(500).json({ success: false, message: error.message || '获取答题记录失败' }); } } }