From 2548f70f403c58d09fbbb0137f0c1278d3abac83 Mon Sep 17 00:00:00 2001 From: SpecialX <47072643+wangxiner55@users.noreply.github.com> Date: Mon, 22 Jun 2026 15:38:26 +0800 Subject: [PATCH] =?UTF-8?q?docs(textbooks):=20=E6=96=B0=E5=A2=9E=E6=95=99?= =?UTF-8?q?=E6=9D=90=E6=A8=A1=E5=9D=97=E5=AE=A1=E8=AE=A1=E6=8A=A5=E5=91=8A?= =?UTF-8?q?=E5=B9=B6=E5=90=8C=E6=AD=A5=E6=9E=B6=E6=9E=84=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 docs/architecture/audit/textbooks-audit-report.md,覆盖三层架构、权限、i18n、类型安全、错误边界、组件复用、a11y、可测试性、性能、安全等维度的审计,并给出 P0/P1/P2 改进优先级与重构方案要点 - 同步 004 架构影响地图 §2.5:修正 actions/data-access 行数与导出函数名(移除不存在的读 Action,补充 reorderChaptersAction),补充跨模块 UI 依赖、已知问题清单 - 同步 005 架构数据 JSON:补充 getKnowledgePointOptions 跨模块接口、uiDeps、knownIssues、auditReport 字段,修正 getTextbooks/getTextbookById 的 usedBy 以包含学生端页面 --- .../004_architecture_impact_map.md | 39 +- docs/architecture/005_architecture_data.json | 38 +- .../audit/textbooks-audit-report.md | 510 ++++++++++++++++++ 3 files changed, 575 insertions(+), 12 deletions(-) create mode 100644 docs/architecture/audit/textbooks-audit-report.md diff --git a/docs/architecture/004_architecture_impact_map.md b/docs/architecture/004_architecture_impact_map.md index 66f448e..a738dda 100644 --- a/docs/architecture/004_architecture_impact_map.md +++ b/docs/architecture/004_architecture_impact_map.md @@ -625,32 +625,44 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions" --- -## 2.5 textbooks(教材模块)— 标杆模块 +## 2.5 textbooks(教材模块)— 标杆模块(data-access 层) **职责**:教材与知识体系管理(教材/章节树形结构、知识点 CRUD、Markdown 内容编辑、知识图谱)。 **导出函数**: -- Actions:`getTextbooksAction` / `getTextbookByIdAction` / `createTextbookAction` / `updateTextbookAction` / `deleteTextbookAction` / `getChaptersAction` / `createChapterAction` / `updateChapterAction` / `deleteChapterAction` / `getKnowledgePointsAction` / `createKnowledgePointAction` / `updateKnowledgePointAction` / `deleteKnowledgePointAction` -- Data-access:与 actions 一一对应的 data-access 函数 +- Actions(10 个,均为写操作;读操作由 RSC 页面直接调用 data-access):`createTextbookAction` / `updateTextbookAction` / `deleteTextbookAction` / `createChapterAction` / `updateChapterContentAction` / `deleteChapterAction` / `reorderChaptersAction` / `createKnowledgePointAction` / `updateKnowledgePointAction` / `deleteKnowledgePointAction` +- Data-access:`getTextbooks` / `getTextbookById` / `getChaptersByTextbookId` / `getKnowledgePointsByChapterId` / `getKnowledgePointsByTextbookId` / `createTextbook` / `updateTextbook` / `deleteTextbook` / `createChapter` / `updateChapterContent` / `deleteChapter` / `createKnowledgePoint` / `updateKnowledgePoint` / `deleteKnowledgePoint` / `reorderChapters` / `getTextbooksDashboardStats` / `getKnowledgePointOptions`(跨模块接口,供 questions 调用) **依赖关系**: - 依赖:`shared/*`、`@/auth` - 被依赖:`questions`(✅ P1-1 已修复:通过 textbooks data-access)、`exams`(通过类型)、`dashboard`(通过 data-access,P0-4 已修复) +- ⚠️ UI 层跨模块依赖:`textbooks/components/knowledge-point-dialogs.tsx` 直接 import `questions/components/create-question-dialog`(P0 待解耦,详见 [textbooks-audit-report.md](audit/textbooks-audit-report.md)) **已知问题**: -- ✅ 无跨模块 DB 访问 +- ✅ 无跨模块 DB 访问(data-access 层) - ✅ actions 层编排模式标杆(权限校验 → 调用 data-access → revalidatePath) - ✅ data-access 层职责单一 - ✅ P2 已修复:`data-access.ts` 中 `byId.get(pid)!.children.push` 非空断言清理为安全守卫;`or(...)!` 非空断言清理为条件 push +- ⚠️ P0 跨模块 UI 依赖:`knowledge-point-dialogs.tsx` 直接 import questions 模块组件 +- ⚠️ P0 前端权限硬编码:`canEdit={true}` 按路由写死,未用 `usePermission().hasPermission()` +- ⚠️ P0 全模块零 i18n:中英文文案硬编码,未接入 next-intl +- ⚠️ P1 Server Action 未校验资源归属(chapterId 是否属于 textbookId) +- ⚠️ P1 data-access 缺数据范围过滤(学生端未按年级过滤) +- ⚠️ P1 缺 Error Boundary(无 error.tsx) +- ⚠️ P1 知识点列表/弹窗存在重复实现(knowledge-point-panel.tsx 无调用方) +- ⚠️ P1 学科/年级选项硬编码三处且彼此不一致 +- ⚠️ P1 纯逻辑未导出,零单测 **文件清单**: | 文件 | 行数 | 职责 | |------|------|------| -| `actions.ts` | 276 | 13 个 Server Action(标杆) | -| `data-access.ts` | 428 | 教材/章节/知识点 CRUD | -| `types.ts` | 79 | 类型定义 | +| `actions.ts` | 317 | 10 个 Server Action(写操作) | +| `data-access.ts` | 514 | 教材/章节/知识点 CRUD + 跨模块查询接口 | +| `types.ts` | 45 | 类型定义 | +| `schema.ts` | 64 | Zod 校验 | | `hooks/use-knowledge-point-actions.ts` | 121 | 知识点操作 Hook | -| `components/*` | 12 文件 | 教材编辑/知识图谱组件 | +| `hooks/use-text-selection.ts` | 57 | 文本选区捕获 Hook | +| `components/*` | 11 文件 | 教材编辑/知识图谱组件 | --- @@ -1068,11 +1080,13 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions" **导出函数**: - Data-access:`getChildren` / `getChildBasicInfo` / `getChildDashboardData` / `getParentDashboardData` / `verifyParentChildRelation` / `getChildNameList`(✅ v4 新增:用于详情页头部多子女切换器,一次批量查询避免 N+1) -- Components:`ParentDashboard` / `ChildCard` / `ChildDetailHeader` / `ChildDetailPanel` / `SiblingSwitcher` / `ChildHomeworkSummary` / `ChildGradeSummary` / `ChildScheduleCard` / `ParentChildrenDataPage` / `ParentNoChildrenPage` / `ParentAttentionBanner`(v4 新增)/ `ParentAttendanceWarning`(v4 新增)/ `ParentExportButton`(v4 新增) +- Components:`ParentDashboard` / `ChildCard` / `ChildDetailHeader` / `ChildDetailPanel` / `SiblingSwitcher` / `ChildHomeworkSummary` / `ChildHomeworkDetail`(v4 新增)/ `ChildGradeSummary` / `ChildGradeDetail`(v4 新增)/ `ChildScheduleCard` / `ParentChildrenDataPage` / `ParentNoChildrenPage` / `ParentAttentionBanner`(v4 新增)/ `ParentAttendanceWarning`(v4 新增)/ `ParentAttendanceRateCard`(v4 新增)/ `ParentAttendanceCalendar`(v4 新增)/ `ParentExportButton`(v4 新增) **v4 修复(产品/UX 维度)**: - ✅ FEAT-G01:新增 `/parent/leave` 请假申请占位页(含 loading.tsx) - ✅ FEAT-G02:详情页 Schedule Tab 支持完整周课表(新增 `weeklySchedule` 字段 + `ChildWeeklyScheduleItem` 类型 + `buildWeeklySchedule` 函数) +- ✅ FEAT-G03:详情页 Grades Tab 新增 `ChildGradeDetail` 按科目分组展示(平均分、趋势、最近成绩) +- ✅ FEAT-G04:详情页 Homework Tab 新增 `ChildHomeworkDetail` 展示完整作业信息(状态、截止、提交时间、尝试次数) - ✅ FEAT-G05:考勤页新增 `ParentAttendanceWarning` 异常预警横幅(聚合缺勤/迟到/低出勤率) - ✅ FEAT-G06:详情页底部新增"Contact Teacher"快捷入口 - ✅ FEAT-G07:详情页头部新增 `SiblingSwitcher` 多子女切换器 @@ -1083,12 +1097,18 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions" - ✅ LAYOUT-P05:详情页新增面包屑导航 - ✅ LAYOUT-P07:成绩趋势图 X 轴改用序号,避免日期重叠 - ✅ LAYOUT-P08:成绩页新增 `ParentExportButton` 导出按钮(占位) +- ✅ LAYOUT-P09:考勤页新增 `ParentAttendanceCalendar` 月历视图(按状态着色,支持按月切换) +- ✅ LAYOUT-P10:考勤异常高亮(与 FEAT-G05 同步实现) +- ✅ NAV-P02:Grades/Attendance 页面描述明确职责(多子女对比 vs 单子女详情) - ✅ NAV-P03:详情页实现 `?tab=` 参数支持 - ✅ NAV-P04:所有 parent 路由新增 `loading.tsx` 骨架屏 + `error.tsx` 错误边界 - ✅ DATA-P02:成绩卡片新增 TrendIcon 进步/退步/持平标识 - ✅ DATA-P03:排名展示新增"Top X%"百分比 - ✅ DATA-P04:作业列表新增科目标识 Badge +- ✅ DATA-P05:作业分数显示新增"pts"单位 +- ✅ DATA-P06:考勤页新增 `ParentAttendanceRateCard` 出勤率汇总卡片 - ✅ HABIT-P01:仪表盘"一眼定位异常"能力(AttentionBanner 聚合) +- ✅ HABIT-P02:待办横幅作业项直接跳转详情页 homework tab(1 次点击到达) - ✅ HABIT-P03:多子女切换无需返回仪表盘 - ✅ HABIT-P06:仪表盘展示未读/待办数量 - ✅ A11Y-P02:Overdue 状态增加 AlertTriangle 图标辅助 @@ -1096,6 +1116,7 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions" - ✅ PERF-P01/P02:骨架屏 + 错误边界 - ✅ PERF-P03:空状态新增"Contact support"引导按钮 - ✅ PERF-P04:`ChildCard` Link 添加 `prefetch` +- ✅ MOBILE-P03:移动端子女卡片改为水平滑动 Carousel(snap-x) - ✅ MOBILE-P04:作业/成绩列表项 `min-h-[44px]` 触摸区域 **依赖关系**: diff --git a/docs/architecture/005_architecture_data.json b/docs/architecture/005_architecture_data.json index e70172a..d90fcca 100644 --- a/docs/architecture/005_architecture_data.json +++ b/docs/architecture/005_architecture_data.json @@ -4076,14 +4076,16 @@ "name": "getTextbooks", "signature": "(query?, subject?, grade?) => Promise", "usedBy": [ - "teacher/textbooks/page.tsx" + "teacher/textbooks/page.tsx", + "student/learning/textbooks/page.tsx" ] }, { "name": "getTextbookById", "signature": "(id) => Promise", "usedBy": [ - "teacher/textbooks/[id]/page.tsx" + "teacher/textbooks/[id]/page.tsx", + "student/learning/textbooks/[id]/page.tsx" ] }, { @@ -4194,6 +4196,14 @@ "usedBy": [ "dashboard/data-access.getAdminDashboardData" ] + }, + { + "name": "getKnowledgePointOptions", + "signature": "() => Promise", + "purpose": "跨模块接口:获取所有知识点选项(含章节/教材信息),供 questions 模块调用", + "usedBy": [ + "questions/data-access.getKnowledgePointOptions" + ] } ], "hooks": [ @@ -4350,7 +4360,29 @@ "name": "TextbookSettingsDialog", "purpose": "教材设置对话框" } - ] + ], + "uiDeps": [ + { + "from": "textbooks/components/knowledge-point-dialogs.tsx", + "to": "questions/components/create-question-dialog", + "status": "P0 待解耦(直接 import 跨模块业务组件,应改为 props/Context 注入)", + "auditRef": "audit/textbooks-audit-report.md §2.1.1" + } + ], + "knownIssues": [ + "P0 跨模块 UI 依赖:knowledge-point-dialogs.tsx 直接 import questions 模块 CreateQuestionDialog", + "P0 前端权限硬编码:canEdit={true} 按路由写死,未用 usePermission().hasPermission()", + "P0 全模块零 i18n:中英文文案硬编码,未接入 next-intl", + "P1 Server Action 未校验资源归属(chapterId 是否属于 textbookId)", + "P1 data-access 缺数据范围过滤(学生端未按年级过滤)", + "P1 缺 Error Boundary(无 error.tsx)", + "P1 知识点列表/弹窗重复实现(knowledge-point-panel.tsx 无调用方)", + "P1 学科/年级选项硬编码三处且彼此不一致", + "P1 纯逻辑未导出,零单测", + "P2 类型断言:chapter-sidebar-list.tsx 用 ! 、knowledge-graph.tsx 用 as+!", + "P2 删除确认不一致:textbook-settings-dialog 用 confirm(),其余用 AlertDialog" + ], + "auditReport": "audit/textbooks-audit-report.md" } }, "classes": { diff --git a/docs/architecture/audit/textbooks-audit-report.md b/docs/architecture/audit/textbooks-audit-report.md new file mode 100644 index 0000000..6f10abc --- /dev/null +++ b/docs/architecture/audit/textbooks-audit-report.md @@ -0,0 +1,510 @@ +# 教材(Textbooks)模块审计报告 + +> 审计日期:2026-06-22 +> 审计范围:`src/modules/textbooks/**`、`src/app/(dashboard)/teacher/textbooks/**`、`src/app/(dashboard)/student/learning/textbooks/**` +> 参照规则:`docs/architecture/004_architecture_impact_map.md`、`docs/architecture/005_architecture_data.json`、`.trae/rules/project_rules.md` + +--- + +## 一、现有实现概要 + +### 1.1 文件分布 + +教材模块作为 K12 系统的"标杆模块"(架构图原文),文件分布如下: + +| 层 | 文件 | 行数 | 职责 | +|------|------|------|------| +| 数据访问 | [data-access.ts](file:///e:/Desktop/CICD/src/modules/textbooks/data-access.ts) | 514 | 教材/章节/知识点 CRUD + 跨模块查询接口 | +| Server Actions | [actions.ts](file:///e:/Desktop/CICD/src/modules/textbooks/actions.ts) | 317 | 13 个 Server Action(含权限校验) | +| 类型 | [types.ts](file:///e:/Desktop/CICD/src/modules/textbooks/types.ts) | 45 | Textbook / Chapter / KnowledgePoint 类型 | +| 校验 | [schema.ts](file:///e:/Desktop/CICD/src/modules/textbooks/schema.ts) | 64 | Zod 校验 schema | +| Hook | [hooks/use-knowledge-point-actions.ts](file:///e:/Desktop/CICD/src/modules/textbooks/hooks/use-knowledge-point-actions.ts) | 121 | 知识点增删改状态机 | +| Hook | [hooks/use-text-selection.ts](file:///e:/Desktop/CICD/src/modules/textbooks/hooks/use-text-selection.ts) | 57 | 文本选区捕获 | +| 组件 | [components/textbook-reader.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-reader.tsx) | 319 | 阅读器主壳(Tabs:目录/知识点/图谱) | +| 组件 | [components/textbook-content-panel.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-content-panel.tsx) | 170 | Markdown 渲染 + 编辑切换 | +| 组件 | [components/chapter-sidebar-list.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/chapter-sidebar-list.tsx) | 348 | 递归章节树 + 拖拽排序 | +| 组件 | [components/knowledge-point-list.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/knowledge-point-list.tsx) | 107 | 知识点列表 | +| 组件 | [components/knowledge-graph.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/knowledge-graph.tsx) | 181 | 知识图谱 SVG 可视化 | +| 组件 | [components/knowledge-point-panel.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/knowledge-point-panel.tsx) | 157 | 知识点面板(旧版,与 list 重叠) | +| 组件 | [components/knowledge-point-dialogs.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/knowledge-point-dialogs.tsx) | 148 | 创建/编辑知识点弹窗集合 | +| 组件 | [components/textbook-card.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-card.tsx) | 121 | 教材卡片 | +| 组件 | [components/textbook-filters.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-filters.tsx) | 71 | 筛选栏 | +| 组件 | [components/textbook-form-dialog.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-form-dialog.tsx) | 134 | 新建教材弹窗 | +| 组件 | [components/textbook-settings-dialog.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-settings-dialog.tsx) | 160 | 教材设置/删除弹窗 | +| 组件 | [components/create-chapter-dialog.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/create-chapter-dialog.tsx) | 95 | 新建章节弹窗 | +| 组件 | [components/create-knowledge-point-dialog.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/create-knowledge-point-dialog.tsx) | 95 | 新建知识点弹窗(旧版) | +| 页面 | [teacher/textbooks/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/teacher/textbooks/page.tsx) | 68 | 教师端列表页(RSC) | +| 页面 | [teacher/textbooks/[id]/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/teacher/textbooks/[id]/page.tsx) | 65 | 教师端详情页(RSC) | +| 页面 | [student/learning/textbooks/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/student/learning/textbooks/page.tsx) | 66 | 学生端列表页(RSC) | +| 页面 | [student/learning/textbooks/[id]/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/student/learning/textbooks/[id]/page.tsx) | 64 | 学生端详情页(RSC) | +| 骨架屏 | 4 个 `loading.tsx` | — | 列表/详情骨架屏 | + +### 1.2 数据流 + +``` +page.tsx (RSC) + └─ getTextbooks / getTextbookById / getChaptersByTextbookId / getKnowledgePointsByTextbookId (data-access) + └─ db (drizzle) → textbooks / chapters / knowledgePoints 表 + └─ (client) + ├─ → deleteChapterAction / reorderChaptersAction + ├─ → updateChapterContentAction + ├─ → useKnowledgePointActions → create/update/deleteKnowledgePointAction + └─ → ⚠️ 直接 import @/modules/questions/components/create-question-dialog +``` + +### 1.3 架构图记录完整性 + +经核对 [004_architecture_impact_map.md](file:///e:/Desktop/CICD/docs/architecture/004_architecture_impact_map.md) §2.5 与 [005_architecture_data.json](file:///e:/Desktop/CICD/docs/architecture/005_architecture_data.json),架构图对教材模块的记录**存在以下偏差**(详见第五节): + +- 行数统计过期:图记 `actions.ts 276 行 / data-access.ts 428 行`,实际为 `317 / 514`。 +- 导出函数名错误:图记 `getTextbooksAction / getTextbookByIdAction / getChaptersAction / getKnowledgePointsAction` 等"读 Action",实际不存在——读操作直接走 data-access(RSC),未包装成 Action。 +- 组件文件数:图记"12 文件",实际 11 个组件文件。 +- 未记录跨模块 UI 依赖:`knowledge-point-dialogs.tsx` 直接 import questions 模块的 `CreateQuestionDialog`,图未标注。 + +--- + +## 二、现存问题与原因分析 + +### 2.1 架构解耦 + +#### 问题 2.1.1 | 跨模块直接 import 业务组件(P0) + +- **位置**:[knowledge-point-dialogs.tsx#L16](file:///e:/Desktop/CICD/src/modules/textbooks/components/knowledge-point-dialogs.tsx#L16) +- **现象**:`import { CreateQuestionDialog } from "@/modules/questions/components/create-question-dialog"` +- **违反规则**:项目规则"该模块必须作为独立功能单元……模块内部组件绝不直接 import 其他业务模块的 actions 或 data-access(只能通过注入的接口调用)"以及"模块间只能通过对方 data-access 通信"。 +- **原因**:教材知识点页希望"一键创建相关题目",直接耦合了 questions 模块的弹窗组件,而非通过接口注入或事件回调。 +- **后果**:questions 模块任何对 `CreateQuestionDialog` props/位置的变更都会破坏教材模块编译;无法独立测试、独立部署教材模块;新增 admin/parent 角色时无法替换该弹窗实现。 + +#### 问题 2.1.2 | 前端权限硬编码 `canEdit`(P0) + +- **位置**: + - [teacher/textbooks/[id]/page.tsx#L60](file:///e:/Desktop/CICD/src/app/(dashboard)/teacher/textbooks/[id]/page.tsx#L60):`canEdit={true}` + - [student/learning/textbooks/[id]/page.tsx#L58](file:///e:/Desktop/CICD/src/app/(dashboard)/student/learning/textbooks/[id]/page.tsx#L58):未传 `canEdit`(默认 `false`) +- **违反规则**:项目规则"前端权限判断统一使用 `usePermission().hasPermission()`,严禁出现 `role === "xxx"` 硬编码"。此处虽未出现 `role ===`,但用"路由前缀"(teacher/student)隐式决定编辑权,本质等价于角色硬编码。 +- **原因**:图省事直接按路由写死布尔值,未接入权限上下文。 +- **后果**:一旦 admin 也需编辑教材、或 teacher 在某些场景被回收 `TEXTBOOK_UPDATE`,前端仍会展示编辑按钮,造成"按钮可见但点击 403"的体验;权限策略变更需改多处代码。 + +#### 问题 2.1.3 | data-access 缺少数据范围过滤(P1) + +- **位置**:[data-access.ts#L75](file:///e:/Desktop/CICD/src/modules/textbooks/data-access.ts#L75) `getTextbooks`、[#L125](file:///e:/Desktop/CICD/src/modules/textbooks/data-access.ts#L125) `getTextbookById` +- **现象**:查询未结合当前用户身份(年级、班级、学科权限)做过滤,任何能进入路由的用户都能读到全量教材。 +- **违反规则**:项目规则"所有敏感数据查询必须在 data-access 层结合当前用户权限过滤"。 +- **原因**:学生端页面虽调用 `getCurrentStudentUser()`,但拿到的 student 信息并未用于过滤教材(如按学生年级筛选)。 +- **后果**:跨年级学生可看到非本年级教材;多租户场景下数据越权。 + +### 2.2 国际化(i18n) + +#### 问题 2.2.1 | 全模块零 i18n 覆盖(P0) + +- **位置**:模块全部 19 个源文件 +- **现象**:项目已接入 next-intl(见 [i18n/request.ts](file:///e:/Desktop/CICD/src/i18n/request.ts)),但教材模块**没有任何一处**使用 `useTranslations` / `getTranslations`,所有文案硬编码,且中英文混杂: + - 中文硬编码:`"章节目录"`、`"知识点"`、`"图谱"`、`"请选择一个章节查看知识点。"`、`"该章节暂无知识点。"`、`"添加知识点"`、`"取消"`、`"删除"`、`"保存"`、`"确认删除"`、`"确定要删除这个知识点吗?此操作无法撤销。"`、`"创建中..."`、`"保存中..."`、`"知识点已创建"`、`"发生错误"`、`"删除失败"`、`"更新失败"`、`"返回教材列表"` 等([textbook-reader.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-reader.tsx)、[knowledge-point-list.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/knowledge-point-list.tsx)、[knowledge-point-dialogs.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/knowledge-point-dialogs.tsx)、[use-knowledge-point-actions.ts](file:///e:/Desktop/CICD/src/modules/textbooks/hooks/use-knowledge-point-actions.ts)、[teacher/textbooks/[id]/page.tsx](file:///e:/Desktop/CICD/src/app/(dashboard)/teacher/textbooks/[id]/page.tsx)) + - 英文硬编码:`"Textbooks"`、`"Manage your digital curriculum resources and chapters."`、`"Add Textbook"`、`"Add New Textbook"`、`"Create a new digital textbook."`、`"Save changes"`、`"Search by title, publisher..."`、`"All Subjects"`、`"All Grades"`、`"Subject"`、`"Grade"`、`"Publisher"`、`"Title"`、`"Chapters"`、`"Updated"`、`"Edit Content"`、`"Delete"`、`"Settings"`、`"Textbook Settings"`、`"Delete Textbook"`、`"Add Chapter"`、`"Add Knowledge Point"`、`"Knowledge Points"`、`"No points yet"`、`"Select a chapter to manage knowledge points"` 等([textbook-filters.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-filters.tsx)、[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)、[textbook-card.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-card.tsx)、[knowledge-point-panel.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/knowledge-point-panel.tsx)) +- **违反规则**:项目规则"所有用户可见文本必须适配 i18n(使用 next-intl),提取翻译键"。 +- **原因**:模块开发时未跟进 i18n 改造,文案随写随定。 +- **后果**:无法切换语言;同一界面中英混杂,专业度差;后续做国际化需返工全部组件。 + +### 2.3 类型安全 + +#### 问题 2.3.1 | 非空断言与 `as` 断言(P1) + +- **位置**: + - [chapter-sidebar-list.tsx#L141](file:///e:/Desktop/CICD/src/modules/textbooks/components/chapter-sidebar-list.tsx#L141):`items={chapter.children!}` —— 已在 `hasChildren` 守卫后仍用 `!`,应改用 narrowing。 + - [knowledge-graph.tsx#L105](file:///e:/Desktop/CICD/src/modules/textbooks/components/knowledge-graph.tsx#L105):`positions.get(kp.parentId as string)!` —— `as string` + `!` 双重断言。 + - [knowledge-graph.tsx#L106](file:///e:/Desktop/CICD/src/modules/textbooks/components/knowledge-graph.tsx#L106):`positions.get(kp.id)!` +- **违反规则**:项目规则"禁止 `as` 断言(除非从 `unknown` 转换)"、"可选链后禁止跟非空断言 `!`"。 +- **后果**:运行时若数据不一致(如 parentId 指向已删除节点),直接抛错而非优雅降级。 + +#### 问题 2.3.2 | `data-access.ts` 使用 `select()` 无类型投影(P2) + +- **位置**:[data-access.ts#L413](file:///e:/Desktop/CICD/src/modules/textbooks/data-access.ts#L413):`db.select().from(chapters)` +- **现象**:`select()` 不传参数返回整行,类型推断为全表 schema,与模块对外 `Chapter` 类型不完全一致(如 `content` 可空性)。 +- **后果**:类型边界模糊,后续 schema 变更可能静默破坏调用方。 + +### 2.4 错误与边界处理 + +#### 问题 2.4.1 | 缺少 React Error Boundary(P1) + +- **位置**:`src/app/(dashboard)/teacher/textbooks/**`、`src/app/(dashboard)/student/learning/textbooks/**` 均无 `error.tsx` +- **现象**:详情页 `getTextbookById` 返回 `undefined` 时走 `notFound()`,但章节/知识点查询失败、Server Action 抛错时整页崩溃,无降级 UI。 +- **违反规则**:项目规则"每个独立的数据区块必须用 React Error Boundary 包裹"。 +- **后果**:一次 DB 抖动导致整个阅读器白屏,无法隔离故障域。 + +#### 问题 2.4.2 | 删除确认交互不一致(P2) + +- **位置**:[textbook-settings-dialog.tsx#L52](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-settings-dialog.tsx#L52):`if (!confirm("Are you sure..."))` 使用浏览器原生 `confirm` +- **现象**:模块内其他删除(章节、知识点)均用 `AlertDialog`,唯独教材删除用 `confirm()`。 +- **违反规则**:项目规则"组合优先"与 UI 一致性;`confirm()` 阻塞主线程且不可定制样式。 +- **后果**:交互体验割裂;移动端 `confirm` 表现不一。 + +#### 问题 2.4.3 | 空状态文案与组件不统一(P2) + +- **位置**: + - [textbook-reader.tsx#L222](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-reader.tsx#L222):内联 `
请选择一个章节查看知识点。
` + - [knowledge-point-list.tsx#L32](file:///e:/Desktop/CICD/src/modules/textbooks/components/knowledge-point-list.tsx#L32):内联 `
该章节暂无知识点。
` + - [textbook-content-panel.tsx#L67](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-content-panel.tsx#L67):内联 `
请选择一个章节开始阅读。
` + - 列表页则用 `EmptyState` 组件 +- **后果**:同一模块内空状态有三种写法,维护成本高,a11y 属性缺失。 + +### 2.5 组件复用与组合 + +#### 问题 2.5.1 | 知识点列表/面板存在重复实现(P1) + +- **位置**: + - [knowledge-point-list.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/knowledge-point-list.tsx)(107 行,被 `TextbookReader` 使用) + - [knowledge-point-panel.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/knowledge-point-panel.tsx)(157 行,未被任何页面引用,疑似旧版遗留) +- **现象**:两个组件职责几乎相同(展示章节知识点 + 删除),`KnowledgePointPanel` 还自带 `router.refresh()`,但实际无调用方。 +- **违反规则**:项目规则"最大化复用"。 +- **后果**:死代码增加认知负担;修改知识点展示逻辑需同步两处。 + +#### 问题 2.5.2 | 创建知识点弹窗存在两套实现(P1) + +- **位置**: + - [create-knowledge-point-dialog.tsx](file:///e:/Desktop/CICD/src/modules/textbooks/components/create-knowledge-point-dialog.tsx)(独立弹窗,被 `KnowledgePointPanel` 引用,但 `KnowledgePointPanel` 本身无调用方) + - [knowledge-point-dialogs.tsx#L56-L85](file:///e:/Desktop/CICD/src/modules/textbooks/components/knowledge-point-dialogs.tsx#L56)(内嵌创建弹窗,被 `TextbookReader` 使用) +- **现象**:两套创建知识点弹窗,文案一中一英,字段一致但实现独立。 +- **后果**:同上,双份维护。 + +#### 问题 2.5.3 | 学科/年级选项硬编码三处(P1) + +- **位置**: + - [textbook-filters.tsx#L43-L66](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-filters.tsx#L43):Select 选项 + - [textbook-form-dialog.tsx#L89-L113](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-form-dialog.tsx#L89):Select 选项(且 form 与 settings 的学科列表不一致:form 含 Biology/Geography,settings 缺这两项) + - [textbook-settings-dialog.tsx#L106-L112](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-settings-dialog.tsx#L106):Select 选项 + - [textbook-card.tsx#L26-L34](file:///e:/Desktop/CICD/src/modules/textbooks/components/textbook-card.tsx#L26):`subjectColorMap` 学科颜色映射 +- **现象**:学科、年级枚举在 4 个文件里各写一份,且**彼此不一致**(settings 弹窗的学科列表少了 Biology 和 Geography)。 +- **违反规则**:项目规则"最大化复用……抽象为泛型组件和 hooks"、"配置驱动设计"。 +- **后果**:新增学科需改 4 处;当前已出现数据不一致——用户在 form 里能选 Biology,但 settings 里看不到,编辑时学科被覆盖。 + +### 2.6 可访问性(a11y) + +#### 问题 2.6.1 | 知识图谱 SVG 缺少无障碍属性(P1) + +- **位置**:[knowledge-graph.tsx#L142-L158](file:///e:/Desktop/CICD/src/modules/textbooks/components/knowledge-graph.tsx#L142) +- **现象**:`` 无 `role="img"`、无 `aria-label`、无 ``;节点用 `