# 知识图谱重构设计文档 - **日期**:2026-06-22 - **模块**:textbooks - **范围**:知识图谱功能全面重构 - **状态**:已批准,待实现 ## 1. 背景与动机 ### 1.1 当前问题 教材模块的知识图谱功能([knowledge-graph.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/knowledge-graph.tsx))处于"基本无用"状态: - **静态 SVG 树状布局**:仅 `parentId` 父子关系,无前置依赖 - **仅显示当前章节**:无法跨章节/全书查看知识体系 - **交互单一**:点击节点仅高亮正文,无缩放/平移/拖拽/键盘导航 - **信息密度低**:节点只有名称,无关联题目数、无掌握度 - **无师生区分**:教师和学生看到相同的图,无学情数据 ### 1.2 同类平台调研 | 平台 | 核心设计 | 可借鉴点 | |------|---------|---------| | 人教数字教材 | 按学科/章节聚合的层级图,节点关联资源 | 跨章节聚合 + 资源关联 | | Khan Academy | 力导向图,前置依赖边,节点显示掌握度 | 前置依赖 + 掌握度 + 跳转练习 | | 学而思/猿辅导 | 学习路径图,红/黄/绿表示掌握度 | 掌握度色彩 + 路径推荐 | | ClassIn/Seewo | 师生双视角,教师看班级整体 | 师生双视角 | | 洋葱学院 | 章节→知识点→题目三级下钻 | 下钻交互 + 缩放 | **共性特征**:① 跨章节/全书视图 ② 前置依赖关系 ③ 掌握度可视化 ④ 关联题目/资源 ⑤ 缩放平移 ⑥ 师生双视角 ### 1.3 已有可复用数据 - `questionsToKnowledgePoints` 关联表(题目↔知识点多对多) - `knowledgePointMastery` 表(学生掌握度,已被 `diagnostic` 模块填充) - `questions` 模块已支持按 `knowledgePointId` 筛选 - `@xyflow/react`(React Flow 12)已在 `lesson-preparation` 模块使用 ## 2. 设计目标 1. **跨章节全书视图**:支持单章节和全书两种范围切换 2. **前置依赖关系**:新增数据模型,支持声明任意知识点间的前置依赖 3. **掌握度可视化**:学生看个人,教师看班级,红/黄/绿/灰着色 4. **关联题目预览**:节点显示关联题目数,详情面板可跳转题目库 5. **缩放平移交互**:React Flow 内置画布交互 6. **师生双视角**:同一组件,通过 prop 注入不同数据源 7. **侧边栏详情面板**:点击节点显示详情,不离开当前页面 ## 3. 数据模型扩展 ### 3.1 新增表:knowledge_point_prerequisites ```typescript 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) ```typescript // 全书知识点 + 前置依赖 + 关联题目数,一次查询聚合 export async function getKnowledgePointsWithRelations( textbookId: string ): Promise // 学生个人掌握度(按教材范围) export async function getStudentKpMastery( studentId: string, textbookId: string ): Promise> // 班级平均掌握度(教师视角) export async function getClassKpMastery( teacherId: string, textbookId: string ): Promise> // 单个知识点的前置列表 export async function getPrerequisitesForKp( kpId: string ): Promise ``` **性能考量**: - 全书知识点通常 50-300 个,一次查询无压力 - 关联题目数用子查询 `COUNT` 聚合,避免 N+1 - 掌握度查询走索引(`mastery_kp_idx` + `mastery_student_idx`) ### 4.4 Server Actions(actions.ts) ```typescript // 图谱数据懒加载入口 export async function getKnowledgeGraphDataAction( textbookId: string, viewMode: GraphViewMode ): Promise> // 声明前置依赖(含循环检测) export async function createPrerequisiteAction( input: CreatePrerequisiteInput ): Promise> // 删除前置依赖 export async function deletePrerequisiteAction( input: DeletePrerequisiteInput ): Promise> ``` **权限**: - `getKnowledgeGraphDataAction`:`requirePermission(Permissions.TEXTBOOK_READ)`,掌握度数据按当前用户角色过滤 - `createPrerequisiteAction` / `deletePrerequisiteAction`:`requirePermission(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=` 查询参数) - 前置知识点列表(可点击跳转) - 后置知识点列表(可点击跳转) - 教师/有权限者:编辑前置依赖入口(添加/删除前置) ## 6. 数据流与性能 ### 6.1 数据加载策略 - 图谱数据按教材维度一次性加载(全书知识点 + 依赖 + 题目数聚合) - 掌握度数据按 viewMode 懒加载:切换到 mastery 模式时才请求 - 使用 Server Action `getKnowledgeGraphDataAction(textbookId, viewMode)` 统一入口 - 客户端缓存:`use-graph-data.ts` 用 `useState` + `useRef` 防重复请求(复用 P2-3 模式) ### 6.2 布局计算 - dagre 布局在客户端 `useMemo` 中执行 - 知识点量级(50-300)下 dagre 计算时间 <10ms - 布局参数:`rankdir=TB`(从上到下)、`nodesep=40`、`ranksep=90` ### 6.3 错误处理 - 图谱数据加载失败 → 复用 `TextbookSectionErrorBoundary` - 掌握度数据缺失 → 节点显示"未测评"灰色状态,不阻断图谱渲染 - 前置依赖循环检测 → Server Action 返回结构化错误,前端 toast 提示 ## 7. 权限与 i18n ### 7.1 权限 - `TEXTBOOK_READ` — 查看图谱 - `TEXTBOOK_UPDATE` — 编辑前置依赖 - 掌握度查看:学生只能看自己,教师看班级(由 data-access 层按 `getCurrentStudentUser`/`getCurrentTeacherUser` 过滤) ### 7.2 i18n 新增键 ```json { "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.ts`:dagre 集成后布局正确性、空数据、循环容错 - `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) 以下功能不在本次范围内,后续迭代考虑: - 节点位置持久化(拖拽后保存布局) - 学习路径自动推荐 - 关联视频/课件资源(当前仅关联题目) - 教材阅读进度跟踪 - 学生笔记/标注 - 教材导入/导出 - 多版本教材对比