Files
NextEdu/docs/architecture/audit/grades-diagnostic-audit-report.md
SpecialX 45ee1ae43c refactor(grades,diagnostic): 成绩和学情诊断模块审计修复
P0-1: 10 个页面补充 requirePermission 权限校验
P0-2: diagnostic/data-access-reports.ts 移除直查 users 表,改用 getUserNamesByIds
P0-3: 新增 grade/grades/diagnostic 三组 i18n 翻译文件(zh-CN/en)
P0-4: 新增 /management/grade 重定向页面

P1-2: 抽取 toNumber/normalize/buildScopeClassFilter 到 lib/grade-utils.ts
P1-3: 为 12 个 Action 新增 Zod safeParse 校验(schema.ts +12 查询 schema)
P1-4: 修复 as 断言违规,改用类型守卫函数

P2-2: 移除 diagnostic 组件中 Tailwind 任意值

同步更新架构图文档 004 和 005
2026-06-22 16:23:34 +08:00

42 KiB
Raw Blame History

成绩和学情诊断模块审计报告

审查日期2026-06-22 审查范围:src/modules/grades/**(成绩模块)、src/modules/diagnostic/**(学情诊断模块)、src/app/(dashboard)/{admin,teacher,student,parent}/grades/**src/app/(dashboard)/{teacher,student}/diagnostic/**src/app/(dashboard)/management/grade/**、相关 i18n 翻译文件 架构图参考:docs/architecture/004_architecture_impact_map.md §2.6grades、§2.22diagnosticdocs/architecture/005_architecture_data.json L7362grades、L10927diagnostic


一、现有实现概要

1.1 文件分布

grades 模块(成绩分析)

路径 文件数 行数 说明
Actions src/modules/grades/actions.ts 1 312 10 个 Server ActionCRUD + 查询 + 导出)
Actions src/modules/grades/actions-analytics.ts 1 133 5 个分析 Server Action趋势/对比/分布/排名)
Data-access src/modules/grades/data-access.ts 1 433 成绩 CRUD + 统计(含统计业务逻辑)
Data-access src/modules/grades/data-access-analytics.ts 1 337 趋势/对比/分布分析(含统计业务逻辑)
Data-access src/modules/grades/data-access-ranking.ts 1 119 排名查询(含 normalize 逻辑)
Export src/modules/grades/export.ts 1 200 Excel 导出(明细 + 班级汇总)
Schema src/modules/grades/schema.ts 1 52 4 个 Zod schema
Types src/modules/grades/types.ts 1 186 14 个类型定义
Components src/modules/grades/components/* 16 41~442 16 个组件(含 batch-grade-entry 442 行)

diagnostic 模块(学情诊断)

路径 文件数 行数 说明
Actions src/modules/diagnostic/actions.ts 1 172 6 个 Server Action生成/发布/删除/查询)
Data-access src/modules/diagnostic/data-access.ts 1 257 知识点掌握度查询 + 更新
Data-access src/modules/diagnostic/data-access-reports.ts 1 203 诊断报告 CRUD直查 users 表
Schema src/modules/diagnostic/schema.ts 1 48 6 个 Zod schema
Types src/modules/diagnostic/types.ts 1 97 11 个类型定义
Components src/modules/diagnostic/components/* 4 69~267 4 个组件(含 class-diagnostic-view 267 行)

路由层

角色 路由 文件数 说明
admin /admin/school/grades//admin/school/grades/insights/ 4 含 loading.tsx + error.tsx
teacher /teacher/grades//teacher/grades/analytics//teacher/grades/entry//teacher/grades/stats/ 4 无 loading.tsx / error.tsx
teacher /teacher/diagnostic//teacher/diagnostic/class/[classId]//teacher/diagnostic/student/[studentId]/ 3 无 loading.tsx / error.tsx
student /student/grades//student/diagnostic/ 4 含 loading.tsx无 error.tsx
parent /parent/grades/ 2 含 loading.tsx无 error.tsx
management /management/grade//management/grade/classes//management/grade/insights/ 5 /management/grade/page.tsx 缺失(孤儿 loading/error

1.2 主要数据流

[成绩录入] teacher/grades/entry
   └─▶ grades/actions.batchCreateGradeRecordsAction
         ├─▶ requirePermission(GRADE_RECORD_MANAGE)
         └─▶ data-access.batchCreateGradeRecords → db.insert(gradeRecords)

[成绩查询] teacher/grades / student/grades / parent/grades
   └─▶ grades/actions.getGradeRecordsAction
         ├─▶ requirePermission(GRADE_RECORD_READ)
         ├─▶ data-access.getGradeRecords含 scope 行级过滤)
         └─▶ 跨模块classes/school/users data-access

[成绩分析] teacher/grades/analytics
   └─▶ grades/actions-analytics.getGradeTrendAction / getClassComparisonAction / ...
         ├─▶ requirePermission(GRADE_RECORD_READ)
         └─▶ data-access-analytics含统计计算逻辑

[学情诊断-学生] teacher/diagnostic/student/[id] / student/diagnostic
   └─▶ diagnostic/data-access.getStudentMasterySummary
         └─▶ 跨模块users data-accessgetUserNamesByIds

[学情诊断-班级] teacher/diagnostic/class/[id]
   └─▶ diagnostic/data-access.getClassMasterySummary
         └─▶ 跨模块classes/exams/questions/users data-access

[诊断报告生成] teacher/diagnostic
   └─▶ diagnostic/actions.generateStudentReportAction / generateClassReportAction
         ├─▶ requirePermission(DIAGNOSTIC_MANAGE)
         └─▶ data-access-reports.createDiagnosticReport
               └─▶ ⚠️ 直查 users 表(违反三层架构)

1.3 架构图记录情况

004_architecture_impact_map.md §2.6grades和 §2.22diagnostic已记录两个模块的导出函数、依赖关系、已知问题和文件清单。架构图信息基本完整但存在以下遗漏

  • grades 模块行数过时:架构图 L681 标注 data-access.ts 419 行(实际 433 行、L682 data-access-analytics.ts 293 行(实际 337 行)
  • diagnostic 模块 deps 过时005_architecture_data.json L10922/L10937-10941/L10954-10958/L10972-10975 仍记录 diagnostic 直查对方表,实际代码已通过 data-access 接口访问P1-1 已修复但文档未同步)
  • diagnostic data-access-reports.ts 直查 users 表未记录:架构图未标注此违规
  • grades 模块 actions-analytics.ts 的 5 个 Action 未完整列入 exports 清单
  • /management/grade/page.tsx 缺失未在路由清单中标注
  • teacher 端 grades/diagnostic 路由普遍缺少 loading.tsx/error.tsx 未标注

二、现存问题与原因分析

2.1 安全性权限校验缺失或不一致P0

位置 问题 违反规则
teacher/grades/entry/page.tsx 无任何权限校验(既无 requirePermission 也无 getAuthContext "所有 Server Action 必须调用 requirePermission() 进行权限校验"
teacher/grades/stats/page.tsx 无任何权限校验 同上
teacher/grades/page.tsx getAuthContext(),无 requirePermission(GRADE_RECORD_READ) 同上
teacher/grades/analytics/page.tsx getAuthContext(),无 requirePermission(GRADE_RECORD_READ) 同上
teacher/diagnostic/page.tsx getAuthContext(),无 requirePermission(DIAGNOSTIC_READ) 同上
teacher/diagnostic/class/[classId]/page.tsx getAuthContext()(有 dataScope 校验),无 requirePermission(DIAGNOSTIC_READ) 同上
teacher/diagnostic/student/[studentId]/page.tsx getAuthContext()(有 dataScope 校验),无 requirePermission(DIAGNOSTIC_READ) 同上
student/grades/page.tsx getAuthContext(),无 requirePermission(GRADE_RECORD_READ) 同上
student/diagnostic/page.tsx getAuthContext(),无 requirePermission(DIAGNOSTIC_READ) 同上
parent/grades/page.tsx getAuthContext()(有 dataScope 校验),无 requirePermission(GRADE_RECORD_READ) 同上

后果:成绩录入页面(/teacher/grades/entry)和成绩统计页面(/teacher/grades/stats)完全无权限校验,依赖路由中间件做粗粒度角色路由。若中间件配置错误或绕过,任意已登录用户可访问成绩录入页面并调用 batchCreateGradeRecordsAction(虽然 Action 层有 requirePermission,但页面层缺少二次校验不符合"Server Action 二次校验"要求)。

2.2 架构分层:跨模块直接查询 users 表P0

位置 问题 违反规则
diagnostic/data-access-reports.ts L8 import { learningDiagnosticReports, users } from "@/shared/db/schema" "modules/ 之间通过对方 data-access 通信,不直接查询对方 DB 表"
diagnostic/data-access-reports.ts L137-140 getDiagnosticReports 直接 leftJoin(users, ...) 查询学生姓名 同上
diagnostic/data-access-reports.ts L149-153 直接 db.select({ id: users.id, name: users.name }).from(users) 查询生成者姓名 同上
diagnostic/data-access-reports.ts L168-170 getDiagnosticReportById 直接 leftJoin(users, ...) 同上
diagnostic/data-access-reports.ts L177-182 直接 db.select({ name: users.name }).from(users) 同上

后果diagnostic 模块绕过 users 模块的 data-access 层直接查询 users 表,破坏模块封装性。users 表 schema 变更将直接影响 diagnostic 模块。同模块的 data-access.ts 已正确通过 getUserNamesByIds 访问,但 data-access-reports.ts 却绕过,存在不一致。

2.3 架构分层:统计业务逻辑混入 data-accessP1

位置 问题 违反规则
grades/data-access.ts L217-270 getClassGradeStats 包含 average/median/max/min/variance/stdDev/passRate/excellentRate 计算53 行统计逻辑) "严格三层架构,依赖方向单向" — 统计计算属业务逻辑层
grades/data-access.ts L272-337 getStudentGradeSummary 包含 averageScore 计算 同上
grades/data-access.ts L339-373 getClassRanking 包含 rank 计算 同上
grades/data-access-analytics.ts L59-119 getGradeTrend 包含 normalized/avg 计算 同上
grades/data-access-analytics.ts L128-218 getClassComparison 包含 normalized/median/avg/passCount/excellentCount 计算90 行) 同上
grades/data-access-analytics.ts L226-289 getSubjectComparison 包含 median/avg/passRate/excellentRate 计算 同上
grades/data-access-analytics.ts L299-336 getGradeDistribution 包含 bucket 分类逻辑 同上
grades/data-access-ranking.ts L31-118 getRankingTrend 包含 normalize/rank 计算逻辑 同上

后果data-access 层职责混乱,既负责数据读取又负责业务计算,难以单独测试统计逻辑。架构图 L671 已标记此 P2 问题。应抽取到独立的 stats-service.ts(参考 homework 模块的 stats-service.ts 范例)。

2.4 重复代码工具函数多处重复P1

重复函数 出现位置 违反规则
buildScopeClassFilter grades/data-access.ts L57-75、grades/data-access-analytics.ts L34-48 "工具函数:建议 ≤ 40 行" + DRY 原则
toNumber grades/data-access.ts L34-37、grades/data-access-analytics.ts L24-27、grades/data-access-ranking.ts L16-19 同上
normalize grades/data-access.tsgrades/data-access-analytics.ts L29-32、grades/data-access-ranking.ts L21-24 同上

后果3 个文件重复实现相同工具函数,修改时需同步多处,易遗漏导致行为不一致。应抽取到 grades/lib/stats-utils.tsshared/lib/grade-utils.ts

2.5 国际化完全缺失P0

位置 问题 违反规则
src/modules/grades/components/*16 个文件) 全部使用硬编码英文字符串0 处 useTranslations 调用 "所有用户可见文本必须适配 i18n使用 next-intl提取翻译键"
src/modules/diagnostic/components/*4 个文件) 全部使用硬编码英文字符串0 处 useTranslations 调用 同上
grades/export.ts L12-17, L54-61, L68-80, L287, L295 Excel 导出表头、指标名、文件名硬编码中文 同上
src/shared/i18n/messages/{zh-CN,en}/ 不存在 grades.jsondiagnostic.json 翻译文件 同上
i18n/request.ts L22-28 仅加载 5 个命名空间common/auth/onboarding/classes/errors未加载 grades/diagnostic 同上
src/modules/grade-management/components/*7 个文件12 处) 调用 useTranslations("grade")grade.json 翻译文件不存在,运行时会报 MISSING_MESSAGE 错误 同上

后果

  1. grades 和 diagnostic 模块完全无法国际化,所有用户可见文本固定为英文(部分中文混合),无法支持多语言。
  2. grade-management 模块(年级管理,与成绩模块不同)调用未加载的 grade 命名空间,访问 /management/grade//admin/school/grades/insights 等页面会因找不到翻译键而运行时报错

2.6 前端规范Error Boundary 和 Suspense 缺失P1

位置 问题 违反规则
src/modules/grades/components/*16 个文件) 全部无 Error Boundary "每个独立的数据区块必须用 React Error Boundary 包裹"
src/modules/diagnostic/components/*4 个文件) 全部无 Error Boundary 同上
src/modules/grades/components/*16 个文件) 全部无 Suspense + 骨架屏 "异步数据使用 React Suspense + 骨架屏"
src/modules/diagnostic/components/*4 个文件) 全部无 Suspense + 骨架屏 同上
src/app/(dashboard)/teacher/grades/ 无 loading.tsx / error.tsx 路由级错误边界和加载态缺失
src/app/(dashboard)/teacher/diagnostic/ 无 loading.tsx / error.tsx 同上

后果:单个组件抛错会导致整个页面崩溃;异步加载无骨架屏过渡,用户体验差(白屏等待)。

2.7 前端规范a11y 无障碍缺失P2

位置 问题 违反规则
src/modules/grades/components/*15/16 个文件) 无 ARIA 属性(仅 batch-grade-entry.tsx 有 aria-hiddenaria-invalid "可访问性a11y语义化标签、ARIA 属性、键盘导航"
src/modules/diagnostic/components/*4 个文件) 无 ARIA 属性 同上
grades/components/grade-record-list.tsx L93-100 删除按钮无 aria-label 同上
diagnostic/components/class-diagnostic-view.tsx L128-139 热力图色块仅靠 title 属性,无 role="img"aria-label 同上
diagnostic/components/mastery-radar-chart.tsx L38-66 雷达图无 aria-label / role="img" 描述 同上
diagnostic/components/report-list.tsx L192-200, L202-210 发布/删除按钮仅 title,无 aria-label 同上

后果:屏幕阅读器用户无法识别图表内容、按钮用途,不符合 WCAG 2.1 AA 标准。

2.8 TypeScript 规范:as 断言违规P1

位置 问题 违反规则
grades/components/batch-grade-entry.tsx L221 remark: undefined as string | undefined "禁止 as 断言(除非从 unknown 转换或测试中,需注释原因)"
grades/components/batch-grade-entry.tsx L312 setType(v as typeof type) 同上
grades/components/grade-record-form.tsx L142 setType(v as typeof type) 同上
grades/components/grade-distribution-chart.tsx L66-67 payload as { payload?: {...} }(从 unknown 转换但未使用类型守卫) 同上

后果as 断言绕过 TypeScript 类型检查,可能隐藏运行时类型错误。应使用类型守卫或 Zod 运行时校验。

2.9 Tailwind 规范任意值违规P2

位置 问题 违反规则
diagnostic/components/class-diagnostic-view.tsx L255 className="w-[180px]" "禁止使用任意值(w-[137px]),除非有充分理由并注释"
diagnostic/components/mastery-radar-chart.tsx L45 className="mx-auto h-[360px] w-full max-w-[520px]" 同上

后果:绕过设计令牌系统,无法统一调整尺寸主题。

2.10 数据模型缺陷:班级报告 studentId 字段语义错误P2

位置 问题 违反规则
diagnostic/data-access.ts L111-114 班级报告 studentId: generatedBy 将生成者 ID 写入 studentId 字段 "数据模型设计应语义清晰"
diagnostic/data-access-reports.ts L111 同上 同上

后果report-list.tsx L178 显示 r.studentName 时,班级报告会显示生成者(教师)姓名而非学生姓名,存在数据语义错误。架构图 L1245 已标记此 P2 问题。

2.11 Server Action 规范Zod 校验缺失P1

位置 问题 违反规则
grades/actions.ts L154-170 deleteGradeRecordAction 无 Zod 校验(仅 id 字符串) "输入使用 Zod 验证,验证失败返回结构化错误"
grades/actions.ts L171-190 getGradeRecordsAction 无 Zod 校验(使用 GradeQueryParams 类型) 同上
grades/actions.ts L191-208 getClassGradeStatsAction 无 Zod 校验 同上
grades/actions.ts L209-232 getStudentGradeSummaryAction 无 Zod 校验 同上
grades/actions.ts L233-250 getClassRankingAction 无 Zod 校验 同上
grades/actions.ts L251-269 getGradeRecordByIdAction 无 Zod 校验 同上
grades/actions.ts L270-312 exportGradesAction 无 Zod 校验params 为内联对象类型) 同上
grades/actions-analytics.ts L26-45 getGradeTrendAction 无 Zod 校验 同上
grades/actions-analytics.ts L46-64 getClassComparisonAction 无 Zod 校验 同上
grades/actions-analytics.ts L65-83 getSubjectComparisonAction 无 Zod 校验 同上
grades/actions-analytics.ts L84-103 getGradeDistributionAction 无 Zod 校验 同上
grades/actions-analytics.ts L104-133 getRankingTrendAction 无 Zod 校验 同上

后果12 个 Action 缺失 Zod 校验,客户端可传入任意类型参数,可能导致运行时错误或 SQL 注入风险。diagnostic 模块的 6 个 Action 全部使用 Zod 校验,是标杆范例。

2.12 业务逻辑漏洞grade_managed scope 返回空数据P2

位置 问题 违反规则
grades/data-access.ts L62-64 grade_managed scope 返回 sql\1=0``(始终无数据) "权限过滤应正确反映角色数据范围"
grades/data-access-analytics.ts L39 同上 同上

后果年级管理员grade_managed scope无法查看任何成绩数据可能是业务逻辑漏洞。年级管理员应能查看所管年级的所有班级成绩。

2.13 路由缺陷page.tsx 缺失P1

位置 问题 违反规则
src/app/(dashboard)/management/grade/page.tsx 文件缺失,但有 loading.tsx/error.tsx 孤儿文件 "路由页面应完整"

后果:访问 /management/grade 会 404但 loading.tsx 和 error.tsx 仍存在,造成混乱。

2.14 角色覆盖不一致admin/parent 无 diagnostic UIP2

位置 问题 违反规则
005_architecture_data.json L174-175, L214-215 admin 和 parent 都有 DIAGNOSTIC_MANAGE/DIAGNOSTIC_READ 权限 "权限点应有对应 UI"
src/app/(dashboard)/admin/ 无 diagnostic 页面 同上
src/app/(dashboard)/parent/ 无 diagnostic 页面 同上

后果admin 和 parent 拥有 diagnostic 权限但无对应 UI权限与 UI 覆盖不一致。家长无法查看子女的学情诊断报告。

2.15 SearchParams 工具未统一P3

位置 问题 违反规则
student/grades/page.tsx 自定义 SearchParams 类型和 getParam 函数 "最大化复用"
management/grade/insights/page.tsx 自定义 SearchParams 类型和 getParam 函数 同上

后果:与 teacher 端 grades 页面已复用 @/shared/lib/search-params 的做法不一致,存在重复代码。


三、行业差距对比

3.1 成绩模块grades行业对标

功能维度 行业优秀实践K12 成绩管理系统) 当前实现 差距影响
成绩录入 支持Excel批量导入、扫码录入、语音录入录入时实时校验分数范围自动计算总分、平均分 仅支持单条录入 + 批量录入(表单式);有分数范围校验;无 Excel 导入 教师录入效率低,大班级成绩录入耗时
成绩分析 多维度分析(班级/年级/个人/科目);支持自定义分析维度;提供归因分析(哪些题目失分多) 5 种分析(趋势/班级对比/科目对比/分布/排名);无归因分析;无自定义维度 教师无法定位失分原因,难以针对性教学
可视化 交互式图表hover 显示详情、点击下钻);支持图表下载为图片;支持自定义图表配置 静态图表TrendLineChart/SimpleBarChart无 hover 详情;无下载功能 数据呈现不够直观,教师难以深入分析
报告导出 支持 PDF/Excel/CSV 多格式;支持自定义报告模板;支持批量导出(按班级/年级) 仅 Excel 导出(明细 + 班级汇总);无 PDF无自定义模板 无法满足学校正式报告需求(如家长会报告需 PDF
预警机制 成绩异常预警(突然下降/持续低迷);及格率预警;班级对比异常预警 无预警机制 教师无法及时发现学生成绩异常
多角色视图 学生看自己 + 班级平均;家长看子女 + 班级排名;教师看所教班级;管理员看全校 4 角色都有基本视图;但 parent 无 diagnosticadmin 无 diagnostic 家长无法全面了解子女学情
空状态/加载态 完善的空状态插画 + 引导操作;骨架屏过渡 仅部分页面有 loading.tsx组件无 Suspense 用户体验差,白屏等待
数据联动 成绩 → 学情诊断 → 推荐练习;成绩 → 作业 → 知识点掌握度 grades 与 diagnostic 无数据联动;无推荐练习 无法形成"诊断-练习-反馈"闭环

3.2 学情诊断模块diagnostic行业对标

功能维度 行业优秀实践K12 学情诊断系统) 当前实现 差距影响
知识点掌握度 基于IRT项目反应理论计算支持知识点权重支持时间衰减近期表现权重更高 基于正确率简单计算;无权重;无时间衰减 掌握度计算不够精准
诊断报告 自动生成 PDF 报告;支持自定义模板;含学习建议、练习推荐、进步轨迹 生成 draft 报告JSON 存储);无 PDF建议为静态文本 报告不够专业,无法直接发给家长
可视化 雷达图 + 热力图 + 知识图谱;支持知识点下钻;支持时间对比 雷达图 + 热力图;无知识图谱;无下钻 知识结构呈现不够清晰
个性化推荐 基于弱项推荐练习题/微课;支持难度自适应;支持学习路径规划 仅列出弱项知识点 + "Practice" 链接(跳转到作业列表) 无法精准推荐练习内容
班级诊断 班级整体掌握度 + 重点关注学生列表 + 教学建议;支持按知识点筛选学生 班级掌握度摘要 + 需关注学生列表;无教学建议 教师难以根据诊断调整教学
历史趋势 掌握度随时间变化曲线;支持对比多个时间段 无历史趋势(仅当前快照) 无法评估学习进步情况
多角色覆盖 学生/家长/教师/管理员都能查看;家长看子女诊断报告 仅 teacher + student 有 UIparent/admin 无 UI 家长无法了解子女学情

3.3 关键差距总结

  1. 数据孤岛grades 和 diagnostic 模块无数据联动,无法形成"成绩 → 诊断 → 练习 → 反馈"闭环。行业优秀产品(如猿题库、作业帮)已实现完整学习闭环。
  2. 家长端缺失parent 无 diagnostic UI家长无法查看子女学情诊断报告。K12 场景下家长是重要决策者,缺失影响家校沟通。
  3. 报告专业度不足diagnostic 报告为 JSON 存储,无 PDF 导出,无法直接用于家长会。行业产品普遍支持专业 PDF 报告。
  4. 预警机制空白:成绩异常、掌握度低迷无预警,教师无法主动干预。
  5. 可视化深度不足:无知识图谱、无下钻分析、无时间对比,数据呈现停留在表层。

四、改进优先级建议

P0紧急 — 安全与合规)

# 问题 改进方向
P0-1 权限校验缺失10 个页面) 所有页面调用 requirePermission()teacher/grades 用 GRADE_RECORD_READ/GRADE_RECORD_MANAGEteacher/diagnostic 用 DIAGNOSTIC_READ/DIAGNOSTIC_MANAGEstudent/parent 用对应 READ 权限
P0-2 diagnostic/data-access-reports.ts 直查 users 表 改为调用 @/modules/users/data-accessgetUserNamesByIds,删除 users 表 import
P0-3 i18n 完全缺失 + grade-management 运行时报错 创建 grades.jsondiagnostic.json 翻译文件zh-CN + en修复 grade-management 模块的 grade 命名空间(创建 grade.json 或改用 gradeManagement);在 i18n/request.ts 注册新命名空间
P0-4 /management/grade/page.tsx 缺失 补齐 page.tsx 或删除孤儿 loading.tsx/error.tsx

P1较严重 — 架构与质量)

# 问题 改进方向
P1-1 统计业务逻辑混入 data-access 抽取 grades/stats-service.ts,将 getClassGradeStats/getClassComparison/getSubjectComparison/getGradeDistribution/getRankingTrend 的统计计算迁移至纯函数(参考 homework/stats-service.ts 范例)
P1-2 重复工具函数 抽取 grades/lib/scope-filter.tsbuildScopeClassFilter)和 grades/lib/stats-utils.tstoNumber/normalize
P1-3 12 个 Action 缺失 Zod 校验 deleteGradeRecordAction/getGradeRecordsAction/getClassGradeStatsAction/getStudentGradeSummaryAction/getClassRankingAction/getGradeRecordByIdAction/exportGradesAction + 5 个 analytics Action 创建对应 Zod schema
P1-4 as 断言违规4 处) 使用类型守卫或 Zod 运行时校验替代
P1-5 Error Boundary 和 Suspense 缺失 创建 grades/components/widget-boundary.tsxError Boundary + Suspense + Skeleton 组合每个数据区块独立包裹teacher/grades 和 teacher/diagnostic 路由补齐 loading.tsx/error.tsx
P1-6 架构图同步 更新 004005 文档grades 行数、diagnostic deps、新增 stats-service.ts、新增 lib/、补齐 actions-analytics exports

P2优化 — 体验与扩展)

# 问题 改进方向
P2-1 a11y 无障碍缺失 补充 ARIA 属性:图表 role="img" + aria-label;按钮 aria-label;表格 caption;列表 role="list"
P2-2 Tailwind 任意值 移除 w-[180px]/h-[360px]/max-w-[520px],改用设计令牌或注释说明
P2-3 班级报告 studentId 字段语义错误 修改 learningDiagnosticReports schemastudentId 改为可空,或增加 classId/generatedBy 字段
P2-4 grade_managed scope 返回空数据 修复 buildScopeClassFiltergrade_managed scope 应返回所管年级的班级过滤条件
P2-5 admin/parent 无 diagnostic UI 新增 /parent/diagnostic/ 页面家长查看子女诊断报告admin 可复用 teacher 视图
P2-6 SearchParams 工具未统一 student/grades 和 management/grade/insights 改用 @/shared/lib/search-params

P3长期 — 行业对标)

# 问题 改进方向
P3-1 grades 与 diagnostic 无数据联动 设计联动接口:成绩录入后触发掌握度更新;诊断报告含成绩趋势
P3-2 无预警机制 新增 grades/alerts-service.ts:成绩下降预警、及格率预警、掌握度低迷预警
P3-3 诊断报告无 PDF 导出 集成 PDF 生成库(如 @react-pdf/renderer支持专业报告模板
P3-4 无知识图谱可视化 引入知识图谱组件(如 react-flow展示知识点关系与掌握度
P3-5 无个性化练习推荐 基于弱项推荐练习题,对接 questions 模块
P3-6 Widget 配置系统 设计 GradesWidgetConfig/DiagnosticWidgetConfig 类型,按角色配置渲染哪些 Widget

五、架构图同步说明

本次审计发现架构图存在以下遗漏或不一致,需在实现后同步更新:

5.1 004_architecture_impact_map.md 需补充

  1. §2.6 grades 模块

    • 更新文件清单行数:data-access.ts 419→433、data-access-analytics.ts 293→337
    • 补充 actions-analytics.ts 的 5 个 Action 到 exports 清单(当前仅列 11 个,实际 15 个)
    • 新增 stats-service.tsP1-1 抽取后)
    • 新增 lib/scope-filter.tslib/stats-utils.tsP1-2 抽取后)
    • 新增 components/widget-boundary.tsxP1-5 新增)
  2. §2.22 diagnostic 模块

    • 更新已知问题:标注 data-access-reports.ts 直查 users 表P0-2 修复前)
    • 更新文件清单行数(如有变化)
  3. 路由清单

    • 标注 /management/grade/page.tsx 缺失P0-4 修复前)
    • 标注 teacher/grades 和 teacher/diagnostic 路由缺少 loading.tsx/error.tsx
    • 新增 /parent/diagnostic/ 路由P2-5 实现后)

5.2 005_architecture_data.json 需修改

  1. modules.grades 节点L7362

    • 更新 dataAccess 中各函数的 deps:移除直查 classes/classEnrollments/subjects/users,改为 classes/data-access.*/school/data-access.*/users/data-access.*
    • 新增 stats-service.ts 的 exports
    • 新增 lib/scope-filter.tslib/stats-utils.ts 的 exports
    • 补充 actions-analytics.ts 的 5 个 Action 到 actions 数组
  2. modules.diagnostic 节点L10927

    • 更新 dataAccess 中各函数的 deps:移除直查 users/classes/classEnrollments/examSubmissions/submissionAnswers/questionsToKnowledgePoints,改为对应模块 data-access
    • 标注 data-access-reports.tsgetDiagnosticReports/getDiagnosticReportById 依赖 users/data-access.getUserNamesByIdsP0-2 修复后)
  3. permissions 节点:

    • 确认 GRADE_RECORD_READ/GRADE_RECORD_MANAGE/DIAGNOSTIC_READ/DIAGNOSTIC_MANAGE 权限点已定义(已存在 ✓)
  4. routes 节点:

    • 补充 teacher/grades/entry、teacher/grades/stats、teacher/diagnostic/class/[classId]、teacher/diagnostic/student/[studentId] 路由
    • 标注 /management/grade/page.tsx 缺失
    • 新增 /parent/diagnostic/ 路由P2-5 实现后)
  5. dependencyMatrix

    • 更新 grades → classes/school/users 的依赖关系(通过 data-access已正确
    • 更新 diagnostic → classes/exams/questions/users 的依赖关系(通过 data-accessP0-2 修复后完全正确)

5.3 翻译文件结构示例

src/shared/i18n/messages/
├─ zh-CN/
│  ├─ grades.json       # 新增(成绩模块)
│  ├─ diagnostic.json   # 新增(学情诊断模块)
│  └─ grade.json        # 新增grade-management 模块,修复运行时报错)
└─ en/
   ├─ grades.json       # 新增
   ├─ diagnostic.json   # 新增
   └─ grade.json        # 新增

grades.json 结构示例zh-CN

{
  "title": {
    "list": "成绩查询",
    "entry": "成绩录入",
    "analytics": "成绩分析",
    "stats": "成绩统计"
  },
  "filters": {
    "class": "班级",
    "subject": "科目",
    "type": "类型",
    "semester": "学期",
    "allClasses": "全部班级",
    "allSubjects": "全部科目",
    "allTypes": "全部类型",
    "allSemesters": "全部学期",
    "searchPlaceholder": "按标题搜索..."
  },
  "type": {
    "exam": "考试",
    "quiz": "测验",
    "homework": "作业",
    "other": "其他"
  },
  "semester": {
    "s1": "第一学期",
    "s2": "第二学期"
  },
  "list": {
    "empty": "暂无成绩记录",
    "columns": {
      "student": "学生",
      "class": "班级",
      "subject": "科目",
      "title": "标题",
      "score": "分数",
      "type": "类型",
      "semester": "学期",
      "recordedBy": "录入人",
      "date": "日期"
    }
  },
  "form": {
    "title": "录入成绩",
    "save": "保存",
    "saving": "保存中...",
    "cancel": "取消",
    "selectClass": "选择班级",
    "selectSubject": "选择科目",
    "selectStudent": "选择学生",
    "titlePlaceholder": "如期中考试",
    "score": "分数",
    "fullScore": "满分",
    "remark": "备注(可选)",
    "remarkPlaceholder": "关于此成绩的备注..."
  },
  "delete": {
    "title": "删除成绩记录",
    "confirmation": "确定要删除此成绩记录吗?此操作不可撤销。",
    "confirm": "删除",
    "cancel": "取消",
    "deleting": "删除中..."
  },
  "export": {
    "detail": "导出成绩明细",
    "classReport": "导出班级成绩总表",
    "success": "导出成功",
    "failed": "导出失败"
  },
  "stats": {
    "title": "统计",
    "average": "平均分",
    "median": "中位数",
    "max": "最高分",
    "min": "最低分",
    "stdDev": "标准差",
    "variance": "方差",
    "passRate": "及格率",
    "excellentRate": "优秀率",
    "count": "人数"
  },
  "analytics": {
    "trend": "成绩趋势",
    "classComparison": "班级对比",
    "subjectComparison": "科目对比",
    "distribution": "分数分布",
    "ranking": "排名",
    "rankingTrend": "排名趋势"
  },
  "batch": {
    "title": "批量录入",
    "saving": "保存中...",
    "restored": "已恢复未保存的成绩草稿",
    "invalidScores": "存在无效分数",
    "fullScoreRequired": "满分必填",
    "saved": "已录入"
  },
  "empty": {
    "noRecords": "暂无成绩记录",
    "noData": "暂无数据"
  },
  "error": {
    "loadFailed": "加载失败",
    "saveFailed": "保存失败",
    "deleteFailed": "删除失败",
    "retry": "重试"
  }
}

diagnostic.json 结构示例zh-CN

{
  "title": {
    "student": "学生学情诊断",
    "class": "班级学情诊断",
    "reportList": "诊断报告"
  },
  "type": {
    "individual": "个人",
    "class": "班级",
    "grade": "年级"
  },
  "status": {
    "draft": "草稿",
    "published": "已发布",
    "archived": "已归档"
  },
  "filters": {
    "reportType": "报告类型",
    "status": "状态",
    "allTypes": "全部类型",
    "allStatuses": "全部状态"
  },
  "summary": {
    "overallMastery": "总体掌握度",
    "strengths": "强项",
    "weaknesses": "弱项",
    "students": "学生数",
    "avgMastery": "平均掌握度",
    "needAttention": "需重点关注"
  },
  "chart": {
    "radarTitle": "知识点掌握度",
    "radarDescription": "掌握度雷达图",
    "heatmapTitle": "知识点掌握度热力图",
    "rankingTitle": "知识点排名"
  },
  "report": {
    "generate": "生成诊断报告",
    "generateStudent": "生成学生诊断报告",
    "generateClass": "生成班级诊断报告",
    "publish": "发布",
    "delete": "删除",
    "publishTitle": "发布报告",
    "deleteTitle": "删除报告",
    "recommendations": "学习建议",
    "history": "报告历史"
  },
  "strengths": {
    "title": "强项≥80%",
    "practice": "练习"
  },
  "weaknesses": {
    "title": "弱项(<60%",
    "practice": "练习"
  },
  "empty": {
    "noData": "暂无诊断数据",
    "noClassData": "无法加载班级掌握度摘要",
    "noMastery": "暂无知识点掌握度记录",
    "noReports": "暂无诊断报告"
  },
  "error": {
    "generateFailed": "生成报告失败",
    "publishFailed": "发布失败",
    "deleteFailed": "删除失败",
    "loadFailed": "加载失败",
    "retry": "重试"
  }
}

六、合规项确认

以下条目已通过审计

  • grades 模块跨模块依赖全部通过 data-access所有跨模块访问classes/school/users均通过对方 data-access 函数
  • diagnostic 模块 data-access.ts 跨模块依赖通过 data-access(仅 data-access-reports.ts 违规)
  • 所有 Server Action 调用 requirePermission()grades 15 个 + diagnostic 6 个 = 21 个 Action 全部合规
  • 所有 Server Action 返回 ActionState<T>
  • 所有 Server Action 使用 revalidatePath 精确刷新
  • role === "xxx" 硬编码:全模块无
  • diagnostic 组件使用 usePermission().hasPermission()class-diagnostic-view.tsx 和 report-list.tsx 已使用
  • dangerouslySetInnerHTML
  • any 类型
  • 文件行数全部合规:最大为 grades/components/batch-grade-entry.tsx 442 行 < 500 行组件建议上限
  • "use client" / "use server" / "server-only" 正确放置
  • import type 使用规范
  • diagnostic schema.ts 枚举与 types.ts 联合类型一致
  • 接口命名规范(无 I 前缀PascalCase

七、重构方案设计要点(供后续实现参考)

7.1 完全解耦

  • 定义 GradesDataService 接口抽象数据依赖,使用 React Context 注入
  • 模块内部组件绝不直接 import 其他业务模块的 actions 或 data-access
  • 不同角色差异通过接口不同实现隔离(如 TeacherGradesService/StudentGradesService/ParentGradesService

7.2 组合优先

  • 所有 UI 通过组件组合children、slots、render props实现灵活性
  • 逻辑复用抽取为自定义 hooksuseGradeRecords/useGradeTrend/useMasterySummary
  • 严禁继承或深层嵌套 HOC

7.3 最大化复用

  • 识别四角色共用 UI 块:GradeTrendChart/GradeStatsCard/MasteryRadarChart/WidgetBoundary
  • 抽象泛型组件:<DataTable<T>>/<FilterBar>/<EmptyState>/<ErrorState>
  • 各角色模块仅组合复用单元,可配置化显示内容

7.4 配置驱动

  • 设计 GradesWidgetConfig 类型,按角色配置渲染哪些 Widget
  • 示例teacher 看 [录入, 查询, 分析, 统计]student 看 [我的成绩, 趋势]parent 看 [子女成绩, 趋势]

7.5 错误与边界处理

  • 每个独立数据区块用 <WidgetBoundary>Error Boundary + Suspense + Skeleton 组合)包裹
  • 明确处理空数据、无权限、网络异常等边界状态
  • 支持流式渲染React Server Components 获取初始数据)

7.6 可测试性

  • 数据获取、计算、格式化等纯逻辑放入 stats-service.ts 或 hooks
  • 导出清晰接口类型以便 mock
  • 统计函数为纯函数,易于单测

7.7 监控埋点

  • 预留关键操作埋点接口:成绩录入、报告生成、报告发布、导出操作
  • 通过 shared/lib/analytics 统一上报