feat: 实现教师资料更新、操作日志和系统设置功能
新增教师资料更新功能,包括个人信息修改和密码更新 添加操作日志记录系统,记录用户关键操作 实现系统设置模块,支持动态配置系统参数 重构数据库模型,新增教师表和系统设置表 优化成绩录入逻辑,支持平时分、期中和期末成绩计算 添加数据导出功能,支持学生、教师和成绩数据导出 完善管理员后台,增加统计图表和操作日志查看
This commit is contained in:
343
backend/init_db.js
Normal file
343
backend/init_db.js
Normal file
@@ -0,0 +1,343 @@
|
||||
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();
|
||||
Reference in New Issue
Block a user