Files
NextEdu/docs/architecture/audit/archive/lesson-preparation-audit-report-v3.md

19 KiB
Raw Blame History

备课模块审计报告 v3

审计日期2026-06-24 审计范围:src/modules/lesson-preparation/ 全部文件 + src/app/(dashboard)/{teacher,admin,student,parent}/lesson-plans/ 全部页面 审计依据:e:\Desktop\CICD\.trae\rules\project_rules.mddocs/architecture/004_architecture_impact_map.md §2.27、docs/architecture/005_architecture_data.json modules.lesson_preparation 前序报告:lesson-preparation-audit-report-v2.md


一、现有实现概要

1.1 文件分布

备课模块共 45 个文件,分布如下:

层级 文件数 主要文件
types/schema/constants 3 types.ts(345行)、schema.ts(63行)、constants.ts(117行)
data-access 4 data-access.ts(593行)、data-access-versions.ts(181行)、data-access-templates.ts(129行)、data-access-knowledge.ts(95行)
actions 4 actions.ts(391行)、actions-publish.ts(82行)、actions-ai.ts(46行)、actions-kp.ts(47行)
services 2 publish-service.ts(198行)、ai-suggest.ts(82行)
lib 6 type-guards.ts(213行)、i18n-errors.ts(50行)、document-migration.ts(330行)、anchor-injector.ts(304行)、node-summary.ts(133行)、rf-mappers.ts(172行)
config 1 block-registry.tsx(194行)
providers 2 lesson-plan-provider.tsx(336行)、lesson-plan-provider-setup.tsx(29行)
services 1 default-data-service.ts(165行)
hooks 1 use-lesson-plan-editor.ts(303行)
components 18 含 11 个 block 组件 + 4 个 node 组件 + 7 个业务组件
seed 1 seed-templates.ts(9行)
页面 13 teacher(3) + admin(2) + student(2) + parent(2) + loading/error(4)

1.2 数据流

页面(Server Component) → getAuthContext() → data-access → DB
                       ↓
                       LessonPlanProviderSetup → LessonPlanProvider
                       ↓
                       LessonPlanEditor(LessonPlanList) → useLessonPlanContextSafe()
                       ↓
                       default-data-service → Server Actions → requirePermission → data-access → DB

1.3 架构图完整性

架构影响地图 §2.27 记录的导出函数、文件清单、依赖关系与实际代码基本一致,但存在以下遗漏:

  • providers/lesson-plan-provider-setup.tsx 已在 V3 续审计中补充
  • components/nodes/anchor-node-selector.tsxtextbook-segments.tsx 已在 V3 续审计中补充
  • node-edit-panel.tsx@/modules/ai 的直接依赖未在架构图依赖关系中记录

二、现存问题与原因分析

2.1 安全与权限问题P0

问题 1Parent/Student 路由未校验孩子/班级归属 — 信息泄露漏洞

  • 位置data-access.ts 第 197-204 行 buildScopeCondition
  • 问题childrenclass_members 类型的 DataScope 仅过滤 status = 'published'完全未使用 scope.childrenIds/scope.classIds/scope.gradeIds 进行归属过滤
  • 违反规则:项目规则 "Parent routes must include permission checks with both parentId and studentId to prevent information leakage"
  • 后果:任何家长可查看全校所有已发布课案;任何学生可查看全校所有已发布课案,构成信息泄露

问题 2createLessonPlanVersion 未校验 planId 归属

  • 位置data-access-versions.ts 第 56-82 行
  • 问题:函数接收 planIduserId,但事务内只查询 lessonPlanVersions 表的 max(versionNo)未校验该 planId 是否属于 userId
  • 违反规则:项目规则 "All Server Actions must call requirePermission() for permission verification" + "软删除/权限过滤在 data-access 层结合 userId 过滤"
  • 后果:调用方传入任意 planId 即可为他人课案创建版本记录,越权写入

问题 3pruneAutoVersions 完全无权限校验

  • 位置data-access-versions.ts 第 152-181 行
  • 问题:函数签名 pruneAutoVersions(planId, keep = 50) 没有 userId 参数,任何调用方传入 planId 即可删除该课案的自动版本记录
  • 违反规则:同问题 2
  • 后果:越权删除他人课案的版本历史

问题 4getLessonPlansByKnowledgePoint/getLessonPlansByQuestion 无权限过滤

  • 位置data-access-knowledge.ts 第 11-95 行
  • 问题:两个函数直接 db.select().from(lessonPlans) 查询全表,未过滤 creatorId也未过滤 statusarchived 课案也会被查出)
  • 违反规则:同问题 2
  • 后果:任意调用方可获取所有课案(含他人 draft、archived 状态)的列表,严重越权

问题 5saveLessonPlanVersionAction 的 schema 不包含 content 字段

  • 位置actions.ts 第 154-178 行、schema.ts 第 26-28 行
  • 问题saveVersionSchema 仅校验 planIdlabel完全不包含 content 字段input.content(类型为 LessonPlanDocument)未经任何运行时校验直接持久化
  • 违反规则:项目规则 "输入使用 Zod 验证,验证失败返回结构化错误"
  • 后果:恶意或畸形文档结构可被写入数据库

问题 6publishLessonPlanHomeworkAction 中 homeworkTitle 传入 planId

  • 位置actions-publish.ts 第 35 行
  • 问题homeworkTitle = t("publish.homeworkTitle", { title: parsed.data.planId })title 参数传入的是 planIdUUID而非课案标题
  • 违反规则:业务逻辑正确性
  • 后果:作业标题将包含 UUID 而非有意义的课案名称

问题 7getLessonPlansAction 的 params 未经验证

  • 位置actions.ts 第 43-53 行
  • 问题params(含 query/textbookId/chapterId/subjectId/status未经过 Zod 验证直接传入 getLessonPlans()
  • 违反规则:项目规则 "输入使用 Zod 验证"
  • 后果:若 query 用于 SQL LIKE 查询且未调用 escapeLikePattern(),存在 LIKE 通配符注入风险

2.2 架构违规问题P0/P1

问题 8node-edit-panel.tsx 直接 import @/modules/ai

  • 位置node-edit-panel.tsx 第 11-12 行
  • 问题:直接 import AiLessonContentGeneratoruseAiClientOptional,形成模块间紧耦合
  • 违反规则:项目规则 "模块内部组件绝不直接 import 其他业务模块的 actions 或 data-access只能通过注入的接口调用"
  • 后果模块间紧耦合无法独立测试AI 模块变更影响备课模块

问题 9admin/student/parent 列表页未包裹 LessonPlanProviderSetup

  • 位置admin/lesson-plans/page.tsxstudent/lesson-plans/page.tsxparent/lesson-plans/page.tsx
  • 问题LessonPlanList 调用 useLessonPlanContextSafe() 获取 service,当未在 Provider 内使用时 servicenull,导致 handleFilter 静默返回 — 筛选功能在 admin/student/parent 页面完全失效
  • 违反规则:项目规则 "Provider 是否正确注入数据服务"
  • 后果:筛选 UI 仍可见但点击无反应,用户体验差

问题 10publish-service.ts 多步写操作未包裹事务

  • 位置publish-service.ts 第 56-197 行
  • 问题publishLessonPlanHomework 包含 5 个写操作步骤(创建题目、创建 exam 草稿、插入 exam 题目关联、下发作业、回写溯源标记),均未包裹在 db.transaction
  • 违反规则:项目规则 "需要原子性的操作必须包裹在 db.transaction 中"
  • 后果:任一步骤失败将导致数据不一致(孤儿题目、草稿残留、溯源断裂)

2.3 类型安全问题P1

问题 11rf-mappers.ts 颜色映射 Bug

  • 位置rf-mappers.ts 第 113 行
  • 问题getNodeColor(anchor.nodeId) 传入 nodeId 而非 node typegetNodeColor 期望接收节点类型(如 "objective"),传入 nodeId 必然找不到匹配项
  • 违反规则:功能性 Bug
  • 后果:锚点边始终使用默认灰色 #9e9e9e,注释声称的"P1-4 修复"实际未生效

问题 12富文本类型配置三处不一致

  • 位置constants.ts 第 25-36 行、block-registry.tsx 第 50、56-69 行
  • 问题RICH_TEXT_BLOCK_TYPES10项vs RICH_TEXT_TYPES2项vs BLOCK_REGISTRY.isRichText2项三处定义语义冲突
  • 违反规则:配置一致性
  • 后果isRichTextBlock()RICH_TEXT_BLOCK_TYPES 行为完全相反,调用方混用会产生矛盾结果

问题 13多处 as 断言违规

  • 位置
    • node-summary.ts 第 28-55 行:as { html?: string; ... } 将联合类型转为内联接口
    • rf-mappers.ts 第 81、95 行:as Record<string, unknown>
    • block-renderer.tsx 第 107、115、121、125 行4 处 as XxxBlockData
    • lesson-plan-readonly-view.tsx 第 91 行:as Edge[]
    • inline-question-editor.tsx 第 34 行:as typeof QUESTION_TYPES[number]
    • data-access-knowledge.ts 第 24、39、67、82 行4 处 as 断言
    • use-lesson-plan-editor.ts 第 137、138、154、155 行4 处 as 断言
    • 4 个页面文件中 findChapteras typeof chapters(冗余断言)
    • actions.ts 第 144 行:as unknown as LessonPlanDocument 双重断言
  • 违反规则:项目规则 "禁止 as 断言(除类型收窄外)"
  • 后果:类型安全被绕过,潜在运行时错误

问题 14actions-ai.ts/actions-kp.ts 隐式 any

  • 位置actions-ai.ts 第 34 行 Array.isArray(doc.nodes) 收窄为 any[]actions-kp.ts 第 30 行 let kps; 隐式 any
  • 违反规则:项目规则 "禁止 any"
  • 后果:类型安全缺失

2.4 i18n 与错误处理问题P1

问题 15actions-ai.ts/actions-kp.ts 未使用 handleActionErrortranslateFieldErrors

  • 位置actions-ai.ts 第 23、41-45 行;actions-kp.ts 第 23、42-46 行
  • 问题Zod 错误未调用 translateFieldErrors() 翻译catch 块未使用 handleActionError(),非权限错误被静默吞掉,无日志
  • 违反规则:项目规则 "所有 Server Action catch 块改用 handleActionError"
  • 后果:错误不可观测,用户看到未翻译的 i18n 键

问题 16block-renderer.tsx 硬编码中文

  • 位置block-renderer.tsx 第 130 行
  • 问题"未知 block 类型" 直接硬编码为中文,整个文件未 import useTranslations
  • 违反规则:项目规则 "所有用户可见文本必须适配 i18n"
  • 后果:多语言环境下显示中文

问题 17schema.ts 多个 schema 缺少 i18n 错误消息

  • 位置schema.ts 第 14-32 行
  • 问题:仅 createLessonPlanSchemapublishLessonPlanHomeworkSchema 部分字段使用 i18n 键,其他 6 个 schema 均未使用
  • 违反规则:项目规则 "Zod schema 错误消息应使用 i18n 键"
  • 后果Zod 校验错误返回英文默认消息

2.5 a11y 与 UX 问题P1/P2

问题 18inline-question-editor.tsx Modal 缺少焦点陷阱

  • 位置inline-question-editor.tsx 第 72-227 行
  • 问题:模态框设置了 role="dialog"aria-modal="true"但未实现焦点陷阱、Escape 关闭、焦点管理
  • 违反规则a11y 键盘导航
  • 后果:键盘用户无法正常使用模态框

问题 19多处缺少 aria-label

  • 位置lesson-plan-editor.tsx 第 239-243 行(标题输入框)、第 330-335 行(添加节点按钮);block-renderer.tsx 第 71-102 行4 个图标按钮);lesson-plan-readonly-view.tsx 第 89-103 行(画布缺少 aria
  • 违反规则a11y 语义化标签
  • 后果:屏幕阅读器无法识别元素用途

问题 20getLessonPlanStats 的 archived 恒为 0

  • 位置data-access.ts 第 553-566 行、admin/lesson-plans/page.tsx 第 63-72 行
  • 问题WHERE 子句排除 archived 后硬编码返回 archived: 0,但 admin 页面仍渲染"已归档"统计卡片
  • 违反规则:数据准确性
  • 后果:统计卡片永远显示 0具有误导性

2.6 性能与代码质量问题P2

问题 21use-lesson-plan-editor.ts 303 行超标

  • 位置hooks/use-lesson-plan-editor.ts
  • 问题:文件 303 行,远超 Hook 80 行建议上限
  • 违反规则:项目规则 "自定义 Hook建议 ≤ 80 行"
  • 后果:可维护性差

问题 22findChapter 在 4 个页面重复

  • 位置teacher edit、admin view、student view、parent view
  • 问题4 个页面文件中存在完全相同的 findChapter 递归函数实现
  • 违反规则DRY 原则
  • 后果:代码重复,维护成本高

问题 23lesson-plan-card.tsx 4 个异步函数未使用 useCallback

  • 位置lesson-plan-card.tsx 第 72、89、105、122 行
  • 问题handleArchive/handleDuplicate/handlePublish/handleUnpublish 每次渲染重新创建
  • 违反规则:性能最佳实践
  • 后果:不必要渲染

问题 24多处 Tailwind 任意值

  • 位置lesson-plan-editor.tsx4处node-edit-panel.tsx2处inline-question-editor.tsx3处
  • 违反规则:项目规则 "禁止使用任意值(w-[137px]),除非有充分理由并注释"

三、行业差距对比

3.1 与优秀 K12 产品的差距

维度 优秀实践 当前实现 差距影响
权限隔离 家长仅能查看自己孩子班级的课案,学生仅能查看自己班级课案 家长/学生可查看全校已发布课案 信息泄露风险,违反 FERPA/GDPR 等隐私法规
数据一致性 发布作业等关键操作使用事务保证原子性 多步写操作无事务 部分失败导致数据不一致,需要人工修复
版本管理 版本创建/删除有严格的归属校验 版本创建/删除可越权操作 用户可篡改他人课案历史
a11y 模态框实现完整焦点管理,所有交互元素有 aria-label Modal 无焦点陷阱,多处缺少 aria-label 残障用户无法使用
错误可观测性 所有异常记录日志,有统一错误处理 部分Action 静默吞掉错误 生产问题无法排查
配置一致性 单一数据源定义 Block 类型属性 三处富文本配置冲突 运行时行为不可预测
模块解耦 通过接口抽象依赖,支持独立测试 直接 import AI 模块组件 无法独立测试,变更影响扩散

3.2 缺失的功能

  • 课案协作:优秀产品支持多位教师协作编辑同一课案,当前仅支持单教师
  • 课案评课:教研组长可对课案添加评课意见,当前缺失
  • 课案资源库:跨学期/学年的课案资源库检索,当前仅按教材章节组织
  • AI 智能备课:基于课标自动生成教学目标/重难点,当前仅有知识点推荐

四、改进优先级建议

P0严重 — 安全/正确性,必须立即修复)

# 问题 改进方向
P0-1 Parent/Student 路由未校验归属关系 修复 buildScopeConditionchildren/class_members 分支,使用 scope.classIds/scope.gradeIds/scope.childrenIds 过滤;修复 getLessonPlanById 增加归属校验
P0-2 createLessonPlanVersion 未校验 planId 归属 事务开头增加 lessonPlans.creatorId = userId 校验
P0-3 pruneAutoVersions 无 userId 参数 增加 userId 参数,删除前校验 planId 归属
P0-4 getLessonPlansByKnowledgePoint/getLessonPlansByQuestion 无权限过滤 增加 userId 参数,过滤 status != 'archived',按 creatorId/DataScope 限制
P0-5 saveLessonPlanVersionAction schema 不含 content 定义 lessonPlanDocumentSchema 递归校验文档结构
P0-6 publishLessonPlanHomeworkAction homeworkTitle 传入 planId 先查询课案标题再传入
P0-7 getLessonPlansAction params 未验证 定义 Zod schemadata-access 使用 escapeLikePattern
P0-8 rf-mappers.ts 颜色映射 Bug 传入节点 type 而非 nodeId
P0-9 富文本配置三处不一致 统一为单一数据源,删除冗余定义
P0-10 block-renderer.tsx 硬编码中文 + as 断言 使用 i18n + 类型守卫
P0-11 node-edit-panel.tsx 直接 import @/modules/ai 通过 props 注入 AI 组件
P0-12 publish-service.ts 多步写操作无事务 包裹 db.transaction
P0-13 admin/student/parent 列表页未包裹 Provider 包裹 LessonPlanProviderSetup

P1高 — 类型安全/规范一致性)

# 问题 改进方向
P1-1 多处 as 断言node-summary/rf-mappers/block-renderer/readonly-view/inline-question-editor/data-access-knowledge/use-lesson-plan-editor/4页面 使用类型守卫替代
P1-2 actions-ai.ts/actions-kp.ts 隐式 any 显式类型标注
P1-3 actions-ai.ts/actions-kp.ts 未用 handleActionError/translateFieldErrors 统一错误处理
P1-4 schema.ts 多个 schema 缺 i18n 错误消息 补全 i18n 键
P1-5 getLessonPlanStats archived 恒为 0 修复统计逻辑
P1-6 findChapter 4 处重复 提取为 textbooks 共享工具
P1-7 inline-question-editor.tsx Modal 缺焦点陷阱 实现焦点管理
P1-8 多处缺少 aria-label 补全 a11y 属性
P1-9 data-access-knowledge.ts LIKE 未用 escapeLikePattern 统一转义
P1-10 actions-publish.ts 重复 requirePermission 复用 AuthContext

P2中 — 性能/代码质量)

# 问题 改进方向
P2-1 use-lesson-plan-editor.ts 303 行超标 拆分为多个 slice
P2-2 lesson-plan-card.tsx 4 函数未 useCallback 包裹 useCallback
P2-3 多处 Tailwind 任意值 提取设计令牌
P2-4 lesson-plan-list.tsx 缺筛选加载状态 增加 isLoading
P2-5 block-renderer.tsx 废弃文件 评估是否可删除
P2-6 ai-suggest.ts prompt 硬编码中文 参数化注入

五、架构图同步说明

本次审计发现架构图需补充以下内容:

  1. 依赖关系补充node-edit-panel.tsx@/modules/ai 的直接依赖需在 005_architecture_data.jsonlesson_preparation.dependsOn 中记录
  2. auditFixes 补充:本次 V3 续续审计的修复记录V3-23 ~ V3-35需添加到 005_architecture_data.jsonauditFixes 对象
  3. 文件清单更新:若 use-lesson-plan-editor.ts 拆分为多个 slice需更新 004 文件清单和 005 files 数组
  4. 配置冲突修复constants.tsblock-registry.tsx 的富文本配置统一后,需更新 004 中两者的描述

六、实施计划

本报告所有问题将按 P0 → P1 → P2 顺序完整实施,实施过程中同步更新架构图 004/005。