feat(ai): V2 深度增强 — SSE 流式/全局助手/内容安全/多角色覆盖

对标 Khanmigo/Duolingo Max/Squirrel AI/Century Tech 实现:

- SSE 流式响应:createAiChatCompletionStream AsyncGenerator + /api/ai/chat/stream SSE 端点 + useAiChatStream hook(AbortController 停止生成 + localStorage 持久化)

- Markdown 渲染:AiMarkdownRenderer(react-markdown + remark-gfm + 代码块/表格/列表 + hover 复制按钮)

- 全局 AI 助手:AiAssistantWidget 浮动按钮 + Sheet 侧抽屉 + usePathname 路由推断上下文(7 类场景系统提示)+ dashboard layout 全局注入 AiClientProvider

- 内容安全:content-safety.ts 多层过滤(输入/输出安全过滤 + 每日限制 student 50/teacher 200/parent 30/admin 500 + 学生苏格拉底模式),COPPA/FERPA K12 合规

- 多角色 AI 覆盖:家长端 AiChildSummary(学情摘要)+ 管理员端 AiUsageDashboard(使用监控)+ 学生端 AiStudyPath(个性化学习路径)

- i18n 修复:8 处错误键引用 + zh-CN/en ai.json 全面扩展

- 架构文档 004/005 同步更新
This commit is contained in:
SpecialX
2026-06-23 01:34:37 +08:00
parent a60105455e
commit 4da9194a5e
27 changed files with 3522 additions and 172 deletions

View File

@@ -430,6 +430,8 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
- `cn()` / `formatDate()` / `formatFileSize()` — 通用工具
- `getInitials(name)` / `formatDateForFile(d?)` — 通用工具P1-c / P1-a 重构新增:从 parent/lib/utils.ts、grades/export-button.tsx 等多处重复实现抽取)
- `downloadBase64File(base64, filename, mimeType?)` / `downloadBlob(blob, filename)` — 客户端文件下载P1-c 重构新增:从 grades/export-button、users/user-import-dialog、audit/audit-log-export-button 三处重复实现抽取,位于 `lib/download.ts`
- `handleActionError(e)` / `safeActionCall(action, options?)` / `safeJsonParse(json, msg)` / `safeParseDate(v, field)` / `safeParseNumber(v, field)` / `escapeLikePattern(input)` — Server Action 错误处理与客户端调用包装2026-06-23 审计修复新增:位于 `lib/action-utils.ts`,统一所有 Server Action catch 块错误处理,避免内部错误消息暴露给客户端)
- `BusinessError` / `NotFoundError` / `ValidationError` — 错误类2026-06-23 审计修复新增:位于 `lib/action-utils.ts`已知业务错误基类message 可安全返回客户端)
**共享组件导出**P0-b / P1-a / P1-b / P1-c / P2-a / P2-b / P3-a / P3-b / P3-c / P3-d / 第二轮 P0-1/P0-2/P0-3/P1-1/P1-2/P1-3/P1-4 重构新增,按类别组织):
@@ -601,6 +603,7 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
- ✅ V3-8新增 `getHomeworkAssignmentsByExamId` + `getGradedSubmissionsByExamId`,供 exams 模块跨模块调用
- ✅ V3-9新增 `getStudentSubmissionResult` + `HomeworkSubmissionResult` + 路由 `/student/learning/assignments/[assignmentId]/result``homework-take-view.tsx` 提交后跳转结果页
- ✅ V3-12`homework-take-view.tsx` 移动端触摸目标尺寸优化
- ✅ 2026-06-23 审计修复data-access 层 4 个查询函数新增 `scope?: DataScope` 参数(`getHomeworkAssignments`/`getHomeworkAssignmentById`/`getHomeworkSubmissions`/`getHomeworkAssignmentReviewList`),教师仅查看自己创建的作业,学生/家长仅查看相关作业Server Action catch 块改用 `handleActionError` 统一错误处理
**文件清单**
| 文件 | 行数 | 职责 |
@@ -717,11 +720,12 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
**职责**:成绩分析(录入/查询/统计/导出/趋势对比分析)。
**导出函数**
- Actions`getGradeRecordsAction` / `createGradeRecordAction` / `updateGradeRecordAction` / `deleteGradeRecordAction` / `exportGradesAction` / `getGradeTrendAction` / `getClassComparisonAction` / `getSubjectComparisonAction` / `getGradeDistributionAction` / `getClassRankingAction` / `getRankingTrendAction` / `getGradeRecordByIdAction` / `getClassGradeStatsAction` / `getStudentGradeSummaryAction` / `batchCreateGradeRecordsAction` / `assertClassInScope`(✅ P3 新增导出:班级 scope 校验工具,供 actions-analytics 复用)
- Data-access`getGradeRecords` / `getStudentGradeSummary` / `getClassRanking` / `getClassStudentsForEntry` / `getClassGradeStats` / `getClassGradeStatsWithMeta` / `getGradeTrend` / `getClassComparison` / `getSubjectComparison` / `getGradeDistribution` / `getRankingTrend` / `PaginatedGradeRecords`(✅ P3 新增:分页结果接口 `{ records, total }`
- Actions`getGradeRecordsAction` / `createGradeRecordAction` / `updateGradeRecordAction` / `deleteGradeRecordAction` / `exportGradesAction` / `getGradeTrendAction` / `getClassComparisonAction` / `getSubjectComparisonAction` / `getGradeDistributionAction` / `getClassRankingAction` / `getRankingTrendAction` / `getGradeRecordByIdAction` / `getClassGradeStatsAction` / `getStudentGradeSummaryAction` / `batchCreateGradeRecordsAction` / `assertClassInScope`(✅ P3 新增导出:班级 scope 校验工具,供 actions-analytics 复用)/ `saveGradeDraftAction` / `getGradeDraftAction` / `deleteGradeDraftAction`(✅ v3-P2 新增:成绩录入草稿 Server Actions分别使用 GRADE_RECORD_MANAGE/GRADE_RECORD_READ/GRADE_RECORD_MANAGE 权限)
- Data-access`getGradeRecords` / `getStudentGradeSummary` / `getClassRanking` / `getClassStudentsForEntry` / `getClassGradeStats` / `getClassGradeStatsWithMeta` / `getGradeTrend` / `getClassComparison` / `getSubjectComparison` / `getGradeDistribution` / `getRankingTrend` / `PaginatedGradeRecords`(✅ P3 新增:分页结果接口 `{ records, total }`/ `saveGradeDraft` / `getGradeDraft` / `deleteGradeDraft`(✅ v3-P2 新增:成绩录入草稿 CRUDupsert + 24 小时过期)/ `getExamOptionsForGrades` / `getSchoolWideGradeSummary`(✅ v3-P2 新增:考试选项查询 + 全校各年级成绩汇总,管理员视图按年级聚合平均分/及格率/优秀率/学生数/班级数,加权平均计算全校汇总)
- Types✅ v3-P2 新增):`SchoolWideGradeSummaryItem`全校汇总按年级聚合项gradeId/gradeName/schoolName/classCount/studentCount/averageScore/passRate/excellentRate/recordCount/ `SchoolWideGradeSummary`全校汇总grades 数组 + totals 汇总对象)/ `GradeDraftData`(草稿数据接口:{ scores: Record<string, string>, timestamp: number },位于 data-access.ts
- Lib✅ P1-2 新增,✅ P3 更新签名):`toNumber` / `normalize` / `buildScopeClassFilter(scope, currentUserId?)`P3 修复:`class_members` scope 内置 studentId 过滤,需传入 currentUserId 参数)
- Stats-service✅ P1-1 新增):`computeGradeStats` / `computeAverageScore` / `buildGradeTrendPoints` / `computeTrendAverage` / `computeClassComparisonStats` / `computeSubjectComparisonStats` / `computeGradeDistribution` / `buildRankingTrendPoints`(从 3 个 data-access 文件抽取的纯函数,使数据层专注 DB I/O统计逻辑可独立测试
- Components✅ P1-5 新增):`WidgetBoundary`Error Boundary + Suspense + Skeleton 组合,含 a11y 属性)
- Components✅ P1-5 新增):`WidgetBoundary`Error Boundary + Suspense + Skeleton 组合,含 a11y 属性)/ `SchoolWideSummaryCard`(✅ v3-P2 新增管理员全校成绩汇总卡片4 个统计卡片 + 各年级对比表格)
**依赖关系**
- 依赖:`shared/*``@/auth``classes`(✅ P1-1 已修复:通过 classes data-access.getClassExists/getClassNameById/getClassNamesByIds/getActiveStudentIdsByClassId/getStudentActiveClassId/getClassesByGradeId`school`(✅ P1-1 已修复:通过 school data-access.getSubjectOptions/getGradeOptions`users`(✅ P1-1 已修复:通过 users data-access.getUserNamesByIds
@@ -754,20 +758,27 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
- ✅ P3 修复2026-06-22~~组件直接 try/catch 调用 Server Action~~ 改用 `safeActionCall` 包装器onError/onFinally 回调);`batch-grade-entry.tsx` localStorage 访问包裹 `typeof window !== "undefined"` 检查;区分"未录入"与"录入 0"
- ✅ P3 修复2026-06-22~~`grade-trend-card.tsx` 排序未处理 NaN 日期、除法未检查 fullScore > 0~~ 新增日期有效性检查与 `fullScore > 0` 守卫
- ✅ P3 修复2026-06-22~~页面文件缺少 scope 传递~~ teacher/grades/page.tsx 使用 DB 层分页analytics/page.tsx 添加 EmptyStateentry/page.tsx 与 stats/page.tsx 过滤 class_taught scope 班级student/grades/page.tsx 与 parent/grades/page.tsx 传递 `ctx.dataScope` 到 data-access
- ✅ v3-P2 改进2026-06-23新增 `grade_drafts`成绩录入草稿userId+classId+subjectId+type 唯一键content JSON 存储 scores24 小时过期schema.ts 第 1444-1469 行);新增 `saveGradeDraft`/`getGradeDraft`/`deleteGradeDraft` data-access 函数 + 对应 3 个 Server Actions支持成绩批量录入草稿服务端持久化
- ✅ v3-P2 改进2026-06-23新增 `getExamOptionsForGrades` data-access 函数,获取指定班级/科目下有成绩记录的考试列表;`AnalyticsFilters` 组件新增 `exams`/`currentExamId`/`currentSemester` props添加学期和考试筛选 ChipNavteacher/grades/analytics/page.tsx 新增 semester/examId 搜索参数解析
- ✅ v3-P2 改进2026-06-23`getGradeTrend`/`getGradeDistribution`/`getSubjectComparison`/`getClassComparison` 均新增 `semester``examId` 可选参数,支持按学期和考试筛选分析
- ✅ v3-P2 改进2026-06-23新增 `getSchoolWideGradeSummary` data-access 函数 + `SchoolWideSummaryCard` 组件 + `SchoolWideGradeSummary`/`SchoolWideGradeSummaryItem` 类型,管理员全校成绩汇总视图(按年级聚合平均分/及格率/优秀率/学生数/班级数加权平均计算全校汇总admin/school/grades/insights/page.tsx 顶部新增 SchoolWideSummaryCard
- ✅ v3-P2 改进2026-06-23parent/grades/page.tsx 为每个子女并行查询 `getClassAverageTrend`,渲染 GradeTrendCard
**文件清单**
| 文件 | 行数 | 职责 |
|------|------|------|
| `actions.ts` | 396 | 15 个 Server Action含 Zod 校验,含 v2-P1-5 安全修复assertClassInScope + 行级 scope 校验P3 修复handleActionError + safeJsonParse + scope 传递 + DB 层分页) |
| `actions.ts` | 396+ | 18 个 Server Action含 Zod 校验,含 v2-P1-5 安全修复assertClassInScope + 行级 scope 校验P3 修复handleActionError + safeJsonParse + scope 传递 + DB 层分页v3-P2 新增saveGradeDraftAction/getGradeDraftAction/deleteGradeDraftAction |
| `actions-analytics.ts` | 170 | 5 个分析 Action含 Zod 校验P3 修复handleActionError + assertClassInScope 校验) |
| `data-access.ts` | 428 | 成绩 CRUD + 统计(含 v2-P2-9 修复recorderName 批量查询P3 修复PaginatedGradeRecords 接口 + DB 层分页 + 事务 + 存在性检查 + scope 过滤 + 并列排名) |
| `data-access-analytics.ts` | 200 | 趋势/对比分析P3 修复getClassComparison 应用 buildScopeClassFilter |
| `data-access.ts` | 428+ | 成绩 CRUD + 统计 + 草稿(含 v2-P2-9 修复recorderName 批量查询P3 修复PaginatedGradeRecords 接口 + DB 层分页 + 事务 + 存在性检查 + scope 过滤 + 并列排名v3-P2 新增saveGradeDraft/getGradeDraft/deleteGradeDraft + GradeDraftData 接口 |
| `data-access-analytics.ts` | 200+ | 趋势/对比分析P3 修复getClassComparison 应用 buildScopeClassFilterv3-P2 新增getExamOptionsForGrades/getSchoolWideGradeSummarygetGradeTrend/getClassComparison/getSubjectComparison/getGradeDistribution 新增 semester/examId 可选参数 |
| `data-access-ranking.ts` | 83 | 排名查询P3 修复getRankingTrend 接受 scope 参数 + class_taught 校验) |
| `stats-service.ts` | 279 | 统计计算纯函数P1-1 新增8 个纯函数 + 2 个常量 + 2 个接口) |
| `export.ts` | 189 | Excel 导出v2-P1-5 修复:传递 currentUserId 到 data-accessP3 修复:适配 PaginatedGradeRecords 结构 + 传递 scope |
| `schema.ts` | 113 | Zod 校验(含 12 个查询 schemaP3 修复score .max(1000) + records .max(500) + 补全查询字段) |
| `schema.ts` | 113+ | Zod 校验(含 12 个查询 schemaP3 修复score .max(1000) + records .max(500) + 补全查询字段v3-P2 新增grade_drafts 表定义第 1444-1469 行 |
| `lib/grade-utils.ts` | 66 | 公共工具函数toNumber/normalize/buildScopeClassFilterv2-P2-2 修复:改用 classes data-access 子查询P3 修复buildScopeClassFilter 新增 currentUserId 参数) |
| `types.ts` | 168+ | 类型定义v3-P2 新增SchoolWideGradeSummaryItem/SchoolWideGradeSummary |
| `components/widget-boundary.tsx` | 136 | Widget 边界组件P1-5 新增v2-P1-1 已在 3 个页面应用) |
| `components/school-wide-summary-card.tsx` | - | v3-P2 新增管理员全校成绩汇总卡片4 个统计卡片 + 各年级对比表格) |
| `components/grade-trend-card.tsx` | 69 | 趋势卡片v2-P2-9 修复a11yv2-P1-4i18nP3 修复NaN 日期检查 + fullScore > 0 守卫) |
| `components/grade-record-list.tsx` | 125 | 成绩记录列表v2-P1-4i18nP3 修复safeActionCall 包装删除操作) |
| `components/grade-distribution-chart.tsx` | 100 | 分数分布图v2-P1-4i18n |
@@ -775,16 +786,15 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
| `components/class-comparison-chart.tsx` | 58 | 班级对比图v2-P1-4i18n |
| `components/grade-trend-chart.tsx` | 59 | 趋势图v2-P1-4i18n |
| `components/grade-record-form.tsx` | 177 | 录入表单v2-P2-7 修复Label htmlForv2-P1-4i18nP3 修复safeActionCall 包装提交) |
| `components/batch-grade-entry.tsx` | 435 | 批量录入v2-P2-7 修复Label htmlForv2-P1-4i18nP3 修复safeActionCall + localStorage 安全检查 + 区分未录入与录入 0 |
| `components/batch-grade-entry.tsx` | 435+ | 批量录入v2-P2-7 修复Label htmlForv2-P1-4i18nP3 修复safeActionCall + localStorage 安全检查 + 区分未录入与录入 0v3-P2 新增:接入服务端草稿 saveGradeDraftAction/getGradeDraftAction/deleteGradeDraftAction |
| `components/grade-filters.tsx` | 76 | 过滤器v2-P1-4i18n |
| `components/student-grade-summary.tsx` | 107 | 学生成绩摘要v2-P1-4i18n |
| `components/export-button.tsx` | 79 | 导出按钮v2-P1-4i18nP3 修复safeActionCall 包装导出操作) |
| `components/analytics-filters.tsx` | 86 | 分析过滤器v2-P1-4i18n |
| `components/analytics-filters.tsx` | 86+ | 分析过滤器v2-P1-4i18nv3-P2 新增exams/currentExamId/currentSemester props添加学期和考试筛选 ChipNav |
| `components/stats-class-selector.tsx` | 40 | 统计班级选择器v2-P1-4i18n |
| `components/grade-stats-card.tsx` | 74 | 统计卡片v2-P1-4i18n |
| `components/class-grade-report.tsx` | 90 | 班级成绩报告v2-P1-4i18n |
| `components/grade-query-filters.tsx` | 96 | 查询过滤器v2-P2-7 修复Label htmlForv2-P1-4i18n |
| `types.ts` | 168 | 类型定义 |
---
@@ -1362,6 +1372,7 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
- ⚠️ v4 保留:`/parent/leave` 为占位页,待后端实现请假审批流后接入
- ⚠️ v4 保留:`ParentExportButton` 为占位,待后端实现成绩导出 Server Action 后接入
- ⚠️ v4 保留:详情页 Attendance/Diagnostic Tab 为占位提示,待对应功能实现后填充
- ✅ v3-P2 改进2026-06-23parent/grades/page.tsx 为每个子女并行查询 `getClassAverageTrend`,渲染 GradeTrendCardparent/diagnostic/page.tsx 传入 `practiceHrefBase={null}` 隐藏练习按钮
- ✅ 职责单一,正确复用其他模块 data-access
**文件清单**
@@ -1390,8 +1401,8 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
| 路由 | 文件 | 说明 |
|------|------|------|
| `/parent/dashboard` | `dashboard/page.tsx` + `loading.tsx` | 家长仪表盘 |
| `/parent/grades` | `grades/page.tsx` + `loading.tsx` | 多子女成绩聚合 |
| `/parent/diagnostic` | `diagnostic/page.tsx` + `loading.tsx` + `error.tsx` | P2-5 新增:多子女学情诊断聚合 |
| `/parent/grades` | `grades/page.tsx` + `loading.tsx` | 多子女成绩聚合v3-P2并行查询 getClassAverageTrend + GradeTrendCard |
| `/parent/diagnostic` | `diagnostic/page.tsx` + `loading.tsx` + `error.tsx` | P2-5 新增:多子女学情诊断聚合v3-P2practiceHrefBase={null} 隐藏练习按钮) |
| `/parent/attendance` | `attendance/page.tsx` + `loading.tsx` | 多子女考勤聚合v4 新增预警横幅) |
| `/parent/leave` | `leave/page.tsx` + `loading.tsx` | v4 新增:请假申请(占位) |
| `/parent/children/[studentId]` | `children/[studentId]/page.tsx` + `loading.tsx` | 子女详情页v4 重构Tab 布局 + 多子女切换) |
@@ -1513,6 +1524,7 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
- ✅ v2-P1-4 已修复:~~4 个组件 i18n 完全未接入~~ 全部接入 `useTranslations("diagnostic")`
- ✅ v2-P2-7 已修复:~~`report-list.tsx` 过滤器 Label 缺少 `htmlFor`~~ 添加 `htmlFor``id`
- ✅ 与 grades 模块无职责重叠
- ✅ v3-P2 改进2026-06-23`StudentDiagnosticView` 新增 `practiceHrefBase` propstring | nullnull 时隐藏练习按钮teacher/diagnostic/student/[studentId] 传入 `practiceHrefBase="/teacher/questions"`parent/diagnostic 传入 `practiceHrefBase={null}` 隐藏练习按钮
**文件清单**
| 文件 | 行数 | 职责 |
@@ -1524,7 +1536,7 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
| `schema.ts` | 23 | Zod 校验4 个 schemav2-P2-3 删除 2 个死代码 schema |
| `types.ts` | 87 | 类型定义 |
| `components/class-diagnostic-view.tsx` | 266 | 班级诊断视图v2-P1-6 热力图 a11yv2-P1-4 i18n |
| `components/student-diagnostic-view.tsx` | 225 | 学生诊断视图v2-P1-4 i18n |
| `components/student-diagnostic-view.tsx` | 225+ | 学生诊断视图v2-P1-4 i18nv3-P2 新增practiceHrefBase propnull 时隐藏练习按钮 |
| `components/mastery-radar-chart.tsx` | 72 | 雷达图v2-P1-4 i18n |
| `components/report-list.tsx` | 265 | 报告列表v2-P2-7 Label htmlForv2-P1-4 i18n |
@@ -1758,6 +1770,12 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
> - **P2-1 a11y 修复**5 个组件question-bank-picker/publish-homework-dialog/knowledge-point-picker/exercise-block/text-study-block添加 `role="dialog"`/`aria-modal`/`aria-label`inline-question-editor 添加 `role="dialog"`/`aria-modal`/`aria-label`
> - **P2-4 监控埋点预留**`providers/lesson-plan-provider.tsx` 定义 `LessonPlanTracker` 接口 + `noopTracker` 默认空实现,生产环境可替换为真实埋点
> 架构变更2026-06-23本次审计修复
> - **Zod schema 补全**`schema.ts` 新增 `publishLessonPlanHomeworkSchema`planId/blockId 必填classIds 至少 1 个availableAt/dueAt 日期格式校验),导出类型 `PublishLessonPlanHomeworkInput`3 个 action 文件actions/actions-publish/actions-ai补全 Zod 校验
> - **data-access-versions.ts 事务修复**`revertToVersion` 包裹 `db.transaction` 确保原子性(回滚版本时同步更新 lessonPlans.content 与版本记录)
> - **统一错误处理**:所有 Server Action catch 块改用 `handleActionError``JSON.parse` 改用 `safeJsonParse`
> - **block-renderer 拖拽 BUG 修复**:修复拖拽时节点位置计算错误
**文件清单**
| 文件 | 职责 |
|------|------|
@@ -1896,14 +1914,15 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
---
## 2.29 aiAI 模块)— ✅ 新增
## 2.29 aiAI 模块)— ✅ 新增 / V2 增强
**职责**:统一 AI 能力封装,为备课、错题集、试卷、改题等业务模块提供 AI 服务。
**职责**:统一 AI 能力封装,为备课、错题集、试卷、改题等业务模块提供 AI 服务。V2 增加流式响应、Markdown 渲染、全局助手、内容安全过滤、家长学情摘要、管理员使用统计、学生学习路径推荐。
**架构定位**
- 位于 `modules/` 层,通过 `shared/lib/ai` 调用底层 AI SDK
- 通过 `AiClientProvider`React Context向客户端组件注入 Server Action 引用
- 业务模块不直接 import `ai/actions`,仅通过 Context 消费
- V2在 dashboard layout 全局注入 `AiClientProvider`,所有页面均可使用 AI 助手
**核心导出**
@@ -1915,19 +1934,34 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
| **Server Actions** | `generateLessonContentAction` | `modules/ai/actions.ts` | 备课内容生成权限AI_CHAT + LESSON_PLAN_READ |
| **Server Actions** | `generateQuestionVariantAction` | `modules/ai/actions.ts` | 题目变体生成权限AI_CHAT + EXAM_AI_GENERATE |
| **Server Actions** | `analyzeWeaknessAction` | `modules/ai/actions.ts` | 薄弱点分析权限AI_CHAT + ERROR_BOOK_READ |
| **Service** | `AiService` | `modules/ai/types.ts` | 服务端 AI 服务接口chat/suggestSimilarQuestions/suggestGrading/generateLessonContent/generateQuestionVariant/analyzeWeakness |
| **Server Actions** | `generateChildSummaryAction` | `modules/ai/actions.ts` | 家长学情摘要权限AI_CHAT— V2 新增 |
| **Server Actions** | `recommendStudyPathAction` | `modules/ai/actions.ts` | 学习路径推荐权限AI_CHAT— V2 新增 |
| **Server Actions** | `getAiUsageStatsAction` | `modules/ai/actions.ts` | AI 使用统计权限AI_CONFIGURE— V2 新增 |
| **SSE Route** | `POST /api/ai/chat/stream` | `app/api/ai/chat/stream/route.ts` | 流式 AI 对话SSE— V2 新增 |
| **Service** | `AiService` | `modules/ai/types.ts` | 服务端 AI 服务接口(含 8 个方法) |
| **Service** | `AiClientService` | `modules/ai/types.ts` | 客户端 AI 服务接口Server Action 引用集合) |
| **Provider** | `AiClientProvider` | `modules/ai/context/ai-client-provider.tsx` | React Context Provider注入 AiClientService |
| **Hook** | `useAiClient` | `modules/ai/context/ai-client-provider.tsx` | 消费 AiClientService必须在 Provider 内使用) |
| **Hook** | `useAiClientOptional` | `modules/ai/context/ai-client-provider.tsx` | 可选消费 AiClientService未注入返回 null |
| **Hook** | `useAiChatStream` | `modules/ai/hooks/use-ai-chat-stream.ts` | 流式 AI 对话 HookSSE + AbortController— V2 新增 |
| **Hook** | `useAiChat` | `modules/ai/hooks/use-ai-chat.ts` | 非流式 AI 对话 Hook |
| **Hook** | `useAiSuggestion` | `modules/ai/hooks/use-ai-suggestion.ts` | AI 建议 Hook |
| **Component** | `AiAssistantWidget` | `modules/ai/components/ai-assistant-widget.tsx` | 全局 AI 助手悬浮按钮(上下文感知)— V2 新增 |
| **Component** | `AiChatPanel` | `modules/ai/components/ai-chat-panel.tsx` | AI 对话面板(流式 + Markdown + 复制 + 停止 + 建议)— V2 增强 |
| **Component** | `AiMarkdownRenderer` | `modules/ai/components/ai-markdown-renderer.tsx` | Markdown 渲染器GFM + 复制按钮)— V2 新增 |
| **Component** | `AiGradingAssist` | `modules/ai/components/ai-grading-assist.tsx` | AI 批改辅助(主观题预评分 + 反馈建议) |
| **Component** | `AiErrorBookAnalysis` | `modules/ai/components/ai-error-book-analysis.tsx` | 错题本 AI 分析(相似题 + 薄弱点) |
| **Component** | `AiLessonContentGenerator` | `modules/ai/components/ai-lesson-content-generator.tsx` | 备课内容生成器(活动/评估/讨论题/素材) |
| **Component** | `AiQuestionVariantGenerator` | `modules/ai/components/ai-question-variant-generator.tsx` | 题目变体生成器(同知识点/不同难度/不同题型) |
| **Component** | `AiChatPanel` | `modules/ai/components/ai-chat-panel.tsx` | AI 对话面板 |
| **Component** | `AiChildSummary` | `modules/ai/components/ai-child-summary.tsx` | 家长 AI 学情摘要 — V2 新增 |
| **Component** | `AiUsageDashboard` | `modules/ai/components/ai-usage-dashboard.tsx` | 管理员 AI 使用统计仪表盘 — V2 新增 |
| **Component** | `AiStudyPath` | `modules/ai/components/ai-study-path.tsx` | 学生学习路径推荐 — V2 新增 |
| **Component** | `AiErrorBoundary` | `modules/ai/components/ai-error-boundary.tsx` | AI 功能错误边界 |
| **Component** | `AiSuggestionSkeleton` | `modules/ai/components/ai-skeleton.tsx` | AI 建议加载骨架屏 |
| **Component** | `AiProviderSelector` | `modules/ai/components/ai-provider-selector.tsx` | AI 服务商选择器 |
| **Safety** | `filterUserInput` | `modules/ai/services/content-safety.ts` | 输入安全过滤 — V2 新增 |
| **Safety** | `filterAiOutput` | `modules/ai/services/content-safety.ts` | 输出安全过滤 — V2 新增 |
| **Safety** | `checkDailyLimit` | `modules/ai/services/content-safety.ts` | 每日交互限制 — V2 新增 |
**集成点**
@@ -1937,41 +1971,62 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
| error-book | `AiErrorBookAnalysis` | `student/error-book` | 相似题推荐 + 薄弱点分析 |
| lesson-preparation | `AiLessonContentGenerator` | `teacher/lesson-plans/[planId]/edit` | 备课内容生成 |
| exams | `AiQuestionVariantGenerator` | `teacher/exams/[id]/build` | 题目变体生成 |
| **全局** | `AiAssistantWidget` | `app/(dashboard)/layout.tsx` | 全局 AI 助手悬浮按钮(所有页面)— V2 新增 |
| **parent** | `AiChildSummary` | `parent/*` | 家长学情 AI 摘要 — V2 新增 |
| **admin** | `AiUsageDashboard` | `admin/*` | AI 使用统计仪表盘 — V2 新增 |
| **student** | `AiStudyPath` | `student/*` | 学习路径推荐 — V2 新增 |
**依赖关系**
- `modules/ai``shared/lib/ai`AI SDK 封装)
- `modules/ai``shared/lib/ai`AI SDK 封装,含流式 `createAiChatCompletionStream`
- `modules/ai``shared/lib/auth-guard`(权限校验)
- `modules/ai``shared/lib/track-event`(使用量埋点)
- `modules/ai``shared/types/permissions`(权限常量)
- `modules/ai``shared/types/action-state`(返回值类型)
- `app/(dashboard)/layout``modules/ai`(全局 Provider + Widget— V2 新增
- 业务模块 → `modules/ai/context/ai-client-provider`(通过 Context 注入)
- 业务模块 → `modules/ai/components/*`(组合 AI 组件)
**安全机制V2 新增)**
- 输入过滤:`filterUserInput` 拦截暴力/自残/色情/PII
- 输出过滤:`filterAiOutput` 扫描 AI 回复
- 每日限制:学生 50 次/天,教师 200 次/天,家长 30 次/天
- 学生 Socratic 模式system prompt 强制不直接给答案
- SSE 端点权限校验:`requirePermission(AI_CHAT)`
**文件清单**
| 文件 | 行数 | 职责 |
|------|------|------|
| `modules/ai/types.ts` | 194 | 类型定义AiService/AiClientService/业务场景类型 |
| `modules/ai/schema.ts` | - | Zod 验证 schema |
| `modules/ai/actions.ts` | 244 | 6 个 Server Actions含权限校验 |
| `modules/ai/services/ai-service.ts` | - | DefaultAiService 实现 |
| `modules/ai/services/prompt-templates.ts` | - | 6 个系统提示词模板 |
| `modules/ai/services/usage-tracker.ts` | - | AI 使用量埋点 |
| `modules/ai/context/ai-client-provider.tsx` | 59 | React Context Provider + Hooks |
| `modules/ai/types.ts` | ~270 | 类型定义(8 个业务场景类型 + AiService/AiClientService |
| `modules/ai/schema.ts` | ~205 | Zod 验证 schema8 个输入 + 8 个输出) |
| `modules/ai/actions.ts` | ~340 | 9 个 Server Actions含权限校验 |
| `modules/ai/services/ai-service.ts` | ~400 | DefaultAiService 实现8 个方法) |
| `modules/ai/services/prompt-templates.ts` | ~210 | 8 个系统提示词模板 |
| `modules/ai/services/usage-tracker.ts` | ~83 | AI 使用量埋点 |
| `modules/ai/services/content-safety.ts` | ~130 | 内容安全过滤(输入/输出/每日限制)— V2 新增 |
| `modules/ai/context/ai-client-provider.tsx` | ~62 | React Context Provider + Hooks |
| `modules/ai/components/ai-assistant-widget.tsx` | ~170 | 全局 AI 助手悬浮按钮 — V2 新增 |
| `modules/ai/components/ai-chat-panel.tsx` | ~305 | AI 对话面板(流式 + Markdown— V2 增强 |
| `modules/ai/components/ai-markdown-renderer.tsx` | ~100 | Markdown 渲染器 — V2 新增 |
| `modules/ai/components/ai-child-summary.tsx` | ~170 | 家长学情摘要 — V2 新增 |
| `modules/ai/components/ai-usage-dashboard.tsx` | ~180 | 管理员使用统计 — V2 新增 |
| `modules/ai/components/ai-study-path.tsx` | ~170 | 学生学习路径 — V2 新增 |
| `modules/ai/components/ai-grading-assist.tsx` | 173 | AI 批改辅助组件 |
| `modules/ai/components/ai-error-book-analysis.tsx` | 246 | 错题本 AI 分析组件 |
| `modules/ai/components/ai-lesson-content-generator.tsx` | - | 备课内容生成器 |
| `modules/ai/components/ai-question-variant-generator.tsx` | - | 题目变体生成器 |
| `modules/ai/components/ai-chat-panel.tsx` | - | AI 对话面板 |
| `modules/ai/components/ai-error-boundary.tsx` | - | AI 错误边界 |
| `modules/ai/components/ai-skeleton.tsx` | - | AI 骨架屏 |
| `modules/ai/components/ai-provider-selector.tsx` | - | 服务商选择器 |
| `modules/ai/hooks/use-ai-chat.ts` | - | AI 对话 Hook |
| `modules/ai/hooks/use-ai-suggestion.ts` | - | AI 建议 Hook |
| `modules/ai/components/ai-lesson-content-generator.tsx` | ~187 | 备课内容生成器 |
| `modules/ai/components/ai-question-variant-generator.tsx` | ~208 | 题目变体生成器 |
| `modules/ai/components/ai-error-boundary.tsx` | ~88 | AI 错误边界 |
| `modules/ai/components/ai-skeleton.tsx` | ~47 | AI 骨架屏 |
| `modules/ai/components/ai-provider-selector.tsx` | ~129 | 服务商选择器 |
| `modules/ai/hooks/use-ai-chat-stream.ts` | ~170 | 流式 AI 对话 Hook — V2 新增 |
| `modules/ai/hooks/use-ai-chat.ts` | ~57 | 非流式 AI 对话 Hook |
| `modules/ai/hooks/use-ai-suggestion.ts` | ~72 | AI 建议 Hook |
| `app/api/ai/chat/stream/route.ts` | ~160 | SSE 流式端点 — V2 新增 |
**i18n**
- 翻译文件:`shared/i18n/messages/{locale}/ai.json`
- 命名空间:`ai`
- 包含chat/provider/suggestion/grading/errorBook/lessonPrep/exam/error/capability
- V2 新增键:`chat.streaming/stopGeneration/copy/clearConfirm/suggestedPrompts``grading.description/batch*``lessonPrep.description/additionalContext/insertContent``exam.variantType.*/targetDifficulty/addVariant``parent.*``admin.*``studyPath.*``widget.*``safety.*`
---
@@ -2240,6 +2295,82 @@ shared/lib/{audit-logger, change-logger, auth-guard} → @/auth → shared/lib/*
---
## 3.6 教师端全模块审计修复2026-06-23
> 本次审计修复覆盖教师端 13 个模块组、约 80+ 个文件,聚焦越权访问漏洞、错误处理一致性、类型安全与 React 性能。以下仅记录涉及架构图同步的变更(函数签名/导出/新增文件),其余纯实现修复(如 try/catch 补齐、useMemo/useCallback、key prop、as 断言清理)不在此列出。
### 3.6.1 新增共享文件 `src/shared/lib/action-utils.ts`
统一 Server Action 错误处理与客户端 Action 调用模式,避免内部错误消息暴露给客户端。
| 导出 | 类型 | 签名 | 用途 |
|------|------|------|------|
| `handleActionError` | function | `(e: unknown) => ActionState<never>` | 统一 Server Action catch 块错误处理PermissionDeniedError/BusinessError 返回其 message其他 Error 返回通用消息并 console.error 记录 |
| `safeActionCall` | function | `<T>(action: () => Promise<ActionState<T>>, options?: { onError?, onFinally? }) => Promise<ActionState<T> \| null>` | 客户端调用 Server Action 的 try/catch/finally 包装器,防止 UI 永久卡 loading |
| `safeJsonParse` | function | `<T>(json: string, errorMessage: string) => T` | 安全 JSON.parse失败抛 ValidationError替代裸 JSON.parse |
| `safeParseDate` | function | `(value: string, fieldName: string) => Date` | 校验日期字符串有效性,无效抛 ValidationError |
| `safeParseNumber` | function | `(value: string, fieldName: string) => number` | 校验数字字符串,无效抛 ValidationError |
| `escapeLikePattern` | function | `(input: string) => string` | 转义 SQL LIKE 通配符(% _ \\),防止用户输入干扰模糊查询 |
| `BusinessError` | class | `extends Error` | 已知业务错误基类message 可安全返回客户端(含可选 code |
| `NotFoundError` | class | `extends BusinessError` | 资源不存在错误(自动生成 `${resource} 不存在` 消息code: not_found |
| `ValidationError` | class | `extends BusinessError` | 输入校验错误code: validation_error |
### 3.6.2 grades 模块签名变更
**data-access 层新增 scope 参数**P3 修复:所有查询函数应用 `buildScopeClassFilter` 进行行级 scope 过滤):
| 函数 | 文件 | 旧签名 | 新签名 |
|------|------|--------|--------|
| `getGradeRecords` | `data-access.ts` | `(params: GradeQueryParams) => Promise<GradeRecordListItem[]>` | `(params: GradeQueryParams & { scope: DataScope; currentUserId?: string; limit?: number; offset?: number }) => Promise<PaginatedGradeRecords>` |
| `getClassGradeStats` | `data-access.ts` | `(classId, subjectId?, examId?) => Promise<GradeStats>` | `(classId, subjectId?, examId?, scope?: DataScope, currentUserId?: string) => Promise<GradeStats \| null>` |
| `getStudentGradeSummary` | `data-access.ts` | `(studentId) => Promise<StudentGradeSummary>` | `(studentId, scope?: DataScope) => Promise<StudentGradeSummary \| null>`class_taught scope 校验学生归属) |
| `getClassRanking` | `data-access.ts` | `(classId, subjectId?, examId?) => Promise<ClassRankingItem[]>` | `(classId, subjectId?, examId?, scope?: DataScope, currentUserId?: string) => Promise<ClassRankingItem[]>`(含并列排名处理) |
| `getClassStudentsForEntry` | `data-access.ts` | `(classId) => Promise<{id,name}[]>` | `(classId, scope?: DataScope) => Promise<{id,name,email}[]>`class_taught scope 校验 classId |
| `getRankingTrend` | `data-access-ranking.ts` | `(studentId, subjectId?, semester?) => Promise<RankingTrendResult \| null>` | `(studentId, subjectId?, semester?, scope?: DataScope) => Promise<RankingTrendResult \| null>`class_taught scope 校验) |
**lib 层签名变更**
- `buildScopeClassFilter(scope: DataScope, currentUserId?: string): SQL | null`(新增 `currentUserId` 参数,`class_members` scope 内置 `eq(gradeRecords.studentId, currentUserId)` 过滤)
**新增导出**
- `assertClassInScope(scope: DataScope, classId: string): string | null``actions.ts`,校验 classId 是否在 scope 允许范围内,供 actions.ts 与 actions-analytics.ts 复用)
- `PaginatedGradeRecords` 接口(`data-access.ts``{ records: GradeRecordListItem[]; total: number }`,配合 DB 层分页)
### 3.6.3 homework 模块签名变更
**data-access 层新增 scope 参数**P3 修复:教师仅查看自己创建的作业,学生/家长仅查看相关作业):
| 函数 | 文件 | 新签名 |
|------|------|--------|
| `getHomeworkAssignments` | `data-access.ts` | `(params?: { creatorId?, ids?, classId?, scope?: DataScope }) => Promise<HomeworkAssignmentListItem[]>` |
| `getHomeworkAssignmentById` | `data-access.ts` | `(id: string, scope?: DataScope) => Promise<HomeworkAssignmentListItem \| null>` |
| `getHomeworkSubmissions` | `data-access.ts` | `(params?: { assignmentId?, classId?, creatorId?, scope?: DataScope }) => Promise<HomeworkSubmissionListItem[]>` |
| `getHomeworkAssignmentReviewList` | `data-access.ts` | `(params: { creatorId: string; scope?: DataScope }) => Promise<HomeworkReviewListItem[]>` |
### 3.6.4 lesson-preparation 模块变更
**新增 Zod schema**`schema.ts`
- `publishLessonPlanHomeworkSchema`发布作业输入校验planId/blockId 必填classIds 至少 1 个availableAt/dueAt 日期格式校验)
- 导出类型 `PublishLessonPlanHomeworkInput = z.infer<typeof publishLessonPlanHomeworkSchema>`
**data-access-versions.ts 事务变更**
- `revertToVersion` 包裹 `db.transaction` 确保原子性(回滚版本时同步更新 lessonPlans.content 与版本记录)
- `createLessonPlanVersion` 已使用 `db.transaction`(版本号自增 + 插入版本记录原子化)
### 3.6.5 其他修复(不涉及签名变更,仅记录范围)
- 越权访问修复15+ 处页面添加 `requirePermission`data-access 层添加 scope 过滤
- 功能性 BUG 修复question-actions 删除字段名、question-columns 时间显示、textbook-card 删除按钮、block-renderer 拖拽
- 统一错误处理:所有 Server Action catch 块改用 `handleActionError`
- 客户端 Action 调用添加 try/catch/finally20+ 处,使用 `safeActionCall`
- `JSON.parse` 改用 `safeJsonParse`8+ 处)
- Date 解析添加校验15+ 处,使用 `safeParseDate`
- 数据库事务添加:`batchCreateGradeRecords``deleteChapter``revertToVersion`
- 性能优化:`Promise.all` 并行化、DB 层分页grades `getGradeRecords`
- TypeScript `as` 断言清理30+ 处,改用类型守卫)
- React 性能修复:`useMemo``useCallback``key` prop、渲染期间副作用移除
---
# 附录 A模块间依赖矩阵
> 行表示使用方,列表示被使用方。`✅` 合理依赖,`❌` 违规直查,`⟳` 循环依赖。