# 学情诊断（Diagnostic）模块审计报告 v2

> 审计日期：2026-06-25
> 审计范围：`src/modules/diagnostic/**`、`src/app/(dashboard)/{teacher,student,parent}/diagnostic/**`
> 参照规则：`.trae/rules/project_rules.md`、`docs/architecture/004_architecture_impact_map.md` §2.22、`docs/architecture/005_architecture_data.json`
> 前置文档：
> - [diagnostic-audit-report.md](./diagnostic-audit-report.md)（v1，2026-06-22，3 P0 + 5 P1 + 5 P2 全部完成）
> - [grades-diagnostic-audit-report-v4.md](./grades-diagnostic-audit-report-v4.md)（v4，2026-06-23，12 项 P1 全部完成）

---

## 一、现有实现概要

### 1.1 v1/v4 已完成项回顾

v1 与 v4 审计共完成 **25 项**改进（3 P0 + 5 P1 + 5 P2 + 12 v4-P1），涵盖：跨模块 WidgetBoundary 提升到 shared、教师页面标题 i18n 化、教师 error.tsx i18n 化、as 断言消除、分享按钮移除、报告内容 i18n 驱动、Excel 导出 i18n 化、班级报告导出明细、角色配置驱动（role-config.ts）、年级诊断报告纵向切片、热力图键盘导航、DataScope 行级权限、师生关系校验、草稿隔离、通知机制、热力图图例、移动端表格滚动等。

### 1.2 当前文件分布（v2 实测）

| 层 | 文件 | 行数 | 职责 |
|------|------|------|------|
| 类型 | [types.ts](file:///e:/Desktop/CICD/src/modules/diagnostic/types.ts) | 126 | DiagnosticReport / Mastery / Summary 类型定义（含 v4-P2-3 GradeMasterySummary） |
| 数据访问 | [data-access.ts](file:///e:/Desktop/CICD/src/modules/diagnostic/data-access.ts) | 519 | 掌握度查询 + 从提交/作业/成绩更新掌握度（含事务） |
| 数据访问 | [data-access-reports.ts](file:///e:/Desktop/CICD/src/modules/diagnostic/data-access-reports.ts) | 323 | 诊断报告 CRUD + DataScope 过滤 + 结构化错误码 |
| 统计服务 | [stats-service.ts](file:///e:/Desktop/CICD/src/modules/diagnostic/stats-service.ts) | 506 | 14 个纯统计函数（含年级聚合） |
| Server Actions | [actions.ts](file:///e:/Desktop/CICD/src/modules/diagnostic/actions.ts) | 302 | 6 个 Action（含年级生成 + 通知） |
| 校验 | [schema.ts](file:///e:/Desktop/CICD/src/modules/diagnostic/schema.ts) | 39 | 5 个 Zod schema |
| 导出 | [export.ts](file:///e:/Desktop/CICD/src/modules/diagnostic/export.ts) | 175 | Excel 导出（含班级明细 3 Sheet） |
| 角色配置 | [role-config.ts](file:///e:/Desktop/CICD/src/modules/diagnostic/role-config.ts) | 40 | 角色配置驱动（v4-P2-2） |
| 组件 | [components/student-diagnostic-view.tsx](file:///e:/Desktop/CICD/src/modules/diagnostic/components/student-diagnostic-view.tsx) | 299 | 学生诊断视图（概览+雷达+强弱项+报告+历史） |
| 组件 | [components/class-diagnostic-view.tsx](file:///e:/Desktop/CICD/src/modules/diagnostic/components/class-diagnostic-view.tsx) | 449 | 班级诊断视图（热力图+筛选+排名+关注列表+生成） |
| 组件 | [components/report-list.tsx](file:///e:/Desktop/CICD/src/modules/diagnostic/components/report-list.tsx) | 373 | 报告列表（过滤+表格+发布/删除/导出） |
| 组件 | [components/mastery-radar-chart.tsx](file:///e:/Desktop/CICD/src/modules/diagnostic/components/mastery-radar-chart.tsx) | 85 | 雷达图封装 |
| 组件 | [components/confidence-utils.ts](file:///e:/Desktop/CICD/src/modules/diagnostic/components/confidence-utils.ts) | 31 | 置信度计算 |
| 页面 | 4 个 `page.tsx` + 5 个 `loading.tsx` + 5 个 `error.tsx` | — | teacher/student/parent 三角色路由 |
| i18n | [zh-CN/diagnostic.json](file:///e:/Desktop/CICD/src/shared/i18n/messages/zh-CN/diagnostic.json) + [en/diagnostic.json](file:///e:/Desktop/CICD/src/shared/i18n/messages/en/diagnostic.json) | 252 / 同步 | 翻译文件 |

### 1.3 数据流

```
page.tsx (RSC)
  ├─ getStudentMasterySummary / getClassMasterySummary / getGradeMasterySummary / getKnowledgePointStats  (data-access)
  │    └─ db (drizzle) → knowledgePointMastery / knowledgePoints 表
  │    └─ 跨模块 data-access：classes / users / school / exams / homework / questions
  ├─ getDiagnosticReports (data-access-reports, 含 DataScope 过滤)
  │    └─ db → learningDiagnosticReports 表
  └─ <StudentDiagnosticView> / <ClassDiagnosticView> / <ReportList>  (client)
       └─ generateStudentReportAction / generateClassReportAction / generateGradeReportAction
          / publishReportAction / deleteReportAction / exportDiagnosticReportAction
          / getClassStudentsByKnowledgePointAction
```

### 1.4 架构图记录完整性

经核对 [004_architecture_impact_map.md](file:///e:/Desktop/CICD/docs/architecture/004_architecture_impact_map.md) §2.22 与 [005_architecture_data.json](file:///e:/Desktop/CICD/docs/architecture/005_architecture_data.json) `modules.diagnostic` 节点，架构图对诊断模块的记录**基本完整**，已涵盖 v1/v4 全部修复。但本次 v2 审计发现以下偏差需后续同步：

- 架构图未记录 `getDiagnosticReports` 中 `grade_managed` scope 未过滤的已知缺陷（v2-P1-1）。
- 架构图未记录 `confidence-utils.ts` 的置信度计算逻辑过于简化（v2-P1-5）。
- 架构图未记录 3 个子路由 error.tsx 仍存在硬编码中文（v2-P0-1）。

---

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

### 2.1 国际化

#### 问题 2.1.1 ｜ 3 个子路由 error.tsx 硬编码中文（P0）

- **位置**：
  - [teacher/diagnostic/class/[classId]/error.tsx#L17-22](file:///e:/Desktop/CICD/src/app/(dashboard)/teacher/diagnostic/class/[classId]/error.tsx#L17)：`title="班级学情诊断加载失败"` `description="抱歉，加载班级诊断数据时发生了意外错误。请稍后重试。"` `label="重试"`
  - [teacher/diagnostic/student/[studentId]/error.tsx#L17-22](file:///e:/Desktop/CICD/src/app/(dashboard)/teacher/diagnostic/student/[studentId]/error.tsx#L17)：`title="学生学情诊断加载失败"` 同样硬编码
  - [parent/diagnostic/error.tsx#L17-22](file:///e:/Desktop/CICD/src/app/(dashboard)/parent/diagnostic/error.tsx#L17)：`title="子女学情诊断加载失败"` 同样硬编码
- **现象**：三个 error.tsx 客户端组件未使用 `useTranslations`，全部硬编码中文文案。
- **违反规则**：项目规则"所有用户可见文本必须适配 i18n（使用 next-intl），提取翻译键"。
- **原因**：v1 审计 P0-3 仅修复了 `teacher/diagnostic/error.tsx`（教师报告列表页），遗漏了教师子路由和学生/家长子路由的 error.tsx。
- **后果**：英文环境下这三个错误页显示中文，与系统其他已 i18n 化的错误页风格不一致。

#### 问题 2.1.2 ｜ i18n 标签与代码逻辑不一致（P1）

- **位置**：
  - i18n：[zh-CN/diagnostic.json#L78](file:///e:/Desktop/CICD/src/shared/i18n/messages/zh-CN/diagnostic.json#L78)：`"weaknesses": { "title": "弱项（<60%）" }`
  - 代码：[stats-service.ts#L83-99](file:///e:/Desktop/CICD/src/modules/diagnostic/stats-service.ts#L83)：`classifyStrengthsWeaknesses` 中弱项阈值为 `< 80`（P3-16 修复：消除 60-79 盲区）
- **现象**：i18n 标签显示"弱项（<60%）"，但代码实际将掌握度 < 80 的知识点都归类为弱项。
- **违反规则**：项目规则"所有用户可见文本必须适配 i18n"——文本需与逻辑一致。
- **原因**：P3-16 修复弱项分类阈值时未同步更新 i18n 标签。
- **后果**：用户看到"弱项（<60%）"标签，但实际列表包含 60-79% 的知识点，造成认知混乱。

#### 问题 2.1.3 ｜ 死 i18n 键未清理（P2）

- **位置**：[zh-CN/diagnostic.json#L157-165](file:///e:/Desktop/CICD/src/shared/i18n/messages/zh-CN/diagnostic.json#L157)
- **现象**：`reportList.share`、`reportList.shareAriaLabel`、`reportList.shareTitle`、`reportList.shareDescription`、`reportList.shareLinkLabel`、`reportList.copyLink`、`reportList.copyLinkSuccess`、`reportList.copyLinkFailed`、`reportList.shareLinkAriaLabel` 共 9 个键仍保留在翻译文件中，但 v1-P1-3 已移除分享按钮，这些键不再被引用。
- **违反规则**：项目规则精神——保持代码与配置一致，避免死代码。
- **原因**：移除分享按钮时未清理对应的 i18n 键。
- **后果**：翻译文件臃肿，维护成本增加；新增语言时需翻译无用的键。

#### 问题 2.1.4 ｜ 热力图 aria-label 硬编码中文标点格式（P1）

- **位置**：[class-diagnostic-view.tsx#L195](file:///e:/Desktop/CICD/src/modules/diagnostic/components/class-diagnostic-view.tsx#L195)
- **现象**：`aria-label={`${kp.knowledgePointName}：${kp.averageMastery.toFixed(1)}%，${levelLabel}，${kp.masteredCount}/${kp.totalStudents}`}` 使用硬编码中文全角冒号"："和逗号"，"。
- **违反规则**：项目规则"所有用户可见文本必须适配 i18n"。
- **原因**：aria-label 拼接时未使用 i18n 模板。
- **后果**：英文环境下屏幕阅读器读出中文标点，影响无障碍体验。

#### 问题 2.1.5 ｜ export.ts 错误消息硬编码英文（P2）

- **位置**：[export.ts#L28](file:///e:/Desktop/CICD/src/modules/diagnostic/export.ts#L28)：`throw new Error("Report not found")`
- **现象**：导出报告不存在时抛出硬编码英文错误。
- **违反规则**：项目规则"所有用户可见文本必须适配 i18n"。
- **后果**：中文环境下用户看到英文错误消息。

### 2.2 权限与安全

#### 问题 2.2.1 ｜ grade_managed DataScope 未过滤（P1，安全漏洞）

- **位置**：[data-access-reports.ts#L220-238](file:///e:/Desktop/CICD/src/modules/diagnostic/data-access-reports.ts#L220)
- **现象**：`getDiagnosticReports` 仅处理 `children` 和 `class_taught` 两种 DataScope，对 `grade_managed`（年级主任）和 `class_members`（学生）scope 不做任何过滤。代码注释明确写道：`// grade_managed 需要跨模块查询年级学生，由调用方自行过滤`。
- **违反规则**：项目规则"所有敏感数据查询必须在 data-access 层结合当前用户权限过滤，Server Action 二次校验"。
- **原因**：grade_managed scope 需要跨模块查询年级学生 ID（通过 `getUserIdsByGradeId`），实现时为避免跨模块依赖未在 data-access 层完成过滤。
- **后果**：年级主任（grade_head / teaching_head）角色调用时，`getDiagnosticReports` 返回全校所有学生的诊断报告，存在数据越权风险。虽然当前 teacher/diagnostic/page.tsx 在客户端对 class_members 做了二次过滤，但 grade_managed 完全未过滤。

#### 问题 2.2.2 ｜ 教师报告列表页客户端过滤 class_members（P2）

- **位置**：[teacher/diagnostic/page.tsx#L56-59](file:///e:/Desktop/CICD/src/app/(dashboard)/teacher/diagnostic/page.tsx#L56)
- **现象**：`const visibleReports = ctx.dataScope.type === "class_members" ? reports.reports.filter((r) => r.studentId === ctx.userId) : reports.reports`
- **违反规则**：项目规则"所有敏感数据查询必须在 data-access 层结合当前用户权限过滤"——客户端过滤不安全。
- **原因**：教师页面理论上不应被学生角色访问，但代码保留了 class_members 分支作为防御性过滤。这种过滤应在 data-access 层完成。
- **后果**：虽然不影响功能（学生不会访问教师路由），但违背了"数据过滤在 data-access 层"的原则，且 data-access 已返回了不该返回的数据。

### 2.3 架构解耦

#### 问题 2.3.1 ｜ 组件直接 import actions，无服务接口抽象（P1）

- **位置**：
  - [report-list.tsx#L41](file:///e:/Desktop/CICD/src/modules/diagnostic/components/report-list.tsx#L41)：`import { publishReportAction, deleteReportAction, exportDiagnosticReportAction } from "../actions"`
  - [class-diagnostic-view.tsx#L33](file:///e:/Desktop/CICD/src/modules/diagnostic/components/class-diagnostic-view.tsx#L33)：`import { generateClassReportAction, getClassStudentsByKnowledgePointAction } from "../actions"`
- **现象**：客户端组件直接 import 并调用 Server Actions，未通过接口抽象或依赖注入。
- **违反规则**：项目规则"通过定义 TypeScript 接口抽象数据依赖，使用 React Context 注入数据服务，模块内部组件绝不直接 import 其他业务模块的 actions 或 data-access（只能通过注入的接口调用）"。
- **原因**：v1 审计将此项列为"后续建议"未实施，但用户在本次审计中将其升级为强制要求。
- **后果**：组件无法独立测试（测试时必须 mock 整个 actions 模块）；无法在不修改组件代码的情况下替换 actions 实现；组件与 Server Action 实现紧耦合。

### 2.4 错误处理与边界

#### 问题 2.4.1 ｜ 无 Error Boundary 包裹独立数据区块（P1）

- **位置**：
  - [student-diagnostic-view.tsx](file:///e:/Desktop/CICD/src/modules/diagnostic/components/student-diagnostic-view.tsx)：概览卡片、雷达图、强弱项、报告、历史列表均在同一组件内，无 Error Boundary 隔离。
  - [class-diagnostic-view.tsx](file:///e:/Desktop/CICD/src/modules/diagnostic/components/class-diagnostic-view.tsx)：概览、热力图、筛选、排名、关注列表、生成报告均在同一组件内。
- **现象**：仅页面级有 error.tsx 错误边界，组件内部各数据区块无独立 Error Boundary。
- **违反规则**：项目规则"每个独立的数据区块必须用 React Error Boundary 包裹"。
- **原因**：v4-P2 已将 WidgetBoundary 提升到 shared 层并用于页面级包裹，但未下沉到组件内部各数据区块。
- **后果**：雷达图渲染失败会导致整个诊断页面崩溃；热力图数据异常会波及排名表和关注列表。

#### 问题 2.4.2 ｜ 异步数据无 Suspense + 骨架屏（P2）

- **位置**：所有页面均使用 `await Promise.all` 一次性获取所有数据后传给客户端组件。
- **现象**：未使用 React Suspense 流式渲染，数据获取完成前整个页面阻塞。
- **违反规则**：项目规则"异步数据使用 React Suspense + 骨架屏"。
- **后果**：首屏白屏时间长；无法渐进式展示数据区块。

### 2.5 可测试性与置信度

#### 问题 2.5.1 ｜ 置信度计算过于简化（P1）

- **位置**：[confidence-utils.ts#L18-21](file:///e:/Desktop/CICD/src/modules/diagnostic/components/confidence-utils.ts#L18)
- **现象**：`getConfidenceLevel` 仅判断 `overallScore === null` 返回 "insufficient"，否则一律返回 "high"。注释承认"后续可扩展为基于 totalQuestions 等数据量字段的多级判断"，但未实施。
- **违反规则**：项目规则"数据获取、计算、格式化等纯逻辑全部放入纯函数或 hooks，与 UI 分离"——置信度计算虽已提取为纯函数，但逻辑不完整。
- **原因**：v4-P3-7 引入置信度时为简化首版，未实现多级判断。
- **后果**：仅做了 1 道题的报告也显示"高置信度"，误导教师判断报告可信度。

#### 问题 2.5.2 ｜ stats-service 14 个纯函数无单测（P2）

- **位置**：[stats-service.ts](file:///e:/Desktop/CICD/src/modules/diagnostic/stats-service.ts)
- **现象**：14 个纯函数（`computeAverageMastery`、`classifyStrengthsWeaknesses`、`aggregateClassMastery` 等）已正确提取为纯函数，但无对应的单元测试文件。
- **违反规则**：项目规则"可测试性：数据获取、计算、格式化等纯逻辑全部放入纯函数或 hooks，与 UI 分离；导出清晰的接口类型以便 mock"。
- **后果**：统计数据计算逻辑变更无回归保障；P3-16 弱项阈值修复这类边界 case 无法自动验证。

### 2.6 可复用性与扩展性

#### 问题 2.6.1 ｜ 雷达图知识点名称截断无 tooltip（P2）

- **位置**：[mastery-radar-chart.tsx#L22-26](file:///e:/Desktop/CICD/src/modules/diagnostic/components/mastery-radar-chart.tsx#L22)
- **现象**：`shortName: d.knowledgePoint.length > 8 ? ${d.knowledgePoint.slice(0, 8)}... : d.knowledgePoint` 截断超过 8 字符的知识点名称，但未提供 tooltip 显示完整名称。
- **违反规则**：项目规则"明确处理空数据、无权限、网络异常等边界状态"——信息截断是边界状态。
- **后果**：长名称知识点被截断，用户无法查看完整名称，影响数据理解。

#### 问题 2.6.2 ｜ 通知类型使用 "grade" 而非专用类型（P2）

- **位置**：[actions.ts#L157, L173](file:///e:/Desktop/CICD/src/modules/diagnostic/actions.ts#L157)
- **现象**：`createNotification({ type: "grade", ... })` 诊断报告发布通知复用了成绩通知类型 "grade"。
- **违反规则**：项目规则精神——类型应语义准确，便于分类与过滤。
- **后果**：学生无法区分"成绩通知"和"诊断报告通知"；通知筛选功能无法按类型精确过滤诊断报告。

#### 问题 2.6.3 ｜ 无监控埋点接口（P2）

- **位置**：整个模块无任何监控/埋点代码。
- **现象**：报告生成、发布、删除、导出等关键操作无埋点。
- **违反规则**：项目规则"监控：方案中预留关键操作埋点接口"。
- **后果**：无法追踪诊断报告的使用情况；无法度量报告生成/发布转化率；异常无法主动发现。

### 2.7 parent 页面细节

#### 问题 2.7.1 ｜ parent/diagnostic/page.tsx noRecordsTitle 与 noRecordsDescription 重复（P2）

- **位置**：[parent/diagnostic/page.tsx#L97-98](file:///e:/Desktop/CICD/src/app/(dashboard)/parent/diagnostic/page.tsx#L97)
- **现象**：`noRecordsTitle={t("parent.noReports")}` 和 `noRecordsDescription={t("parent.noReports")}` 使用同一个 i18n 键。
- **原因**：复制粘贴时未区分标题和描述。
- **后果**：空状态时标题和描述显示相同文本，UI 不够精细。

---

## 三、行业差距对比

对标 PowerSchool、Infinite Campus、Skyward、Alma、智学网、班级小管家、超星学习通、ClassIn 等 K12 系统，本模块在 v1/v4 修复后仍有以下差距：

| 维度 | 优秀实践 | 本模块现状 | 影响 |
|------|---------|-----------|------|
| **掌握度时间线** | PowerSchool/智学网支持掌握度时间线，展示知识点掌握度随时间的变化趋势，支持按知识点钻取历史 | 仅展示当前快照，`knowledgePointMastery` 表无历史版本，`lastAssessedAt` 仅记录最近一次 | 教师无法判断学生是否在进步或退步；无法评估教学干预效果 |
| **个性化学习路径** | Alma/智学网基于弱项推荐具体学习资源（题目、视频、文档），形成学习路径 | 仅提供练习按钮跳转题目库，无资源推荐算法 | 推荐不够精准，学生需自行筛选练习内容 |
| **学生×知识点掌握度矩阵** | Infinite Campus/Alma 提供学生×知识点矩阵热力图，支持点击单元格查看明细 | 班级视图仅有知识点聚合热力图，无学生维度矩阵 | 教师无法快速定位"哪个学生在哪个知识点上薄弱" |
| **预测性分析（at-risk 预警）** | PowerSchool/Infinite Campus 基于历史数据预测 at-risk 学生，提前干预 | 无预测模型，仅基于当前掌握度 <60 判定需关注 | 无法主动预警，错失早期干预窗口 |
| **报告模板自定义** | PowerSchool 支持学校自定义报告模板（推荐话术、评分区间、logo） | 报告内容固定由 stats-service 生成，仅 i18n 可切换语言 | 无法按学校需求定制报告风格 |
| **多维度诊断** | Infinite Campus 结合成绩+出勤+行为做多维综合诊断 | 仅基于知识点掌握度单一维度 | 诊断维度单一，无法反映学生综合学习状态 |
| **PDF 导出** | 所有对标系统支持 PDF 导出（便于打印分发） | 仅支持 Excel 导出 | 无法满足打印分发场景 |
| **数据置信度可视化** | Alma 在报告上标注数据量与置信度，帮助教师判断结论可靠性 | 置信度计算过于简化（仅 null/非 null 两级） | 教师无法判断报告可信度 |
| **流式渲染** | 现代 K12 系统使用 React Suspense 流式渲染，首屏快速可见 | 全部 `await Promise.all` 阻塞渲染 | 首屏白屏时间长 |

---

## 四、改进优先级建议

### P0（紧急，影响核心规范或安全）

| 编号 | 问题 | 改进方向 |
|------|------|---------|
| v2-P0-1 | 3 个子路由 error.tsx 硬编码中文 | 接入 `useTranslations("diagnostic")`，新增对应 i18n 键 |

### P1（重要，影响安全/解耦/一致性）

| 编号 | 问题 | 改进方向 |
|------|------|---------|
| v2-P1-1 | grade_managed DataScope 未过滤 | 在 data-access-reports.ts 中处理 grade_managed scope，调用 `getUserIdsByGradeId` 过滤 |
| v2-P1-2 | i18n 弱项标签与代码逻辑不一致 | 更新 i18n 标签为"弱项（<80%）" |
| v2-P1-3 | 热力图 aria-label 硬编码中文标点 | 使用 i18n 模板键 `heatmapCellAriaLabel` |
| v2-P1-4 | 组件直接 import actions 无接口抽象 | 定义 `DiagnosticService` 接口，通过 React Context 注入；组件通过 `useDiagnosticService()` 获取 |
| v2-P1-5 | 置信度计算过于简化 | 基于 `totalQuestions` 实现多级置信度（<5 insufficient / 5-15 low / 16-30 medium / >30 high） |
| v2-P1-6 | 无 Error Boundary 包裹独立数据区块 | 在概览、雷达图、强弱项、报告、历史等区块外包裹 WidgetBoundary |

### P2（增强，提升完整性与企业级能力）

| 编号 | 问题 | 改进方向 |
|------|------|---------|
| v2-P2-1 | 死 i18n 键未清理 | 移除 9 个 share 相关 i18n 键 |
| v2-P2-2 | 教师页面客户端过滤 class_members | 在 data-access 层过滤，移除客户端 filter |
| v2-P2-3 | export.ts 错误消息硬编码 | 改用 DiagnosticReportError 结构化错误码 |
| v2-P2-4 | 异步数据无 Suspense 流式渲染 | 拆分数据获取为独立 async 组件，使用 Suspense 包裹（中长期） |
| v2-P2-5 | 雷达图名称截断无 tooltip | 为截断的名称添加 Tooltip 显示完整名称 |
| v2-P2-6 | 通知类型使用 "grade" | 新增 "diagnostic" 通知类型（需 notifications 模块配合） |
| v2-P2-7 | 无监控埋点接口 | 定义 `DiagnosticMonitor` 接口，在关键操作处调用 |
| v2-P2-8 | stats-service 无单测 | 为 14 个纯函数补充单元测试 |
| v2-P2-9 | parent noRecordsTitle/Description 重复 | 区分标题和描述 i18n 键 |

### P3（长期，需较大投入）

| 编号 | 问题 | 改进方向 |
|------|------|---------|
| v2-P3-1 | 掌握度时间线 | 新增 `knowledgePointMasteryHistory` 表记录历史版本，前端展示时间线图表 |
| v2-P3-2 | 个性化学习路径推荐 | 基于弱项推荐具体学习资源 |
| v2-P3-3 | 学生×知识点掌握度矩阵 | 新增矩阵视图组件 |
| v2-P3-4 | 预测性分析 | 基于 historical mastery 训练 at-risk 预测模型 |
| v2-P3-5 | 报告模板自定义 | 允许学校配置报告模板 |
| v2-P3-6 | PDF 导出 | 新增 PDF 导出能力 |

---

## 五、架构图同步说明

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

### 004_architecture_impact_map.md §2.22

1. **已知问题新增**：
   - 记录 v2-P0-1 三个子路由 error.tsx 硬编码中文及修复
   - 记录 v2-P1-1 grade_managed DataScope 未过滤及修复
   - 记录 v2-P1-4 组件解耦 Context 注入
   - 记录 v2-P1-5 置信度计算改进
   - 记录 v2-P1-6 数据区块 Error Boundary 包裹
2. **依赖关系更新**：标注 diagnostic 模块新增 `DiagnosticServiceContext` 依赖注入机制

### 005_architecture_data.json

1. `modules.diagnostic.exports` 补充 `DiagnosticService` 接口、`DiagnosticServiceProvider`、`useDiagnosticService` hook
2. `modules.diagnostic.knownIssues` 新增 v2 系列问题记录
3. `modules.diagnostic.dependencies` 标注 grade_managed scope 现已调用 `getUserIdsByGradeId` 过滤

---

## 六、实施计划

### 6.1 本次实施范围（P0 + P1 + 可快速完成的 P2）

本次实施 **P0 全部 + P1 全部 + 6 项 P2**，P3 长期项记录备查不实施。

### 6.2 重构方案设计（满足强制原则）

#### 完全解耦

定义 TypeScript 接口 `DiagnosticService` 抽象所有数据依赖：

```typescript
// src/modules/diagnostic/services/diagnostic-service.ts
export interface DiagnosticService {
  generateStudentReport(studentId: string, period: string): Promise<string>
  generateClassReport(classId: string, period: string): Promise<string>
  generateGradeReport(gradeId: string, period: string): Promise<string>
  publishReport(id: string): Promise<void>
  deleteReport(id: string): Promise<void>
  exportReport(reportId: string): Promise<{ buffer: string; filename: string }>
  getClassStudentsByKp(classId: string, kpId: string, threshold?: number): Promise<...>
}
```

通过 React Context 注入：

```typescript
// src/modules/diagnostic/services/diagnostic-service-context.tsx
const DiagnosticServiceContext = createContext<DiagnosticService | null>(null)
export function DiagnosticServiceProvider({ service, children }) { ... }
export function useDiagnosticService(): DiagnosticService { ... }
```

默认实现绑定现有 Server Actions；测试时可注入 mock 实现。

#### 组合优先

- `StudentDiagnosticView`、`ClassDiagnosticView`、`ReportList` 通过 `children` / slots 组合子区块。
- 逻辑复用提取为 `useDiagnosticActions` hook（内部调用 `useDiagnosticService()`）。

#### 国际化就绪

- 所有新增文本使用 `diagnostic.*` 命名空间翻译键。
- aria-label 模板使用 i18n 占位符：`heatmapCellAriaLabel: "{name}：{level}%，{label}，{mastered}/{total}"`。

#### 最大化复用

- `DiagnosticSection` 通用区块组件（含 WidgetBoundary + Suspense + 标题 + 内容 slot）。
- `useDiagnosticActions` hook 统一封装 actions 调用 + toast + router.refresh。

#### 错误与边界处理

- 每个数据区块外包裹 `WidgetBoundary`。
- 异步子区块使用 `Suspense` + 骨架屏（本次仅在页面级流式拆分，组件级 Suspense 列入 P2-4 长期）。

#### 可测试性

- `DiagnosticService` 接口清晰，可 mock。
- `confidence-utils` 置信度计算改为基于 `totalQuestions` 的多级判断。

#### 可扩展性

- 角色差异通过 `role-config.ts` 配置驱动（v4-P2-2 已实现）。
- 通知类型、监控埋点通过接口预留扩展点。

#### 企业级补充

- a11y：热力图 aria-label i18n 化；雷达图 tooltip。
- 性能：保持 RSC 获取初始数据。
- 安全：grade_managed scope 在 data-access 层过滤。
- 监控：定义 `DiagnosticMonitor` 接口预留埋点。

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

```json
{
  "diagnostic": {
    "error": {
      "classLoadFailed": "班级学情诊断加载失败",
      "classLoadFailedDesc": "抱歉，加载班级诊断数据时发生了意外错误。请稍后重试。",
      "studentLoadFailed": "学生学情诊断加载失败",
      "studentLoadFailedDesc": "抱歉，加载学生诊断数据时发生了意外错误。请稍后重试。",
      "parentLoadFailed": "子女学情诊断加载失败",
      "parentLoadFailedDesc": "抱歉，加载子女诊断数据时发生了意外错误。请稍后重试。",
      "retry": "重试"
    },
    "weaknesses": {
      "title": "弱项（<80%）"
    },
    "classDiagnostic": {
      "heatmapCellAriaLabel": "{name}：{level}%，{label}，{mastered}/{total}"
    },
    "reportList": {
      "radarPointTooltip": "{fullName}"
    }
  }
}
```
