Files
NextEdu/docs/superpowers/specs/2026-06-22-knowledge-graph-design.md
SpecialX 58656da983 feat(textbooks): 知识图谱功能全面重构 — 前置依赖 + dagre 布局 + React Flow 可视化 + 师生双视角
将教材模块图谱从基本无用状态升级为完整知识图谱可视化系统。

数据层:新增 knowledgePointPrerequisites 表(复合主键+双外键 cascade);新增 data-access-graph.ts(server-only)知识点关联聚合、学生/班级掌握度查询;utils.ts 新增 hasCycleAfterAddingEdge(DFS 循环依赖检测)。

业务层:3 个新 Server Action(getKnowledgeGraphDataAction 三视图模式、createPrerequisiteAction 含循环检测、deletePrerequisiteAction);graph-layout.ts 重写为 dagre 分层有向图布局。

视图层:knowledge-graph.tsx 重写为 React Flow 主组件(全书视图+搜索高亮+关联节点高亮+章节着色);4 个新组件(graph-kp-node/graph-prerequisite-edge/graph-toolbar/graph-node-detail-panel);use-graph-data.ts 派生值模式避免 effect 中 setState。

架构:严格三层架构,客户端通过 Server Action 间接访问 server-only 数据层;权限校验+ i18n 全覆盖;架构文档 004/005 同步。

测试:utils.test.ts 新增 5 个循环检测测试,graph-layout.test.ts 重写 5 个 dagre 布局测试,全部 30 个教材模块单元测试通过。

附带提交 drizzle/0005 error-book 迁移文件以保持 journal 一致性。
2026-06-23 00:13:03 +08:00

13 KiB
Raw Blame History

知识图谱重构设计文档

  • 日期2026-06-22
  • 模块textbooks
  • 范围:知识图谱功能全面重构
  • 状态:已批准,待实现

1. 背景与动机

1.1 当前问题

教材模块的知识图谱功能(knowledge-graph.tsx)处于"基本无用"状态:

  • 静态 SVG 树状布局:仅 parentId 父子关系,无前置依赖
  • 仅显示当前章节:无法跨章节/全书查看知识体系
  • 交互单一:点击节点仅高亮正文,无缩放/平移/拖拽/键盘导航
  • 信息密度低:节点只有名称,无关联题目数、无掌握度
  • 无师生区分:教师和学生看到相同的图,无学情数据

1.2 同类平台调研

平台 核心设计 可借鉴点
人教数字教材 按学科/章节聚合的层级图,节点关联资源 跨章节聚合 + 资源关联
Khan Academy 力导向图,前置依赖边,节点显示掌握度 前置依赖 + 掌握度 + 跳转练习
学而思/猿辅导 学习路径图,红/黄/绿表示掌握度 掌握度色彩 + 路径推荐
ClassIn/Seewo 师生双视角,教师看班级整体 师生双视角
洋葱学院 章节→知识点→题目三级下钻 下钻交互 + 缩放

共性特征:① 跨章节/全书视图 ② 前置依赖关系 ③ 掌握度可视化 ④ 关联题目/资源 ⑤ 缩放平移 ⑥ 师生双视角

1.3 已有可复用数据

  • questionsToKnowledgePoints 关联表(题目↔知识点多对多)
  • knowledgePointMastery 表(学生掌握度,已被 diagnostic 模块填充)
  • questions 模块已支持按 knowledgePointId 筛选
  • @xyflow/reactReact Flow 12已在 lesson-preparation 模块使用

2. 设计目标

  1. 跨章节全书视图:支持单章节和全书两种范围切换
  2. 前置依赖关系:新增数据模型,支持声明任意知识点间的前置依赖
  3. 掌握度可视化:学生看个人,教师看班级,红/黄/绿/灰着色
  4. 关联题目预览:节点显示关联题目数,详情面板可跳转题目库
  5. 缩放平移交互React Flow 内置画布交互
  6. 师生双视角:同一组件,通过 prop 注入不同数据源
  7. 侧边栏详情面板:点击节点显示详情,不离开当前页面

3. 数据模型扩展

3.1 新增表knowledge_point_prerequisites

export const knowledgePointPrerequisites = mysqlTable("knowledge_point_prerequisites", {
  id: id("id").primaryKey(),
  knowledgePointId: varchar("knowledge_point_id", { length: 128 }).notNull()
    .references(() => knowledgePoints.id, { onDelete: "cascade" }),
  prerequisiteKpId: varchar("prerequisite_kp_id", { length: 128 }).notNull()
    .references(() => knowledgePoints.id, { onDelete: "cascade" }),
  createdAt: timestamp("created_at").defaultNow().notNull(),
}, (table) => ({
  kpPairPk: primaryKey({ columns: [table.knowledgePointId, table.prerequisiteKpId] }),
  kpIdx: index("kp_prereq_kp_idx").on(table.knowledgePointId),
  prereqIdx: index("kp_prereq_prereq_idx").on(table.prerequisiteKpId),
}))

设计说明

  • 多对多自关联表,表达"学习 KP_B 前应先掌握 KP_A"
  • knowledgePointId = 目标知识点,prerequisiteKpId = 前置知识点
  • 联合主键防止重复声明
  • 级联删除:知识点删除时自动清理关联
  • 循环依赖检测由 Server Action 层 DFS 校验,拒绝形成环的声明

3.2 不修改的表

  • knowledgePoints:已有 parentId(树归属)和 chapterId(章节归属),不变
  • knowledgePointMastery:已有 masteryLevel/totalQuestions/correctQuestions,不变
  • questionsToKnowledgePoints:不变

4. 架构与模块结构

4.1 新增文件清单

src/modules/textbooks/
├─ data-access-graph.ts              # 新增:图谱专用数据访问
├─ components/
│  ├─ knowledge-graph.tsx            # 重写React Flow 渲染器
│  ├─ graph-node-detail-panel.tsx    # 新增:节点详情侧边栏
│  ├─ graph-kp-node.tsx              # 新增React Flow 自定义节点
│  ├─ graph-prerequisite-edge.tsx    # 新增React Flow 自定义边
│  └─ graph-toolbar.tsx              # 新增:视图切换/筛选/搜索工具栏
└─ hooks/
   └─ use-graph-data.ts              # 新增:图谱数据加载与缓存 Hook

4.2 修改的文件

文件 修改内容
src/shared/db/schema.ts 新增 knowledgePointPrerequisites 表定义
data-access.ts 新增 prerequisite CRUD 函数
actions.ts 新增 3 个 Server Action
schema.ts 新增 prerequisite 声明的 Zod 校验
types.ts 新增 GraphNodeData / GraphViewMode / KpWithRelations 等类型
graph-layout.ts 重写:调用 dagre保留纯函数签名
components/textbook-reader.tsx 图谱 Tab 接入新组件
i18n/messages/zh-CN/textbooks.json 新增 graph.* 翻译键
i18n/messages/en/textbooks.json 新增 graph.* 翻译键

4.3 数据访问层data-access-graph.ts

// 全书知识点 + 前置依赖 + 关联题目数,一次查询聚合
export async function getKnowledgePointsWithRelations(
  textbookId: string
): Promise<KpWithRelations[]>

// 学生个人掌握度(按教材范围)
export async function getStudentKpMastery(
  studentId: string,
  textbookId: string
): Promise<Map<string, MasteryInfo>>

// 班级平均掌握度(教师视角)
export async function getClassKpMastery(
  teacherId: string,
  textbookId: string
): Promise<Map<string, MasteryInfo>>

// 单个知识点的前置列表
export async function getPrerequisitesForKp(
  kpId: string
): Promise<KnowledgePoint[]>

性能考量

  • 全书知识点通常 50-300 个,一次查询无压力
  • 关联题目数用子查询 COUNT 聚合,避免 N+1
  • 掌握度查询走索引(mastery_kp_idx + mastery_student_idx

4.4 Server Actionsactions.ts

// 图谱数据懒加载入口
export async function getKnowledgeGraphDataAction(
  textbookId: string,
  viewMode: GraphViewMode
): Promise<ActionState<KnowledgeGraphData>>

// 声明前置依赖(含循环检测)
export async function createPrerequisiteAction(
  input: CreatePrerequisiteInput
): Promise<ActionState<void>>

// 删除前置依赖
export async function deletePrerequisiteAction(
  input: DeletePrerequisiteInput
): Promise<ActionState<void>>

权限

  • getKnowledgeGraphDataActionrequirePermission(Permissions.TEXTBOOK_READ),掌握度数据按当前用户角色过滤
  • createPrerequisiteAction / deletePrerequisiteActionrequirePermission(Permissions.TEXTBOOK_UPDATE)

循环检测createPrerequisiteAction 中做 DFS若声明 A→B 后从 B 可达 A 则拒绝。

5. 图谱视图与交互

5.1 视图模式

模式 数据源 节点着色 适用角色
structure 全书知识点 + parentId + prerequisites 按章节分色 教师/学生
student-mastery + 学生个人掌握度 红(<60%)/黄(60-85%)/绿(>85%)/灰(未测) 学生
class-mastery + 班级平均掌握度 同上但聚合班级数据 教师

5.2 节点设计graph-kp-node.tsx

  • 矩形卡片,宽度 180px高度自适应
  • 内容:知识点名称 + 关联题目数徽章 + 掌握度进度条mastery 模式下)
  • 双击节点 → 打开右侧详情面板
  • 单击节点 → 高亮关联节点(前置+后置),其余节点降低透明度
  • 节点支持拖拽(位置不持久化,切换章节后重新布局)

5.3 边设计

  • parentId 关系:实线,无箭头(树归属)
  • prerequisite 关系:虚线 + 箭头(依赖方向)
  • 选中节点时:关联边高亮,其余边降低透明度

5.4 画布交互

  • React Flow 内置缩放滚轮、平移拖拽空白、小地图、键盘导航Tab/方向键)
  • 工具栏(graph-toolbar.tsx
    • 视图模式切换structure / student-mastery / class-mastery
      • 学生角色:仅显示 structure + student-mastery
      • 教师角色:显示 structure + class-mastery(教师看班级整体,不看个人)
    • 章节筛选(多选下拉,默认全选)
    • 关键词搜索(高亮匹配节点,非匹配节点降低透明度)
    • 重置视图按钮

5.5 详情面板graph-node-detail-panel.tsx

  • 知识点描述
  • 掌握度详情(个人/班级,含答题数/正确率)
  • 关联题目列表(前 5 条 + "查看全部"跳转题目库,带 ?kp=<id> 查询参数)
  • 前置知识点列表(可点击跳转)
  • 后置知识点列表(可点击跳转)
  • 教师/有权限者:编辑前置依赖入口(添加/删除前置)

6. 数据流与性能

6.1 数据加载策略

  • 图谱数据按教材维度一次性加载(全书知识点 + 依赖 + 题目数聚合)
  • 掌握度数据按 viewMode 懒加载:切换到 mastery 模式时才请求
  • 使用 Server Action getKnowledgeGraphDataAction(textbookId, viewMode) 统一入口
  • 客户端缓存:use-graph-data.tsuseState + useRef 防重复请求(复用 P2-3 模式)

6.2 布局计算

  • dagre 布局在客户端 useMemo 中执行
  • 知识点量级50-300下 dagre 计算时间 <10ms
  • 布局参数:rankdir=TB(从上到下)、nodesep=40ranksep=90

6.3 错误处理

  • 图谱数据加载失败 → 复用 TextbookSectionErrorBoundary
  • 掌握度数据缺失 → 节点显示"未测评"灰色状态,不阻断图谱渲染
  • 前置依赖循环检测 → Server Action 返回结构化错误,前端 toast 提示

7. 权限与 i18n

7.1 权限

  • TEXTBOOK_READ — 查看图谱
  • TEXTBOOK_UPDATE — 编辑前置依赖
  • 掌握度查看:学生只能看自己,教师看班级(由 data-access 层按 getCurrentStudentUser/getCurrentTeacherUser 过滤)

7.2 i18n 新增键

{
  "graph": {
    "viewMode": {
      "structure": "结构图",
      "studentMastery": "个人掌握度",
      "classMastery": "班级掌握度"
    },
    "node": {
      "questions": "题目",
      "mastery": "掌握度",
      "prerequisite": "前置",
      "successor": "后置"
    },
    "detail": {
      "title": "知识点详情",
      "noDescription": "暂无描述",
      "viewAllQuestions": "查看全部题目",
      "editPrerequisite": "编辑前置依赖",
      "addPrerequisite": "添加前置",
      "removePrerequisite": "移除",
      "noPrerequisites": "暂无前置知识点",
      "noSuccessors": "暂无后置知识点",
      "masteryNotAssessed": "未测评",
      "correctRate": "正确率"
    },
    "toolbar": {
      "search": "搜索知识点",
      "filterByChapter": "按章节筛选",
      "resetView": "重置视图"
    },
    "empty": {
      "noPrerequisites": "暂无前置依赖关系",
      "noData": "暂无图谱数据"
    },
    "error": {
      "cyclicDependency": "不能添加循环依赖",
      "loadFailed": "图谱加载失败"
    }
  }
}

8. 测试策略

8.1 单元测试

  • graph-layout.tsdagre 集成后布局正确性、空数据、循环容错
  • data-access-graph.ts:聚合查询正确性、权限过滤
  • 循环依赖检测DFS 算法单测

8.2 组件测试

  • graph-kp-node.tsx:节点渲染、掌握度进度条、徽章
  • graph-node-detail-panel.tsx:详情展示、前置/后置列表、跳转链接
  • graph-toolbar.tsx:视图切换、搜索、筛选

8.3 集成测试

  • 图谱数据加载 → 渲染 → 节点点击 → 详情面板
  • 视图模式切换 → 掌握度数据懒加载
  • 前置依赖 CRUD → 图谱边更新

9. 依赖变更

9.1 新增依赖

  • @dagrejs/dagre — 分层有向图布局算法(~50KB gzip

9.2 复用依赖

  • @xyflow/react(已在项目中使用)

10. 架构图同步

实现完成后需同步以下架构文档:

  • docs/architecture/004_architecture_impact_map.md — §2.5 教材模块章节
    • 更新文件清单(新增 6 个文件)
    • 更新导出函数(新增 4 个 data-access + 3 个 actions
    • 更新 knownIssues移除"P2 图谱方向键导航未实现"
  • docs/architecture/005_architecture_data.json — modules.textbooks 节点
    • 更新 exports、dbTables新增 knowledge_point_prerequisites、dependencyMatrix

11. 非目标YAGNI

以下功能不在本次范围内,后续迭代考虑:

  • 节点位置持久化(拖拽后保存布局)
  • 学习路径自动推荐
  • 关联视频/课件资源(当前仅关联题目)
  • 教材阅读进度跟踪
  • 学生笔记/标注
  • 教材导入/导出
  • 多版本教材对比