const db = require('../config/database'); const bcrypt = require('bcryptjs'); const User = require('../models/User'); const Student = require('../models/Student'); const Teacher = require('../models/Teacher'); const SystemSetting = require('../models/SystemSetting'); const OperationLog = require('../models/OperationLog'); class AdminService { static async getUsers(params) { const page = parseInt(params.page) || 1; const limit = parseInt(params.limit) || 10; const search = params.search || ''; const role = params.role || ''; 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 ?'; const dataQueryParams = [...queryParams, limit, offset]; const users = await db.query(queryStr, dataQueryParams); return { data: users, pagination: { page: parseInt(page), limit: parseInt(limit), total, pages: Math.ceil(total / limit) } }; } static async getStats() { const usersCount = await db.query('SELECT COUNT(*) as count FROM users'); const studentsCount = await db.query('SELECT COUNT(*) as count FROM students'); const teachersCount = await db.query('SELECT COUNT(*) as count FROM users WHERE role = "teacher"'); const coursesCount = await db.query('SELECT COUNT(*) as count FROM courses'); return { users: usersCount[0].count, students: studentsCount[0].count, teachers: teachersCount[0].count, courses: coursesCount[0].count }; } static async createUser(userData, operator) { const { id, role, name } = userData; // 检查 ID const existingUser = await User.findById(id); if (existingUser) { throw new Error('用户ID已存在'); } // 创建用户 const userId = await User.create(userData); // 如果是学生,同时创建学生记录 if (role === 'student') { await Student.create(userData); } else if (role === 'teacher') { await Teacher.create({ id, name, department: userData.class || '' }); } if (operator) { await OperationLog.add({ user_id: operator.user_id, type: '用户管理', target: `user:${id}`, description: `创建了${role === 'student' ? '学生' : (role === 'teacher' ? '教师' : '管理员')}用户: ${name}(${id})`, ip: operator.ip }); } return userId; } static async updateUser(id, userData, operator) { // 如果修改密码 if (userData.password) { const salt = await bcrypt.genSalt(10); userData.password = await bcrypt.hash(userData.password, salt); } else { delete userData.password; } const fields = []; const values = []; if (userData.name) { fields.push('name = ?'); values.push(userData.name); } if (userData.role) { fields.push('role = ?'); values.push(userData.role); } if (userData.class !== undefined) { fields.push('class = ?'); values.push(userData.class); } // class can be empty string if (userData.password) { fields.push('password = ?'); values.push(userData.password); } if (fields.length === 0) return true; values.push(id); const sql = `UPDATE users SET ${fields.join(', ')} WHERE id = ?`; const result = await db.query(sql, values); if (operator) { await OperationLog.add({ user_id: operator.user_id, type: '用户管理', target: `user:${id}`, description: `修改了用户信息: ${id}`, ip: operator.ip }); } return result.affectedRows > 0; } static async deleteUser(id, operator) { // 删除用户 await Student.delete(id); await Teacher.delete(id); const result = await db.query('DELETE FROM users WHERE id = ?', [id]); if (operator) { await OperationLog.add({ user_id: operator.user_id, type: '用户管理', target: `user:${id}`, description: `删除了用户: ${id}`, ip: operator.ip }); } return result.affectedRows > 0; } // ================= Student Management ================= static async getStudents(params) { const page = parseInt(params.page) || 1; const limit = parseInt(params.limit) || 10; const search = params.search || ''; const offset = (page - 1) * limit; let queryStr = 'SELECT * FROM students 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); } // Count const countSql = queryStr.replace('SELECT *', 'SELECT COUNT(*) as total'); const countRows = await db.query(countSql, queryParams); const total = countRows[0].total; // Data queryStr += ' ORDER BY id ASC LIMIT ? OFFSET ?'; const dataQueryParams = [...queryParams, limit, offset]; const students = await db.query(queryStr, dataQueryParams); return { data: students, pagination: { page: parseInt(page), limit: parseInt(limit), total, pages: Math.ceil(total / limit) } }; } static async createStudent(studentData, operator) { const { id, name, class: className } = studentData; // Check ID const existing = await User.findById(id); if (existing) throw new Error('学号已存在'); // Create User first await User.create({ id, name, password: id, // Default password role: 'student', class: className }); // Create Student await Student.create(studentData); if (operator) { await OperationLog.add({ user_id: operator.user_id, type: '学生管理', target: `student:${id}`, description: `创建了学生: ${name}(${id})`, ip: operator.ip }); } return id; } static async updateStudent(id, data, operator) { // Update students table await Student.update(id, data); // Sync User table (name, class) const userFields = []; const userValues = []; if (data.name) { userFields.push('name = ?'); userValues.push(data.name); } if (data.class) { userFields.push('class = ?'); userValues.push(data.class); } if (userFields.length > 0) { userValues.push(id); await db.query(`UPDATE users SET ${userFields.join(', ')} WHERE id = ?`, userValues); } if (operator) { await OperationLog.add({ user_id: operator.user_id, type: '学生管理', target: `student:${id}`, description: `修改了学生信息: ${id}`, ip: operator.ip }); } return true; } static async deleteStudent(id, operator) { await Student.delete(id); await db.query('DELETE FROM users WHERE id = ?', [id]); if (operator) { await OperationLog.add({ user_id: operator.user_id, type: '学生管理', target: `student:${id}`, description: `删除了学生: ${id}`, ip: operator.ip }); } return true; } // ================= Teacher Management ================= static async getTeachers(params) { const page = parseInt(params.page) || 1; const limit = parseInt(params.limit) || 10; const search = params.search || ''; const offset = (page - 1) * limit; let queryStr = 'SELECT t.*, u.created_at FROM teachers t JOIN users u ON t.id = u.id WHERE 1=1'; let queryParams = []; if (search) { queryStr += ' AND (t.id LIKE ? OR t.name LIKE ? OR t.department LIKE ?)'; const searchTerm = `%${search}%`; queryParams.push(searchTerm, searchTerm, searchTerm); } // Count const countSql = `SELECT COUNT(*) as total FROM teachers t WHERE 1=1 ${search ? 'AND (t.id LIKE ? OR t.name LIKE ? OR t.department LIKE ?)' : ''}`; const countRows = await db.query(countSql, queryParams); const total = countRows[0].total; // Data queryStr += ' ORDER BY t.id ASC LIMIT ? OFFSET ?'; const dataQueryParams = [...queryParams, limit, offset]; const teachers = await db.query(queryStr, dataQueryParams); return { data: teachers, pagination: { page: parseInt(page), limit: parseInt(limit), total, pages: Math.ceil(total / limit) } }; } static async createTeacher(teacherData, operator) { const { id, name, department } = teacherData; // Check ID const existing = await User.findById(id); if (existing) throw new Error('工号已存在'); // Create User await User.create({ id, name, password: id, // Default password role: 'teacher', class: department // Use class field for department in user table }); // Create Teacher await Teacher.create(teacherData); if (operator) { await OperationLog.add({ user_id: operator.user_id, type: '教师管理', target: `teacher:${id}`, description: `创建了教师: ${name}(${id})`, ip: operator.ip }); } return id; } static async updateTeacher(id, data, operator) { await Teacher.update(id, data); // Sync User table (name, department) const userFields = []; const userValues = []; if (data.name) { userFields.push('name = ?'); userValues.push(data.name); } if (data.department) { userFields.push('class = ?'); userValues.push(data.department); } if (userFields.length > 0) { userValues.push(id); await db.query(`UPDATE users SET ${userFields.join(', ')} WHERE id = ?`, userValues); } if (operator) { await OperationLog.add({ user_id: operator.user_id, type: '教师管理', target: `teacher:${id}`, description: `修改了教师信息: ${id}`, ip: operator.ip }); } return true; } static async deleteTeacher(id, operator) { await Teacher.delete(id); await db.query('DELETE FROM users WHERE id = ?', [id]); if (operator) { await OperationLog.add({ user_id: operator.user_id, type: '教师管理', target: `teacher:${id}`, description: `删除了教师: ${id}`, ip: operator.ip }); } return true; } // ================= Grade Statistics ================= static async getGradeStats() { const sql = ` SELECT c.course_code, c.course_name, u.name as teacher_name, COUNT(g.student_id) as student_count, AVG(g.total_score) as avg_score, MAX(g.total_score) as max_score, MIN(g.total_score) as min_score, SUM(CASE WHEN g.total_score >= 60 THEN 1 ELSE 0 END) as pass_count FROM courses c LEFT JOIN grades g ON c.id = g.course_id LEFT JOIN users u ON c.teacher_id = u.id GROUP BY c.id ORDER BY avg_score DESC `; const rows = await db.query(sql); return rows.map(row => ({ ...row, pass_rate: row.student_count > 0 ? ((row.pass_count / row.student_count) * 100).toFixed(1) : 0, avg_score: row.avg_score ? Number(row.avg_score).toFixed(1) : 0 })); } // ================= System Settings ================= static async getSettings() { return await SystemSetting.getAll(); } static async saveSettings(settings, operator) { await SystemSetting.setMany(settings); await OperationLog.add({ user_id: operator.user_id, type: '系统设置', target: 'system_settings', description: '修改了系统基础设置', ip: operator.ip }); return true; } // ================= Data Maintenance ================= static async backupDatabase(operator) { const path = require('path'); const fs = require('fs'); const source = path.resolve(__dirname, '../database.sqlite'); const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const backupDir = path.resolve(__dirname, '../backups'); const target = path.resolve(backupDir, `database-${timestamp}.sqlite`); if (!fs.existsSync(backupDir)) { fs.mkdirSync(backupDir); } fs.copyFileSync(source, target); await OperationLog.add({ user_id: operator.user_id, type: '数据维护', target: 'database', description: `手动备份数据库: ${path.basename(target)}`, ip: operator.ip }); return { filename: path.basename(target) }; } static async clearCache(operator) { await OperationLog.add({ user_id: operator.user_id, type: '数据维护', target: 'cache', description: '清理系统缓存', ip: operator.ip }); return true; } static async resetStudentPasswords(operator) { const hashedPassword = await bcrypt.hash('123456', 10); await db.query('UPDATE users SET password = ? WHERE role = "student"', [hashedPassword]); await OperationLog.add({ user_id: operator.user_id, type: '数据维护', target: 'users', description: '重置了所有学生的密码为 123456', ip: operator.ip }); return true; } // ================= Operation Logs ================= static async getLogs(params) { return await OperationLog.findAll(params); } // ================= Data Export ================= static async exportStudents() { return await db.query('SELECT * FROM students'); } static async exportTeachers() { return await db.query('SELECT t.*, u.created_at FROM teachers t JOIN users u ON t.id = u.id'); } static async exportGrades() { const sql = ` SELECT g.student_id, s.name as student_name, c.course_code, c.course_name, g.total_score, g.grade_point, g.grade_level, u.name as teacher_name FROM grades g JOIN students s ON g.student_id = s.id JOIN courses c ON g.course_id = c.id JOIN users u ON g.teacher_id = u.id `; return await db.query(sql); } } module.exports = AdminService;