feat: 添加学生个人中心页面和数据库备份功能
refactor(auth): 重构认证模块适配Bootstrap 5样式 feat(controller): 在登录响应中返回用户对象 feat(server): 添加学生个人中心路由 refactor(models): 重构学生和成绩模型结构 style: 更新登录和注册页面UI设计 chore: 添加数据库备份脚本和空备份文件
This commit is contained in:
@@ -14,7 +14,7 @@ class AuthController {
|
||||
// 设置 Session
|
||||
req.session.user = user;
|
||||
|
||||
success(res, user, '登录成功');
|
||||
success(res, { user }, '登录成功');
|
||||
} catch (err) {
|
||||
if (err.message === '用户名或密码错误') {
|
||||
return error(res, err.message, 401);
|
||||
|
||||
@@ -3,9 +3,11 @@ const db = require('../config/database');
|
||||
class Score {
|
||||
static async findByStudentId(studentId) {
|
||||
const sql = `
|
||||
SELECT s.*, c.course_code, c.course_name, c.credit,
|
||||
SELECT s.id, s.student_id, s.course_id, s.teacher_id,
|
||||
s.total_score as score, s.grade_point, s.grade_level, s.created_at,
|
||||
c.course_code, c.course_name, c.credit,
|
||||
u.name as teacher_name
|
||||
FROM scores s
|
||||
FROM grades s
|
||||
JOIN courses c ON s.course_id = c.id
|
||||
JOIN users u ON s.teacher_id = u.id
|
||||
WHERE s.student_id = ?
|
||||
@@ -16,10 +18,13 @@ class Score {
|
||||
|
||||
static async findDetailsById(scoreId, studentId) {
|
||||
const sql = `
|
||||
SELECT s.*, c.course_code, c.course_name, c.credit, c.semester,
|
||||
SELECT s.id, s.student_id, s.course_id, s.teacher_id,
|
||||
s.usual_score, s.midterm_score, s.final_score, s.total_score as score,
|
||||
s.grade_point, s.grade_level, s.created_at, s.remark,
|
||||
c.course_code, c.course_name, c.credit, c.semester,
|
||||
u.name as teacher_name, u.email as teacher_email,
|
||||
st.id as student_number, st.class as class_name
|
||||
FROM scores s
|
||||
FROM grades s
|
||||
JOIN courses c ON s.course_id = c.id
|
||||
JOIN users u ON s.teacher_id = u.id
|
||||
JOIN students st ON s.student_id = st.id
|
||||
@@ -30,22 +35,23 @@ class Score {
|
||||
}
|
||||
|
||||
static async create(scoreData) {
|
||||
const { studentId, courseId, teacherId, score, gradePoint, gradeLevel, examDate, remark } = scoreData;
|
||||
const { studentId, courseId, teacherId, score, gradePoint, gradeLevel, remark } = scoreData;
|
||||
// 既然TeacherService只传了score,我们默认把它作为 final_score 和 total_score
|
||||
// 如果需要支持平时分/期中分,需要修改前端和Controller
|
||||
const sql = `
|
||||
INSERT INTO scores (student_id, course_id, teacher_id, score,
|
||||
INSERT INTO grades (student_id, course_id, teacher_id, final_score, total_score,
|
||||
grade_point, grade_level, created_at, remark)
|
||||
VALUES (?, ?, ?, ?, ?, ?, NOW(), ?)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, NOW(), ?)
|
||||
`;
|
||||
// 注意:这里用 created_at 替代了 examDate,如果数据库有 examDate 列可以改回去
|
||||
const result = await db.pool.execute(sql, [
|
||||
studentId, courseId, teacherId, score, gradePoint, gradeLevel, remark
|
||||
studentId, courseId, teacherId, score, score, gradePoint, gradeLevel, remark
|
||||
]);
|
||||
return result[0].insertId;
|
||||
}
|
||||
|
||||
static async findByStudentAndCourse(studentId, courseId) {
|
||||
const rows = await db.query(
|
||||
'SELECT * FROM scores WHERE student_id = ? AND course_id = ?',
|
||||
'SELECT * FROM grades WHERE student_id = ? AND course_id = ?',
|
||||
[studentId, courseId]
|
||||
);
|
||||
return rows[0];
|
||||
|
||||
@@ -1,21 +1,16 @@
|
||||
const db = require('../config/database');
|
||||
|
||||
class Student {
|
||||
static async findByUserId(userId) {
|
||||
const students = await db.query('SELECT * FROM students WHERE user_id = ?', [userId]);
|
||||
return students[0];
|
||||
}
|
||||
|
||||
static async findById(studentId) {
|
||||
const students = await db.query('SELECT * FROM students WHERE student_id = ?', [studentId]);
|
||||
static async findById(id) {
|
||||
const students = await db.query('SELECT * FROM students WHERE id = ?', [id]);
|
||||
return students[0];
|
||||
}
|
||||
|
||||
static async create(studentData) {
|
||||
const { id, name, className, userId } = studentData;
|
||||
const { id, name, className } = studentData;
|
||||
await db.query(
|
||||
'INSERT INTO students (id, name, class, user_id) VALUES (?, ?, ?, ?)',
|
||||
[id, name, className, userId]
|
||||
'INSERT INTO students (id, name, class) VALUES (?, ?, ?)',
|
||||
[id, name, className]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
62
backend/scripts/backup.js
Normal file
62
backend/scripts/backup.js
Normal file
@@ -0,0 +1,62 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const db = require('../config/database');
|
||||
|
||||
async function backup() {
|
||||
console.log('开始备份数据库...');
|
||||
const backupDir = path.join(__dirname, '../../database');
|
||||
const filename = `backup_${new Date().toISOString().replace(/[:.]/g, '-')}.sql`;
|
||||
const filepath = path.join(backupDir, filename);
|
||||
|
||||
if (!fs.existsSync(backupDir)) {
|
||||
fs.mkdirSync(backupDir, { recursive: true });
|
||||
}
|
||||
|
||||
try {
|
||||
const fileStream = fs.createWriteStream(filepath, { flags: 'a' });
|
||||
|
||||
// 获取所有表
|
||||
const tables = await db.query('SHOW TABLES');
|
||||
const tableNames = tables.map(t => Object.values(t)[0]);
|
||||
|
||||
for (const tableName of tableNames) {
|
||||
console.log(`正在备份表: ${tableName}`);
|
||||
|
||||
// 1. 获取 CREATE TABLE 语句
|
||||
const createRows = await db.query(`SHOW CREATE TABLE ${tableName}`);
|
||||
const createTableSql = createRows[0]['Create Table'];
|
||||
|
||||
fileStream.write(`\n\n-- Table structure for table \`${tableName}\`\n`);
|
||||
fileStream.write(`DROP TABLE IF EXISTS \`${tableName}\`;\n`);
|
||||
fileStream.write(`${createTableSql};\n`);
|
||||
|
||||
// 2. 获取数据
|
||||
const rows = await db.query(`SELECT * FROM ${tableName}`);
|
||||
if (rows.length > 0) {
|
||||
fileStream.write(`\n-- Dumping data for table \`${tableName}\`\n`);
|
||||
fileStream.write(`INSERT INTO \`${tableName}\` VALUES\n`);
|
||||
|
||||
const values = rows.map(row => {
|
||||
const rowValues = Object.values(row).map(val => {
|
||||
if (val === null) return 'NULL';
|
||||
if (typeof val === 'number') return val;
|
||||
// 转义单引号和反斜杠
|
||||
return `'${String(val).replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`;
|
||||
});
|
||||
return `(${rowValues.join(', ')})`;
|
||||
});
|
||||
|
||||
fileStream.write(values.join(',\n') + ';\n');
|
||||
}
|
||||
}
|
||||
|
||||
fileStream.end();
|
||||
console.log(`备份完成! 文件保存在: ${filepath}`);
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
console.error('备份失败:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
backup();
|
||||
@@ -96,6 +96,9 @@ app.get('/dashboard', requirePageAuth, (req, res) => {
|
||||
app.get('/student/dashboard', requirePageAuth, requirePageRole(['student']), (req, res) => {
|
||||
res.sendFile(path.join(__dirname, '../frontend/views/student/dashboard.html'));
|
||||
});
|
||||
app.get('/student/profile', requirePageAuth, requirePageRole(['student']), (req, res) => {
|
||||
res.sendFile(path.join(__dirname, '../frontend/views/student/profile.html'));
|
||||
});
|
||||
|
||||
// Teacher Pages
|
||||
const teacherPageRouter = express.Router();
|
||||
|
||||
Reference in New Issue
Block a user