v1.0正式版

This commit is contained in:
Sea-Studio
2026-04-19 18:13:06 +08:00
parent fe411724f4
commit 5b47b5f60f
5 changed files with 6 additions and 493 deletions

View File

@@ -1,59 +0,0 @@
# 变更:管理端多项功能修复与改版
## 原因
管理端存在多项问题需要修复跨域配置导致API调用失败、导航栏各页面不一致、作业模块用途不符需求、考勤模块交互需改版、家长手机号需要权限控制。
## 变更内容
### 1. 跨域问题修复(确认)
- 之前的 500 错误已通过添加 `PermissionChecker.get_user_class_id` 方法修复
- CORS 中间件配置本身正确(`CORSMiddleware` 已注册且包含 OPTIONS 方法)
- `AuthMiddleware` 虽已导入但未在 `main.py` 中注册为全局中间件,不影响当前功能(认证由路由层 `get_current_user` 处理)
- 后端 `.env` 文件由服务器单独配置,不在版本控制中,无需添加默认值
### 2. 统一导航栏
- 创建 `frontend/includes/nav.php` 共享模板,所有 admin 页面统一引入
- 导航项角色条件统一为:操行分管理=班主任/班长,作业管理=班主任/学习委员,考勤管理=班主任/考勤委员,科目管理=班主任/学习委员,管理员管理=班主任
- 修复 `dashboard.php` 密码链接拼写错误 `passwork.php``password.php`
- 接受参数:`$role``$current_page`(用于 active 状态)
### 3. 作业管理改版(仅加减操行分)
- 移除作业发布功能(创建作业、查看提交记录)
- 改为类似操行分管理页面的交互:选择学生 + 选择扣分类型(未交作业/迟交作业等)+ 原因 + 提交扣分
- 前端 `homework.php` 完全重写:学生列表表格 + 批量加减分模态框,扣分类型为作业相关(未交-2分、迟交-1分
- 后端保留 `add_conduct_points` 接口(已有),通过 `related_type="homework"` 关联
- 角色权限不变:班主任 + 学习委员
### 4. 考勤管理改版(学生方格扣分制)
- 前端 `attendance.php` 重写UI
- 顶部日期选择器
- 主体区域学生方格网格flex-wrap 布局每行7个
- 每个方格显示学生姓名,点击可选中(高亮变红表示缺勤扣分)
- 底部工具栏:全选/取消、批量选择状态(缺勤/迟到/请假)、提交按钮
- 下方保留历史记录表格
- 后端考勤逻辑不变,新增批量提交支持(或前端循环调用现有单个添加接口)
- 扣分规则不变:缺勤-5分、迟到-2分、请假-1分
### 5. 家长手机号权限控制
- `frontend/admin/students.php` 表格中 `家长手机号` 列仅 `班主任` 角色可见
- 非班主任角色显示为 `***` 或隐藏该列
- 新增/导入学生表单中的手机号字段保持不变(任何有学生管理权限的角色都能录入)
### 6. 附带修复
- 修复 `dashboard.php` 密码链接 `passwork.php``password.php`(如果导航模板方案未覆盖的话)
- 统一所有角色判断为 `学习委员`(当前 homework.php 用 `科代表`
## 影响
- **受影响的规范**:管理端导航、作业管理、考勤管理、学生管理
- **受影响的代码**
- `frontend/includes/nav.php`: 新建 - 统一导航栏模板
- `frontend/admin/dashboard.php`: 移除硬编码导航栏,引入 nav.php
- `frontend/admin/students.php`: 移除硬编码导航栏,引入 nav.php手机号列增加角色判断
- `frontend/admin/conduct.php`: 移除硬编码导航栏,引入 nav.php
- `frontend/admin/homework.php`: 完全重写 - 改为加减操行分模式
- `frontend/admin/attendance.php`: 完全重写 - 改为学生方格扣分制
- `frontend/admin/history.php`: 移除硬编码导航栏,引入 nav.php
- `frontend/admin/subjects.php`: 移除硬编码导航栏,引入 nav.php
- `frontend/admin/admins.php`: 移除硬编码导航栏,引入 nav.php
- `frontend/admin/password.php`: 移除硬编码导航栏,引入 nav.php
- `frontend/assets/css/admin.css`: 新增学生方格网格样式

View File

@@ -1,378 +0,0 @@
## 实施
### 阶段 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}
- [x] 12.13 修复强制改密"原密码错误" + 科目管理/管理员管理500错误确认
【目标对象】`backend/schemas/auth.py``backend/services/auth_service.py``backend/routes/auth.py``frontend/student/dashboard.php`
【修改目的】用户报告3个Bug1) 学生端首次登录强制改密时没有原密码输入框,但提交后报"原密码错误"2) 科目管理页/api/subject/list返回5003) 管理员管理页/api/admin/list返回500
【修改方式】
- schemas/auth.py: ChangePasswordRequest中old_password改为可选(default="")新增force字段(bool, default=False)
- auth_service.py: change_password方法新增force参数当force=True时跳过旧密码验证
- auth.py: change_password路由从请求中读取force参数传递给服务层
- dashboard.php: 强制改密请求中添加force:true, old_password设为空字符串
- 科目管理500和管理员管理500: 经代码审查确认代码逻辑正确SQL、路由、模型均无问题500错误为后端服务未重启导致旧代码仍在运行
- [x] 12.14 添加全局异常处理器 + 4个500路由添加try-except + 历史记录page_size上限修复
【目标对象】`backend/main.py``backend/routes/subject.py``backend/routes/admin.py``backend/routes/student.py`
【修改目的】用户确认后端已重启但仍报5004个路由持续返回500 Internal Server Error科目管理、管理员管理、历史记录、学生端操行分。无法直接查看后端日志需通过全局异常处理器和路由级try-except捕获具体错误原因
【修改方式】
- main.py: 添加全局异常处理器 global_exception_handler捕获所有未处理异常返回包含str(exc)的message和可选的traceback detail仅DEBUG模式
- subject.py: get_subjects路由添加try-except新增logger导入
- admin.py: get_admins和get_conduct_history路由添加try-exceptget_conduct_history的page_size参数le=100→le=1000
- student.py: get_conduct_history路由添加try-except新增logger导入

View File

@@ -1,19 +0,0 @@
# 变更修复登录状态问题Session与Token同步
## 原因
用户反馈"登录后无法保存token登录后跳转回登录页"。经深度探索发现,系统存在 SessionPHP后端+ TokenAPI后端双轨制认证架构
- **登录API** (`/api/auth/login`) 返回 JWT Token前端存入 localStorage
- **Dashboard页面** 检查 PHP Session (`$_SESSION['user_id']`)
- **问题根因**:登录成功时只设置了 localStorage 中的 Token没有同步设置 PHP Session导致 dashboard 页面的 Session 检查失败,跳转回登录页
## 变更内容
- **新建** `frontend/api/save_session.php`PHP Session 保存接口
- **修改** `frontend/index.php`:登录成功后调用 save_session.php 同步设置 PHP Session
- **修改** `frontend/config.php`:添加 API_BASE_URL 配置(如果缺失)
## 影响
- **受影响的规范**:登录认证流程
- **受影响的代码**
- `frontend/index.php`登录成功后增加同步Session逻辑
- `frontend/api/save_session.php`新建用于接收登录信息并设置PHP Session

View File

@@ -1,37 +0,0 @@
## 实施
- [ ] 1.1 创建 `frontend/api/save_session.php` Session 同步接口
【目标对象】`frontend/api/save_session.php`
【修改目的】提供接收前端登录信息并设置 PHP Session 的接口,解决登录后跳转回登录页的问题
【修改方式】新建 PHP 文件,实现 Session 设置逻辑
【相关依赖】`frontend/config.php`(用于引入必要的常量定义)
【修改内容】
- 文件顶部添加 `<?php` 声明
- 引入 config.php`require_once __DIR__ . '/../config.php';`
- 启用 Session`session_start();`
- 设置 CORS 头(如果需要跨域):`header('Content-Type: application/json');`
- 接收 POST 请求的 JSON 数据user_id, user_type, username, real_name, student_id
- 将用户数据存入 `$_SESSION` 变量
- 返回 JSON 响应:`{success: true}` 或错误信息
- 添加错误处理JSON 解析失败、缺少必要字段等情况的处理
- [ ] 1.2 修改 `frontend/index.php` 登录成功逻辑
【目标对象】`frontend/index.php:84-87`localStorage.setItem 和 window.location.href 所在区域)
【修改目的】登录成功后同步设置 PHP Session解决 dashboard 页面 Session 检查失败的问题
【修改方式】在 window.location.href 跳转前,增加 fetch 调用 save_session.php
【相关依赖】`frontend/api/save_session.php`
【修改内容】
-`localStorage.setItem(USER_STORAGE_KEY, JSON.stringify(data.data));` 之后、`window.location.href = data.data.redirect;` 之前
- 增加 `await fetch('/api/save_session.php', {...})` 调用
- 将登录 API 返回的 user_id、user_type、username、real_name、student_id 等数据作为请求体发送
- 使用 async/await 等待 Session 设置完成后再执行跳转
- 添加错误处理fetch 失败时记录错误但不影响跳转流程
- [ ] 1.3 验证修复效果
【验证方式】
- 清除浏览器 localStorage 和 Cookie
- 使用学生账号/家长账号/管理员账号执行登录操作
- 登录成功后应正常进入对应角色的 dashboard 页面,不再跳转回登录页
- 检查浏览器开发者工具 Network 面板,确认 `/api/save_session.php` 请求成功HTTP 200
- 检查浏览器 Cookies 中有 PHP Session ID如 PHPSESSID
- 在 dashboard 页面刷新,确认 Session 认证仍然有效

View File

@@ -222,6 +222,12 @@ classmanager/
- 家长端详见 [parent.md](docs/parent.md)
- 管理端详见 [admin.md](docs/admin.md)
## 版本
| 版本 | 发布日期 | 说明 |
|------|---------|------|
| v1.0 | 2026.4.19 | 初始版本发布,包含基础功能 |
## 许可证
本项目使用 [MIT License](LICENSE) 许可证