- 全项目逐文件审查: 4 份审计报告(shared/core-business/management/new-modules) - 重写 004 架构影响地图: 图优先 + 模块依赖图 + 数据流 + 调用链 + 问题分级 - 更新 005 结构化数据: 新增 architectureOverview/moduleDependencyGraph/knownIssues/dbTables 节点 - 更新 006 功能清单: 143 项功能标注实现状态, P0 覆盖率 80%->92% - 更新 007 差距审计: v2->v3, P0 完成 69%->84%, 新增架构技术债章节 - 更新 001 项目概览: 6 角色/54 权限/26 模块/54 表 - 新增 docs/README.md 文档索引 - 归档 11 份过时文档(002x2/003/designx8) 标注 - 更新 work_log
22 KiB
管理类模块职责与耦合审查报告
审查范围:school / classes / scheduling / attendance / users / audit / course-plans / announcements 审查日期:2026-06-17 审查依据:单一职责原则(SRP)、模块边界清晰度、跨模块耦合度、企业级代码规范(单文件 ≤ 1000 行硬性上限) 审查方式:只读源码分析,未修改任何代码
一、总体评价
| 模块 | 行数(最大文件) | 职责单一性 | 耦合度 | 严重度 |
|---|---|---|---|---|
| school | 325 | ✅ 良好 | ✅ 低 | 🟢 合格 |
| classes | 2104 | ❌ 严重违反 | ❌ 严重 | 🔴 严重 |
| scheduling | 310(算法)/ 302(actions) | ✅ 算法独立 | ⚠️ 中 | 🟡 需改进 |
| attendance | 271 | ✅ 良好 | ⚠️ 中 | 🟢 合格 |
| users | 291(import-export) | ❌ 违反 | ❌ 高 | 🟠 较严重 |
| audit | 212 | ⚠️ 部分违反 | ✅ 低 | 🟡 需改进 |
| course-plans | 320 | ✅ 良好 | ✅ 低 | 🟢 合格 |
| announcements | 242 | ⚠️ 部分违反 | ✅ 低 | 🟡 需改进 |
核心结论:
classes模块是全项目耦合最严重的模块,单文件 2104 行远超 1000 行硬性上限,混入了 schedule、homework、grades 三个业务领域的逻辑。users/import-export.ts违反单一职责,同时处理导入、导出、用户创建、班级注册四类逻辑。scheduling/auto-scheduler.ts是算法独立化的优秀范例,纯函数、无 DB 访问、可独立测试。announcements和audit模块的 data-access 层不完整,写操作或导出逻辑泄漏到 actions 层。
二、模块审查明细
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 (765 行) / data-access.ts (2104 行) / types.ts (201 行)
⚠️
data-access.ts达 2104 行,超出 1000 行硬性上限 2 倍,违反项目代码质量规则。
2.2.1 职责混乱 — 混入三个外部业务领域
data-access.ts 实际承载了四个业务领域的逻辑:
| 行范围 | 逻辑 | 应属模块 |
|---|---|---|
| 49-145 | 教师身份解析、班级访问控制 | classes(合理) |
| 156-601 | 班级列表/学生/教师查询 | classes(合理) |
| 697-735 | 课表查询 getClassSchedule |
scheduling |
| 760-998 | getClassHomeworkInsights 班级作业洞察(238 行) |
homework |
| 1006-1300 | getGradeHomeworkInsights 年级作业洞察(294 行) |
homework |
| 1302-1775 | 班级 CRUD + 邀请码 + 注册 | classes(合理) |
| 1838-1968 | 课表项 CRUD createClassScheduleItem 等 |
scheduling |
| 1970-2103 | getStudentsSubjectScores 学生科目成绩(133 行) |
grades / homework |
关键问题:
getClassHomeworkInsights和getGradeHomeworkInsights合计 532 行作业统计逻辑,直接查询homeworkAssignments、homeworkSubmissions、homeworkAssignmentTargets、homeworkAssignmentQuestions、exams五张表,属于 homework 模块的核心业务,不应存在于 classes 模块。- 课表 CRUD(
createClassScheduleItem/updateClassScheduleItem/deleteClassScheduleItem)与 scheduling 模块的applyAutoScheduleAction写入同一张classSchedule表,两个模块对同一张表有写权限,边界严重模糊。 getStudentsSubjectScores直接关联homeworkSubmissions+exams+subjects计算学生科目分数,属于成绩分析逻辑。
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.tsx、schedule-filters.tsx、class-detail/class-schedule-widget.tsx 等课表相关组件,与 scheduling/components/ 的职责重叠。
整改建议(优先级 P0):
- 将
getClassHomeworkInsights/getGradeHomeworkInsights/getStudentsSubjectScores/getClassStudentSubjectScoresV2迁移至 homework / grades 模块。 - 将课表 CRUD(
createClassScheduleItem等)迁移至 scheduling 模块,统一classSchedule表的写入口。 - 将
data-access.ts拆分为data-access.ts(班级 CRUD)+data-access-enrollments.ts(注册/邀请码)+data-access-insights.ts(如暂不迁移则隔离)。 - 将权限校验中的
grades表查询改为调用 school/data-access 暴露的接口。 - 将
getSessionTeacherId上移至 actions 层或 shared/lib。
2.3 scheduling 模块 — 🟡 需改进(算法层优秀)
文件清单:actions.ts (302 行) / auto-scheduler.ts (310 行) / data-access.ts (272 行) / 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 的 getTeachersForScheduling |
| 168-180 | applyAutoScheduleAction |
直接 db.transaction 写入 classSchedule 表,绕过 data-access 且跨模块写 classes 领域的表 |
applyAutoScheduleAction 的问题尤为突出:它删除并重建 classSchedule 表数据,但该写操作既未经过 scheduling/data-access,也未经过 classes/data-access,形成第三个对 classSchedule 表的写入口(另两个在 classes/data-access 的 createClassScheduleItem 等)。
2.3.3 data-access.ts — 跨模块读查询
包含 getAdminClassesForScheduling / getTeachersForScheduling / getClassroomsForScheduling / getClassSubjectsForScheduling 四个辅助查询,直接访问 classes / classSubjectTeachers / subjects / users / classrooms 表。
这些是排课场景的只读辅助查询,耦合度可接受,但理想情况下应通过各所属模块的 data-access 暴露接口。
2.3.4 actions.ts 末尾 re-export
export { getSchedulingRules, getScheduleChanges, ... } from "./data-access"
actions 层 re-export data-access 函数是反模式,应让消费方直接从 data-access 导入。
整改建议:
- 将
applyAutoScheduleAction中的classSchedule写入逻辑下沉到 data-access(或与 classes 协商统一写入口)。 - 将
autoScheduleAction中的 users 查询改用getTeachersForScheduling。 - 移除 actions.ts 末尾的 re-export。
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 层。
问题:
- ⚠️
getClassStudentsForAttendance(data-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 (151 行) / data-access.ts (71 行) / import-export.ts (291 行)
2.5.1 import-export.ts — 四重职责混合
该文件同时承载四类互不相关的职责:
| 行范围 | 职责 | 应属位置 |
|---|---|---|
| 54-73 | generateUserImportTemplate 生成导入模板 |
export 模块 |
| 78-111 | parseUserImportData 解析+校验导入数据 |
import 模块 |
| 116-207 | batchImportUsers 批量导入(含用户创建) |
data-access |
| 212-291 | exportUsersToExcel 导出用户列表 |
export 模块 |
核心问题:
- 导入与导出未分离:应拆分为
import.ts与export.ts。 - 用户创建逻辑泄漏:
batchImportUsers(行 158-167)直接db.insert(users)+ 密码哈希 +db.insert(usersToRoles),这是 data-access 层的职责,不应存在于 import-export 工具文件中。 - 跨模块写 classEnrollments:行 174-184,
batchImportUsers直接db.insert(classEnrollments)将学生注册到班级,这是 classes 模块的写操作,严重违反模块边界。 - 跨模块读 classes:行 130-137,直接查询
classes表根据邀请码查找班级,应通过 classes/data-access 暴露接口。
2.5.2 actions.ts — 绕过 data-access
updateUserProfile(行 29-51)直接 db.update(users) 写入数据库,绕过了 data-access 层。data-access.ts 仅有 getUserProfile 一个读函数,写操作缺失。
2.5.3 data-access.ts — 过于单薄
仅 71 行,只包含 getUserProfile。用户创建、更新、角色绑定等写操作均未在此层实现。
整改建议(优先级 P1):
- 将
import-export.ts拆分为import.ts(解析+校验)和export.ts(模板生成+列表导出)。 - 将
batchImportUsers中的用户创建逻辑迁移至data-access.ts的createUser函数,import.ts 仅负责编排。 - 将 classEnrollments 写入改为调用 classes/data-access 的注册接口(如
enrollStudentByInvitationCode)。 - 将
updateUserProfile的 DB 写入下沉到 data-access。
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)直接查询subjects表,subjects 无独立模块,暂可接受。
结论:该模块结构可作为其他模块的参考模板。
2.8 announcements 模块 — 🟡 需改进
文件清单:actions.ts (242 行) / data-access.ts (120 行) / schema.ts / types.ts
核心问题 — 写操作泄漏到 actions 层:
data-access.ts 仅包含两个只读函数(getAnnouncements / getAnnouncementById),所有写操作均直接在 actions.ts 中执行 db.insert / db.update / db.delete:
| Action | 直接 DB 操作 | 行号 |
|---|---|---|
createAnnouncementAction |
db.insert(announcements) |
53-63 |
updateAnnouncementAction |
db.update(announcements) |
118-130 |
deleteAnnouncementAction |
db.delete(announcements) |
154 |
publishAnnouncementAction |
db.update(announcements) |
176-183 |
archiveAnnouncementAction |
db.update(announcements) |
206-212 |
这违反了"data-access 层封装所有 DB 操作"的分层约定,导致:
- 写逻辑无法被其他 server 端代码复用
- publishedAt 计算逻辑散落在 actions 中,难以测试
其他问题:
- ⚠️ 死代码:
updateAnnouncementAction行 108 计算wasPublished,行 135void wasPublished显式丢弃,未实际使用。 - ⚠️
getAnnouncementsAction使用requireAuth()而非requirePermission(ANNOUNCEMENT_READ),与其他模块的权限模式不一致。
整改建议:
- 在 data-access.ts 补充
createAnnouncement/updateAnnouncement/deleteAnnouncement/publishAnnouncement/archiveAnnouncement写函数。 - actions 层仅保留权限校验 + 解析 + 委托调用。
- 清理
wasPublished死代码。
三、跨模块直接查询汇总
3.1 跨模块写操作(严重)
| 源文件 | 目标表 | 操作 | 应通过 |
|---|---|---|---|
| classes/data-access.ts | classSchedule | CRUD | scheduling/data-access |
| scheduling/actions.ts | classSchedule | delete + insert | scheduling/data-access |
| users/import-export.ts | classEnrollments | insert | classes/data-access |
| users/import-export.ts | users, usersToRoles | insert | users/data-access |
| announcements/actions.ts | announcements | insert/update/delete | announcements/data-access |
| users/actions.ts | users | update | users/data-access |
⚠️
classSchedule表存在 三个写入口(classes/data-access、scheduling/actions、scheduling/data-access 间接),是数据完整性高风险点。
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 — 立即整改(影响数据完整性 & 严重违反规范)
- classes/data-access.ts 拆分:2104 行远超硬性上限,且混入 homework/scheduling/grades 三个领域。优先迁移
getClassHomeworkInsights/getGradeHomeworkInsights(532 行)至 homework 模块。 - 统一 classSchedule 表写入口:classes 与 scheduling 两个模块对该表有写权限,需协商归属并收敛为单一写入口。
P1 — 尽快整改(模块边界违反)
- users/import-export.ts 拆分:分离导入/导出,用户创建逻辑下沉 data-access,classEnrollments 写入改调 classes 接口。
- announcements 写操作下沉:在 data-access 补充写函数,actions 仅编排。
- classes/actions.ts 权限校验:grades 表查询改通过 school/data-access 接口。
P2 — 持续优化(代码质量)
- audit 导出逻辑抽取:内联的 Excel 列定义移至独立 export.ts。
- school 审计日志补全:department/academicYear/grade 的 CRUD 补充 logAudit。
- attendance 跨模块查询:
getClassStudentsForAttendance改调 classes 接口。 - scheduling/actions.ts:移除 re-export,DB 写入下沉 data-access。
- 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 | 1000 | 🔴 超限 2.1x |
| classes/actions.ts | 765 | 800(建议) | 🟢 合规 |
| classes/types.ts | 201 | 无限制 | 🟢 合规 |
| school/actions.ts | 325 | 800(建议) | 🟢 合规 |
| school/data-access.ts | 186 | 800(建议) | 🟢 合规 |
| scheduling/auto-scheduler.ts | 310 | 无限制 | 🟢 合规 |
| scheduling/actions.ts | 302 | 800(建议) | 🟢 合规 |
| scheduling/data-access.ts | 272 | 800(建议) | 🟢 合规 |
| attendance/actions.ts | 271 | 800(建议) | 🟢 合规 |
| attendance/data-access.ts | 271 | 800(建议) | 🟢 合规 |
| attendance/data-access-stats.ts | 145 | 800(建议) | 🟢 合规 |
| users/import-export.ts | 291 | 800(建议) | 🟢 合规(但职责混合) |
| users/actions.ts | 151 | 800(建议) | 🟢 合规 |
| users/data-access.ts | 71 | 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 | 242 | 800(建议) | 🟢 合规 |
| announcements/data-access.ts | 120 | 800(建议) | 🟢 合规 |
仅
classes/data-access.ts突破硬性上限,需立即拆分。
报告结束。本审查未修改任何源代码。