Files
NextEdu/docs/architecture/audit/school-grade-class-audit-report.md
SpecialX 10c668f36a feat(school,classes): 学校/年级/班级模块审计修复 — 权限校验 + i18n + 架构图同步
- 新增审计报告 docs/architecture/audit/school-grade-class-audit-report.md

- 修复 P0-4: teacher/classes 4 个页面补充 requirePermission 权限校验

- 修复 P0-5: 新增 school.json i18n 文件(zh-CN/en)并接入 schools-view 组件

- 同步架构图 004:补充 grade-management 死模块记录与 teacher/classes 权限修复说明
2026-06-22 16:44:02 +08:00

21 KiB
Raw Blame History

学校/年级/班级管理模块审计报告

审查范围:school(学校/学年/部门/年级 CRUDgrade-management(年级管理重构模块)、classes(班级管理) 审查日期2026-06-22 审查依据项目规则三层架构、权限校验、i18n、TypeScript 严格模式、单文件行数限制、K12 行业优秀实践 审查方式:只读源码分析 + 架构图比对,未修改任何代码


一、现有实现概要

1.1 模块文件分布

模块 核心文件 行数(约) 职责
school actions.ts / data-access.ts / schema.ts / types.ts + 4 个组件 349 / 504 / 51 / 96 学校/学年/部门/年级的 CRUD
grade-management actions.ts / data-access.ts / data-access-insights.ts / schema.ts / types.ts + 11 组件 + 4 hooks + 4 services + 2 widgets + 1 config 213 / 238 / 75 / — / 149 年级管理(重构版,含洞察)
classes actions.ts / data-access.ts / data-access-{admin,stats,schedule,students,invitations}.ts / schema.ts / types.ts + 14 组件 974 / 548 / 406 / 513 / 194 / 253 / — / 152 / 183 班级 CRUD + 学生/教师管理 + 邀请码 + 课表 + 作业洞察

1.2 页面分布(共 13 个 page.tsx

路由分组 页面 权限校验 i18n 调用方式
admin/school/* schools / grades / classes / departments / academic-year requirePermission(SCHOOL_MANAGE) 中文硬编码 直接调用 data-access
management/grade/* classes / insights requirePermission(GRADE_MANAGE/GRADE_RECORD_READ) 英文硬编码 直接调用 classes/school data-access
teacher/classes/* my / my/[id] / schedule / students 无任何校验 英文硬编码 直接调用 data-access

1.3 架构图记录情况

docs/architecture/004_architecture_impact_map.md 中:

  • 已记录 school 模块§2.8)和 classes 模块§2.7)的职责、依赖关系、跨模块通信方式
  • 已记录 classes 模块的文件拆分5 个 data-access 子文件)和 P0-7 修复homework 跨模块封装)
  • 未记录 grade-management 模块 — 该模块拥有完整的 services/hooks/widgets/config 架构,但架构图中完全缺失
  • 未记录 grade-management 模块未被任何页面使用的事实 — 这是重大架构偏差

1.4 数据流概要

admin/school/grades 页面
  └─→ school/data-access.getGrades() / getSchools() / getStaffOptions()
  └─→ school/components/grades-view.tsx客户端组件
  └─→ school/actions.ts → createGradeAction / updateGradeAction / deleteGradeAction

management/grade/classes 页面
  └─→ classes/data-access.getGradeManagedClasses() / getTeacherOptions()
  └─→ school/data-access.getGradesForStaff()
  └─→ classes/components/grade-classes-view.tsx
  └─→ classes/actions.ts → createGradeClassAction / updateGradeClassAction / ...

teacher/classes/* 页面
  └─→ classes/data-access.getTeacherClasses() / getClassStudents() / getClassSchedule()
  └─→ classes/components/*my-classes-grid / students-table / schedule-view
  └─→ classes/actions.ts → createTeacherClassAction / ...

grade-management 模块(⚠️ 完全未被使用)
  └─→ services/grade-service.ts接口定义
  └─→ services/admin-grade-service.ts / teacher-grade-service.ts实现
  └─→ widgets/grade-management-widget.tsx主面板
  └─→ ⚠️ 无任何页面导入此模块

二、现存问题与原因分析

2.1 架构层面

P0-1grade-management 模块完全未被使用(死模块)

  • 位置src/modules/grade-management/ 全模块
  • 问题该模块拥有完整的理想架构Service 接口 + Context 依赖注入 + 角色配置 + Error Boundary + Skeleton + i18n + hooks 分离),但 13 个相关页面中无任何一个导入该模块management/grade/* 页面实际依赖 classesschool 模块的 data-access。
  • 违反规则:架构图优先规则(图未覆盖则先补图)、模块标准结构(该模块存在但未接入)
  • 原因:推测为未完成的重构 — 已建立目标架构但未将页面迁移过来
  • 后果:维护两套年级管理逻辑(school 模块的 grade CRUD + grade-management 模块的 grade CRUD职责重叠、产生混淆理想架构模式无法落地发挥价值

P0-2年级 CRUD 逻辑重复定义

  • 位置
    • src/modules/school/actions.ts L268-349createGradeAction / updateGradeAction / deleteGradeAction
    • src/modules/grade-management/actions.ts L37-203同名函数 createGradeAction / updateGradeAction / deleteGradeAction
    • src/modules/school/data-access.ts L256-285createGrade / updateGrade / deleteGrade
    • src/modules/grade-management/data-access.ts L137-171同名函数 createGrade / updateGrade / deleteGrade
  • 问题:两套模块各自定义了完全相同的年级 CRUD 逻辑,admin/school/grades 页面使用 school 模块版本,grade-management 模块版本无人调用
  • 违反规则DRY 原则、模块标准结构(职责应归属单一模块)
  • 后果修改年级逻辑需同步两处极易遗漏两套实现的审计日志策略不一致school 模块 grade CRUD 无 logAuditgrade-management 模块有)

P0-3classes/actions.ts 接近行数硬上限

  • 位置src/modules/classes/actions.ts974 行)
  • 问题:文件已达 974 行,接近 1000 行硬性上限。包含 3 组近乎重复的 CRUD ActionTeacher 系列 / Admin 系列 / Grade 系列)+ 邀请码 Action + 课表 Action
  • 违反规则单文件行数限制Server Actions 建议 ≤ 800 行,硬性上限 1000 行)
  • 后果:再增加任何功能即超限;文件过大降低可读性和可维护性

2.2 权限层面

P0-4teacher/classes/* 4 个页面完全缺少权限校验

  • 位置
    • src/app/(dashboard)/teacher/classes/my/page.tsx — 无 requirePermission()
    • src/app/(dashboard)/teacher/classes/my/[id]/page.tsx — 无 requirePermission()
    • src/app/(dashboard)/teacher/classes/schedule/page.tsx — 无 requirePermission()
    • src/app/(dashboard)/teacher/classes/students/page.tsx — 无 requirePermission()
  • 问题:这 4 个业务页面直接调用 data-access 获取数据,依赖路由中间件隐式保障身份,无显式权限校验
  • 违反规则Server Action 规范(每个 Action 必须调用 requirePermission());安全性规范(所有敏感数据查询必须在 data-access 层结合当前用户权限过滤)
  • 后果若路由中间件配置错误或被绕过教师可访问任意班级数据data-access 层的 getTeacherClasses() 未接收 userId 参数做范围过滤

P1-1classes/actions.ts 中存在 ctx.roles.includes("xxx") 硬编码

  • 位置src/modules/classes/actions.ts L81、L420、L422、L428、L446、L451
  • 问题Server Action 中使用 ctx.roles.includes("admin") / ctx.roles.includes("teacher") / ctx.roles.includes("student") 进行角色判断
  • 违反规则:前端组件禁止 role === "xxx" 硬编码(虽此处在 Server Action 而非前端组件,但精神一致 — 应使用权限点而非角色名)
  • 后果:新增角色(如 grade_head需修改所有硬编码处角色与权限耦合不符合权限点驱动设计

2.3 国际化层面

P0-5全部 13 个页面均未使用 i18n

  • 位置:所有 13 个 page.tsx 及其引用的组件
  • 问题
    • admin/school/* 页面使用中文硬编码(如 "学校管理"、"年级管理"、"班级管理"
    • management/grade/*teacher/classes/* 页面使用英文硬编码(如 "Class Management"、"Grade Insights"
    • school/components/* 全部使用英文硬编码(如 "New school"、"All schools"、"Edit"、"Delete"
    • classes/components/* 混用中英文
  • 违反规则:所有用户可见文本必须适配 i18n使用 next-intl提取翻译键
  • 后果无法支持多语言中英文混用严重影响一致性和专业度i18n 资源文件(grade.jsonclasses.json)已存在但未被使用

P1-2school 模块无 i18n 资源文件

  • 位置src/shared/i18n/messages/{zh-CN,en}/ 目录
  • 问题:存在 grade.jsonclasses.json,但不存在 school.json。school 模块的学校/学年/部门管理文本无翻译键可用
  • 违反规则i18n 就绪规范
  • 后果:即使想为 school 模块补充 i18n也缺少翻译文件基础设施

2.4 组件质量层面

P1-3school/components/* 缺少 Error Boundary 和 Skeleton

  • 位置src/modules/school/components/schools-view.tsx / grades-view.tsx / departments-view.tsx / academic-year-view.tsx
  • 问题4 个组件均为 "use client" 客户端组件,无 Error Boundary 包裹、无加载骨架屏、无 Suspense 处理。对比 grade-management 模块已有 grade-error-boundary.tsx / grade-skeleton.tsx / grade-states.tsx(但未被使用)
  • 违反规则:错误与边界处理(每个独立数据区块必须用 React Error Boundary 包裹;异步数据使用 React Suspense + 骨架屏)
  • 后果:数据加载失败时整页崩溃无降级;加载过程无反馈

P1-4classes/types.ts 跨领域类型污染

  • 位置src/modules/classes/types.ts
  • 问题:定义了本应属于其他模块的类型:
    • ClassHomeworkInsights / GradeHomeworkInsights / ClassHomeworkAssignmentStats / ScoreStats / AssignmentSummary — 应属 homework 模块
    • ClassScheduleItem / StudentScheduleItem — 与 scheduling 模块概念重叠
  • 违反规则:模块标准结构(类型应归属对应模块)
  • 后果classes 模块承担了 homework/scheduling 的类型定义职责,耦合度高

P1-5school/components/* 未使用组合模式

  • 位置src/modules/school/components/schools-view.tsx
  • 问题SchoolsClient 组件内部硬编码了 Table + Dialog + AlertDialog 的完整结构,无法通过 slots/render props 定制。对比 grade-management 模块的 GradeManagementWidget 通过组合 GradeListTable + GradeListToolbar + GradeFormDialog + GradeDeleteDialog 实现灵活性
  • 违反规则:组合优先(所有 UI 通过组件组合实现灵活性)
  • 后果:无法复用表格/对话框子部件;新增角色差异需复制整个组件

2.5 数据安全层面

P1-6data-access 层部分查询未结合用户权限过滤

  • 位置
    • src/modules/classes/data-access.tsgetTeacherClasses() 未接收 userId 参数
    • src/modules/school/data-access.tsgetGrades() / getSchools() 返回全量数据,无权限过滤
  • 问题data-access 函数为全局查询,不结合当前用户身份做数据范围过滤,完全依赖 actions 层或页面层校验
  • 违反规则:安全性规范(所有敏感数据查询必须在 data-access 层结合当前用户权限过滤)
  • 后果:若上层遗漏校验(如 P0-4 中 teacher/classes 页面),数据越权访问风险

2.6 可测试性层面

P2-1schoolclasses 模块逻辑与 UI 耦合,难以单测

  • 位置school/components/* / classes/components/*
  • 问题:组件内部直接调用 actions、管理状态、处理错误未将数据获取/计算/格式化逻辑抽取为独立 hooks 或纯函数。对比 grade-management 模块已抽取 use-grade-data / use-grade-filters / use-grade-form / use-grade-insights 四个 hooks
  • 违反规则:可测试性(数据获取、计算、格式化等纯逻辑全部放入纯函数或 hooks与 UI 分离)
  • 后果:无法对筛选逻辑、表单校验逻辑进行独立单测

三、行业差距对比

3.1 与优秀 K12 产品的差距

功能/交互 行业优秀实践Google Classroom / 钉钉教育 / 智学网) 当前状态 影响
学校切换 顶部全局学校切换器,切换后所有页面数据联动 仅 admin 跨校可见,无全局切换器 多校区场景下教师/学生无法快速切换视角
年级→班级树形导航 左侧树形结构(学校→年级→班级),支持展开/折叠/搜索 扁平列表,无层级导航 班级数量多时查找效率低
班级详情仪表盘 一页聚合:基本信息 + 学生名单 + 课表 + 作业 + 成绩趋势 teacher/classes/my/[id] 已有 class-detail 子组件,但 admin/grade 视角无详情页 admin/年级组长无法下钻查看班级详情
批量操作 批量导入学生、批量分配教师、批量升级班级 仅支持单条 CRUD + 邮箱注册 开学季配置效率低
空状态引导 空状态带引导按钮和说明文案 schools-view 有 EmptyState其他组件不一致 新用户不知道下一步该做什么
加载骨架屏 数据加载时显示骨架屏保持布局稳定 school/classes 组件无骨架屏grade-management 有但未使用) 加载过程布局跳动,体验差
邀请码加入 二维码 + 链接 + 6 位码三种方式 仅 6 位码v3 已支持有效期/次数) 家长端操作门槛略高
年级升级 学年末一键升级(三年级→四年级),保留历史档案 无此功能 每年需手动重建班级
数据权限隔离 教师仅看到自己班级年级组长看到年级所有班级admin 看到全部 teacher/classes 页面无权限校验P0-4data-access 无范围过滤P1-6 存在越权风险

3.2 多角色体验差距

角色 优秀实践 当前状态
admin 统一管理面板,学校/年级/班级三级联动,支持批量配置 分散在 4 个独立页面,无联动
teacher 我的班级 + 可加入班级 + 邀请码管理一站式 有基本功能,但无权限校验、无 i18n
parent 查看孩子所在班级信息、任课教师、班级通知 无专属页面(依赖 dashboard 间接展示)
student 查看我的班级、同学名单、课表 有基本功能,但无权限校验、无 i18n

四、改进优先级建议

P0紧急 — 安全与架构正确性)

编号 问题 改进方向
P0-1 grade-management 模块完全未被使用 决策:要么将 admin/school/grades 页面迁移到使用 grade-management 模块的 Widget + Service 模式,要么删除该死模块。推荐迁移,因为该模块实现了用户要求的全部原则(解耦/组合/i18n/复用/边界/可测试/可扩展)
P0-2 年级 CRUD 逻辑重复 统一到 grade-management 模块,school 模块仅保留学校/学年/部门 CRUD删除 school 模块中的 grade CRUD
P0-3 classes/actions.ts 974 行接近上限 按职责拆分为 actions-teacher.ts / actions-admin.ts / actions-grade.ts / actions-invitations.ts / actions-schedule.ts
P0-4 teacher/classes/* 4 页面无权限校验 每个页面添加 requirePermission(Permissions.CLASS_READ) 或对应权限点
P0-5 全部 13 页面无 i18n 提取翻译键,使用 getTranslations(服务端组件)或 useTranslations(客户端组件)。补充 school.json 翻译文件

P1重要 — 代码质量与可维护性)

编号 问题 改进方向
P1-1 classes/actions.ts 角色硬编码 ctx.roles.includes("admin") 改为 ctx.hasPermission(Permissions.xxx)ctx.roles 中的权限点判断
P1-2 school 模块无 i18n 文件 新建 src/shared/i18n/messages/{zh-CN,en}/school.json
P1-3 school/components/* 缺少 Error Boundary/Skeleton 参照 grade-management 模块的 grade-error-boundary.tsx / grade-skeleton.tsx 模式补充
P1-4 classes/types.ts 跨领域类型污染 ClassHomeworkInsights 等类型迁移至 homework 模块classes 模块通过 import type 引用
P1-5 school/components/* 未使用组合模式 SchoolsClient 拆分为 SchoolListTable + SchoolFormDialog + SchoolDeleteDialog + SchoolListToolbar
P1-6 data-access 层未结合权限过滤 getTeacherClasses(userId) 接收 userId 参数,在查询中过滤

P2优化 — 体验与扩展性)

编号 问题 改进方向
P2-1 逻辑与 UI 耦合,难以单测 参照 grade-management 模块抽取 use-school-data / use-class-data 等 hooks
P2-2 缺少年级→班级树形导航 新增 OrgTreeNav 组件,学校→年级→班级三级树
P2-3 缺少年级升级功能 新增 promoteGradeAction,学年末批量升级
P2-4 缺少批量操作 批量导入学生、批量分配教师
P2-5 school 模块审计日志不一致 为 department/academicYear/grade 的 CRUD 补充 logAudit

重构方案设计要点(参照用户强制原则)

  1. 完全解耦:以 grade-management 模块的 GradeService 接口 + GradeServiceProvider Context 注入为范本,为 school 和 classes 模块建立对应的 SchoolService / ClassService 接口
  2. 组合优先:参照 GradeManagementWidget 的组合方式Toolbar + Table + FormDialog + DeleteDialog所有模块的 Widget 通过组合子组件实现
  3. 国际化就绪:翻译文件结构示例
    // school.json
    {
      "schools": { "title": "学校管理", "list": { "title": "学校列表", "empty": "暂无学校" }, "form": { ... } },
      "grades": { "title": "年级管理", ... },
      "departments": { "title": "部门管理", ... },
      "academicYear": { "title": "学年管理", ... }
    }
    
  4. 最大化复用:抽取 OrgCrudWidget<T> 泛型组件(列表+工具栏+表单+删除school/grade/department 共用
  5. 错误与边界:每个 Widget 用 Error Boundary 包裹,异步数据用 Suspense + 骨架屏
  6. 可测试性:数据获取/筛选/校验逻辑全部抽取为 hooks
  7. 可扩展性:参照 GRADE_ROLE_CONFIG,为 school/classes 建立角色配置驱动设计
  8. 企业级补充a11y语义化标签 + ARIA、性能RSC 获取初始数据、安全data-access 层权限过滤)、监控(GradeAnalyticsTracker 模式扩展到 school/classes

五、架构图同步说明

本次审计发现架构图存在以下遗漏和不一致,需同步更新:

5.1 需补充的节点

文档 需补充内容
004_architecture_impact_map.md 新增 ## 2.X grade-management年级管理模块 章节,记录其 services/hooks/widgets/config 架构,并标注"⚠️ 该模块当前未被任何 app 页面使用"
004_architecture_impact_map.md ## 2.8 school 章节补充说明school 模块包含 grade CRUD 但与 grade-management 模块职责重叠
004_architecture_impact_map.md 在路由表中补充 teacher/classes/* 4 个页面缺少 requirePermission 的标注
005_architecture_data.json modules 节点新增 grade-management 模块及其 exports/dependencies
005_architecture_data.json dependencyMatrix 新增 grade-management → classes通过 data-access、grade-management → school 的依赖关系
005_architecture_data.json routes 节点补充 teacher/classes/* 的权限缺失标注

5.2 需修改的节点

文档 需修改内容
004_architecture_impact_map.md §2.7 classes 更新 actions.ts 行数676 → 974标注接近硬上限
004_architecture_impact_map.md §2.8 school 更新 data-access.ts 行数186 → 504补充新增的跨模块查询函数getGradeNameById / getSubjectNameById / isGradeHead / isGradeManager / findGradeIdByHeadAndName

5.3 i18n 翻译文件结构示例

src/shared/i18n/messages/
├─ zh-CN/
│  ├─ school.json      ← 新增(学校/学年/部门管理翻译键)
│  ├─ grade.json       ← 已存在年级管理翻译键grade-management 模块用)
│  └─ classes.json     ← 已存在(班级管理翻译键,需扩充)
└─ en/
   ├─ school.json      ← 新增
   ├─ grade.json       ← 已存在
   └─ classes.json     ← 已存在

附录:审计检查清单

检查项 school grade-management classes
三层架构划分合理 ⚠️ actions.ts 过大
文件大小符合规范 actions.ts 974 行
无跨模块直接依赖
Server Action 权限校验 ⚠️ 角色硬编码
前端无 role 硬编码
i18n 适配 (组件层)
错误处理/边界
骨架屏/空状态 ⚠️ 部分 ⚠️ 部分
逻辑与 UI 分离 ⚠️ 部分
组合模式 ⚠️ 部分
配置驱动
被页面实际使用 死模块
审计日志完整 ⚠️ 不一致
监控埋点接口 (预留)