first commit

This commit is contained in:
祀梦
2025-12-21 21:50:37 +08:00
commit 352698044b
30 changed files with 9893 additions and 0 deletions

311
backend/routes/admin.js Normal file
View File

@@ -0,0 +1,311 @@
const express = require('express');
const router = express.Router();
const db = require('../config/database');
const { requireAuth, requireRole } = require('../middleware/auth');
/**
* 获取所有用户
*/
router.get('/users', requireAuth, requireRole(['admin']), async (req, res) => {
try {
const { page = 1, limit = 10, search = '', role = '' } = req.query;
const offset = (page - 1) * limit;
let query = 'SELECT id, name, role, class, created_at FROM users WHERE 1=1';
let params = [];
if (search) {
query += ' AND (id LIKE ? OR name LIKE ? OR class LIKE ?)';
const searchTerm = `%${search}%`;
params.push(searchTerm, searchTerm, searchTerm);
}
if (role) {
query += ' AND role = ?';
params.push(role);
}
// 获取总数
const countQuery = query.replace('SELECT id, name, role, class, created_at', 'SELECT COUNT(*) as total');
const countResult = await db.pool.execute(countQuery, params);
const total = countResult[0][0].total;
// 获取分页数据
query += ' ORDER BY created_at DESC LIMIT ? OFFSET ?';
params.push(parseInt(limit), parseInt(offset));
const users = await db.pool.execute(query, params);
res.json({
success: true,
data: users,
pagination: {
page: parseInt(page),
limit: parseInt(limit),
total,
pages: Math.ceil(total / limit)
}
});
} catch (error) {
console.error('获取用户列表错误:', error);
res.status(500).json({
success: false,
message: '服务器错误'
});
}
});
/**
* 创建用户
*/
router.post('/users', requireAuth, requireRole(['admin']), async (req, res) => {
try {
const { id, name, password, role, className } = req.body;
// 输入验证
if (!id || !name || !password || !role) {
return res.status(400).json({
success: false,
message: '请填写所有必填字段'
});
}
// 检查用户ID是否存在
const existingUsers = await db.pool.execute(
'SELECT id FROM users WHERE id = ?',
[id]
);
if (existingUsers[0].length > 0) {
return res.status(400).json({
success: false,
message: '用户ID已存在'
});
}
// 哈希密码
const bcrypt = require('bcrypt');
const salt = await bcrypt.genSalt(10);
const passwordHash = await bcrypt.hash(password, salt);
// 创建用户
const result = await db.pool.execute(
'INSERT INTO users (id, name, password, role, class) VALUES (?, ?, ?, ?, ?)',
[id, name, passwordHash, role, className || null]
);
const userId = result[0].insertId;
// 根据角色创建相关记录
if (role === 'student') {
const studentId = 'STU' + Date.now().toString().slice(-6);
await db.pool.execute(
'INSERT INTO students (user_id, student_id, full_name, class_name) VALUES (?, ?, ?, ?)',
[userId, studentId, fullName, className || '未分配班级']
);
} else if (role === 'teacher') {
await db.pool.execute(
'INSERT INTO teachers (user_id, full_name) VALUES (?, ?)',
[userId, fullName]
);
}
res.json({
success: true,
message: '用户创建成功',
userId
});
} catch (error) {
console.error('创建用户错误:', error);
res.status(500).json({
success: false,
message: '服务器错误'
});
}
});
/**
* 更新用户
*/
router.put('/users/:id', requireAuth, requireRole(['admin']), async (req, res) => {
try {
const userId = req.params.id;
const { name, role, className } = req.body;
// 检查用户是否存在
const users = await db.pool.execute(
'SELECT * FROM users WHERE id = ?',
[userId]
);
if (users[0].length === 0) {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
const oldRole = users[0][0].role;
// 更新用户信息
await db.pool.execute(
'UPDATE users SET name = ?, role = ?, class = ? WHERE id = ?',
[name, role, className || null, userId]
);
// 如果角色改变,更新相关记录
if (oldRole !== role) {
// 删除旧角色的记录
if (oldRole === 'student') {
await db.pool.execute('DELETE FROM students WHERE user_id = ?', [userId]);
} else if (oldRole === 'teacher') {
await db.pool.execute('DELETE FROM teachers WHERE user_id = ?', [userId]);
}
// 创建新角色的记录
if (role === 'student') {
await db.pool.execute(
'INSERT INTO students (user_id, class) VALUES (?, ?)',
[userId, className || null]
);
} else if (role === 'teacher') {
// 教师不需要额外表
}
} else if (role === 'student' && className) {
// 如果是学生且班级有变化,更新班级
await db.pool.execute(
'UPDATE students SET class = ? WHERE user_id = ?',
[className, userId]
);
}
res.json({
success: true,
message: '用户更新成功'
});
} catch (error) {
console.error('更新用户错误:', error);
res.status(500).json({
success: false,
message: '服务器错误'
});
}
});
/**
* 删除用户
*/
router.delete('/users/:id', requireAuth, requireRole(['admin']), async (req, res) => {
try {
const userId = req.params.id;
// 检查用户是否存在
const users = await db.pool.execute(
'SELECT role FROM users WHERE id = ?',
[userId]
);
if (users[0].length === 0) {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
const userRole = users[0][0].role;
// 删除相关记录
if (userRole === 'student') {
await db.pool.execute('DELETE FROM students WHERE user_id = ?', [userId]);
} else if (userRole === 'teacher') {
await db.pool.execute('DELETE FROM teachers WHERE user_id = ?', [userId]);
}
// 删除用户
await db.pool.execute('DELETE FROM users WHERE id = ?', [userId]);
res.json({
success: true,
message: '用户删除成功'
});
} catch (error) {
console.error('删除用户错误:', error);
res.status(500).json({
success: false,
message: '服务器错误'
});
}
});
/**
* 获取所有班级
*/
router.get('/classes', requireAuth, requireRole(['admin']), async (req, res) => {
try {
const classes = await db.pool.execute(
'SELECT DISTINCT class_name FROM students ORDER BY class_name'
);
res.json({
success: true,
data: classes
});
} catch (error) {
console.error('获取班级列表错误:', error);
res.status(500).json({
success: false,
message: '服务器错误'
});
}
});
/**
* 获取统计数据
*/
router.get('/stats', requireAuth, requireRole(['admin']), async (req, res) => {
try {
// 用户统计
const userStats = await db.pool.execute(
'SELECT role, COUNT(*) as count FROM users GROUP BY role'
);
// 班级统计
const classStats = await db.pool.execute(
'SELECT class_name, COUNT(*) as count FROM students GROUP BY class_name'
);
// 课程统计
const courseStats = await db.pool.execute(
'SELECT COUNT(*) as total_courses FROM courses'
);
// 成绩统计
const gradeStats = await db.pool.execute(
'SELECT COUNT(*) as total_grades FROM scores'
);
res.json({
success: true,
data: {
users: userStats[0],
classes: classStats[0],
total_courses: courseStats[0][0].total_courses,
total_grades: gradeStats[0][0].total_grades
}
});
} catch (error) {
console.error('获取统计数据错误:', error);
res.status(500).json({
success: false,
message: '服务器错误'
});
}
});
module.exports = router;

175
backend/routes/auth.js Normal file
View File

@@ -0,0 +1,175 @@
const express = require('express');
const bcrypt = require('bcryptjs');
const router = express.Router();
const db = require('../config/database');
// 登录
router.post('/login', async (req, res) => {
try {
const { id, password, role } = req.body;
// 输入验证
if (!id || !password || !role) {
return res.status(400).json({
success: false,
message: '请输入完整的登录信息'
});
}
// 查询用户
const users = await db.query(
'SELECT * FROM users WHERE id = ? AND role = ?',
[id, role]
);
if (users.length === 0) {
return res.status(401).json({
success: false,
message: '用户名或密码错误'
});
}
const user = users[0];
// 验证密码
const isValidPassword = await bcrypt.compare(password, user.password);
if (!isValidPassword) {
return res.status(401).json({
success: false,
message: '用户名或密码错误'
});
}
// 设置会话
req.session.user = {
id: user.id,
name: user.name,
role: user.role,
class: user.class
};
// 如果是学生,获取学生信息
if (user.role === 'student') {
const [students] = await db.pool.execute(
'SELECT * FROM students WHERE id = ?',
[user.id]
);
if (students[0].length > 0) {
req.session.user.studentInfo = students[0][0];
}
}
res.json({
success: true,
message: '登录成功',
user: req.session.user
});
} catch (error) {
console.error('登录错误:', error);
res.status(500).json({
success: false,
message: '服务器错误'
});
}
});
// 注册
router.post('/register', async (req, res) => {
try {
const { id, name, password, role, class: userClass } = req.body;
// 输入验证
if (!id || !name || !password || !role) {
return res.status(400).json({
success: false,
message: '请填写所有必填字段ID、姓名、密码、角色'
});
}
// 学生和教师需要班级字段,管理员不需要
if ((role === 'student' || role === 'teacher') && !userClass) {
return res.status(400).json({
success: false,
message: '学生和教师需要填写班级'
});
}
// 检查用户ID是否存在
const existingUsers = await db.query(
'SELECT id FROM users WHERE id = ?',
[id]
);
if (existingUsers.length > 0) {
return res.status(400).json({
success: false,
message: '用户ID已存在'
});
}
// 哈希密码
const salt = await bcrypt.genSalt(10);
const passwordHash = await bcrypt.hash(password, salt);
// 创建用户
await db.pool.execute(
'INSERT INTO users (id, name, password, role, class) VALUES (?, ?, ?, ?, ?)',
[id, name, passwordHash, role, userClass || null]
);
// 如果是学生,创建学生记录
if (role === 'student') {
await db.pool.execute(
'INSERT INTO students (id, name, class) VALUES (?, ?, ?)',
[id, name, userClass]
);
}
res.json({
success: true,
message: '注册成功'
});
} catch (error) {
console.error('注册错误:', error);
res.status(500).json({
success: false,
message: '服务器错误'
});
}
});
// 注销
router.post('/logout', (req, res) => {
req.session.destroy(err => {
if (err) {
return res.status(500).json({
success: false,
message: '注销失败'
});
}
res.clearCookie('session_cookie');
res.json({
success: true,
message: '注销成功'
});
});
});
// 获取当前用户信息
router.get('/me', (req, res) => {
if (!req.session.user) {
return res.status(401).json({
success: false,
message: '未登录'
});
}
res.json({
success: true,
user: req.session.user
});
});
module.exports = router;

143
backend/routes/student.js Normal file
View File

@@ -0,0 +1,143 @@
const express = require('express');
const router = express.Router();
const db = require('../config/database');
const { requireAuth, requireRole } = require('../middleware/auth');
// 获取学生成绩
router.get('/grades', requireAuth, requireRole(['student']), async (req, res) => {
try {
const userId = req.session.user.id;
// 获取学生信息
const students = await db.pool.execute(
'SELECT id FROM students WHERE user_id = ?',
[userId]
);
if (students[0].length === 0) {
return res.status(404).json({
success: false,
message: '学生信息不存在'
});
}
const studentId = students[0][0].id;
// 获取成绩信息
const grades = await db.pool.execute(`
SELECT s.*, c.course_code, c.course_name, c.credit,
u.name as teacher_name
FROM scores s
JOIN courses c ON s.course_id = c.id
JOIN users u ON s.teacher_id = u.id
WHERE s.student_id = ?
ORDER BY s.exam_date DESC
`, [studentId]);
// 计算统计信息
let totalCredits = 0;
let totalGradePoints = 0;
let totalCourses = grades.length;
grades.forEach(grade => {
totalCredits += parseFloat(grade.credit);
if (grade.grade_point) {
totalGradePoints += parseFloat(grade.grade_point) * parseFloat(grade.credit);
}
});
const gpa = totalCredits > 0 ? (totalGradePoints / totalCredits).toFixed(2) : 0;
res.json({
success: true,
grades,
statistics: {
totalCourses,
totalCredits,
gpa,
averageScore: totalCourses > 0 ?
(grades.reduce((sum, g) => sum + parseFloat(g.score || 0), 0) / totalCourses).toFixed(1) : 0
}
});
} catch (error) {
console.error('获取成绩错误:', error);
res.status(500).json({
success: false,
message: '服务器错误'
});
}
});
// 获取成绩详情
router.get('/grades/:id', requireAuth, requireRole(['student']), async (req, res) => {
try {
const gradeId = req.params.id;
const userId = req.session.user.id;
const grades = await db.pool.execute(`
SELECT s.*, c.course_code, c.course_name, c.credit, c.semester,
u.full_name as teacher_name, u.email as teacher_email,
st.student_id as student_number, st.class_name, st.major
FROM scores 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
WHERE s.id = ? AND st.user_id = ?
`, [gradeId, userId]);
if (grades[0].length === 0) {
return res.status(404).json({
success: false,
message: '成绩不存在'
});
}
res.json({
success: true,
grade: grades[0]
});
} catch (error) {
console.error('获取成绩详情错误:', error);
res.status(500).json({
success: false,
message: '服务器错误'
});
}
});
// 获取学生个人信息
router.get('/profile', requireAuth, requireRole(['student']), async (req, res) => {
try {
const userId = req.session.user.id;
const students = await db.pool.execute(`
SELECT s.*, u.username, u.email, u.created_at as account_created
FROM students s
JOIN users u ON s.user_id = u.id
WHERE u.id = ?
`, [userId]);
if (students[0].length === 0) {
return res.status(404).json({
success: false,
message: '学生信息不存在'
});
}
res.json({
success: true,
profile: students[0]
});
} catch (error) {
console.error('获取个人信息错误:', error);
res.status(500).json({
success: false,
message: '服务器错误'
});
}
});
module.exports = router;

243
backend/routes/teacher.js Normal file
View File

@@ -0,0 +1,243 @@
const express = require('express');
const router = express.Router();
const db = require('../config/database');
const { requireAuth, requireRole } = require('../middleware/auth');
// 获取教师教授的课程
router.get('/courses', requireAuth, requireRole(['teacher']), async (req, res) => {
try {
const teacherId = req.session.user.id;
const courses = await db.pool.execute(
'SELECT * FROM courses WHERE teacher_id = ? ORDER BY course_code',
[teacherId]
);
res.json({
success: true,
courses
});
} catch (error) {
console.error('获取课程错误:', error);
res.status(500).json({
success: false,
message: '服务器错误'
});
}
});
// 录入成绩
router.post('/grades', requireAuth, requireRole(['teacher']), async (req, res) => {
try {
const teacherId = req.session.user.id;
const { studentId, courseId, score, examDate, remark } = req.body;
// 验证输入
if (!studentId || !courseId || score === undefined) {
return res.status(400).json({
success: false,
message: '请填写必填字段'
});
}
// 检查学生和课程是否存在
const students = await db.pool.execute(
'SELECT id FROM students WHERE student_id = ?',
[studentId]
);
if (students[0].length === 0) {
return res.status(404).json({
success: false,
message: '学生不存在'
});
}
// 计算绩点和等级
const numericScore = parseFloat(score);
let gradePoint = 0;
let gradeLevel = 'F';
if (numericScore >= 90) {
gradePoint = 4.0; gradeLevel = 'A';
} else if (numericScore >= 80) {
gradePoint = 3.0; gradeLevel = 'B';
} else if (numericScore >= 70) {
gradePoint = 2.0; gradeLevel = 'C';
} else if (numericScore >= 60) {
gradePoint = 1.0; gradeLevel = 'D';
}
// 插入成绩
const result = await db.pool.execute(
`INSERT INTO scores (student_id, course_id, teacher_id, score,
grade_point, grade_level, exam_date, remark)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
[students[0][0].id, courseId, teacherId, numericScore,
gradePoint, gradeLevel, examDate, remark]
);
res.json({
success: true,
message: '成绩录入成功',
gradeId: result.insertId
});
} catch (error) {
console.error('录入成绩错误:', error);
// 处理唯一约束错误
if (error.code === 'ER_DUP_ENTRY') {
return res.status(400).json({
success: false,
message: '该学生此课程成绩已存在'
});
}
res.status(500).json({
success: false,
message: '服务器错误'
});
}
});
// 查询成绩(按班级、课程)
router.get('/grades', requireAuth, requireRole(['teacher']), async (req, res) => {
try {
const { class_name, course_id } = req.query;
const teacherId = req.session.user.id;
let query = `
SELECT sc.*, st.student_id, st.full_name, st.class_name,
c.course_code, c.course_name, c.credit
FROM scores sc
JOIN students st ON sc.student_id = st.id
JOIN courses c ON sc.course_id = c.id
WHERE sc.teacher_id = ?
`;
const params = [teacherId];
if (class_name) {
query += ' AND st.class_name = ?';
params.push(class_name);
}
if (course_id) {
query += ' AND sc.course_id = ?';
params.push(course_id);
}
query += ' ORDER BY st.class_name, st.student_id, sc.exam_date DESC';
const grades = await db.pool.execute(query, params);
res.json({
success: true,
grades
});
} catch (error) {
console.error('查询成绩错误:', error);
res.status(500).json({
success: false,
message: '服务器错误'
});
}
});
// 更新成绩
router.put('/grades/:id', requireAuth, requireRole(['teacher']), async (req, res) => {
try {
const gradeId = req.params.id;
const teacherId = req.session.user.id;
const { score, examDate, remark } = req.body;
// 验证成绩是否存在且属于该教师
const existingGrades = await db.pool.execute(
'SELECT id FROM scores WHERE id = ? AND teacher_id = ?',
[gradeId, teacherId]
);
if (existingGrades[0].length === 0) {
return res.status(404).json({
success: false,
message: '成绩不存在或无权修改'
});
}
// 计算新的绩点和等级
const numericScore = parseFloat(score);
let gradePoint = 0;
let gradeLevel = 'F';
if (numericScore >= 90) {
gradePoint = 4.0; gradeLevel = 'A';
} else if (numericScore >= 80) {
gradePoint = 3.0; gradeLevel = 'B';
} else if (numericScore >= 70) {
gradePoint = 2.0; gradeLevel = 'C';
} else if (numericScore >= 60) {
gradePoint = 1.0; gradeLevel = 'D';
}
// 更新成绩
await db.pool.execute(
`UPDATE scores SET score = ?, grade_point = ?, grade_level = ?,
exam_date = ?, remark = ?, updated_at = CURRENT_TIMESTAMP
WHERE id = ?`,
[numericScore, gradePoint, gradeLevel, examDate, remark, gradeId]
);
res.json({
success: true,
message: '成绩更新成功'
});
} catch (error) {
console.error('更新成绩错误:', error);
res.status(500).json({
success: false,
message: '服务器错误'
});
}
});
// 删除成绩
router.delete('/grades/:id', requireAuth, requireRole(['teacher']), async (req, res) => {
try {
const gradeId = req.params.id;
const teacherId = req.session.user.id;
// 验证成绩是否存在且属于该教师
const existingGrades = await db.pool.execute(
'SELECT id FROM scores WHERE id = ? AND teacher_id = ?',
[gradeId, teacherId]
);
if (existingGrades[0].length === 0) {
return res.status(404).json({
success: false,
message: '成绩不存在或无权删除'
});
}
// 删除成绩
await db.pool.execute('DELETE FROM scores WHERE id = ?', [gradeId]);
res.json({
success: true,
message: '成绩删除成功'
});
} catch (error) {
console.error('删除成绩错误:', error);
res.status(500).json({
success: false,
message: '服务器错误'
});
}
});
module.exports = router;