From 97e59b95a17d906cf6534b403da7e0755a27cb25 Mon Sep 17 00:00:00 2001 From: SpecialX <47072643+wangxiner55@users.noreply.github.com> Date: Mon, 22 Jun 2026 18:45:35 +0800 Subject: [PATCH] =?UTF-8?q?refactor(lesson-preparation):=20V2=20=E5=AE=A1?= =?UTF-8?q?=E8=AE=A1=E6=B7=B1=E5=BA=A6=E4=BF=AE=E5=A4=8D=20=E2=80=94=20Ser?= =?UTF-8?q?ver=20Actions=20i18n=20+=20=E9=94=99=E8=AF=AF=E7=A0=81=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=20+=20=E7=B1=BB=E5=9E=8B=E6=96=AD=E8=A8=80=E6=B8=85?= =?UTF-8?q?=E9=9B=B6=20+=20a11y=20=E6=B7=B1=E5=BA=A6=E4=BF=AE=E5=A4=8D=20+?= =?UTF-8?q?=20Tracker=20=E5=9F=8B=E7=82=B9=E6=8E=A5=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit V2-1: 12 个 Server Action 通过 getTranslations 翻译错误消息;Service/DataAccess 层抛出错误码异常(PublishServiceError/LessonPlanDataError),Actions 层通过 PUBLISH_ERROR_KEY_MAP 翻译为 i18n 消息 V2-2: SYSTEM_TEMPLATES name/title 改为 i18n 键,createLessonPlan 接受 translateTitle 函数在服务端翻译后存储到 DB V2-3: 8 处 as unknown as 断言替换为显式类型映射函数(mapRowToLessonPlan/mapRowToListItem/mapRowToTemplate/mapRowToVersion)+ 类型守卫(isLessonPlanStatus/isTemplateType/isTemplateScope) V2-4: MiniMap nodeColor 复用 lib/node-summary.ts 的 getNodeColor V2-5: a11y 深度修复 — lesson-plan-filters/exercise-block/inline-question-editor 的 select 添加 label htmlFor 关联;exercise-block 题目列表改为 ul/li;node-editor 画布添加 role=application + 键盘导航配置 V2-6: Tracker 埋点接入 — 新增 useLessonPlanTrackerSafe hook,在 create/save/publish/revert/duplicate/archive 6 处调用 tracker.track 同步更新架构图 004 和 005 文档 --- docs/architecture/005_architecture_data.json | 6 +- .../lesson-preparation-audit-report-v2.md | 137 ++++++++++++++++ src/modules/lesson-preparation/actions-ai.ts | 4 +- src/modules/lesson-preparation/actions-kp.ts | 4 +- .../lesson-preparation/actions-publish.ts | 22 ++- src/modules/lesson-preparation/actions.ts | 63 ++++++-- .../components/blocks/exercise-block.tsx | 14 +- .../components/inline-question-editor.tsx | 11 +- .../components/lesson-plan-card.tsx | 9 +- .../components/lesson-plan-editor.tsx | 9 +- .../components/lesson-plan-filters.tsx | 14 +- .../components/node-editor.tsx | 25 ++- .../components/template-picker.tsx | 3 + .../components/version-history-drawer.tsx | 3 + src/modules/lesson-preparation/constants.ts | 64 ++++---- .../data-access-knowledge.ts | 77 +++++++-- .../data-access-templates.ts | 43 ++++- .../data-access-versions.ts | 25 ++- src/modules/lesson-preparation/data-access.ts | 147 ++++++++++++++++-- .../providers/lesson-plan-provider.tsx | 6 + .../lesson-preparation/publish-service.ts | 73 ++++++--- .../i18n/messages/en/lesson-preparation.json | 22 +++ .../messages/zh-CN/lesson-preparation.json | 22 +++ 23 files changed, 668 insertions(+), 135 deletions(-) create mode 100644 docs/architecture/audit/lesson-preparation-audit-report-v2.md diff --git a/docs/architecture/005_architecture_data.json b/docs/architecture/005_architecture_data.json index 479284a..f8d2291 100644 --- a/docs/architecture/005_architecture_data.json +++ b/docs/architecture/005_architecture_data.json @@ -12793,7 +12793,7 @@ }, "lesson_preparation": { "path": "src/modules/lesson-preparation", - "description": "教师备课模块:基于教材章节创建课案(节点图编辑器 React Flow,v2 nodes+edges 数据结构),支持模板、版本管理、知识点标注、题目创建/拉取、作业发布。编辑器从列表式(BlockRenderer + @dnd-kit)升级为节点图式(NodeEditor + @xyflow/react),旧 v1 数据通过 migrateV1ToV2() 自动迁移", + "description": "教师备课模块:基于教材章节创建课案(节点图编辑器 React Flow,v2 nodes+edges 数据结构),支持模板、版本管理、知识点标注、题目创建/拉取、作业发布。编辑器从列表式(BlockRenderer + @dnd-kit)升级为节点图式(NodeEditor + @xyflow/react),旧 v1 数据通过 migrateV1ToV2() 自动迁移。V2 审计修复:Server Actions i18n + 错误码模式、SYSTEM_TEMPLATES i18n 化、as unknown as 类型断言清零、a11y 深度修复、Tracker 埋点接入", "exports": { "dataAccess": [ { @@ -12809,7 +12809,7 @@ { "name": "createLessonPlan", "file": "data-access.ts", - "purpose": "创建课案" + "purpose": "创建课案(V2-2:接受 translateTitle 函数翻译 SYSTEM_TEMPLATES 的 i18n 键后存储到 DB)" }, { "name": "updateLessonPlanContent", @@ -12899,7 +12899,7 @@ { "name": "publishLessonPlanHomework", "file": "publish-service.ts", - "purpose": "发布课案为作业(编排 homework/exams/classes,通过对方 data-access 调用 addExamQuestions/getStudentIdsByClassIds,无直查跨模块表)" + "purpose": "发布课案为作业(编排 homework/exams/classes,通过对方 data-access 调用 addExamQuestions/getStudentIdsByClassIds,无直查跨模块表;V2-1:抛出 PublishServiceError 错误码;V2-3:显式字段映射替代 as unknown as)" }, { "name": "suggestKnowledgePoints", diff --git a/docs/architecture/audit/lesson-preparation-audit-report-v2.md b/docs/architecture/audit/lesson-preparation-audit-report-v2.md new file mode 100644 index 0000000..718be71 --- /dev/null +++ b/docs/architecture/audit/lesson-preparation-audit-report-v2.md @@ -0,0 +1,137 @@ +# 备课模块审计报告 V2(第二轮深度检查) + +> 审查日期:2026-06-22(第二轮) +> 审查范围:基于 V1 审计报告的修复成果,对全模块进行深度复查 +> 前置状态:V1 审计报告中的 P0-1/P0-2/P0-3/P1-2/P1-3/P1-4/P1-5/P1-6/P1-7/P1-8/P2-1(部分)/P2-4(接口)已完成 +> 本次目的:识别 V1 修复中遗留的未完成项,继续全量完整完成 + +--- + +## 一、V1 修复成果确认 + +| 项 | 状态 | 证据 | +|----|------|------| +| P0-1 跨模块直查 | ✅ 已完成 | publish-service.ts 使用 `addExamQuestions`/`getStudentIdsByClassIds` 跨模块接口 | +| P0-2 i18n 接入 | ⚠️ 部分完成 | 消息文件、request.ts、组件 useTranslations 已接入;但 actions 错误消息、constants SYSTEM_TEMPLATES 仍硬编码 | +| P0-3 DataScope | ✅ 已完成 | buildScopeCondition 按 scope 类型精确过滤 | +| P1-1 类型安全 | ⚠️ 部分完成 | `as never` 已修复;但 8 处 `as unknown as` 断言未修复 | +| P1-2 错误边界 | ✅ 已完成 | LessonPlanErrorBoundary 包裹 NodeEditPanel | +| P1-3 骨架屏 | ✅ 已完成 | 4 个 Skeleton 组件已创建 | +| P1-4 阻塞式 UI | ✅ 已完成 | alert/confirm/window.location.reload 全部替换 | +| P1-5 多实例 | ✅ 已完成 | LessonPlanProvider + Context 注入 | +| P1-6 纯函数抽取 | ⚠️ 部分完成 | lib/ 三个文件已抽取;但 node-editor.tsx MiniMap nodeColor 仍内联颜色映射 | +| P1-7 角色配置 | ✅ 已完成 | 4 个角色配置 + ROLE_CONFIGS 注册表 | +| P1-8 Block 注册表 | ✅ 已完成 | BLOCK_REGISTRY 配置驱动渲染 | +| P2-1 a11y | ⚠️ 部分完成 | 5 个对话框 role/aria-label 已添加;但 select 无 label、题目列表非 ul/li、画布无键盘导航 | +| P2-4 监控埋点 | ⚠️ 部分完成 | LessonPlanTracker 接口已定义;但未在关键操作处调用 | + +--- + +## 二、V2 新发现的问题 + +### V2-1:actions 错误消息仍硬编码中文(P0-2 遗留) + +| 位置 | 问题 | 违反规则 | +|------|------|----------| +| [actions.ts:53](file:///e:/Desktop/CICD/src/modules/lesson-preparation/actions.ts#L53) | `"获取课案列表失败"` | i18n 规范 | +| [actions.ts:66](file:///e:/Desktop/CICD/src/modules/lesson-preparation/actions.ts#L66) | `"课案不存在或无权访问"` | 同上 | +| [actions.ts:71](file:///e:/Desktop/CICD/src/modules/lesson-preparation/actions.ts#L71) | `"获取课案失败"` | 同上 | +| [actions.ts:102](file:///e:/Desktop/CICD/src/modules/lesson-preparation/actions.ts#L102) | `"创建课案失败"` | 同上 | +| [actions.ts:125](file:///e:/Desktop/CICD/src/modules/lesson-preparation/actions.ts#L125) | `"保存失败"` | 同上 | +| [actions.ts:152](file:///e:/Desktop/CICD/src/modules/lesson-preparation/actions.ts#L152) | `"保存版本失败"` | 同上 | +| [actions.ts:171](file:///e:/Desktop/CICD/src/modules/lesson-preparation/actions.ts#L171) | `"获取版本失败"` | 同上 | +| [actions.ts:190](file:///e:/Desktop/CICD/src/modules/lesson-preparation/actions.ts#L190) | `"版本不存在或无权操作"` | 同上 | +| [actions.ts:196](file:///e:/Desktop/CICD/src/modules/lesson-preparation/actions.ts#L196) | `"回退失败"` | 同上 | +| [actions.ts:212](file:///e:/Desktop/CICD/src/modules/lesson-preparation/actions.ts#L212) | `"删除失败"` | 同上 | +| [actions.ts:228](file:///e:/Desktop/CICD/src/modules/lesson-preparation/actions.ts#L228) | `"复制失败"` | 同上 | +| [actions.ts:245](file:///e:/Desktop/CICD/src/modules/lesson-preparation/actions.ts#L245) | `"获取模板失败"` | 同上 | +| [actions.ts:267](file:///e:/Desktop/CICD/src/modules/lesson-preparation/actions.ts#L267) | `"保存模板失败"` | 同上 | +| [actions.ts:282](file:///e:/Desktop/CICD/src/modules/lesson-preparation/actions.ts#L282) | `"删除模板失败"` | 同上 | +| [actions-ai.ts:29](file:///e:/Desktop/CICD/src/modules/lesson-preparation/actions-ai.ts#L29) | `"AI 推荐失败,请检查 AI Provider 配置"` | 同上 | +| [actions-kp.ts:37](file:///e:/Desktop/CICD/src/modules/lesson-preparation/actions-kp.ts#L37) | `"加载知识点失败"` | 同上 | +| [actions-publish.ts:48](file:///e:/Desktop/CICD/src/modules/lesson-preparation/actions-publish.ts#L48) | `"发布失败"` | 同上 | +| [publish-service.ts:39,55,60,62,64,70,103,128](file:///e:/Desktop/CICD/src/modules/lesson-preparation/publish-service.ts) | 8 处 `throw new Error("中文")` | 同上 | +| [data-access.ts:183,243](file:///e:/Desktop/CICD/src/modules/lesson-preparation/data-access.ts) | `"模板不存在"`/`"课案不存在或无权访问"` | 同上 | +| [data-access-templates.ts:61](file:///e:/Desktop/CICD/src/modules/lesson-preparation/data-access-templates.ts#L61) | `"课案不存在或无权访问"` | 同上 | + +**修复方案**:Server Actions 使用 `getTranslations("lessonPreparation")` 获取翻译;publish-service/data-access 的 `throw new Error` 改为抛出错误码(如 `LESSON_PLAN_NOT_FOUND`),由 actions 层捕获并翻译。 + +### V2-2:constants.ts SYSTEM_TEMPLATES 仍硬编码中文(P0-2 遗留) + +| 位置 | 问题 | +|------|------| +| [constants.ts:46-106](file:///e:/Desktop/CICD/src/modules/lesson-preparation/constants.ts#L46-L106) | SYSTEM_TEMPLATES 的 `name`/`title`/`hint` 字段硬编码中文("常规课"/"教学目标"/"明确本课的知识、能力、情感目标"等) | + +**修复方案**:将 SYSTEM_TEMPLATES 的 `name`/`title`/`hint` 改为 i18n 键(如 `template.names.tpl_regular`/`blockType.objective`/`template.hints.tpl_regular.objective`),在 buildInitialContent 调用时由 actions 层传入翻译后的标题。 + +### V2-3:8 处 `as unknown as` 断言未修复(P1-1 遗留) + +| 位置 | 代码 | +|------|------| +| [data-access.ts:146](file:///e:/Desktop/CICD/src/modules/lesson-preparation/data-access.ts#L146) | `rows as unknown as LessonPlanListItem[]` | +| [data-access.ts:166](file:///e:/Desktop/CICD/src/modules/lesson-preparation/data-access.ts#L166) | `row as unknown as LessonPlan` | +| [data-access.ts:288](file:///e:/Desktop/CICD/src/modules/lesson-preparation/data-access.ts#L288) | `rows[0] as unknown as LessonPlanTemplate` | +| [data-access-versions.ts:30](file:///e:/Desktop/CICD/src/modules/lesson-preparation/data-access-versions.ts#L30) | `rows as unknown as LessonPlanVersion[]` | +| [data-access-knowledge.ts:25](file:///e:/Desktop/CICD/src/modules/lesson-preparation/data-access-knowledge.ts#L25) | `rows.filter(...) as unknown as LessonPlanListItem[]` | +| [data-access-knowledge.ts:43](file:///e:/Desktop/CICD/src/modules/lesson-preparation/data-access-knowledge.ts#L43) | 同上 | +| [data-access-templates.ts:40](file:///e:/Desktop/CICD/src/modules/lesson-preparation/data-access-templates.ts#L40) | `personalRows as unknown as LessonPlanTemplate[]` | +| [publish-service.ts:40](file:///e:/Desktop/CICD/src/modules/lesson-preparation/publish-service.ts#L40) | `rows[0] as unknown as {...}` | + +**修复方案**:使用 Drizzle 的 `inferSelect` 类型推导,或定义显式类型映射函数替代断言。 + +### V2-4:node-editor.tsx MiniMap nodeColor 仍内联颜色映射(P1-6 遗留) + +| 位置 | 问题 | +|------|------| +| [node-editor.tsx:126-144](file:///e:/Desktop/CICD/src/modules/lesson-preparation/components/node-editor.tsx#L126-L144) | MiniMap nodeColor 内联 colors 对象,未使用 lib/node-summary.ts 的 NODE_COLORS/getNodeColor | + +**修复方案**:改为 `import { getNodeColor } from "../lib/node-summary"` 并在 nodeColor 回调中调用。 + +### V2-5:a11y 遗留问题(P2-1 遗留) + +| 位置 | 问题 | 违反规则 | +|------|------|----------| +| [lesson-plan-filters.tsx:40-51](file:///e:/Desktop/CICD/src/modules/lesson-preparation/components/lesson-plan-filters.tsx#L40-L51) | 2 个 `` 无 `