Files
NextEdu/docs/architecture/audit/archive/dashboard-audit-report-v4.md

18 KiB
Raw Blame History

Dashboard 模块 V4 审计报告

审计日期2026-06-22 审计范围src/modules/dashboard/ + 所有 dashboard 路由文件 + parent dashboard 组件 前置审计

  • v1P0 修复:跨模块 DB 查询、权限、i18n 容器组件)
  • v210 个子组件 i18n、DashboardGreetingHeader 抽象、31 个纯函数单测、a11y 语义化标签)
  • v3ContentRow 标签错配、admin/error.tsx i18n、空趋势数据空状态、loading/error.tsx 补齐、日期 locale、死代码清理、as 断言修复、流式架构 React use()

一、现有实现概要

1.1 文件分布

仪表盘模块位于 src/modules/dashboard/,包含 29 个文件:

文件 行数 职责
actions actions.ts 167 4 个 Server Actionadmin/teacher/student/parent均调用 requirePermission()
data-access data-access.ts 49 admin 仪表盘数据聚合(并行调用 6 个模块 stats 函数)
streams streams.ts 34 admin 流式数据源(返回未解析 Promise 供 React use() 消费)
types types.ts 74 AdminDashboardData / StudentDashboardProps / TeacherDashboardData
lib lib/dashboard-utils.ts 198 6 个纯函数weekday / 统计 / 排序 / 指标计算 / 问候语)
components dashboard-section.tsx 170 Error Boundary + Suspense + 骨架屏5 种变体)
components dashboard-greeting-header.tsx 36 共享问候头部
components dashboard-error-fallback.tsx 30 路由级错误回退
components dashboard-loading-skeleton.tsx 44 路由级加载骨架
admin-dashboard admin-dashboard.tsx 173 管理员视图(流式架构)
admin-dashboard admin-sections.tsx 231 管理员 6 个分区组件
admin-dashboard user-growth-chart.tsx 65 recharts 折线图
teacher-dashboard 9 文件 ~700 教师仪表盘组件
student-dashboard 6 文件 ~530 学生仪表盘组件
tests dashboard-section.test.tsx 70 Error Boundary + 骨架屏单测
tests tests/integration/dashboard/dashboard-utils.test.ts 408 6 个纯函数 31 个单测

1.2 数据流

[Page] → [Action] → [requirePermission] → [data-access / 其他模块 data-access]
                                                    ↓
                                            [lib/dashboard-utils 纯函数计算]
                                                    ↓
                                        [View 组件] → [DashboardSection Suspense]

1.3 架构图记录完整性

架构影响地图004/005已覆盖 dashboard 模块的:

  • 4 个 Server Action 签名、依赖、使用方
  • 6 个纯函数签名和用途
  • 依赖矩阵dependsOn: shared/auth/homework/classes
  • 路由权限映射dashboardRoutePermissions
  • 组件清单和行数

遗漏:架构图未记录 streams.ts 的流式数据源函数,也未记录 parent dashboard 组件实际位于 modules/parent/components/ 的跨模块布局。


二、现存问题与原因分析

P0 问题(严重)

P0-1filterTodaySchedule 仍使用 as T[] 类型断言

  • 文件src/modules/dashboard/lib/dashboard-utils.ts
  • 行号120
  • 问题v3 审计P1-8已识别此问题并改为泛型函数但实现仍保留 as T[] 断言:
    return schedule
      .filter(...)
      .sort(...)
      .map((s) => ({ ... })) as T[]  // ← 违反"禁止 as 断言"
    
  • 违反规则项目规则「TypeScript 严格模式:禁止 as 断言(除类型收窄外)」
  • 后果:类型系统被绕过,map 返回的对象结构若与 T 不匹配,编译器不会报错,潜在运行时错误
  • 修复方向:移除 as T[],让 map 返回类型自然推导;或将映射逻辑提取为泛型映射函数

P1 问题(高)

P1-1组件内嵌纯函数未抽取到 lib

  • 文件
    • teacher-schedule.tsx 行 24-36getStatus(start, end) 计算课程状态
    • student-upcoming-assignments-card.tsx 行 18timeToMinutes(t)
    • student-upcoming-assignments-card.tsx 行 30-40getDueUrgency(dueAt)
    • student-upcoming-assignments-card.tsx 行 18-28getActionLabelKey(status) / getActionVariant(status)
    • student-today-schedule-card.tsx 行 17-20timeToMinutes(t)
  • 问题5 个纯函数散落在 3 个组件文件中,无法被单测覆盖,且 timeToMinutes 在两处重复定义
  • 违反规则:项目规则「可测试性:数据获取、计算、格式化等纯逻辑全部放入纯函数或 hooks与 UI 分离」
  • 后果:单测覆盖率无法提升;timeToMinutes 重复定义易产生不一致
  • 修复方向:全部迁移到 lib/dashboard-utils.ts,导出供组件调用,补充单测

P1-2teacherName 硬编码英文 fallback

  • 文件src/modules/dashboard/actions.ts
  • 行号85
  • 问题teacherName: teacherProfile?.name ?? "Teacher" — 当教师名称为空时 fallback 为硬编码英文 "Teacher",英文/中文用户都会看到英文
  • 违反规则:项目规则「所有用户可见文本必须适配 i18n」
  • 后果:中文用户在教师名称缺失时看到英文 "Teacher"i18n 不一致
  • 修复方向fallback 改为空字符串 "",由前端组件用 t("title.teacher") 处理空值

P1-3teacher-schedule.tsx 本地重复定义类型

  • 文件src/modules/dashboard/components/teacher-dashboard/teacher-schedule.tsx
  • 行号10-18
  • 问题:本地定义 TeacherTodayScheduleItem 类型,与 types.ts 中的同名类型结构完全相同,重复定义
  • 违反规则:项目规则「避免代码重复」
  • 后果:类型变更需同步两处,易产生不一致
  • 修复方向:从 types.ts 导入,删除本地定义

P1-4parent dashboard 组件位于 parent 模块而非 dashboard 模块

  • 文件src/modules/parent/components/parent-dashboard.tsx
  • 问题ParentDashboard 组件位于 parent 模块,但由 dashboard/actions.getParentDashboardAction 提供数据,且 parent/dashboard/page.tsx 同时导入两个模块的组件。架构图标注为"架构决策:保留在 parent 模块以避免移动文件破坏其他 import",但这造成模块边界模糊
  • 违反规则:项目规则「该模块必须作为独立功能单元」
  • 后果dashboard 模块不完整parent 仪表盘的 UI 逻辑分散在两个模块
  • 修复方向:将 ParentDashboard 组件迁移到 modules/dashboard/components/parent-dashboard/parent 模块仅保留数据访问

P2 问题(中)

P2-1无数据服务接口抽象

  • 文件src/modules/dashboard/actions.tsdata-access.ts
  • 问题dashboard 模块直接 import 其他 6 个模块的 data-access 函数classes/homework/users/parent/textbooks/questions/exams无 TypeScript 接口抽象。组件层无法 mock 数据依赖,单测必须 mock 整个模块
  • 违反规则:项目规则「完全解耦:通过定义 TypeScript 接口抽象数据依赖」
  • 后果:模块耦合度高,难以独立测试,新增角色需修改 actions.ts
  • 修复方向:定义 DashboardService 接口,为每个角色提供实现类,通过 React Context 注入

P2-2无配置驱动的 Widget 渲染

  • 文件admin-dashboard.tsxteacher-dashboard-view.tsxstudent-dashboard-view.tsx
  • 问题:每个角色的仪表盘视图硬编码渲染哪些 Widget如 admin 渲染 StatsBar + QuickActions + TrendCharts + 3 Cards + RecentUsersTable。新增角色或调整 Widget 需修改视图组件代码
  • 违反规则:项目规则「可扩展性:采用配置驱动设计」
  • 后果扩展性差4 个角色视图代码结构相似但无法复用
  • 修复方向:定义 DashboardWidgetConfig 类型,通过配置决定渲染哪些 Widget 及其布局

P2-3无监控埋点接口

  • 文件:整个模块
  • 问题:无任何用户行为埋点(如 Widget 点击、页面停留、空状态触发等),无法度量仪表盘使用情况
  • 违反规则:项目规则「监控:方案中预留关键操作埋点接口」
  • 后果:无法度量仪表盘使用情况,无法指导优化
  • 修复方向:定义 DashboardAnalytics 接口在关键交互点调用Widget 点击、空状态触发、错误重试)

P2-4admin dashboard userGrowthhomeworkTrend 仍为占位空数组

  • 文件src/modules/dashboard/data-access.ts
  • 行号46-47
  • 问题v3 已为 UserGrowthChart 添加空状态,但数据源仍硬编码 userGrowth: []homeworkTrend: [],趋势图表永远显示空状态
  • 违反规则:无直接违反,但影响用户体验
  • 后果:管理员无法看到用户增长和作业提交趋势
  • 修复方向:实现真实统计查询,或在 data-access 层添加 TODO 注释标记后续实现

P2-54 个角色 StatCard 使用模式不一致

  • 文件
    • admin-sections.tsxStatCard 直接传 valuenumber
    • teacher-stats.tsxStatCardvalue={String(count)} + color + highlight
    • student-stats-grid.tsxStatCardvalue={String(count)} + color + valueClassName + 条件颜色
  • 问题3 个角色的 StatCard 调用模式不一致admin 不传 colorteacher 传 colorstudent 传 color + valueClassName
  • 违反规则:项目规则「最大化复用:识别四个角色共用的 UI 块」
  • 后果:视觉不一致,维护成本高
  • 修复方向:统一 StatCard 调用模式,通过配置驱动颜色和样式

P3 问题(低)

P3-1无完整键盘导航支持

  • 问题:虽有 aria-label 属性,但 Widget 之间无 tabindex 管理,键盘用户无法按逻辑顺序遍历 Widget
  • 修复方向:为 Widget 容器添加 role="region" + aria-label,管理 tabindex

P3-2AdminTrendCharts 硬编码 data={[]}

  • 文件admin-sections.tsx 行 144、152
  • 问题UserGrowthChart 调用时传 data={[]},与 P2-4 相关
  • 修复方向:从 streams 获取真实趋势数据

P3-3teacher-todo-card.tsx 排序逻辑仍可优化

  • 文件teacher-todo-card.tsx 行 52-56
  • 问题v3 已优化排序逻辑,但仍使用 if (a.variant === "urgent") return -1 模式,可进一步用优先级映射
  • 修复方向:定义 VARIANT_PRIORITY 映射,用数值比较

三、行业差距对比

3.1 与优秀 K12 产品的差距

维度 我们当前 钉钉教育/智学网/ClassIn 差距影响
数据联动 各 Widget 独立展示,无联动 点击统计卡片可下钻到详情页 管理员无法快速从概览定位问题
个性化配置 固定布局,用户无法自定义 支持拖拽 Widget、隐藏/显示 不同用户关注点不同,固定布局降低效率
实时更新 静态数据,需刷新页面 WebSocket 实时推送待办数 待办数不实时,影响响应速度
多角色切换 通过权限路由到不同仪表盘 支持角色快速切换(如班主任+教师) 多角色用户需退出重新登录
数据导出 无导出功能 支持导出 PDF/Excel 管理员无法离线分析
通知集成 无通知集成 仪表盘集成待办通知 用户需切换页面查看通知
移动端适配 基本响应式,但 Widget 布局未优化 移动端优先设计,卡片堆叠 移动端体验不佳

3.2 缺失的关键功能

  1. Widget 下钻导航:统计卡片点击应跳转到对应详情页(部分已实现,但不完整)
  2. 时间范围筛选admin 无法切换"今日/本周/本月"数据范围
  3. 数据对比:无法对比不同时间段数据(如本周 vs 上周)
  4. 自定义仪表盘:用户无法选择显示哪些 Widget
  5. 通知中心集成:仪表盘未集成通知下拉

四、改进优先级建议

P0立即修复

编号 问题 改进方向
P0-1 filterTodayScheduleas T[] 断言 移除断言,改用类型守卫或泛型映射

P1高优先级

编号 问题 改进方向
P1-1 组件内嵌纯函数未抽取 迁移 5 个纯函数到 lib/dashboard-utils.ts,补充单测
P1-2 teacherName 硬编码 fallback 改为空字符串,前端用 i18n 处理
P1-3 teacher-schedule.tsx 本地类型重复 types.ts 导入
P1-4 parent dashboard 组件跨模块 迁移到 modules/dashboard/components/parent-dashboard/

P2中优先级 - 架构改进)

编号 问题 改进方向
P2-1 无数据服务接口抽象 定义 DashboardService 接口 + 角色实现 + Context 注入
P2-2 无配置驱动 Widget 渲染 定义 DashboardWidgetConfig,配置驱动渲染
P2-3 无监控埋点接口 定义 DashboardAnalytics 接口,预留埋点
P2-4 admin 趋势数据占位 添加 TODO 注释,标记后续实现
P2-5 StatCard 使用模式不一致 统一调用模式

P3低优先级 - 长期优化)

编号 问题 改进方向
P3-1 无完整键盘导航 添加 role="region" + tabindex
P3-2 AdminTrendCharts 硬编码空数据 从 streams 获取真实数据
P3-3 TeacherTodoCard 排序优化 用优先级映射

中长期计划(不在本次实施范围)

编号 问题 改进方向 阶段
L1 Widget 下钻导航 统计卡片点击跳转详情页 第二阶段
L2 时间范围筛选 admin 仪表盘添加时间选择器 第二阶段
L3 数据对比 添加"本周 vs 上周"对比卡片 第三阶段
L4 自定义仪表盘 用户可选择显示哪些 Widget 第三阶段
L5 通知中心集成 仪表盘集成通知下拉 第二阶段
L6 实时更新 WebSocket 推送待办数 第三阶段
L7 数据导出 支持 PDF/Excel 导出 第三阶段
L8 移动端优化 Widget 移动端优先布局 第二阶段

五、架构图同步说明

需要补充/修改的节点

  1. streams.ts:架构图未记录 getAdminDashboardStreams 函数,需在 004 的 dashboard 模块章节和 005 的 modules.dashboard.exports 中添加
  2. parent dashboard 组件位置:架构图需注明 ParentDashboard 组件实际位于 modules/parent/components/,由 dashboard actions 提供数据
  3. 新增 DashboardService 接口(本次实施后):在 005 的 modules.dashboard 中添加 services 节点
  4. 新增 DashboardWidgetConfig 类型(本次实施后):在 005 的 modules.dashboard.exports.types 中添加
  5. 新增 DashboardAnalytics 接口(本次实施后):在 005 的 modules.dashboard.exports.services 中添加

六、本次实施计划

实施范围

本次完整实施 P0 + P1 + P2 + P3 + 中长期计划 L1-L8用户明确要求"包括中长期计划也要完整实施")。

实施步骤与完成状态

步骤 状态 说明
P0-1修复 filterTodayScheduleas T[] 断言 已完成 移除断言,改为非泛型函数
P1-1抽取 5 个纯函数到 lib/dashboard-utils.ts 已完成 timeToMinutes/getScheduleStatus/getDueUrgency/getActionLabelKey/getActionVariant
P1-2修复 teacherName 硬编码 fallback 已完成 改为空字符串,前端处理
P1-3修复 teacher-schedule.tsx 本地类型重复 已完成 从 types.ts 导入
P1-4迁移 parent dashboard 组件到 dashboard 模块 已完成 迁移至 components/parent-dashboard/,使用 slots 组合
P2-1定义 DashboardService 接口 + Context 注入 已完成 services/dashboard-service.tsx
P2-2定义 DashboardWidgetConfig 配置驱动渲染 已完成 config/widget-configs.ts
P2-3定义 DashboardAnalytics 监控埋点接口 已完成 services/dashboard-service.tsx
P2-4admin 趋势数据占位 TODO 已完成 data-access.ts 添加 TODO 注释
P2-54 个角色 StatCard 使用模式统一 已完成 统一 color + valueClassName="tabular-nums"
P3-1完整键盘导航支持 已完成 DashboardSection 新增 ariaLabel prop + role="region" + tabIndex
P3-2AdminTrendCharts 硬编码空数据 TODO 已完成 添加 TODO 注释
P3-3TeacherTodoCard 排序优化 已完成 VARIANT_PRIORITY 数值映射
L1Widget 下钻导航 已实施 Admin StatCard 添加 hrefContentRow 支持可选 href
L2时间范围筛选 已实施 DashboardTimeRangeFilter 组件 + URL search param 持久化
L3数据对比 已实施 ComparisonBadge 组件 + computeComparison 纯函数
L4自定义仪表盘 已实施 useDashboardPreferences Hook + localStorage 持久化
L5通知中心集成 已实施 DashboardNotificationWidget 组件
L6实时更新 已实施 useDashboardRealtime HookSSE + 指数退避重连)
L7数据导出 已实施 lib/export-utils.tsCSV 导出 + 浏览器下载)
L8移动端优化 已实施 DashboardResponsiveLayout / MobileSwipeContainer / DesktopGrid
同步架构文档 004 和 005 已完成 所有新组件/函数/类型已记录
验证tsc + lint 零错误 已完成 Dashboard 源码零错误(仅预存测试文件 screen 导入错误ESLint 零错误

新增文件清单

文件 类型 职责
services/dashboard-service.tsx Service DashboardService 接口 + DashboardAnalytics 接口 + Context Provider
config/widget-configs.ts Config 4 个角色 Widget 布局配置
hooks/use-dashboard-preferences.ts Hook 自定义仪表盘偏好L4
hooks/use-dashboard-realtime.ts Hook SSE 实时更新L6
lib/export-utils.ts Lib CSV 导出工具L7
components/parent-dashboard/parent-dashboard.tsx Component 家长仪表盘视图P1-4 迁移)
components/dashboard-time-range-filter.tsx Component 时间范围筛选器L2
components/comparison-badge.tsx Component 数据对比徽章L3
components/dashboard-notification-widget.tsx Component 通知中心 WidgetL5
components/dashboard-responsive-layout.tsx Component 移动端响应式布局L8