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

> 审查范围：`school`（学校/学年/部门/年级 CRUD）、`classes`（班级管理）
> 审查日期：2026-06-22（v2 复审）
> 审查依据：项目规则（三层架构、权限校验、i18n、TypeScript 严格模式、单文件行数限制）、K12 行业优秀实践
> 审查方式：只读源码分析 + 架构图比对 + 行业对标

---

## 一、现有实现概要

### 1.1 v1 审计修复回顾

v1 审计报告识别了 14 个问题（5 个 P0 + 6 个 P1 + 5 个 P2），截至本次复审，**全部 14 个问题已修复**：

| 编号 | 问题 | 状态 |
|------|------|------|
| P0-1 | `grade-management` 死模块 | ✅ 已删除 |
| P0-2 | 年级 CRUD 逻辑重复 | ✅ 统一到 school 模块 |
| P0-3 | `classes/actions.ts` 974 行 | ✅ 拆分为 6 个文件 |
| P0-4 | `teacher/classes/*` 无权限校验 | ✅ 4 个页面已加 `requirePermission` |
| P0-5 | school 模块无 i18n | ✅ 4 个组件已接入 `useTranslations` |
| P1-1 | 角色硬编码 | ✅ 改为 `dataScope.type` 判断 |
| P1-2 | school 无 i18n 文件 | ✅ `school.json` 已创建（413 行翻译键） |
| P1-3 | school 缺 Error Boundary/Skeleton | ✅ 已补充 |
| P1-4 | classes/types.ts 跨领域类型 | ✅ 保留并加注释说明 |
| P1-5 | school 未用组合模式 | ✅ 拆分为 4 个子组件 |
| P1-6 | data-access 无权限过滤 | ✅ 新增 `getSchoolsForUser`/`getGradesForUser` |
| P2-1~P2-5 | 各项优化 | ✅ 全部修复 |

### 1.2 当前模块文件分布

| 模块 | 核心文件 | 行数 | 职责 |
|------|---------|------|------|
| `school` | `actions.ts` | 457 | 13 个 Server Action（含 `promoteGradesAction`） |
| `school` | `data-access.ts` | 757 | 只读查询 + 12 个写操作 + 组织树 + 权限感知函数 |
| `school` | `schema.ts` / `types.ts` | 51 / 90 | Zod 校验 / 类型定义 |
| `school` | `components/` (14 文件) | 60~860 | 学校/学年/部门/年级视图 + 仪表盘 + 组织树 |
| `school` | `hooks/use-school-data.ts` | 36 | 学校数据管理 hook |
| `classes` | `actions.ts` (barrel) | 51 | re-export 入口 |
| `classes` | `actions-{teacher,admin,grade,invitations,schedule,shared}.ts` | 60~448 | 按职责拆分的 6 个 Action 文件 |
| `classes` | `data-access.ts` | 833 | 核心班级 CRUD + 邀请码 + 教师班级管理 |
| `classes` | `data-access-{admin,stats,schedule,students,invitations}.ts` | 93~454 | 按领域拆分的 5 个 data-access |
| `classes` | `schema.ts` / `types.ts` | 152 / 177 | Zod 校验 / 类型定义 |
| `classes` | `components/` (14 文件) | 137~499 | 班级列表/详情/学生/课表/邀请码 |

### 1.3 页面分布（共 13 个 page.tsx）

| 路由分组 | 页面 | 权限校验 | i18n | Error Boundary | Loading |
|---------|------|---------|------|----------------|---------|
| `admin/school/schools` | ✅ `SCHOOL_MANAGE` | ✅ | ✅ | ✅ | ✅ |
| `admin/school/grades` | ✅ `SCHOOL_MANAGE` | ✅ | ✅ | ✅ | ✅ |
| `admin/school/grades/insights` | ✅ `SCHOOL_MANAGE` | ✅ | ✅ | ✅ | ✅ |
| `admin/school/departments` | ✅ `SCHOOL_MANAGE` | ✅ | ✅ | ✅ | ✅ |
| `admin/school/academic-year` | ✅ `SCHOOL_MANAGE` | ✅ | ✅ | ✅ | ✅ |
| `admin/school/classes` | ✅ `SCHOOL_MANAGE` | ❌ | ❌ | ✅ | ✅ |
| `management/grade/classes` | ✅ `GRADE_MANAGE` | ✅ | ✅ | ✅ | ✅ |
| `management/grade/insights` | ✅ `GRADE_MANAGE` | ✅ | ✅ | ✅ | ✅ |
| `management/grade/dashboard` | ✅ `GRADE_MANAGE` | ✅ | ✅ | ❌ | ✅ |
| `teacher/classes/my` | ✅ `CLASS_READ` | ❌ | ❌ | ❌ | ✅ |
| `teacher/classes/my/[id]` | ✅ `CLASS_READ` | ❌ | ❌ | ❌ | ❌ |
| `teacher/classes/schedule` | ✅ `CLASS_READ` | ❌ | ✅(Suspense) | ❌ | ✅ |
| `teacher/classes/students` | ✅ `CLASS_READ` | ❌ | ✅(Suspense) | ❌ | ✅ |

### 1.4 数据流概要

```
admin/school/* 页面（✅ 完整 i18n + Error Boundary + Skeleton）
  └─→ school/data-access.getSchools() / getGrades() / getStaffOptions() / getGradeOverviewStats()
  └─→ school/components/*（✅ 全部使用 useTranslations）
  └─→ school/actions.ts → createXxxAction / updateXxxAction / deleteXxxAction

management/grade/* 页面（✅ 完整 i18n + Error Boundary）
  └─→ classes/data-access.getGradeManagedClasses() / getTeacherOptions()
  └─→ school/data-access.getGradesForStaff()
  └─→ classes/components/grade-classes-view.tsx（❌ 无 i18n）
  └─→ classes/actions.ts → createGradeClassAction / ...

teacher/classes/* 页面（❌ 组件无 i18n，❌ 无 Error Boundary）
  └─→ classes/data-access.getTeacherClasses() / getClassStudents() / getClassSchedule()
  └─→ classes/components/*（❌ 14 个组件中仅 1 个使用 useTranslations）
  └─→ classes/actions.ts → createTeacherClassAction / ...
```

---

## 二、现存问题与原因分析（v2 新发现）

### 2.1 国际化层面

#### P0-A：classes 模块 13/14 个组件未接入 i18n

- **位置**：
  - `src/modules/classes/components/admin-classes-view.tsx` — 16 处英文硬编码（"Failed to create class"、"No classes"、"Select a school" 等）
  - `src/modules/classes/components/grade-classes-view.tsx` — 20+ 处英文硬编码（"Homeroom Teacher"、"Subject Teachers"、"Select a grade" 等）
  - `src/modules/classes/components/my-classes-grid.tsx` — 15+ 处英文硬编码（"Join New Class"、"Invitation Code"、"Cancel" 等）+ 1 处中文硬编码（"教学科目"、"暂无可选科目"）
  - `src/modules/classes/components/schedule-view.tsx` — 10+ 处英文硬编码
  - `src/modules/classes/components/schedule-filters.tsx` — 8 处英文硬编码
  - `src/modules/classes/components/students-filters.tsx` — 5 处英文硬编码
  - `src/modules/classes/components/students-table.tsx` — 英文硬编码
  - `src/modules/classes/components/class-detail/*.tsx`（7 个文件）— 全部英文硬编码（"Recent Homework"、"Weekly Schedule"、"Quick Actions" 等）
- **问题**：14 个组件中仅 `class-invitation-manager.tsx` 使用 `useTranslations`，其余 13 个组件全部硬编码文本，且 `my-classes-grid.tsx` 中存在中英文混用
- **违反规则**：所有用户可见文本必须适配 i18n（使用 next-intl），提取翻译键
- **原因**：v1 审计仅修复了 school 模块的 i18n，classes 模块的 i18n 遗留未处理
- **后果**：teacher/management 视角下的班级管理页面无法支持多语言；中英文混用严重影响专业度

#### P0-B：classes.json i18n 文件内容严重不足

- **位置**：`src/shared/i18n/messages/{zh-CN,en}/classes.json`
- **问题**：当前仅 55 行，只覆盖 `invitation.*` 和 `class.*`（5 个基础字段）。缺少班级 CRUD 表单、列表、筛选器、详情页、学生管理、课表等全部场景的翻译键
- **违反规则**：i18n 就绪规范
- **后果**：即使组件想接入 i18n，也缺少翻译键可用

### 2.2 文件大小层面

#### P1-A：`grades-view.tsx` 860 行超出组件行数限制

- **位置**：`src/modules/school/components/grades-view.tsx`（860 行）
- **问题**：单个客户端组件文件 860 行，超过 React 组件建议上限 500 行（复杂表单可放宽至 800 行，但仍超）。包含年级列表 + 概览卡片 + 筛选器 + 创建/编辑表单 + 删除对话框 + 年级升级对话框全部逻辑
- **违反规则**：单文件行数限制（React 组件建议 ≤ 500 行，复杂表单/大型表格可放宽至 800 行）
- **后果**：可读性差，难以维护；筛选逻辑、表单校验逻辑、对话框状态管理全部耦合

#### P1-B：`classes/data-access.ts` 833 行超出 data-access 限制

- **位置**：`src/modules/classes/data-access.ts`（833 行）
- **问题**：虽然已拆分为 5 个 data-access 子文件，但主 `data-access.ts` 仍达 833 行，超过 data-access 建议 800 行上限。包含核心班级 CRUD + 邀请码 + 教师班级管理 + 跨模块接口
- **违反规则**：单文件行数限制（Server Actions / Data Access 模块建议 ≤ 800 行）
- **后果**：接近硬上限，后续增加功能即超限

### 2.3 错误处理层面

#### P1-C：classes 模块组件缺少 Error Boundary 和 Skeleton

- **位置**：
  - `src/app/(dashboard)/teacher/classes/my/[id]/page.tsx` — 无 Error Boundary
  - `src/app/(dashboard)/admin/school/classes/page.tsx` — 无 Error Boundary
  - `src/app/(dashboard)/management/grade/dashboard/page.tsx` — 无 Error Boundary
  - `src/modules/classes/components/*` — 无骨架屏组件
- **问题**：对比 school 模块已有 `school-error-boundary.tsx` + `school-skeleton.tsx`，classes 模块完全没有错误边界和骨架屏组件
- **违反规则**：错误与边界处理（每个独立数据区块必须用 React Error Boundary 包裹；异步数据使用 React Suspense + 骨架屏）
- **后果**：数据加载失败时整页崩溃无降级；加载过程无反馈

### 2.4 组件质量层面

#### P1-D：classes 组件未使用组合模式

- **位置**：
  - `src/modules/classes/components/admin-classes-view.tsx`（499 行）— 单体组件包含 Table + Dialog + AlertDialog + Select 全部逻辑
  - `src/modules/classes/components/grade-classes-view.tsx`（408 行）— 同上
  - `src/modules/classes/components/my-classes-grid.tsx`（390 行）— 单体组件包含卡片网格 + 加入班级对话框 + 邀请码管理
- **问题**：对比 school 模块已拆分为 `SchoolListToolbar` + `SchoolFormDialog` + `SchoolDeleteDialog` + `useSchoolData` hook，classes 模块仍是单体组件，无法复用子部件
- **违反规则**：组合优先（所有 UI 通过组件组合实现灵活性）
- **后果**：admin/grade/teacher 三个视角的班级管理存在大量重复代码（表单、对话框、筛选器），无法复用

#### P1-E：classes 模块缺少 hooks 抽取

- **位置**：`src/modules/classes/` — 无 `hooks/` 目录
- **问题**：对比 school 模块已抽取 `use-school-data` hook，classes 模块的对话框状态管理、表单校验、筛选逻辑全部耦合在组件内部
- **违反规则**：可测试性（数据获取、计算、格式化等纯逻辑全部放入纯函数或 hooks，与 UI 分离）
- **后果**：无法对筛选逻辑、表单校验逻辑进行独立单测

### 2.5 架构模式层面

#### P2-A：缺少 Service 接口抽象和依赖注入

- **位置**：整个 school + classes 模块
- **问题**：组件直接 import data-access 函数，未通过 Service 接口抽象数据依赖。对比已删除的 `grade-management` 模块曾有完整的 `GradeService` 接口 + Context DI 模式
- **违反规则**：完全解耦（通过定义 TypeScript 接口抽象数据依赖，使用 React Context 注入数据服务）
- **后果**：组件与 data-access 直接耦合，难以 mock 测试；不同角色的差异通过 if/else 硬编码而非接口实现隔离

#### P2-B：缺少角色配置驱动设计

- **位置**：整个 school + classes 模块
- **问题**：admin/teacher/grade 三个视角的班级管理通过 3 套独立的组件实现（`admin-classes-view` / `grade-classes-view` / `my-classes-grid`），而非通过配置驱动同一套组件
- **违反规则**：可扩展性（采用配置驱动设计，通过角色配置决定渲染哪些 Widget）
- **后果**：新增角色需复制整套组件；三套组件存在大量重复代码

---

## 三、行业差距对比

### 3.1 与优秀 K12 产品的差距（v2 更新）

| 功能/交互 | 行业优秀实践 | 当前状态 | 影响 |
|----------|------------|---------|------|
| **i18n 完整覆盖** | 所有角色所有页面支持多语言 | school 模块 ✅，classes 模块 ❌（13/14 组件硬编码） | classes 模块无法支持多语言 |
| **错误边界** | 每个数据区块独立 Error Boundary | school 模块 ✅，classes 模块 ❌ | classes 数据加载失败整页崩溃 |
| **骨架屏** | 加载时保持布局稳定 | school 模块 ✅，classes 模块 ❌ | classes 加载过程布局跳动 |
| **组件复用** | 三个角色共用一套可配置组件 | 三套独立组件（admin/grade/teacher） | 代码重复，维护成本高 |
| **班级详情仪表盘** | 一页聚合基本信息 + 学生 + 课表 + 作业 + 成绩 | `teacher/classes/my/[id]` 已有，但 admin/grade 视角无 | admin/年级组长无法下钻 |
| **批量操作** | 批量导入学生、批量分配教师 | ✅ 已实现（v1 P2-4 修复） | 已达标 |
| **年级升级** | 学年末一键升级 | ✅ 已实现（v1 P2-3 修复） | 已达标 |
| **组织树导航** | 学校→年级→班级三级树 | ✅ 已实现（v1 P2-2 修复） | 已达标 |
| **邀请码加入** | 6 位码 + 有效期 + 次数 | ✅ 已实现 | 已达标 |
| **数据权限隔离** | data-access 层结合用户权限过滤 | ✅ 已实现（v1 P1-6 修复） | 已达标 |

### 3.2 多角色体验差距

| 角色 | 优秀实践 | 当前状态 |
|------|---------|---------|
| **admin** | 统一管理面板，i18n 完整，错误边界完整 | school 模块 ✅，classes 模块 ❌（无 i18n、无 Error Boundary） |
| **teacher** | 我的班级 + 邀请码 + 课表 + 学生，i18n 完整 | 有基本功能，但 ❌ 无 i18n、❌ 无 Error Boundary |
| **grade_head** | 年级班级管理 + 学情洞察，i18n 完整 | 有基本功能，但 ❌ 组件无 i18n |
| **parent** | 查看孩子所在班级信息 | 无专属页面（依赖 dashboard 间接展示） |
| **student** | 查看我的班级、同学名单、课表 | 有基本功能，但 ❌ 无 i18n |

---

## 四、改进优先级建议

### P0（紧急 — i18n 与错误处理）

| 编号 | 问题 | 改进方向 |
|------|------|---------|
| P0-A | classes 模块 13/14 个组件未接入 i18n | 为所有 classes 组件接入 `useTranslations("classes")`，提取全部硬编码文本到翻译键 |
| P0-B | classes.json i18n 文件内容不足 | 扩充 `classes.json`，覆盖班级 CRUD、列表、筛选器、详情页、学生管理、课表、详情子组件等全部场景 |

### P1（重要 — 代码质量与可维护性）

| 编号 | 问题 | 改进方向 |
|------|------|---------|
| P1-A | `grades-view.tsx` 860 行 | 拆分为 `GradeListSection` + `GradeOverviewSection` + `GradeFormDialog` + `GradeDeleteDialog` + `GradePromoteDialog` + `use-grade-data` hook |
| P1-B | `classes/data-access.ts` 833 行 | 将邀请码相关函数迁移至 `data-access-invitations.ts`，将教师班级管理迁移至 `data-access-teacher.ts` |
| P1-C | classes 模块缺少 Error Boundary/Skeleton | 新增 `class-error-boundary.tsx` + `class-skeleton.tsx`，为所有 classes 页面包裹 |
| P1-D | classes 组件未使用组合模式 | 将 `admin-classes-view` / `grade-classes-view` / `my-classes-grid` 拆分为可复用的 `ClassListTable` + `ClassFormDialog` + `ClassDeleteDialog` + `ClassListToolbar` |
| P1-E | classes 模块缺少 hooks | 抽取 `use-class-data` hook（对话框状态管理）+ `use-class-filters` hook（筛选逻辑） |

### P2（优化 — 架构模式）

| 编号 | 问题 | 改进方向 |
|------|------|---------|
| P2-A | 缺少 Service 接口抽象 | 定义 `SchoolService` / `ClassService` 接口，通过 Context DI 注入（中长期） |
| P2-B | 缺少角色配置驱动 | 建立 `CLASS_ROLE_CONFIG`，通过配置决定不同角色渲染哪些 Widget（中长期） |

---

## 五、架构图同步说明

### 5.1 需更新的节点

| 文档 | 需更新内容 |
|------|-----------|
| `004_architecture_impact_map.md` §2.7 classes | 更新组件 i18n 状态（❌ → ✅）、更新 Error Boundary 状态（❌ → ✅）、更新组合模式状态、更新文件行数 |
| `004_architecture_impact_map.md` §2.8 school | 更新 `grades-view.tsx` 行数（860 → 拆分后）、更新组件拆分情况 |
| `005_architecture_data.json` | 更新 classes 模块的 i18n/errorBoundary/composition 状态标记 |

### 5.2 实施后同步

本次审计实施后，需同步更新：
- classes 模块的 i18n 接入状态
- classes 模块的 Error Boundary/Skeleton 新增
- grades-view.tsx 拆分后的文件清单
- classes/data-access.ts 拆分后的文件清单
- classes 组件拆分后的文件清单

---

## 附录：审计检查清单（v2）

| 检查项 | school | classes | 状态 |
|--------|:------:|:-------:|------|
| 三层架构划分合理 | ✅ | ✅ | v1 已修复 |
| 文件大小符合规范 | ⚠️ grades-view 860 行 | ⚠️ data-access 833 行 | P1-A/P1-B |
| 无跨模块直接依赖 | ✅ | ✅ | v1 已修复 |
| Server Action 权限校验 | ✅ | ✅ | v1 已修复 |
| 前端无 role 硬编码 | ✅ | ✅ | v1 已修复 |
| i18n 适配 | ✅ | ❌ 13/14 组件未接入 | P0-A/P0-B |
| 错误处理/边界 | ✅ | ❌ 无 Error Boundary | P1-C |
| 骨架屏/空状态 | ✅ | ⚠️ 有空状态无骨架屏 | P1-C |
| 逻辑与 UI 分离 | ✅ use-school-data | ❌ 无 hooks | P1-E |
| 组合模式 | ✅ | ❌ 三套单体组件 | P1-D |
| 配置驱动 | ❌ | ❌ | P2-B（中长期） |
| 审计日志完整 | ✅ | ✅ | v1 已修复 |
| 监控埋点接口 | ❌ | ❌ | P2（中长期） |
