Files
NextEdu/docs/architecture/audit/archive/question-bank-audit-report.md

19 KiB
Raw Blame History

题库模块审计报告

审计日期2026-06-22 审计范围:src/modules/questions/src/app/(dashboard)/teacher/questions/、共享组件 src/shared/components/question/ 架构图参考:docs/architecture/004_architecture_impact_map.mddocs/architecture/005_architecture_data.json


一、现有实现概要

1.1 文件分布

文件 行数 职责
app src/app/(dashboard)/teacher/questions/page.tsx 129 教师题库列表页Server Component
app src/app/(dashboard)/teacher/questions/loading.tsx 29 骨架屏
module src/modules/questions/actions.ts 158 5 个 Server ActionCRUD + 查询)
module src/modules/questions/data-access.ts 357 数据访问层(含跨模块接口)
module src/modules/questions/types.ts 53 类型定义
module src/modules/questions/schema.ts 18 Zod 校验
module src/modules/questions/components/question-filters.tsx 104 筛选栏(自有实现)
module src/modules/questions/components/question-columns.tsx 142 表格列定义
module src/modules/questions/components/question-data-table.tsx 134 数据表格
module src/modules/questions/components/create-question-button.tsx 20 创建按钮
module src/modules/questions/components/create-question-dialog.tsx 453 创建/编辑对话框
module src/modules/questions/components/question-actions.tsx 188 行操作菜单
shared src/shared/components/question/question-bank-filters.tsx 137 共享筛选栏exams/lesson-prep 复用)

1.2 数据流

teacher/questions/page.tsx (RSC)
  ├─ requirePermission(QUESTION_READ)          ← 服务端权限校验
  ├─ getQuestions(params)                       ← 直接调用 data-access项目规则允许
  ├─ <QuestionFilters />                        ← 客户端组件,调用 getKnowledgePointOptionsAction
  └─ <QuestionDataTable columns data />         ← 客户端表格
       └─ <QuestionActions question />          ← 行操作
            ├─ <CreateQuestionDialog />         ← 编辑(调用 updateQuestionAction
            └─ AlertDialog                      ← 删除(调用 deleteQuestionAction

1.3 架构图记录情况

005_architecture_data.json 已记录 questions 模块的 actions / dataAccess / schema / types / components但存在以下遗漏

  • 依赖矩阵不完整dependencyMatrix.questions.dependsOn 仅列出 ["shared", "auth"]缺失 textbooks。实际 data-access.ts:8 导入了 @/modules/textbooks/data-accessgetKnowledgePointOptions,属于跨模块 data-access 通信,应在依赖矩阵中记录。
  • action 名称不一致:架构图记录 action 名为 createNestedQuestion,实际代码为 createQuestionAction
  • 跨模块接口未记录getKnowledgePointsForQuestionsgetQuestionsContentForErrorCollection 两个跨模块接口未在架构图中记录。

二、现存问题与原因分析

P0 — 严重问题

P0-1筛选方式不符合 K12 教学场景(用户核心痛点)

  • 位置question-filters.tsxpage.tsxdata-access.ts:getQuestions
  • 问题:当前筛选仅支持「关键词搜索 + 题型 + 难度 + 扁平知识点下拉」。知识点下拉将所有教材的所有章节的所有知识点平铺在一个列表中,教师面对数百条选项无法快速定位。
  • 违反规则:项目规则「交互不符合多角色使用习惯」;用户明确要求「选择一个课本 → 一个章节 → 一个课文 → 知识点」级联筛选。
  • 后果:教师无法按教学进度精准筛选题目,题库实用性大打折扣,被迫退回手动翻找。

P0-2完全缺失 i18n 国际化

  • 位置:题库模块全部文件
  • 问题:所有用户可见文本均为硬编码英文("Question Bank"、"Add Question"、"Search questions..."、"All Types"、"Single Choice" 等),不存在 questions.json 翻译文件
  • 违反规则:项目规则「所有用户可见文本必须适配 i18n使用 next-intl提取翻译键」。
  • 后果无法支持中文环境与项目其他模块exams、textbooks、attendance 等均已 i18n不一致。

P0-3缺失 error.tsx 错误边界

  • 位置src/app/(dashboard)/teacher/questions/
  • 问题:仅有 loading.tsxerror.tsx。当 getQuestionsrequirePermission 抛错时,错误会冒泡到上层 layout整个仪表盘白屏。
  • 违反规则:项目规则「所有 student routes 必须包含 loading.tsx 和 error.tsx」此规则虽针对 student 路由,但教师路由同样应遵循);架构原则「每个独立的数据区块必须用 React Error Boundary 包裹」。
  • 后果:单次查询失败导致整页不可用,无法恢复。

P1 — 高优先级问题

P1-1as 类型断言违反 TypeScript 严格规范

  • 位置
    • create-question-dialog.tsx:62(content as { text?: unknown }).text
    • create-question-dialog.tsx:71(content as { options?: unknown }).options
    • create-question-dialog.tsx:78(opt as { id?: unknown; value?: unknown }).id
    • create-question-dialog.tsx:80(opt as { text?: unknown; label?: unknown }).text
    • create-question-dialog.tsx:82(opt as { isCorrect?: unknown }).isCorrect
    • question-columns.tsx:58(content as { text?: unknown }).text
    • question-bank-list.tsx:108-112q.content as { text?: string }parsed as unknown
  • 问题:从 unknown 到具体类型使用了 as 断言而非类型守卫。
  • 违反规则:项目规则「禁止 as 断言(除非从 unknown 转换或测试中,需注释原因)」。虽然这些是从 unknown 转换,但未使用类型守卫函数,且未注释原因。
  • 后果:类型不安全,运行时可能访问不存在的属性。

P1-2筛选栏组件重复实现

  • 位置question-filters.tsx模块内自有实现vs shared/components/question/question-bank-filters.tsx(共享组件)
  • 问题:模块内的 QuestionFilters 没有复用共享 QuestionBankFilters,两者功能高度重叠(搜索 + 题型 + 难度),但实现完全独立。共享组件已被 exams 和 lesson-preparation 模块使用。
  • 违反规则项目规则「Shared components must be extracted when page duplication exceeds 90%」。
  • 后果:维护两套筛选 UI样式和行为不一致修改需同步两处。

P1-3客户端组件未做权限感知

  • 位置create-question-button.tsxquestion-actions.tsx
  • 问题CreateQuestionButton 无条件渲染,不检查用户是否有 QUESTION_CREATE 权限。QuestionActions 的编辑/删除按钮也不检查 QUESTION_UPDATE/QUESTION_DELETE 权限。
  • 违反规则:项目规则「前端权限判断统一使用 usePermission().hasPermission()」。
  • 后果:无权限用户看到操作按钮,点击后才在 Server Action 层被拒绝,体验差。

P1-4题目内容 content: unknown 类型不安全

  • 位置types.ts:27data-access.tscreate-question-dialog.tsxquestion-columns.tsx
  • 问题Question.content 类型为 unknown,各处用 astypeof 手动解析,没有统一的类型定义和解析函数。题目内容实际有明确结构({ text, options?, answer?, explanation? }),但未类型化。
  • 违反规则:项目规则「禁止 any:未知类型用 unknown 并做类型守卫」——虽然用了 unknown,但缺少类型守卫。
  • 后果:解析逻辑分散在 4+ 个文件中,重复且易错。

P1-5无级联筛选的数据访问支持

  • 位置data-access.ts:getQuestions 参数仅有 knowledgePointId,无 textbookId/chapterId 过滤
  • 问题:要实现「课本 → 章节 → 知识点」级联筛选,需要 getQuestions 支持按 textbookIdchapterId 过滤,以及提供 getTextbookOptionsgetChaptersByTextbookId 等级联数据接口。当前均缺失。
  • 违反规则:架构原则「可扩展性」。
  • 后果:无法实现用户要求的级联筛选。

P2 — 中优先级问题

P2-1仅支持教师角色无多角色复用

  • 位置:仅 src/app/(dashboard)/teacher/questions/ 存在
  • 问题admin 角色无法管理题库(虽然 admin 有 QUESTION_* 全部权限parent/student 无法查看题目。组件未抽象为可配置化多角色复用。
  • 违反规则:架构原则「最大化复用:识别四个角色共用的 UI 块」。
  • 后果:新增角色需复制整个页面。

P2-2无题目预览渲染

  • 位置question-columns.tsx:52-72question-actions.tsx:160-163
  • 问题:题目内容预览仅截取前 80 字符的纯文本或 JSON.stringify,无结构化渲染(选项列表、正确答案高亮等)。
  • 违反规则:架构原则「交互不符合多角色使用习惯」。
  • 后果:教师无法快速判断题目内容,需逐个点开查看。

P2-3无批量操作

  • 位置question-data-table.tsx 有行选择 checkbox但无批量操作 UI
  • 问题:表格支持多选但无批量删除、批量导出、批量移动知识点等操作。
  • 违反规则:架构原则「可扩展性」。
  • 后果:教师需逐条操作,效率低。

P2-4无题目导入/导出

  • 位置:缺失
  • 问题:不支持从 Excel/Word 导入题目,也不支持导出。
  • 违反规则:行业差距(见第三节)。
  • 后果:教师无法迁移已有题库。

P2-5create-question-dialog.tsx 接近行数上限

  • 位置create-question-dialog.tsx453 行)
  • 问题:接近 500 行组件上限。包含表单、知识点选择、选项编辑三块逻辑。
  • 违反规则项目规则「React 组件:建议 ≤ 500 行」。
  • 后果:维护困难,难以测试。

P2-6无监控埋点

  • 位置:缺失
  • 问题:无题目创建/删除/搜索的操作埋点接口。
  • 违反规则:架构原则「监控:方案中预留关键操作埋点接口」。
  • 后果:无法分析题库使用情况。

P2-7架构图 action 名称与代码不一致

  • 位置005_architecture_data.json:4708 记录 createNestedQuestion,实际代码为 createQuestionAction
  • 问题:架构图与代码不同步。
  • 违反规则:项目规则「改码必同步图」。
  • 后果:架构图不可信。

三、行业差距对比

3.1 与主流 K12 产品的差距

能力 主流产品(组卷网/学科网/智学网) 当前实现 差距影响
级联筛选 教材→章节→课时→知识点四级级联 扁平知识点下拉 教师无法按教学进度选题
题目预览 结构化渲染(题干+选项+答案+解析) 截取 80 字符纯文本 无法快速判断题目内容
批量操作 批量删除/移动/导出 仅有多选 UI 无操作 效率低
导入导出 Excel/Word 模板导入导出 无法迁移已有题库
题目难度标签 易/中/难 + 星级 数字 1-5 不够直观
题目来源标注 自编/教材/网络 仅记录 author 无法追溯题目来源
题目使用统计 被引用次数/正确率 childrenCount 无法评估题目质量
题目版本管理 修改历史 无法回溯修改
富文本编辑 公式/图片/表格 纯文本 textarea 无法编辑理科题目
多角色支持 教师建题/admin管理/学生练习 仅教师 admin 无法管理

3.2 关键差距分析

  1. 级联筛选是最大痛点K12 教学严格按教材章节进度推进,教师选题时必然先定位到当前教学进度对应的章节,再在该章节的知识点下筛选题目。扁平列表完全不符合此工作流。

  2. 题目预览缺失影响效率:教师在组卷/备课选题时,需要快速浏览多道题目的完整内容(含选项和答案),当前仅显示截断文本,必须逐个点开查看,严重影响效率。

  3. 无导入导出限制迁移学校通常有存量题库Word/Excel无法导入意味着教师需手动重新录入所有题目阻力极大。


四、改进优先级建议

P0 — 立即实施

编号 问题 改进方向
P0-1 筛选方式不符合教学场景 新增「教材→章节→知识点」三级级联筛选组件data-access 层新增 getTextbookOptionsForQuestionsgetChaptersByTextbookId 接口,getQuestions 支持 textbookId/chapterId 参数
P0-2 缺失 i18n 创建 questions.json 翻译文件(中/英),所有组件使用 useTranslations("questions")
P0-3 缺失 error.tsx 新增 error.tsx 错误边界

P1 — 本轮实施

编号 问题 改进方向
P1-1 as 类型断言 提取 parseQuestionContent 类型守卫函数到 utils/parse-content.ts,替换所有 as 断言
P1-2 筛选栏重复 模块内 QuestionFilters 改为复用共享 QuestionBankFilters + 新增级联筛选扩展
P1-3 客户端权限感知 CreateQuestionButtonQuestionActions 使用 usePermission().hasPermission()
P1-4 content 类型不安全 定义 QuestionContent 类型,替换 unknown
P1-5 无级联筛选数据支持 data-access 新增级联数据接口

P2 — 中长期计划

编号 问题 改进方向
P2-1 仅教师角色 抽象角色配置,新增 admin 题库页面
P2-2 无题目预览 新增 QuestionPreview 组件,结构化渲染
P2-3 无批量操作 新增批量操作工具栏
P2-4 无导入导出 中长期Excel 模板导入导出
P2-5 dialog 行数 拆分为 QuestionFormFields + KnowledgePointSelector + OptionsEditor
P2-6 无监控埋点 预留 trackQuestionEvent 接口
P2-7 架构图不同步 同步 action 名称、依赖矩阵、跨模块接口

五、架构图同步说明

需要同步更新以下内容:

5.1 005_architecture_data.json

  1. 依赖矩阵dependencyMatrix.questions.dependsOn 添加 "textbooks"uses 添加 textbooks: ["data-access.getKnowledgePointOptions"]
  2. action 名称createNestedQuestioncreateQuestionAction
  3. 跨模块接口:在 dataAccess 中补充 getKnowledgePointsForQuestionsgetQuestionsContentForErrorCollection
  4. 新增 data-access 函数getTextbookOptionsForQuestionsgetChaptersForCascadegetQuestions 参数新增 textbookId/chapterId
  5. 新增组件QuestionCascadeFilterQuestionPreviewQuestionContentRenderer
  6. 新增 utilsutils/parse-content.ts

5.2 004_architecture_impact_map.md

  1. 题库模块章节:更新筛选方式说明(级联筛选)、新增 i18n 说明
  2. 依赖关系:补充 questions → textbooks 依赖
  3. 组件清单:新增级联筛选、预览组件

六、实施记录

以下改进已在本次审计中实施(含中长期计划,全部完成):

P0 修复

  • P0-1新增「教材→章节→知识点」三级级联筛选data-access 支持 textbookId/chapterId 参数
  • P0-2创建 questions.json i18n 翻译文件(中/英),所有组件迁移到 useTranslations
  • P0-3新增 error.tsx 错误边界teacher + admin

P1 修复

  • P1-1提取 parseQuestionContent 类型守卫,替换所有 as 断言
  • P1-2模块筛选栏复用共享组件 + 级联扩展QuestionCascadeFilter
  • P1-3客户端组件添加 usePermission() 权限感知
  • P1-4定义 QuestionContent 类型
  • P1-5data-access 新增级联数据接口getTextbookOptions/getChapterOptions/getKnowledgePointOptionsByChapter

P2 修复

  • P2-5拆分 create-question-dialog.tsx453→281 行,拆分 KnowledgePointSelector + OptionsEditor
  • P2-6预留 trackQuestionEvent 埋点接口no-op 实现)
  • P2-7同步架构图004 + 005

P2 中长期计划(已全部实施)

  • P2-1多角色支持 — 新增 admin/questions 页面page.tsx + error.tsx + loading.tsx复用全部 questions 模块组件,权限感知自动适配
  • P2-2题目结构化预览 — 新增 QuestionContentRenderer 组件,支持题干/选项/答案/解析结构化渲染,单选圆点/多选方框视觉区分,正确答案高亮,已集成到查看详情对话框
  • P2-3批量操作 — 新增 deleteQuestionsBatch data-access + deleteQuestionsBatchAction Server Action + BatchOperations 组件,表格行选择后显示批量删除工具栏,权限感知,事务保证原子性
  • P2-4导入导出 — 新增 exportQuestions/importQuestions data-access + exportQuestionsAction/importQuestionsAction Server Action + ImportExportButtons 组件,支持 JSON 格式导出下载和文件上传导入,权限感知,导入前预览确认

新增文件清单

文件 行数 职责
src/app/(dashboard)/admin/questions/page.tsx 136 管理员题库页面(复用 questions 模块组件)
src/app/(dashboard)/admin/questions/error.tsx 29 管理员题库错误边界
src/app/(dashboard)/admin/questions/loading.tsx 29 管理员题库加载骨架屏
src/modules/questions/components/question-content-renderer.tsx 110+ 题目结构化渲染(题干/选项/答案/解析)
src/modules/questions/components/batch-operations.tsx 110+ 批量操作工具栏(批量删除)
src/modules/questions/components/import-export-buttons.tsx 180+ 导入导出组件JSON 格式)

修改文件清单

文件 变更
src/modules/questions/data-access.ts 新增 deleteQuestionsBatch/exportQuestions/importQuestions + 类型定义
src/modules/questions/actions.ts 新增 deleteQuestionsBatchAction/exportQuestionsAction/importQuestionsAction,所有 Action 增加 revalidatePath("/admin/questions")
src/modules/questions/components/question-data-table.tsx 集成 BatchOperations 组件,支持 getRowId + 行选择
src/modules/questions/components/question-bank-results-client.tsx 传入 getRowId 支持批量操作
src/modules/questions/components/question-actions.tsx 查看详情改用 QuestionContentRenderer 结构化渲染
src/app/(dashboard)/teacher/questions/page.tsx 新增 ImportExportButtons
src/app/(dashboard)/admin/questions/page.tsx 新增 ImportExportButtons
src/shared/i18n/messages/zh-CN/questions.json 新增 batch + importExport 命名空间
src/shared/i18n/messages/en/questions.json 新增 batch + importExport 命名空间

验证结果

  • TypeScriptquestions 模块零错误(npx tsc --noEmit 仅余其他模块预存错误)
  • ESLintquestions 模块 + admin/questions + teacher/questions 零错误零警告(--max-warnings=0
  • 架构图同步004 + 005 已更新