electron-vue-exam-single/electron/db/question.js

423 lines
14 KiB
JavaScript

import { getSystemDbPath } from './path.js';
import { executeWithRetry } from './utils.js';
import { getDbConnection } from './index.js';
/**
* 添加新题干及相关数据
* @param {Object} questionData - 题干数据
* @param {string} questionData.question_type - 题型代码
* @param {string} questionData.question_description - 题干描述
* @param {Array} questionData.images - 图片数据数组
* @param {Array} questionData.datasets - 数据集数据数组
* @returns {Promise<number>} 新创建的题干ID
*/
async function addQuestion(questionData) {
const { question_type, question_description, images = [], datasets = [] } = questionData;
const db = await getDbConnection(getSystemDbPath());
return executeWithRetry(db, async () => {
// 开始事务
await db.runAsync('BEGIN TRANSACTION');
try {
// 1. 插入题干基本信息
const questionResult = await db.runAsync(
'INSERT INTO questions (question_type, question_name, question_description, created_at, updated_at) VALUES (?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)',
[question_type, '', question_description]
);
const questionId = questionResult.lastID;
// 2. 插入图片数据
if (images.length > 0) {
for (const image of images) {
await db.runAsync(
'INSERT INTO question_images (question_id, image_name, image_base64, created_at, updated_at) VALUES (?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)',
[questionId, image.image || 'image', image.base64]
);
}
}
// 3. 插入数据集数据
if (datasets.length > 0) {
for (const dataset of datasets) {
await db.runAsync(
'INSERT INTO question_datasets (question_id, dataset_name, dataset_data, created_at, updated_at) VALUES (?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)',
[questionId, dataset.name || 'dataset',dataset.content]
);
}
}
// 提交事务
await db.runAsync('COMMIT');
return questionId;
} catch (error) {
// 回滚事务
await db.runAsync('ROLLBACK');
throw error;
}
});
}
/**
* 获取所有题干
* @returns {Promise<Array>} 题干列表
*/
async function getAllQuestions() {
const db = await getDbConnection(getSystemDbPath());
return executeWithRetry(db, async () => {
return await db.allAsync('SELECT * FROM questions ORDER BY id DESC');
});
}
/**
* 根据ID获取题干详情
* @param {number} id - 题干ID
* @returns {Promise<Object>} 题干详情
*/
async function getQuestionById(id) {
const db = await getDbConnection(getSystemDbPath());
return executeWithRetry(db, async () => {
return await db.getAsync('SELECT * FROM questions WHERE id = ?', [id]);
});
}
/**
* 更新题干信息
* @param {number} id - 题干ID
* @param {Object} questionData - 题干数据
* @returns {Promise<boolean>} 是否更新成功
*/
async function updateQuestion(id, questionData) {
const { question_type, question_description } = questionData;
const db = await getDbConnection(getSystemDbPath());
return executeWithRetry(db, async () => {
const result = await db.runAsync(
'UPDATE questions SET question_type = ?, question_description = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?',
[question_type, question_description, id]
);
return result.changes > 0;
});
}
/**
* 删除题干
* @param {number} id - 题干ID
* @returns {Promise<boolean>} 是否删除成功
*/
async function deleteQuestion(id) {
const db = await getDbConnection(getSystemDbPath());
return executeWithRetry(db, async () => {
// 开始事务
await db.runAsync('BEGIN TRANSACTION');
try {
// 先删除关联数据
await db.runAsync('DELETE FROM question_images WHERE question_id = ?', [id]);
await db.runAsync('DELETE FROM question_datasets WHERE question_id = ?', [id]);
// 删除关联的试题数据
await db.runAsync('DELETE FROM question_choices WHERE question_id = ?', [id]);
await db.runAsync('DELETE FROM question_fill_blanks WHERE question_id = ?', [id]);
await db.runAsync('DELETE FROM question_fill_table WHERE question_id = ?', [id]);
await db.runAsync('DELETE FROM question_fill_table_blanks WHERE question_id = ?', [id]);
await db.runAsync('DELETE FROM question_judge WHERE question_id = ?', [id]);
await db.runAsync('DELETE FROM question_short WHERE question_id = ?', [id]);
// 再删除题干
const result = await db.runAsync('DELETE FROM questions WHERE id = ?', [id]);
// 提交事务
await db.runAsync('COMMIT');
return result.changes > 0;
} catch (error) {
// 回滚事务
await db.runAsync('ROLLBACK');
throw error;
}
});
}
/**
* 获取所有题干及其关联信息
* @returns {Promise<Array>} 包含关联信息的题干列表
*/
async function getAllQuestionsWithRelations() {
const db = await getDbConnection(getSystemDbPath());
return executeWithRetry(db, async () => {
// 1. 查询所有题干基本信息及关联的题型名称
const questions = await db.allAsync(`
SELECT q.*, di.item_name as question_type_name
FROM questions q
LEFT JOIN dict_items di ON q.question_type = di.item_code AND di.type_code = 'question_type'
ORDER BY q.id DESC
`);
// 2. 为每个题干查询关联的图片、数据集和特定类型的问题数据
for (const question of questions) {
// 查询关联的图片
const images = await db.allAsync(
'SELECT * FROM question_images WHERE question_id = ?',
[question.id]
);
question.images = images;
// 查询关联的数据集
const datasets = await db.allAsync(
'SELECT * FROM question_datasets WHERE question_id = ?',
[question.id]
);
datasets.forEach(dataset => {
try {
dataset.dataset_data = JSON.parse(dataset.dataset_data);
} catch (e) {
console.error('解析数据集失败:', e);
dataset.dataset_data = null;
}
});
question.datasets = datasets;
// 根据question_type关联不同的问题表
switch (question.question_type) {
case 'choice':
// 关联选择题表
question.choices = await db.allAsync(
'SELECT * FROM question_choices WHERE question_id = ?',
[question.id]
);
break;
case 'fill_blank':
// 关联填空题表
question.fillBlanks = await db.allAsync(
'SELECT * FROM question_fill_blanks WHERE question_id = ?',
[question.id]
);
break;
case 'fill_table':
// 关联表格填空题表和表格填空项表
question.fillTables = await db.allAsync(
'SELECT * FROM question_fill_table WHERE question_id = ?',
[question.id]
);
// 对每个表格查询其填空项
for (let table of question.fillTables) {
table.blanks = await db.allAsync(
'SELECT * FROM question_fill_table_blanks WHERE table_id = ?',
[table.id]
);
}
break;
case 'true_false':
// 关联判断题表
question.judges = await db.allAsync(
'SELECT * FROM question_judge WHERE question_id = ?',
[question.id]
);
break;
case 'short_answer':
case 'analysis':
case 'essay':
// 关联简答题表
question.shorts = await db.allAsync(
'SELECT * FROM question_short WHERE question_id = ?',
[question.id]
);
break;
default:
// 未知题型,不关联任何表
break;
}
}
return questions;
});
}
/**
* 更新题干描述
* @param {number} id - 题干ID
* @param {string} questionDescription - 新的题干描述
* @returns {Promise<boolean>} 是否更新成功
*/
async function updateQuestionDescription(id, questionDescription) {
const db = await getDbConnection(getSystemDbPath());
return executeWithRetry(db, async () => {
const result = await db.runAsync(
'UPDATE questions SET question_description = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?',
[questionDescription, id]
);
return result.changes > 0;
});
}
/**
* 添加选择题问题
* @param {Object} choiceData - 选择题数据
* @param {number} choiceData.question_id - 题干ID
* @param {string} choiceData.choice_description - 问题描述
* @param {string} choiceData.choice_type - 题型(single/multiple)
* @param {Array} choiceData.choice_options - 候选项数组
* @param {Array} choiceData.correct_answers - 正确答案序号数组
* @returns {Promise<number>} 新创建的选择题ID
*/
async function addChoiceQuestion(choiceData) {
const { question_id, choice_description, choice_type, score, choice_options, correct_answers } = choiceData;
const db = await getDbConnection(getSystemDbPath());
return executeWithRetry(db, async () => {
const result = await db.runAsync(
'INSERT INTO question_choices (question_id, choice_description, choice_type, score, choice_options, correct_answers, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)',
[question_id, choice_description, choice_type, score, JSON.stringify(choice_options), JSON.stringify(correct_answers)]
);
return result.lastID;
});
}
/**
* 添加选择题问题
* @param {number} id - 选择题ID
* @param {Object} choiceData - 选择题数据
* @returns {Promise<boolean>} 是否更新成功
*/
async function updateChoiceQuestion(id, choiceData) {
const { choice_description, choice_type, score, choice_options, correct_answers } = choiceData;
const db = await getDbConnection(getSystemDbPath());
return executeWithRetry(db, async () => {
const result = await db.runAsync(
'UPDATE question_choices SET choice_description = ?, choice_type = ?, score = ?, choice_options = ?, correct_answers = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?',
[choice_description, choice_type, score, JSON.stringify(choice_options), JSON.stringify(correct_answers), id]
);
return result.changes > 0;
});
}
/**
* 添加填空题问题
* @param {Object} fillBlankData - 填空题数据
* @param {number} fillBlankData.question_id - 题干ID
* @param {string} fillBlankData.blank_description - 问题描述
* @param {number} fillBlankData.blank_count - 空白数量
* @param {Array} fillBlankData.correct_answers - 正确答案数组
* @param {number} fillBlankData.score - 分值
* @returns {Promise<number>} 新创建的填空题ID
*/
async function addFillBlankQuestion(fillBlankData) {
const { question_id, blank_description, blank_count, correct_answers, score } = fillBlankData;
const db = await getDbConnection(getSystemDbPath());
return executeWithRetry(db, async () => {
const result = await db.runAsync(
'INSERT INTO question_fill_blanks (question_id, blank_description, blank_count, correct_answers, score, created_at, updated_at) VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)',
[question_id, blank_description, blank_count, JSON.stringify(correct_answers), score]
);
return result.lastID;
});
}
/**
* 更新填空题问题
* @param {number} id - 填空题ID
* @param {Object} fillBlankData - 填空题数据
* @returns {Promise<boolean>} 是否更新成功
*/
async function updateFillBlankQuestion(id, fillBlankData) {
const { blank_description, blank_count, correct_answers, score } = fillBlankData;
const db = await getDbConnection(getSystemDbPath());
return executeWithRetry(db, async () => {
const result = await db.runAsync(
'UPDATE question_fill_blanks SET blank_description = ?, blank_count = ?, correct_answers = ?, score = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?',
[blank_description, blank_count, JSON.stringify(correct_answers), score, id]
);
return result.changes > 0;
});
}
/**
* 删除填空题问题
* @param {number} id - 填空题ID
* @returns {Promise<boolean>} 是否删除成功
*/
async function deleteFillBlankQuestion(id) {
const db = await getDbConnection(getSystemDbPath());
return executeWithRetry(db, async () => {
const result = await db.runAsync('DELETE FROM question_fill_blanks WHERE id = ?', [id]);
return result.changes > 0;
});
}
/**
* 根据题干ID查询填空题问题
* @param {number} questionId - 题干ID
* @returns {Promise<Array>} 填空题列表
*/
async function getFillBlankQuestionsByQuestionId(questionId) {
const db = await getDbConnection(getSystemDbPath());
return executeWithRetry(db, async () => {
const questions = await db.allAsync('SELECT * FROM question_fill_blanks WHERE question_id = ?', [questionId]);
// 解析correct_answers为数组
questions.forEach(question => {
try {
question.correct_answers = JSON.parse(question.correct_answers);
} catch (e) {
console.error('解析填空题答案失败:', e);
question.correct_answers = [];
}
});
return questions;
});
}
/**
* 查询试题总数和总分
* @returns {Promise<{totalQuestions: number, totalScore: number}>} 包含试题总数和总分的对象
*/
async function getQuestionsCountAndScore() {
const db = await getDbConnection(getSystemDbPath());
return executeWithRetry(db, async () => {
// 查询选择题总数和总分
const choiceResult = await db.getAsync(`
SELECT COUNT(*) as count, SUM(qc.score) as score
FROM questions q
LEFT JOIN question_choices qc ON q.id = qc.question_id
WHERE q.question_type = 'choice'
`);
// 查询填空题总数和总分
const fillBlankResult = await db.getAsync(`
SELECT COUNT(*) as count, SUM(qfb.score) as score
FROM questions q
LEFT JOIN question_fill_blanks qfb ON q.id = qfb.question_id
WHERE q.question_type = 'fill_blank'
`);
// 计算总题数和总分
const totalQuestions = (choiceResult.count || 0) + (fillBlankResult.count || 0);
const totalScore = (choiceResult.score || 0) + (fillBlankResult.score || 0);
return {
totalQuestions,
totalScore
};
});
}
export {
addQuestion,
getAllQuestions,
getQuestionById,
updateQuestion,
deleteQuestion,
getAllQuestionsWithRelations,
updateQuestionDescription,
addChoiceQuestion,
updateChoiceQuestion,
addFillBlankQuestion,
updateFillBlankQuestion,
deleteFillBlankQuestion,
getFillBlankQuestionsByQuestionId,
getQuestionsCountAndScore
};