# 教材(Textbooks)模块审计报告 v3 > 审计日期:2026-06-24 > 审计范围:`src/modules/textbooks/**`、`src/app/(dashboard)/teacher/textbooks/**`、`src/app/(dashboard)/student/learning/textbooks/**` > 对比基准:[textbooks-audit-report-v2.md](./textbooks-audit-report-v2.md)(v2) > 参照规则:`docs/architecture/004_architecture_impact_map.md`、`docs/architecture/005_architecture_data.json`、`.trae/rules/project_rules.md` --- ## 一、现有实现概要 ### 1.1 模块定位 教材模块是 K12 系统的"标杆模块"(架构图 004 第 2.5 节明确标注),承担**教材与知识体系管理**职责: - 教材/章节树形结构 CRUD - 知识点 CRUD + Markdown 内容编辑 - 知识图谱可视化(React Flow + dagre 布局) - 知识点前置依赖管理(含循环检测) - 学生/班级掌握度聚合查询 ### 1.2 文件分布与规模 | 层 | 文件数 | 总行数 | 最大文件 | 备注 | |----|--------|--------|----------|------| | 模块核心(modules/textbooks) | 26 | ~3,800 | `data-access.ts` 662 行 | 含 5 个 hooks、16 个组件 | | App 层 - 教师 | 6 | ~350 | `[id]/page.tsx` 66 行 | 含 loading/error | | App 层 - 学生 | 6 | ~240 | `page.tsx` 86 行 | 含 loading/error | | 测试 | 2 | ~200 | `utils.test.ts` | 纯函数单测 | ### 1.3 数据流概览 ``` ┌─────────────────────────────────────────────────────────────────┐ │ App 层(RSC) │ │ teacher/textbooks/page.tsx ──┐ │ │ student/learning/textbooks/page.tsx ──┤ │ └────────────────────────────────┼─────────────────────────────────┘ │ 直接调用 data-access(读) ▼ ┌─────────────────────────────────────────────────────────────────┐ │ modules/textbooks │ │ ┌──────────────┐ ┌──────────────┐ ┌────────────────────┐ │ │ │ actions.ts │──▶│ data-access.ts│──▶│ data-access-graph.ts│ │ │ │ (14 Actions) │ │ (662 行) │ │ (207 行, 图谱只读) │ │ │ └──────────────┘ └──────────────┘ └────────────────────┘ │ │ │ │ │ │ │ │ ▼ ▼ │ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ │ utils.ts │ │ graph-layout │ │ │ │ │ (225 行纯函数)│ │ (121 行纯函数)│ │ │ │ └──────────────┘ └──────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ components/(16 个组件)+ hooks/(5 个 Hook) │ │ │ │ textbook-reader / knowledge-graph / chapter-sidebar-list │ │ │ └──────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ │ ▼ 跨模块依赖(通过 data-access,合规) ┌─────────────────────────────────────────────────────────────────┐ │ users.data-access(getCurrentStudentUser) │ │ classes.data-access-students(getClassStudents) │ │ school.data-access(getGradeNameById) │ └─────────────────────────────────────────────────────────────────┘ ``` ### 1.4 架构图记录完整性评估 | 架构图节点 | 记录状态 | 差异 | |------------|----------|------| | 004 § 2.5 textbooks | ✅ 完整 | 导出函数、依赖、已知问题、文件清单均已记录 | | 005 textbooks 节点 | ✅ 完整 | actions/dataAccess/hooks/types/components/files 均已记录 | | 已知问题记录 | ⚠️ 部分过期 | 005 `knownIssues` 仍写"i18n 覆盖率约 98%",实际审计发现残留硬编码英文异常文案、a11y 缺失等问题未记录 | **结论**:架构图对教材模块的覆盖度约 90%,主要差距在 `knownIssues` 字段未同步最新审计发现。 --- ## 二、现存问题与原因分析 ### 2.1 安全与权限维度(P0) #### 问题 T-AUDIT-01 | 学生端页面缺少 `requirePermission` 显式校验 - **位置**:[student/learning/textbooks/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/student/learning/textbooks/page.tsx)、[student/learning/textbooks/[id]/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/student/learning/textbooks/[id]/page.tsx) - **现象**:两个学生端页面均未调用 `requirePermission(Permissions.TEXTBOOK_READ)`,仅靠 `getCurrentStudentUser()` 隐式校验角色。对比教师端 [teacher/textbooks/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/teacher/textbooks/page.tsx) 第 18 行显式调用 `requirePermission`。 - **违反规则**:`project_rules.md` → "Server Action 规范" → "每个 Action 必须调用 `requirePermission()` 进行权限校验";以及"安全规范" → "所有敏感数据查询必须在 data-access 层结合当前用户权限过滤,Server Action 二次校验" - **原因**:学生端页面是 RSC 直接调用 data-access(非 Server Action),开发者认为 `getCurrentStudentUser` 已隐含角色校验,但权限点 `TEXTBOOK_READ` 未被显式校验,无法通过权限矩阵灵活调整 - **后果**:若未来权限矩阵调整(如临时禁止某学生查看教材),学生端无法响应;权限审计日志缺失"教材阅读"事件 #### 问题 T-AUDIT-02 | `getKnowledgeGraphDataAction` 缺少数据范围过滤 - **位置**:[actions.ts](file:///e:/Desktop/CICD/src/modules/textbooks/actions.ts) 第 373-413 行 - **现象**:`getKnowledgeGraphDataAction` 仅校验 `TEXTBOOK_READ` 权限,未按学生年级做 scope 过滤(对比 `getTextbooksWithScope` 有 scope.grade 过滤)。学生可传任意 `textbookId` 查看跨年级教材的图谱数据 - **违反规则**:`project_rules.md` → "安全规范" → "所有敏感数据查询必须在 data-access 层结合当前用户权限过滤" - **原因**:图谱查询是 Task 7 新增功能,开发时复用了教师端逻辑,未区分学生数据范围 - **后果**:学生可越权查看非本年级教材的知识图谱与掌握度数据 #### 问题 T-AUDIT-03 | `class-mastery` 视图缺少教师-班级归属校验 - **位置**:[actions.ts](file:///e:/Desktop/CICD/src/modules/textbooks/actions.ts) 第 393-403 行 - **现象**:`getKnowledgeGraphDataAction` 的 `class-mastery` 分支调用 `getClassStudents({ status: "active" })` 获取全量班级学生 ID,未校验当前教师是否拥有这些班级 - **违反规则**:`project_rules.md` → "安全规范" → "Server Action 二次校验" - **原因**:直接调用 classes 模块的 `getClassStudents` 未传入教师 scope - **后果**:任何持有 `TEXTBOOK_READ` 权限的教师可查看全校所有班级的掌握度数据 ### 2.2 数据完整性维度(P0) #### 问题 T-AUDIT-04 | `deleteChapter` 未清理孤儿前置依赖记录 - **位置**:[data-access.ts](file:///e:/Desktop/CICD/src/modules/textbooks/data-access.ts) 第 259-298 行 - **现象**:递归删除章节及其知识点时,未删除 `knowledgePointPrerequisites` 表中引用被删知识点的边(包括作为 `prerequisiteKpId` 的记录) - **违反规则**:数据完整性最佳实践 - **原因**:删除逻辑只关注了 `knowledgePoints` 表本身,遗漏了外键关联表 - **后果**:图谱渲染时出现指向已删除知识点的孤儿边,循环检测算法 `hasCycleAfterAddingEdge` 可能因引用不存在节点而异常 ### 2.3 类型安全维度(P1) #### 问题 T-AUDIT-05 | `as` 断言违反规范 - **位置**: - [data-access.ts](file:///e:/Desktop/CICD/src/modules/textbooks/data-access.ts) 第 661 行 `as [string, string]` - [knowledge-point-dialogs.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/knowledge-point-dialogs.tsx) 第 84 行 `as (formData: FormData) => void` - [graph-kp-node.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/graph-kp-node.tsx) 第 35、37 行 `as unknown as GraphLayoutNodeData`(双重断言,缺注释) - **违反规则**:`project_rules.md` → "TypeScript 规则" → "禁止 `as` 断言(除非从 `unknown` 转换或测试中,需注释原因)" - **原因**:React Flow 的 `NodeProps` 类型与自定义数据结构不匹配,开发者用 `as unknown as` 绕过类型检查 - **后果**:类型安全被削弱,运行时可能因数据形状不符而崩溃 #### 问题 T-AUDIT-06 | 类型与 DB Schema 不一致 - **位置**:[types.ts](file:///e:/Desktop/CICD/src/modules/textbooks/types.ts) - **现象**: - 第 42 行 `KnowledgePoint.chapterId?: string`(可选),但 DB schema 中该字段非空 - 第 13 行 `Textbook.grade: string | null`(可空),但 `CreateTextbookSchema.grade` 要求 `min(1)` 非空 - **违反规则**:`project_rules.md` → "TypeScript 规则" → "禁止 `any`" - **原因**:类型手动定义而非从 Drizzle schema 推断(types.ts 第 1-3 行注释承认) - **后果**:下游需大量 `?? undefined` 兜底(data-access.ts 第 320、347 行),类型契约不可信 ### 2.4 错误处理维度(P0/P1) #### 问题 T-AUDIT-07 | 前置依赖操作无 try/catch,loading 状态卡死 - **位置**:[knowledge-graph.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/knowledge-graph.tsx) 第 202-219 行(`handleAddPrerequisite`)、第 222-235 行(`handleRemovePrerequisite`) - **现象**:两个函数均无 try/catch,若 action 抛异常,`setIsSavingPrereq(false)` 不会执行 - **违反规则**:`project_rules.md` → "错误处理" 隐含要求 - **原因**:开发者假设 Server Action 不会抛异常(实际网络异常、服务端 500 均会抛) - **后果**:用户点击"添加前置依赖"失败后,按钮永久 loading,需刷新页面 #### 问题 T-AUDIT-08 | `textbook-reader.tsx` `onCreateKnowledgePoint` 无 try/catch - **位置**:[textbook-reader.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-reader.tsx) 第 177-181 行 - **现象**:`onCreateKnowledgePoint` 回调中调用 `handleCreateKnowledgePoint`,无 try/catch,异常时 `setIsCreating(false)` 不执行 - **后果**:创建知识点失败时,UI 永久显示创建中状态 #### 问题 T-AUDIT-09 | `section-error-boundary.tsx` 缺 `componentDidCatch` - **位置**:[section-error-boundary.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/section-error-boundary.tsx) - **现象**:仅实现 `getDerivedStateFromError`,未实现 `componentDidCatch`,错误被吞没未上报 - **违反规则**:React Error Boundary 最佳实践 - **后果**:生产环境无法追踪章节内容渲染错误 #### 问题 T-AUDIT-10 | 4 个 error.tsx 完全重复且未记录日志 - **位置**:4 个 error.tsx 文件(教师/学生 × 列表/详情) - **现象**:内容完全相同(仅函数名不同),`error` 参数接收但未 `console.error` 记录、未展示 `error.digest`、未区分错误类型 - **违反规则**:DRY 原则、Next.js error.tsx 最佳实践 - **后果**:错误无法追踪;4 份重复代码维护成本高 ### 2.5 国际化维度(P1) #### 问题 T-AUDIT-11 | data-access 层硬编码英文异常文案 - **位置**:[data-access.ts](file:///e:/Desktop/CICD/src/modules/textbooks/data-access.ts) 第 188、244、383 行 - **现象**:`throw new Error("Textbook not found")` / `"Chapter not found"` 等英文硬编码 - **违反规则**:`project_rules.md` → "所有用户可见文本必须适配 i18n" - **原因**:data-access 层无法直接调用 `getTranslations`(需 async context) - **后果**:错误冒泡到 `handleActionError` 后,非英文用户看到英文错误信息 #### 问题 T-AUDIT-12 | `use-graph-data.ts` 错误状态值类型不一致 - **位置**:[use-graph-data.ts](file:///e:/Desktop/CICD/src/modules/textbooks/hooks/use-graph-data.ts) 第 60、66 行 - **现象**:`error` 状态有时是服务端已翻译文案(`result.message`),有时是原始 i18n key 字符串(`"graph.error.loadFailed"`),有时是 JS Error.message - **后果**:下游组件无法判断该直接显示还是再 `t()` 翻译 ### 2.6 可访问性维度(P1) #### 问题 T-AUDIT-13 | 章节项可点击 div 无键盘支持 - **位置**:[chapter-sidebar-list.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/chapter-sidebar-list.tsx) 第 100-110 行 - **现象**:章节项点击区是 `
`,缺少 `role="button"`、`tabIndex={0}`、`onKeyDown` - **违反规则**:WCAG 2.1 AA → 键盘可达性 - **后果**:键盘用户无法选中章节 #### 问题 T-AUDIT-14 | 图标按钮缺 `aria-label` - **位置**:[graph-node-detail-panel.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/graph-node-detail-panel.tsx) 第 45、139-146 行 - **现象**:关闭按钮(X 图标)、删除前置按钮(Trash2 图标)无 `aria-label` - **后果**:屏幕阅读器用户无法识别按钮用途 #### 问题 T-AUDIT-15 | loading.tsx 缺少 ARIA 标记 - **位置**:4 个 loading.tsx 文件 - **现象**:根容器缺少 `role="status"` 和 `aria-busy="true"` - **后果**:屏幕阅读器无法感知加载状态 #### 问题 T-AUDIT-16 | error.tsx 缺少 `role="alert"` - **位置**:4 个 error.tsx 文件 - **现象**:错误容器缺少 `role="alert"`,屏幕阅读器不会自动播报错误 ### 2.7 跨模块耦合维度(P1) #### 问题 T-AUDIT-17 | 跨模块 URL 硬编码 - **位置**: - [graph-node-detail-panel.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/graph-node-detail-panel.tsx) 第 101 行 `` - [textbook-reader.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-reader.tsx) 第 378 行 `` - [textbook-card.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-card.tsx) 第 47 行 `hrefBase` 默认值 `/teacher/textbooks` - [textbook-settings-dialog.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-settings-dialog.tsx) 第 80 行 `router.push("/teacher/textbooks")` - **现象**:组件内硬编码角色前缀 URL(`/teacher/...`),无法复用于学生/家长端 - **违反规则**:`project_rules.md` → "前端组件禁止使用 `role === "xxx"` 硬编码"(URL 硬编码角色前缀同理) - **原因**:组件未通过 props 注入路由前缀 - **后果**:组件无法跨角色复用;新增角色需修改组件源码 ### 2.8 代码质量维度(P2) #### 问题 T-AUDIT-18 | `use-kp-crud.ts` 行数超限 - **位置**:[hooks/use-kp-crud.ts](file:///e:/Desktop/CICD/src/modules/textbooks/hooks/use-kp-crud.ts) 122 行 - **现象**:超过 Hook 上限 80 行 - **违反规则**:`project_rules.md` → "自定义 Hook:建议 ≤ 80 行" - **原因**:CRUD 操作集中在一个 Hook - **后果**:维护困难 #### 问题 T-AUDIT-19 | 表单字段代码重复 - **位置**:[textbook-form-dialog.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-form-dialog.tsx) 与 [textbook-settings-dialog.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-settings-dialog.tsx) - **现象**:表单字段(title/subject/grade/publisher)几乎完全相同,应抽取共享 `TextbookFormFields` 组件 - **违反规则**:DRY 原则 - **后果**:字段变更需同步修改两处 #### 问题 T-AUDIT-20 | 死代码 - **位置**: - [data-access.ts](file:///e:/Desktop/CICD/src/modules/textbooks/data-access.ts) `verifyKnowledgePointBelongsToChapter`(全项目未使用) - [schema.ts](file:///e:/Desktop/CICD/src/modules/textbooks/schema.ts) `ReorderChaptersSchema`(未使用) - [textbook-reader.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-reader.tsx) `canEdit` deprecated prop - **违反规则**:代码整洁 - **后果**:增加维护负担 #### 问题 T-AUDIT-21 | `textbook-content-panel.tsx` props 膨胀 - **位置**:[textbook-content-panel.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-content-panel.tsx) 17 个 props - **现象**:props 过多,违反组合优先原则 - **违反规则**:`project_rules.md` → "组合优先" - **原因**:编辑态、内容态、选区态未分离 - **后果**:组件难以复用和测试 #### 问题 T-AUDIT-22 | 函数返回类型未显式标注 - **位置**:`use-knowledge-point-actions.ts`、`use-kp-dialog-state.ts`、`use-text-selection.ts`、`use-graph-data.ts`、`constants.ts`(`getSubjectColor` 等) - **违反规则**:`project_rules.md` → "函数返回值必须显式标注,特别是 `Promise`" - **后果**:类型推导不透明 ### 2.9 性能与可测试性维度(P2) #### 问题 T-AUDIT-23 | `textbook-reader.tsx` 命令式 DOM 操作 - **位置**:[textbook-reader.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-reader.tsx) 第 229-235 行 - **现象**:`document.querySelector(\`[data-kp-id="${highlightedKpId}"]\`)` + `el.classList.add/remove` - **风险**:模板字符串选择器存在选择器注入风险;命令式 DOM 操作与 React 数据流冲突 - **后果**:难以测试、潜在 XSS #### 问题 T-AUDIT-24 | `graph-layout.ts` O(n²) 性能 - **位置**:[graph-layout.ts](file:///e:/Desktop/CICD/src/modules/textbooks/graph-layout.ts) 第 56、64、89、103 行 - **现象**:多次 `knowledgePoints.some(...)` 线性查找 - **后果**:大图谱(>100 节点)性能差 #### 问题 T-AUDIT-25 | `cache()` 包裹影响可测试性 - **位置**:[data-access.ts](file:///e:/Desktop/CICD/src/modules/textbooks/data-access.ts)、[data-access-graph.ts](file:///e:/Desktop/CICD/src/modules/textbooks/data-access-graph.ts) - **现象**:所有查询用 `cache()` 包裹,测试时需处理 React cache 上下文 - **后果**:单元测试需额外 mock --- ## 三、行业差距对比 基于对 PowerSchool、Veracross、钉钉教育、智学网、班级小管家、ClassIn、智慧树等 K12 产品的调研,教材模块相比行业优秀实践存在以下差距: ### 3.1 功能差距 | 差距项 | 行业实践 | 当前实现 | 影响角色 | |--------|----------|----------|----------| | **多角色视图缺失** | admin 全校教材管理、parent 查看子女教材 | 仅 teacher/student 两端 | admin、parent | | **教材版本管理** | 支持多版本教材对比、历史版本回滚 | 无版本概念 | teacher、admin | | **协作编辑** | 多教师协作编辑同一教材(锁机制/CRDT) | 单人编辑,无锁 | teacher | | **离线阅读** | 移动端缓存章节内容离线访问 | 无离线缓存 | student | | **PDF/打印导出** | 章节导出为 PDF、打印友好版式 | 无导出功能 | teacher、student | | **阅读进度追踪** | 记录学生阅读进度、停留时长 | 无进度追踪 | student、teacher | | **教材资源附件** | 章节关联视频、音频、PDF 附件 | 仅 Markdown 文本 | teacher、student | | **知识点掌握度联动** | 图谱节点点击跳转至相关题目/作业练习 | 仅展示掌握度,无跳转练习入口 | student | | **教材评论/批注** | 教师可在章节内添加批注,学生可见 | 无批注功能 | teacher、student | | **智能推荐** | 基于掌握度推荐复习路径 | `analytics.tsx` 仅预留接口,未实现 | student | ### 3.2 UI/UX 差距 | 差距项 | 行业实践 | 当前实现 | 影响 | |--------|----------|----------|------| | **骨架屏布局不匹配** | 骨架屏与实际页面结构一致 | [id]/loading.tsx 渲染 grid+sidebar,实际是阅读器布局 | 用户体验割裂 | | **空状态引导** | 空状态提供"创建教材"CTA 按钮 | 仅文字提示 | 转化率低 | | **筛选持久化** | 筛选条件持久化到 URL(nuqs) | 部分持久化,不一致 | 刷新丢失筛选 | | **移动端阅读体验** | 上下滑动阅读、字号调节、夜间模式 | 有 Sheet 抽屉,无字号/主题调节 | 阅读体验差 | | **图谱交互** | 节点拖拽、缩放、小地图、方向键导航 | 有拖拽/缩放,缺方向键导航 | 键盘用户不可达 | ### 3.3 架构差距 | 差距项 | 行业实践 | 当前实现 | 影响 | |--------|----------|----------|------| | **配置驱动** | 角色配置决定渲染哪些 Widget | 组件内硬编码角色 URL | 新增角色需改组件 | | **依赖注入** | 通过 Context 注入数据服务 | 组件直接调用 data-access | 难以 mock 测试 | | **错误恢复** | 错误边界提供重试按钮 + 自动上报 | 仅 router.refresh,无上报 | 错误不可追踪 | | **监控埋点** | 关键操作(创建/删除/阅读)埋点 | `analytics.tsx` 仅 stub | 无行为数据 | --- ## 四、改进优先级建议 ### 4.1 P0(紧急,安全与数据完整性) | 编号 | 问题 | 改进方向 | 影响范围 | |------|------|----------|----------| | T-FIX-01 | T-AUDIT-01 学生端缺 requirePermission | 学生端两个页面添加 `requirePermission(Permissions.TEXTBOOK_READ)` | 2 文件 | | T-FIX-02 | T-AUDIT-02 图谱缺 scope 过滤 | `getKnowledgeGraphDataAction` 学生端按年级 scope 过滤 textbookId | actions.ts | | T-FIX-03 | T-AUDIT-03 class-mastery 缺教师-班级校验 | 传入教师 scope,校验班级归属 | actions.ts | | T-FIX-04 | T-AUDIT-04 deleteChapter 孤儿记录 | 级联删除 `knowledgePointPrerequisites` 表相关记录 | data-access.ts | | T-FIX-05 | T-AUDIT-07/08 前置依赖/创建知识点无 try/catch | 补 try/catch/finally,finally 中复位 loading | knowledge-graph.tsx、textbook-reader.tsx | | T-FIX-06 | T-AUDIT-09 Error Boundary 缺 componentDidCatch | 添加 `componentDidCatch` 记录错误 | section-error-boundary.tsx | ### 4.2 P1(重要,规范合规) | 编号 | 问题 | 改进方向 | 影响范围 | |------|------|----------|----------| | T-FIX-07 | T-AUDIT-05 as 断言 | 用 `NodeProps` 泛型替代双重断言;data-access 用 `satisfies` | 3 文件 | | T-FIX-08 | T-AUDIT-06 类型与 DB Schema 不一致 | chapterId 改为必填;grade 与 schema 对齐 | types.ts | | T-FIX-09 | T-AUDIT-10 error.tsx 重复 | 抽取 `shared/components/error-boundary.tsx` 共享组件 | 4 文件 → 1 共享 | | T-FIX-10 | T-AUDIT-11 data-access 硬编码英文 | 改用错误码,actions 层翻译 | data-access.ts | | T-FIX-11 | T-AUDIT-12 错误状态值类型不一致 | 统一为 i18n key 或错误码 | use-graph-data.ts | | T-FIX-12 | T-AUDIT-13 章节项无键盘支持 | 添加 `role="button"`/`tabIndex`/`onKeyDown` | chapter-sidebar-list.tsx | | T-FIX-13 | T-AUDIT-14 图标按钮缺 aria-label | 添加 `aria-label` | graph-node-detail-panel.tsx | | T-FIX-14 | T-AUDIT-15/16 loading/error 缺 ARIA | 添加 `role="status"`/`aria-busy`/`role="alert"` | 8 文件 | | T-FIX-15 | T-AUDIT-17 跨模块 URL 硬编码 | 通过 props 注入 `hrefBase`,移除角色前缀 | 4 文件 | | T-FIX-16 | T-AUDIT-18 use-kp-crud 行数超限 | 按 create/update/delete 拆为 3 个子 Hook | use-kp-crud.ts | ### 4.3 P2(优化,代码质量) | 编号 | 问题 | 改进方向 | 影响范围 | |------|------|----------|----------| | T-FIX-17 | T-AUDIT-19 表单字段重复 | 抽取 `TextbookFormFields` 共享组件 | 2 文件 | | T-FIX-18 | T-AUDIT-20 死代码 | 删除 `verifyKnowledgePointBelongsToChapter`、`ReorderChaptersSchema`、`canEdit` prop | 3 文件 | | T-FIX-19 | T-AUDIT-21 props 膨胀 | 拆分为编辑态/内容态/选区态 Context | textbook-content-panel.tsx | | T-FIX-20 | T-AUDIT-22 返回类型未标注 | 显式标注所有 Hook/工具函数返回类型 | 5 文件 | | T-FIX-21 | T-AUDIT-23 命令式 DOM 操作 | 改用 React 状态驱动高亮 | textbook-reader.tsx | | T-FIX-22 | T-AUDIT-24 O(n²) 性能 | 预建 Set 替代 `some()` | graph-layout.ts | | T-FIX-23 | T-AUDIT-25 cache() 可测试性 | 导出非 cache 版本供测试 | data-access.ts | ### 4.4 中长期计划(P3,功能增强) | 编号 | 功能 | 优先级 | 说明 | |------|------|--------|------| | T-FEAT-01 | admin/parent 端教材视图 | 中 | 复用现有组件,新增配置驱动 | | T-FEAT-02 | 教材版本管理 | 低 | 新增 `textbook_versions` 表 | | T-FEAT-03 | PDF/打印导出 | 中 | 服务端生成 PDF | | T-FEAT-04 | 阅读进度追踪 | 中 | 新增 `reading_progress` 表 | | T-FEAT-05 | 教材资源附件 | 中 | 章节关联 files 模块 | | T-FEAT-06 | 智能推荐学习路径 | 低 | 接入 AI 模块 | | T-FEAT-07 | 图谱方向键导航 | 低 | React Flow 键盘交互 | | T-FEAT-08 | 监控埋点接入 | 中 | 实现 `analytics.tsx` 真实逻辑 | --- ## 五、架构图同步说明 ### 5.1 需要更新的节点 | 文档 | 节点 | 修改内容 | |------|------|----------| | 004 § 2.5 | 已知问题 | 新增 v3 审计发现:学生端缺 requirePermission、图谱缺 scope 过滤、deleteChapter 孤儿记录、error.tsx 重复、a11y 缺失等 | | 004 § 2.5 | 文件清单 | 更新行数(如 use-kp-crud 拆分后行数变化) | | 005 textbooks.knownIssues | knownIssues | 同步上述新发现 | | 005 textbooks.files | files | 同步行数变化 | ### 5.2 无需修改的部分 - 导出函数清单:本次审计未发现新增/删除/重命名导出函数 - 依赖关系:未新增跨模块依赖 - 权限点:未新增权限点 - 数据库表:未新增表(中长期计划中的新表待实施时再同步) --- ## 附录:审计方法说明 - **审计基准**:2026-06-24 代码库快照 - **审计工具**:人工阅读全部 26 个模块文件 + 13 个 app 层文件 - **覆盖维度**:安全/权限、数据完整性、类型安全、错误处理、i18n、a11y、跨模块耦合、代码质量、性能、可测试性 - **未覆盖**:E2E 测试、性能压测、安全渗透测试(建议后续补充)