396 lines
27 KiB
Markdown
396 lines
27 KiB
Markdown
# 学情诊断(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}"
|
||
}
|
||
}
|
||
}
|
||
```
|