diff --git a/backend/main.py b/backend/main.py index fb7a7c4..df88275 100644 --- a/backend/main.py +++ b/backend/main.py @@ -85,13 +85,28 @@ app.add_middleware( async def global_exception_handler(request: Request, exc: Exception): """全局异常处理器 - 捕获所有未处理异常""" logger.error(f"未处理异常: {exc}", exc_info=True) + + # 获取origin用于CORS头 + origin = request.headers.get("origin", "") + allowed_origins = settings.CORS_ORIGINS or [] + + # 使用HTTP 200 + 业务错误码返回,避免CORS头丢失问题 + # (FastAPI exception_handler返回的500响应可能不经过CORS中间件,导致跨域读取失败) + headers = {} + if origin in allowed_origins: + headers["access-control-allow-origin"] = origin + headers["access-control-allow-credentials"] = "true" + headers["access-control-expose-headers"] = "*" + return JSONResponse( - status_code=500, + status_code=200, content={ "success": False, + "code": 500, "message": f"服务器内部错误: {str(exc)}", "detail": traceback.format_exc() if settings.DEBUG else None - } + }, + headers=headers ) diff --git a/backend/middleware/permission.py b/backend/middleware/permission.py index f864617..42539aa 100644 --- a/backend/middleware/permission.py +++ b/backend/middleware/permission.py @@ -77,6 +77,12 @@ class PermissionChecker: role = await PermissionChecker.get_user_role(user_id) return role == "劳动委员" + @staticmethod + async def check_is_volunteer_rep(user_id: int) -> bool: + """检查是否为志愿委员""" + role = await PermissionChecker.get_user_role(user_id) + return role == "志愿委员" + @staticmethod async def check_can_manage_subjects(user_id: int) -> bool: """检查是否可以管理科目(班主任或学习委员)""" @@ -127,7 +133,7 @@ class PermissionChecker: if not record: return False role = await PermissionChecker.get_user_role(user_id) - if role in ["班主任", "班长"]: + if role in ["班主任", "班长", "志愿委员"]: return True return record["recorder_id"] == user_id diff --git a/backend/routes/admin.py b/backend/routes/admin.py index b6632e0..d805cb8 100644 --- a/backend/routes/admin.py +++ b/backend/routes/admin.py @@ -194,7 +194,7 @@ async def get_conduct_history( return success_response(data=result) except Exception as e: logger.error(f"获取历史记录失败: {e}", exc_info=True) - return error_response(message=f"获取历史记录失败: {str(e)}", code=500) + return error_response(message=f"获取历史记录失败: {str(e)}") # ========== 作业管理 ========== @@ -299,7 +299,8 @@ async def add_attendance(request: Request, req: AddAttendanceRequest): status=req.status, reason=req.reason, apply_deduction=req.apply_deduction, - recorder_id=user["user_id"] + recorder_id=user["user_id"], + custom_deduction=req.custom_deduction ) if result["success"]: await LogService.write_operation_log( @@ -339,7 +340,7 @@ async def add_admin(request: Request, req: AddAdminRequest): is_teacher = await PermissionChecker.check_is_teacher(user["user_id"]) if not is_teacher: return error_response(message="仅班主任可添加管理员", code=403) - if req.role_type not in ["班长", "学习委员", "考勤委员", "劳动委员"]: + if req.role_type not in ["班长", "学习委员", "考勤委员", "劳动委员", "志愿委员"]: return error_response(message="无效的角色类型", code=400) result = await AdminService.add_admin( username=req.username, @@ -373,4 +374,4 @@ async def get_admins(request: Request): return success_response(data=result) except Exception as e: logger.error(f"获取管理员列表失败: {e}", exc_info=True) - return error_response(message=f"获取管理员列表失败: {str(e)}", code=500) \ No newline at end of file + return error_response(message=f"获取管理员列表失败: {str(e)}") \ No newline at end of file diff --git a/backend/routes/debug.py b/backend/routes/debug.py index 5f66e4e..ad5d80f 100644 --- a/backend/routes/debug.py +++ b/backend/routes/debug.py @@ -34,7 +34,7 @@ class AddAdminDebugRequest(BaseModel): async def debug_add_admin(request: Request, req: AddAdminDebugRequest): from models.user import UserModel - valid_roles = ["班主任", "班长", "学习委员", "考勤委员", "劳动委员"] + valid_roles = ["班主任", "班长", "学习委员", "考勤委员", "劳动委员", "志愿委员"] if req.role_type not in valid_roles: return error_response(message=f"无效的角色类型,可选: {', '.join(valid_roles)}") diff --git a/backend/routes/student.py b/backend/routes/student.py index 35a5611..bcaa87b 100644 --- a/backend/routes/student.py +++ b/backend/routes/student.py @@ -47,7 +47,7 @@ async def get_conduct_history( return success_response(data=result) except Exception as e: logger.error(f"获取学生操行分失败: {e}", exc_info=True) - return error_response(message=f"获取学生操行分失败: {str(e)}", code=500) + return error_response(message=f"获取学生操行分失败: {str(e)}") @router.get("/homework/{student_id}") diff --git a/backend/routes/subject.py b/backend/routes/subject.py index a4c4b87..913a5d4 100644 --- a/backend/routes/subject.py +++ b/backend/routes/subject.py @@ -28,7 +28,7 @@ async def get_subjects(request: Request, is_active: Optional[bool] = None): return success_response(data=result) except Exception as e: logger.error(f"获取科目列表失败: {e}", exc_info=True) - return error_response(message=f"获取科目列表失败: {str(e)}", code=500) + return error_response(message=f"获取科目列表失败: {str(e)}") @router.post("/create") async def create_subject(request: Request, req: CreateSubjectRequest): diff --git a/backend/schemas/admin.py b/backend/schemas/admin.py index 5b8ebee..99a2122 100644 --- a/backend/schemas/admin.py +++ b/backend/schemas/admin.py @@ -84,4 +84,5 @@ class AddAttendanceRequest(BaseModel): date: date status: str reason: Optional[str] = None - apply_deduction: bool = False \ No newline at end of file + apply_deduction: bool = True + custom_deduction: Optional[int] = Field(default=None, gt=0, description="自定义扣分值") \ No newline at end of file diff --git a/backend/services/attendance_service.py b/backend/services/attendance_service.py index d859934..6322f45 100644 --- a/backend/services/attendance_service.py +++ b/backend/services/attendance_service.py @@ -32,7 +32,8 @@ class AttendanceService: status: str, reason: Optional[str], apply_deduction: bool, - recorder_id: int + recorder_id: int, + custom_deduction: Optional[int] = None ) -> Dict[str, Any]: """添加考勤记录""" # 检查权限 @@ -57,8 +58,10 @@ class AttendanceService: # 应用扣分 if apply_deduction and status in ["absent", "late", "leave"]: - # 确定扣分数值 - if status == "absent": + # 确定扣分数值(优先使用自定义扣分) + if custom_deduction is not None: + points_change = -custom_deduction + elif status == "absent": points_change = -settings.DEDUCTION_ATTENDANCE_ABSENT elif status == "late": points_change = -settings.DEDUCTION_ATTENDANCE_LATE diff --git a/backend/services/conduct_service.py b/backend/services/conduct_service.py index cb19e30..3fddf9d 100644 --- a/backend/services/conduct_service.py +++ b/backend/services/conduct_service.py @@ -55,6 +55,10 @@ class ConductService: # 劳动委员固定 ±1分 if points_change not in [settings.LABOR_POINTS_ADD, settings.LABOR_POINTS_SUBTRACT]: return {"success": False, "message": "劳动委员只能进行±1分操作"} + elif role == "志愿委员": + # 志愿委员只能加分,不限制正分上限 + if points_change < 0: + return {"success": False, "message": "志愿委员只能加分"} elif role in ["学习委员", "考勤委员"]: # 学习委员和考勤委员只能扣分 if points_change > 0: @@ -147,8 +151,8 @@ class ConductService: role = await PermissionChecker.get_user_role(user_id) offset = (page - 1) * page_size - # 班主任/班长可查看全班 - if role in ["班主任", "班长"]: + # 班主任/班长/志愿委员可查看全班 + if role in ["班主任", "班长", "志愿委员"]: records = await ConductModel.get_all_records( limit=page_size, offset=offset, diff --git a/frontend/admin/admins.php b/frontend/admin/admins.php index d2fc80a..cc36ff0 100644 --- a/frontend/admin/admins.php +++ b/frontend/admin/admins.php @@ -70,10 +70,11 @@ include __DIR__ . '/../includes/header.php';