Files
NextEdu/docs/architecture/audit/archive/diagnostic-audit-report.md

28 KiB
Raw Blame History

学情诊断Diagnostic模块审计报告

审计日期2026-06-22 审计范围:src/modules/diagnostic/**src/app/(dashboard)/teacher/diagnostic/**src/app/(dashboard)/student/diagnostic/**src/app/(dashboard)/parent/diagnostic/** 参照规则:docs/architecture/004_architecture_impact_map.md §2.22、docs/architecture/005_architecture_data.json.trae/rules/project_rules.md 前置文档:grades-diagnostic-audit-report-v4.mdv4 已完成 12 项 P1 数据安全修复)


一、现有实现概要

1.1 文件分布

文件 行数 职责
类型 types.ts 109 DiagnosticReport / Mastery / Summary 类型定义
数据访问 data-access.ts 477 掌握度查询 + 从提交/成绩更新掌握度
数据访问 data-access-reports.ts 256 诊断报告 CRUD + DataScope 过滤
统计服务 stats-service.ts 388 12 个纯统计函数
Server Actions actions.ts 274 5 个 Action生成/发布/删除/导出/按知识点筛选)
校验 schema.ts 31 4 个 Zod schema
导出 export.ts 122 Excel 导出
组件 components/class-diagnostic-view.tsx 448 班级诊断视图(热力图+筛选+排名+关注列表+生成)
组件 components/student-diagnostic-view.tsx 293 学生诊断视图(概览+雷达+强弱项+报告+历史)
组件 components/report-list.tsx 445 报告列表(过滤+表格+发布/删除/导出/分享)
组件 components/mastery-radar-chart.tsx 85 雷达图封装
组件 components/confidence-utils.ts 31 置信度计算
页面 teacher/diagnostic/page.tsx 67 教师报告列表页
页面 teacher/diagnostic/student/[studentId]/page.tsx 86 教师查看学生诊断
页面 teacher/diagnostic/class/[classId]/page.tsx 50 教师班级诊断
页面 student/diagnostic/page.tsx 40 学生自我诊断
页面 parent/diagnostic/page.tsx 128 家长多子女诊断
骨架屏 5 个 loading.tsx 各路由骨架屏
错误边界 5 个 error.tsx 各路由错误边界
i18n diagnostic.json 204 中文翻译

1.2 数据流

page.tsx (RSC)
  ├─ getStudentMasterySummary / getClassMasterySummary / getKnowledgePointStats  (data-access)
  │    └─ db (drizzle) → knowledgePointMastery / knowledgePoints 表
  ├─ getDiagnosticReports (data-access-reports, 含 DataScope 过滤)
  │    └─ db → learningDiagnosticReports 表
  └─ <StudentDiagnosticView> / <ClassDiagnosticView> / <ReportList>  (client)
       └─ generateStudentReportAction / generateClassReportAction / publishReportAction / deleteReportAction / exportDiagnosticReportAction / getClassStudentsByKnowledgePointAction

1.3 架构图记录完整性

经核对 004_architecture_impact_map.md §2.22 与 005_architecture_data.json,架构图对诊断模块的记录基本完整,但存在以下偏差:

  • 行数统计略有滞后:图记 data-access.ts 179 行,实际为 477 行(含 updateMasteryFromHomeworkSubmissionupdateMasteryFromExamScore 两个大函数)。
  • 未记录 export.ts 的存在(架构图文件清单缺少此文件)。
  • 未记录 confidence-utils.ts 组件文件。
  • 未记录跨模块 UI 依赖:teacher/diagnostic/student/[studentId]/page.tsxteacher/diagnostic/class/[classId]/page.tsx 直接 import @/modules/grades/components/widget-boundary,架构图未标注此跨模块 UI 依赖。

二、现存问题与原因分析

2.1 架构解耦

问题 2.1.1 跨模块直接 import UI 组件 WidgetBoundaryP0

  • 位置
  • 现象import { WidgetBoundary } from "@/modules/grades/components/widget-boundary"
  • 违反规则:项目规则"模块间只能通过对方 data-access 通信,禁止跨模块直接查询数据库表"的精神延伸——UI 组件跨模块直接 import 同样破坏模块独立性。WidgetBoundary 是通用错误边界组件,不应属于 grades 业务模块。
  • 原因WidgetBoundary 最初为 grades 模块创建diagnostic 模块复用时直接 import 了 grades 模块的实现,而非将其提升到 shared 层。
  • 后果grades 模块对 WidgetBoundary 的任何变更重命名、删除、props 修改)都会破坏 diagnostic 模块编译diagnostic 模块无法独立测试、独立部署。

问题 2.1.2 组件直接 import actions无服务接口抽象P1

  • 位置
  • 现象:客户端组件直接 import 并调用 Server Actions未通过接口抽象或依赖注入。
  • 违反规则:项目规则"通过定义 TypeScript 接口抽象数据依赖,使用 React Context 注入数据服务,模块内部组件绝不直接 import 其他业务模块的 actions 或 data-access"。
  • 原因:模块未采用依赖注入模式,组件与 actions 紧耦合。
  • 后果:组件无法独立测试(测试时必须 mock 整个 actions 模块);无法在不修改组件代码的情况下替换 actions 实现。

2.2 国际化

问题 2.2.1 教师页面标题硬编码英文P0

  • 位置
  • 违反规则:项目规则"所有用户可见文本必须适配 i18n使用 next-intl提取翻译键"。
  • 原因:教师端 3 个页面未使用 getTranslations 获取翻译,直接硬编码英文文案。学生端和家端已正确使用 i18n。
  • 后果:中文环境下教师看到英文标题,与系统其他页面风格不一致。

问题 2.2.2 teacher/diagnostic/error.tsx 硬编码中文P0

  • 位置teacher/diagnostic/error.tsx#L17-22
  • 现象title="学情诊断页面加载失败" description="抱歉,页面加载时发生了意外错误。请稍后重试。" label="重试" 全部硬编码。
  • 违反规则:项目规则"所有用户可见文本必须适配 i18n"。
  • 原因error.tsx 是客户端组件但未使用 useTranslations
  • 后果:英文环境下错误页显示中文,国际化不一致。对比 student/diagnostic/error.tsx 已正确使用 i18n。

问题 2.2.3 parent/diagnostic/page.tsx 错误卡片中英文混用P1

  • 位置parent/diagnostic/page.tsx#L116-119
  • 现象{t("error.loadFailed")} for {item.studentName}.Please refresh the page or contact the school administrator if the problem persists. 混用 i18n key 和硬编码英文。
  • 违反规则:项目规则"所有用户可见文本必须适配 i18n"。
  • 后果:中文环境下显示"加载失败 for 张三.",中英文混杂,用户体验差。

问题 2.2.4 报告内容硬编码中文P1

  • 位置stats-service.ts#L280-353
  • 现象buildStudentReportContentbuildClassReportContent 生成中文报告内容,如 "建议复习「${m.knowledgePointName}」知识点""学生 ${summary.studentName} 在 ${period} 期间整体掌握度" 等。
  • 违反规则:项目规则"所有用户可见文本必须适配 i18n"。
  • 原因:纯函数层生成报告内容时直接硬编码中文,未通过 i18n。
  • 后果:英文环境下生成的诊断报告内容为中文,无法国际化。报告内容存储在数据库中,已生成的历史报告无法回溯翻译。

问题 2.2.5 Excel 导出表头硬编码中文P1

  • 位置export.ts#L40-99
  • 现象Excel 表头如 "学生姓名""报告周期""综合得分""知识点掌握度" 等硬编码中文;文件名 诊断报告_${safePeriod}_${formatDateForFile()}.xlsx 也硬编码。
  • 违反规则:项目规则"所有用户可见文本必须适配 i18n"。
  • 后果:英文环境下导出的 Excel 文件表头和文件名为中文。

2.3 类型安全

问题 2.3.1 as 类型断言P1

  • 位置teacher/diagnostic/page.tsx#L24, L28
  • 现象(v as DiagnosticReportType)(v as DiagnosticReportStatus) 使用 as 断言。
  • 违反规则:项目规则"禁止 as 断言(除非从 unknown 转换或测试中,需注释原因)"。
  • 原因:虽有类型守卫 VALID_REPORT_TYPES.has(v) 校验,但转换时使用了 as 而非类型守卫函数返回值收窄。
  • 后果:绕过 TypeScript 严格类型检查,潜在类型不安全。

2.4 错误处理与边界

问题 2.4.1 分享链接指向不存在的路由P1

  • 位置components/report-list.tsx#L157, L208
  • 现象const url = \${window.location.origin}/teacher/diagnostic/reports/${shareId}`指向/teacher/diagnostic/reports/[id]` 路由,但该路由在项目中不存在(无对应 page.tsx
  • 原因:分享功能开发时未创建对应路由页面。
  • 后果:用户点击分享链接后得到 404 页面,功能不可用。

问题 2.4.2 班级报告导出缺少明细P2

  • 位置export.ts#L85-113
  • 现象:班级报告仅导出概览 Sheet缺少知识点统计和需关注学生明细。代码注释明确说明"班级报告的 studentId 为 null需要从 period 反查 classId 不现实"。
  • 原因v4-P1 已为 learningDiagnosticReports 表新增 classId 字段,但 export.ts 未同步更新使用该字段查询班级明细。
  • 后果:教师导出班级报告时只能看到概览,无法获取知识点统计和需关注学生列表,导出功能不完整。

2.5 可复用性与配置驱动

问题 2.5.1 角色差异通过 props 硬编码而非配置驱动P2

  • 位置components/student-diagnostic-view.tsx#L32
  • 现象practiceHrefBase prop 区分角色(学生默认 /student/learning/assignments,教师传 /teacher/questions,家长传 null)。
  • 违反规则:项目规则"采用配置驱动设计,例如通过角色配置决定该模块渲染哪些 Widget/子模块"。
  • 原因:角色差异通过 props 传递,而非通过角色配置对象统一管理。
  • 后果:新增角色需修改组件 props 传递逻辑,而非仅修改配置。

问题 2.5.2 无年级诊断报告生成入口P2

  • 位置types.ts#L3
  • 现象DiagnosticReportType 定义了 "grade" 类型,但 actions.tsgenerateGradeReportActiondata-access-reports.tsgenerateGradeDiagnosticReport 函数。
  • 原因:年级报告功能定义了类型但未实现。
  • 后果管理员无法生成年级级别的诊断报告功能不完整。report-list 过滤器中可选"年级"类型但永远无数据。

2.6 可访问性

问题 2.6.1 热力图色块缺少键盘导航P2

  • 位置components/class-diagnostic-view.tsx#L190-201
  • 现象:热力图色块为 <div> 且仅有 role="img",无 tabIndex 和键盘焦点样式,键盘用户无法逐个聚焦查看详情。
  • 违反规则:项目规则"可访问性a11y语义化标签、ARIA 属性、键盘导航"。
  • 后果:键盘用户无法通过 Tab 遍历热力图色块查看 tooltip/title 详情。

三、行业差距对比

对标 PowerSchool、Infinite Campus、Skyward、Alma、智学网、班级小管家等 K12 系统,本模块在以下方面存在差距:

维度 优秀实践 本模块现状 影响
诊断趋势分析 PowerSchool/智学网支持掌握度时间线,展示知识点掌握度随时间的变化趋势 仅展示当前快照,无历史趋势对比 教师无法判断学生是否在进步或退步
年级诊断报告 Infinite Campus 支持年级级别诊断,对比班级间差异 类型已定义但无实现入口 管理员无法做年级层面决策
报告详情页 所有同类系统都有独立的报告详情页,支持分享链接 分享链接指向不存在的路由 分享功能不可用
班级报告导出明细 PowerSchool/Infinite Campus 导出含知识点统计+学生列表 班级报告仅导出概览 教师无法离线分析
掌握度时间线 智学网展示每个知识点的掌握度变化曲线 无时间维度数据 无法评估教学干预效果
个性化学习路径 Alma/智学网基于弱项推荐学习路径和资源 仅提供练习按钮跳转题目库 推荐不够精准
诊断报告模板 PowerSchool 支持自定义报告模板 报告内容固定硬编码 无法按学校需求定制
多维度诊断 Infinite Campus 结合成绩+出勤+行为做多维诊断 仅基于知识点掌握度 诊断维度单一

四、改进优先级建议

P0紧急影响功能正确性或核心规范

编号 问题 改进方向
P0-1 跨模块 import WidgetBoundary 将 WidgetBoundary 提升到 shared/components/diagnostic 和 grades 模块统一从 shared 引用
P0-2 教师页面标题硬编码英文 3 个教师页面使用 getTranslations("diagnostic") 获取标题和描述
P0-3 teacher/diagnostic/error.tsx 硬编码中文 接入 useTranslations("diagnostic"),与其他 error.tsx 一致

P1重要影响用户体验或类型安全

编号 问题 改进方向
P1-1 parent 错误卡片中英文混用 提取完整 i18n 键,消除硬编码英文
P1-2 as 类型断言 改用类型守卫函数返回值收窄,消除 as
P1-3 分享链接指向不存在路由 移除分享功能或创建对应路由页面。鉴于当前无报告详情页需求,移除分享按钮避免 404
P1-4 报告内容硬编码中文 stats-service 的报告内容生成改为接收 i18n 翻译函数参数,或在 actions 层调用时注入翻译后的模板
P1-5 Excel 导出表头硬编码 export.ts 接收 i18n 翻译参数,表头和文件名使用翻译键

P2增强提升完整性

编号 问题 改进方向
P2-1 班级报告导出缺少明细 利用 v4-P1 新增的 classId 字段查询班级掌握度,导出知识点统计+需关注学生 Sheet
P2-2 角色差异通过 props 硬编码 定义角色配置对象,通过配置驱动 practiceHrefBase 等角色差异
P2-3 无年级诊断报告入口 实现 generateGradeDiagnosticReport + 对应 Action中长期
P2-4 热力图色块缺少键盘导航 添加 tabIndex={0} 和 focus-visible 样式
P2-5 架构图行数统计滞后 同步 data-access.ts 实际行数,补充 export.ts 和 confidence-utils.ts 记录

五、架构图同步说明

本次审计发现架构图需同步以下内容:

004_architecture_impact_map.md §2.22

  1. 文件清单更新
    • data-access.ts 行数从 179 更新为 477updateMasteryFromHomeworkSubmissionupdateMasteryFromExamScore
    • 补充 export.ts122 行Excel 导出)
    • 补充 components/confidence-utils.ts31 行,置信度计算)
  2. 已知问题新增
    • 记录 P0-1 跨模块 import WidgetBoundary 问题及修复
    • 记录 P0-2/P0-3 i18n 遗漏问题及修复
    • 记录 P1-3 分享链接 404 问题及修复
  3. 依赖关系更新:标注 WidgetBoundary 已从 grades 模块提升到 shared 层

005_architecture_data.json

  1. modules.diagnostic.exports 补充 export.tsconfidence-utils.ts 文件记录
  2. modules.diagnostic.dependencies 更新:移除对 grades/components/widget-boundary 的 UI 依赖,改为 shared/components/widget-boundary
  3. modules.diagnostic.fileList 行数同步更新

六、实施状态2026-06-24 全部完成)

本章节记录审计报告中所有 P0/P1/P2 项的实施完成情况。所有项均已通过 npx tsc --noEmitnpm run lint 校验(诊断模块零错误)。

6.1 P0 项实施状态

编号 状态 实施内容 涉及文件
P0-1 已完成 WidgetBoundary 已从 modules/grades/components/widget-boundary.tsx 提升到 shared/components/widget-boundary.tsxdiagnostic 与 grades 模块统一从 @/shared/components/widget-boundary 引用grades 模块原文件已删除 src/shared/components/widget-boundary.tsx(新建)、src/modules/grades/components/widget-boundary.tsx(删除)、src/app/(dashboard)/teacher/diagnostic/student/[studentId]/page.tsxsrc/app/(dashboard)/teacher/diagnostic/class/[classId]/page.tsx
P0-2 已完成 3 个教师页面(teacher/diagnostic/page.tsxteacher/diagnostic/student/[studentId]/page.tsxteacher/diagnostic/class/[classId]/page.tsx)均使用 getTranslations("diagnostic") 获取标题与描述,新增对应 i18n 键 teacherTitleteacherDescriptionstudentTitlestudentDescriptionclassTitleclassDescription 上述 3 个页面 + src/shared/i18n/messages/zh-CN/diagnostic.json + src/shared/i18n/messages/en/diagnostic.json
P0-3 已完成 teacher/diagnostic/error.tsx 接入 useTranslations("diagnostic"),与 student/diagnostic/error.tsx 风格一致;新增 i18n 键 errorTitleerrorDescriptionerrorRetry src/app/(dashboard)/teacher/diagnostic/error.tsx + 两个 i18n 文件

6.2 P1 项实施状态

编号 状态 实施内容 涉及文件
P1-1 已完成 parent/diagnostic/page.tsx 错误卡片中英文混用已消除;新增 i18n 键 errorForStudenterrorContactAdmin,使用 t("errorForStudent", { name: item.studentName }) 替代硬编码 src/app/(dashboard)/parent/diagnostic/page.tsx + 两个 i18n 文件
P1-2 已完成 teacher/diagnostic/page.tsx(v as DiagnosticReportType)(v as DiagnosticReportStatus) 已替换为类型守卫函数返回值收窄:VALID_REPORT_TYPES.has(v) ? v : DEFAULT_REPORT_TYPE 模式,消除 as 断言 src/app/(dashboard)/teacher/diagnostic/page.tsx
P1-3 已完成 components/report-list.tsx 中分享按钮与相关逻辑已移除(包括 shareReportAction 调用、window.location.origin URL 构造、分享对话框),避免 404保留发布/删除/导出三个核心操作 src/modules/diagnostic/components/report-list.tsx
P1-4 已完成 stats-service.tsbuildStudentReportContentbuildClassReportContent 已重构为接收 ReportContentTranslations 接口参数;新增 getReportContentTranslations() 在 data-access-reports 层调用 getTranslations 注入翻译;报告内容生成改为 i18n 驱动 src/modules/diagnostic/stats-service.tssrc/modules/diagnostic/data-access-reports.ts、两个 i18n 文件
P1-5 已完成 export.ts 中 Excel 表头和文件名已改为接收 i18n 翻译参数;exportDiagnosticReportAction 在调用 exportDiagnosticReportToExcel 前通过 getTranslations("diagnostic") 注入翻译;新增 i18n 键 sheetOverviewsheetClassStatssheetAttentionStudentscolStudentNamecolPeriodcolReportTypecolStatuscolScorecolGeneratedAtcolSummarycolStrengthscolWeaknessescolRecommendationsfilenameDiagnosticReport src/modules/diagnostic/export.tssrc/modules/diagnostic/actions.ts、两个 i18n 文件

6.3 P2 项实施状态

编号 状态 实施内容 涉及文件
P2-1 已完成 班级报告导出已利用 v4-P1 新增的 classId 字段调用 getClassMasterySummary,导出包含三个 Sheet概览、知识点统计、需关注学生明细新增 i18n 键 sheetClassStatssheetAttentionStudentsmetricClassmetricStudentCountmetricAttentionCountcolMasteredCountcolNotMasteredCountcolTotalStudentscolAverageMasterycolWeakCountnoAttentionStudents src/modules/diagnostic/export.ts + 两个 i18n 文件
P2-2 已完成 新建 src/modules/diagnostic/role-config.ts,定义 DiagnosticRole 类型、DiagnosticRoleConfig 接口、DIAGNOSTIC_ROLE_CONFIG 记录student/teacher/parent 三角色配置)和 getDiagnosticRoleConfig 辅助函数;StudentDiagnosticView 组件新增 role prop内部通过 getDiagnosticRoleConfig(role).practiceHrefBase 解析配置;原 practiceHrefBase prop 标记 @deprecated 保留向后兼容(同时传入时 role 优先3 个调用点已迁移为 role="student" / role="teacher" / role="parent" src/modules/diagnostic/role-config.ts(新建)、src/modules/diagnostic/components/student-diagnostic-view.tsxsrc/app/(dashboard)/student/diagnostic/page.tsxsrc/app/(dashboard)/teacher/diagnostic/student/[studentId]/page.tsxsrc/app/(dashboard)/parent/diagnostic/page.tsx
P2-3 已完成 完整实现年级诊断报告纵向切片:① DB schema 新增 gradeId 字段 + gradeIdx 索引 + 迁移 SQL 0012_diagnostic_grade_id.sql;② 类型新增 GradeMasterySummary 接口,DiagnosticReport 接口新增 gradeId: string | null;③ data-access 新增 getGradeMasterySummary(缓存,并行查询年级名+学生 ID+掌握度行);④ stats-service 新增 buildGradeMasterySummarybuildGradeReportContent 纯函数;⑤ data-access-reports 新增 generateGradeDiagnosticReport,含 GRADE_NOT_FOUND / GRADE_NO_MASTERY_DATA 错误码;⑥ schema 新增 GenerateGradeReportSchema;⑦ actions 新增 generateGradeReportAction Server ActionrequirePermission + revalidatePath);⑧ i18n 新增 gradeSummarygradeRecommendationgradeNoWeakness src/shared/db/schema.tsdrizzle/0012_diagnostic_grade_id.sql(新建)、src/modules/diagnostic/types.tssrc/modules/diagnostic/data-access.tssrc/modules/diagnostic/stats-service.tssrc/modules/diagnostic/data-access-reports.tssrc/modules/diagnostic/schema.tssrc/modules/diagnostic/actions.ts、两个 i18n 文件
P2-4 已完成 班级诊断视图热力图色块新增 tabIndex={0}focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 样式;外层容器 role"img" 改为 "group"(因容器内现含可聚焦元素);保留每个色块的 aria-label 提供完整描述 src/modules/diagnostic/components/class-diagnostic-view.tsx
P2-5 已完成 架构图 004 和 005 已同步:① data-access.ts 行数更新为实际值;② 补充 export.tsrole-config.tsconfidence-utils.ts 文件记录;③ 已知问题章节新增 11 条 P0-1 至 P2-4 修复记录;④ 依赖矩阵新增 school 模块依赖(getGradeNameByIdgetUserIdsByGradeId)和 shared/components/widget-boundary;⑤ learningDiagnosticReports 表描述补充 gradeId 字段;⑥ modules.diagnostic.exports 新增 getGradeMasterySummarygenerateGradeDiagnosticReportbuildGradeMasterySummarybuildGradeReportContentgenerateGradeReportActionGenerateGradeReportSchema docs/architecture/004_architecture_impact_map.mddocs/architecture/005_architecture_data.json

6.4 验证结果

  • TypeScriptnpx tsc --noEmit 通过,诊断模块零错误(仅 dashboard/services/dashboard-service.ts 存在与本模块无关的预存语法错误)。
  • ESLintnpm run lint 通过,诊断模块零警告。
  • 架构图一致性004 与 005 两份架构文档已与源码同步,所有新增/修改的导出函数、类型、依赖关系、DB 表字段均已记录。

6.5 后续建议(未列入本次实施范围)

以下为审计过程中识别但未列入本次实施的长期增强项,建议后续按需推进:

  1. 掌握度时间线:新增 knowledgePointMasteryHistory 表记录每次掌握度变化,前端展示时间线图表。
  2. 个性化学习路径推荐:基于弱项知识点推荐具体学习资源(题目、视频、文档),而非仅跳转题目库。
  3. 多维度诊断:结合成绩、出勤、行为数据做多维综合诊断。
  4. 报告模板自定义:允许学校配置报告内容模板(如自定义推荐话术、评分区间)。
  5. 报告详情页:若未来需要分享功能,创建 /teacher/diagnostic/reports/[id] 路由页面。
  6. 可测试性增强:为 stats-service.ts 中 12 个纯函数补充单元测试,导出 ReportContentTranslations 接口便于 mock。
  7. 依赖注入抽象:将 report-list.tsxclass-diagnostic-view.tsx 中直接 import actions 的模式重构为通过 React Context 注入数据服务接口,提升可测试性。