Files
NextEdu/docs/architecture/audit/school-grade-class-audit-report.md
SpecialX 89b9e181d2 docs(audit): add audit reports for grades, homework, lesson-preparation, messaging, permissions, question-bank, settings, textbooks
- Add grades-audit-report

- Add homework-audit-report and homework-exams-audit-report

- Add lesson-preparation-audit-report-v3 and v4

- Add messaging-audit-report

- Add permissions-audit-report

- Add question-bank-audit-report

- Add settings-profile-audit-report-v3

- Add textbooks-audit-report-v3
2026-07-03 10:23:34 +08:00

275 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 学校/年级/班级管理模块审计报告 v2
> 审查范围:`school`(学校/学年/部门/年级 CRUD、`classes`(班级管理)
> 审查日期2026-06-22v2 复审)
> 审查依据项目规则三层架构、权限校验、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-Aclasses 模块 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 模块的 i18nclasses 模块的 i18n 遗留未处理
- **后果**teacher/management 视角下的班级管理页面无法支持多语言;中英文混用严重影响专业度
#### P0-Bclasses.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-Cclasses 模块组件缺少 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-Dclasses 组件未使用组合模式
- **位置**
- `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` hookclasses 模块仍是单体组件,无法复用子部件
- **违反规则**:组合优先(所有 UI 通过组件组合实现灵活性)
- **后果**admin/grade/teacher 三个视角的班级管理存在大量重复代码(表单、对话框、筛选器),无法复用
#### P1-Eclasses 模块缺少 hooks 抽取
- **位置**`src/modules/classes/` — 无 `hooks/` 目录
- **问题**:对比 school 模块已抽取 `use-school-data` hookclasses 模块的对话框状态管理、表单校验、筛选逻辑全部耦合在组件内部
- **违反规则**:可测试性(数据获取、计算、格式化等纯逻辑全部放入纯函数或 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中长期 |