# 教材（Textbooks）模块审计报告 v2

> 审计日期：2026-06-22
> 审计范围：`src/modules/textbooks/**`、`src/app/(dashboard)/teacher/textbooks/**`、`src/app/(dashboard)/student/learning/textbooks/**`
> 对比基准：[textbooks-audit-report.md](./textbooks-audit-report.md)（v1）
> 参照规则：`docs/architecture/004_architecture_impact_map.md`、`docs/architecture/005_architecture_data.json`、`.trae/rules/project_rules.md`

---

## 一、v1 改进项完成状态总览

### 1.1 完成度统计

| 优先级 | 总数 | 已完成 | 部分完成 | 未完成 |
|--------|------|--------|----------|--------|
| P0 | 4 | 3 | 1（P0-3 i18n） | 0 |
| P1 | 8 | 7 | 1（P1-6 类型断言） | 0 |
| P2 | 6 | 4 | 1（P2-5 架构图同步） | 1（图谱方向键导航） |
| **合计** | **18** | **14** | **3** | **1** |

### 1.2 各项状态明细

| 编号 | 标题 | 状态 | 关键证据 |
|------|------|------|----------|
| P0-1 | 跨模块 UI 依赖解耦 | ✅ 已完成 | `knowledge-point-dialogs.tsx` 改为 render prop，页面层注入 |
| P0-2 | 前端权限硬编码 canEdit | ✅ 已完成 | `textbook-reader.tsx` 使用 `usePermission().hasPermission()` |
| P0-3 | 全模块 i18n 改造 | ⚠️ 部分完成 | 约 85%，`chapter-sidebar-list.tsx`/`actions.ts`/`section-error-boundary.tsx` 未接入 |
| P0-4 | Server Action 资源归属校验 | ✅ 已完成 | `actions.ts` 全部写 Action 调用 `verify*` 函数 |
| P1-1 | data-access 数据范围过滤 | ✅ 已完成 | `getTextbooksWithScope` + 学生端按年级过滤 |
| P1-2 | Error Boundary | ✅ 已完成 | 4 个 `error.tsx` + `section-error-boundary.tsx` |
| P1-3 | 消除重复组件 | ✅ 已完成 | 删除 `knowledge-point-panel.tsx` 和 `create-knowledge-point-dialog.tsx` |
| P1-4 | 抽取学科/年级配置 | ✅ 已完成 | `constants.ts` 集中管理 `SUBJECTS`/`GRADES`/`SUBJECT_COLORS` |
| P1-5 | 导出纯函数并补单测 | ✅ 已完成 | `utils.ts` + `graph-layout.ts` + 两个测试文件 |
| P1-6 | 修复类型断言 | ⚠️ 部分完成 | v1 的 3 处已修复，残留 3 处 `as string` |
| P1-7 | 图谱 a11y | ✅ 已完成 | `role="img"`/`aria-label`/`<title>`/`aria-pressed` |
| P1-8 | 统一删除确认 | ✅ 已完成 | `textbook-settings-dialog.tsx` 用 `AlertDialog` |
| P2-1 | 统一空状态 | ✅ 已完成 | 全部使用 `EmptyState` |
| P2-2 | 知识点高亮性能优化 | ✅ 已完成 | 单遍 alternation 正则 + `useMemo` |
| P2-3 | 知识点懒加载 | ✅ 已完成 | 按章节懒加载 + 缓存 + 派生加载状态 |
| P2-4 | 移动端阅读优化 | ✅ 已完成 | `Sheet` 抽屉 + 桌面端内联复用 |
| P2-5 | 架构图同步 | ⚠️ 部分完成 | 005 JSON 新函数已加，`knownIssues`/`uiDeps`/`components` 过期 |
| P2-6 | 埋点接口预留 | ✅ 已完成 | `analytics.tsx` 定义接口 + Provider + Hook |

---

## 二、v2 新发现的问题

### 2.1 i18n 完整性（P0，v1 遗留）

#### 问题 v2-1 ｜ `chapter-sidebar-list.tsx` 完全未接入 i18n（P0）

- **位置**：[chapter-sidebar-list.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/chapter-sidebar-list.tsx)
- **现象**：第 90 行 `"Toggle"`、第 118 行 `"Add Subchapter"`、第 130 行 `"Delete Chapter"`、第 258 行 `"Order updated"`、第 278 行 `"Cannot delete chapter with subchapters"`、第 332-341 行删除对话框文案全部硬编码英文
- **翻译键已存在**：`dialog.chapter.deleteTitle`/`delete`/`deleting`/`cannotDeleteWithSubchapters`/`addSubchapter` 等
- **影响**：中文用户看到英文文案，i18n 覆盖率不完整

#### 问题 v2-2 ｜ `actions.ts` 错误消息全部硬编码英文（P0）

- **位置**：[actions.ts](file:///e:/Desktop/CICD/src/modules/textbooks/actions.ts)
- **现象**：约 20+ 条消息硬编码，如第 47 行 `"Chapter does not belong to this textbook"`、第 56 行 `"Failed to reorder chapters"`
- **影响**：用户看到的 toast 消息无法本地化
- **建议**：Server Action 内使用 `getTranslations("textbooks.action")` 获取翻译

#### 问题 v2-3 ｜ `section-error-boundary.tsx` 默认文案硬编码中文（P1）

- **位置**：[section-error-boundary.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/section-error-boundary.tsx) 第 52/55/59 行
- **现象**：默认 fallback `"区块加载失败"` / `"请重试或刷新页面"` / `"重试"` 硬编码
- **影响**：英文用户看到中文默认值

### 2.2 学科/年级显示未本地化（P1）

#### 问题 v2-4 ｜ `textbook-card.tsx` 学科显示未本地化（P1）

- **位置**：[textbook-card.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-card.tsx) 第 41 行
- **现象**：`{textbook.subject}` 直接显示原始值（如 "Mathematics"），未通过 `t(\`subject.${labelKey}\`)` 转换

#### 问题 v2-5 ｜ 页面层学科/年级显示未本地化（P1）

- **位置**：
  - [teacher/textbooks/[id]/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/teacher/textbooks/[id]/page.tsx) 第 64、66 行
  - [student/learning/textbooks/[id]/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/student/learning/textbooks/[id]/page.tsx) 第 47、49 行
- **现象**：`{textbook.subject}` 和 `{textbook.grade}` 直接显示原始值

### 2.3 类型安全（P2）

#### 问题 v2-6 ｜ 残留 `as string` 断言（P2）

- **位置**：
  - [chapter-sidebar-list.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/chapter-sidebar-list.tsx) 第 226、257 行：`active.id as string`
  - [graph-layout.ts](file:///e:/Desktop/CICD/src/modules/textbooks/graph-layout.ts) 第 123 行：`kp.parentId as string`
- **建议**：用类型守卫或 narrowing 替代

### 2.4 重复代码（P2）

#### 问题 v2-7 ｜ `findParent` 与 `utils.ts` 的 `findChapterParent` 重复（P2）

- **位置**：[chapter-sidebar-list.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/chapter-sidebar-list.tsx) 第 215-224 行
- **现象**：内联 `findParent` 函数与 `utils.ts` 导出的 `findChapterParent` 功能完全相同
- **建议**：替换为 `import { findChapterParent } from "../utils"`

#### 问题 v2-8 ｜ 4 个 `error.tsx` 文件几乎完全相同（P2）

- **位置**：4 个 `error.tsx` 文件
- **现象**：内容完全一致（仅函数名不同）
- **建议**：抽取为共享组件 `TextbookRouteError`

#### 问题 v2-9 ｜ `student/learning/textbooks/page.tsx` 重复定义 `getParam`（P2）

- **位置**：[student/learning/textbooks/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/student/learning/textbooks/page.tsx) 第 13-18 行
- **现象**：本地定义 `getParam`，但 `@/shared/lib/search-params` 已导出
- **建议**：统一从 `@/shared/lib/search-params` 导入

### 2.5 a11y 改进（P2）

#### 问题 v2-10 ｜ 拖拽手柄无 `aria-label`（P2）

- **位置**：[chapter-sidebar-list.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/chapter-sidebar-list.tsx) 第 71-73 行
- **现象**：`<div {...attributes} {...listeners}>` 拖拽手柄仅含 `GripVertical` 图标，无 `aria-label`

#### 问题 v2-11 ｜ 知识点高亮 span 无可交互语义（P2）

- **位置**：[textbook-content-panel.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-content-panel.tsx) 第 133-148 行
- **现象**：高亮的知识点 `<span>` 仅 `data-kp-id` + `title`，无 `role="button"`/`aria-label`/`tabIndex`

#### 问题 v2-12 ｜ 移动端抽屉触发按钮无 `aria-expanded`（P2）

- **位置**：[textbook-reader.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-reader.tsx) 第 361-368 行
- **现象**：`<Button>` 未关联 `aria-expanded`/`aria-controls`

### 2.6 性能与状态管理（P2）

#### 问题 v2-13 ｜ `textbook-reader.tsx` textbookId 变化时未清理缓存（P2）

- **位置**：[textbook-reader.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-reader.tsx) 第 110-142 行
- **现象**：`requestedChaptersRef` 是 ref，当 textbookId 变化（用户切换教材）时不会清理，可能导致缓存命中错误章节的数据
- **建议**：在 `useEffect` 中增加 textbookId 变化时清理 `kpsByChapter` 和 `requestedChaptersRef`

#### 问题 v2-14 ｜ `TextbookContentPanel` 存在未使用的 props（P2）

- **位置**：[textbook-content-panel.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-content-panel.tsx) 第 23-46 行
- **现象**：`knowledgePoints`/`createDialogOpen`/`isCreating`/`onCreateKnowledgePoint` 4 个 props 在接口中定义但函数体内未解构使用
- **建议**：移除这 4 个 props 及对应的传参

### 2.7 架构图同步（P2，v1 遗留）

#### 问题 v2-15 ｜ 架构图 005 JSON 与 004 MD 同步不完整（P2）

- **005 JSON 未同步部分**：
  - `knownIssues` 数组仍列出所有 v1 的 P0/P1 问题为未解决
  - `uiDeps` 仍标注 "P0 待解耦"，但代码已通过 render prop 解耦
  - `components` 数组仍列出已删除的组件，遗漏新增的 `SectionErrorBoundary`
  - `hooks` 签名函数名简写与实际不一致
  - 遗漏 `analytics.tsx`/`constants.ts`/`utils.ts`/`graph-layout.ts` 等新文件
- **004 MD 未同步部分**：
  - §2.5 仍写 "⚠️ UI 层跨模块依赖（P0 待解耦）" — 已修复
  - "已知问题"列表未更新
  - 文件行数过期：`actions.ts` 317→377、`data-access.ts` 514→619、组件数 11→12

---

## 三、v2 改进优先级建议

### P0（紧急，i18n 完整性收尾）

1. **`chapter-sidebar-list.tsx` 接入 i18n**：替换所有硬编码英文为 `t(...)` 调用
2. **`actions.ts` 接入 i18n**：使用 `getTranslations("textbooks.action")` 替换硬编码消息
3. **`section-error-boundary.tsx` 默认文案 i18n**：默认值改为从 i18n 获取或使用翻译键

### P1（重要）

1. **学科/年级显示本地化**：`textbook-card.tsx`、`teacher/textbooks/[id]/page.tsx`、`student/learning/textbooks/[id]/page.tsx` 中 `{textbook.subject}`/`{textbook.grade}` 改为 `t(...)` 调用
2. **架构图同步**（P2-5 收尾）：更新 005 JSON 的 `knownIssues`/`uiDeps`/`components`/`hooks` 签名；更新 004 MD §2.5 的"已知问题"列表和文件清单行数
3. **移除 `TextbookContentPanel` 的 4 个未使用 props**

### P2（优化）

1. **类型断言清理**：`chapter-sidebar-list.tsx` 的 `as string`、`graph-layout.ts` 的 `as string`
2. **重复代码消除**：`findParent` 重复、4 个 error.tsx 重复、`getParam` 重复
3. **a11y 补全**：拖拽手柄 aria-label、高亮 span role/aria-label、移动端抽屉 aria-expanded
4. **`textbook-reader.tsx` textbookId 变化时清理缓存**
5. **`highlightKnowledgePoints` 补 Markdown 边界测试**

---

## 四、行业差距对比（v1 第三节中仍未完成的项目）

以下 v1 报告中"行业差距对比"的项目在 v2 中仍未实现，作为长期路线图保留：

| 差距项 | 优先级 | 说明 |
|--------|--------|------|
| 富媒体嵌入（图片/音频/视频/公式/3D） | 长期 | 仍仅 Markdown + RichTextEditor |
| 公式编辑（LaTeX/MathML） | 长期 | 无 |
| 翻阅式阅读（页码/书签/进度记忆） | 长期 | 仍滚动 + URL chapterId |
| 朗读/TTS | 长期 | 无 |
| 笔记/划线/高亮/书签 | 长期 | 仅有"选区创建知识点" |
| 知识图谱缩放/拖拽/力导向 | 长期 | 仍静态 SVG 树状布局 |
| 知识点多级层级/跨章节关联/前置后置依赖 | 长期 | 仅 parentId 树 + chapterId 归属 |
| admin 多教师协作编辑 + 版本历史 | 长期 | 无版本管理 |
| parent 角色教材查看 | 长期 | 无 parent 入口 |
| 章节跨级拖拽移动 | 长期 | reorderChapters 仅支持同级排序 |
| 全文搜索（标题+正文+知识点） | 长期 | 仅列表页按 title/subject/grade/publisher 模糊搜索 |
| 阅读进度条/章节完成度 | 长期 | 无 |
| 知识点难度标注/教师标注重点 | 长期 | 仅有 level 字段，无 UI 录入 |

---

## 五、总结

### 关键成果（v1 → v2）

1. **架构解耦**：P0-1 跨模块 UI 依赖通过 render prop 完全解耦
2. **权限安全**：P0-2 前端权限接入 `usePermission`，P0-4 Server Action 资源归属校验全覆盖，P1-1 学生端数据范围过滤
3. **可维护性**：P1-3 重复组件删除，P1-4 配置集中化，P1-5 纯函数抽离 + 单测
4. **用户体验**：P1-2 Error Boundary 全覆盖，P1-8 删除确认统一，P2-1 空状态统一，P2-2 高亮性能优化，P2-3 懒加载，P2-4 移动端抽屉
5. **可扩展性**：P2-6 埋点接口预留

### 主要遗留（v2 需解决）

1. **i18n 完整性**：`chapter-sidebar-list.tsx`、`actions.ts`、`section-error-boundary.tsx` 三处未接入，学科/年级显示未本地化
2. **架构图同步**：005 JSON 的 `knownIssues`/`uiDeps`/`components` 过期，004 MD §2.5 已知问题未更新
3. **类型断言**：3 处 `as string` 可改善
4. **重复代码**：`findParent`/`error.tsx`/`getParam` 三处重复
5. **a11y**：拖拽手柄 aria-label、高亮 span 可交互性、移动端抽屉 aria-expanded
6. **未使用 props**：`TextbookContentPanel` 的 4 个 props
