跨域bug修复2

This commit is contained in:
2026-04-14 15:56:57 +08:00
parent 74053bdbdc
commit fd3535f884
14 changed files with 65 additions and 65 deletions

View File

@@ -222,3 +222,22 @@
- admin.js 第12-16行`let``var`4个变量声明 - admin.js 第12-16行`let``var`4个变量声明
- conduct.php 第59行`let selectedStudentIds = []``var selectedStudentIds = []` - conduct.php 第59行`let selectedStudentIds = []``var selectedStudentIds = []`
- homework.php 第94行`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_idget_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 参数

View File

@@ -16,6 +16,7 @@ from functools import wraps
from utils.response import forbidden_response from utils.response import forbidden_response
from utils.database import execute_one from utils.database import execute_one
from utils.logger import get_logger from utils.logger import get_logger
from models.admin_role import AdminRoleModel
logger = get_logger(__name__) logger = get_logger(__name__)
@@ -91,6 +92,25 @@ class PermissionChecker:
# 本系统为单班级设计class_id 固定为 1 # 本系统为单班级设计class_id 固定为 1
return 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 @staticmethod
async def check_can_revoke(user_id: int, record_id: int) -> bool: async def check_can_revoke(user_id: int, record_id: int) -> bool:
""" """

View File

@@ -36,7 +36,6 @@ class AttendanceModel:
@staticmethod @staticmethod
async def get_class_records( async def get_class_records(
class_id: int,
date: str = None, date: str = None,
student_id: int = None student_id: int = None
) -> List[Dict[str, Any]]: ) -> List[Dict[str, Any]]:
@@ -44,9 +43,9 @@ class AttendanceModel:
SELECT ar.*, s.name as student_name, s.student_no SELECT ar.*, s.name as student_name, s.student_no
FROM attendance_records ar FROM attendance_records ar
JOIN students s ON ar.student_id = s.student_id JOIN students s ON ar.student_id = s.student_id
WHERE s.class_id = %s WHERE 1=1
""" """
params = [class_id] params = []
if date: if date:
sql += " AND ar.date = %s" sql += " AND ar.date = %s"

View File

@@ -78,7 +78,6 @@ class ConductModel:
@staticmethod @staticmethod
async def get_all_records( async def get_all_records(
class_id: int = None,
limit: int = 100, limit: int = 100,
offset: int = 0, offset: int = 0,
start_date: str = None, start_date: str = None,
@@ -94,9 +93,7 @@ class ConductModel:
""" """
params = [] params = []
if class_id: # 单班级系统,无需 class_id 过滤
sql += " AND s.class_id = %s"
params.append(class_id)
if start_date: if start_date:
sql += " AND DATE(cr.created_at) >= %s" sql += " AND DATE(cr.created_at) >= %s"

View File

@@ -17,19 +17,18 @@ class HomeworkModel:
"""作业数据模型""" """作业数据模型"""
@staticmethod @staticmethod
async def get_assignments_by_class(class_id: int) -> List[Dict[str, Any]]: async def get_all_assignments() -> List[Dict[str, Any]]:
sql = """ sql = """
SELECT a.*, s.subject_name, u.real_name as created_by_name SELECT a.*, s.subject_name, u.real_name as created_by_name
FROM assignments a FROM assignments a
JOIN subjects s ON a.subject_id = s.subject_id JOIN subjects s ON a.subject_id = s.subject_id
JOIN users u ON a.created_by = u.user_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 ORDER BY a.deadline ASC, a.created_at DESC
""" """
return await execute_query(sql, (class_id,)) return await execute_query(sql)
@staticmethod @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: if not subject_ids:
return [] return []
placeholders = ','.join(['%s'] * len(subject_ids)) placeholders = ','.join(['%s'] * len(subject_ids))
@@ -38,11 +37,10 @@ class HomeworkModel:
FROM assignments a FROM assignments a
JOIN subjects s ON a.subject_id = s.subject_id JOIN subjects s ON a.subject_id = s.subject_id
JOIN users u ON a.created_by = u.user_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 ORDER BY a.deadline ASC, a.created_at DESC
""" """
params = [class_id] + subject_ids return await execute_query(sql, tuple(subject_ids))
return await execute_query(sql, tuple(params))
@staticmethod @staticmethod
async def get_student_homework(student_id: int) -> List[Dict[str, Any]]: async def get_student_homework(student_id: int) -> List[Dict[str, Any]]:
@@ -52,10 +50,9 @@ class HomeworkModel:
FROM assignments a FROM assignments a
JOIN subjects s ON a.subject_id = s.subject_id 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 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 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 @staticmethod
async def get_submission(submission_id: int) -> Optional[Dict[str, Any]]: async def get_submission(submission_id: int) -> Optional[Dict[str, Any]]:
@@ -70,7 +67,6 @@ class HomeworkModel:
@staticmethod @staticmethod
async def create_assignment( async def create_assignment(
class_id: int,
subject_id: int, subject_id: int,
title: str, title: str,
description: str, description: str,
@@ -78,14 +74,14 @@ class HomeworkModel:
created_by: int created_by: int
) -> int: ) -> int:
sql = """ sql = """
INSERT INTO assignments (class_id, subject_id, title, description, deadline, created_by) INSERT INTO assignments (subject_id, title, description, deadline, created_by)
VALUES (%s, %s, %s, %s, %s, %s) 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 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: for student in students:
sql_sub = """ sql_sub = """

View File

@@ -55,18 +55,17 @@ class StudentModel:
@staticmethod @staticmethod
async def create( async def create(
student_no: str, student_no: str,
name: str, name: str,
class_id: int,
parent_phone: str = None, parent_phone: str = None,
initial_points: int = 60 initial_points: int = 60
) -> int: ) -> int:
"""创建学生初始操行分默认60分""" """创建学生初始操行分默认60分"""
sql = """ sql = """
INSERT INTO students (student_no, name, class_id, parent_phone, total_points) INSERT INTO students (student_no, name, parent_phone, total_points)
VALUES (%s, %s, %s, %s, %s) 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 @staticmethod
async def update(student_id: int, name: str = None, parent_phone: str = None, status: int = None) -> bool: 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_id = await StudentModel.create(
student_no=student.get('student_no'), student_no=student.get('student_no'),
name=student.get('name'), name=student.get('name'),
class_id=1, # 单班级固定为1
parent_phone=student.get('parent_phone'), parent_phone=student.get('parent_phone'),
initial_points=initial_points initial_points=initial_points
) )

View File

@@ -88,7 +88,6 @@ async def get_attendance_records(
@router.get("/ranking") @router.get("/ranking")
async def get_ranking( async def get_ranking(
request: Request, request: Request,
class_id: Optional[int] = None,
limit: int = Query(50, ge=1, le=100) limit: int = Query(50, ge=1, le=100)
): ):
""" """
@@ -98,7 +97,6 @@ async def get_ranking(
result = await StudentService.get_ranking( result = await StudentService.get_ranking(
user_id=user["user_id"], user_id=user["user_id"],
class_id=class_id,
limit=limit limit=limit
) )

View File

@@ -52,7 +52,6 @@ class AddAdminRequest(BaseModel):
real_name: str = Field(..., min_length=1, max_length=50, description="真实姓名") real_name: str = Field(..., min_length=1, max_length=50, description="真实姓名")
password: Optional[str] = Field(None, description="密码(不填则自动生成)") password: Optional[str] = Field(None, description="密码(不填则自动生成)")
role_type: str = Field(..., description="角色类型") role_type: str = Field(..., description="角色类型")
class_id: int = Field(..., description="班级ID")
subject_id: Optional[int] = Field(None, description="科目ID科代表需要") subject_id: Optional[int] = Field(None, description="科目ID科代表需要")

View File

@@ -19,8 +19,6 @@ class StudentInfo(BaseModel):
student_id: int student_id: int
student_no: str student_no: str
name: str name: str
class_id: int
class_name: Optional[str] = None
total_points: int total_points: int
parent_phone: Optional[str] = None parent_phone: Optional[str] = None
status: int status: int
@@ -74,6 +72,5 @@ class StudentRanking(BaseModel):
student_id: int student_id: int
student_no: str student_no: str
name: str name: str
class_name: str
total_points: int total_points: int
rank_in_class: int rank_in_class: int

View File

@@ -41,9 +41,7 @@ class AttendanceService:
return {"success": False, "message": "无权进行此操作"} 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( attendance_id = await AttendanceModel.create_record(
@@ -96,17 +94,12 @@ class AttendanceService:
role = await PermissionChecker.get_user_role(user_id) role = await PermissionChecker.get_user_role(user_id)
if role in ["班主任", "考勤委员"]: if role in ["班主任", "考勤委员"]:
class_id = await PermissionChecker.get_user_class_id(user_id)
records = await AttendanceModel.get_class_records( records = await AttendanceModel.get_class_records(
class_id=class_id,
date=date, date=date,
student_id=student_id student_id=student_id
) )
elif 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) records = await AttendanceModel.get_student_records(student_id)
else: else:
records = [] records = []

View File

@@ -146,8 +146,6 @@ class AuthService:
if student: if student:
result["student_no"] = student["student_no"] result["student_no"] = student["student_no"]
result["student_name"] = student["name"] result["student_name"] = student["name"]
result["class_id"] = student["class_id"]
result["class_name"] = student["class_name"]
result["total_points"] = student["total_points"] result["total_points"] = student["total_points"]
# 获取管理员角色 # 获取管理员角色
@@ -155,7 +153,6 @@ class AuthService:
admin_role = await AdminRoleModel.get_by_user_id(user_id) admin_role = await AdminRoleModel.get_by_user_id(user_id)
if admin_role: if admin_role:
result["role"] = admin_role["role_type"] result["role"] = admin_role["role_type"]
result["class_id"] = admin_role["class_id"]
return result return result

View File

@@ -133,9 +133,7 @@ class ConductService:
# 班主任/班长可查看全班 # 班主任/班长可查看全班
if role in ["班主任", "班长"]: if role in ["班主任", "班长"]:
user_class = await PermissionChecker.get_user_class_id(user_id)
records = await ConductModel.get_all_records( records = await ConductModel.get_all_records(
class_id=user_class,
limit=page_size, limit=page_size,
offset=offset, offset=offset,
start_date=start_date, start_date=start_date,
@@ -147,16 +145,13 @@ class ConductService:
count_sql = """ count_sql = """
SELECT COUNT(*) as total FROM conduct_records cr SELECT COUNT(*) as total FROM conduct_records cr
JOIN students s ON cr.student_id = s.student_id 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 total = total_result["total"] if total_result else 0
elif 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 ConductModel.get_student_records( records = await ConductModel.get_student_records(
student_id=student_id, student_id=student_id,

View File

@@ -31,12 +31,10 @@ class HomeworkService:
role = await PermissionChecker.get_user_role(user_id) role = await PermissionChecker.get_user_role(user_id)
if role == "班主任": if role == "班主任":
class_id = await PermissionChecker.get_user_class_id(user_id) assignments = await HomeworkModel.get_all_assignments()
assignments = await HomeworkModel.get_assignments_by_class(class_id)
elif role == "科代表": elif role == "科代表":
class_id = await PermissionChecker.get_user_class_id(user_id)
subject_ids = await PermissionChecker.get_user_subject_ids(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: else:
assignments = [] assignments = []
@@ -51,10 +49,7 @@ class HomeworkService:
created_by: int created_by: int
) -> Dict[str, Any]: ) -> Dict[str, Any]:
"""创建作业""" """创建作业"""
class_id = await PermissionChecker.get_user_class_id(created_by)
assignment_id = await HomeworkModel.create_assignment( assignment_id = await HomeworkModel.create_assignment(
class_id=class_id,
subject_id=subject_id, subject_id=subject_id,
title=title, title=title,
description=description, description=description,

View File

@@ -119,15 +119,12 @@ class StudentService:
@staticmethod @staticmethod
async def get_ranking( async def get_ranking(
user_id: int, user_id: int,
class_id: Optional[int] = None,
limit: int = 50 limit: int = 50
) -> Dict[str, Any]: ) -> Dict[str, Any]:
"""获取排行榜(单班级系统)""" """获取排行榜(单班级系统)"""
# 单班级系统,直接获取排行榜
ranking = await StudentModel.get_ranking(limit=limit) ranking = await StudentModel.get_ranking(limit=limit)
return { return {
"class_id": class_id or 1,
"ranking": ranking "ranking": ranking
} }