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

396 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 学情诊断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)v12026-06-223 P0 + 5 P1 + 5 P2 全部完成)
> - [grades-diagnostic-audit-report-v4.md](./grades-diagnostic-audit-report-v4.md)v42026-06-2312 项 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-accessclasses / 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_membersP2
- **位置**[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 雷达图知识点名称截断无 tooltipP2
- **位置**[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}"
}
}
}
```