# 教材（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 行
- **现象**：章节项点击区是 `<div onClick>`，缺少 `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 行 `<Link href={`/teacher/questions?kp=${kp.id}`}>`
  - [textbook-reader.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-reader.tsx) 第 378 行 `<Link href={`/teacher/lesson-plans/new?...`}>`
  - [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<T>`"
- **后果**：类型推导不透明

### 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<GraphLayoutNodeData>` 泛型替代双重断言；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 测试、性能压测、安全渗透测试（建议后续补充）
