Files
NextEdu/docs/architecture/audit/management-modules-audit.md
SpecialX 49291fcc31 refactor: fix all P0/P1/P2 bugs and architecture issues
Bug fixes (from bugs/ directory):

- Fix cross-module DB queries in 9 modules (homework, grades, parent, diagnostic, elective, proctoring, notifications, scheduling, classes) by routing through data-access functions

- Fix shared/lib <-> auth circular dependency via new session.ts module

- Fix divide-by-zero guard in grades data-access

- Fix audit export data truncation (paginated fetch for full datasets)

- Fix missing transactions in homework grading and elective lottery

- Fix missing revalidatePath in course-plans actions

- Fix frontend permission checks using requirePermission instead of requireAuth

- Fix dashboard role routing using session.user.roles

- Fix student auth pattern (migrate getDemoStudentUser to users module)

- Fix ActionState return type handling in components

Code quality fixes:

- Remove 60+ as type assertions (replace with type guards)

- Remove non-null assertions (use optional chaining or explicit checks)

- Convert dynamic imports to static imports (grades, diagnostic)

- Add React.cache() wrapping for read functions

- Parallelize independent queries with Promise.all

- Add explicit return types to 30+ arrow functions

- Replace any with unknown + type guards

- Fix import type for type-only imports

- Add Zod validation schemas for classes and diagnostic modules

- Extract duplicate code (normalizeRoleName, normalizeBcryptHash, logger IP extraction)

- Add console.error to silent catch blocks

- Fix permission naming consistency (exam:proctor_read -> exam:proctor:read)

Architecture doc sync:

- Update 004_architecture_impact_map.md and 005_architecture_data.json

- Update management-modules-audit.md for P0-7 cross-module fix

Moved deleted proctoring event route to deletes/ folder.
2026-06-19 05:13:34 +08:00

24 KiB
Raw Blame History

管理类模块职责与耦合审查报告

审查范围school / classes / scheduling / attendance / users / audit / course-plans / announcements 审查日期2026-06-17 审查依据单一职责原则SRP、模块边界清晰度、跨模块耦合度、企业级代码规范单文件 ≤ 1000 行硬性上限) 审查方式:只读源码分析,未修改任何代码


一、总体评价

模块 行数(最大文件) 职责单一性 耦合度 严重度
school 325 良好 🟢 合格
classes 2104 → 548 已修复 严重 🟡 需改进
scheduling 335data-access/ 266actions 算法独立 ⚠️ 🟡 需改进
attendance 271 良好 ⚠️ 🟢 合格
users 157import-export 已修复 ⚠️ 🟢 合格
audit 212 ⚠️ 部分违反 🟡 需改进
course-plans 320 良好 🟢 合格
announcements 197 已修复 🟢 合格

核心结论

  1. classes 模块是全项目耦合最严重的模块,单文件 2104 行远超 1000 行硬性上限,混入了 schedule、homework、grades 三个业务领域的逻辑。 已修复2026-06-17 拆分为 5 个文件,均 ≤800 行)
  2. users/import-export.ts 违反单一职责,同时处理导入、导出、用户创建、班级注册四类逻辑。 已修复2026-06-17 拆分为 import-export.ts + user-service.ts + class-registration.ts
  3. scheduling/auto-scheduler.ts 是算法独立化的优秀范例,纯函数、无 DB 访问、可独立测试。
  4. announcementsaudit 模块的 data-access 层不完整,写操作或导出逻辑泄漏到 actions 层。 announcements 已修复(写操作下沉 data-accessaudit 仍有导出逻辑内联。

二、模块审查明细

2.1 school 模块 — 🟢 合格

文件清单actions.ts (325 行) / data-access.ts (186 行) / schema.ts (51 行) / types.ts (42 行)

职责边界 清晰。仅负责 schools / academicYears / departments / grades 的 CRUD。

优点

  • actions.ts 每个 Action 职责单一:权限校验 → 解析 → DB 写入 → 审计日志 → revalidatePath。
  • data-access.ts 仅包含只读查询,无跨模块写入。
  • 权限校验完整SCHOOL_MANAGE / GRADE_MANAGE 均接入 requirePermission。

问题

  • ⚠️ 审计日志不一致:仅 school 实体的 create/update/delete 调用了 logAudit,而 department / academicYear / grade 的 CRUD 均未记录审计日志。
  • ⚠️ getStaffOptions / getGrades 直接查询 users / roles / usersToRoles 表(跨模块读),但属于展示用关联查询,可接受。

建议:为 department / academicYear / grade 的 CRUD 补充 logAudit 调用,保持审计一致性。


2.2 classes 模块 — 🟡 需改进(文件拆分已修复,跨模块耦合部分已修复)

文件清单actions.ts (676 行) / data-access.ts (548 行) / data-access-stats.ts (513 行) / data-access-schedule.ts (194 行) / data-access-students.ts (253 行) / data-access-admin.ts (406 行) / types.ts (201 行)

data-access.ts 已于 2026-06-17 拆分为 5 个文件,所有文件均 ≤800 行,通过 re-export 保持向后兼容。 P0-7 已于 2026-06-18 修复:data-access-stats.tsdata-access-students.ts 不再直查 homework/exams 表,改为调用 homework/data-access-classes.ts 暴露的函数。

2.2.1 职责混乱 — 混入三个外部业务领域(拆分后仍存在于子文件中)

data-access-*.ts 文件群仍承载了四个业务领域的逻辑已按职责分文件homework 跨域查询已通过 data-access-classes 封装):

文件 逻辑 应属模块
data-access.ts 教师身份解析、班级访问控制、班级 CRUD classes合理
data-access-students.ts 班级学生查询 classes合理
data-access-stats.ts getClassHomeworkInsights / getGradeHomeworkInsights 班级/年级作业洞察 classes P0-7 已修复:通过 homework/data-access-classes 获取数据)
data-access-schedule.ts 课表查询 getClassSchedule、课表项 CRUD scheduling
data-access-admin.ts getStudentsSubjectScores 学生科目成绩 classes P0-7 已修复:通过 homework/data-access-classes 获取数据)

关键问题P1-1 部分已修复):

  • P0-7 已修复:getClassHomeworkInsightsgetGradeHomeworkInsights 不再直接查询 homeworkAssignmentshomeworkSubmissionshomeworkAssignmentTargetshomeworkAssignmentQuestionsexams 表,改为调用 homework/data-access-classes.ts 暴露的函数(getAssignmentIdsForStudents/getHomeworkAssignmentsWithSubject/getHomeworkAssignmentsByIds/getAssignmentMaxScoreById/getAssignmentTargetCounts/getHomeworkSubmissionsForStudents)。
  • P0-7 已修复:getStudentsSubjectScores 不再直接关联 homeworkSubmissions + exams + subjects,改为调用 homework/data-access-classes.ts 暴露的函数(getAssignmentIdsForStudents/getPublishedHomeworkAssignmentsWithSubject/getHomeworkSubmissionsForAssignments)。
  • 课表 CRUDcreateClassScheduleItem / updateClassScheduleItem / deleteClassScheduleItem)写入 classScheduleP0-6 已统一 scheduling/data-access 为写入口,但 classes 侧的写函数仍存在(待后续迁移)。

2.2.2 types.ts 跨领域类型污染

types.ts 定义了本应属于其他模块的类型:

  • ClassHomeworkInsights / GradeHomeworkInsights / ClassHomeworkAssignmentStats / ScoreStats / AssignmentSummary — 应属 homework 模块
  • ClassScheduleItem / CreateClassScheduleItemInput / UpdateClassScheduleItemInput / StudentScheduleItem — 与 scheduling 模块的 types.ts 存在概念重叠

2.2.3 actions.ts 跨模块直接查询

actions.ts 多处直接查询 grades 表(属于 school 模块)进行权限验证,绕过了 school/data-access

行号 函数 直接查询
60-68 createTeacherClassAction db.select().from(grades) 校验 gradeHead 权限
186-194 createGradeClassAction db.select().from(grades) 校验年级管理权
241-249 updateGradeClassAction db.select().from(classes) 绕过自身 data-access
251-272 updateGradeClassAction db.select().from(grades) 校验源/目标年级
340-348 deleteGradeClassAction db.select().from(grades) 校验权限

问题:权限校验逻辑散落在 actions 层,既未下沉到 data-access也未通过 school 模块暴露的查询接口。

2.2.4 actions.ts 职责重复

存在三组近乎重复的 Action 集合:

  • Teacher 系列:createTeacherClassAction / updateTeacherClassAction / deleteTeacherClassAction
  • Admin 系列:createAdminClassAction / updateAdminClassAction / deleteAdminClassAction
  • Grade 系列:createGradeClassAction / updateGradeClassAction / deleteGradeClassAction

三者表单解析、字段校验逻辑高度重复,仅权限上下文不同。

2.2.5 data-access.ts 内调用 auth()

getSessionTeacherId(行 49-62在 data-access 层直接调用 auth() 获取会话,违反"data-access 不感知请求上下文"的分层原则。会话信息应由 actions 层传入。

2.2.6 组件层边界模糊

classes/components/ 包含 schedule-view.tsxschedule-filters.tsxclass-detail/class-schedule-widget.tsx 等课表相关组件,与 scheduling/components/ 的职责重叠。

整改建议(优先级 P0

  1. getClassHomeworkInsights / getGradeHomeworkInsights / getStudentsSubjectScores / getClassStudentSubjectScoresV2 迁移至 homework / grades 模块。
  2. 将课表 CRUDcreateClassScheduleItem 等)迁移至 scheduling 模块,统一 classSchedule 表的写入口。
  3. data-access.ts 拆分为 data-access.ts(班级 CRUD+ data-access-enrollments.ts(注册/邀请码)+ data-access-insights.ts(如暂不迁移则隔离)。
  4. 将权限校验中的 grades 表查询改为调用 school/data-access 暴露的接口。
  5. getSessionTeacherId 上移至 actions 层或 shared/lib。

2.3 scheduling 模块 — 🟡 需改进(算法层优秀,写入口已统一)

文件清单actions.ts (266 行) / auto-scheduler.ts (310 行) / data-access.ts (335 行) / schema.ts / types.ts

2.3.1 auto-scheduler.ts — 优秀范例

这是全项目算法独立化的最佳实践

  • 纯函数:findOptimalSlot / validateSchedule / autoSchedule / buildDefaultTimeSlots
  • "server-only" 副作用,无 DB 访问,无 import { db }
  • 仅依赖 types.ts 的类型导入
  • 可独立单元测试:给定输入即可断言输出,无需 mock 数据库
  • 算法清晰:贪心 + 约束检查(午餐、每日上限、教师/教室冲突、避免连排)

建议:以此为模板,指导其他模块的算法抽取(如 homework 的批改评分算法、grades 的统计算法)。

2.3.2 actions.ts — 跨模块直接查询(写入口已统一)

行号 函数 问题
110-116 autoScheduleAction 直接 db.select().from(users) 查询教师,绕过 data-access 的 getTeachersForSchedulingP1-2 待修复)
168-180 applyAutoScheduleAction 直接 db.transaction 写入 classSchedule 已修复P0-6改为调用 replaceClassSchedule

applyAutoScheduleAction 的直接 transaction 写入问题已于 P0-6 修复,现在通过 scheduling/data-access.tsreplaceClassSchedule() 统一写入。但 autoScheduleAction 仍直接查询 users 表P1-2 待修复)。

2.3.3 data-access.ts — 跨模块读查询 + 统一写入口

包含 getAdminClassesForScheduling / getTeachersForScheduling / getClassroomsForScheduling / getClassSubjectsForScheduling 四个辅助查询,直接访问 classes / classSubjectTeachers / subjects / users / classrooms 表。

P0-6 后新增 replaceClassSchedule() 作为 classSchedule 表的统一写入口。

这些是排课场景的只读辅助查询,耦合度可接受,但理想情况下应通过各所属模块的 data-access 暴露接口。

2.3.4 actions.ts 末尾 re-export

export { getSchedulingRules, getScheduleChanges, ... } from "./data-access"

actions 层 re-export data-access 函数是反模式,应让消费方直接从 data-access 导入。P2 待修复)

整改建议

  1. applyAutoScheduleAction 中的 classSchedule 写入逻辑下沉到 data-access 已完成P0-6
  2. autoScheduleAction 中的 users 查询改用 getTeachersForSchedulingP1-2 待修复)
  3. 移除 actions.ts 末尾的 re-exportP2 待修复)

2.4 attendance 模块 — 🟢 合格(结构典范)

文件清单actions.ts (271 行) / data-access.ts (271 行) / data-access-stats.ts (145 行) / schema.ts / types.ts

优点

  • stats 独立拆分data-access-stats.ts 专门承载统计逻辑,是 classes 模块应学习的拆分模式。
  • actions.ts 每个 Action 职责单一:权限校验 → 解析 → 委托 data-access → revalidate。
  • DataScope 数据范围控制完整接入,支持 6 种 scope 类型。
  • 无写操作泄漏到 actions 层。

问题

  • ⚠️ getClassStudentsForAttendancedata-access.ts 行 206-217直接查询 classEnrollments 表获取班级学生列表,属于 classes 模块数据,应通过 classes/data-access 暴露的接口调用。
  • ⚠️ data-access.ts 和 data-access-stats.ts 均直接 JOIN classes 表获取班级名称(只读展示,可接受)。

整改建议:在 classes/data-access 暴露 getClassStudentIds(classId) 接口,供 attendance 调用。


2.5 users 模块 — 🟢 合格(已修复)

文件清单actions.ts (131 行) / data-access.ts (133 行) / import-export.ts (157 行) / user-service.ts (82 行) / class-registration.ts (21 行)

import-export.ts 已于 2026-06-17 拆分为 3 个文件,四重职责已分离。

2.5.1 import-export.ts — 四重职责已修复

原文件同时承载四类互不相关的职责,现已拆分:

文件 职责 行数
import-export.ts 文件解析与生成(generateUserImportTemplate / parseUserImportData / exportUsersToExcel 157
user-service.ts 用户创建(含密码哈希 + 角色绑定) 82
class-registration.ts 班级注册(调用 classes/data-access 21

已修复问题

  1. 导入与导出未分离 → import-export.ts 仅负责文件解析与生成
  2. 用户创建逻辑泄漏 → 迁移至 user-service.ts
  3. 跨模块写 classEnrollments → 迁移至 class-registration.ts,调用 classes/data-access
  4. 跨模块读 classes → 通过 classes/data-access 暴露接口调用

2.5.2 actions.ts — 绕过 data-access部分修复

updateUserProfile(行 29-51仍直接 db.update(users) 写入数据库,绕过了 data-access 层。P1-2 待修复)

2.5.3 data-access.ts — 已扩展

从 71 行扩展到 133 行,包含 getUserProfile 及 dashboard 聚合查询函数 getUsersDashboardStats。用户创建、更新等写操作部分仍在 user-service.ts 中P1-2 待进一步下沉)。

整改建议(优先级 P1

  1. import-export.ts 拆分为 import.tsexport.ts 已完成(采用按职责拆分)
  2. batchImportUsers 中的用户创建逻辑迁移至 user-service.ts 已完成
  3. 将 classEnrollments 写入改为调用 classes/data-access 已完成
  4. updateUserProfile 的 DB 写入下沉到 data-accessP1-2 待修复)

2.6 audit 模块 — 🟡 需改进

文件清单actions.ts (212 行) / data-access.ts (260 行) / types.ts

问题

  • ⚠️ Excel 导出逻辑内联在 actions 层exportAuditLogsAction / exportLoginLogsAction / exportDataChangeLogsAction 三个 Action 各自内联了 exportToExcel 调用及完整的列定义(表头、宽度、字段映射),每个约 40 行。这是展示/格式化逻辑,应抽取到独立的 export.ts
  • ⚠️ 三个导出 Action 的结构高度重复(权限校验 → 查询 → 构造 columns → exportToExcel → 返回 buffer可抽象为通用导出工厂。

优点

  • data-access.ts 职责清晰,仅包含日志查询,无跨模块问题。
  • 分页逻辑统一clampPage / clampPageSize

整改建议:抽取 export.ts,封装 exportAuditLogsToExcel(items) / exportLoginLogsToExcel(items) / exportDataChangeLogsToExcel(items)actions 层仅负责编排。


2.7 course-plans 模块 — 🟢 合格

文件清单actions.ts (265 行) / data-access.ts (320 行) / schema.ts / types.ts

优点

  • actions.ts 使用 handleError / revalidatePlanPaths 辅助函数消除重复,是 actions 层的良好范例
  • data-access.ts 职责清晰:课程计划 CRUD + 周计划项 CRUD + 排序。
  • 跨模块查询仅为 classes / subjects / users 的 LEFT JOIN 取展示名称(只读,可接受)。

小问题

  • ⚠️ getSubjectOptions(行 310-320直接查询 subjectssubjects 无独立模块,暂可接受。

结论:该模块结构可作为其他模块的参考模板。


2.8 announcements 模块 — 🟢 合格(已修复)

文件清单actions.ts (197 行) / data-access.ts (171 行) / schema.ts / types.ts

写操作已下沉到 data-access 层2026-06-17commit 84d6636

核心问题 — 写操作泄漏到 actions 层 已修复

data-access.ts 仅包含两个只读函数(getAnnouncements / getAnnouncementById),所有写操作均直接在 actions.ts 中执行 db.insert / db.update / db.delete

已完成修复data-access.ts 从 120 行扩展到 171 行,新增 5 个写函数:

  • createAnnouncement
  • updateAnnouncement
  • deleteAnnouncement
  • publishAnnouncement
  • archiveAnnouncement

actions.ts 从 242 行降至 197 行,仅保留权限校验 + 解析 + 委托调用。

其他问题

  • ⚠️ 死代码:updateAnnouncementAction 行 108 计算 wasPublished,行 135 void wasPublished 显式丢弃未实际使用。P2 待清理)
  • ⚠️ getAnnouncementsAction 使用 requireAuth() 而非 requirePermission(ANNOUNCEMENT_READ)与其他模块的权限模式不一致。P2 待统一)

整改建议

  1. 在 data-access.ts 补充 createAnnouncement / updateAnnouncement / deleteAnnouncement / publishAnnouncement / archiveAnnouncement 写函数 已完成
  2. actions 层仅保留权限校验 + 解析 + 委托调用 已完成
  3. 清理 wasPublished 死代码P2 待处理)

三、跨模块直接查询汇总

3.1 跨模块写操作(严重)

源文件 目标表 操作 应通过 状态
classes/data-access.ts classSchedule CRUD scheduling/data-access ⚠️ P0-6 已统一 scheduling 为写入口classes 侧写函数待迁移
scheduling/actions.ts classSchedule delete + insert scheduling/data-access 已修复P0-6改用 replaceClassSchedule
users/import-export.ts classEnrollments insert classes/data-access 已修复(迁移至 class-registration.ts
users/import-export.ts users, usersToRoles insert users/data-access 已修复(迁移至 user-service.ts
announcements/actions.ts announcements insert/update/delete announcements/data-access 已修复P1-2写操作下沉
users/actions.ts users update users/data-access 待修复P1-2

classSchedule 表写入口已于 P0-6 统一到 scheduling/data-access.tsreplaceClassSchedule()

3.2 跨模块读操作(需评估)

源文件 目标表 用途 评估
classes/data-access.ts homeworkAssignments, homeworkSubmissions, homeworkAssignmentTargets, homeworkAssignmentQuestions, exams 作业洞察统计 应迁移至 homework
classes/data-access.ts grades, schools 年级/学校关联 ⚠️ 应通过 school data-access
classes/actions.ts grades 权限校验 ⚠️ 应通过 school data-access
scheduling/data-access.ts classes, classSubjectTeachers, subjects, users, classrooms 排课辅助查询 🟡 可接受(只读)
attendance/data-access.ts classEnrollments 获取班级学生 ⚠️ 应通过 classes data-access
users/import-export.ts classes 邀请码查询 ⚠️ 应通过 classes data-access
course-plans/data-access.ts classes, subjects, users 展示名称 JOIN 🟢 可接受

四、actions 层多职责问题汇总

Action 混入职责 应拆分
users/import-export.ts: batchImportUsers 用户创建 + 密码哈希 + 角色绑定 + 班级注册 拆为 createUser + enrollStudent
classes/actions.ts: updateGradeClassAction 班级更新 + 科任教师批量分配 拆为 updateClass + setClassSubjectTeachers已部分拆分但仍在同一 Action 内编排)
classes/actions.ts: updateAdminClassAction 同上 同上
audit/actions.ts: exportAuditLogsAction 查询 + Excel 列定义 + 导出 拆为 query + exportAuditLogsToExcel
audit/actions.ts: exportLoginLogsAction 同上 同上
audit/actions.ts: exportDataChangeLogsAction 同上 同上
announcements/actions.ts: createAnnouncementAction 解析 + publishedAt 计算 + DB 写入 DB 写入下沉 data-access

五、整改优先级

P0 — 立即整改(影响数据完整性 & 严重违反规范)

  1. classes/data-access.ts 拆分2104 行远超硬性上限,且混入 homework/scheduling/grades 三个领域。优先迁移 getClassHomeworkInsights / getGradeHomeworkInsights532 行)至 homework 模块。 已完成(拆分为 5 个文件)
  2. 统一 classSchedule 表写入口classes 与 scheduling 两个模块对该表有写权限,需协商归属并收敛为单一写入口。 已完成P0-6replaceClassSchedule 统一入口)

P1 — 尽快整改(模块边界违反)

  1. users/import-export.ts 拆分:分离导入/导出,用户创建逻辑下沉 data-accessclassEnrollments 写入改调 classes 接口。 已完成
  2. announcements 写操作下沉:在 data-access 补充写函数actions 仅编排。 已完成
  3. classes/actions.ts 权限校验grades 表查询改通过 school/data-access 接口。P1-1 待处理)

P2 — 持续优化(代码质量)

  1. audit 导出逻辑抽取:内联的 Excel 列定义移至独立 export.ts。
  2. school 审计日志补全department/academicYear/grade 的 CRUD 补充 logAudit。
  3. attendance 跨模块查询getClassStudentsForAttendance 改调 classes 接口。
  4. scheduling/actions.ts:移除 re-exportautoScheduleAction 的 users 查询下沉 data-accessP1-2
  5. announcements 死代码清理:移除 void wasPublished

六、优秀实践(建议推广)

实践 模块 说明
算法纯函数化 scheduling/auto-scheduler.ts 无 DB 依赖,可独立测试,应作为算法抽取模板
stats 文件拆分 attendance/data-access-stats.ts 统计逻辑独立成文件classes 应效仿
actions 辅助函数 course-plans/actions.ts handleError / revalidatePlanPaths 消除重复
DataScope 接入 attendance/actions.ts 6 种数据范围完整支持
权限统一接入 school / attendance / course-plans 全部 Action 使用 requirePermission

七、附:文件行数统计

文件 行数 上限 状态
classes/data-access.ts 2104 → 548 1000 已拆分5 个文件均 ≤800 行)
classes/data-access-stats.ts 531 800建议 🟢 合规
classes/data-access-admin.ts 406 800建议 🟢 合规
classes/data-access-students.ts 244 800建议 🟢 合规
classes/data-access-schedule.ts 194 800建议 🟢 合规
classes/actions.ts 676 800建议 🟢 合规
classes/types.ts 201 无限制 🟢 合规
school/actions.ts 325 800建议 🟢 合规
school/data-access.ts 186 800建议 🟢 合规
scheduling/auto-scheduler.ts 310 无限制 🟢 合规
scheduling/actions.ts 266 800建议 🟢 合规
scheduling/data-access.ts 335 800建议 🟢 合规
attendance/actions.ts 271 800建议 🟢 合规
attendance/data-access.ts 271 800建议 🟢 合规
attendance/data-access-stats.ts 145 800建议 🟢 合规
users/import-export.ts 157 800建议 🟢 合规(已拆分)
users/user-service.ts 82 800建议 🟢 合规(新增)
users/class-registration.ts 21 800建议 🟢 合规(新增)
users/actions.ts 131 800建议 🟢 合规
users/data-access.ts 133 800建议 🟢 合规
audit/actions.ts 212 800建议 🟢 合规
audit/data-access.ts 260 800建议 🟢 合规
course-plans/actions.ts 265 800建议 🟢 合规
course-plans/data-access.ts 320 800建议 🟢 合规
announcements/actions.ts 197 800建议 🟢 合规
announcements/data-access.ts 171 800建议 🟢 合规

所有文件均已在 1000 行硬性上限内。原 classes/data-access.ts2104 行)已拆分为 5 个文件。


报告结束。本审查未修改任何源代码。