feat: 实现成绩管理系统核心功能

添加响应工具、错误处理中间件和数据库模型
创建用户、学生、课程和成绩相关服务
实现管理员、教师和学生控制器的基本功能
重构路由处理并优化数据库查询
This commit is contained in:
祀梦
2025-12-21 22:10:48 +08:00
parent bcf2c71fad
commit b9a975004b
20 changed files with 659 additions and 937 deletions

View File

@@ -0,0 +1,60 @@
const db = require('../config/database');
const bcrypt = require('bcryptjs');
const User = require('../models/User');
class AdminService {
static async getUsers(params) {
const { page = 1, limit = 10, search = '', role = '' } = params;
const offset = (page - 1) * limit;
let queryStr = 'SELECT id, name, role, class, created_at FROM users WHERE 1=1';
let queryParams = [];
if (search) {
queryStr += ' AND (id LIKE ? OR name LIKE ? OR class LIKE ?)';
const searchTerm = `%${search}%`;
queryParams.push(searchTerm, searchTerm, searchTerm);
}
if (role) {
queryStr += ' AND role = ?';
queryParams.push(role);
}
// Count
const countSql = queryStr.replace('SELECT id, name, role, class, created_at', 'SELECT COUNT(*) as total');
const countRows = await db.query(countSql, queryParams);
const total = countRows[0].total;
// Data
queryStr += ' ORDER BY created_at DESC LIMIT ? OFFSET ?';
queryParams.push(parseInt(limit), parseInt(offset));
const users = await db.query(queryStr, queryParams);
return {
data: users,
pagination: {
page: parseInt(page),
limit: parseInt(limit),
total,
pages: Math.ceil(total / limit)
}
};
}
static async createUser(userData) {
const { id } = userData;
// 检查 ID
const existingUser = await User.findById(id);
if (existingUser) {
throw new Error('用户ID已存在');
}
// 创建用户
return await User.create(userData);
}
}
module.exports = AdminService;

View File

@@ -0,0 +1,61 @@
const User = require('../models/User');
const Student = require('../models/Student');
class AuthService {
static async login(id, password, role) {
const user = await User.findByIdAndRole(id, role);
if (!user) {
throw new Error('用户名或密码错误');
}
const isValid = await User.verifyPassword(password, user.password);
if (!isValid) {
throw new Error('用户名或密码错误');
}
const sessionUser = {
id: user.id,
name: user.name,
role: user.role,
class: user.class
};
if (user.role === 'student') {
const studentInfo = await Student.findById(user.id); // 这里的 id 既是 users.id 也是 students.id
if (studentInfo) {
sessionUser.studentInfo = studentInfo;
}
}
return sessionUser;
}
static async register(userData) {
const { id, role, class: userClass } = userData;
// 检查是否存在
const existingUser = await User.findById(id);
if (existingUser) {
throw new Error('用户ID已存在');
}
// 创建用户
const newUser = await User.create(userData);
// 如果是学生,需要同步创建 students 表记录
// 注意:目前的逻辑好像混淆了 users.id 和 students.id根据之前的 SQLstudents.id 是主键且外键关联 users.id
// 我们假设 users.id 就是学号
if (role === 'student') {
// 需要确保 students 表结构匹配
await db.query(
'INSERT INTO students (id, name, class) VALUES (?, ?, ?)',
[id, userData.name, userClass]
);
}
return newUser;
}
}
const db = require('../config/database'); // 补充引用
module.exports = AuthService;

View File

@@ -0,0 +1,55 @@
const Score = require('../models/Score');
const Student = require('../models/Student');
class StudentService {
static async getStudentGrades(userId) {
// 先通过 userId 获取 studentId
// 假设 users.id = students.id或者通过 user_id 关联
// 根据之前的 authService我们假设 users.id 就是 students.id
// 确认学生是否存在
const student = await Student.findById(userId);
if (!student) {
throw new Error('学生信息不存在');
}
const grades = await Score.findByStudentId(userId);
// 计算统计信息
let totalCredits = 0;
let totalGradePoints = 0;
const totalCourses = grades.length;
grades.forEach(grade => {
const credit = parseFloat(grade.credit);
totalCredits += credit;
if (grade.grade_point) {
totalGradePoints += parseFloat(grade.grade_point) * credit;
}
});
const gpa = totalCredits > 0 ? (totalGradePoints / totalCredits).toFixed(2) : 0;
const averageScore = totalCourses > 0 ?
(grades.reduce((sum, g) => sum + parseFloat(g.score || 0), 0) / totalCourses).toFixed(1) : 0;
return {
grades,
statistics: {
totalCourses,
totalCredits,
gpa,
averageScore
}
};
}
static async getGradeDetails(scoreId, userId) {
const grade = await Score.findDetailsById(scoreId, userId);
if (!grade) {
throw new Error('成绩不存在');
}
return grade;
}
}
module.exports = StudentService;

View File

@@ -0,0 +1,50 @@
const Course = require('../models/Course');
const Score = require('../models/Score');
const Student = require('../models/Student');
class TeacherService {
static async getCourses(teacherId) {
return await Course.findByTeacherId(teacherId);
}
static async addScore(teacherId, scoreData) {
const { studentId, courseId, score } = scoreData;
// 验证学生
const student = await Student.findById(studentId);
if (!student) {
throw new Error('学生不存在');
}
// 验证课程(可选:验证是否是该教师的课程)
// const course = await Course.findById(courseId);
// 检查重复
const existingScore = await Score.findByStudentAndCourse(studentId, courseId);
if (existingScore) {
throw new Error('该学生此课程成绩已存在');
}
// 计算绩点和等级
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 fullScoreData = {
...scoreData,
teacherId,
gradePoint,
gradeLevel
};
const gradeId = await Score.create(fullScoreData);
return gradeId;
}
}
module.exports = TeacherService;