/** * 客观题结果服务模块 * 负责试卷结果数据获取和处理 */ import { questionApi } from '../../config/question_api.js'; /** * 格式化时间显示(秒数转为 MM:SS 或 HH:MM:SS 格式) * @param {Number} totalSeconds - 总秒数 * @returns {String} 格式化后的时间字符串 */ export function formatTime(totalSeconds) { if (totalSeconds < 0) totalSeconds = 0; const hours = Math.floor(totalSeconds / 3600); const minutes = Math.floor((totalSeconds % 3600) / 60); const seconds = totalSeconds % 60; const formatNumber = (num) => { return num < 10 ? '0' + num : String(num); }; if (hours > 0) { return `${formatNumber(hours)}:${formatNumber(minutes)}:${formatNumber(seconds)}`; } else { return `${formatNumber(minutes)}:${formatNumber(seconds)}`; } } /** * 获取试卷和答题记录 * @param {String} paperId - 试卷ID * @param {String} taskId - 任务ID * @param {String} userId - 用户ID * @returns {Promise} 处理后的结果数据 */ export function fetchQuestionPaper(paperId, taskId, userId) { return new Promise((resolve, reject) => { if (!paperId) { reject(new Error('试卷ID不能为空')); return; } if (!userId) { reject(new Error('用户未登录')); return; } Promise.all([ questionApi.getAnswerRecord(userId, paperId, 1, 361), questionApi.getPaperWithAnswers(paperId), // 使用新接口获取包含答案和解析的试卷数据 ]).then(([recordRes, paperRes]) => { // 处理试卷数据 let paperInfo = null; if (paperRes.code === 200 && paperRes.data) { paperInfo = paperRes.data; } else if (paperRes.success === true && (paperRes.paper || paperRes.data)) { paperInfo = paperRes.paper || paperRes.data; } if (!paperInfo) { reject(new Error(paperRes.msg || '获取试卷失败')); return; } // 处理答题记录 const records = recordRes.records || recordRes.data || []; const answerRecordMap = {}; records.forEach(record => { answerRecordMap[record.question_id] = record; }); // 统计信息 const totalCount = records.length; const correctCount = records.filter(r => r.is_correct === true).length; const correctRate = totalCount > 0 ? (correctCount / totalCount * 100) : 0; // 计算总用时 let usedTimeSeconds = 0; if (records.length > 0) { const sortedRecords = records.sort((a, b) => a.start_time - b.start_time); const firstStartTime = sortedRecords[0].start_time; const lastEndTime = sortedRecords[sortedRecords.length - 1].end_time; usedTimeSeconds = lastEndTime - firstStartTime; } const usedTime = formatTime(usedTimeSeconds); // 合并试卷题目和答题记录 const mapped = paperInfo.questions.map(q => { let mappedQ = null; if (q && (q.question_id || q.question_content || q.question_type)) { // 旧结构 mappedQ = { ...q, id: q.question_id, title: q.question_content, type: q.question_type, materials: q.materials || [], options: (q.options || []).map(opt => ({ ...opt, id: opt.option_id, content: opt.option_content, label: opt.option_label })), explanation: q.explanation || '' }; } else { // 新结构 const opts = Array.isArray(q.options) ? q.options.map((text, idx) => { if (typeof text === 'string') { return { id: idx + 1, content: text, label: String.fromCharCode(65 + idx) }; } else { return { ...text, id: text.id || text.option_id || (idx + 1), content: text.content || text.option_content || text, label: text.label || text.option_label || String.fromCharCode(65 + idx) }; } }) : []; const mappedType = (q.type === 2 || q.type === 'single_choice') ? 'single_choice' : (q.type === 3 || q.type === 'multiple_choice' ? 'multiple_choice' : 'single_choice'); mappedQ = { ...q, id: q.id, title: q.title || q.content || '', type: mappedType, materials: q.materials || [], options: opts, explanation: q.explanation || '' }; } // 获取答题记录 const record = answerRecordMap[mappedQ.id]; const userAnswer = record ? record.user_answer : ''; // 优先使用题目本身的答案(新接口会返回),如果没有则使用答题记录中的答案 const correctAnswer = q.answer || (record ? record.correct_answer : ''); const isCorrect = record ? record.is_correct : false; // 处理用户答案 const userAnswerArray = userAnswer ? (Array.isArray(userAnswer) ? userAnswer : [userAnswer]) : []; const isUserAnswerEmpty = !userAnswer || userAnswer === ''; // 处理选项样式 const optionsWithStyle = mappedQ.options.map((opt, optIndex) => { const optionLabel = opt.label || opt.option_label; const isOptionCorrect = opt.is_correct || (opt.answer === 'true') || (optionLabel === correctAnswer); const isUserSelected = userAnswerArray.includes(optionLabel); let cssClass = ''; if (isOptionCorrect) { cssClass = 'option-correct'; } else if (isUserSelected) { cssClass = 'option-wrong'; } // 确保每个选项都有唯一的 id const optionId = opt.id || opt.option_id || (mappedQ.id ? `${mappedQ.id}_opt_${optIndex}` : `opt_${optIndex}`); return { ...opt, id: optionId, content: opt.content || opt.option_content, label: optionLabel, isCorrect: isOptionCorrect, cssClass: cssClass }; }); // 获取解析内容,优先使用题目本身的解析(新接口会返回) // 注意:空字符串也是有效值,需要明确检查 let answerAnalysis = ''; if (q.explanation !== undefined && q.explanation !== null && q.explanation !== '') { answerAnalysis = q.explanation; } else if (mappedQ.explanation !== undefined && mappedQ.explanation !== null && mappedQ.explanation !== '') { answerAnalysis = mappedQ.explanation; } else if (q.answer_analysis !== undefined && q.answer_analysis !== null && q.answer_analysis !== '') { answerAnalysis = q.answer_analysis; } return { ...mappedQ, options: optionsWithStyle, cssClass: isCorrect ? 'arc-correct' : !isUserAnswerEmpty ? 'arc-wrong' : 'arc-unanswered', correct_answer: correctAnswer, user_answer: userAnswer, is_correct: isCorrect, answer_analysis: answerAnalysis }; }); resolve({ paperInfo, questions: mapped, stats: { totalCount, correctCount, correctRate, usedTime, usedTimeSeconds } }); }) .catch(err => { console.error('API调用失败:', err); reject(err); }); }); }