refactor(dashboard): V2 审计重构 — i18n 补齐 + 共享抽象 + 单测 + a11y

V2 审计报告(docs/architecture/audit/dashboard-audit-report-v2.md)发现并修复:

- P0 i18n:10 个子组件硬编码字符串全部接入 next-intl(teacher-quick-actions /
  teacher-classes-card / teacher-homework-card / teacher-schedule /
  recent-submissions / teacher-grade-trends / student-grades-card /
  student-today-schedule-card / student-upcoming-assignments-card /
  admin-dashboard),新增 ~50 个翻译键
- P1 共享抽象:新增 DashboardGreetingHeader 组件,消除 teacher/student
  头部 90% 重复代码,两个 Header 改为薄包装
- P2 单测:为 6 个纯函数添加 31 个单元测试
  (tests/integration/dashboard/dashboard-utils.test.ts)
- P2 a11y:admin 表格 caption、teacher/student 视图语义化标签
  (header / section aria-label / aside aria-label)
- 同步架构图 004/005
This commit is contained in:
SpecialX
2026-06-22 17:01:00 +08:00
parent 10c668f36a
commit e997abaf5e
41 changed files with 1811 additions and 516 deletions

View File

@@ -0,0 +1,121 @@
# 仪表盘模块审计报告 v2
> 审查日期2026-06-22第二轮
> 审查范围:基于 v1 重构后代码commit `868ac5f` + `21c1e7a`)的再次分析
> 前置报告:`docs/architecture/audit/dashboard-audit-report.md`v1
> 架构图参考:`docs/architecture/004_architecture_impact_map.md` §2.12、`docs/architecture/005_architecture_data.json`
---
## 一、v1 重构成果回顾
v1 报告识别的 P0/P1/P2 项目已完成的部分:
| # | 项目 | 状态 | 证据 |
|---|------|------|------|
| P0-1 | 权限校验 | ✅ 已完成 | [actions.ts](file:///e:/Desktop/CICD/src/modules/dashboard/actions.ts) 4 个 Server Action 均调用 `requirePermission()` |
| P0-2 | 根重定向角色硬编码 | ✅ 已完成 | [dashboard/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/dashboard/page.tsx) 改用 `resolvePermissions()` |
| P0-3 | i18n 零覆盖 | ⚠️ 部分完成 | 仅容器组件接入 i18n**10 个子组件仍英文硬编码** |
| P0-4 | 页面层越权编排 | ✅ 已完成 | teacher/student/parent 编排下沉至 actions.ts |
| P1-1 | 业务逻辑耦合 UI | ✅ 已完成 | [lib/dashboard-utils.ts](file:///e:/Desktop/CICD/src/modules/dashboard/lib/dashboard-utils.ts) 抽取 6 个纯函数 |
| P1-3 | 仅路由级错误边界 | ✅ 已完成 | [dashboard-section.tsx](file:///e:/Desktop/CICD/src/modules/dashboard/components/dashboard-section.tsx) 分区 Error Boundary + Suspense |
| P2-2 | a11y 不足 | ❌ 未完成 | 仍缺语义化标签、表格 caption |
---
## 二、v2 新发现问题
### 2.1 i18n 覆盖严重不完整P0 — v1 遗漏)
v1 仅对容器组件(`admin-dashboard.tsx``teacher-dashboard-view.tsx``teacher-dashboard-header.tsx``teacher-stats.tsx``teacher-todo-card.tsx``student-stats-grid.tsx``student-dashboard-header.tsx``parent-dashboard.tsx``user-growth-chart.tsx`)接入 i18n**10 个子组件仍全英文硬编码**
| # | 文件 | 硬编码示例 | 违反规则 |
|---|------|-----------|----------|
| 1 | [teacher-quick-actions.tsx](file:///e:/Desktop/CICD/src/modules/dashboard/components/teacher-dashboard/teacher-quick-actions.tsx) L12-24 | `"Create Assignment"` / `"Grade"` / `"My Classes"` | "所有用户可见文本必须适配 i18n" |
| 2 | [teacher-classes-card.tsx](file:///e:/Desktop/CICD/src/modules/dashboard/components/teacher-dashboard/teacher-classes-card.tsx) L15-27 | `"My Classes"` / `"View all"` / `"No classes yet"` / `"Create a class to start managing students and schedules."` / `"Create class"` / `"Homeroom"` / `"Room"` | 同上 |
| 3 | [teacher-homework-card.tsx](file:///e:/Desktop/CICD/src/modules/dashboard/components/teacher-dashboard/teacher-homework-card.tsx) L17-87 | `"Homework"` / `"Create new assignment"` / `"No assignments"` / `"Create an assignment to get started."` / `"Create"` / `"No due date"` / `"View all assignments"` | 同上 |
| 4 | [teacher-schedule.tsx](file:///e:/Desktop/CICD/src/modules/dashboard/components/teacher-dashboard/teacher-schedule.tsx) L41-141 | `"Today's Schedule"` / `"No Classes Today"` / `"No timetable entries."` / `"View schedule"` / `"LIVE"` / `"Scroll for more"` / `"No more classes today"` | 同上 |
| 5 | [recent-submissions.tsx](file:///e:/Desktop/CICD/src/modules/dashboard/components/teacher-dashboard/recent-submissions.tsx) L22-105 | `"Recent Submissions"` / `"No New Submissions"` / `"All caught up!..."` / `"View All"` / `"View submissions"` / `"Student"` / `"Assignment"` / `"Submitted"` / `"Action"` / `"Late"` / `"Grade"` | 同上 |
| 6 | [teacher-grade-trends.tsx](file:///e:/Desktop/CICD/src/modules/dashboard/components/teacher-dashboard/teacher-grade-trends.tsx) L25-69 | `"Class Performance"` / `"Average scores for the last X assignments"` / `"No data available"` / `"Publish assignments to see class performance trends."` / `"Average Score (%)"` / `"X/Y submitted"` | 同上 |
| 7 | [student-grades-card.tsx](file:///e:/Desktop/CICD/src/modules/dashboard/components/student-dashboard/student-grades-card.tsx) L30-101 | `"Recent Grades"` / `"No graded work yet"` / `"Finish and submit assignments to see your score trend."` / `"View all"` / `"Score (%)"` / `"Latest:"` / `"Points:"` / `"Assignment"` / `"Score"` / `"When"` | 同上 |
| 8 | [student-today-schedule-card.tsx](file:///e:/Desktop/CICD/src/modules/dashboard/components/student-dashboard/student-today-schedule-card.tsx) L52-83 | `"Today's Schedule"` / `"View all"` / `"No classes today"` / `"Your timetable is clear for today."` / `"In Progress"` / `"Up Next"` | 同上 |
| 9 | [student-upcoming-assignments-card.tsx](file:///e:/Desktop/CICD/src/modules/dashboard/components/student-dashboard/student-upcoming-assignments-card.tsx) L17-22,49-72 | `"Review"` / `"View"` / `"Continue"` / `"Start"` / `"Upcoming Assignments"` / `"View all"` / `"No assignments"` / `"You have no assigned homework right now."` / `"Title"` / `"Status"` / `"Due"` / `"Score"` / `"Action"` / `"Late"` | 同上 |
| 10 | [admin-dashboard.tsx](file:///e:/Desktop/CICD/src/modules/dashboard/components/admin-dashboard/admin-dashboard.tsx) L212 | `{u.role ?? "unknown"}` 硬编码 `"unknown"` | 同上 |
**后果**:中文用户看到大量英文,体验割裂;无法切换语言;维护时需逐文件改字符串。
### 2.2 四角色仍零共享抽象P1 — v1 未处理)
| 维度 | 现状 | 期望 |
|------|------|------|
| 问候语头部 | `TeacherDashboardHeader``StudentDashboardHeader` 代码 90% 重复(仅 props 名不同) | 抽象为 `DashboardGreetingHeader` |
| 快捷操作 | admin 的 `QuickActionCard`内联、parent 的 `QUICK_ENTRIES`内联、teacher 的 `TeacherQuickActions` — 三套独立实现 | 抽象为 `DashboardQuickActions` |
| 仪表盘布局容器 | admin/teacher/student 各写一套 `<div className="space-y-*">` | 抽象为 `DashboardLayout` |
**违反规则**"最大化复用:识别四个角色共用的 UI 块和业务逻辑块,抽象为泛型组件和 hooks"。
### 2.3 无单测P2 — v1 未处理)
`lib/dashboard-utils.ts` 抽取了 6 个纯函数但**无任何单测**
| 函数 | 测试覆盖 | 风险 |
|------|----------|------|
| `toWeekday` | ❌ 无 | 周日映射错误未被发现 |
| `countStudentAssignments` | ❌ 无 | 边界条件(无截止日期/已批改)未验证 |
| `sortUpcomingAssignments` | ❌ 无 | 排序稳定性未验证 |
| `filterTodaySchedule` | ❌ 无 | 空课表/排序未验证 |
| `computeTeacherMetrics` | ❌ 无 | 提交率分母为零等边界未验证 |
| `getGreetingKey` | ❌ 无 | 时段边界12:00/18:00未验证 |
**违反规则**"数据获取、计算、格式化等纯逻辑全部放入纯函数或 hooks与 UI 分离;导出清晰的接口类型以便 mock" + "可测试性"。
### 2.4 a11y 不足P2 — v1 未处理)
| 位置 | 问题 | 违反规则 |
|------|------|----------|
| `admin-dashboard.tsx` 表格 | 无 `<caption>` | "语义化标签、ARIA 属性、键盘导航" |
| `recent-submissions.tsx` 表格 | 无 `<caption>` | 同上 |
| `student-upcoming-assignments-card.tsx` 表格 | 无 `<caption>` | 同上 |
| `teacher-dashboard-view.tsx` 布局 | 无 `<section>` / `<aside>` 语义化标签 | 同上 |
| `student-dashboard-view.tsx` 布局 | 同上 | 同上 |
| `teacher-schedule.tsx` 时间线 | 无 `aria-label` 描述当前/过去/未来状态 | 同上 |
### 2.5 流式渲染未实现P1 — v1 未处理)
所有 `page.tsx``export const dynamic = "force-dynamic"` + `Promise.all` 等全部数据就绪后才渲染。虽然 `DashboardSection` 内部有 Suspense但 page 层已无 Suspense 边界,无法流式渲染首屏。
---
## 三、改进优先级v2
### P0紧急 — v1 遗漏的 i18n
| # | 问题 | 改进方向 |
|---|------|----------|
| v2-P0-1 | 10 个组件英文硬编码 | 全部接入 `useTranslations` / `getTranslations`;补充翻译键 |
### P1较严重 — 共享抽象 + 单测)
| # | 问题 | 改进方向 |
|---|------|----------|
| v2-P1-1 | 问候语头部重复 | 抽象 `DashboardGreetingHeader` 组件 |
| v2-P1-2 | 纯函数无单测 | 为 `lib/dashboard-utils.ts` 6 个函数添加单测 |
### P2优化 — a11y + 流式)
| # | 问题 | 改进方向 |
|---|------|----------|
| v2-P2-1 | 表格无 caption / 布局无语义化标签 | 补充 `<caption>` / `<section>` / `aria-label` |
---
## 四、架构图同步说明
v2 修改完成后需同步更新:
### 4.1 `004_architecture_impact_map.md`
- §2.12 dashboard 章节:补充新增共享组件(`DashboardGreetingHeader`)、单测文件(`lib/dashboard-utils.test.ts`
### 4.2 `005_architecture_data.json`
- `modules.dashboard.exports.components`:新增 `DashboardGreetingHeader`
- `modules.dashboard.exports.lib`:补充单测覆盖说明