新增教师资料更新功能,包括个人信息修改和密码更新 添加操作日志记录系统,记录用户关键操作 实现系统设置模块,支持动态配置系统参数 重构数据库模型,新增教师表和系统设置表 优化成绩录入逻辑,支持平时分、期中和期末成绩计算 添加数据导出功能,支持学生、教师和成绩数据导出 完善管理员后台,增加统计图表和操作日志查看
344 lines
12 KiB
JavaScript
344 lines
12 KiB
JavaScript
const sqlite3 = require('sqlite3').verbose();
|
|
const path = require('path');
|
|
const bcrypt = require('bcryptjs');
|
|
|
|
const dbPath = path.resolve(__dirname, 'database.sqlite');
|
|
const db = new sqlite3.Database(dbPath);
|
|
|
|
const run = (sql, params = []) => {
|
|
return new Promise((resolve, reject) => {
|
|
db.run(sql, params, function(err) {
|
|
if (err) reject(err);
|
|
else resolve(this);
|
|
});
|
|
});
|
|
};
|
|
|
|
const insertUser = async (user) => {
|
|
await run(
|
|
'INSERT INTO users (id, name, password, role, class) VALUES (?, ?, ?, ?, ?)',
|
|
[user.id, user.name, user.password, user.role, user.class]
|
|
);
|
|
};
|
|
|
|
const insertStudent = async (student) => {
|
|
await run(
|
|
'INSERT INTO students (id, name, class, major, grade, contact_info) VALUES (?, ?, ?, ?, ?, ?)',
|
|
[student.id, student.name, student.class, student.major, student.grade, student.contact_info]
|
|
);
|
|
};
|
|
|
|
const insertClass = async (cls) => {
|
|
return await run(
|
|
'INSERT INTO classes (class_name, grade, major, teacher_id) VALUES (?, ?, ?, ?)',
|
|
[cls.class_name, cls.grade, cls.major, cls.teacher_id]
|
|
);
|
|
};
|
|
|
|
const insertCourse = async (course) => {
|
|
return await run(
|
|
'INSERT INTO courses (course_code, course_name, credit, teacher_id, class_id, semester, academic_year, category) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
|
|
[course.course_code, course.course_name, course.credit, course.teacher_id, course.class_id, course.semester, course.academic_year, course.category]
|
|
);
|
|
};
|
|
|
|
const insertGrade = async (grade) => {
|
|
await run(
|
|
`INSERT INTO grades (
|
|
student_id, course_id, teacher_id,
|
|
usual_score, midterm_score, final_score, total_score,
|
|
grade_point, grade_level, remark
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
[
|
|
grade.student_id, grade.course_id, grade.teacher_id,
|
|
grade.usual_score, grade.midterm_score, grade.final_score, grade.total_score,
|
|
grade.grade_point, grade.grade_level, grade.remark
|
|
]
|
|
);
|
|
};
|
|
|
|
// Helper to calculate grade point
|
|
const calculateGradePoint = (score) => {
|
|
if (score >= 90) return 4.0;
|
|
if (score >= 85) return 3.7;
|
|
if (score >= 82) return 3.3;
|
|
if (score >= 78) return 3.0;
|
|
if (score >= 75) return 2.7;
|
|
if (score >= 72) return 2.3;
|
|
if (score >= 68) return 2.0;
|
|
if (score >= 64) return 1.5;
|
|
if (score >= 60) return 1.0;
|
|
return 0.0;
|
|
};
|
|
|
|
const calculateGradeLevel = (score) => {
|
|
if (score >= 90) return 'A';
|
|
if (score >= 80) return 'B';
|
|
if (score >= 70) return 'C';
|
|
if (score >= 60) return 'D';
|
|
return 'F';
|
|
};
|
|
|
|
const init = async () => {
|
|
console.log('开始初始化 SQLite 数据库...');
|
|
const hashedPassword = await bcrypt.hash('123456', 10);
|
|
|
|
try {
|
|
// Drop tables
|
|
await run('DROP TABLE IF EXISTS grades');
|
|
await run('DROP TABLE IF EXISTS courses');
|
|
await run('DROP TABLE IF EXISTS classes');
|
|
await run('DROP TABLE IF EXISTS students');
|
|
await run('DROP TABLE IF EXISTS users');
|
|
await run('DROP TABLE IF EXISTS operation_logs');
|
|
|
|
// Create tables
|
|
console.log('创建表结构...');
|
|
|
|
await run(`
|
|
CREATE TABLE users (
|
|
id TEXT PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
password TEXT NOT NULL,
|
|
role TEXT NOT NULL,
|
|
class TEXT,
|
|
created_at TEXT DEFAULT (datetime('now', 'localtime'))
|
|
)
|
|
`);
|
|
|
|
await run(`
|
|
CREATE TABLE students (
|
|
id TEXT PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
class TEXT,
|
|
major TEXT,
|
|
grade TEXT,
|
|
contact_info TEXT,
|
|
FOREIGN KEY (id) REFERENCES users(id) ON DELETE CASCADE
|
|
)
|
|
`);
|
|
|
|
await run(`
|
|
CREATE TABLE classes (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
class_name TEXT NOT NULL,
|
|
grade TEXT,
|
|
major TEXT,
|
|
teacher_id TEXT,
|
|
created_at TEXT DEFAULT (datetime('now', 'localtime'))
|
|
)
|
|
`);
|
|
|
|
await run(`
|
|
CREATE TABLE courses (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
course_code TEXT UNIQUE NOT NULL,
|
|
course_name TEXT NOT NULL,
|
|
credit REAL DEFAULT 2.0,
|
|
teacher_id TEXT NOT NULL,
|
|
class_id INTEGER NOT NULL,
|
|
semester TEXT,
|
|
academic_year TEXT,
|
|
category TEXT,
|
|
created_at TEXT DEFAULT (datetime('now', 'localtime'))
|
|
)
|
|
`);
|
|
|
|
await run(`
|
|
CREATE TABLE grades (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
student_id TEXT NOT NULL,
|
|
course_id INTEGER NOT NULL,
|
|
teacher_id TEXT NOT NULL,
|
|
usual_score REAL,
|
|
midterm_score REAL,
|
|
final_score REAL,
|
|
total_score REAL,
|
|
grade_point REAL,
|
|
grade_level TEXT,
|
|
remark TEXT,
|
|
created_at TEXT DEFAULT (datetime('now', 'localtime')),
|
|
UNIQUE(student_id, course_id)
|
|
)
|
|
`);
|
|
|
|
await run(`
|
|
CREATE TABLE operation_logs (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id TEXT,
|
|
operation_type TEXT,
|
|
operation_target TEXT,
|
|
description TEXT,
|
|
ip_address TEXT,
|
|
created_at TEXT DEFAULT (datetime('now', 'localtime'))
|
|
)
|
|
`);
|
|
|
|
console.log('生成基础数据...');
|
|
|
|
// 1. Admin
|
|
await insertUser({
|
|
id: 'admin',
|
|
name: '系统管理员',
|
|
password: hashedPassword,
|
|
role: 'admin',
|
|
class: null
|
|
});
|
|
|
|
// 2. Teachers (20 teachers)
|
|
const teachers = [];
|
|
for (let i = 1; i <= 20; i++) {
|
|
const id = `T${1000 + i}`;
|
|
const name = `教师${String.fromCharCode(65 + (i % 26))}${i}`;
|
|
await insertUser({
|
|
id,
|
|
name,
|
|
password: hashedPassword,
|
|
role: 'teacher',
|
|
class: null
|
|
});
|
|
teachers.push(id);
|
|
}
|
|
|
|
// 3. Classes (10 classes)
|
|
const majors = ['软件工程', '计算机科学', '信息管理'];
|
|
const classIds = [];
|
|
const classes = [];
|
|
|
|
for (let i = 1; i <= 10; i++) {
|
|
const major = majors[i % 3];
|
|
const gradeYear = 2021 + Math.floor((i-1)/5); // 2021, 2022
|
|
const className = `${major.substr(0, 2)}${gradeYear}${String(i).padStart(2, '0')}`;
|
|
const teacherId = teachers[i % teachers.length];
|
|
|
|
const result = await insertClass({
|
|
class_name: className,
|
|
grade: String(gradeYear),
|
|
major: major,
|
|
teacher_id: teacherId
|
|
});
|
|
classIds.push(result.lastID);
|
|
classes.push({ id: result.lastID, name: className, year: gradeYear, major });
|
|
}
|
|
|
|
// 4. Students (50 per class -> 500 students)
|
|
console.log('生成学生数据...');
|
|
const students = [];
|
|
for (let clsIdx = 0; clsIdx < classes.length; clsIdx++) {
|
|
const cls = classes[clsIdx];
|
|
for (let i = 1; i <= 50; i++) {
|
|
const id = `${cls.year}${String(clsIdx + 1).padStart(2, '0')}${String(i).padStart(3, '0')}`;
|
|
const name = `学生${cls.name.substr(0,1)}${i}`;
|
|
|
|
await insertUser({
|
|
id,
|
|
name,
|
|
password: hashedPassword,
|
|
role: 'student',
|
|
class: cls.name
|
|
});
|
|
|
|
await insertStudent({
|
|
id,
|
|
name,
|
|
class: cls.name,
|
|
major: cls.major,
|
|
grade: String(cls.year),
|
|
contact_info: `13800${id.substr(0, 6)}`
|
|
});
|
|
students.push({ id, classId: cls.id });
|
|
}
|
|
}
|
|
|
|
// 5. Courses and Grades
|
|
console.log('生成课程和成绩数据...');
|
|
const courseNames = [
|
|
{ name: '高等数学', credit: 4, category: '必修' },
|
|
{ name: '大学英语', credit: 3, category: '必修' },
|
|
{ name: '程序设计基础', credit: 4, category: '必修' },
|
|
{ name: '数据结构', credit: 4, category: '必修' },
|
|
{ name: '操作系统', credit: 3, category: '必修' },
|
|
{ name: '计算机网络', credit: 3, category: '必修' },
|
|
{ name: '数据库原理', credit: 3, category: '必修' },
|
|
{ name: '软件工程导论', credit: 2, category: '必修' },
|
|
{ name: 'Web开发技术', credit: 3, category: '选修' },
|
|
{ name: '人工智能基础', credit: 2, category: '选修' },
|
|
{ name: '大数据分析', credit: 2, category: '选修' },
|
|
{ name: '音乐鉴赏', credit: 1, category: '通识' },
|
|
{ name: '心理健康', credit: 1, category: '通识' },
|
|
{ name: '职业规划', credit: 1, category: '通识' }
|
|
];
|
|
|
|
const semesters = ['2021-2022-1', '2021-2022-2', '2022-2023-1', '2022-2023-2', '2023-2024-1'];
|
|
|
|
for (const cls of classes) {
|
|
// For each class, assign some courses
|
|
for (let semIdx = 0; semIdx < semesters.length; semIdx++) {
|
|
const semester = semesters[semIdx];
|
|
// Select random 5-8 courses for this semester
|
|
const semCourses = courseNames.sort(() => 0.5 - Math.random()).slice(0, 6);
|
|
|
|
for (const cTemplate of semCourses) {
|
|
const teacherId = teachers[Math.floor(Math.random() * teachers.length)];
|
|
const courseCode = `C${cls.id}${semIdx}${Math.floor(Math.random() * 1000)}`;
|
|
|
|
const result = await insertCourse({
|
|
course_code: courseCode,
|
|
course_name: cTemplate.name,
|
|
credit: cTemplate.credit,
|
|
teacher_id: teacherId,
|
|
class_id: cls.id,
|
|
semester: semester,
|
|
academic_year: semester.substring(0, 9),
|
|
category: cTemplate.category
|
|
});
|
|
|
|
const courseId = result.lastID;
|
|
|
|
// Generate grades for students in this class
|
|
const classStudents = students.filter(s => s.classId === cls.id);
|
|
|
|
// Batch insert could be faster, but let's keep it simple for now
|
|
const stmt = db.prepare(`INSERT INTO grades (
|
|
student_id, course_id, teacher_id,
|
|
usual_score, midterm_score, final_score, total_score,
|
|
grade_point, grade_level, remark
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
|
|
for (const stu of classStudents) {
|
|
// 90% chance to have a grade
|
|
if (Math.random() > 0.1) {
|
|
const totalScore = Math.floor(Math.random() * 40) + 60; // 60-100 mostly
|
|
// 10% chance to fail
|
|
const finalTotal = Math.random() > 0.1 ? totalScore : Math.floor(Math.random() * 59);
|
|
|
|
stmt.run([
|
|
stu.id,
|
|
courseId,
|
|
teacherId,
|
|
finalTotal, // usual
|
|
finalTotal, // midterm
|
|
finalTotal, // final
|
|
finalTotal, // total
|
|
calculateGradePoint(finalTotal),
|
|
calculateGradeLevel(finalTotal),
|
|
''
|
|
]);
|
|
}
|
|
}
|
|
stmt.finalize();
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log('数据库初始化完成!');
|
|
db.close();
|
|
|
|
} catch (err) {
|
|
console.error('Initialization failed:', err);
|
|
process.exit(1);
|
|
}
|
|
};
|
|
|
|
init();
|