迁移config, dict, question接口
This commit is contained in:
parent
f3b783bf86
commit
6022e8e17a
143
src/background/db/config.js
Normal file
143
src/background/db/config.js
Normal file
@ -0,0 +1,143 @@
|
||||
const { getSystemDbPath } = require('./path.js');
|
||||
const { executeWithRetry } = require('./utils.js');
|
||||
const { getDbConnection } = require('./index.js');
|
||||
|
||||
/**
|
||||
* 从config表获取配置项
|
||||
* @param {string} key - 配置项键名
|
||||
* @returns {Promise<{key: string, value: string} | null>} 配置项对象,如果不存在返回null
|
||||
*/
|
||||
async function getConfig(key) {
|
||||
try {
|
||||
const db = await getDbConnection(getSystemDbPath());
|
||||
|
||||
const result = await executeWithRetry(db, async () => {
|
||||
return await db.getAsync('SELECT * FROM config WHERE key = ?', [key]);
|
||||
});
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error(`获取配置项${key}失败:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有配置项列表
|
||||
* @returns {Promise<Array<{id: number, key: string, value: string, protected: number}>>} 配置项列表
|
||||
*/
|
||||
async function getAllConfigs() {
|
||||
try {
|
||||
const db = await getDbConnection(getSystemDbPath());
|
||||
|
||||
const result = await executeWithRetry(db, async () => {
|
||||
return await db.allAsync('SELECT * FROM config');
|
||||
});
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('获取配置项列表失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过ID获取配置项
|
||||
* @param {number} id - 配置项ID
|
||||
* @returns {Promise<{id: number, key: string, value: string, protected: number} | null>} 配置项对象,如果不存在返回null
|
||||
*/
|
||||
async function getConfigById(id) {
|
||||
try {
|
||||
const db = await getDbConnection(getSystemDbPath());
|
||||
|
||||
const result = await executeWithRetry(db, async () => {
|
||||
return await db.getAsync('SELECT * FROM config WHERE id = ?', [id]);
|
||||
});
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error(`通过ID获取配置项${id}失败:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新或插入配置项
|
||||
* @param {string} key - 配置项键名
|
||||
* @param {string} value - 配置项值
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function setConfig(key, value) {
|
||||
try {
|
||||
const db = await getDbConnection(getSystemDbPath());
|
||||
|
||||
// 先检查是否存在
|
||||
const existing = await executeWithRetry(db, async () => {
|
||||
return await db.getAsync('SELECT * FROM config WHERE key = ?', [key]);
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
// 检查是否受保护
|
||||
// if (existing.protected === 1) {
|
||||
// throw new Error(`配置项${key}是受保护的,无法修改`);
|
||||
// }
|
||||
// 更新
|
||||
await executeWithRetry(db, async () => {
|
||||
await db.runAsync('UPDATE config SET value = ? WHERE key = ?', [value, key]);
|
||||
console.log(`成功更新配置项: ${key}`);
|
||||
});
|
||||
} else {
|
||||
// 插入 (默认不保护)
|
||||
await executeWithRetry(db, async () => {
|
||||
await db.runAsync('INSERT INTO config (key, value, protected) VALUES (?, ?, 0)', [key, value]);
|
||||
console.log(`成功插入配置项: ${key}`);
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`设置配置项${key}失败:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除配置项
|
||||
* @param {number} id - 配置项ID
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function deleteConfig(id) {
|
||||
try {
|
||||
const db = await getDbConnection(getSystemDbPath());
|
||||
|
||||
// 先检查是否存在
|
||||
const existing = await executeWithRetry(db, async () => {
|
||||
return await db.getAsync('SELECT * FROM config WHERE id = ?', [id]);
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
throw new Error(`配置项ID ${id} 不存在`);
|
||||
}
|
||||
|
||||
// 检查是否受保护
|
||||
if (existing.protected === 1) {
|
||||
throw new Error(`配置项 ${existing.key} 是受保护的,无法删除`);
|
||||
}
|
||||
|
||||
// 删除
|
||||
await executeWithRetry(db, async () => {
|
||||
await db.runAsync('DELETE FROM config WHERE id = ?', [id]);
|
||||
console.log(`成功删除配置项ID: ${id}`);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`删除配置项ID ${id} 失败:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 导出使用CommonJS格式
|
||||
module.exports = {
|
||||
getConfig,
|
||||
setConfig,
|
||||
getAllConfigs,
|
||||
getConfigById,
|
||||
deleteConfig
|
||||
};
|
259
src/background/db/dict.js
Normal file
259
src/background/db/dict.js
Normal file
@ -0,0 +1,259 @@
|
||||
const { getSystemDbPath } = require('./path.js');
|
||||
const { executeWithRetry } = require('./utils.js');
|
||||
const { getDbConnection } = require('./index.js');
|
||||
|
||||
/**
|
||||
* 查询dict_items连接dict_types后的记录列表
|
||||
* @returns {Promise<Array>} 记录列表
|
||||
*/
|
||||
async function getDictItemsWithTypes() {
|
||||
const db = await getDbConnection(getSystemDbPath());
|
||||
return executeWithRetry(db, async () => {
|
||||
const sql = `
|
||||
SELECT di.*, dt.type_name
|
||||
FROM dict_items di
|
||||
JOIN dict_types dt ON di.type_code = dt.type_code
|
||||
ORDER BY dt.type_code, di.item_code
|
||||
`;
|
||||
return await db.allAsync(sql);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询dict_types列表
|
||||
* @returns {Promise<Array>} 类型列表
|
||||
*/
|
||||
async function getDictTypes() {
|
||||
const db = await getDbConnection(getSystemDbPath());
|
||||
return executeWithRetry(db, async () => {
|
||||
const sql = 'SELECT * FROM dict_types ORDER BY type_code';
|
||||
return await db.allAsync(sql);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加dict_types
|
||||
* @param {Object} typeData 类型数据
|
||||
* @returns {Promise<Object>} 添加的类型
|
||||
*/
|
||||
async function addDictType(typeData) {
|
||||
const { type_code, type_name, description } = typeData;
|
||||
const db = await getDbConnection(getSystemDbPath());
|
||||
return executeWithRetry(db, async () => {
|
||||
const sql = 'INSERT INTO dict_types (type_code, type_name, description) VALUES (?, ?, ?)';
|
||||
const result = await db.runAsync(sql, [type_code, type_name, description || null]);
|
||||
return { id: result.lastID, ...typeData };
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加dict_items
|
||||
* @param {Object} itemData 字典项数据
|
||||
* @returns {Promise<Object>} 添加的字典项
|
||||
*/
|
||||
async function addDictItem(itemData) {
|
||||
const { type_code, item_code, item_name, item_description, parent_code, is_active } = itemData;
|
||||
const db = await getDbConnection(getSystemDbPath());
|
||||
return executeWithRetry(db, async () => {
|
||||
const sql = 'INSERT INTO dict_items (type_code, item_code, item_name, item_description, parent_code, is_active) VALUES (?, ?, ?, ?, ?, ?)';
|
||||
const result = await db.runAsync(sql, [
|
||||
type_code,
|
||||
item_code,
|
||||
item_name,
|
||||
item_description || null,
|
||||
parent_code || null,
|
||||
is_active !== undefined ? is_active : 1
|
||||
]);
|
||||
|
||||
// 检查result是否存在,如果不存在则获取最后插入的ID
|
||||
let lastId;
|
||||
if (result && result.lastID) {
|
||||
lastId = result.lastID;
|
||||
} else {
|
||||
// 使用另一种方式获取最后插入的ID
|
||||
const idResult = await db.getAsync('SELECT last_insert_rowid() as id');
|
||||
lastId = idResult ? idResult.id : null;
|
||||
}
|
||||
|
||||
if (!lastId) {
|
||||
throw new Error('无法获取插入的字典项ID');
|
||||
}
|
||||
|
||||
return { id: lastId, ...itemData };
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询一条dict_types
|
||||
* @param {number} id 类型ID
|
||||
* @returns {Promise<Object|null>} 类型数据或null
|
||||
*/
|
||||
async function getDictTypeById(id) {
|
||||
const db = await getDbConnection(getSystemDbPath());
|
||||
return executeWithRetry(db, async () => {
|
||||
const sql = 'SELECT * FROM dict_types WHERE id = ?';
|
||||
return await db.getAsync(sql, [id]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询一条dict_items
|
||||
* @param {number} id 字典项ID
|
||||
* @returns {Promise<Object|null>} 字典项数据或null
|
||||
*/
|
||||
async function getDictItemById(id) {
|
||||
const db = await getDbConnection(getSystemDbPath());
|
||||
return executeWithRetry(db, async () => {
|
||||
const sql = 'SELECT * FROM dict_items WHERE id = ?';
|
||||
return await db.getAsync(sql, [id]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据dict_types的type_code查询dict_items列表
|
||||
* @param {string} typeCode 类型编码
|
||||
* @param {number} isActive 是否激活(1=激活, 0=未激活, 不传=全部)
|
||||
* @returns {Promise<Array>} 字典项列表
|
||||
*/
|
||||
async function getDictItemsByTypeCode(typeCode, isActive = undefined) {
|
||||
const db = await getDbConnection(getSystemDbPath());
|
||||
return executeWithRetry(db, async () => {
|
||||
let sql = 'SELECT * FROM dict_items WHERE type_code = ?';
|
||||
const params = [typeCode];
|
||||
|
||||
if (isActive !== undefined) {
|
||||
sql += ' AND is_active = ?';
|
||||
params.push(isActive);
|
||||
}
|
||||
|
||||
sql += ' ORDER BY item_code';
|
||||
return await db.allAsync(sql, params);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新一条dict_types
|
||||
* @param {number} id 类型ID
|
||||
* @param {Object} typeData 更新的数据
|
||||
* @returns {Promise<boolean>} 是否更新成功
|
||||
*/
|
||||
async function updateDictType(id, typeData) {
|
||||
const { type_code, type_name, description } = typeData;
|
||||
const db = await getDbConnection(getSystemDbPath());
|
||||
return executeWithRetry(db, async () => {
|
||||
const sql = 'UPDATE dict_types SET type_code = ?, type_name = ?, description = ? WHERE id = ?';
|
||||
const result = await db.runAsync(sql, [type_code, type_name, description || null, id]);
|
||||
return result.changes > 0;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新一条dict_items
|
||||
* @param {number} id 字典项ID
|
||||
* @param {Object} itemData 更新的数据
|
||||
* @returns {Promise<boolean>} 是否更新成功
|
||||
*/
|
||||
async function updateDictItem(id, itemData) {
|
||||
const { type_code, item_code, item_name, item_description, parent_code, is_active } = itemData;
|
||||
const db = await getDbConnection(getSystemDbPath());
|
||||
return executeWithRetry(db, async () => {
|
||||
const sql = 'UPDATE dict_items SET type_code = ?, item_code = ?, item_name = ?, item_description = ?, parent_code = ?, is_active = ? WHERE id = ?';
|
||||
const result = await db.runAsync(sql, [
|
||||
type_code,
|
||||
item_code,
|
||||
item_name,
|
||||
item_description || null,
|
||||
parent_code || null,
|
||||
is_active !== undefined ? is_active : 1,
|
||||
id
|
||||
]);
|
||||
|
||||
// 检查result是否存在以及是否有changes属性
|
||||
if (!result || result.changes === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return result.changes > 0;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一条dict_types
|
||||
* @param {number} id 类型ID
|
||||
* @returns {Promise<boolean>} 是否删除成功
|
||||
*/
|
||||
async function deleteDictType(id) {
|
||||
const db = await getDbConnection(getSystemDbPath());
|
||||
return executeWithRetry(db, async () => {
|
||||
// 先检查是否有关联的字典项
|
||||
const checkSql = 'SELECT COUNT(*) as count FROM dict_items WHERE type_code = (SELECT type_code FROM dict_types WHERE id = ?)';
|
||||
const result = await db.getAsync(checkSql, [id]);
|
||||
if (result.count > 0) {
|
||||
throw new Error('该字典类型下有关联的字典项,不允许删除');
|
||||
}
|
||||
|
||||
// 删除字典类型
|
||||
const deleteSql = 'DELETE FROM dict_types WHERE id = ?';
|
||||
const deleteResult = await db.runAsync(deleteSql, [id]);
|
||||
return deleteResult.changes > 0;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一条dict_items
|
||||
* @param {number} id 字典项ID
|
||||
* @returns {Promise<boolean>} 是否删除成功
|
||||
*/
|
||||
async function deleteDictItem(id) {
|
||||
const db = await getDbConnection(getSystemDbPath());
|
||||
return executeWithRetry(db, async () => {
|
||||
const sql = 'DELETE FROM dict_items WHERE id = ?';
|
||||
const result = await db.runAsync(sql, [id]);
|
||||
|
||||
// 检查result是否存在以及是否有changes属性
|
||||
if (!result || result.changes === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return result.changes > 0;
|
||||
});
|
||||
}
|
||||
|
||||
// 检查parent_code是否存在
|
||||
async function checkParentCodeExists(parentCode) {
|
||||
if (!parentCode) return true; // 允许空的parent_code
|
||||
|
||||
const db = await getDbConnection(getSystemDbPath());
|
||||
return executeWithRetry(db, async () => {
|
||||
const sql = 'SELECT COUNT(*) as count FROM dict_items WHERE item_code = ?';
|
||||
const result = await db.getAsync(sql, [parentCode]);
|
||||
return result.count > 0;
|
||||
});
|
||||
}
|
||||
|
||||
// 检查是否有其他记录引用了该item_code作为parent_code
|
||||
async function hasChildReferences(itemCode) {
|
||||
const db = await getDbConnection(getSystemDbPath());
|
||||
return executeWithRetry(db, async () => {
|
||||
const sql = 'SELECT COUNT(*) as count FROM dict_items WHERE parent_code = ?';
|
||||
const result = await db.getAsync(sql, [itemCode]);
|
||||
return result.count > 0;
|
||||
});
|
||||
}
|
||||
|
||||
// 导出使用CommonJS格式
|
||||
module.exports = {
|
||||
getDictItemsWithTypes,
|
||||
getDictTypes,
|
||||
addDictType,
|
||||
addDictItem,
|
||||
getDictTypeById,
|
||||
getDictItemById,
|
||||
getDictItemsByTypeCode,
|
||||
updateDictType,
|
||||
updateDictItem,
|
||||
deleteDictType,
|
||||
deleteDictItem,
|
||||
checkParentCodeExists,
|
||||
hasChildReferences
|
||||
};
|
423
src/background/db/question.js
Normal file
423
src/background/db/question.js
Normal file
@ -0,0 +1,423 @@
|
||||
const { executeWithRetry } = require('./utils.js');
|
||||
const { getDbConnection } = require('./index.js');
|
||||
const { getSystemDbPath } = require('./path.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
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// 导出使用CommonJS格式
|
||||
module.exports = {
|
||||
addQuestion,
|
||||
getAllQuestions,
|
||||
getQuestionById,
|
||||
updateQuestion,
|
||||
deleteQuestion,
|
||||
getAllQuestionsWithRelations,
|
||||
updateQuestionDescription,
|
||||
addChoiceQuestion,
|
||||
updateChoiceQuestion,
|
||||
addFillBlankQuestion,
|
||||
updateFillBlankQuestion,
|
||||
deleteFillBlankQuestion,
|
||||
getFillBlankQuestionsByQuestionId,
|
||||
getQuestionsCountAndScore
|
||||
};
|
@ -9,6 +9,12 @@ const isDevelopment = process.env.NODE_ENV !== 'production'
|
||||
|
||||
// 导入数据库相关函数
|
||||
const { checkDatabaseInitialized, initializeDatabase } = require('./db/index.js');
|
||||
// 导入配置服务
|
||||
const { initConfigIpc } = require('./service/configService.js');
|
||||
// 导入字典服务
|
||||
const { initDictIpc } = require('./service/dictService.js');
|
||||
// 导入试题服务
|
||||
const { initQuestionIpc } = require('./service/questionService.js');
|
||||
|
||||
// Scheme must be registered before the app is ready
|
||||
protocol.registerSchemesAsPrivileged([
|
||||
@ -18,12 +24,10 @@ protocol.registerSchemesAsPrivileged([
|
||||
async function createWindow() {
|
||||
// Create the browser window.
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
width: 800, // 默认宽度(实际会被最大化覆盖)
|
||||
height: 600, // 默认高度(实际会被最大化覆盖)
|
||||
show: false, // 先隐藏窗口,避免闪烁
|
||||
webPreferences: {
|
||||
// 修改这一行
|
||||
preload: require('path').join(__dirname, 'preload.js'),
|
||||
|
||||
// 改为使用绝对路径解析
|
||||
preload: require('path').join(process.cwd(), 'src/preload.js'),
|
||||
nodeIntegration: false,
|
||||
@ -31,6 +35,11 @@ async function createWindow() {
|
||||
}
|
||||
})
|
||||
|
||||
// 在窗口显示前设置最大化
|
||||
win.maximize();
|
||||
// 然后显示窗口
|
||||
win.show();
|
||||
|
||||
if (process.env.WEBPACK_DEV_SERVER_URL) {
|
||||
// Load the url of the dev server if in development mode
|
||||
await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
|
||||
@ -119,6 +128,13 @@ app.on('ready', async () => {
|
||||
console.error('SQLite测试异常:', error);
|
||||
}
|
||||
|
||||
// 初始化配置相关的IPC处理程序
|
||||
initConfigIpc(ipcMain);
|
||||
// 初始化字典相关的IPC处理程序
|
||||
initDictIpc(ipcMain);
|
||||
// 初始化试题相关的IPC处理程序
|
||||
await initQuestionIpc(ipcMain);
|
||||
|
||||
createWindow();
|
||||
});
|
||||
|
||||
|
250
src/background/service/configService.js
Normal file
250
src/background/service/configService.js
Normal file
@ -0,0 +1,250 @@
|
||||
const {
|
||||
getConfig,
|
||||
setConfig,
|
||||
getAllConfigs,
|
||||
getConfigById,
|
||||
deleteConfig,
|
||||
} = require('../db/config.js');
|
||||
const bcrypt = require('bcryptjs');
|
||||
|
||||
/**
|
||||
* 服务层:获取配置项
|
||||
* @param {string} key - 配置项键名
|
||||
* @returns {Promise<{key: string, value: string} | null>}
|
||||
*/
|
||||
async function fetchConfig(key) {
|
||||
try {
|
||||
return await getConfig(key);
|
||||
} catch (error) {
|
||||
console.error("服务层: 获取配置项失败", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:获取所有配置项
|
||||
* @returns {Promise<Array<{id: number, key: string, value: string, protected: number}>>}
|
||||
*/
|
||||
async function fetchAllConfigs() {
|
||||
try {
|
||||
return await getAllConfigs();
|
||||
} catch (error) {
|
||||
console.error("服务层: 获取所有配置项失败", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:通过ID获取配置项
|
||||
* @param {number} id - 配置项ID
|
||||
* @returns {Promise<{id: number, key: string, value: string, protected: number} | null>}
|
||||
*/
|
||||
async function fetchConfigById(id) {
|
||||
try {
|
||||
return await getConfigById(id);
|
||||
} catch (error) {
|
||||
console.error("服务层: 通过ID获取配置项失败", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:保存配置项
|
||||
* @param {string} key - 配置项键名
|
||||
* @param {string} value - 配置项值
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function saveConfig(key, value) {
|
||||
try {
|
||||
await setConfig(key, value);
|
||||
} catch (error) {
|
||||
console.error("服务层: 保存配置项失败", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:删除配置项
|
||||
* @param {number} id - 配置项ID
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function removeConfig(id) {
|
||||
try {
|
||||
await deleteConfig(id);
|
||||
} catch (error) {
|
||||
console.error("服务层: 删除配置项失败", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统配置并转为Map
|
||||
* @returns {Promise<{[key: string]: string}>}
|
||||
*/
|
||||
async function getSystemConfig() {
|
||||
try {
|
||||
const configs = await getAllConfigs();
|
||||
const configMap = {};
|
||||
configs.forEach((config) => {
|
||||
configMap[config.key] = config.value;
|
||||
});
|
||||
return configMap;
|
||||
} catch (error) {
|
||||
console.error("获取系统配置失败:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新系统配置
|
||||
* @param {{[key: string]: string}} config - 配置对象
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async function updateSystemConfig(config) {
|
||||
try {
|
||||
for (const [key, value] of Object.entries(config)) {
|
||||
await setConfig(key, value);
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("更新系统配置失败:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加题库版本号
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async function increaseQuestionBankVersion() {
|
||||
try {
|
||||
const currentVersion = await getConfig("question_bank_version");
|
||||
const newVersion = currentVersion ? parseInt(currentVersion.value) + 1 : 1;
|
||||
await setConfig("question_bank_version", newVersion.toString());
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("增加题库版本号失败:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理员登录验证
|
||||
* @param {string} password - 用户输入的密码
|
||||
* @returns {Promise<{success: boolean, message: string}>}
|
||||
*/
|
||||
async function verifyAdminPassword(password) {
|
||||
try {
|
||||
const config = await getConfig("admin_password");
|
||||
if (!config || !config.value) {
|
||||
return { success: false, message: "管理员密码未设置" };
|
||||
}
|
||||
|
||||
const isMatch = await bcrypt.compare(password, config.value);
|
||||
if (isMatch) {
|
||||
return { success: true, message: "登录成功" };
|
||||
} else {
|
||||
return { success: false, message: "密码错误" };
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("验证管理员密码失败:", error);
|
||||
return { success: false, message: "验证过程发生错误" };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化配置相关的IPC处理程序
|
||||
* @param {import('electron').IpcMain} ipcMain - IPC主进程实例
|
||||
*/
|
||||
function initConfigIpc(ipcMain) {
|
||||
// 管理员登录验证
|
||||
ipcMain.handle("admin-login", async (event, credentials) => {
|
||||
try {
|
||||
return await verifyAdminPassword(credentials.password);
|
||||
} catch (error) {
|
||||
console.error("Failed to verify admin password:", error);
|
||||
return { success: false, message: "验证过程发生错误" };
|
||||
}
|
||||
});
|
||||
|
||||
// 系统相关
|
||||
ipcMain.handle("system-get-config", async () => {
|
||||
try {
|
||||
return await getSystemConfig();
|
||||
} catch (error) {
|
||||
console.error("Failed to get system config:", error);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle("system-update-config", async (event, config) => {
|
||||
try {
|
||||
return await updateSystemConfig(config);
|
||||
} catch (error) {
|
||||
console.error("Failed to update system config:", error);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle("system-increase-question-band-version", async () => {
|
||||
try {
|
||||
return await increaseQuestionBankVersion();
|
||||
} catch (error) {
|
||||
console.error("Failed to increase question band version:", error);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// 配置项管理相关IPC
|
||||
ipcMain.handle("config-fetch-all", async () => {
|
||||
try {
|
||||
return await fetchAllConfigs();
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch all configs:", error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle("config-fetch-by-id", async (event, id) => {
|
||||
try {
|
||||
return await fetchConfigById(id);
|
||||
} catch (error) {
|
||||
console.error(`Failed to fetch config by id ${id}:`, error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle("config-save", async (event, { key, value }) => {
|
||||
try {
|
||||
await saveConfig(key, value);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error(`Failed to save config ${key}:`, error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle("config-delete", async (event, id) => {
|
||||
try {
|
||||
await removeConfig(id);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error(`Failed to delete config ${id}:`, error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 导出使用CommonJS格式
|
||||
module.exports = {
|
||||
fetchConfig,
|
||||
fetchAllConfigs,
|
||||
fetchConfigById,
|
||||
saveConfig,
|
||||
removeConfig,
|
||||
getSystemConfig,
|
||||
updateSystemConfig,
|
||||
increaseQuestionBankVersion,
|
||||
verifyAdminPassword,
|
||||
initConfigIpc
|
||||
};
|
332
src/background/service/dictService.js
Normal file
332
src/background/service/dictService.js
Normal file
@ -0,0 +1,332 @@
|
||||
const {
|
||||
getDictItemsWithTypes,
|
||||
getDictTypes,
|
||||
addDictType,
|
||||
addDictItem,
|
||||
getDictTypeById,
|
||||
getDictItemById,
|
||||
getDictItemsByTypeCode,
|
||||
updateDictType,
|
||||
updateDictItem,
|
||||
deleteDictType,
|
||||
deleteDictItem,
|
||||
checkParentCodeExists,
|
||||
hasChildReferences,
|
||||
} = require('../db/dict.js');
|
||||
|
||||
/**
|
||||
* 服务层:查询字典项及其类型
|
||||
* @returns {Promise<Array>} 字典项列表
|
||||
*/
|
||||
async function fetchDictItemsWithTypes() {
|
||||
try {
|
||||
return await getDictItemsWithTypes();
|
||||
} catch (error) {
|
||||
console.error("服务层: 查询字典项及其类型失败", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:查询字典类型列表
|
||||
* @returns {Promise<Array>} 字典类型列表
|
||||
*/
|
||||
async function fetchDictTypes() {
|
||||
try {
|
||||
return await getDictTypes();
|
||||
} catch (error) {
|
||||
console.error("服务层: 查询字典类型列表失败", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:添加字典类型
|
||||
* @param {Object} typeData 字典类型数据
|
||||
* @returns {Promise<Object>} 添加的字典类型
|
||||
*/
|
||||
async function createDictType(typeData) {
|
||||
try {
|
||||
return await addDictType(typeData);
|
||||
} catch (error) {
|
||||
console.error("服务层: 添加字典类型失败", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:添加字典项
|
||||
* @param {Object} itemData 字典项数据
|
||||
* @returns {Promise<Object>} 添加的字典项
|
||||
*/
|
||||
async function createDictItem(itemData) {
|
||||
try {
|
||||
return await addDictItem(itemData);
|
||||
} catch (error) {
|
||||
console.error("服务层: 添加字典项失败", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:根据ID查询字典类型
|
||||
* @param {number} id 字典类型ID
|
||||
* @returns {Promise<Object|null>} 字典类型数据
|
||||
*/
|
||||
async function fetchDictTypeById(id) {
|
||||
try {
|
||||
return await getDictTypeById(id);
|
||||
} catch (error) {
|
||||
console.error("服务层: 根据ID查询字典类型失败", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:根据ID查询字典项
|
||||
* @param {number} id 字典项ID
|
||||
* @returns {Promise<Object|null>} 字典项数据
|
||||
*/
|
||||
async function fetchDictItemById(id) {
|
||||
try {
|
||||
return await getDictItemById(id);
|
||||
} catch (error) {
|
||||
console.error("服务层: 根据ID查询字典项失败", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:根据类型编码查询字典项
|
||||
* @param {string} typeCode 类型编码
|
||||
* @param {number} isActive 是否激活(1=激活, 0=未激活, 不传=全部)
|
||||
* @returns {Promise<Array>} 字典项列表
|
||||
*/
|
||||
async function fetchDictItemsByTypeCode(typeCode, isActive = undefined) {
|
||||
try {
|
||||
return await getDictItemsByTypeCode(typeCode, isActive);
|
||||
} catch (error) {
|
||||
console.error("服务层: 根据类型编码查询字典项失败", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:更新字典类型
|
||||
* @param {number} id 字典类型ID
|
||||
* @param {Object} typeData 更新的数据
|
||||
* @returns {Promise<boolean>} 是否更新成功
|
||||
*/
|
||||
async function modifyDictType(id, typeData) {
|
||||
try {
|
||||
return await updateDictType(id, typeData);
|
||||
} catch (error) {
|
||||
console.error("服务层: 更新字典类型失败", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:更新字典项
|
||||
* @param {number} id 字典项ID
|
||||
* @param {Object} itemData 更新的数据
|
||||
* @returns {Promise<boolean>} 是否更新成功
|
||||
*/
|
||||
async function modifyDictItem(id, itemData) {
|
||||
try {
|
||||
return await updateDictItem(id, itemData);
|
||||
} catch (error) {
|
||||
console.error("服务层: 更新字典项失败", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:删除字典类型
|
||||
* @param {number} id 字典类型ID
|
||||
* @returns {Promise<boolean>} 是否删除成功
|
||||
*/
|
||||
async function removeDictType(id) {
|
||||
try {
|
||||
return await deleteDictType(id);
|
||||
} catch (error) {
|
||||
console.error("服务层: 删除字典类型失败", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:删除字典项
|
||||
* @param {number} id 字典项ID
|
||||
* @returns {Promise<boolean>} 是否删除成功
|
||||
*/
|
||||
async function removeDictItem(id) {
|
||||
try {
|
||||
return await deleteDictItem(id);
|
||||
} catch (error) {
|
||||
console.error("服务层: 删除字典项失败", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:检查父级编码是否存在
|
||||
* @param {string} parentCode 父级编码
|
||||
* @returns {Promise<boolean>} 是否存在
|
||||
*/
|
||||
async function checkDictParentCode(parentCode) {
|
||||
try {
|
||||
return await checkParentCodeExists(parentCode);
|
||||
} catch (error) {
|
||||
console.error("服务层: 检查父级编码失败", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:检查是否有子引用
|
||||
* @param {string} itemCode 字典项编码
|
||||
* @returns {Promise<boolean>} 是否有子引用
|
||||
*/
|
||||
async function checkDictChildReferences(itemCode) {
|
||||
try {
|
||||
return await hasChildReferences(itemCode);
|
||||
} catch (error) {
|
||||
console.error("服务层: 检查子引用失败", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化字典相关的IPC处理程序
|
||||
* @param {import('electron').IpcMain} ipcMain - IPC主进程实例
|
||||
*/
|
||||
function initDictIpc(ipcMain) {
|
||||
// 字典管理相关IPC
|
||||
ipcMain.handle("dict-fetch-types", async () => {
|
||||
try {
|
||||
return await fetchDictTypes();
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch dict types:", error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle(
|
||||
"dict-fetch-items-by-type",
|
||||
async (event, typeCode, isActive = undefined) => {
|
||||
try {
|
||||
return await fetchDictItemsByTypeCode(typeCode, isActive);
|
||||
} catch (error) {
|
||||
console.error(`Failed to fetch dict items by type ${typeCode}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
ipcMain.handle("dict-create-type", async (event, dictType) => {
|
||||
try {
|
||||
return await createDictType(dictType);
|
||||
} catch (error) {
|
||||
console.error("Failed to create dict type:", error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
// 将 updateDictType 改为 modifyDictType
|
||||
ipcMain.handle("dict-update-type", async (event, dictType) => {
|
||||
try {
|
||||
return await modifyDictType(dictType.id, dictType);
|
||||
} catch (error) {
|
||||
console.error("Failed to update dict type:", error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
// 将 deleteDictType 改为 removeDictType
|
||||
ipcMain.handle("dict-delete-type", async (event, typeCode) => {
|
||||
try {
|
||||
return await removeDictType(typeCode);
|
||||
} catch (error) {
|
||||
console.error(`Failed to delete dict type ${typeCode}:`, error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle("dict-create-item", async (event, dictItem) => {
|
||||
try {
|
||||
return await createDictItem(dictItem);
|
||||
} catch (error) {
|
||||
console.error("Failed to create dict item:", error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
// 将 updateDictItem 改为 modifyDictItem
|
||||
ipcMain.handle("dict-update-item", async (event, dictItem) => {
|
||||
try {
|
||||
return await modifyDictItem(dictItem.id, dictItem);
|
||||
} catch (error) {
|
||||
console.error("Failed to update dict item:", error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
// 将 deleteDictItem 改为 removeDictItem
|
||||
ipcMain.handle("dict-delete-item", async (event, id) => {
|
||||
try {
|
||||
return await removeDictItem(id);
|
||||
} catch (error) {
|
||||
console.error(`Failed to delete dict item ${id}:`, error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
// 将 fetchAllDictItemsWithTypes 改为 fetchDictItemsWithTypes
|
||||
ipcMain.handle("dict-fetch-all-items-with-types", async () => {
|
||||
try {
|
||||
return await fetchDictItemsWithTypes();
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch all dict items with types:", error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
// 检查parent_code是否存在
|
||||
ipcMain.handle("dict-check-parent-code", async (event, parentCode) => {
|
||||
try {
|
||||
return await checkDictParentCode(parentCode);
|
||||
} catch (error) {
|
||||
console.error("检查parent_code失败:", error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
// 检查是否有子引用
|
||||
ipcMain.handle("dict-check-child-references", async (event, itemCode) => {
|
||||
try {
|
||||
return await checkDictChildReferences(itemCode);
|
||||
} catch (error) {
|
||||
console.error("检查子引用失败:", error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 导出使用CommonJS格式
|
||||
module.exports = {
|
||||
fetchDictItemsWithTypes,
|
||||
fetchDictTypes,
|
||||
createDictType,
|
||||
createDictItem,
|
||||
fetchDictTypeById,
|
||||
fetchDictItemById,
|
||||
fetchDictItemsByTypeCode,
|
||||
modifyDictType,
|
||||
modifyDictItem,
|
||||
removeDictType,
|
||||
removeDictItem,
|
||||
checkDictParentCode,
|
||||
checkDictChildReferences,
|
||||
initDictIpc
|
||||
};
|
407
src/background/service/questionService.js
Normal file
407
src/background/service/questionService.js
Normal file
@ -0,0 +1,407 @@
|
||||
const {
|
||||
addQuestion,
|
||||
getAllQuestions,
|
||||
getQuestionById,
|
||||
updateQuestion,
|
||||
deleteQuestion,
|
||||
getAllQuestionsWithRelations,
|
||||
updateQuestionDescription,
|
||||
addChoiceQuestion,
|
||||
updateChoiceQuestion,
|
||||
addFillBlankQuestion,
|
||||
updateFillBlankQuestion,
|
||||
deleteFillBlankQuestion,
|
||||
getFillBlankQuestionsByQuestionId,
|
||||
getQuestionsCountAndScore
|
||||
} = require('../db/question.js');
|
||||
|
||||
// 导入configService中的increaseQuestionBankVersion方法
|
||||
const {
|
||||
increaseQuestionBankVersion
|
||||
} = require('./configService.js');
|
||||
|
||||
/**
|
||||
* 服务层:添加题干
|
||||
* @param {Object} questionData - 题干数据
|
||||
* @returns {Promise<number>} 新创建的题干ID
|
||||
*/
|
||||
async function createQuestion(questionData) {
|
||||
try {
|
||||
const result = await addQuestion(questionData);
|
||||
// 调用增加题库版本号的方法
|
||||
await increaseQuestionBankVersion();
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('服务层: 添加题干失败', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:获取所有题干
|
||||
* @returns {Promise<Array>} 题干列表
|
||||
*/
|
||||
async function fetchAllQuestions() {
|
||||
try {
|
||||
return await getAllQuestions();
|
||||
} catch (error) {
|
||||
console.error('服务层: 获取所有题干失败', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:获取所有题干及其关联信息
|
||||
* @returns {Promise<Array>} 包含关联信息的题干列表
|
||||
*/
|
||||
async function fetchAllQuestionsWithRelations() {
|
||||
try {
|
||||
return await getAllQuestionsWithRelations();
|
||||
} catch (error) {
|
||||
console.error('服务层: 获取所有题干及其关联信息失败', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:根据ID获取题干
|
||||
* @param {number} id - 题干ID
|
||||
* @returns {Promise<Object|null>} 题干详情
|
||||
*/
|
||||
async function fetchQuestionById(id) {
|
||||
try {
|
||||
return await getQuestionById(id);
|
||||
} catch (error) {
|
||||
console.error('服务层: 根据ID获取题干失败', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:更新题干
|
||||
* @param {number} id - 题干ID
|
||||
* @param {Object} questionData - 题干数据
|
||||
* @returns {Promise<boolean>} 是否更新成功
|
||||
*/
|
||||
async function modifyQuestion(id, questionData) {
|
||||
try {
|
||||
const result = await updateQuestion(id, questionData);
|
||||
// 调用增加题库版本号的方法
|
||||
await increaseQuestionBankVersion();
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('服务层: 更新题干失败', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:更新题干描述
|
||||
* @param {number} id - 题干ID
|
||||
* @param {string} questionDescription - 新的题干描述
|
||||
* @returns {Promise<boolean>} 是否更新成功
|
||||
*/
|
||||
async function modifyQuestionDescription(id, questionDescription) {
|
||||
try {
|
||||
const result = await updateQuestionDescription(id, questionDescription);
|
||||
// 调用增加题库版本号的方法
|
||||
await increaseQuestionBankVersion();
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('服务层: 更新题干描述失败', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:删除题干
|
||||
* @param {number} id - 题干ID
|
||||
* @returns {Promise<boolean>} 是否删除成功
|
||||
*/
|
||||
async function removeQuestion(id) {
|
||||
try {
|
||||
const result = await deleteQuestion(id);
|
||||
// 调用增加题库版本号的方法
|
||||
await increaseQuestionBankVersion();
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('服务层: 删除题干失败', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:添加选择题问题
|
||||
* @param {Object} choiceData - 选择题数据
|
||||
* @returns {Promise<number>} 新创建的选择题ID
|
||||
*/
|
||||
async function createChoiceQuestion(choiceData) {
|
||||
try {
|
||||
const result = await addChoiceQuestion(choiceData);
|
||||
// 调用增加题库版本号的方法
|
||||
await increaseQuestionBankVersion();
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('服务层: 添加选择题失败', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:添加选择题问题
|
||||
* @param {number} id - 选择题ID
|
||||
* @param {Object} choiceData - 选择题数据
|
||||
* @returns {Promise<boolean>} 是否更新成功
|
||||
*/
|
||||
async function modifyChoiceQuestion(id, choiceData) {
|
||||
try {
|
||||
const result = await updateChoiceQuestion(id, choiceData);
|
||||
// 调用增加题库版本号的方法
|
||||
await increaseQuestionBankVersion();
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('服务层: 更新选择题失败', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:添加填空题问题
|
||||
* @param {Object} fillBlankData - 填空题数据
|
||||
* @returns {Promise<number>} 新创建的填空题ID
|
||||
*/
|
||||
async function createFillBlankQuestion(fillBlankData) {
|
||||
try {
|
||||
const result = await addFillBlankQuestion(fillBlankData);
|
||||
// 调用增加题库版本号的方法
|
||||
await increaseQuestionBankVersion();
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('服务层: 添加填空题失败', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:更新填空题问题
|
||||
* @param {number} id - 填空题ID
|
||||
* @param {Object} fillBlankData - 填空题数据
|
||||
* @returns {Promise<boolean>} 是否更新成功
|
||||
*/
|
||||
async function modifyFillBlankQuestion(id, fillBlankData) {
|
||||
try {
|
||||
const result = await updateFillBlankQuestion(id, fillBlankData);
|
||||
// 调用增加题库版本号的方法
|
||||
await increaseQuestionBankVersion();
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('服务层: 更新填空题失败', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:删除填空题问题
|
||||
* @param {number} id - 填空题ID
|
||||
* @returns {Promise<boolean>} 是否删除成功
|
||||
*/
|
||||
async function removeFillBlankQuestion(id) {
|
||||
try {
|
||||
const result = await deleteFillBlankQuestion(id);
|
||||
// 调用增加题库版本号的方法
|
||||
await increaseQuestionBankVersion();
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('服务层: 删除填空题失败', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:根据题干ID查询填空题问题
|
||||
* @param {number} questionId - 题干ID
|
||||
* @returns {Promise<Array>} 填空题列表
|
||||
*/
|
||||
async function fetchFillBlankQuestionsByQuestionId(questionId) {
|
||||
try {
|
||||
return await getFillBlankQuestionsByQuestionId(questionId);
|
||||
} catch (error) {
|
||||
console.error('服务层: 根据题干ID查询填空题失败', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:查询试题总数和总分
|
||||
* @returns {Promise<{totalQuestions: number, totalScore: number}>} 包含试题总数和总分的对象
|
||||
*/
|
||||
async function fetchQuestionsCountAndScore() {
|
||||
try {
|
||||
return await getQuestionsCountAndScore();
|
||||
} catch (error) {
|
||||
console.error('服务层: 查询试题总数和总分失败', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化试题相关IPC通信
|
||||
* @param {Electron.IpcMain} ipcMain - IPC主进程实例
|
||||
*/
|
||||
async function initQuestionIpc(ipcMain) {
|
||||
// 题干管理相关IPC
|
||||
ipcMain.handle("question-create", async (event, questionData) => {
|
||||
try {
|
||||
return await createQuestion(questionData);
|
||||
} catch (error) {
|
||||
console.error("Failed to create question:", error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle("question-fetch-all", async () => {
|
||||
try {
|
||||
return await fetchAllQuestions();
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch questions:", error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle("question-fetch-by-id", async (event, id) => {
|
||||
try {
|
||||
return await fetchQuestionById(id);
|
||||
} catch (error) {
|
||||
console.error(`Failed to fetch question by id ${id}:`, error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle("question-update", async (event, id, questionData) => {
|
||||
try {
|
||||
return await modifyQuestion(id, questionData);
|
||||
} catch (error) {
|
||||
console.error("Failed to update question:", error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle("question-delete", async (event, id) => {
|
||||
try {
|
||||
return await removeQuestion(id);
|
||||
} catch (error) {
|
||||
console.error(`Failed to delete question ${id}:`, error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
// 在已有的 question 相关 IPC 处理程序区域添加
|
||||
ipcMain.handle("question-fetch-all-with-relations", async (event) => {
|
||||
try {
|
||||
return await fetchAllQuestionsWithRelations();
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch all questions with relations:", error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
// 添加更新题干描述的 IPC 处理程序
|
||||
ipcMain.handle("question-update-description", async (event, id, questionDescription) => {
|
||||
try {
|
||||
return await modifyQuestionDescription(id, questionDescription);
|
||||
} catch (error) {
|
||||
console.error("Failed to update question description:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 添加选择题问题的IPC处理程序
|
||||
ipcMain.handle('question-update-choice', async (event, id, choiceData) => {
|
||||
try {
|
||||
return await modifyChoiceQuestion(id, choiceData);
|
||||
} catch (error) {
|
||||
console.error('Failed to update choice question:', error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
// 添加选择题问题的IPC处理程序
|
||||
ipcMain.handle("question-create-choice", async (event, choiceData) => {
|
||||
try {
|
||||
return await createChoiceQuestion(choiceData);
|
||||
} catch (error) {
|
||||
console.error("Failed to create choice question:", error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
// 添加填空题问题的IPC处理程序
|
||||
ipcMain.handle('question-create-fill-blank', async (event, fillBlankData) => {
|
||||
try {
|
||||
return await createFillBlankQuestion(fillBlankData);
|
||||
} catch (error) {
|
||||
console.error('Failed to create fill blank question:', error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
// 更新填空题问题的IPC处理程序
|
||||
ipcMain.handle('question-update-fill-blank', async (event, id, fillBlankData) => {
|
||||
try {
|
||||
return await modifyFillBlankQuestion(id, fillBlankData);
|
||||
} catch (error) {
|
||||
console.error('Failed to update fill blank question:', error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
// 删除填空题问题的IPC处理程序
|
||||
ipcMain.handle('question-delete-fill-blank', async (event, id) => {
|
||||
try {
|
||||
return await removeFillBlankQuestion(id);
|
||||
} catch (error) {
|
||||
console.error(`Failed to delete fill blank question ${id}:`, error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
// 根据题干ID查询填空题问题的IPC处理程序
|
||||
ipcMain.handle('question-fetch-fill-blank-by-question-id', async (event, questionId) => {
|
||||
try {
|
||||
return await fetchFillBlankQuestionsByQuestionId(questionId);
|
||||
} catch (error) {
|
||||
console.error(`Failed to fetch fill blank questions by question id ${questionId}:`, error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
// 注册查询试题总数和总分的IPC处理程序
|
||||
ipcMain.handle('question-get-count-and-score', async () => {
|
||||
try {
|
||||
return await fetchQuestionsCountAndScore();
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch questions count and score:', error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 导出使用CommonJS格式
|
||||
module.exports = {
|
||||
createQuestion,
|
||||
fetchAllQuestions,
|
||||
fetchAllQuestionsWithRelations,
|
||||
fetchQuestionById,
|
||||
modifyQuestion,
|
||||
modifyQuestionDescription,
|
||||
removeQuestion,
|
||||
createChoiceQuestion,
|
||||
modifyChoiceQuestion,
|
||||
createFillBlankQuestion,
|
||||
modifyFillBlankQuestion,
|
||||
removeFillBlankQuestion,
|
||||
fetchFillBlankQuestionsByQuestionId,
|
||||
fetchQuestionsCountAndScore,
|
||||
initQuestionIpc
|
||||
};
|
@ -7,6 +7,46 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
||||
checkDatabaseInitialized: () => ipcRenderer.invoke('check-database-initialized'),
|
||||
initializeDatabase: () => ipcRenderer.invoke('initialize-database'),
|
||||
|
||||
// 配置服务相关接口
|
||||
adminLogin: (credentials) => ipcRenderer.invoke('admin-login', credentials),
|
||||
systemGetConfig: () => ipcRenderer.invoke('system-get-config'),
|
||||
systemUpdateConfig: (config) => ipcRenderer.invoke('system-update-config', config),
|
||||
systemIncreaseQuestionBankVersion: () => ipcRenderer.invoke('system-increase-question-band-version'),
|
||||
configFetchAll: () => ipcRenderer.invoke('config-fetch-all'),
|
||||
configFetchById: (id) => ipcRenderer.invoke('config-fetch-by-id', id),
|
||||
configSave: (key, value) => ipcRenderer.invoke('config-save', { key, value }),
|
||||
configDelete: (id) => ipcRenderer.invoke('config-delete', id),
|
||||
|
||||
// 字典服务相关接口
|
||||
dictFetchTypes: () => ipcRenderer.invoke('dict-fetch-types'),
|
||||
dictFetchItemsByType: (typeCode, isActive = undefined) => ipcRenderer.invoke('dict-fetch-items-by-type', typeCode, isActive),
|
||||
dictCreateType: (dictType) => ipcRenderer.invoke('dict-create-type', dictType),
|
||||
dictUpdateType: (dictType) => ipcRenderer.invoke('dict-update-type', dictType),
|
||||
dictDeleteType: (typeCode) => ipcRenderer.invoke('dict-delete-type', typeCode),
|
||||
dictCreateItem: (dictItem) => ipcRenderer.invoke('dict-create-item', dictItem),
|
||||
dictUpdateItem: (dictItem) => ipcRenderer.invoke('dict-update-item', dictItem),
|
||||
dictDeleteItem: (id) => ipcRenderer.invoke('dict-delete-item', id),
|
||||
dictFetchAllItemsWithTypes: () => ipcRenderer.invoke('dict-fetch-all-items-with-types'),
|
||||
dictCheckParentCode: (parentCode) => ipcRenderer.invoke('dict-check-parent-code', parentCode),
|
||||
dictCheckChildReferences: (itemCode) => ipcRenderer.invoke('dict-check-child-references', itemCode),
|
||||
|
||||
// 试题服务相关接口
|
||||
questionCreate: (questionData) => ipcRenderer.invoke('question-create', questionData),
|
||||
questionFetchAll: () => ipcRenderer.invoke('question-fetch-all'),
|
||||
questionFetchAllWithRelations: () => ipcRenderer.invoke('question-fetch-all-with-relations'),
|
||||
questionUpdateDescription: (questionData) => ipcRenderer.invoke('question-update-description', questionData),
|
||||
questionAddChoice: (choiceData) => ipcRenderer.invoke('question-add-choice', choiceData),
|
||||
questionUpdateChoice: (choiceData) => ipcRenderer.invoke('question-update-choice', choiceData),
|
||||
questionDeleteChoice: (id) => ipcRenderer.invoke('question-delete-choice', id),
|
||||
questionAddFillBlank: (fillBlankData) => ipcRenderer.invoke('question-add-fill-blank', fillBlankData),
|
||||
questionUpdateFillBlank: (fillBlankData) => ipcRenderer.invoke('question-update-fill-blank', fillBlankData),
|
||||
questionDeleteFillBlank: (id) => ipcRenderer.invoke('question-delete-fill-blank', id),
|
||||
questionGetQuestionWithChoices: (questionId) => ipcRenderer.invoke('question-get-question-with-choices', questionId),
|
||||
questionGetQuestionWithFillBlanks: (questionId) => ipcRenderer.invoke('question-get-question-with-fill-blanks', questionId),
|
||||
questionRemove: (questionId) => ipcRenderer.invoke('question-remove', questionId),
|
||||
questionGetStatistics: () => ipcRenderer.invoke('question-get-statistics'),
|
||||
questionGetQuestionById: (questionId) => ipcRenderer.invoke('question-get-question-by-id', questionId),
|
||||
|
||||
// 保留原有的ipcRenderer接口,确保兼容性
|
||||
ipcRenderer: {
|
||||
invoke: (channel, data) => ipcRenderer.invoke(channel, data)
|
||||
|
@ -130,6 +130,68 @@
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<!-- 配置列表显示 -->
|
||||
<div class="config-list-test" v-if="dbInitialized">
|
||||
<h3>配置列表</h3>
|
||||
<el-card class="card-test">
|
||||
<template slot="header">
|
||||
<div class="card-header">
|
||||
<span>系统配置项</span>
|
||||
<el-button type="primary" size="small" @click="fetchConfigList">刷新列表</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-if="loadingConfigList" class="loading-container">
|
||||
<el-row type="flex" justify="center">
|
||||
<el-col :span="6">
|
||||
<el-empty description="正在加载配置..." />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<div v-else-if="configList.length === 0" class="empty-container">
|
||||
<el-row type="flex" justify="center">
|
||||
<el-col :span="6">
|
||||
<el-empty description="暂无配置项" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<div v-else class="config-table-container">
|
||||
<el-table :data="configList" stripe border style="width: 100%">
|
||||
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||
<el-table-column prop="key" label="配置键" min-width="180" />
|
||||
<el-table-column prop="value" label="配置值" min-width="200">
|
||||
<template slot-scope="scope">
|
||||
<div v-if="editingConfigId === scope.row.id">
|
||||
<el-input v-model="configEdit[scope.row.id]" placeholder="配置值" />
|
||||
</div>
|
||||
<div v-else class="config-value-display">
|
||||
{{ scope.row.value }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="description" label="描述" min-width="250" />
|
||||
<el-table-column prop="created_at" label="创建时间" width="180" align="center" />
|
||||
<el-table-column prop="updated_at" label="更新时间" width="180" align="center" />
|
||||
<el-table-column label="操作" width="150" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button v-if="editingConfigId !== scope.row.id" type="primary" size="small" @click="startEditConfig(scope.row)">编辑</el-button>
|
||||
<div v-else>
|
||||
<el-button type="success" size="small" @click="saveConfig(scope.row)">保存</el-button>
|
||||
<el-button size="small" @click="cancelEdit">取消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div v-if="configListError" class="config-list-error">
|
||||
<el-alert :title="`获取配置列表失败: ${configListError}`" type="error" show-icon :closable="false" />
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap组件测试 -->
|
||||
<div class="bootstrap-test">
|
||||
<h3>Bootstrap 组件测试</h3>
|
||||
@ -260,7 +322,13 @@ export default {
|
||||
dbInitialized: null,
|
||||
dbInitResult: null,
|
||||
dbInitError: null,
|
||||
initializing: false
|
||||
initializing: false,
|
||||
// 配置列表相关状态
|
||||
configList: [],
|
||||
loadingConfigList: false,
|
||||
configListError: null,
|
||||
editingConfigId: null,
|
||||
configEdit: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -336,6 +404,8 @@ export default {
|
||||
// 如果初始化成功,更新状态
|
||||
if (result && result.success) {
|
||||
this.dbInitialized = true
|
||||
// 初始化成功后立即获取配置列表
|
||||
await this.fetchConfigList()
|
||||
}
|
||||
} else {
|
||||
throw new Error('Electron API 不可用')
|
||||
@ -346,12 +416,82 @@ export default {
|
||||
} finally {
|
||||
this.initializing = false
|
||||
}
|
||||
},
|
||||
|
||||
// 获取配置列表
|
||||
async fetchConfigList() {
|
||||
try {
|
||||
this.loadingConfigList = true
|
||||
this.configListError = null
|
||||
|
||||
// 安全地检查 window.electronAPI 是否可用
|
||||
if (typeof window !== 'undefined' && window.electronAPI) {
|
||||
const configList = await window.electronAPI.configFetchAll()
|
||||
this.configList = configList
|
||||
} else {
|
||||
throw new Error('Electron API 不可用')
|
||||
}
|
||||
} catch (err) {
|
||||
this.configListError = err.message
|
||||
console.error('获取配置列表失败:', err)
|
||||
} finally {
|
||||
this.loadingConfigList = false
|
||||
}
|
||||
},
|
||||
|
||||
// 开始编辑配置
|
||||
startEditConfig(config) {
|
||||
this.editingConfigId = config.id
|
||||
// 存储当前值用于编辑
|
||||
this.$set(this.configEdit, config.id, config.value)
|
||||
},
|
||||
|
||||
// 保存配置
|
||||
async saveConfig(config) {
|
||||
try {
|
||||
// 安全地检查 window.electronAPI 是否可用
|
||||
if (typeof window !== 'undefined' && window.electronAPI) {
|
||||
const updatedConfig = {
|
||||
id: config.id,
|
||||
key: config.key,
|
||||
value: this.configEdit[config.id],
|
||||
description: config.description
|
||||
}
|
||||
|
||||
await window.electronAPI.configSave(updatedConfig)
|
||||
|
||||
// 保存成功后更新本地数据
|
||||
const index = this.configList.findIndex(item => item.id === config.id)
|
||||
if (index !== -1) {
|
||||
this.configList[index].value = this.configEdit[config.id]
|
||||
this.configList[index].updated_at = new Date().toLocaleString()
|
||||
}
|
||||
|
||||
this.$message.success('配置保存成功')
|
||||
this.cancelEdit()
|
||||
} else {
|
||||
throw new Error('Electron API 不可用')
|
||||
}
|
||||
} catch (err) {
|
||||
this.$message.error(`保存失败: ${err.message}`)
|
||||
console.error('保存配置失败:', err)
|
||||
}
|
||||
},
|
||||
|
||||
// 取消编辑
|
||||
cancelEdit() {
|
||||
this.editingConfigId = null
|
||||
this.configEdit = {}
|
||||
}
|
||||
},
|
||||
|
||||
// 组件挂载后检查数据库状态
|
||||
async mounted() {
|
||||
await this.checkDatabaseInitialized()
|
||||
// 数据库初始化成功后,获取配置列表
|
||||
if (this.dbInitialized) {
|
||||
await this.fetchConfigList()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -460,4 +600,28 @@ pre {
|
||||
.bootstrap-test .btn {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
/* 配置列表样式 */
|
||||
.config-list-test {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.loading-container,
|
||||
.empty-container {
|
||||
padding: 40px 0;
|
||||
}
|
||||
|
||||
.config-table-container {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.config-list-error {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
/* 配置值显示样式 */
|
||||
.config-value-display {
|
||||
padding: 5px 0;
|
||||
word-break: break-all;
|
||||
}
|
||||
</style>
|
||||
|
Loading…
Reference in New Issue
Block a user