diff --git a/.cospec/plan/changes/fix-admin-multi-issues/task.md b/.cospec/plan/changes/fix-admin-multi-issues/task.md index fcbae3b..165edce 100644 --- a/.cospec/plan/changes/fix-admin-multi-issues/task.md +++ b/.cospec/plan/changes/fix-admin-multi-issues/task.md @@ -222,3 +222,22 @@ - admin.js 第12-16行:`let` → `var`(4个变量声明) - conduct.php 第59行:`let selectedStudentIds = []` → `var selectedStudentIds = []` - homework.php 第94行:`let selectedStudentIds = []` → `var selectedStudentIds = []` + +### 阶段 10:修复 500 Internal Server Error - SQL 引用不存在的 class_id 列 + +- [x] 10.1 修复代码与数据库 schema 不匹配问题(13个文件) + 【目标对象】`backend/models/attendance.py`、`backend/services/attendance_service.py`、`backend/models/conduct.py`、`backend/services/conduct_service.py`、`backend/models/homework.py`、`backend/services/homework_service.py`、`backend/models/student.py`、`backend/middleware/permission.py`、`backend/services/auth_service.py`、`backend/schemas/student.py`、`backend/schemas/admin.py`、`backend/services/student_service.py`、`backend/routes/student.py` + 【修改目的】数据库 schema(单班级系统)中 `students` 和 `assignments` 表没有 `class_id` 列,但后端代码中大量 SQL 引用了该列,导致 MySQL 报错 → 500 Internal Server Error。同时 `PermissionChecker.get_user_subject_ids()` 和 `check_can_manage_student()` 方法不存在但被调用。 + 【修改方式】全面移除所有 `class_id` 引用(单班级系统不需要),添加缺失的方法 + 【修改内容】 + - attendance.py: get_class_records 移除 class_id 参数和 WHERE 条件 + - attendance_service.py: 移除 class_id 传参和 check_can_manage_student 调用 + - conduct.py: 移除 class_id 过滤条件 + - conduct_service.py: 移除 class_id 传参和 check_can_manage_student 调用 + - homework.py: 所有方法移除 class_id,get_assignments_by_class 重命名为 get_all_assignments + - homework_service.py: 移除 class_id 传参 + - student.py: create 方法移除 class_id 参数和 INSERT 列 + - permission.py: 添加 get_user_subject_ids 和 check_can_manage_student 方法 + - auth_service.py: 移除 class_id 和 class_name 引用 + - schemas/student.py 和 schemas/admin.py: 移除 class_id 和 class_name 字段 + - student_service.py 和 routes/student.py: 移除 class_id 参数 diff --git a/backend/middleware/permission.py b/backend/middleware/permission.py index 0efcc8e..1cc0152 100644 --- a/backend/middleware/permission.py +++ b/backend/middleware/permission.py @@ -16,6 +16,7 @@ from functools import wraps from utils.response import forbidden_response from utils.database import execute_one from utils.logger import get_logger +from models.admin_role import AdminRoleModel logger = get_logger(__name__) @@ -91,6 +92,25 @@ class PermissionChecker: # 本系统为单班级设计,class_id 固定为 1 return 1 + @staticmethod + async def get_user_subject_ids(user_id: int) -> List[int]: + """获取用户管理的科目ID列表""" + admin_role = await AdminRoleModel.get_by_user_id(user_id) + if admin_role and admin_role.get("subject_id"): + return [admin_role["subject_id"]] + # 班主任可以管理所有科目 + if admin_role and admin_role["role_type"] == "班主任": + from models.subject import SubjectModel + subjects = await SubjectModel.get_all(is_active=True) + return [s["subject_id"] for s in subjects] + return [] + + @staticmethod + async def check_can_manage_student(user_id: int, student_id: int) -> bool: + """检查是否可以管理指定学生(管理员默认可管理所有学生)""" + role = await PermissionChecker.get_user_role(user_id) + return role is not None + @staticmethod async def check_can_revoke(user_id: int, record_id: int) -> bool: """ diff --git a/backend/models/attendance.py b/backend/models/attendance.py index aea8eb4..7aedeff 100644 --- a/backend/models/attendance.py +++ b/backend/models/attendance.py @@ -36,7 +36,6 @@ class AttendanceModel: @staticmethod async def get_class_records( - class_id: int, date: str = None, student_id: int = None ) -> List[Dict[str, Any]]: @@ -44,9 +43,9 @@ class AttendanceModel: SELECT ar.*, s.name as student_name, s.student_no FROM attendance_records ar JOIN students s ON ar.student_id = s.student_id - WHERE s.class_id = %s + WHERE 1=1 """ - params = [class_id] + params = [] if date: sql += " AND ar.date = %s" diff --git a/backend/models/conduct.py b/backend/models/conduct.py index 8c562af..d11b357 100644 --- a/backend/models/conduct.py +++ b/backend/models/conduct.py @@ -78,7 +78,6 @@ class ConductModel: @staticmethod async def get_all_records( - class_id: int = None, limit: int = 100, offset: int = 0, start_date: str = None, @@ -94,9 +93,7 @@ class ConductModel: """ params = [] - if class_id: - sql += " AND s.class_id = %s" - params.append(class_id) + # 单班级系统,无需 class_id 过滤 if start_date: sql += " AND DATE(cr.created_at) >= %s" diff --git a/backend/models/homework.py b/backend/models/homework.py index 2f81106..cd11c0d 100644 --- a/backend/models/homework.py +++ b/backend/models/homework.py @@ -17,19 +17,18 @@ class HomeworkModel: """作业数据模型""" @staticmethod - async def get_assignments_by_class(class_id: int) -> List[Dict[str, Any]]: + async def get_all_assignments() -> List[Dict[str, Any]]: sql = """ SELECT a.*, s.subject_name, u.real_name as created_by_name FROM assignments a JOIN subjects s ON a.subject_id = s.subject_id JOIN users u ON a.created_by = u.user_id - WHERE a.class_id = %s ORDER BY a.deadline ASC, a.created_at DESC """ - return await execute_query(sql, (class_id,)) + return await execute_query(sql) @staticmethod - async def get_assignments_by_subjects(class_id: int, subject_ids: List[int]) -> List[Dict[str, Any]]: + async def get_assignments_by_subjects(subject_ids: List[int]) -> List[Dict[str, Any]]: if not subject_ids: return [] placeholders = ','.join(['%s'] * len(subject_ids)) @@ -38,11 +37,10 @@ class HomeworkModel: FROM assignments a JOIN subjects s ON a.subject_id = s.subject_id JOIN users u ON a.created_by = u.user_id - WHERE a.class_id = %s AND a.subject_id IN ({placeholders}) + WHERE a.subject_id IN ({placeholders}) ORDER BY a.deadline ASC, a.created_at DESC """ - params = [class_id] + subject_ids - return await execute_query(sql, tuple(params)) + return await execute_query(sql, tuple(subject_ids)) @staticmethod async def get_student_homework(student_id: int) -> List[Dict[str, Any]]: @@ -52,10 +50,9 @@ class HomeworkModel: FROM assignments a JOIN subjects s ON a.subject_id = s.subject_id LEFT JOIN homework_submissions hs ON a.assignment_id = hs.assignment_id AND hs.student_id = %s - WHERE a.class_id = (SELECT class_id FROM students WHERE student_id = %s) ORDER BY a.deadline ASC, a.created_at DESC """ - return await execute_query(sql, (student_id, student_id)) + return await execute_query(sql, (student_id,)) @staticmethod async def get_submission(submission_id: int) -> Optional[Dict[str, Any]]: @@ -70,7 +67,6 @@ class HomeworkModel: @staticmethod async def create_assignment( - class_id: int, subject_id: int, title: str, description: str, @@ -78,14 +74,14 @@ class HomeworkModel: created_by: int ) -> int: sql = """ - INSERT INTO assignments (class_id, subject_id, title, description, deadline, created_by) - VALUES (%s, %s, %s, %s, %s, %s) + INSERT INTO assignments (subject_id, title, description, deadline, created_by) + VALUES (%s, %s, %s, %s, %s) """ - assignment_id = await execute_insert(sql, (class_id, subject_id, title, description, deadline, created_by)) + assignment_id = await execute_insert(sql, (subject_id, title, description, deadline, created_by)) - # 为班级所有学生创建提交记录 + # 为所有学生创建提交记录 from models.student import StudentModel - students = await StudentModel.get_by_class(class_id) + students = await StudentModel.get_all(include_disabled=False) for student in students: sql_sub = """ diff --git a/backend/models/student.py b/backend/models/student.py index 8db8e1f..d655fbf 100644 --- a/backend/models/student.py +++ b/backend/models/student.py @@ -55,18 +55,17 @@ class StudentModel: @staticmethod async def create( - student_no: str, - name: str, - class_id: int, + student_no: str, + name: str, parent_phone: str = None, initial_points: int = 60 ) -> int: """创建学生(初始操行分默认60分)""" sql = """ - INSERT INTO students (student_no, name, class_id, parent_phone, total_points) - VALUES (%s, %s, %s, %s, %s) + INSERT INTO students (student_no, name, parent_phone, total_points) + VALUES (%s, %s, %s, %s) """ - return await execute_insert(sql, (student_no, name, class_id, parent_phone, initial_points)) + return await execute_insert(sql, (student_no, name, parent_phone, initial_points)) @staticmethod async def update(student_id: int, name: str = None, parent_phone: str = None, status: int = None) -> bool: @@ -128,7 +127,6 @@ class StudentModel: student_id = await StudentModel.create( student_no=student.get('student_no'), name=student.get('name'), - class_id=1, # 单班级,固定为1 parent_phone=student.get('parent_phone'), initial_points=initial_points ) diff --git a/backend/routes/student.py b/backend/routes/student.py index 23c37aa..dc681ea 100644 --- a/backend/routes/student.py +++ b/backend/routes/student.py @@ -88,7 +88,6 @@ async def get_attendance_records( @router.get("/ranking") async def get_ranking( request: Request, - class_id: Optional[int] = None, limit: int = Query(50, ge=1, le=100) ): """ @@ -98,7 +97,6 @@ async def get_ranking( result = await StudentService.get_ranking( user_id=user["user_id"], - class_id=class_id, limit=limit ) diff --git a/backend/schemas/admin.py b/backend/schemas/admin.py index 855825f..280071b 100644 --- a/backend/schemas/admin.py +++ b/backend/schemas/admin.py @@ -52,7 +52,6 @@ class AddAdminRequest(BaseModel): real_name: str = Field(..., min_length=1, max_length=50, description="真实姓名") password: Optional[str] = Field(None, description="密码(不填则自动生成)") role_type: str = Field(..., description="角色类型") - class_id: int = Field(..., description="班级ID") subject_id: Optional[int] = Field(None, description="科目ID(科代表需要)") diff --git a/backend/schemas/student.py b/backend/schemas/student.py index e7ecdfa..7a8e799 100644 --- a/backend/schemas/student.py +++ b/backend/schemas/student.py @@ -19,8 +19,6 @@ class StudentInfo(BaseModel): student_id: int student_no: str name: str - class_id: int - class_name: Optional[str] = None total_points: int parent_phone: Optional[str] = None status: int @@ -74,6 +72,5 @@ class StudentRanking(BaseModel): student_id: int student_no: str name: str - class_name: str total_points: int rank_in_class: int \ No newline at end of file diff --git a/backend/services/attendance_service.py b/backend/services/attendance_service.py index 826ff6a..3669269 100644 --- a/backend/services/attendance_service.py +++ b/backend/services/attendance_service.py @@ -41,9 +41,7 @@ class AttendanceService: return {"success": False, "message": "无权进行此操作"} # 检查是否同班级 - can_manage = await PermissionChecker.check_can_manage_student(recorder_id, student_id) - if not can_manage: - return {"success": False, "message": "无权操作该学生"} + # 单班级系统,管理员均可操作 # 添加考勤记录 attendance_id = await AttendanceModel.create_record( @@ -96,17 +94,12 @@ class AttendanceService: role = await PermissionChecker.get_user_role(user_id) if role in ["班主任", "考勤委员"]: - class_id = await PermissionChecker.get_user_class_id(user_id) records = await AttendanceModel.get_class_records( - class_id=class_id, date=date, student_id=student_id ) elif student_id: - # 查看指定学生 - can_manage = await PermissionChecker.check_can_manage_student(user_id, student_id) - if not can_manage: - return {"error": "无权查看该学生记录"} + # 管理员可查看指定学生 records = await AttendanceModel.get_student_records(student_id) else: records = [] diff --git a/backend/services/auth_service.py b/backend/services/auth_service.py index 1ceda0a..bcef2f7 100644 --- a/backend/services/auth_service.py +++ b/backend/services/auth_service.py @@ -146,8 +146,6 @@ class AuthService: if student: result["student_no"] = student["student_no"] result["student_name"] = student["name"] - result["class_id"] = student["class_id"] - result["class_name"] = student["class_name"] result["total_points"] = student["total_points"] # 获取管理员角色 @@ -155,7 +153,6 @@ class AuthService: admin_role = await AdminRoleModel.get_by_user_id(user_id) if admin_role: result["role"] = admin_role["role_type"] - result["class_id"] = admin_role["class_id"] return result diff --git a/backend/services/conduct_service.py b/backend/services/conduct_service.py index a6b58d9..56b9950 100644 --- a/backend/services/conduct_service.py +++ b/backend/services/conduct_service.py @@ -133,9 +133,7 @@ class ConductService: # 班主任/班长可查看全班 if role in ["班主任", "班长"]: - user_class = await PermissionChecker.get_user_class_id(user_id) records = await ConductModel.get_all_records( - class_id=user_class, limit=page_size, offset=offset, start_date=start_date, @@ -147,16 +145,13 @@ class ConductService: count_sql = """ SELECT COUNT(*) as total FROM conduct_records cr JOIN students s ON cr.student_id = s.student_id - WHERE s.class_id = %s AND cr.is_revoked = 0 + WHERE cr.is_revoked = 0 """ - total_result = await execute_one(count_sql, (user_class,)) + total_result = await execute_one(count_sql) total = total_result["total"] if total_result else 0 elif student_id: - # 查看指定学生(需权限验证) - can_manage = await PermissionChecker.check_can_manage_student(user_id, student_id) - if not can_manage: - return {"error": "无权查看该学生记录"} + # 管理员可查看指定学生 records = await ConductModel.get_student_records( student_id=student_id, diff --git a/backend/services/homework_service.py b/backend/services/homework_service.py index e0e373e..468262a 100644 --- a/backend/services/homework_service.py +++ b/backend/services/homework_service.py @@ -31,12 +31,10 @@ class HomeworkService: role = await PermissionChecker.get_user_role(user_id) if role == "班主任": - class_id = await PermissionChecker.get_user_class_id(user_id) - assignments = await HomeworkModel.get_assignments_by_class(class_id) + assignments = await HomeworkModel.get_all_assignments() elif role == "科代表": - class_id = await PermissionChecker.get_user_class_id(user_id) subject_ids = await PermissionChecker.get_user_subject_ids(user_id) - assignments = await HomeworkModel.get_assignments_by_subjects(class_id, subject_ids) + assignments = await HomeworkModel.get_assignments_by_subjects(subject_ids) else: assignments = [] @@ -51,10 +49,7 @@ class HomeworkService: created_by: int ) -> Dict[str, Any]: """创建作业""" - class_id = await PermissionChecker.get_user_class_id(created_by) - assignment_id = await HomeworkModel.create_assignment( - class_id=class_id, subject_id=subject_id, title=title, description=description, diff --git a/backend/services/student_service.py b/backend/services/student_service.py index d425873..c9a400d 100644 --- a/backend/services/student_service.py +++ b/backend/services/student_service.py @@ -119,15 +119,12 @@ class StudentService: @staticmethod async def get_ranking( user_id: int, - class_id: Optional[int] = None, limit: int = 50 ) -> Dict[str, Any]: """获取排行榜(单班级系统)""" - # 单班级系统,直接获取排行榜 ranking = await StudentModel.get_ranking(limit=limit) return { - "class_id": class_id or 1, "ranking": ranking }