# 仪表盘模块审计报告

> 审查日期：2026-06-22
> 审查范围：`src/modules/dashboard/**`、`src/app/(dashboard)/*/dashboard/**`、`src/modules/parent/components/parent-dashboard.tsx`（家长端仪表盘）
> 架构图参考：`docs/architecture/004_architecture_impact_map.md` §1.4.3、`docs/architecture/005_architecture_data.json`

---

## 一、现有实现概要

### 1.1 文件分布

| 层 | 路径 | 文件数 | 说明 |
|----|------|--------|------|
| 路由层 | `src/app/(dashboard)/{admin,teacher,student,parent}/dashboard/` | 4 个 `page.tsx` + 3 个 `error.tsx` + 3 个 `loading.tsx` | 各角色独立路由，另有根 `/dashboard/page.tsx` 做角色重定向 |
| 模块层 - admin | `src/modules/dashboard/components/admin-dashboard/` | 2 个（`admin-dashboard.tsx` 263 行、`user-growth-chart.tsx` 46 行） | |
| 模块层 - teacher | `src/modules/dashboard/components/teacher-dashboard/` | 9 个组件 | `teacher-dashboard-view.tsx` 为容器，含业务计算逻辑 |
| 模块层 - student | `src/modules/dashboard/components/student-dashboard/` | 6 个组件 | `student-dashboard-view.tsx` 为容器 |
| 模块层 - parent | `src/modules/parent/components/parent-dashboard.tsx` | 1 个（108 行） | **不在 dashboard 模块内**，位于 parent 模块 |
| 数据层 | `src/modules/dashboard/data-access.ts` | 1 个（49 行） | 仅 `getAdminDashboardData`，并行调用 6 个模块的 stats 函数 |
| 类型层 | `src/modules/dashboard/types.ts` | 1 个（74 行） | Admin / Teacher / Student 类型定义 |
| Actions 层 | **缺失** | 0 | 无 `actions.ts`，页面直接调用 data-access |

### 1.2 数据流

```
[Route] /admin/dashboard/page.tsx
   └─▶ dashboard/data-access.getAdminDashboardData()
         └─▶ Promise.all(users/classes/textbooks/questions/exams/homework stats)

[Route] /teacher/dashboard/page.tsx
   ├─▶ classes/data-access.getTeacherClasses / getClassSchedule
   ├─▶ homework/data-access.getHomeworkAssignments / getHomeworkSubmissions / getTeacherGradeTrends
   └─▶ users/data-access.getUserBasicInfo
   （页面层直接编排 3 个模块的 data-access）

[Route] /student/dashboard/page.tsx
   ├─▶ users/data-access.getCurrentStudentUser
   ├─▶ classes/data-access.getStudentClasses / getStudentSchedule
   └─▶ homework/data-access.getStudentHomeworkAssignments / getStudentDashboardGrades
   （页面层直接编排 3 个模块的 data-access + 业务计算）

[Route] /parent/dashboard/page.tsx
   └─▶ parent/data-access.getParentDashboardData
```

### 1.3 架构图记录情况

`004_architecture_impact_map.md` §1.4.3 记录了 admin 仪表盘聚合链路（P0-4 已修复跨模块直查），但存在遗漏：
- **未记录 teacher / student / parent 仪表盘的调用链路**
- **未记录 dashboard 模块的 exports 清单**（005 JSON 中 dashboard 节点缺失 `exports` 字段）
- **未记录 parent 仪表盘组件位于 parent 模块这一结构异常**

---

## 二、现存问题与原因分析

### 2.1 安全性：权限校验完全缺失（P0）

| 位置 | 问题 | 违反规则 |
|------|------|----------|
| [admin/dashboard/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/admin/dashboard/page.tsx) | 直接调用 `getAdminDashboardData()`，**无任何 auth/permission 校验** | "所有 Server Action 必须调用 `requirePermission()` 进行权限校验" |
| [teacher/dashboard/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/teacher/dashboard/page.tsx) | 仅调用 `getAuthContext()`，未校验任何权限点 | 同上 |
| [student/dashboard/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/student/dashboard/page.tsx) | **无任何 auth 调用**，完全依赖 layout 守卫 | 同上 |
| [parent/dashboard/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/parent/dashboard/page.tsx) | 调用 `requireAuth()`，未校验具体权限 | 同上 |
| [permissions.ts](file:///e:/Desktop/CICD/src/shared/types/permissions.ts) | **无 dashboard 相关权限点定义** | 权限体系不完整 |

**后果**：admin 仪表盘数据（含全校用户数、活跃会话数、最近注册用户列表）可被任意已登录用户访问，属于严重越权。即使 layout 层有路由组守卫，data-access 层仍缺乏二次校验，不符合"Server Action 二次校验"要求。

### 2.2 架构分层：页面层越权编排 + 模块归属错位（P0）

| 位置 | 问题 | 违反规则 |
|------|------|----------|
| [teacher/dashboard/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/teacher/dashboard/page.tsx) L16-23 | 页面层直接 `Promise.all` 调用 classes/homework/users 三个模块的 data-access | "app/ 只能调用 modules/ 的 Server Actions 和 data-access" — 虽然语法允许，但编排逻辑应在 dashboard 模块的 actions/data-access 层完成 |
| [student/dashboard/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/student/dashboard/page.tsx) L36-86 | 页面层包含 weekday 转换、作业状态统计、排序切片等 **80 行业务逻辑** | "Server Actions / Data Access 模块"应承担编排职责；纯逻辑应抽为 hooks/纯函数 |
| [parent-dashboard.tsx](file:///e:/Desktop/CICD/src/modules/parent/components/parent-dashboard.tsx) | 家长仪表盘组件位于 `modules/parent` 而非 `modules/dashboard` | 仪表盘模块不完整，四角色仪表盘分散在两个模块 |
| dashboard 模块无 `actions.ts` | 缺失编排层 | "模块标准结构"要求 `actions.ts`（编排层） |

**后果**：页面层臃肿、逻辑不可复用、不可测试；新增角色需复制粘贴整页编排逻辑。

### 2.3 角色硬编码（P0）

| 位置 | 代码 | 违反规则 |
|------|------|----------|
| [dashboard/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/dashboard/page.tsx) L12-15 | `roles.includes("admin")` / `roles.includes("student")` / `roles.includes("parent")` | "前端权限判断统一使用 `usePermission().hasPermission()`，严禁出现 `role === 'xxx'` 硬编码" |
| [auth-guard.ts](file:///e:/Desktop/CICD/src/shared/lib/auth-guard.ts) L69/L74/L86/L118/L131 | `roleNames.includes("admin"/"teacher"/"student"/"parent")` | 同上（dataScope 解析也基于角色硬编码） |

**后果**：新增角色（如 grade_head 已存在但未处理仪表盘重定向）无法正确路由；权限策略变更需改多处代码。

### 2.4 国际化：零覆盖 + 中英混杂（P0）

| 位置 | 问题 |
|------|------|
| [admin-dashboard.tsx](file:///e:/Desktop/CICD/src/modules/dashboard/components/admin-dashboard/admin-dashboard.tsx) | L34 `"Dashboard"`、L63 `"Users"` 为英文；L74 `"批量导入用户"`、L113 `"用户增长趋势（近30天）"` 为中文 — **同一文件中英混杂** |
| [teacher-dashboard-header.tsx](file:///e:/Desktop/CICD/src/modules/dashboard/components/teacher-dashboard/teacher-dashboard-header.tsx) L13-16 | `greeting = "早上好"/"下午好"/"晚上好"` 硬编码 |
| [teacher-dashboard-view.tsx](file:///e:/Desktop/CICD/src/modules/dashboard/components/teacher-dashboard/teacher-dashboard-view.tsx) L53-55 | `"待批改作业"` / `"今日待考勤"` / `"进行中作业"` 硬编码 |
| [student-stats-grid.tsx](file:///e:/Desktop/CICD/src/modules/dashboard/components/student-dashboard/student-stats-grid.tsx) | `"Enrolled Classes"` / `"Average Score"` 等全英文硬编码 |
| [parent-dashboard.tsx](file:///e:/Desktop/CICD/src/modules/parent/components/parent-dashboard.tsx) L28-31 | `"Good morning"` / `"Good afternoon"` 硬编码 |
| [user-growth-chart.tsx](file:///e:/Desktop/CICD/src/modules/dashboard/components/admin-dashboard/user-growth-chart.tsx) L42 | `name="新增用户"` 硬编码 |
| `messages/` 目录 | **无 `dashboard.json`**，仅 onboarding/classes/auth/errors/common 有翻译文件 |

**违反规则**："所有用户可见文本必须适配 i18n（使用 next-intl），提取翻译键"。

**后果**：无法切换语言；维护时需逐文件改字符串；中英混杂给用户造成混乱。

### 2.5 错误与边界处理：仅路由级（P1）

| 位置 | 问题 |
|------|------|
| `error.tsx` / `loading.tsx` | 仅存在于路由级（`app/(dashboard)/*/dashboard/`），**无按数据区块的 Error Boundary** |
| [admin-dashboard.tsx](file:///e:/Desktop/CICD/src/modules/dashboard/components/admin-dashboard/admin-dashboard.tsx) | 6 张 Card + 1 张表格，任一数据源异常导致整页崩溃 |
| [teacher-dashboard-view.tsx](file:///e:/Desktop/CICD/src/modules/dashboard/components/teacher-dashboard/teacher-dashboard-view.tsx) | 7 个子区块，无独立 Suspense 包裹 |
| error.tsx 文案 | `"页面加载失败"` 硬编码中文，未 i18n |

**违反规则**："每个独立的数据区块必须用 React Error Boundary 包裹"、"异步数据使用 React Suspense + 骨架屏"。

**后果**：单个 Widget 故障导致整页不可用；无法流式渲染，首屏白屏时间长。

### 2.6 可测试性：业务逻辑与 UI 耦合（P1）

| 位置 | 耦合的逻辑 |
|------|-----------|
| [teacher-dashboard-view.tsx](file:///e:/Desktop/CICD/src/modules/dashboard/components/teacher-dashboard/teacher-dashboard-view.tsx) L18-56 | `toWeekday`、`todayScheduleItems` 过滤排序、`toGradeCount`/`submissionRate` 计算、`todoItems` 聚合 — 全部内联在组件中 |
| [student/dashboard/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/student/dashboard/page.tsx) L13-86 | `toWeekday`、`dueSoonCount`/`overdueCount`/`gradedCount` 单次遍历统计、`upcomingAssignments` 排序切片 — 80 行纯逻辑在 Server Component 中 |
| [parent-dashboard.tsx](file:///e:/Desktop/CICD/src/modules/parent/components/parent-dashboard.tsx) L27-31 | greeting 时段判断内联在组件中 |

**违反规则**："数据获取、计算、格式化等纯逻辑全部放入纯函数或 hooks，与 UI 分离"。

**后果**：无法对统计逻辑做单元测试；逻辑变更需改组件代码；复用需复制粘贴。

### 2.7 可复用性：四角色零共享抽象（P1）

| 维度 | 现状 |
|------|------|
| 布局容器 | admin/teacher/student/parent 各写一套 `<div className="space-y-*">`，无统一 `DashboardLayout` |
| 统计卡片 | 已复用 `shared/components/ui/stat-card.tsx`（✅ 良好） |
| 快捷操作 | admin 的 `QuickActionCard`（内联）、parent 的 `QUICK_ENTRIES`（内联）、teacher 的 `TeacherQuickActions` — 三套独立实现，无统一 `QuickActions` 组件 |
| 待办/任务 | 仅 teacher 有 `TeacherTodoCard`，student/admin/parent 无类似组件 |
| 问候语 | teacher/parent 各写一套 `hour < 12 ? "早上好" : ...`，无统一 `useGreeting` hook |
| Widget 配置 | 无配置驱动设计，新增角色需新建整套组件 |

**违反规则**："最大化复用：识别四个角色共用的 UI 块和业务逻辑块，抽象为泛型组件和 hooks"、"采用配置驱动设计"。

### 2.8 性能：全量 force-dynamic 无流式渲染（P2）

| 位置 | 问题 |
|------|------|
| 所有 `page.tsx` | `export const dynamic = "force-dynamic"`，`Promise.all` 等全部数据就绪后才渲染 |
| 无 `<Suspense>` 包裹 | 无法流式渲染，首屏 TTFB 到 FCP 全部阻塞 |

**违反规则**："优先使用 React Server Components 获取初始数据；客户端组件仅负责交互；支持流式渲染"。

### 2.9 可访问性（P2）

| 位置 | 问题 |
|------|------|
| admin-dashboard.tsx | 表格无 `caption`，快捷操作 Card 作为链接无 `aria-label` |
| teacher-dashboard-view.tsx | 布局 div 无语义化标签（`<section>` / `<aside>`） |
| student-dashboard-view.tsx | 同上 |

**违反规则**："语义化标签、ARIA 属性、键盘导航"。

---

## 三、行业差距对比

### 3.1 K12 仪表盘主流设计模式

| 模式 | 行业实践 | 本项目现状 | 差距影响 |
|------|----------|------------|----------|
| **Widget 网格系统** | 可拖拽、可配置的 Widget 卡片（如 PowerSchool、Veracross） | 四角色各自硬编码布局 | 无法个性化，新增角色需重写 |
| **跨角色数据联动** | 家长端预览孩子仪表盘、教师端查看学生上下文 | 四角色完全隔离 | 家长需跳转多个页面才能了解孩子情况 |
| **可操作洞察** | "3 名学生成绩下滑"、"2 份作业待批改超 3 天" 等智能提醒 | 仅展示静态数字 | 管理者/教师需手动分析，效率低 |
| **通知中心集成** | 仪表盘首屏显示未读通知摘要 | 无通知集成 | 用户需进入消息模块查看 |
| **统一日历** | 跨模块日历视图（作业/考试/考勤/请假） | 无 | 师生需在多个模块间切换查看日程 |
| **学习进度可视化** | 学生学习路径、知识点掌握雷达图 | student 仅有成绩卡片 | 学生无法直观了解学习状态 |
| **空状态引导** | 无数据时提供 CTA（"创建第一个作业"） | admin 有部分 EmptyState，其他角色缺失 | 新用户不知下一步操作 |
| **实时更新** | 活跃会话数、待批改数 WebSocket 推送 | 全静态 | 数据滞后，需手动刷新 |
| **响应式适配** | 移动端优先布局 | parent 有移动端横向滑动，其他角色仅 `md:` 断点 | 移动端体验差 |

### 3.2 各角色差距详述

**Admin**：
- 缺少学校运营关键指标（出勤率、作业完成率趋势）
- 用户增长趋势图为空（`userGrowth: []` 硬编码在 data-access L46）
- 无系统健康监控（DB 连接数、API 延迟等）

**Teacher**：
- 缺少班级对比视图（哪个班表现最好/最差）
- 缺少学生预警列表（成绩下滑/未提交作业的学生）
- 课表仅显示今日，无本周概览

**Student**：
- 缺少学习目标/进度跟踪
- 缺少同学协作入口（小组作业、学习伙伴）
- 成绩仅显示排名，无知识点维度分析

**Parent**：
- 缺少多孩子对比视图
- 缺少与教师沟通快捷入口
- 缺少孩子出勤/成绩异常告警

---

## 四、改进优先级建议

### P0（紧急 — 安全与合规）

| # | 问题 | 改进方向 |
|---|------|----------|
| P0-1 | 权限校验完全缺失 | 新增 `DASHBOARD_ADMIN_READ` / `DASHBOARD_TEACHER_READ` / `DASHBOARD_STUDENT_READ` / `DASHBOARD_PARENT_READ` 权限点；创建 `actions.ts`，每个 Action 调用 `requirePermission()` |
| P0-2 | 根重定向页角色硬编码 | 改用 `hasPermission(DASHBOARD_*_READ)` 决定重定向目标 |
| P0-3 | i18n 零覆盖 | 创建 `messages/{zh-CN,en}/dashboard.json`；所有组件接入 `useTranslations` / `getTranslations` |
| P0-4 | 页面层越权编排 | 将 teacher/student/parent 的数据编排下沉到 `dashboard/actions.ts` 或 `data-access.ts` |

### P1（较严重 — 架构与质量）

| # | 问题 | 改进方向 |
|---|------|----------|
| P1-1 | 业务逻辑耦合 UI | 抽取 `hooks/use-teacher-dashboard-metrics.ts`、`hooks/use-student-dashboard-metrics.ts`、`lib/weekday.ts`（纯函数） |
| P1-2 | 四角色零共享 | 抽象 `DashboardLayout`、`QuickActions`、`GreetingHeader`、`WidgetBoundary`（Error Boundary + Suspense 组合） |
| P1-3 | 仅路由级错误边界 | 每个数据区块用 `<WidgetBoundary>` 包裹，支持独立 fallback |
| P1-4 | parent 仪表盘归属错位 | 将 `parent-dashboard.tsx` 迁移至 `modules/dashboard/components/parent-dashboard/`，或保留在 parent 模块但在架构图中明确标注 |
| P1-5 | 无流式渲染 | 用 `<Suspense>` 包裹各 Widget，数据获取改为独立 async 组件 |

### P2（优化 — 体验与扩展）

| # | 问题 | 改进方向 |
|---|------|----------|
| P2-1 | 无 Widget 配置系统 | 设计 `DashboardWidgetConfig` 类型，按角色配置渲染哪些 Widget |
| P2-2 | a11y 不足 | 补充语义化标签、ARIA 属性、表格 caption |
| P2-3 | 无单测 | 为抽取的纯函数/hooks 添加单测 |
| P2-4 | 行业功能差距 | 逐步补齐通知集成、统一日历、学生预警等（按角色优先级迭代） |

---

## 五、架构图同步说明

本次审计发现架构图存在以下遗漏，需在实现后同步更新：

### 5.1 `004_architecture_impact_map.md` 需补充

1. **§1.4 调用链路**：新增 teacher / student / parent 仪表盘调用链路（当前仅记录 admin）
2. **dashboard 模块章节**：补充 `actions.ts`（新增）、`hooks/`（新增）、`lib/`（新增）描述
3. **parent 模块章节**：标注 parent-dashboard 组件的归属决策

### 5.2 `005_architecture_data.json` 需修改

1. `modules.dashboard` 节点：
   - 新增 `exports`：`getAdminDashboardData`、`getTeacherDashboardData`（新增）、`getStudentDashboardData`（新增）、`getParentDashboardData`（迁移或代理）
   - 新增 `actions`：`getAdminDashboardAction` 等
   - 新增 `hooks`：`useTeacherDashboardMetrics`、`useStudentDashboardMetrics`
2. `permissions` 节点：新增 `DASHBOARD_*_READ` 四个权限点
3. `routes` 节点：补充 teacher/student/parent dashboard 调用链
4. `dependencyMatrix`：更新 dashboard → classes/homework/users 的依赖关系（通过 actions 层而非页面层）

### 5.3 翻译文件结构示例

```
src/shared/i18n/messages/
├─ zh-CN/
│  └─ dashboard.json    # 新增
└─ en/
   └─ dashboard.json    # 新增
```

`dashboard.json` 结构示例（zh-CN）：

```json
{
  "title": {
    "admin": "管理控制台",
    "teacher": "教师工作台",
    "student": "学生中心",
    "parent": "家长中心"
  },
  "greeting": {
    "morning": "早上好",
    "afternoon": "下午好",
    "evening": "晚上好",
    "welcome": "欢迎回来"
  },
  "stats": {
    "users": "用户总数",
    "classes": "班级数",
    "activeSessions": "活跃会话",
    "toGrade": "待批改",
    "enrolledClasses": "已选课程",
    "averageScore": "平均分",
    "classRank": "班级排名",
    "graded": "已批改",
    "dueSoon": "即将到期",
    "overdue": "已逾期"
  },
  "quickActions": {
    "importUsers": "批量导入用户",
    "newAnnouncement": "发布公告",
    "approveSchedule": "审批课表变更",
    "autoSchedule": "自动排课",
    "fileManagement": "文件管理",
    "attendanceOverview": "考勤总览"
  },
  "todo": {
    "title": "今日待办",
    "toGrade": "待批改作业",
    "todayAttendance": "今日待考勤",
    "activeAssignments": "进行中作业",
    "empty": "今日无待办事项"
  },
  "empty": {
    "noUsers": "暂无用户",
    "noChildren": "未绑定孩子",
    "allGraded": "全部批改完成！"
  },
  "error": {
    "loadFailed": "页面加载失败",
    "retry": "重试"
  }
}
```
