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 = []; for (const answer of answers) { const question = await QuestionModel.findById(answer.questionId); if (!question) { 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 isCorrect = answer.userAnswer === question.answer; answer.score = isCorrect ? question.score : 0; answer.isCorrect = isCorrect; } processedAnswers.push(answer); } const result = await QuizModel.submitQuiz({ userId, answers: processedAnswers }); if (subjectId || taskId) { const sql = ` UPDATE quiz_records SET subject_id = ?, task_id = ? WHERE id = ? `; await import('../database').then(({ run }) => run(sql, [subjectId || null, taskId || null, 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 || '获取答题记录失败' }); } } }