Files
NextEdu/docs/feature/f_bk_design.md
SpecialX 978d9a8309
Some checks failed
Security / deep-security-scan (push) Failing after 20m5s
DR Drill / dr-drill (push) Failing after 1m31s
CI / scheduled-backup (push) Failing after 1m31s
CI / backup-verify (push) Has been skipped
CI / weekly-dr-drill (push) Failing after 0s
CI / build-deploy (push) Has been cancelled
CI / security-scan (push) Has been cancelled
feat: 新增备课模块并修复全模块 P0/P1/P2 缺陷
主要变更:

- 新增 lesson-preparation 模块: 备课编辑器、节点编辑、AI 建议、知识点选择、版本历史、作业发布

- 新增 shared 通用组件: charts/question-bank-filters/schedule-list/ui (chip-nav/filter-bar/page-header/stat-card/stat-item)

- 新增 student/admin 端 loading.tsx 与 error.tsx, 优化加载与错误态体验

- 新增 teacher/lesson-plans 页面 (列表/新建/编辑)

- 新增 drizzle 迁移 0002_tiny_lionheart 及 snapshot

- 新增 textbooks/schema.ts 与 exams/utils/normalize-structure.ts

- 修复 Tiptap v3 SSR hydration 崩溃 (rich-text-block immediatelyRender: false)

- 重构多模块 data-access/actions/组件, 修复权限校验与类型规范

- 同步架构文档 004/005 反映新增模块、导出、依赖关系

- 归档 bugs/* 测试报告与 e2e 测试脚本 (admin/parent/student/teacher web_test)
2026-06-22 01:06:16 +08:00

609 lines
27 KiB
Markdown
Raw Permalink 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.
# 备课模块lesson-preparation设计文档
> 配套原型:`docs/feature/f_bk.md`
> 架构依据:`docs/architecture/004_architecture_impact_map.md`、`005_architecture_data.json`
> 编写日期2026-06-18
> 范围:**P0 地基 + P1 联动**P2 协作 / P3 AI 与学情回看留作后续 spec
---
## 0. 关键决策摘要
| 决策项 | 最终选择 | 理由 |
|--------|----------|------|
| 本次 spec 范围 | P0 + P1 | 构成"备课→出题→下发"最小可用闭环,体量适中 |
| 编辑器形态 | Block 编辑器为主 | 与蓝图文字一致;设计稿的"课文+节点画布"作为 `text_study` block 的内部交互 |
| Block 存储模型 | 方案 AJSON 文档 + 版本快照表 | 与现有 `questions.content` / `homeworkAssignments.structure` 的 JSON 模式一致;为 P2 批注 / P3 AI 重写预留稳定 blockId 锚点;零跨模块 DB 访问 |
| 知识点同步 | P1 仅"关联已有 + AI 推荐",不回写教材树 | 避免 P1 引入审核流拖慢闭环;回写留作后续 spec |
| 作业发布闭环 | 复用 exam 中转 | 课案练习块 → 打包成 exam 草稿 → 调用现有 `createHomeworkAssignmentAction` 下发;零 schema 侵入、溯源清晰作业→exam→课案 |
---
## 1. 模块定位与边界
### 1.1 模块名
`lesson-preparation`(目录 `src/modules/lesson-preparation/`),中文"备课"。
### 1.2 与 `course-plans` 的关系(互补,不合并)
| 模块 | 粒度 | 回答的问题 |
|------|------|-----------|
| `course-plans` | 学期/周宏观排课totalHours/weeklyHours/week/topic | "这学期每周教什么" |
| `lesson-preparation` | 具体一节课的教学设计(目标/重难点/导入/新授/练习/作业…) | "这节课怎么教" |
软关联:课案可记录来源 `coursePlanItemId`(可空,无强外键),便于"周计划→具体课案"下钻。
### 1.3 依赖关系(严格三层架构,零跨模块直查)
- 依赖 `shared/*``@/auth`
- 通过对方 data-access 通信(不直接查询对方表):
- `textbooks` — 只读章节树 / 知识点树
- `questions` — 创建题目(含知识点关联)、查询题目
- `exams` — 创建 exam 草稿(用于发布中转)
- `homework` — 创建作业下发到班级
- `classes` — 查询教师班级(用于下发目标选择)
- `files` — 附件引用
- 被依赖P0/P1 阶段无被依赖方
---
## 2. 数据模型(新增 3 张表)
### 2.1 `lesson_plans`(课案主表)
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| id | id | PK | CUID2 |
| title | varchar(255) | notNull | 课案标题 |
| textbookId | varchar(128) | FK→textbooks, nullable | 教材(允许非教材备课) |
| chapterId | varchar(128) | FK→chapters, nullable | 章节 |
| coursePlanItemId | varchar(128) | nullable, 无 FK | 软关联课程计划项 |
| subjectId | varchar(128) | FK→subjects, nullable | 学科 |
| gradeId | varchar(128) | FK→grades, nullable | 年级 |
| templateId | varchar(128) | nullable | 使用的模板 ID |
| templateName | varchar(100) | nullable | 模板名快照(防模板改名) |
| content | json | notNull | block 文档 JSON见 §3 |
| status | varchar(50) | default 'draft' | `draft`/`published`/`archived` |
| creatorId | varchar(128) | FK→users, notNull | 创建者 |
| lastSavedAt | timestamp | nullable | 最后自动保存时间 |
| createdAt | timestamp | defaultNow | |
| updatedAt | timestamp | defaultNow onUpdateNow | |
索引:`creatorIdx(creatorId)``statusIdx(status)``textbookChapterIdx(textbookId, chapterId)``subjectGradeIdx(subjectId, gradeId)`
### 2.2 `lesson_plan_versions`(版本快照表)
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| id | id | PK | |
| planId | varchar(128) | FK→lesson_plans, onDelete cascade | |
| versionNo | int | notNull | 每 plan 内自增 |
| label | varchar(100) | nullable | 手动保存时的标签 |
| content | json | notNull | 该版本 content 快照 |
| isAuto | boolean | default false | true=自动保存触发 |
| creatorId | varchar(128) | FK→users, notNull | |
| createdAt | timestamp | defaultNow | |
索引:`planVersionIdx(planId, versionNo)`(唯一)、`planCreatedIdx(planId, createdAt desc)`
### 2.3 `lesson_plan_templates`(模板表)
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| id | id | PK | |
| name | varchar(100) | notNull | 模板名 |
| type | varchar(50) | notNull | `system`/`personal` |
| scope | varchar(50) | notNull | `regular`/`review`/`experiment`/`inquiry`/`blank`/`custom` |
| blocks | json | notNull | 预置 block 骨架blockType + 默认标题 + 提示语,无内容) |
| creatorId | varchar(128) | FK→users, nullable | personal 模板拥有者system 为 null |
| createdAt | timestamp | defaultNow | |
| updatedAt | timestamp | defaultNow onUpdateNow | |
索引:`typeCreatorIdx(type, creatorId)`personal 模板按创建者过滤)
> 系统预设 4+1 套模板由 seed 脚本写入type=system。教师"另存为我的模板"写入 type=personal。
---
## 3. Block 文档 JSON 结构
### 3.1 顶层结构
```json
{
"version": 1,
"blocks": [
{
"id": "blk_xxx",
"type": "objective",
"title": "教学目标",
"data": { },
"order": 0
}
]
}
```
- `id`:客户端生成的稳定 IDCUID2是 P2 批注锚点、P3 AI 单 block 重写的定位依据
- `type`:见 §3.2 枚举
- `title`:环节名(可改,模板提供默认值)
- `data`:类型相关数据,见 §3.3
- `order`:排序索引(整数,编辑器拖拽后重排)
### 3.2 Block 类型枚举
| type | 用途 | 出现模板 |
|------|------|---------|
| `objective` | 教学目标 | 常规/复习/实验 |
| `key_point` | 教学重难点 | 常规 |
| `import` | 导入 | 常规 |
| `new_teaching` | 新授 | 常规 |
| `consolidation` | 巩固 | 常规 |
| `summary` | 小结 | 常规/复习/实验/探究 |
| `homework` | 作业布置(文字描述型) | 常规 |
| `blackboard` | 板书设计 | 常规 |
| `text_study` | 文本研习(设计稿画布形态) | 语文/英语精读课自定义添加 |
| `exercise` | 练习/作业块P1 核心,关联题目) | 任意模板可添加 |
| `rich_text` | 通用富文本(自定义环节) | 复习/实验/探究的自定义环节 |
| `reflection` | 教学反思P3 预留P0/P1 不渲染特殊 UI | 任意 |
### 3.3 各 block.data 结构
**富文本类**`objective`/`key_point`/`import`/`new_teaching`/`consolidation`/`summary`/`homework`/`blackboard`/`rich_text`/`reflection`
```json
{
"html": "<p>...</p>",
"knowledgePointIds": ["kp_1", "kp_2"]
}
```
**`text_study`**(设计稿画布形态的 block 化):
```json
{
"sourceText": "天气凉了,树叶黄了...",
"annotations": [
{
"id": "ann_xxx",
"anchor": { "start": 12, "end": 20 },
"nodeType": "language_feature",
"title": "语言特色",
"note": "关注'那么'的反复",
"color": "yellow"
}
],
"knowledgePointIds": ["kp_1"]
}
```
**`exercise`**P1 核心):
```json
{
"items": [
{
"questionId": "q_xxx",
"source": "bank",
"score": 5,
"order": 0
},
{
"questionId": "inline_draft_xxx",
"source": "inline",
"inlineContent": {
"content": { },
"type": "single_choice",
"difficulty": 3,
"knowledgePointIds": ["kp_1"]
},
"score": 10,
"order": 1
}
],
"purpose": "class_practice",
"knowledgePointIds": ["kp_1"]
}
```
- `source``bank`=从题库拉取questionId 已存在于 questions 表);`inline`=课案内新建(编辑期 questionId 为占位 `inline_draft_${cuid}`,发布时入库后回填真实 ID
- `inlineContent`:仅 source=inline 时存在,结构与 `questions` 表字段对齐content/type/difficulty/knowledgePointIds发布时作为 `createQuestionWithRelations` 的入参
- `purpose``class_practice`=课堂练习;`after_class_homework`=课后作业(发布闭环仅处理此类型)
---
## 4. 模板系统
### 4.1 系统预设模板4+1 套,由 seed 脚本写入)
| 模板 | scope | block 序列 |
|------|-------|-----------|
| 常规课 | `regular` | objective → key_point → import → new_teaching → consolidation → summary → homework → blackboard |
| 复习课 | `review` | objective → rich_text("知识网络梳理") → rich_text("典型例题精讲") → rich_text("变式训练") → exercise("当堂检测") → summary |
| 实验课 | `experiment` | objective → rich_text("器材准备") → rich_text("实验步骤") → rich_text("观察记录表") → rich_text("交流讨论") → summary |
| 探究课 | `inquiry` | rich_text("情境导入") → rich_text("问题驱动") → rich_text("小组探究") → rich_text("成果展示") → rich_text("归纳提升") |
| 空白 | `blank` | (无预置 block |
模板 `blocks` JSON 仅定义骨架type + 默认 title + 提示语),不含内容。教师选用后生成对应 block 序列,可自由增删、改序、改名、改内容。
### 4.2 自定义模板
- 教师在编辑器内"另存为我的模板"→ 写入 `lesson_plan_templates`type=personal, creatorId=教师)
- personal 模板仅创建者可见、可编辑、可删除
- 创建课案时模板选择器并列展示 system + 我的 personal 模板
---
## 5. Block 编辑器与版本管理
### 5.1 编辑器交互
- 主体:可拖拽 block 列表(类 Notion/BlockNote 的块状编辑器)
- 每个 block标题栏可改名+ 内容区(按 type 渲染不同编辑组件)+ 拖拽手柄 + 删除/上移/下移/复制
- block 增删:顶部"+"按钮选择 block 类型插入;可从模板侧栏拖入预置环节
- 自动保存:编辑器 debounce 3s 无操作后触发自动保存(写 `lesson_plans.content` + `lastSavedAt`,不生成版本)
- 手动保存Ctrl+S 或按钮 → 生成新版本(写 `lesson_plan_versions`isAuto=false
- 版本历史侧栏抽屉展示版本列表versionNo + label + 时间 + isAuto 标记),点击预览该版本 content"回退到此版本"= 用该版本 content 覆盖当前 + 生成新版本
### 5.2 版本策略
- 自动保存:只更新 `lesson_plans.content``lastSavedAt`**不**写 versions 表(避免版本爆炸)
- 手动保存:写一条 versions 记录isAuto=false
- 定时自动版本:每 30 分钟若有过改动,自动写一条 versions 记录isAuto=true防止教师长时间未手动保存丢失历史
- 版本上限:每 plan 保留最近 50 条 versions超出删除最旧的 isAuto=true 记录(手动版本永不被自动清理)
### 5.3 我的课案库
- 路由:`/teacher/lesson-plans`
- 列表展示:卡片网格,显示 title / 教材章节 / 学科年级 / 模板 / status / 最后保存时间
- 筛选:教材(级联章节)、学科、年级、状态、标签(标题关键词搜索)
- 操作编辑、复制生成副本title 加" - 副本"、删除软删除status=archived、发布status=published
---
## 6. P1知识点标注与关联
### 6.1 手动标注
- 在富文本类 block 内选中文本 → 弹出知识点选择器(从 `textbooks` 模块的章节-知识点树勾选)
- 选中的 knowledgePointId 写入该 block 的 `data.knowledgePointIds`
- block 渲染时在关联的知识点旁显示标签 chip
### 6.2 AI 推荐轻量P1 不做完整 AI 课案生成)
- 编辑器顶部"AI 推荐知识点"按钮 → 读取当前课案所有 block 的纯文本 → 调用 `shared/lib/ai.createAiChatCompletion` → 返回推荐 knowledgePointId 列表
- 教师在弹窗中勾选确认 → 合并到对应 block 的 `knowledgePointIds`
- AI 仅做"推荐候选",不自动写入;知识点池来自教材已有知识点(不创建新知识点)
### 6.3 知识点-课案映射查询data-access 暴露)
- `getLessonPlansByKnowledgePoint(knowledgePointId)`:反查哪些课案重点讲解了某知识点(供后续学情分析/教材知识点树反查使用P1 仅实现 data-access 函数,不做 UI
---
## 7. P1题目创建 / 拉取 / 同步题库
### 7.1 从题库拉取source=bank
- exercise block 侧栏:题库搜索器(按知识点 / 题型 / 难度筛选,调用 `questions/data-access.getQuestions`
- 选中题目 → 插入 exercise.itemssource=bank, questionId=真实 ID
- 仅引用,不复制题目内容;渲染时按 questionId 查询展示
### 7.2 课案内新建题目source=inline
- exercise block 内"新建题目"按钮 → 弹出题目编辑器(复用 `questions/components/create-question-dialog` 的表单逻辑)
- 编辑期:题目暂存为 inline draft完整内容存入 `exercise.items[].inlineContent`(结构与 questions 表字段对齐),`questionId` 为占位 `inline_draft_${cuid}`
- 保存课案时inline 题目**不立即入库**,保持 draft 状态inlineContent 随课案 content 一起持久化)
- 发布作业时(见 §8inline 题目先入库(调用 `questions/data-access.createQuestionWithRelations`,入参取自 inlineContent用真实 questionId 替换占位 ID回写到课案 content
### 7.3 题目-课案关联查询
- `getLessonPlansByQuestion(questionId)`:反查某题在哪些课案的哪个 exercise block 被使用data-access 函数P1 仅实现,不做 UI
---
## 8. P1作业 / 考试发布打通(复用 exam 中转)
### 8.1 发布流程
```
教师点击 exercise blockpurpose=after_class_homework的"发布作业"按钮
[Action] publishLessonPlanHomeworkAction
├─ requirePermission(LESSON_PLAN_PUBLISH)
├─ 1. inline 题目入库
│ └─ 遍历 exercise.items对 source=inline 的调用
│ questions/data-access.createQuestionWithRelations
authorId=教师,关联 knowledgePointIds
│ └─ 用真实 questionId 替换课案 content 中的占位 ID
│ └─ 更新 lesson_plans.content
├─ 2. 打包成 exam 草稿
│ └─ 调用 exams/data-access.persistExamDraft
title=课案标题+" - 作业"creatorId=教师,
│ sourceLessonPlanId=课案ID关联 textbookId/chapterId/subjectId/gradeId
│ examQuestions = exercise.items 映射)
│ └─ 得到 examId
├─ 3. 下发作业
│ └─ 调用 homework/data-access-write.createHomeworkAssignment
sourceExamId=examId, title, targets=班级学生列表,
│ availableAt, dueAt
│ └─ 得到 assignmentId
├─ 4. 记录溯源
│ └─ 在课案 content 的 exercise block.data 写入
│ publishedAssignmentId + publishedExamId + publishedAt
└─ revalidatePath("/teacher/lesson-plans") + revalidatePath("/teacher/homework")
```
### 8.2 溯源标记
- 课案 exercise block 渲染时:若 `data.publishedAssignmentId` 存在,显示"已发布为作业"徽章 + 跳转链接
- 作业侧P1 不改 homework 模块):作业的 sourceExamId → exam → 可查到 sourceLessonPlanIdexam 草稿创建时记录),实现"作业→课案"反查链路
- 学情报告P3通过 assignmentId → exam → lessonPlanId 回链到课案
### 8.3 发布前置校验
- exercise block 至少有 1 道题
- inline 题目必须填写完整content/type/difficulty/knowledgePointIds
- 教师必须对目标班级有 `class_taught` DataScope 权限
- 同一 exercise block 不可重复发布(已有 publishedAssignmentId 则禁用发布按钮,提供"重新发布为新作业"选项)
---
## 9. 模块文件结构
```
src/modules/lesson-preparation/
├─ actions.ts # Server Actions编排层
├─ data-access.ts # 课案 CRUD + 版本查询
├─ data-access-versions.ts # 版本快照写入 + 查询 + 回退
├─ data-access-templates.ts # 模板 CRUDsystem + personal
├─ data-access-knowledge.ts # 知识点-课案映射查询P1
├─ publish-service.ts # 发布编排inline 入库 → exam 草稿 → 作业下发)
├─ ai-suggest.ts # AI 知识点推荐P1 轻量)
├─ schema.ts # Zod 验证
├─ types.ts # 类型定义(含 Block 类型联合)
├─ constants.ts # 模板预设、block 类型枚举、状态常量
├─ seed.ts # 系统预设模板 seed 脚本
├─ hooks/
│ └─ use-lesson-plan-editor.ts # 编辑器状态管理(自动保存/版本/拖拽)
└─ components/
├─ lesson-plan-list.tsx # 我的课案库列表
├─ lesson-plan-card.tsx # 课案卡片
├─ lesson-plan-filters.tsx # 筛选器
├─ lesson-plan-editor.tsx # 编辑器主壳block 列表容器)
├─ block-renderer.tsx # block 分发渲染
├─ blocks/
│ ├─ rich-text-block.tsx # 富文本类 block 编辑器
│ ├─ text-study-block.tsx # 文本研习画布 block
│ ├─ exercise-block.tsx # 练习/作业 block
│ └─ reflection-block.tsx # 教学反思P3 预留P1 简单渲染)
├─ template-picker.tsx # 模板选择器
├─ version-history-drawer.tsx # 版本历史抽屉
├─ knowledge-point-picker.tsx # 知识点选择器(复用 textbooks 组件)
├─ question-bank-picker.tsx # 题库拉取侧栏(复用 questions 组件)
├─ inline-question-editor.tsx # 课案内新建题目(复用 questions 表单)
└─ publish-homework-dialog.tsx # 发布作业弹窗(选班级/时间)
```
---
## 10. Server Actions 清单
所有 Action 遵循项目规范:`requirePermission()` → Zod 校验 → 调用 data-access → `revalidatePath` → 返回 `ActionState<T>`
| Action | 权限点 | 用途 |
|--------|--------|------|
| `getLessonPlansAction` | LESSON_PLAN_READ | 我的课案库列表(含筛选) |
| `getLessonPlanByIdAction` | LESSON_PLAN_READ | 获取单个课案含权限校验creator 或 published |
| `createLessonPlanAction` | LESSON_PLAN_CREATE | 创建课案(选模板 → 生成初始 content |
| `updateLessonPlanAction` | LESSON_PLAN_UPDATE | 更新课案(自动保存,不生成版本) |
| `saveLessonPlanVersionAction` | LESSON_PLAN_UPDATE | 手动保存生成版本 |
| `revertLessonPlanVersionAction` | LESSON_PLAN_UPDATE | 回退到指定版本(生成新版本) |
| `getLessonPlanVersionsAction` | LESSON_PLAN_READ | 获取版本列表 |
| `deleteLessonPlanAction` | LESSON_PLAN_DELETE | 删除课案软删除status=archived |
| `duplicateLessonPlanAction` | LESSON_PLAN_CREATE | 复制课案 |
| `getLessonPlanTemplatesAction` | LESSON_PLAN_READ | 获取模板列表system + 我的 personal |
| `saveAsTemplateAction` | LESSON_PLAN_CREATE | 另存为我的模板 |
| `deleteTemplateAction` | LESSON_PLAN_DELETE | 删除 personal 模板 |
| `suggestKnowledgePointsAction` | LESSON_PLAN_READ + AI_CHAT | AI 推荐知识点(只读返回候选,不写入课案;教师确认后另调 updateLessonPlanAction |
| `publishLessonPlanHomeworkAction` | LESSON_PLAN_PUBLISH + HOMEWORK_CREATE | 发布作业§8 编排) |
---
## 11. data-access 清单
| 函数 | 用途 |
|------|------|
| `getLessonPlans(params & scope)` | 课案列表DataScope 过滤) |
| `getLessonPlanById(id, scope)` | 单课案详情 |
| `createLessonPlan(input)` | 创建(含模板初始化 content |
| `updateLessonPlanContent(id, content, userId)` | 更新 content + lastSavedAt自动保存 |
| `softDeleteLessonPlan(id, userId)` | status=archived |
| `duplicateLessonPlan(id, userId)` | 复制 |
| `getLessonPlanVersions(planId)` | 版本列表 |
| `createLessonPlanVersion(planId, content, userId, isAuto, label?)` | 写版本快照 |
| `revertToVersion(planId, versionNo, userId)` | 用版本 content 覆盖当前 + 生成新版本 |
| `pruneAutoVersions(planId, keep=50)` | 清理超出上限的自动版本 |
| `getLessonPlanTemplates(userId)` | system + 该用户 personal |
| `createPersonalTemplate(input, userId)` | 创建 personal 模板 |
| `deletePersonalTemplate(id, userId)` | 删除(仅 owner |
| `getLessonPlansByKnowledgePoint(kpId)` | 知识点反查课案P1 data-access only |
| `getLessonPlansByQuestion(questionId)` | 题目反查课案P1 data-access only |
---
## 12. 权限点(新增 5 个)
`src/shared/types/permissions.ts` 新增:
```typescript
// Lesson Plan (备课)
LESSON_PLAN_CREATE: "lesson_plan:create",
LESSON_PLAN_READ: "lesson_plan:read",
LESSON_PLAN_UPDATE: "lesson_plan:update",
LESSON_PLAN_DELETE: "lesson_plan:delete",
LESSON_PLAN_PUBLISH: "lesson_plan:publish",
```
`src/shared/lib/permissions.ts``ROLE_PERMISSIONS` 映射中:
- `teacher`:全部 5 个
- `admin`:全部 5 个
- `student`/`parent`/其他:无
---
## 13. 路由
| 路由 | 页面 | 权限 |
|------|------|------|
| `/teacher/lesson-plans` | 我的课案库列表 | LESSON_PLAN_READ |
| `/teacher/lesson-plans/new` | 新建课案(选模板) | LESSON_PLAN_CREATE |
| `/teacher/lesson-plans/[planId]/edit` | 课案编辑器 | LESSON_PLAN_UPDATEcreator或 LESSON_PLAN_READpublished 只读) |
侧边栏导航:在 `layout/config/navigation.ts` 的 teacher 角色菜单新增"备课"项。
---
## 14. DataScope 接入
- `getLessonPlans` 接受 `scope` 参数:
- `teacher`/`admin`type=all 或 class_taught返回自己创建的 + 公开 published 的
- 其他角色:仅 published 的
- `getLessonPlanById`creator 可看自己的 draft非 creator 仅当 status=published 可看
- 写操作update/delete/publish仅 creatorDataScope 不适用,直接校验 `creatorId === userId`
---
## 15. 架构图同步计划
按项目规则"改码必同步图",实现完成后需更新:
### 15.1 `docs/architecture/004_architecture_impact_map.md`
- §1.1 分层架构图modules 行新增 `lesson-preparation`
- §1.2 模块依赖关系图:新增 `lesson-preparation` 节点,标注对 textbooks/questions/exams/homework/classes/files 的合理依赖(───▶ data-access
- 第二部分新增 §2.27 lesson-preparation 模块清单(职责/导出函数/依赖/文件清单)
- 附录 A 依赖矩阵新增一行一列
### 15.2 `docs/architecture/005_architecture_data.json`
- `modules.lesson_preparation`:完整模块节点
- `dbTables`:新增 `lesson_plans` / `lesson_plan_versions` / `lesson_plan_templates`
- `permissions`:新增 5 个权限点
- `routes`:新增 3 个路由
- `dependencyMatrix`:新增依赖关系
### 15.3 `src/shared/db/schema.ts`
新增 3 张表定义(按现有分节风格,加在合适 section。schema.ts 当前 1111 行已超 1000 硬上限P0 已知问题),新增 3 表会加剧;建议本次新增时一并按业务域拆分 schema.ts但拆分属独立任务不在本 spec 范围,仅在备注中提示)。
---
## 16. 实施分阶段计划
### P0 地基(先做)
1. 新增 3 张表 schema + 迁移
2. 新增 5 个权限点 + 角色映射
3. seed 系统预设模板4+1 套)
4. data-access + data-access-versions + data-access-templates
5. 基础 CRUD Actionscreate/get/update/delete/duplicate
6. 版本管理 Actionssave version / revert / list
7. 模板 Actionslist / save as / delete
8. 我的课案库列表页 + 筛选
9. Block 编辑器主壳 + 富文本类 block + 拖拽排序 + 自动保存
10. 版本历史抽屉
11. 模板选择器(新建课案入口)
12. 路由 + 侧边栏导航
13. 同步架构图 004/005
### P1 联动P0 完成后)
14. `text_study` block设计稿画布形态
15. `exercise` block + 题库拉取侧栏source=bank
16. `exercise` block + 课案内新建题目source=inlinedraft 暂存)
17. 知识点选择器 + block 内 knowledgePointIds 标注
18. AI 知识点推荐 Action + 编辑器入口
19. publish-serviceinline 入库 → exam 草稿 → 作业下发)
20. 发布作业弹窗(选班级/时间)
21. 溯源标记渲染(已发布徽章 + 跳转)
22. data-access-knowledge反查函数无 UI
23. 同步架构图(若 P1 新增了导出函数)
---
## 17. 验收标准
### P0 验收
- [ ] 教师可创建课案(选教材/章节/模板)
- [ ] 5 套系统预设模板可选,选用后生成对应 block 骨架
- [ ] Block 编辑器:增删改 block、拖拽排序、富文本编辑
- [ ] 自动保存3s debounce+ 手动保存生成版本
- [ ] 版本历史:列表、预览、回退
- [ ] 我的课案库:列表、筛选、复制、删除
- [ ] 权限校验:非 creator 无法编辑 draft
- [ ] `npm run lint` + `npx tsc --noEmit` 零错误
- [ ] 架构图 004/005 已同步
### P1 验收
- [ ] exercise block 可从题库拉取题目并展示
- [ ] exercise block 可课案内新建题目draft 暂存)
- [ ] 富文本 block 可标注知识点(选择器 + chip 展示)
- [ ] AI 推荐知识点按钮可用,推荐结果可勾选确认
- [ ] exercise blockpurpose=after_class_homework可发布为作业
- [ ] 发布后 inline 题目已入库,课案 content 中占位 ID 已替换
- [ ] 发布后作业可通过 sourceExamId → exam → sourceLessonPlanId 反查课案
- [ ] 已发布 exercise block 显示溯源徽章
- [ ] 重复发布被拦截(提供"重新发布为新作业"
- [ ] `npm run lint` + `npx tsc --noEmit` 零错误
- [ ] 架构图已同步
---
## 18. 未覆盖范围P2/P3 预告,本次不实现)
以下功能在蓝图中提及,但**不在本 spec 范围**,留作后续独立 spec
### P2 协作
- 分享链接(密码/有效期)
- block 级批注线程(依赖本 spec 的稳定 blockId
- 采纳建议生成新版本
### P3 智能与回看
- AI 课案初稿生成(按模板结构填充)
- 环节级 AI 重写(选中 block → 指令 → 替换该 block
- 一致性检查(目标-活动-评价对齐)
- 系统内资源推荐(微课/课件/实验视频)
- 外部资源对接(国家中小学智慧教育平台 API
- AI 生成资源草稿(课件大纲/微课脚本/学案)
- 教学反思 block 完整 UI
- 学情回看(班级知识点掌握率/高频错题内嵌)
- AI 补救教学建议
- 知识点回写教材树(含审核流)
---
## 19. 风险与备注
| 风险 | 影响 | 缓解 |
|------|------|------|
| `schema.ts` 已 1111 行超 1000 硬上限,新增 3 表加剧 | 违反编码规范 | 本次新增时备注提示schema.ts 按业务域拆分作为独立任务跟进 |
| Block 编辑器复杂度高 | P0 工期风险 | 优先用成熟库(如 BlockNote/Plate二次封装不自研底层 |
| inline 题目发布时入库失败 | 数据不一致 | publish-service 用事务包裹;失败则回滚 exam 草稿创建,课案 content 不替换占位 ID |
| 自动保存频率高导致 versions 表膨胀 | 存储压力 | 自动保存不写 versions定时自动版本 30min 一次pruneAutoVersions 保留上限 50 |
| AI 推荐知识点依赖 AI Provider 配置 | 功能可用性 | AI 不可用时按钮置灰 + 提示"未配置 AI Provider";不阻塞主流程 |
---
> 本 spec 完成后,下一步进入 `writing-plans` skill 生成详细实施计划。