# 教材(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 测试、性能压测、安全渗透测试(建议后续补充)