From 47be179c097303f96ccf5a818d13c748cfccc7b7 Mon Sep 17 00:00:00 2001 From: canglan Date: Wed, 6 May 2026 13:32:12 +0800 Subject: [PATCH] =?UTF-8?q?v1.6=E7=89=88=E6=9C=AC=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 ++- backend/.env.example | 2 +- backend/models/conduct.py | 97 +++++++++++++++++++++++------ backend/services/conduct_service.py | 25 ++++++-- frontend/.env.example | 7 +-- frontend/admin/conduct.php | 13 ++-- frontend/admin/homework.php | 2 +- frontend/includes/header.php | 1 - 8 files changed, 115 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index c8906d4..668e36e 100644 --- a/README.md +++ b/README.md @@ -228,9 +228,9 @@ classmanager/ | 班主任 | 全班 | 无限制 | 可撤销任何记录 | 全班所有记录 | 学生/管理员/科目管理、数据导出 | | 班长 | 全班 | ±5分 | 可撤销任何记录 | 全班所有记录 | - | | 学习委员 | 全班 | ±5分以内(加减分) | 不可撤销 | 仅自己提交的 | 作业管理、科目管理 | -| 考勤委员 | 全班 | 仅扣分(按规则) | 不可撤销 | 仅自己提交的 | 考勤管理 | -| 劳动委员 | 全班 | 仅±1分(卫生值日) | 不可撤销 | 仅自己提交的 | - | -| 志愿委员 | 全班 | 仅加分 | 不可撤销 | 仅自己提交的 | - | +| 考勤委员 | 全班 | 仅扣分,最多扣8分 | 不可撤销 | 仅自己提交的 | 考勤管理 | +| 劳动委员 | 全班 | ±1分以内 | 不可撤销 | 仅自己提交的 | - | +| 志愿委员 | 全班 | 仅加分,最多+5分 | 不可撤销 | 仅自己提交的 | - | | 学生 | 自己 | 无 | 无 | 自己的历史 | 修改密码 | | 家长 | 子女总分 | 无 | 无 | 不可见详情 | - | @@ -267,6 +267,8 @@ classmanager/ | v1.3 | 2026.4.27 | 考勤时段系统(早上/中午/晚修三时段)、历史记录扣分类型筛选、管理员/科目信息编辑、全链路输入安全校验 | | v1.4 | 2026.4.28 | 全量代码审查修复:双重密码哈希bug、学生端XSS漏洞、性能优化、Pydantic schema统一、权限检查补全、考勤委员撤销权限 | | v1.5 | 2026.4.29 | 用户反馈修复:登录封禁5分钟+手动解锁、加减分回显修复、学习委员5分限制修复、按钮样式补全 | +| v1.5.1 | 2026.4.29 | 权限修复:考勤委员提示遗漏、历史记录权限泄露、时间筛选失效、作业页分数限制与后端同步 | +| v1.6 | 2026.4.29 | 全量一致性审计:前后端配置统一(.env.example/config.py/config.php)、清理废弃全局变量、角色权限表精确化 | ## 许可证 diff --git a/backend/.env.example b/backend/.env.example index 368c74e..66cf913 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -109,7 +109,7 @@ MONITOR_MAX_SUBTRACT=-5 STUDY_COMMISSIONER_MAX_POINTS=5 # 考勤委员单次扣分上限(绝对值) -ATTENDANCE_REP_MAX_POINTS=5 +ATTENDANCE_REP_MAX_POINTS=8 # 劳动委员单次加减分上限(绝对值) LABOR_REP_MAX_POINTS=1 diff --git a/backend/models/conduct.py b/backend/models/conduct.py index 1817f9f..5725de1 100644 --- a/backend/models/conduct.py +++ b/backend/models/conduct.py @@ -41,18 +41,50 @@ class ConductModel: )) @staticmethod - async def count_student_records(student_id: int, include_revoked: bool = False) -> int: + async def count_student_records( + student_id: int, + include_revoked: bool = False, + start_date: str = None, + end_date: str = None, + recorder_id: int = None + ) -> int: """统计学生操行分记录总数""" - revoked_condition = "" if include_revoked else " AND is_revoked = 0" - sql = f"SELECT COUNT(*) as total FROM conduct_records WHERE student_id = %s{revoked_condition}" - result = await execute_one(sql, (student_id,)) + conditions = ["student_id = %s"] + params = [student_id] + if not include_revoked: + conditions.append("is_revoked = 0") + if start_date: + conditions.append("DATE(created_at) >= %s") + params.append(start_date) + if end_date: + conditions.append("DATE(created_at) <= %s") + params.append(end_date) + if recorder_id: + conditions.append("recorder_id = %s") + params.append(recorder_id) + where = " AND ".join(conditions) + sql = f"SELECT COUNT(*) as total FROM conduct_records WHERE {where}" + result = await execute_one(sql, tuple(params)) return result["total"] if result else 0 @staticmethod - async def count_records_by_recorder(recorder_id: int) -> int: + async def count_records_by_recorder( + recorder_id: int, + start_date: str = None, + end_date: str = None + ) -> int: """统计记录人提交的操行分记录总数""" - sql = "SELECT COUNT(*) as total FROM conduct_records WHERE recorder_id = %s" - result = await execute_one(sql, (recorder_id,)) + conditions = ["recorder_id = %s"] + params = [recorder_id] + if start_date: + conditions.append("DATE(created_at) >= %s") + params.append(start_date) + if end_date: + conditions.append("DATE(created_at) <= %s") + params.append(end_date) + where = " AND ".join(conditions) + sql = f"SELECT COUNT(*) as total FROM conduct_records WHERE {where}" + result = await execute_one(sql, tuple(params)) return result["total"] if result else 0 @staticmethod @@ -60,36 +92,65 @@ class ConductModel: student_id: int, limit: int = 50, offset: int = 0, - include_revoked: bool = False + include_revoked: bool = False, + start_date: str = None, + end_date: str = None, + recorder_id: int = None ) -> List[Dict[str, Any]]: """获取学生操行分记录""" - sql = """ + conditions = ["cr.student_id = %s"] + params = [student_id] + if not include_revoked: + conditions.append("cr.is_revoked = 0") + if start_date: + conditions.append("DATE(cr.created_at) >= %s") + params.append(start_date) + if end_date: + conditions.append("DATE(cr.created_at) <= %s") + params.append(end_date) + if recorder_id: + conditions.append("cr.recorder_id = %s") + params.append(recorder_id) + where = " AND ".join(conditions) + sql = f""" SELECT cr.*, u.real_name as recorder_name FROM conduct_records cr LEFT JOIN users u ON cr.recorder_id = u.user_id - WHERE cr.student_id = %s + WHERE {where} + ORDER BY cr.created_at DESC + LIMIT %s OFFSET %s """ - if not include_revoked: - sql += " AND cr.is_revoked = 0" - sql += " ORDER BY cr.created_at DESC LIMIT %s OFFSET %s" - return await execute_query(sql, (student_id, limit, offset)) + params.extend([limit, offset]) + return await execute_query(sql, tuple(params)) @staticmethod async def get_records_by_recorder( recorder_id: int, limit: int = 50, - offset: int = 0 + offset: int = 0, + start_date: str = None, + end_date: str = None ) -> List[Dict[str, Any]]: """获取操作人提交的记录""" - sql = """ + conditions = ["cr.recorder_id = %s", "cr.is_revoked = 0"] + params = [recorder_id] + if start_date: + conditions.append("DATE(cr.created_at) >= %s") + params.append(start_date) + if end_date: + conditions.append("DATE(cr.created_at) <= %s") + params.append(end_date) + where = " AND ".join(conditions) + sql = f""" SELECT cr.*, s.name as student_name FROM conduct_records cr JOIN students s ON cr.student_id = s.student_id - WHERE cr.recorder_id = %s AND cr.is_revoked = 0 + WHERE {where} ORDER BY cr.created_at DESC LIMIT %s OFFSET %s """ - return await execute_query(sql, (recorder_id, limit, offset)) + params.extend([limit, offset]) + return await execute_query(sql, tuple(params)) @staticmethod async def get_all_records( diff --git a/backend/services/conduct_service.py b/backend/services/conduct_service.py index 6d0c68d..ba08092 100644 --- a/backend/services/conduct_service.py +++ b/backend/services/conduct_service.py @@ -273,22 +273,35 @@ class ConductService: total = total_result["total"] if total_result else 0 elif student_id: - # 管理员可查看指定学生 - + # 普通管理员查看指定学生(仅返回自己操作的记录) records = await ConductModel.get_student_records( student_id=student_id, limit=page_size, - offset=offset + offset=offset, + start_date=start_date, + end_date=end_date, + recorder_id=user_id + ) + total = await ConductModel.count_student_records( + student_id=student_id, + start_date=start_date, + end_date=end_date, + recorder_id=user_id ) - total = await ConductModel.count_student_records(student_id) else: # 查看自己提交的记录 records = await ConductModel.get_records_by_recorder( recorder_id=user_id, limit=page_size, - offset=offset + offset=offset, + start_date=start_date, + end_date=end_date + ) + total = await ConductModel.count_records_by_recorder( + recorder_id=user_id, + start_date=start_date, + end_date=end_date ) - total = await ConductModel.count_records_by_recorder(user_id) return { "records": records, diff --git a/frontend/.env.example b/frontend/.env.example index f7a5b65..a87bd19 100644 --- a/frontend/.env.example +++ b/frontend/.env.example @@ -41,11 +41,8 @@ ICP_NUMBER=京ICP备1234567890号-x DEDUCTION_HOMEWORK_NOT_SUBMIT=2 # 作业-迟交扣分 DEDUCTION_HOMEWORK_LATE=1 -# 作业-每次加减分上限(绝对值) -HOMEWORK_MAX_POINTS=3 - -# 学习委员单次加减分上限(绝对值) -STUDY_COMMISSIONER_MAX_POINTS=5 +# 作业-每次加减分上限(绝对值,仅影响作业管理页面的前端输入限制) +HOMEWORK_MAX_POINTS=5 # 考勤-缺勤扣分 DEDUCTION_ATTENDANCE_ABSENT=3 diff --git a/frontend/admin/conduct.php b/frontend/admin/conduct.php index 92a80cc..aea4b40 100644 --- a/frontend/admin/conduct.php +++ b/frontend/admin/conduct.php @@ -208,11 +208,14 @@ loadStudents(); '班长单次±5分以内', + '学习委员' => '学习委员单次±5分以内', + '考勤委员' => '考勤委员仅限扣分,单次最多扣8分', + '劳动委员' => '劳动委员单次±1分以内', + '志愿委员' => '志愿委员仅限加分,最多+5分', + ]; + echo $hints[$role] ?? '班主任无限制'; ?>
diff --git a/frontend/admin/homework.php b/frontend/admin/homework.php index 796d623..69d370b 100644 --- a/frontend/admin/homework.php +++ b/frontend/admin/homework.php @@ -106,7 +106,7 @@ var selectedStudentIds = []; const hwRole = ''; // 初始化扣分配置 -const hwMaxPoints = hwRole === '班主任' ? 100 : (window.HOMEWORK_MAX_POINTS || 5); +const hwMaxPoints = hwRole === '班主任' ? 100 : 5; const hwNotSubmit = window.DEDUCTION_HOMEWORK_NOT_SUBMIT || 2; const hwLate = window.DEDUCTION_HOMEWORK_LATE || 1; diff --git a/frontend/includes/header.php b/frontend/includes/header.php index 8181285..e2586ab 100644 --- a/frontend/includes/header.php +++ b/frontend/includes/header.php @@ -47,7 +47,6 @@ $page_title = $page_title ?? '首页'; window.USER_STORAGE_KEY = ''; window.DEDUCTION_HOMEWORK_NOT_SUBMIT = ; window.DEDUCTION_HOMEWORK_LATE = ; - window.HOMEWORK_MAX_POINTS = ; window.DEDUCTION_ATTENDANCE_ABSENT = ; window.DEDUCTION_ATTENDANCE_LATE = ; window.DEDUCTION_ATTENDANCE_LEAVE = ;