From 1da167638ab856a289fc154419e691d220fbc52d Mon Sep 17 00:00:00 2001 From: chenqiang Date: Sun, 10 Aug 2025 18:34:54 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E8=80=83=E7=94=9F=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E5=90=8E=EF=BC=8C=E5=BC=80=E5=A7=8B=E8=80=83=E8=AF=95?= =?UTF-8?q?=E5=89=8D=E7=9A=84=E8=87=AA=E5=8A=A8=E7=BB=84=E5=8D=B7=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- absolute/path/to/electron/db/examing.js | 0 electron/db/examing.js | 217 ++++++++++++++++++++++++ electron/db/schema.js | 14 +- electron/main.js | 6 + electron/preload.js | 7 +- electron/service/examingService.js | 165 ++++++++++++++++++ src/views/user/ExamineeHomeView.vue | 76 +++++++-- 7 files changed, 460 insertions(+), 25 deletions(-) create mode 100644 absolute/path/to/electron/db/examing.js create mode 100644 electron/db/examing.js create mode 100644 electron/service/examingService.js diff --git a/absolute/path/to/electron/db/examing.js b/absolute/path/to/electron/db/examing.js new file mode 100644 index 0000000..e69de29 diff --git a/electron/db/examing.js b/electron/db/examing.js new file mode 100644 index 0000000..5d7388a --- /dev/null +++ b/electron/db/examing.js @@ -0,0 +1,217 @@ +// 导入必要的模块和函数 +import { getSystemDbPath, getUserDbPath } from './path.js'; +import { getDbConnection } from './index.js'; +import { batchInsert } from './utils.js'; + +/** + * 生成考生试卷 + * @param {Object} examineeData - 考生数据 + * @param {number} examDuration - 考试时长(分钟) + * @returns {Promise} - 包含试卷ID和状态的对象 + */ +// 变更函数入参,从examDuration改为examData +export async function generateExamineePaper(examineeData, examData) { + try { + // 1. 获取数据库连接 + const systemDb = await getDbConnection(getSystemDbPath()); + const userDb = await getDbConnection(getUserDbPath()); + + // 2. 处理考生数据,将空值转换为空字符串 + console.log('开始处理考生数据...'); + const processedExamineeData = { + id: examineeData.id, + examinee_name: examineeData.examinee_name || '', + examinee_gender: examineeData.examinee_gender || '', + examinee_unit: examineeData.examinee_unit || '', + written_exam_room: examineeData.written_exam_room || '', + written_exam_seat: examineeData.written_exam_seat || '', + computer_exam_room: examineeData.computer_exam_room || '', + computer_exam_seat: examineeData.computer_exam_seat || '', + examinee_id_card: examineeData.examinee_id_card || '', + examinee_admission_ticket: examineeData.examinee_admission_ticket || '' + }; + + // 3. 在user库中添加考生数据 + console.log('开始添加考生数据...'); + const examineeResult = await userDb.runAsync( + `INSERT INTO examinee ( + id, examinee_name, examinee_gender, examinee_unit, + written_exam_room, written_exam_seat, computer_exam_room, + computer_exam_seat, examinee_id_card, examinee_admission_ticket, created_at + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)`, + [ + processedExamineeData.id, + processedExamineeData.examinee_name, + processedExamineeData.examinee_gender, + processedExamineeData.examinee_unit, + processedExamineeData.written_exam_room, + processedExamineeData.written_exam_seat, + processedExamineeData.computer_exam_room, + processedExamineeData.computer_exam_seat, + processedExamineeData.examinee_id_card, + processedExamineeData.examinee_admission_ticket + ] + ); + const examineeId = examineeResult.lastID || processedExamineeData.id; + console.log(`成功添加考生数据,ID: ${examineeId}`); + + // 3. 生成examinee_papers记录 + console.log('开始生成试卷记录...'); + // 使用examData中的exam_minutes和exam_minutes_min + const paperResult = await userDb.runAsync( + `INSERT INTO examinee_papers ( + examinee_id, paper_minutes, paper_minuts_min, paper_status + ) VALUES (?, ?, ?, ?)`, + [examineeId, examData.exam_minutes, examData.exam_minutes_min, 0] // 0表示未开始 + ); + const paperId = paperResult.lastID; + console.log(`成功生成试卷记录,ID: ${paperId}`); + + // 4. 从system库获取所有questions记录 + console.log('开始获取系统题库...'); + const systemQuestions = await systemDb.allAsync('SELECT * FROM questions'); + console.log(`成功获取 ${systemQuestions.length} 道题目`); + + // 打乱题目顺序 + const shuffledQuestions = [...systemQuestions].sort(() => Math.random() - 0.5); + + // 5. 准备paper_questions数据 + console.log('开始准备试卷题目数据...'); + const paperQuestionsData = shuffledQuestions.map(question => ({ + examinee_id: examineeId, + paper_id: paperId, + question_type: question.question_type, + question_name: question.question_name, + question_description: question.question_description, + created_at: new Date().toISOString() + })); + + // 创建question_id映射关系(system_id -> user_id) + const questionIdMap = new Map(); + + // 6. 插入paper_questions数据并记录映射关系 + console.log('开始插入试卷题目数据...'); + for (let i = 0; i < paperQuestionsData.length; i++) { + const result = await userDb.runAsync( + `INSERT INTO paper_questions ( + examinee_id, paper_id, question_type, + question_name, question_description, created_at + ) VALUES (?, ?, ?, ?, ?, ?)`, + [ + paperQuestionsData[i].examinee_id, + paperQuestionsData[i].paper_id, + paperQuestionsData[i].question_type, + paperQuestionsData[i].question_name, + paperQuestionsData[i].question_description, + paperQuestionsData[i].created_at + ] + ); + questionIdMap.set(systemQuestions[i].id, result.lastID); + } + console.log(`成功插入 ${paperQuestionsData.length} 条试卷题目数据`); + + // 7. 处理question_datasets表 + console.log('开始处理数据集...'); + const systemDatasets = await systemDb.allAsync('SELECT * FROM question_datasets'); + const userDatasets = systemDatasets + .filter(dataset => questionIdMap.has(dataset.question_id)) + .map(dataset => ({ + question_id: questionIdMap.get(dataset.question_id), + dataset_name: dataset.dataset_name, + dataset_data: dataset.dataset_data, + created_at: new Date().toISOString() + })); + if (userDatasets.length > 0) { + await batchInsert(userDb, 'question_datasets', userDatasets); + } + console.log(`成功处理 ${userDatasets.length} 个数据集`); + + // 8. 处理question_images表 + console.log('开始处理图片...'); + const systemImages = await systemDb.allAsync('SELECT * FROM question_images'); + const userImages = systemImages + .filter(image => questionIdMap.has(image.question_id)) + .map(image => ({ + question_id: questionIdMap.get(image.question_id), + image_name: image.image_name, + image_base64: image.image_base64, + created_at: new Date().toISOString(), + updated_at: new Date().toISOString() + })); + if (userImages.length > 0) { + await batchInsert(userDb, 'question_images', userImages); + } + console.log(`成功处理 ${userImages.length} 张图片`); + + // 9. 处理question_choices表 + console.log('开始处理选择题...'); + const systemChoices = await systemDb.allAsync('SELECT * FROM question_choices'); + let userChoices = systemChoices + .filter(choice => questionIdMap.has(choice.question_id)) + .map(choice => { + // 打乱选项顺序 + const options = JSON.parse(choice.choice_options); + const shuffledOptions = [...options].sort(() => Math.random() - 0.5); + + return { + question_id: questionIdMap.get(choice.question_id), + choice_description: choice.choice_description, + choice_type: choice.choice_type, + choice_options: JSON.stringify(shuffledOptions), + correct_answers: choice.correct_answers, + examinee_answers: '', + score: choice.score, + score_real: 0, + created_at: new Date().toISOString(), + updated_at: new Date().toISOString() + }; + }); + + // 打乱选择题顺序 + userChoices = userChoices.sort(() => Math.random() - 0.5); + + if (userChoices.length > 0) { + await batchInsert(userDb, 'question_choices', userChoices); + } + console.log(`成功处理 ${userChoices.length} 道选择题`); + + // 10. 处理question_fill_blanks表 + console.log('开始处理填空题...'); + const systemFillBlanks = await systemDb.allAsync('SELECT * FROM question_fill_blanks'); + let userFillBlanks = systemFillBlanks + .filter(blank => questionIdMap.has(blank.question_id)) + .map(blank => ({ + question_id: questionIdMap.get(blank.question_id), + blank_description: blank.blank_description, + blank_count: blank.blank_count, + correct_answers: blank.correct_answers, + examinee_answers: '', + score: blank.score, + score_real: 0, + created_at: new Date().toISOString(), + updated_at: new Date().toISOString() + })); + + // 打乱填空题顺序 + userFillBlanks = userFillBlanks.sort(() => Math.random() - 0.5); + + if (userFillBlanks.length > 0) { + await batchInsert(userDb, 'question_fill_blanks', userFillBlanks); + } + console.log(`成功处理 ${userFillBlanks.length} 道填空题`); + + // 返回成功结果 + return { + success: true, + paperId, + examineeId, + message: '试卷生成成功' + }; + } catch (error) { + console.error('生成试卷失败:', error); + return { + success: false, + message: `生成试卷失败: ${error.message}` + }; + } +} \ No newline at end of file diff --git a/electron/db/schema.js b/electron/db/schema.js index ee8e885..9ee1760 100644 --- a/electron/db/schema.js +++ b/electron/db/schema.js @@ -13,7 +13,7 @@ export const systemSchema = { config: ` CREATE TABLE IF NOT EXISTS config ( id INTEGER PRIMARY KEY AUTOINCREMENT, - key TEXT NOT NULL, + key TEXT NOT NULL UNIQUE, value TEXT NOT NULL, protected INTEGER DEFAULT 0 ); @@ -187,12 +187,12 @@ export const userSchema = { CREATE TABLE IF NOT EXISTS examinee ( id INTEGER PRIMARY KEY AUTOINCREMENT, examinee_name TEXT NOT NULL DEFAULT '', - examinee_gender TEXT NOT NULL DEFAULT '', - examinee_unit TEXT NOT NULL DEFAULT '', - written_exam_room TEXT NOT NULL DEFAULT '', - written_exam_seat TEXT NOT NULL DEFAULT '', - computer_exam_room TEXT NOT NULL DEFAULT '', - computer_exam_seat TEXT NOT NULL DEFAULT '', + examinee_gender TEXT, + examinee_unit TEXT, + written_exam_room TEXT, + written_exam_seat TEXT, + computer_exam_room TEXT, + computer_exam_seat TEXT, examinee_id_card TEXT NOT NULL DEFAULT '', examinee_admission_ticket TEXT NOT NULL DEFAULT '', created_at TEXT DEFAULT CURRENT_TIMESTAMP diff --git a/electron/main.js b/electron/main.js index 468d94b..d325ead 100644 --- a/electron/main.js +++ b/electron/main.js @@ -27,6 +27,10 @@ import { initExamineeIpc } from "./service/examineeService.js"; +import { + initExamingIpc +} from "./service/examingService.js"; + // 定义 __dirname 和 __filename const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -157,6 +161,8 @@ function setupIpcMain() { // 初始化考生相关IPC initExamineeIpc(ipcMain); + + initExamingIpc(ipcMain); } // 确保在 app.whenReady() 中调用 setupIpcMain() diff --git a/electron/preload.js b/electron/preload.js index 881db27..3249b33 100644 --- a/electron/preload.js +++ b/electron/preload.js @@ -63,5 +63,10 @@ contextBridge.exposeInMainWorld('electronAPI', { createExaminee: (examineeData) => ipcRenderer.invoke('examinee-create', examineeData), updateExaminee: (id, examineeData) => ipcRenderer.invoke('examinee-update', { id, examineeData }), deleteExaminee: (id) => ipcRenderer.invoke('examinee-delete', id), - userLogin: (idCard, admissionTicket) => ipcRenderer.invoke('user-login', { idCard, admissionTicket }) + userLogin: (idCard, admissionTicket) => ipcRenderer.invoke('user-login', { idCard, admissionTicket }), + + // 考试进行相关API + generateExamineePaper: (examineeData, examData) => ipcRenderer.invoke('examing-generate-paper', { examineeData, examData }), + getExamineePaperStatus: (examineeId) => ipcRenderer.invoke('examing-get-paper-status', examineeId), + updatePaperStatus: (paperId, statusData) => ipcRenderer.invoke('examing-update-paper-status', { paperId, statusData }), }); \ No newline at end of file diff --git a/electron/service/examingService.js b/electron/service/examingService.js new file mode 100644 index 0000000..2fbfd92 --- /dev/null +++ b/electron/service/examingService.js @@ -0,0 +1,165 @@ +// 导入数据库操作函数 +import { generateExamineePaper } from '../db/examing.js'; +import { getDbConnection, closeAllConnections } from '../db/index.js'; +import { getUserDbPath } from '../db/path.js'; + +/** + * 服务层:生成考生试卷 + * @param {Object} examineeData - 考生数据 + * @param {number} examDuration - 考试时长(分钟) + * @returns {Promise} - 包含试卷ID和状态的对象 + */ +// 变更函数入参和验证逻辑 +export async function generateExamineePaperService(examineeData, examData) { + try { + // 数据验证 + if (!examineeData || !examineeData.id || !examineeData.examinee_name) { + throw new Error('考生数据不完整,必须包含ID和姓名'); + } + + if (!examData || !examData.exam_minutes || examData.exam_minutes <= 0) { + throw new Error('考试时长必须为正数'); + } + + if (examData.exam_minutes_min === undefined || examData.exam_minutes_min < 0) { + throw new Error('最短考试时长必须为非负数'); + } + + const result = await generateExamineePaper(examineeData, examData); + return result; + } catch (error) { + console.error('服务层: 生成考生试卷失败', error); + return { + success: false, + message: `生成试卷失败: ${error.message}` + }; + } +} + +/** + * 服务层:获取考生试卷状态 + * @param {number} examineeId - 考生ID + * @returns {Promise} - 试卷状态信息 + */ +export async function getExamineePaperStatusService(examineeId) { + try { + if (!examineeId || examineeId <= 0) { + throw new Error('考生ID必须为正数'); + } + + const userDb = await getDbConnection(getUserDbPath()); + const paperStatus = await userDb.getAsync( + 'SELECT * FROM examinee_papers WHERE examinee_id = ?', + [examineeId] + ); + + return paperStatus; + } catch (error) { + console.error('服务层: 获取考生试卷状态失败', error); + throw error; + } +} + +/** + * 服务层:更新试卷状态 + * @param {number} paperId - 试卷ID + * @param {Object} statusData - 状态数据 + * @returns {Promise} - 是否更新成功 + */ +export async function updatePaperStatusService(paperId, statusData) { + try { + if (!paperId || paperId <= 0) { + throw new Error('试卷ID必须为正数'); + } + + const userDb = await getDbConnection(getUserDbPath()); + + // 构建更新字段 + const fields = []; + const values = []; + + if (statusData.paper_start_time !== undefined) { + fields.push('paper_start_time = ?'); + values.push(statusData.paper_start_time); + } + + if (statusData.paper_last_time !== undefined) { + fields.push('paper_last_time = ?'); + values.push(statusData.paper_last_time); + } + + if (statusData.paper_submit_time !== undefined) { + fields.push('paper_submit_time = ?'); + values.push(statusData.paper_submit_time); + } + + if (statusData.paper_end_time !== undefined) { + fields.push('paper_end_time = ?'); + values.push(statusData.paper_end_time); + } + + if (statusData.paper_status !== undefined) { + fields.push('paper_status = ?'); + values.push(statusData.paper_status); + } + + if (statusData.paper_score_real !== undefined) { + fields.push('paper_score_real = ?'); + values.push(statusData.paper_score_real); + } + + if (fields.length === 0) { + return true; // 没有需要更新的字段 + } + + // 添加WHERE条件的值 + values.push(paperId); + + const sql = `UPDATE examinee_papers SET ${fields.join(', ')} WHERE id = ?`; + await userDb.runAsync(sql, values); + + return true; + } catch (error) { + console.error('服务层: 更新试卷状态失败', error); + throw error; + } +} + +/** + * 初始化考试相关的IPC处理程序 + * @param {import('electron').IpcMain} ipcMain - IPC主进程实例 + */ +export function initExamingIpc(ipcMain) { + // 生成考生试卷 + ipcMain.handle('examing-generate-paper', async (event, { examineeData, examData }) => { + try { + return await generateExamineePaperService(examineeData, examData); + } catch (error) { + console.error('生成考生试卷失败:', error); + return { + success: false, + message: `生成试卷失败: ${error.message}` + }; + } + }); + + // 获取考生试卷状态 + ipcMain.handle('examing-get-paper-status', async (event, examineeId) => { + try { + return await getExamineePaperStatusService(examineeId); + } catch (error) { + console.error('获取考生试卷状态失败:', error); + return null; + } + }); + + // 更新试卷状态 + ipcMain.handle('examing-update-paper-status', async (event, { paperId, statusData }) => { + try { + return await updatePaperStatusService(paperId, statusData); + } catch (error) { + console.error('更新试卷状态失败:', error); + return false; + } + }); +} \ No newline at end of file diff --git a/src/views/user/ExamineeHomeView.vue b/src/views/user/ExamineeHomeView.vue index e6ab6eb..a5fe14b 100644 --- a/src/views/user/ExamineeHomeView.vue +++ b/src/views/user/ExamineeHomeView.vue @@ -139,8 +139,9 @@ import Header from '@/components/common/Header.vue' import Footer from '@/components/common/Footer.vue' import { useRouter } from 'vue-router' -import { ref, computed, onMounted, watch } from 'vue' // 添加watch导入 -import { ElMessage, ElDescriptions, ElDescriptionsItem, ElIcon } from 'element-plus' +import { ref, computed, onMounted, watch } from 'vue' +// 添加ElMessageBox的导入 +import { ElMessage, ElDescriptions, ElDescriptionsItem, ElIcon, ElMessageBox } from 'element-plus' import { User, Postcard, Ticket, Clock, ScaleToOriginal, Timer } from '@element-plus/icons-vue' // 导入用户状态管理 @@ -255,25 +256,66 @@ const startExam = async () => { return } - console.log('开始考试 - 调用接口') - // isLoading.value = true + if (!examinee.value) { + ElMessage.warning('未获取到考生信息') + return + } + + isLoading.value = true try { - // 这里可以添加开始考试的逻辑 - // 例如,调用API获取考试信息 - // const examInfo = await window.electronAPI.startExam(lastExam.value.id) + console.log('开始生成试卷...') - // 模拟API调用延迟 - setTimeout(() => { - console.log('开始考试 - 成功') - ElMessage.success('即将开始考试!') - // 跳转到考试页面 - router.push('/examinee/examing') - isLoading.value = false - }, 1000) + // 创建可序列化的考生数据对象 + const examineeData = { + id: examinee.value.id, + examinee_name: examinee.value.examinee_name || '', + examinee_id_card: examinee.value.examinee_id_card || '', + examinee_admission_ticket: examinee.value.examinee_admission_ticket || '', + examinee_gender: examinee.value.examinee_gender || '', + examinee_unit: examinee.value.examinee_unit || '', + written_exam_room: examinee.value.written_exam_room || '', + written_exam_seat: examinee.value.written_exam_seat || '', + computer_exam_room: examinee.value.computer_exam_room || '', + computer_exam_seat: examinee.value.computer_exam_seat || '' + } + + // 使用JSON序列化/反序列化确保对象可克隆 + const examData = JSON.parse(JSON.stringify({ + id: lastExam.value.id, + exam_name: lastExam.value.exam_name || '', + exam_description: lastExam.value.exam_description || '', + exam_minutes: lastExam.value.exam_minutes || 0, + exam_minutes_min: lastExam.value.exam_minutes_min || 0, + exam_notice: lastExam.value.exam_notice || [] + })) + + // 调用生成试卷API + const result = await window.electronAPI.generateExamineePaper( + examineeData, + examData + ) + + if (result.success) { + console.log('生成试卷成功:', result) + ElMessageBox.alert( + '已完成组卷,点击"进入考试"开始答题', + '组卷完成', + { + confirmButtonText: '进入考试', + type: 'success' + } + ).then(() => { + router.push('/examinee/examing') + }) + } else { + console.error('生成试卷失败:', result) + ElMessage.error(`生成试卷失败: ${result.message || '未知错误'}`) + } } catch (error) { - console.error('开始考试 - 异常:', error) - ElMessage.error(`无法开始考试: ${error.message || '未知错误'}`) + console.error('生成试卷异常:', error) + ElMessage.error(`无法生成试卷: ${error.message || '未知错误'}`) + } finally { isLoading.value = false } }