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

> 审查范围：`school`（学校/学年/部门/年级 CRUD）、`grade-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-1：`grade-management` 模块完全未被使用（死模块）

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

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

- **位置**：
  - `src/modules/school/actions.ts` L268-349：`createGradeAction` / `updateGradeAction` / `deleteGradeAction`
  - `src/modules/grade-management/actions.ts` L37-203：同名函数 `createGradeAction` / `updateGradeAction` / `deleteGradeAction`
  - `src/modules/school/data-access.ts` L256-285：`createGrade` / `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 无 `logAudit`，grade-management 模块有）

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

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

### 2.2 权限层面

#### P0-4：`teacher/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-1：`classes/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.json`、`classes.json`）已存在但未被使用

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

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

### 2.4 组件质量层面

#### P1-3：`school/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-4：`classes/types.ts` 跨领域类型污染

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

#### P1-5：`school/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-6：data-access 层部分查询未结合用户权限过滤

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

### 2.6 可测试性层面

#### P2-1：`school` 和 `classes` 模块逻辑与 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-4），data-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. **国际化就绪**：翻译文件结构示例
   ```json
   // 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 分离 | ❌ | ✅ | ⚠️ 部分 |
| 组合模式 | ❌ | ✅ | ⚠️ 部分 |
| 配置驱动 | ❌ | ✅ | ❌ |
| 被页面实际使用 | ✅ | ❌ **死模块** | ✅ |
| 审计日志完整 | ⚠️ 不一致 | ✅ | ✅ |
| 监控埋点接口 | ❌ | ✅（预留） | ❌ |
