重新实现数据库初始化,改变user.db库中的表结构

This commit is contained in:
chenqiang 2025-08-09 19:50:03 +08:00
parent 0e1b9a496b
commit fc110beee0
6 changed files with 412 additions and 182 deletions

View File

@ -20,7 +20,7 @@ export async function getDbConnection(dbPath) {
} }
// 关闭所有数据库连接 // 关闭所有数据库连接
function closeAllConnections() { export function closeAllConnections() {
dbConnections.forEach((db, path) => { dbConnections.forEach((db, path) => {
try { try {
db.close(); db.close();
@ -57,57 +57,157 @@ async function initializeSystemDatabase() {
const systemDbPath = getSystemDbPath(); const systemDbPath = getSystemDbPath();
const systemDb = await getDbConnection(systemDbPath); const systemDb = await getDbConnection(systemDbPath);
try { // 记录成功和失败的操作
// 开始事务 const results = { success: [], failed: [] };
await systemDb.runAsync('BEGIN TRANSACTION');
console.log('开始事务');
// 创建表结构 // 创建表结构
console.log('开始创建系统数据库表结构...'); console.log('开始创建系统数据库表结构...');
// 创建config表
try {
await systemDb.execAsync(systemSchema.config.trim()); await systemDb.execAsync(systemSchema.config.trim());
console.log('创建 config 表成功'); console.log('创建 config 表成功');
results.success.push('创建 config 表');
} catch (error) {
console.error('创建 config 表失败:', error);
results.failed.push({ operation: '创建 config 表', error: error.message });
}
// 创建dict_types表
try {
await systemDb.execAsync(systemSchema.dictTypes.trim()); await systemDb.execAsync(systemSchema.dictTypes.trim());
console.log('创建 dict_types 表成功'); console.log('创建 dict_types 表成功');
results.success.push('创建 dict_types 表');
} catch (error) {
console.error('创建 dict_types 表失败:', error);
results.failed.push({ operation: '创建 dict_types 表', error: error.message });
}
// 创建dict_items表
try {
await systemDb.execAsync(systemSchema.dictItems.trim()); await systemDb.execAsync(systemSchema.dictItems.trim());
console.log('创建 dict_items 表成功'); console.log('创建 dict_items 表成功');
results.success.push('创建 dict_items 表');
} catch (error) {
console.error('创建 dict_items 表失败:', error);
results.failed.push({ operation: '创建 dict_items 表', error: error.message });
}
// 创建questions表
try {
await systemDb.execAsync(systemSchema.questions.trim()); await systemDb.execAsync(systemSchema.questions.trim());
console.log('创建 questions 表成功'); console.log('创建 questions 表成功');
results.success.push('创建 questions 表');
} catch (error) {
console.error('创建 questions 表失败:', error);
results.failed.push({ operation: '创建 questions 表', error: error.message });
}
// 创建question_datasets表
try {
await systemDb.execAsync(systemSchema.questionDatasets.trim()); await systemDb.execAsync(systemSchema.questionDatasets.trim());
console.log('创建 question_datasets 表成功'); console.log('创建 question_datasets 表成功');
results.success.push('创建 question_datasets 表');
} catch (error) {
console.error('创建 question_datasets 表失败:', error);
results.failed.push({ operation: '创建 question_datasets 表', error: error.message });
}
// 创建question_images表
try {
await systemDb.execAsync(systemSchema.questionImages.trim()); await systemDb.execAsync(systemSchema.questionImages.trim());
console.log('创建 question_images 表成功'); console.log('创建 question_images 表成功');
results.success.push('创建 question_images 表');
} catch (error) {
console.error('创建 question_images 表失败:', error);
results.failed.push({ operation: '创建 question_images 表', error: error.message });
}
// 创建question_fill_table表
try {
await systemDb.execAsync(systemSchema.questionFillTable.trim()); await systemDb.execAsync(systemSchema.questionFillTable.trim());
console.log('创建 question_fill_table 表成功'); console.log('创建 question_fill_table 表成功');
results.success.push('创建 question_fill_table 表');
} catch (error) {
console.error('创建 question_fill_table 表失败:', error);
results.failed.push({ operation: '创建 question_fill_table 表', error: error.message });
}
// 创建question_fill_table_blanks表
try {
await systemDb.execAsync(systemSchema.questionFillTableBlanks.trim()); await systemDb.execAsync(systemSchema.questionFillTableBlanks.trim());
console.log('创建 question_fill_table_blanks 表成功'); console.log('创建 question_fill_table_blanks 表成功');
results.success.push('创建 question_fill_table_blanks 表');
} catch (error) {
console.error('创建 question_fill_table_blanks 表失败:', error);
results.failed.push({ operation: '创建 question_fill_table_blanks 表', error: error.message });
}
// 创建question_choices表
try {
await systemDb.execAsync(systemSchema.questionChoices.trim()); await systemDb.execAsync(systemSchema.questionChoices.trim());
console.log('创建 question_choices 表成功'); console.log('创建 question_choices 表成功');
results.success.push('创建 question_choices 表');
} catch (error) {
console.error('创建 question_choices 表失败:', error);
results.failed.push({ operation: '创建 question_choices 表', error: error.message });
}
// 创建question_fill_blanks表
try {
await systemDb.execAsync(systemSchema.questionFillBlanks.trim()); await systemDb.execAsync(systemSchema.questionFillBlanks.trim());
console.log('创建 question_fill_blanks 表成功'); console.log('创建 question_fill_blanks 表成功');
results.success.push('创建 question_fill_blanks 表');
} catch (error) {
console.error('创建 question_fill_blanks 表失败:', error);
results.failed.push({ operation: '创建 question_fill_blanks 表', error: error.message });
}
// 创建question_judge表
try {
await systemDb.execAsync(systemSchema.questionJudge.trim()); await systemDb.execAsync(systemSchema.questionJudge.trim());
console.log('创建 question_judge 表成功'); console.log('创建 question_judge 表成功');
results.success.push('创建 question_judge 表');
} catch (error) {
console.error('创建 question_judge 表失败:', error);
results.failed.push({ operation: '创建 question_judge 表', error: error.message });
}
// 创建question_short表
try {
await systemDb.execAsync(systemSchema.questionShort.trim()); await systemDb.execAsync(systemSchema.questionShort.trim());
console.log('创建 question_short 表成功'); console.log('创建 question_short 表成功');
results.success.push('创建 question_short 表');
} catch (error) {
console.error('创建 question_short 表失败:', error);
results.failed.push({ operation: '创建 question_short 表', error: error.message });
}
// 创建exam表
try {
await systemDb.execAsync(systemSchema.exam.trim()); await systemDb.execAsync(systemSchema.exam.trim());
console.log('创建 exam 表成功'); console.log('创建 exam 表成功');
results.success.push('创建 exam 表');
} catch (error) {
console.error('创建 exam 表失败:', error);
results.failed.push({ operation: '创建 exam 表', error: error.message });
}
// 创建examinee表
try {
await systemDb.execAsync(systemSchema.examinee.trim()); await systemDb.execAsync(systemSchema.examinee.trim());
console.log('创建 examinee 表成功'); console.log('创建 examinee 表成功');
results.success.push('创建 examinee 表');
} catch (error) {
console.error('创建 examinee 表失败:', error);
results.failed.push({ operation: '创建 examinee 表', error: error.message });
}
// 插入默认数据 // 插入默认数据
console.log('开始插入默认数据...'); console.log('开始插入默认数据...');
// 处理密码哈希
try {
const plainPassword = defaultData.config.find(item => item.key === 'admin_password').value; const plainPassword = defaultData.config.find(item => item.key === 'admin_password').value;
const hashedPassword = await argon2.hash(plainPassword); const hashedPassword = await argon2.hash(plainPassword);
@ -119,26 +219,54 @@ async function initializeSystemDatabase() {
return item; return item;
}); });
await batchInsert(systemDb, 'config', configData); // 插入config表数据
console.log('插入 config 表数据成功'); try {
await batchInsert(systemDb, 'config', configData);
console.log('插入 config 表数据成功');
results.success.push('插入 config 表数据');
} catch (error) {
console.error('插入 config 表数据失败:', error);
results.failed.push({ operation: '插入 config 表数据', error: error.message });
}
} catch (error) {
console.error('处理密码哈希失败:', error);
results.failed.push({ operation: '处理密码哈希', error: error.message });
}
// 插入dict_types表数据
try {
await batchInsert(systemDb, 'dict_types', defaultData.dictTypes); await batchInsert(systemDb, 'dict_types', defaultData.dictTypes);
console.log('插入 dict_types 表数据成功'); console.log('插入 dict_types 表数据成功');
results.success.push('插入 dict_types 表数据');
} catch (error) {
console.error('插入 dict_types 表数据失败:', error);
results.failed.push({ operation: '插入 dict_types 表数据', error: error.message });
}
// 插入dict_items表数据
try {
await batchInsert(systemDb, 'dict_items', defaultData.dictItems); await batchInsert(systemDb, 'dict_items', defaultData.dictItems);
console.log('插入 dict_items 表数据成功'); console.log('插入 dict_items 表数据成功');
results.success.push('插入 dict_items 表数据');
// 提交事务
await systemDb.runAsync('COMMIT');
console.log('提交事务成功');
return true;
} catch (error) { } catch (error) {
// 处理错误 console.error('插入 dict_items 表数据失败:', error);
await systemDb.runAsync('ROLLBACK'); results.failed.push({ operation: '插入 dict_items 表数据', error: error.message });
console.error('回滚事务:', error);
throw error;
} }
console.log('系统数据库初始化结果:');
console.log('成功操作:', results.success);
console.log('失败操作:', results.failed);
// 如果有失败操作,抛出错误
if (results.failed.length > 0) {
console.log(`系统数据库初始化有 ${results.failed.length} 个操作失败,请查看日志`);
// 输出详细的失败信息
results.failed.forEach((item, index) => {
console.log(`${index + 1}. ${item.operation} 失败: ${item.error}`);
});
}
return true;
} }
// 初始化用户数据库 // 初始化用户数据库
@ -147,54 +275,96 @@ async function initializeUserDatabase() {
const userDbPath = getUserDbPath(); const userDbPath = getUserDbPath();
const userDb = await getDbConnection(userDbPath); const userDb = await getDbConnection(userDbPath);
// 记录成功和失败的操作
const results = { success: [], failed: [] };
// 创建表结构
console.log('开始创建用户数据库表结构...');
// 创建examinee表
try { try {
// 开始事务 await userDb.execAsync(userSchema.examinee.trim());
await userDb.runAsync('BEGIN TRANSACTION'); console.log('创建 examinee 表成功');
console.log('开始事务'); results.success.push('创建 examinee 表');
// 创建表结构
console.log('开始创建用户数据库表结构...');
await userDb.execAsync(userSchema.examineeLog.trim());
console.log('创建 examinee_log 表成功');
await userDb.execAsync(userSchema.examineeExam.trim());
console.log('创建 examinee_exam 表成功');
await userDb.execAsync(userSchema.examineePapers.trim());
console.log('创建 examinee_papers 表成功');
await userDb.execAsync(userSchema.paperQuestions.trim());
console.log('创建 paper_questions 表成功');
await userDb.execAsync(userSchema.paperQuestionChoices.trim());
console.log('创建 paper_question_choices 表成功');
await userDb.execAsync(userSchema.paperQuestionBlanks.trim());
console.log('创建 paper_question_blanks 表成功');
await userDb.execAsync(userSchema.paperQuestionJudge.trim());
console.log('创建 paper_question_judge 表成功');
await userDb.execAsync(userSchema.paperQuestionFillTable.trim());
console.log('创建 paper_question_fill_table 表成功');
await userDb.execAsync(userSchema.paperQuestionFillTableBlanks.trim());
console.log('创建 paper_question_fill_table_blanks 表成功');
await userDb.execAsync(userSchema.paperQuestionSubjective.trim());
console.log('创建 paper_question_subjective 表成功');
// 提交事务
await userDb.runAsync('COMMIT');
console.log('提交事务成功');
return true;
} catch (error) { } catch (error) {
// 处理错误 console.error('创建 examinee 表失败:', error);
await userDb.runAsync('ROLLBACK'); results.failed.push({ operation: '创建 examinee 表', error: error.message });
console.error('回滚事务:', error);
throw error;
} }
// 创建examinee_papers表
try {
await userDb.execAsync(userSchema.examinee_papers.trim());
console.log('创建 examinee_papers 表成功');
results.success.push('创建 examinee_papers 表');
} catch (error) {
console.error('创建 examinee_papers 表失败:', error);
results.failed.push({ operation: '创建 examinee_papers 表', error: error.message });
}
// 创建paper_questions表
try {
await userDb.execAsync(userSchema.paper_questions.trim());
console.log('创建 paper_questions 表成功');
results.success.push('创建 paper_questions 表');
} catch (error) {
console.error('创建 paper_questions 表失败:', error);
results.failed.push({ operation: '创建 paper_questions 表', error: error.message });
}
// 创建question_datasets表
try {
await userDb.execAsync(userSchema.question_datasets.trim());
console.log('创建 question_datasets 表成功');
results.success.push('创建 question_datasets 表');
} catch (error) {
console.error('创建 question_datasets 表失败:', error);
results.failed.push({ operation: '创建 question_datasets 表', error: error.message });
}
// 创建question_images表
try {
await userDb.execAsync(userSchema.question_images.trim());
console.log('创建 question_images 表成功');
results.success.push('创建 question_images 表');
} catch (error) {
console.error('创建 question_images 表失败:', error);
results.failed.push({ operation: '创建 question_images 表', error: error.message });
}
// 创建question_choices表
try {
await userDb.execAsync(userSchema.question_choices.trim());
console.log('创建 question_choices 表成功');
results.success.push('创建 question_choices 表');
} catch (error) {
console.error('创建 question_choices 表失败:', error);
results.failed.push({ operation: '创建 question_choices 表', error: error.message });
}
// 创建question_fill_blanks表
try {
await userDb.execAsync(userSchema.question_fill_blanks.trim());
console.log('创建 question_fill_blanks 表成功');
results.success.push('创建 question_fill_blanks 表');
} catch (error) {
console.error('创建 question_fill_blanks 表失败:', error);
results.failed.push({ operation: '创建 question_fill_blanks 表', error: error.message });
}
console.log('用户数据库初始化结果:');
console.log('成功操作:', results.success);
console.log('失败操作:', results.failed);
// 如果有失败操作,仅打印错误信息,不抛出异常
if (results.failed.length > 0) {
console.error(`用户数据库初始化有 ${results.failed.length} 个操作失败,请查看日志`);
// 输出详细的失败信息
results.failed.forEach((item, index) => {
console.error(`${index + 1}. ${item.operation} 失败: ${item.error}`);
});
}
return true;
} }
// 初始化数据库 // 初始化数据库

View File

@ -370,6 +370,40 @@ async function getFillBlankQuestionsByQuestionId(questionId) {
}); });
} }
/**
* 查询试题总数和总分
* @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 { export {
addQuestion, addQuestion,
getAllQuestions, getAllQuestions,
@ -383,5 +417,6 @@ export {
addFillBlankQuestion, addFillBlankQuestion,
updateFillBlankQuestion, updateFillBlankQuestion,
deleteFillBlankQuestion, deleteFillBlankQuestion,
getFillBlankQuestionsByQuestionId getFillBlankQuestionsByQuestionId,
}; getQuestionsCountAndScore
};

View File

@ -1,5 +1,5 @@
// 辅助函数将db.run包装为Promise // 辅助函数将db.run包装为Promise
const runAsync = (db, sql, params = []) => { export const runAsync = (db, sql, params = []) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
db.run(sql, params, function (err) { db.run(sql, params, function (err) {
if (err) reject(err); if (err) reject(err);
@ -8,10 +8,8 @@ const runAsync = (db, sql, params = []) => {
}); });
}; };
export { runAsync };
// 系统数据库表结构 // 系统数据库表结构
const systemSchema = { export const systemSchema = {
config: ` config: `
CREATE TABLE IF NOT EXISTS config ( CREATE TABLE IF NOT EXISTS config (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
@ -184,124 +182,97 @@ const systemSchema = {
}; };
// 用户数据库表结构 // 用户数据库表结构
const userSchema = { export const userSchema = {
examineeLog: ` examinee: `
CREATE TABLE IF NOT EXISTS examinee_log ( CREATE TABLE IF NOT EXISTS examinee (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
examinee_id INTEGER NOT NULL, examinee_name TEXT NOT NULL DEFAULT '',
operation TEXT NOT NULL DEFAULT '', examinee_gender TEXT NOT NULL DEFAULT '',
operation_time TEXT NOT NULL, 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_id_card TEXT NOT NULL DEFAULT '',
examinee_admission_ticket TEXT NOT NULL DEFAULT '',
created_at TEXT DEFAULT CURRENT_TIMESTAMP created_at TEXT DEFAULT CURRENT_TIMESTAMP
); );
`, `,
examineeExam: ` examinee_papers: `
CREATE TABLE IF NOT EXISTS examinee_exam ( CREATE TABLE IF NOT EXISTS examinee_papers (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
examinee_id INTEGER NOT NULL, examinee_id INTEGER NOT NULL,
exam_name TEXT NOT NULL DEFAULT '', paper_minutes INTEGER NOT NULL DEFAULT 0,
exam_description TEXT NOT NULL DEFAULT '', paper_minuts_min INTEGER NOT NULL DEFAULT 0,
exam_examinee_type TEXT NOT NULL DEFAULT '', paper_start_time TEXT,
exam_notice TEXT NOT NULL DEFAULT '', paper_last_time TEXT,
exam_minutes INTEGER NOT NULL DEFAULT 0, paper_submit_time TEXT,
start_time TEXT NOT NULL, paper_end_time TEXT,
latest_end_time TEXT NOT NULL, paper_status INTEGER NOT NULL DEFAULT 0, -- 试卷状态0未开始1进行中2已交卷
end_time TEXT NOT NULL, paper_score_real REAL DEFAULT 0,
exam_duration INTEGER NOT NULL DEFAULT 0, paper_score REAL DEFAULT 0,
created_at TEXT DEFAULT CURRENT_TIMESTAMP
);
`,
paper_questions: `
CREATE TABLE IF NOT EXISTS paper_questions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
examinee_id INTEGER NOT NULL,
paper_id INTEGER NOT NULL,
question_type TEXT NOT NULL,
question_name TEXT NOT NULL DEFAULT '',
question_description TEXT NOT NULL DEFAULT '',
created_at TEXT DEFAULT CURRENT_TIMESTAMP
);
`,
question_datasets: `
CREATE TABLE IF NOT EXISTS question_datasets (
id INTEGER PRIMARY KEY AUTOINCREMENT,
question_id INTEGER NOT NULL,
dataset_name TEXT NOT NULL DEFAULT '',
dataset_data TEXT NOT NULL,
created_at TEXT DEFAULT CURRENT_TIMESTAMP
);
`,
question_images: `
CREATE TABLE IF NOT EXISTS question_images (
id INTEGER PRIMARY KEY AUTOINCREMENT,
question_id INTEGER NOT NULL,
image_name TEXT NOT NULL DEFAULT '',
image_base64 TEXT NOT NULL,
created_at TEXT DEFAULT CURRENT_TIMESTAMP, created_at TEXT DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT DEFAULT CURRENT_TIMESTAMP updated_at TEXT DEFAULT CURRENT_TIMESTAMP
); );
`, `,
examineePapers: ` question_choices: `
CREATE TABLE IF NOT EXISTS examinee_papers ( CREATE TABLE IF NOT EXISTS question_choices (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
examinee_id INTEGER NOT NULL, question_id INTEGER NOT NULL,
exam_id INTEGER NOT NULL, choice_description TEXT NOT NULL DEFAULT '',
paper_minutes INTEGER NOT NULL DEFAULT 0, choice_type TEXT NOT NULL DEFAULT 'single',
paper_total_score INTEGER NOT NULL DEFAULT 0,
created_at TEXT DEFAULT CURRENT_TIMESTAMP
);
`,
paperQuestions: `
CREATE TABLE IF NOT EXISTS paper_questions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
examinee_paper_id INTEGER NOT NULL,
question_type TEXT NOT NULL,
question_name TEXT NOT NULL DEFAULT '',
question_description TEXT NOT NULL DEFAULT '',
question_score INTEGER NOT NULL DEFAULT 0,
created_at TEXT DEFAULT CURRENT_TIMESTAMP
);
`,
paperQuestionChoices: `
CREATE TABLE IF NOT EXISTS paper_question_choices (
id INTEGER PRIMARY KEY AUTOINCREMENT,
paper_question_id INTEGER NOT NULL,
choice_options TEXT NOT NULL, choice_options TEXT NOT NULL,
user_answers TEXT NOT NULL DEFAULT '', correct_answers TEXT NOT NULL,
examinee_answers TEXT NOT NULL DEFAULT '',
score REAL,
score_real REAL DEFAULT 0, -- 本题得分
created_at TEXT DEFAULT CURRENT_TIMESTAMP, created_at TEXT DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP
FOREIGN KEY (paper_question_id) REFERENCES paper_questions(id)
); );
`, `,
paperQuestionBlanks: ` question_fill_blanks: `
CREATE TABLE IF NOT EXISTS paper_question_blanks ( CREATE TABLE IF NOT EXISTS question_fill_blanks (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
paper_question_id INTEGER NOT NULL, question_id INTEGER NOT NULL,
blank_description TEXT NOT NULL DEFAULT '',
blank_count INTEGER NOT NULL DEFAULT 0, blank_count INTEGER NOT NULL DEFAULT 0,
user_answers TEXT NOT NULL DEFAULT '', correct_answers TEXT NOT NULL DEFAULT '',
examinee_answers TEXT NOT NULL DEFAULT '',
score REAL,
score_real REAL DEFAULT 0, -- 本题得分
created_at TEXT DEFAULT CURRENT_TIMESTAMP, created_at TEXT DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP
FOREIGN KEY (paper_question_id) REFERENCES paper_questions(id)
); );
`, `
paperQuestionJudge: `
CREATE TABLE IF NOT EXISTS paper_question_judge (
id INTEGER PRIMARY KEY AUTOINCREMENT,
paper_question_id INTEGER NOT NULL,
judge_ask TEXT NOT NULL DEFAULT '',
user_answer INTEGER NOT NULL DEFAULT 0,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (paper_question_id) REFERENCES paper_questions(id)
);
`,
paperQuestionFillTable: `
CREATE TABLE IF NOT EXISTS paper_question_fill_table (
id INTEGER PRIMARY KEY AUTOINCREMENT,
paper_question_id INTEGER NOT NULL,
table_name TEXT NOT NULL DEFAULT '',
table_data TEXT NOT NULL,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (paper_question_id) REFERENCES paper_questions(id)
);
`,
paperQuestionFillTableBlanks: `
CREATE TABLE IF NOT EXISTS paper_question_fill_table_blanks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
paper_question_fill_table_id INTEGER NOT NULL,
cell_position TEXT NOT NULL,
cell_type TEXT NOT NULL DEFAULT 'number',
user_answer TEXT NOT NULL DEFAULT '',
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (paper_question_fill_table_id) REFERENCES paper_question_fill_table(id)
);
`,
paperQuestionSubjective: `
CREATE TABLE IF NOT EXISTS paper_question_subjective (
id INTEGER PRIMARY KEY AUTOINCREMENT,
paper_question_id INTEGER NOT NULL,
subjective_type TEXT NOT NULL,
user_answer TEXT NOT NULL DEFAULT '',
score INTEGER NOT NULL DEFAULT -1,
scored_by TEXT NOT NULL DEFAULT '',
scored_at TEXT DEFAULT CURRENT_TIMESTAMP,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (paper_question_id) REFERENCES paper_questions(id)
);
`,
}; };
// 初始化默认数据 // 初始化默认数据
@ -310,7 +281,7 @@ const plainPassword = "t2t6a9"; // 明文密码变量定义
// 注意在实际初始化数据库时需要使用argon2对plainPassword进行哈希 // 注意在实际初始化数据库时需要使用argon2对plainPassword进行哈希
// 这里只定义默认数据结构哈希操作应在index.js中的初始化函数中完成 // 这里只定义默认数据结构哈希操作应在index.js中的初始化函数中完成
const defaultData = { export const defaultData = {
config: [ config: [
{ key: "admin_password", value: plainPassword, protected: 1 }, { key: "admin_password", value: plainPassword, protected: 1 },
{ key: "question_bank_version", value: "1", protected: 1 }, { key: "question_bank_version", value: "1", protected: 1 },
@ -368,7 +339,7 @@ const defaultData = {
item_code: "fill_blank", item_code: "fill_blank",
item_name: "填空题", item_name: "填空题",
item_description: "填写空白处的答案", item_description: "填写空白处的答案",
is_active: 0, is_active: 1,
parent_code: "objective", parent_code: "objective",
}, },
{ {
@ -431,5 +402,3 @@ const defaultData = {
], ],
}; };
// 导出对象
export { systemSchema, userSchema, defaultData };

View File

@ -49,6 +49,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
updateFillBlankQuestion: (id, fillBlankData) => ipcRenderer.invoke('question-update-fill-blank', id, fillBlankData), updateFillBlankQuestion: (id, fillBlankData) => ipcRenderer.invoke('question-update-fill-blank', id, fillBlankData),
deleteFillBlankQuestion: (id) => ipcRenderer.invoke('question-delete-fill-blank', id), deleteFillBlankQuestion: (id) => ipcRenderer.invoke('question-delete-fill-blank', id),
fetchFillBlankQuestionsByQuestionId: (questionId) => ipcRenderer.invoke('question-fetch-fill-blank-by-question-id', questionId), fetchFillBlankQuestionsByQuestionId: (questionId) => ipcRenderer.invoke('question-fetch-fill-blank-by-question-id', questionId),
getQuestionsCountAndScore: () => ipcRenderer.invoke('question-get-count-and-score'),
// 考试管理相关API // 考试管理相关API
createExam: (examData) => ipcRenderer.invoke('exam-create', examData), createExam: (examData) => ipcRenderer.invoke('exam-create', examData),
fetchAllExams: () => ipcRenderer.invoke('exam-fetch-all'), fetchAllExams: () => ipcRenderer.invoke('exam-fetch-all'),

View File

@ -11,7 +11,8 @@ import {
addFillBlankQuestion, addFillBlankQuestion,
updateFillBlankQuestion, updateFillBlankQuestion,
deleteFillBlankQuestion, deleteFillBlankQuestion,
getFillBlankQuestionsByQuestionId getFillBlankQuestionsByQuestionId,
getQuestionsCountAndScore
} from '../db/question.js'; } from '../db/question.js';
// 导入configService中的increaseQuestionBankVersion方法 // 导入configService中的increaseQuestionBankVersion方法
@ -231,8 +232,21 @@ export async function fetchFillBlankQuestionsByQuestionId(questionId) {
} }
/** /**
* 初始化question相关IPC * 服务层查询试题总数和总分
* @param {*} ipcMain * @returns {Promise<{totalQuestions: number, totalScore: number}>} 包含试题总数和总分的对象
*/
export async function fetchQuestionsCountAndScore() {
try {
return await getQuestionsCountAndScore();
} catch (error) {
console.error('服务层: 查询试题总数和总分失败', error);
throw error;
}
}
/**
* 初始化试题相关IPC通信
* @param {Electron.IpcMain} ipcMain - IPC主进程实例
*/ */
export async function initQuestionIpc(ipcMain) { export async function initQuestionIpc(ipcMain) {
// 题干管理相关IPC // 题干管理相关IPC
@ -361,4 +375,14 @@ export async function initQuestionIpc(ipcMain) {
throw 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;
}
});
}

View File

@ -85,7 +85,7 @@
考题数量 考题数量
</div> </div>
</template> </template>
<span class="text-gray-500">待设置</span> <span class="text-gray-500">{{ totalQuestions }}</span>
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item> <el-descriptions-item>
<template #label> <template #label>
@ -96,7 +96,7 @@
考试总分 考试总分
</div> </div>
</template> </template>
<span class="text-gray-500">待设置</span> <span class="text-gray-500">{{ totalScore }}</span>
</el-descriptions-item> </el-descriptions-item>
</el-descriptions> </el-descriptions>
@ -120,6 +120,10 @@
暂无考试须知 暂无考试须知
</div> </div>
</div> </div>
<div class="text-center">
<el-button type="info" @click="exitExam">返回</el-button>
<el-button type="primary" @click="startExam">开始考试</el-button>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -135,7 +139,6 @@
import Header from '@/components/common/Header.vue' import Header from '@/components/common/Header.vue'
import Footer from '@/components/common/Footer.vue' import Footer from '@/components/common/Footer.vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import { ref, computed, onMounted, watch } from 'vue' // watch import { ref, computed, onMounted, watch } from 'vue' // watch
import { ElMessage, ElDescriptions, ElDescriptionsItem, ElIcon } from 'element-plus' import { ElMessage, ElDescriptions, ElDescriptionsItem, ElIcon } from 'element-plus'
import { User, Postcard, Ticket, Clock, ScaleToOriginal, Timer } from '@element-plus/icons-vue' import { User, Postcard, Ticket, Clock, ScaleToOriginal, Timer } from '@element-plus/icons-vue'
@ -161,6 +164,8 @@ const size = ref('default')
const lastExam = ref(null) const lastExam = ref(null)
const examLoading = ref(true) const examLoading = ref(true)
const examNotices = ref([]) const examNotices = ref([])
const totalQuestions = ref(0)
const totalScore = ref(0)
// watchlastExam // watchlastExam
watch(lastExam, (newValue) => { watch(lastExam, (newValue) => {
@ -195,6 +200,18 @@ const formatExamDuration = (minutes) => {
return `${hours}小时${mins}分钟` return `${hours}小时${mins}分钟`
} }
//
const getQuestionsCountAndScore = async () => {
try {
const result = await window.electronAPI.getQuestionsCountAndScore()
totalQuestions.value = result.totalQuestions
totalScore.value = result.totalScore
} catch (error) {
console.error('获取考题数量和总分失败:', error)
ElMessage.error(`获取考题数量和总分失败: ${error.message || '未知错误'}`)
}
}
// //
const fetchLastExam = async () => { const fetchLastExam = async () => {
examLoading.value = true examLoading.value = true
@ -218,6 +235,19 @@ const fetchLastExam = async () => {
} }
} }
const exitExam = async () => {
try {
if (!confirm('确定要退出考试吗?')) {
return
}
userStore.state.isLoggedIn = false
router.push('/')
} catch (error) {
console.error('退出考试失败:', error)
ElMessage.error(`退出考试失败: ${error.message || '未知错误'}`)
}
}
// //
const startExam = async () => { const startExam = async () => {
if (!lastExam.value) { if (!lastExam.value) {
@ -251,6 +281,7 @@ const startExam = async () => {
// //
onMounted(() => { onMounted(() => {
fetchLastExam() fetchLastExam()
getQuestionsCountAndScore()
}) })
</script> </script>