Files
SharedClassManager/.cospec/plan/changes/fix-admin-multi-issues/task.md
2026-04-15 08:41:44 +08:00

360 lines
34 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 实施
### 阶段 1统一导航栏
- [x] 1.1 创建统一导航栏模板
【目标对象】`frontend/includes/nav.php`(新建)
【修改目的】将所有 admin 页面硬编码的导航栏抽取为共享模板,解决各页面导航不一致的问题;同时修复 dashboard.php 中密码链接 `passwork.php` 的拼写错误
【修改方式】新建 PHP 文件,定义导航栏 HTML 结构,直接读取 `$role``$current_page` 变量(已由 `header.php` 第 17-19 行定义)来动态生成导航项和 active 状态
【相关依赖】`frontend/includes/header.php`(第 17-19 行已定义 `$current_page``$user_type``$role` 变量,在 include nav.php 之前已可用)
【修改内容】
- 直接使用 `$role``$current_page` 变量(无需参数传递,因为 header.php 在 nav.php 之前被 include
- 导航项及角色条件统一为:
- 首页dashboard所有管理员可见
- 学生管理students所有管理员可见
- 操行分管理conduct$role==='班主任' || $role==='班长'
- 作业管理homework$role==='班主任' || $role==='学习委员'
- 考勤管理attendance$role==='班主任' || $role==='考勤委员'
- 科目管理subjects$role==='班主任' || $role==='学习委员'
- 管理员管理admins$role==='班主任'
- 历史记录history所有管理员可见
- 修改密码password所有管理员可见
- 根据 `$current_page`(值为不含 `.php` 后缀的文件名,如 `dashboard``students`)为当前页面对应的导航项添加 `active` class
- 密码链接统一写为 `password.php`(修复 dashboard 中 `passwork.php` 拼写错误)
- 导航栏外层容器沿用现有 `<div class="nav">` 结构
- [x] 1.2 各 admin 页面引入统一导航模板
【目标对象】所有 `frontend/admin/*.php` 页面
【修改目的】移除各页面硬编码的 `<div class="nav">...</div>` 块,替换为 `include nav.php`,统一导航栏
【修改方式】在以下每个页面中,找到 `<div class="nav">` 到对应的 `</div>` 之间的导航栏块,整块替换为 `<?php include __DIR__ . '/../includes/nav.php'; ?>`
【相关依赖】`frontend/includes/nav.php`(任务 1.1 创建)
【修改内容】
- `frontend/admin/dashboard.php`:替换第 25-43 行的 `<div class="nav">...</div>` 为 include nav.php
- `frontend/admin/students.php`:替换第 25-43 行的 `<div class="nav">...</div>` 为 include nav.php
- `frontend/admin/conduct.php`:替换第 31-47 行的 `<div class="nav">...</div>` 为 include nav.php
- `frontend/admin/homework.php`:替换第 31-47 行的 `<div class="nav">...</div>` 为 include nav.php注意此页面后续会在任务 2.2 完全重写,此处仅替换导航栏部分)
- `frontend/admin/attendance.php`:替换第 31-47 行的 `<div class="nav">...</div>` 为 include nav.php注意此页面后续会在任务 3.3 完全重写,此处仅替换导航栏部分)
- `frontend/admin/history.php`:替换第 25-43 行的 `<div class="nav">...</div>` 为 include nav.php
- `frontend/admin/subjects.php`:替换第 30-48 行的 `<div class="nav">...</div>` 为 include nav.php
- `frontend/admin/admins.php`:替换第 30-46 行的 `<div class="nav">...</div>` 为 include nav.php
- `frontend/admin/password.php`:替换第 25-43 行的 `<div class="nav">...</div>` 为 include nav.php
- 替换后需确认 include 语句位于 `include header.php;` 之后、页面主体内容之前
### 阶段 2作业管理改版
- [x] 2.1 清理 `admin.js` 中废弃的作业管理函数
【目标对象】`frontend/assets/js/admin.js`
【修改目的】移除作业管理改版后不再使用的旧作业管理函数,避免全局函数污染和潜在冲突
【修改方式】删除第 174-220 行的三个函数:`showAddAssignmentModal()``loadSubjectsForSelect()``submitAddAssignment()`
【相关依赖】无homework.php 将在任务 2.2 完全重写,不再依赖这些全局函数)
【修改内容】
- 删除第 174-178 行的 `showAddAssignmentModal()` 函数
- 删除第 180-192 行的 `loadSubjectsForSelect()` 函数
- 删除第 194-220 行的 `submitAddAssignment()` 函数
- 注意:`admin.js` 中其他通用函数(如 `submitBatchPoints()``closeModal()``escapeHtml()``toggleSelectAll()`)保持不变,仍被 conduct.php 和 students.php 使用
- [x] 2.2 重写作业管理前端页面
【目标对象】`frontend/admin/homework.php`
【修改目的】将作业发布/提交管理模式改为单纯的加减操行分模式,交互方式参照 `conduct.php`
【修改方式】完全重写页面 HTML 内容和 `<script>` 中的 JavaScript 逻辑(保留文件头部的 PHP 鉴权部分和 include 结构)
【相关依赖】
- `frontend/includes/nav.php`(任务 1.1,导航栏引入)
- `frontend/assets/js/admin.js`(提供 `submitBatchPoints()``showBatchPointsModal()``closeModal()``escapeHtml()``toggleSelectAll()` 等全局复用函数)
- 后端 API `GET /api/admin/students`(加载学生列表,参见 `conduct.php` 第 77-95 行的 `loadStudents()` 调用方式)
- 后端 API `POST /api/admin/conduct/add`(加减分接口,请求体:`{ student_ids: number[], points_change: number, reason: string }`
【修改内容】
- 角色权限判断(第 23 行)统一为 `in_array($role, ['班主任', '学习委员'])`(之前是 `科代表`,与后端 `admin.py` 第 228 行的角色检查一致)
- 移除发布作业按钮和发布作业模态框
- 移除作业列表和提交记录展示
- 新增学生列表表格(列:复选框、学号、姓名、当前操行分、操作-加减分按钮),参照 `conduct.php` 第 57-71 行的表格结构
- 新增批量加减分模态框(复用 `conduct.php` 第 116-143 行和 `students.php` 第 214-241 行的 `batchPointsModal` 模态框结构),包含:
- 已选学生数量显示
- 扣分类型快捷选择:未交作业(-2分)、迟交作业(-1分)、自定义(选择后自动填入分数变动输入框)
- 分数变动输入框(支持正数加分、负数扣分)
- 原因输入框textarea
- 确认提交按钮
- 页面级 JS 中定义 `let selectedStudentIds = [];``loadStudents()``showSinglePointsModal()` 函数(与 conduct.php 和 students.php 的页面级变量风格一致)
- 提交时复用 `admin.js` 中已有的 `submitBatchPoints()` 函数(该函数调用 `POST /api/admin/conduct/add``reason` 字段格式如 `"[作业扣分] 未交作业 - 原因内容"` 以区分来源
- 错误处理遵循仓库既有风格:调用 `apiPost()` 后检查 `res.success`,失败时使用 `showToast(res.message || '操作失败', 'error')` 提示(参见 `admin.js` 第 57-64 行的 `submitBatchPoints` 错误处理模式)
- 加载学生列表失败时,在表格区域显示错误提示文本
### 阶段 3考勤管理改版
- [x] 3.1 清理 `admin.js` 中废弃的考勤管理函数
【目标对象】`frontend/assets/js/admin.js`
【修改目的】移除考勤管理改版后不再使用的旧考勤添加函数,避免全局函数污染和潜在冲突
【修改方式】删除原始文件第 222-268 行的三个函数(因任务 2.1 已删除第 174-220 行,实际操作时行号已前移约 47 行,请按函数名定位):`showAddAttendanceModal()``loadStudentsForSelect()``submitAddAttendance()`
【相关依赖】任务 2.1(先删除作业相关函数,行号会偏移)
【修改内容】
- 删除 `showAddAttendanceModal()` 函数(原始第 222-227 行)
- 删除 `loadStudentsForSelect()` 函数(原始第 229-238 行)
- 删除 `submitAddAttendance()` 函数(原始第 241-268 行)
- 建议按函数名搜索定位删除,而非依赖行号
- 注意attendance.php 页面重写后,所有考勤相关 JS 逻辑将在页面级 `<script>` 中定义,不依赖 `admin.js` 中的旧函数
- [x] 3.2 新增考勤方格网格 CSS 样式
【目标对象】`frontend/assets/css/admin.css`
【修改目的】为考勤页面的学生方格网格提供样式
【修改方式】在文件末尾追加新样式规则
【相关依赖】无
【修改内容】
- `.student-grid` 容器:`display: flex; flex-wrap: wrap; gap: 10px;` 布局
- `.student-cell` 方格:`width: calc(100% / 7 - 10px);`(每行 7 个带边框、圆角、居中文字、内边距、cursor: pointer
- `.student-cell.selected` 选中状态:红色/粉色背景高亮(如 `background: #fee2e2; border-color: #ef4444;`
- `.student-cell.has-record` 已有考勤记录标记:灰色虚线边框(如 `border: 2px dashed #9ca3af;`),用于标识当天已有考勤记录的学生
- `.student-cell:hover` 悬停效果:浅色背景变化
- `.attendance-toolbar` 工具栏样式flex 布局,按钮间距
- 移动端响应式:小屏幕下 `.student-cell` 宽度调整为 `calc(100% / 4 - 10px)` 或更宽
- [x] 3.3 重写考勤管理前端页面
【目标对象】`frontend/admin/attendance.php`
【修改目的】将单个学生下拉选择模式改为学生方格网格模式(一行 7 个),选中扣分制
【修改方式】完全重写页面 UI 和 `<script>` 中的 JavaScript 逻辑(保留文件头部的 PHP 鉴权部分和 include 结构)
【相关依赖】
- `frontend/includes/nav.php`(任务 1.1,导航栏引入)
- `frontend/assets/css/admin.css`(任务 3.2,方格网格样式)
- 后端 API `GET /api/admin/students`(加载学生列表)
- 后端 API `POST /api/admin/attendance`(添加考勤记录,请求体:`{ student_id: number, date: string, status: string, reason?: string, apply_deduction: boolean }`
- 后端 API `GET /api/admin/attendance/records`(查询考勤记录,参数:`{ date: string }`
【修改内容】
- 页面顶部工具栏:日期选择器(默认当天)+ 考勤状态选择(缺勤/迟到/请假单选按钮组)+ 原因输入框
- 主体区域:加载所有学生,以方格网格展示(使用 `.student-grid` 容器和 `.student-cell` 方格)
- 每个方格显示学生姓名,点击切换选中/取消状态(添加/移除 `.selected` class
- 底部工具栏:全选按钮 + 取消全选按钮 + 提交按钮
- 提交逻辑:遍历所有选中的学生,对每个学生调用 `POST /api/admin/attendance`,参数为 `{ student_id, date, status, reason, apply_deduction: true }`
- 提交过程中的错误处理:使用 `Promise.allSettled()` 并发提交,提交完成后汇总成功/失败数量,使用 `showToast()` 提示结果(与仓库既有风格一致)
- 重复考勤记录边界处理:后端 `AttendanceService.add_attendance` 不检查同一学生同一天是否已有考勤记录,允许重复提交。前端在提交前先调用 `GET /api/admin/attendance/records` 获取当天已有记录,对已有考勤记录的学生在方格上添加视觉标记(如 `.has-record` 样式,灰色虚线边框),并在提交时 `confirm()` 提示"以下学生已有考勤记录,是否继续提交?"
- 下方保留历史考勤记录表格:调用 `GET /api/admin/attendance/records` 查询指定日期的记录并渲染(参照现有 `attendance.php` 第 112-131 行的渲染逻辑)
- 移除原有的单个学生下拉选择添加考勤模态框
- 加载学生列表失败时,在网格区域显示错误提示文本
- 错误提示风格遵循仓库既有模式:使用 `showToast(message, 'error')` 函数
### 阶段 4家长手机号权限控制
- [x] 4.1 家长手机号权限控制
### 阶段 5扣分规则配置化
- [x] 5.1 作业和考勤扣分规则配置化
【目标对象】`frontend/.env.example``frontend/config.php``frontend/includes/header.php``frontend/admin/homework.php``frontend/admin/attendance.php`
【修改目的】将作业和考勤的默认扣分量从 .env 文件配置,前端快捷按钮动态读取配置值,作业加减分有上限限制
【修改方式】
- .env.example 新增6个扣分配置项有默认值
- config.php 读取并 define 为常量
- header.php 注入到 JS 全局变量
- homework.php 快捷按钮使用配置值 + 自定义输入 + ±HOMEWORK_MAX_POINTS 限制
- attendance.php 状态按钮使用配置值
### 阶段 6CORS 跨域拦截修复
- [x] 6.1 注册 AuthMiddleware 为全局中间件并修复 CORS 执行顺序
【目标对象】`backend/main.py``backend/middleware/auth_middleware.py`
【修改目的】修复 CORS 跨域拦截问题AuthMiddleware 未注册导致 request.state 属性缺失,路由层 500 错误被浏览器误报为 CORS 错误
【修改方式】
- auth_middleware.py: dispatch 方法顶部添加 OPTIONS 请求跳过逻辑
- main.py: 注册 AuthMiddleware 为全局中间件先注册后执行CORS 在 Auth 之后注册(后注册先执行)
- main.py: 添加 CORS 配置启动日志和空值警告
【中间件执行顺序】CORS → Auth → access_log → 路由
【后续修复】将 AuthMiddleware 从 BaseHTTPMiddleware 改为纯 ASGI 中间件,解决 BaseHTTPMiddleware 提前返回响应时 CORS 头丢失的问题
【目标对象】`frontend/admin/students.php`
【修改目的】除班主任角色外,隐藏家长手机号列的显示内容,保护隐私
【修改方式】在表头 HTML 和 JS 渲染处添加 `$role` 判断
【相关依赖】`$_SESSION['role']`(已存储在 `$role` 变量中,由各页面顶部从 session 读取)
【修改内容】
- 第 68 行表头处:`<th>家长手机号</th>` 改为 `<?php if ($role === '班主任'): ?><th>家长手机号</th><?php endif; ?>`(表头列有条件显示)
- 在页面 `<script>` 标签开头注入角色信息:`const userRole = '<?php echo $role; ?>';`
- 第 148 行 JS 渲染处:`<td>${student.parent_phone || '-'}</td>` 改为根据 `userRole` 判断渲染内容:班主任显示真实手机号,其他角色显示 `***`
- 第 156 行空数据提示处:`colspan="6"` 需根据角色动态调整——班主任 6 列,非班主任 5 列(可用 PHP 输出:`colspan="<?php echo $role === '班主任' ? '6' : '5'; ?>"`
- 新增学生表单(第 101-129 行)和导入学生功能中的手机号字段保持不变,任何有学生管理权限的角色都能录入(与 proposal 一致)
### 阶段 7修复 AuthMiddleware 401 无限循环
- [x] 7.1 AuthMiddleware 添加调试日志和修复潜在问题
【目标对象】`backend/middleware/auth_middleware.py`
【修改目的】修复 401 无限循环问题。当前 AuthMiddleware 注册为全局中间件后,所有非公开路径的请求都需要 JWT 认证,但缺少详细日志无法定位 Token 验证失败的具体原因。需要添加详细日志并修复 OPEN_PATHS 未被检查的 Bug。
【修改方式】在 dispatch 方法中添加每个认证步骤的调试日志,修复 OPEN_PATHS 逻辑,添加更多公开路径
【修改内容】
- 在 dispatch 方法中每个关键检查点添加 logger.debug 日志:
1. 请求路径和方法
2. Authorization header 是否存在
3. JWT 验证结果(成功时记录 user_id失败时记录原因
4. Redis Token 查询结果stored_token 是否存在、是否匹配)
- 修复 OPEN_PATHS 未被检查的 Bug在 is_public_path 检查后增加 OPEN_PATHS 检查OPEN_PATHS 中的路径跳过 Token 验证但仍继续到路由层
-`/api/auth/me``/api/auth/change-password` 添加到 PUBLIC_PATHS目前 OPEN_PATHS 逻辑未被 dispatch 使用,导致这些路径被拦截)
- `_cors_response` 方法中的 allowed_origins 改为从 config.settings 读取,而非硬编码
- [x] 7.2 前端 common.js 添加 401 防循环机制
【目标对象】`frontend/assets/js/common.js`
【修改目的】防止 401 响应导致的无限刷新循环。当前 apiRequest 在收到 401 时直接 clearAuth + redirect如果用户重新登录后 Token 仍然无效(如 Redis 重启),会形成 401 → 登录 → 401 → 登录的无限循环。
【修改方式】在 401 处理逻辑中添加防循环机制
【修改内容】
- 在 401 处理中添加路径判断:如果当前已在 `/index.php`(登录页),不执行重定向
- 添加重定向频率限制:使用 sessionStorage 记录最近一次 401 重定向时间,如果 5 秒内重复 401停止重定向并在控制台输出警告
- 清除认证信息后,在跳转前添加延迟或在 URL 中添加标记参数,防止浏览器缓存导致的循环
### 阶段 8修复 302 循环 - 401 时同步清除 PHP Session
- [x] 8.1 修复 302 循环 - 401 时同步清除 PHP Session
【目标对象】`frontend/assets/js/common.js`
【修改目的】修复 PHP Session 与 JWT Token 不同步导致的 302 无限重定向循环。当 JWT Token 无效(如后端重启后 Redis 清空)导致 API 返回 401 时,`clearAuth()` 只清除了 localStorage 中的 JWT Token但 PHP Session 仍然有效,导致 `index.php` 第 15-23 行检测到有效 Session 后又 302 重定向到 dashboard形成循环。
【修改方式】在 `apiRequest` 函数的 401 处理逻辑中,`clearAuth()` 之后增加对 `/api/clear_session.php` 的 POST 调用,以同步清除 PHP Session
【修改内容】
-`clearAuth()` 调用后、路径检查之前,添加 `fetch('/api/clear_session.php', { method: 'POST', headers: { 'Content-Type': 'application/json' } })` 调用
- 使用 try-catch 包裹,失败时仅输出 console.warn 警告,不阻塞后续重定向逻辑
- `/api/clear_session.php` 是同源路径(由 Nginx 直接处理,不经过后端 FastAPI不需要 Authorization header也不会触发跨域问题
### 阶段 9修复 500 响应 CORS 头丢失 + admin.js 变量重复声明
- [x] 9.1 修复 500 响应 CORS 头丢失
【目标对象】`backend/middleware/auth_middleware.py`
【修改目的】当路由层抛出异常导致 500 时BaseHTTPMiddleware 的 call_next 返回的 500 响应不会经过 CORSMiddleware导致 CORS 头缺失。浏览器报告"CORS Missing Allow Origin",前端无法读取错误信息。
【修改方式】在 dispatch 方法中将 `return await call_next(request)` 改为 try-except 包裹,确保所有响应都有 CORS 头
【修改内容】
- 用 try-except 包裹 `call_next(request)` 调用
- 在 try 中获取 response 后检查是否已有 CORS 头,若无则补充
- 在 except 中捕获路由层异常,返回带 CORS 头的 500 响应
- [x] 9.2 修复 admin.js selectedStudentIds 重复声明
【目标对象】`frontend/assets/js/admin.js``frontend/admin/conduct.php``frontend/admin/homework.php`
【修改目的】admin.js 通过 footer.php 在所有 admin 页面加载,其 `let selectedStudentIds = []` 与 conduct.php 和 homework.php 页面级 `<script>` 中的同名声明冲突,导致 SyntaxError
【修改方式】将 `let` 改为 `var``var` 允许重复声明
【修改内容】
- 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个文件
### 阶段 11修复 422 参数校验 + JS 变量重复声明 + RANK() 兼容性
- [x] 11.1 修复历史记录页 422 和 JS 变量声明问题
- [x] 11.2 修复首页 RANK() OVER 窗口函数兼容性
### 阶段 12全面项目审查 - 消灭潜在问题
- [x] 12.1 修复后端角色名"科代表"→"学习委员"3处+ 存储过程缺失 + 权限逻辑 + security工具类
【目标对象】`backend/services/conduct_service.py``backend/services/homework_service.py``backend/models/conduct.py``backend/middleware/permission.py``backend/utils/security.py`
【修改目的】1) "科代表"角色在数据库ENUM中不存在应为"学习委员"2) revoke_conduct_record存储过程在init.sql中不存在需替换为内联SQL3) get_user_subject_ids方法逻辑顺序有误4) validate_points_change返回类型不一致
【修改方式】
- conduct_service.py 第58行: `elif role in ["科代表", "考勤委员"]:``elif role in ["学习委员", "考勤委员"]:`
- homework_service.py 第35行: `elif role == "科代表":``elif role == "学习委员":`
- homework_service.py 第82行: `if role == "科代表":``if role == "学习委员":`
- conduct.py revoke_record方法: 替换 `call_procedure('revoke_conduct_record', ...)` 为内联SQL: `UPDATE conduct_records SET is_revoked=1, revoked_by=%s, revoked_at=NOW() WHERE record_id=%s`,使用 `execute_update` 执行
- permission.py get_user_subject_ids: 调整逻辑顺序先检查role_type是否为班主任再检查subject_id
- security.py 第126行: `return f"单次分值变动不能超过{max_abs}分"``return False, f"单次分值变动不能超过{max_abs}分"`
- [x] 12.2 修复前端学生端/家长端关键问题 + 管理端小问题 + 配置修复
【目标对象】`frontend/student/dashboard.php``frontend/api/save_session.php``frontend/index.php``frontend/student/attendance.php``frontend/admin/homework.php``frontend/config.php``frontend/student/conduct_history.php`
【修改目的】1) student/dashboard.php的common.js加载顺序错误导致API函数未定义2) save_session.php将user_id错误存储为student_id3) student/dashboard.php中hw.subject字段名错误4) homework.php重复调用loadStudents5) config.php中ICP_ENABLED逻辑反转6) conduct_history.php文件为空
【修改方式】
- student/dashboard.php: 将 `<script src="/assets/js/common.js"></script>` 移到内联script之前删除局部 `const API_BASE_URL``const JWT_STORAGE_KEY`/`USER_STORAGE_KEY`,改用 `window.API_BASE_URL = '<?php echo API_BASE_URL; ?>'` 等全局变量在common.js加载之前设置`hw.subject` 改为 `hw.subject_name`
- save_session.php: 第106行 `$_SESSION['student_id'] = $data['user_id'];` 改为从请求中获取实际student_id`$_SESSION['student_id'] = $data['student_id'] ?? $data['user_id'];`同时添加student_id到requiredFields验证仅学生时必须
- index.php: 登录成功后save_session调用中添加 `student_id: userData.student_id` 参数
- student/attendance.php: 第21行 `$student_id = $_SESSION['student_id'];` 保持不变修复save_session后此值会正确
- admin/homework.php: 删除第176行重复的 `loadStudents();` 调用
- config.php: 第61行 `define('ICP_ENABLED', $config['ICP_ENABLED'] === 'false');` 改为 `define('ICP_ENABLED', $config['ICP_ENABLED'] !== 'false');`
- student/conduct_history.php: 文件为空需要重定向到dashboard或创建基本页面。最简方案创建一个重定向到 /student/dashboard.php 的页面
【目标对象】`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 参数
- [x] 12.3 修复CRITICAL: total_points在所有Service中从未更新操行分系统完全失效
【目标对象】`backend/services/conduct_service.py``backend/services/attendance_service.py``backend/services/homework_service.py`
【修改目的】`StudentModel.update_total_points()` 方法已存在但从未被调用,导致 `students.total_points` 永远保持初始值60分排行榜、仪表盘、家长端显示的所有总分都是错误的
【修改方式】
- conduct_service.py add_points: 在 `ConductModel.create_record()` 后添加 `await StudentModel.update_total_points(student_id, points_change)`
- conduct_service.py revoke_record: 重写撤销逻辑,先获取原记录,撤销后调用 `await StudentModel.update_total_points(record["student_id"], -record["points_change"])` 反向恢复总分
- attendance_service.py add_attendance: 在 `ConductModel.create_record()` 后添加 `await StudentModel.update_total_points(student_id, points_change)`
- homework_service.py update_submission_status: 在 `ConductModel.create_record()` 后添加 `await StudentModel.update_total_points(submission["student_id"], points_change)`
- [x] 12.4 修复前端: student/homework.php字段名 + nav.php劳动委员 + parent/attendance.php死链
【目标对象】`frontend/student/homework.php``frontend/includes/nav.php``frontend/parent/attendance.php`
【修改目的】1) homework.php中hw.subject字段名与后端API不匹配2) 劳动委员无法在导航栏看到操行分管理入口README要求±1分权限3) parent/attendance.php有指向不存在的/parent/homework.php的死链接
【修改方式】
- student/homework.php 第56行: `hw.subject``hw.subject_name`
- nav.php 第4行: 添加 `|| $role === '劳动委员'` 到操行分管理导航条件
- parent/attendance.php: 删除指向不存在的 /parent/homework.php 的死链接,导航只保留"首页"和"考勤记录"
- [x] 12.5 修复学生端导航死链接: 3个页面的"操行分"链接指向不存在的/student/conduct.php
【目标对象】`frontend/student/homework.php``frontend/student/attendance.php``frontend/student/password.php`
【修改目的】学生端3个子页面导航栏中的"操行分"链接指向 `/student/conduct.php`,但实际文件名是 `conduct_history.php`导致404
【修改方式】
- password.php 第26行: `/student/conduct.php``/student/conduct_history.php`
- [x] 12.6 修复劳动委员页面级权限被拦截 + 仪表盘快捷操作缺失
【目标对象】`frontend/admin/conduct.php``frontend/admin/dashboard.php`
【修改目的】README规定劳动委员有操行分管理权限±1分但conduct.php页面级权限检查只允许班主任和班长访问劳动委员会被重定向到dashboarddashboard.php快捷操作也缺少劳动委员
【修改方式】
- conduct.php 第23行: `['班主任', '班长']``['班主任', '班长', '劳动委员']`
- conduct.php 第115行: 分数变动提示文字改为 if/elseif/else 结构,劳动委员显示"劳动委员仅限±1分"
- dashboard.php 第61行: 快捷操作条件添加劳动委员
- attendance.php 第27行: `/student/conduct.php``/student/conduct_history.php`
- password.php 第26行: `/student/conduct.php``/student/conduct_history.php`
- [x] 12.7 同步学生端dashboard.php页眉使用header.php/footer.php
【目标对象】`frontend/student/dashboard.php`
【修改目的】学生端dashboard.php使用自定义HTML结构与管理端不统一缺少共享页眉和ICP备案号
【修改方式】
- 移除自定义的 `<!DOCTYPE html>`, `<head>`, `<body>` 和自定义 header div
- 添加 `$page_title = '学生端'; include __DIR__ . '/../includes/header.php';`
- 移除重复的 `window.API_BASE_URL` 等变量设置header.php已处理
- 添加 `include __DIR__ . '/../includes/footer.php';` 以显示ICP备案号
- [x] 12.8 登录页面补充备案号悬挂
【目标对象】`frontend/index.php`
【修改目的】登录页面的页脚缺少ICP备案号
【修改方式】
- 在登录页 footer 的版权信息后添加ICP备案号链接使用 `ICP_ENABLED``ICP_NUMBER` 常量)
- 链接指向 https://beian.miit.gov.cn/target="_blank"
- [x] 12.9 修复用户报告的4个运行时Bug
【目标对象】`backend/schemas/admin.py``frontend/admin/conduct.php``frontend/admin/homework.php``frontend/admin/attendance.php`
【修改目的】用户报告1) 历史记录在加减分后无内容; 2) 管理员无法添加学生; 3) 加减分页面仅显示21人; 4) 管理端修改密码失败
【修改方式】
- 12.9a 历史记录问题已在12.3中通过添加 `StudentModel.update_total_points()` 调用修复
- 12.9b 添加学生422错误`add_student` 路由使用裸函数参数而非Pydantic bodyFastAPI从query string读取导致422。创建 `AddStudentRequest` schema路由改用Pydantic body
- 12.9c 仅显示21人`loadStudents()` 未传 `page_size`后端默认20。前端3个文件添加 `{page_size: 1000}`
- 12.9d 修改密码:已确认代码正确(`sha1_md5_password` + `need_change_password=0`
- [x] 12.10 修复登录及管理员日志未写入数据库
【目标对象】`backend/models/log.py`(新建)、`backend/services/log_service.py`(新建)、`backend/services/auth_service.py``backend/routes/auth.py``backend/routes/admin.py`
【修改目的】数据库中 `login_logs``operation_logs` 表已存在但没有任何后端代码写入数据。README中规划了 `models/operation_log.py``services/log_service.py`,但文件不存在
【修改方式】
- 新建 `backend/models/log.py`:包含 `LoginLogModel``OperationLogModel`,分别向 login_logs 和 operation_logs 表写入记录
- 新建 `backend/services/log_service.py``LogService` 提供 `write_login_log``write_operation_log` 方法,用 try-except 包裹确保日志写入失败不影响主业务
- 修改 `auth_service.py`login 方法增加 `user_agent` 参数在5个退出点失败次数过多、用户不存在、密码错误、账号禁用、登录成功均写入 login_logs
- 修改 `auth.py`:从 HTTP 请求头获取 user-agent 并传递给 AuthService.login()
- 修改 `admin.py`8个管理操作成功后写入 operation_logsimport_students、add_student、add_points、revoke_record、create_assignment、update_submission、add_attendance、add_admin
- [x] 12.11 修复CRITICAL: 登录返回数据缺少student_id导致学生端完全无法工作
【目标对象】`backend/services/auth_service.py``backend/routes/auth.py`
【修改目的】登录成功后返回给前端的数据中没有 student_id 字段,导致 index.php 无法传递 student_id 给 save_session.phpSession 中缺少 student_id学生端所有页面 $student_id 为 null所有 API 调用路径变成 /api/student/conduct/null 导致失败。这是学生端显示"假数据"的根本原因
【修改方式】
- auth_service.py: login 方法返回字典添加 `"student_id": user["student_id"]`
- auth.py: 登录路由 success_response data 字典添加 `"student_id": result.get("student_id")`
- [x] 12.12 批量修复6个管理端/学生端Bug
【目标对象】`backend/models/user.py``backend/routes/admin.py``frontend/assets/js/admin.js``backend/services/conduct_service.py``backend/models/conduct.py``frontend/admin/history.php`
【修改目的】用户报告6个运行时Bug1) 修改密码失败2) 管理端学生列表4223) 添加管理员4044) admin.js报错null5) 历史记录5006) history.php学生列表不完整
【修改方式】
- user.py: get_by_user_id SQL 添加 password_hash 字段(修改密码时验证旧密码需要此字段)
- admin.py: page_size 上限 le=100→le=1000路由 /admin/add→/add, /admin/list→/list避免双重/api/admin前缀
- admin.js: showAddAdminModal 中 addAdminForm.reset() 改为 ?.reset()
- conduct_service.py: get_history 方法开头添加空字符串→None转换
- conduct.py: get_all_records 方法开头添加空字符串→None转换
- history.php: loadStudentsForSelect 传 {page_size: 1000}