Files
NextEdu/bugs/teacher_bug.md
SpecialX 49291fcc31 refactor: fix all P0/P1/P2 bugs and architecture issues
Bug fixes (from bugs/ directory):

- Fix cross-module DB queries in 9 modules (homework, grades, parent, diagnostic, elective, proctoring, notifications, scheduling, classes) by routing through data-access functions

- Fix shared/lib <-> auth circular dependency via new session.ts module

- Fix divide-by-zero guard in grades data-access

- Fix audit export data truncation (paginated fetch for full datasets)

- Fix missing transactions in homework grading and elective lottery

- Fix missing revalidatePath in course-plans actions

- Fix frontend permission checks using requirePermission instead of requireAuth

- Fix dashboard role routing using session.user.roles

- Fix student auth pattern (migrate getDemoStudentUser to users module)

- Fix ActionState return type handling in components

Code quality fixes:

- Remove 60+ as type assertions (replace with type guards)

- Remove non-null assertions (use optional chaining or explicit checks)

- Convert dynamic imports to static imports (grades, diagnostic)

- Add React.cache() wrapping for read functions

- Parallelize independent queries with Promise.all

- Add explicit return types to 30+ arrow functions

- Replace any with unknown + type guards

- Fix import type for type-only imports

- Add Zod validation schemas for classes and diagnostic modules

- Extract duplicate code (normalizeRoleName, normalizeBcryptHash, logger IP extraction)

- Add console.error to silent catch blocks

- Fix permission naming consistency (exam:proctor_read -> exam:proctor:read)

Architecture doc sync:

- Update 004_architecture_impact_map.md and 005_architecture_data.json

- Update management-modules-audit.md for P0-7 cross-module fix

Moved deleted proctoring event route to deletes/ folder.
2026-06-19 05:13:34 +08:00

39 KiB
Raw Blame History

src/app/(dashboard)/teacher 前端规范核查报告

核查日期2026-06-18 核查范围:src/app/(dashboard)/teacher/ 目录下所有前端文件page.tsx / loading.tsx 依据文档:项目规则、编码规范 docs/standards/coding-standards.md、架构影响地图 004、架构数据 005 应用技能:vercel-react-best-practices(性能优化)、web-artifacts-builder(界面优化)、web-design-guidelinesWeb 界面规范审查)


一、核查文件清单

文件 行数 类型 用途
dashboard/page.tsx 37 页面 教师仪表盘
attendance/page.tsx 83 页面 考勤记录列表
attendance/sheet/page.tsx 49 页面 考勤登记
attendance/stats/page.tsx 120 页面 考勤统计
classes/page.tsx 5 页面 重定向到 my
classes/my/page.tsx 18 页面 我的班级
classes/my/[id]/page.tsx 109 页面 班级详情
classes/my/loading.tsx 31 加载态 班级列表骨架屏
classes/schedule/page.tsx 81 页面 班级课表
classes/schedule/loading.tsx 28 加载态 课表骨架屏
classes/students/page.tsx 102 页面 学生列表
classes/students/loading.tsx 20 加载态 学生列表骨架屏
course-plans/page.tsx 49 页面 课程计划列表
course-plans/[id]/page.tsx 26 页面 课程计划详情
diagnostic/page.tsx 48 页面 学习诊断报告
diagnostic/class/[classId]/page.tsx 45 页面 班级诊断
diagnostic/student/[studentId]/page.tsx 65 页面 学生诊断
elective/page.tsx 50 页面 选修课程
exams/page.tsx 5 页面 重定向到 all
exams/all/page.tsx 148 页面 考试列表
exams/all/loading.tsx 24 加载态 考试列表骨架屏
exams/create/page.tsx 10 页面 创建考试
exams/create/loading.tsx 16 加载态 创建考试骨架屏
exams/[id]/build/page.tsx 120 页面 组卷
exams/[id]/proctoring/page.tsx 55 页面 监考
exams/grading/page.tsx 5 页面 重定向
exams/grading/[submissionId]/page.tsx 6 页面 重定向
exams/grading/loading.tsx 20 加载态 批改骨架屏
grades/page.tsx 101 页面 成绩管理
grades/analytics/page.tsx 259 页面 成绩分析
grades/entry/page.tsx 52 页面 批量录入
grades/stats/page.tsx 139 页面 成绩统计
homework/page.tsx 5 页面 重定向
homework/assignments/page.tsx 119 页面 作业列表
homework/assignments/create/page.tsx 43 页面 创建作业
homework/assignments/[id]/page.tsx 100 页面 作业详情
homework/assignments/[id]/submissions/page.tsx 86 页面 作业提交列表
homework/submissions/page.tsx 80 页面 提交审阅
homework/submissions/[submissionId]/page.tsx 44 页面 批改详情
questions/page.tsx 120 页面 题库
questions/loading.tsx 29 加载态 题库骨架屏
schedule-changes/page.tsx 69 页面 课表变更
textbooks/page.tsx 74 页面 教材列表
textbooks/loading.tsx 48 加载态 教材骨架屏
textbooks/[id]/page.tsx 63 页面 教材详情
textbooks/[id]/loading.tsx 66 加载态 教材详情骨架屏

共计 45 个文件37 个 page.tsx + 8 个 loading.tsx


二、违规问题清单

2.1 架构分层违规 — 严重度:高

BUG-T01app 层直接访问数据库dashboard/page.tsx

  • 位置dashboard/page.tsx:4-6, 18-21
  • 问题:页面直接 import { db } from "@/shared/db" 并调用 db.query.users.findFirst(),违反项目规则「app/ 只能调用 modules/ 的 Server Actions 和 data-access不直接访问 DB」
  • 现状
    import { db } from "@/shared/db"
    import { users } from "@/shared/db/schema"
    // ...
    db.query.users.findFirst({
      where: eq(users.id, teacherId),
      columns: { name: true },
    })
    
  • 改进建议:通过 modules/users/data-access.ts 暴露 getUserNameById(id) 函数调用

BUG-T02app 层直接访问数据库grades/page.tsx

  • 位置grades/page.tsx:5-7, 35
  • 问题:直接 db.query.subjects.findMany() 查询科目列表,违反三层架构
  • 改进建议:在 modules/school/data-access.tsmodules/grades/data-access.ts 暴露 getSubjects() 函数

BUG-T03app 层直接访问数据库grades/analytics/page.tsx

BUG-T04app 层直接访问数据库grades/entry/page.tsx

BUG-T05app 层直接访问数据库grades/stats/page.tsx

BUG-T06认证上下文获取方式不一致

  • 位置
  • 问题:使用 import { auth } from "@/auth" + auth() 获取 session而其他页面统一使用 getAuthContext()(含 DataScope 解析)
  • 影响:无法获得 dataScope,无法做数据范围过滤;与项目其他页面不一致
  • 改进建议:统一改为 const ctx = await getAuthContext(); const teacherId = ctx.userId

2.2 Prettier 配置违规 — 严重度:中

项目 .prettierrc 配置 "semi": false,但以下文件使用分号结尾:

BUG-T07textbooks/page.tsx 使用分号

  • 位置textbooks/page.tsx:3, 73
  • 问题import { TextbookCard } from "..."; 等多处使用分号
  • 改进建议:运行 npx prettier --write 统一格式

BUG-T08textbooks/[id]/page.tsx 使用分号

BUG-T09textbooks/loading.tsx 使用分号

BUG-T10textbooks/[id]/loading.tsx 使用分号


2.3 TypeScript 规范违规 — 严重度:高

BUG-T11使用 as 类型断言exams/[id]/build/page.tsx

  • 位置exams/[id]/build/page.tsx:32-34
  • 问题:使用 as 断言转换类型,违反编码规范「禁止 as 断言(除非从 unknown 转换)」
  • 现状
    content: q.content as Question["content"],
    type: q.type as Question["type"],
    
  • 改进建议:在 data-access 层返回正确类型,或使用类型守卫函数

BUG-T12使用 as 类型断言attendance/page.tsx

  • 位置attendance/page.tsx:39
  • 问题status as "present" | "absent" | "late" | "early_leave" | "excused" 直接断言
  • 改进建议:使用类型守卫函数 isAttendanceStatus(value): value is AttendanceStatus

BUG-T13使用 as 类型断言grades/page.tsx

  • 位置grades/page.tsx:43-44
  • 问题type as "exam" | "quiz" | "homework" | "other"semester as "1" | "2" 直接断言
  • 改进建议:使用类型守卫

BUG-T14使用 as 类型断言grades/analytics/page.tsx

BUG-T15使用 as 类型断言diagnostic/page.tsx

  • 位置diagnostic/page.tsx:27-28
  • 问题reportType as DiagnosticReportTypestatus as DiagnosticReportStatus
  • 改进建议:使用类型守卫

BUG-T16函数返回值未显式标注getParam 工具函数)

  • 位置:以下 15 个文件中的 getParam 函数均未标注返回类型
    • attendance/page.tsx:15
    • attendance/sheet/page.tsx:9
    • attendance/stats/page.tsx:12
    • classes/schedule/page.tsx:14
    • classes/students/page.tsx:14
    • course-plans/page.tsx:10
    • diagnostic/page.tsx:10
    • elective/page.tsx:10
    • exams/all/page.tsx:16
    • grades/page.tsx:19
    • grades/analytics/page.tsx:28
    • grades/entry/page.tsx:12
    • grades/stats/page.tsx:15
    • homework/assignments/page.tsx:23
    • questions/page.tsx:15
    • textbooks/page.tsx:13
  • 问题:违反编码规范「函数返回值必须显式标注,特别是 Promise<T>
  • 现状const getParam = (params: SearchParams, key: string) => { ... }
  • 改进建议const getParam = (params: SearchParams, key: string): string | undefined => { ... }

BUG-T17页面默认导出函数未标注返回类型

  • 位置:所有 page.tsx 文件的 export default async function XxxPage()
  • 问题:未标注 Promise<JSX.Element>Promise<React.ReactNode>
  • 规范依据:编码规范 5.2 示例 export default async function UsersPage(): Promise<JSX.Element>
  • 改进建议:统一补充返回类型标注

2.4 DRY 违规(重复代码) — 严重度:中

BUG-T18getParam 工具函数在 16 个文件中重复定义

  • 位置:见 BUG-T16 列表
  • 问题:完全相同的工具函数 getParam 和类型 SearchParams 在 16 个页面文件中复制粘贴
  • 改进建议:提取到 shared/lib/search-params.ts
    export type SearchParams = { [key: string]: string | string[] | undefined }
    export function getParam(params: SearchParams, key: string): string | undefined {
      const v = params[key]
      return Array.isArray(v) ? v[0] : v
    }
    

BUG-T19StatsClassSelector 模式重复


2.5 性能问题vercel-react-best-practices — 严重度:高

BUG-T20串行数据获取 waterfallattendance/page.tsx

  • 位置attendance/page.tsx:32-41
  • 问题getTeacherClasses()getAttendanceRecords() 串行执行,但二者无依赖关系
  • 违反规则async-parallel - 独立操作应使用 Promise.all()
  • 改进建议
    const [classes, result] = await Promise.all([
      getTeacherClasses(),
      getAttendanceRecords({ ... }),
    ])
    

BUG-T21串行数据获取 waterfallattendance/sheet/page.tsx

  • 位置attendance/sheet/page.tsx:24-29
  • 问题getTeacherClasses()getClassStudentsForAttendance() 串行,但 students 依赖 defaultClassId来自 searchParams可与 classes 并行
  • 改进建议:使用 Promise.all 并行

BUG-T22串行数据获取 waterfallattendance/stats/page.tsx

  • 位置attendance/stats/page.tsx:28-53
  • 问题getTeacherClasses()getClassAttendanceStats() 串行,但 stats 依赖 classId可从 classes[0] 取默认),可优化
  • 改进建议:先并行获取 classes再取 targetClassId 后获取 stats当前逻辑合理但可考虑预取

BUG-T23串行数据获取 waterfallgrades/page.tsx

  • 位置grades/page.tsx:33-45
  • 问题Promise.all([getTeacherClasses, db.query]) 之后串行 getGradeRecords,但 getGradeRecords 不依赖前两者结果
  • 改进建议:三个查询全部 Promise.all

BUG-T24串行数据获取 waterfallgrades/entry/page.tsx

  • 位置grades/entry/page.tsx:23-34
  • 问题Promise.all([getTeacherClasses, db.query]) 后串行 getClassStudentsForEntry,但 students 依赖 defaultClassId来自 searchParams可并行
  • 改进建议Promise.all 三个查询

BUG-T25串行数据获取 waterfallgrades/stats/page.tsx

  • 位置grades/stats/page.tsx:26-54
  • 问题Promise.all([getTeacherClasses, db.query])Promise.all([stats, ranking]) 两段串行
  • 改进建议:合并为单个 Promise.all

BUG-T26串行数据获取 waterfallclasses/my/[id]/page.tsx

  • 位置classes/my/[id]/page.tsx:21-30
  • 问题Promise.all([insights, students, schedule]) 后串行 getClassStudentSubjectScoresV2
  • 改进建议:将 getClassStudentSubjectScoresV2 加入第一个 Promise.all

BUG-T27串行数据获取 waterfalldiagnostic/student/[studentId]/page.tsx

BUG-T28串行数据获取 waterfallexams/[id]/build/page.tsx

  • 位置exams/[id]/build/page.tsx:12-26
  • 问题getExamByIdgetQuestionsgetQuestions(ids) 三段串行
  • 改进建议:前两个可并行;第三个依赖 exam.questions 的 ID 列表,需串行但可优化

BUG-T29Bundle 优化 - barrel importslucide-react

  • 位置:几乎所有页面文件
  • 问题import { PlusCircle, BarChart3, ClipboardList } from "lucide-react" 使用 barrel 文件导入,违反 bundle-barrel-imports 规则
  • 改进建议lucide-react 已支持 tree-shaking但可考虑使用 lucide-react/icons 直接导入路径

BUG-T30缺少 export const dynamic = "force-dynamic" 声明


2.6 Web 界面规范违规web-design-guidelines — 严重度:中

BUG-T31<a> 标签缺少 focus-visible 焦点样式

BUG-T32<a> 标签作为筛选按钮语义不当

  • 位置:同 BUG-T31
  • 问题:筛选操作使用 <a> 标签导航到带 query 的 URL虽然支持 Cmd/Ctrl+click但视觉上是按钮形态应使用 <button> 或添加 role="button"
  • 违反规则<button> for actions, <a>/<Link> for navigation
  • 改进建议:使用 Next.js <Link> 并补充焦点样式,或改为 <button> + useRouter + useSearchParams

BUG-T33标题层级缺失exams/[id]/build/page.tsx

  • 位置exams/[id]/build/page.tsx:104-118
  • 问题:页面无 <h1> 标题,直接渲染 <ExamAssembly> 组件违反「Headings hierarchical <h1><h6>
  • 改进建议:在页面顶部添加 <h1> 标题如「Build Exam」

BUG-T34标题层级缺失exams/[id]/proctoring/page.tsx

BUG-T35标题层级缺失classes/my/[id]/page.tsx

  • 位置classes/my/[id]/page.tsx:65-108
  • 问题:页面无 <h1>,依赖 <ClassHeader> 组件渲染标题,需确认组件内是否有 h1
  • 改进建议:确认 ClassHeader 包含 <h1>

BUG-T36长文本未截断homework/assignments/page.tsx

  • 位置homework/assignments/page.tsx:99-101
  • 问题:作业标题 <Link>{a.title}</Link> 未限制长度,长标题会破坏表格布局
  • 违反规则Content Handling - Text containers handle long content
  • 改进建议:添加 line-clamp-2truncate max-w-[200px]

BUG-T37长文本未截断homework/submissions/page.tsx

BUG-T38长文本未截断homework/assignments/[id]/submissions/page.tsx

BUG-T39Flex 子元素缺少 min-w-0

BUG-T40使用 transition: alltransition-colors 未列明属性

  • 位置
  • 问题transition-colors 实际上列明了属性,符合规范;但需检查是否有 transition: all 使用
  • 现状:未发现 transition: all,此项通过

BUG-T41硬编码日期/数字格式

  • 位置:所有使用 formatDate 的文件
  • 问题:需确认 formatDate 内部是否使用 Intl.DateTimeFormat,若使用硬编码格式则违规
  • 违反规则Locale & i18n - Dates/times: use Intl.DateTimeFormat
  • 改进建议:检查 shared/lib/utils.tsformatDate 实现

BUG-T42数字列未使用 tabular-nums

BUG-T43大列表未虚拟化

  • 位置
  • 问题:题库页面一次加载 200 条题目,若渲染全部 DOM 节点会卡顿
  • 违反规则Performance - Large lists (>50 items): virtualize
  • 改进建议:使用 virtuacontent-visibility: auto 虚拟化长列表

2.7 组件规范违规 — 严重度:中

BUG-T44不必要的包装组件classes/my/page.tsx

  • 位置classes/my/page.tsx:6-17
  • 问题:默认导出 MyClassesPage 仅调用 MyClassesPageImpl,多此一举
  • 现状
    export default function MyClassesPage() {
      return <MyClassesPageImpl />
    }
    
    async function MyClassesPageImpl() {
      // ...
    }
    
  • 改进建议:直接默认导出 async 函数:
    export default async function MyClassesPage() {
      const [classes, subjectOptions] = await Promise.all([...])
      return <MyClassesGrid classes={classes} subjectOptions={subjectOptions} />
    }
    

BUG-T45非导出组件定义在 page.tsx 中

BUG-T46exams/create/page.tsx 顶部多余空行

  • 位置exams/create/page.tsx:5
  • 问题JSX 开始标签前有多余空行
  • 现状
    return (
    
      <div className="...">
    
  • 改进建议:删除空行

2.8 安全与权限违规 — 严重度:高

BUG-T47缺少权限校验course-plans/page.tsx

  • 位置course-plans/page.tsx
  • 问题:仅通过 auth() 获取 session未调用 requirePermission()getAuthContext() 进行权限校验
  • 改进建议:使用 getAuthContext() 替代 auth(),并在 data-access 层做 DataScope 过滤

BUG-T48缺少权限校验elective/page.tsx

BUG-T49缺少权限校验dashboard/page.tsx

  • 位置dashboard/page.tsx
  • 问题依赖路由层代理proxy.ts做角色路由但页面本身未做二次权限校验
  • 改进建议:添加 getAuthContext() 确认教师身份

BUG-T50权限校验方式不一致


2.9 加载态缺失 — 严重度:低

BUG-T51缺少 loading.tsx 的目录

  • 位置
    • attendance/(含 sheet/、stats/
    • course-plans/(含 [id]/
    • diagnostic/(含 class/、student/
    • elective/
    • exams/[id]/(含 build/、proctoring/
    • grades/(含 analytics/、entry/、stats/
    • homework/(含 assignments/、submissions/
    • schedule-changes/
  • 问题:以上目录无 loading.tsx,导航时无骨架屏反馈
  • 改进建议:为每个动态页面目录添加 loading.tsx,参考 classes/my/loading.tsx 模式

BUG-T52exams/grading/loading.tsx 实际无用

  • 位置exams/grading/loading.tsx
  • 问题exams/grading/page.tsx 仅做 redirect()loading.tsx 永远不会显示
  • 改进建议:删除该 loading.tsx

2.10 逻辑与代码质量问题 — 严重度:中

BUG-T53homework/assignments/page.tsx 条件取数逻辑反直觉

  • 位置homework/assignments/page.tsx:33-36
  • 问题classId && classId !== "all" ? getTeacherClasses() : Promise.resolve([]) 仅在有 classId 时才获取班级列表,逻辑反直觉(通常应始终获取班级列表用于筛选下拉)
  • 现状classes 仅用于查找 className 显示,逻辑正确但可读性差
  • 改进建议:始终获取 classes或添加注释说明「仅在过滤时需要 className」

BUG-T54exams/[id]/build/page.tsx normalizeStructure 函数过长

  • 位置exams/[id]/build/page.tsx:52-91
  • 问题40 行的 normalizeStructure 函数定义在组件内部,包含嵌套递归逻辑,可读性差
  • 改进建议:提取到 modules/exams/utils/normalize-structure.ts,并添加单元测试

BUG-T55exams/[id]/build/page.tsx 使用 satisfies 但混合 as

  • 位置exams/[id]/build/page.tsx:74, 84, 86
  • 问题:同时使用 satisfies ExamNode(好)和 as ExamNode[](违规),类型处理不一致
  • 改进建议:移除 as ExamNode[],改用类型守卫或 Array.from() 配合 filter

BUG-T56grades/analytics/page.tsx 文件过长

  • 位置grades/analytics/page.tsx - 259 行
  • 问题:单文件 259 行,接近 React 组件 500 行建议上限的 50%,包含页面 + AnalyticsFilters 组件
  • 改进建议:将 AnalyticsFilters 提取到 modules/grades/components/analytics-filters.tsx

BUG-T57exams/all/page.tsx 缺少 export const dynamic

  • 位置exams/all/page.tsx
  • 问题:使用 Suspense 但未声明 force-dynamic,可能导致构建时尝试静态生成
  • 改进建议:添加 export const dynamic = "force-dynamic"

2.11 可访问性问题 — 严重度:中

BUG-T58图标按钮缺少 aria-label

BUG-T59装饰性图标未标记 aria-hidden

  • 位置:几乎所有页面中的 lucide 图标
  • 问题:如 <BarChart3 className="mr-2 h-4 w-4" /> 等装饰性图标未添加 aria-hidden="true"
  • 违反规则Accessibility - Decorative icons need aria-hidden="true"
  • 改进建议:装饰性图标添加 aria-hidden="true"
  • 位置:所有页面
  • 问题:页面无「跳到主内容」的 skip link键盘用户需 Tab 遍历整个侧边栏
  • 违反规则Accessibility - include skip link for main content
  • 改进建议:在 dashboard layout 添加 skip link应在 layout 层处理)

2.12 其他问题

BUG-T61homework/assignments/[id]/page.tsx 使用 h1 但其他页面用 h2

BUG-T62textbooks/page.tsx 使用 h1其他页面用 h2

BUG-T63exams/create/page.tsx 缺少页面标题

  • 位置exams/create/page.tsx:3-9
  • 问题:页面无任何标题,直接渲染表单
  • 改进建议:添加 <h1>Create Exam</h1>

BUG-T64loading.tsx 文件命名风格不一致

  • 位置
  • 问题:骨架屏风格不统一,部分用 Card 组件,部分用纯 div
  • 改进建议:统一骨架屏风格,提取共享骨架屏组件

三、改进优先级汇总

P0 - 立即修复(架构与安全)

BUG ID 问题 影响
T01-T05 app 层直接访问 DB 破坏三层架构,模块封装失效
T06 认证方式不一致 数据范围过滤缺失
T47-T50 权限校验缺失/不一致 越权访问风险

P1 - 高优先级TypeScript 与性能)

BUG ID 问题 影响
T11-T15 使用 as 类型断言 类型安全受损
T16-T17 函数返回值未标注 类型推导不显式
T20-T28 串行数据获取 waterfall 页面加载性能差
T43 大列表未虚拟化 题库页面卡顿

P2 - 中优先级(规范与可访问性)

BUG ID 问题 影响
T07-T10 Prettier 分号违规 代码风格不一致
T18-T19 DRY 违规 维护成本高
T31-T32 筛选按钮焦点样式/语义 键盘可访问性差
T36-T39 长文本未截断 布局破坏风险
T42 数字列未用 tabular-nums 数字对齐不整齐
T58-T60 可访问性缺失 屏幕阅读器体验差

P3 - 低优先级(代码质量)

BUG ID 问题 影响
T44-T46 组件定义问题 可读性差
T51-T52 loading.tsx 缺失/冗余 用户体验不一致
T53-T57 逻辑与长度问题 可维护性
T61-T64 标题层级与风格 一致性

四、推荐改进方案

4.1 提取共享工具(解决 T16, T18

新建 src/shared/lib/search-params.ts

export type SearchParams = { [key: string]: string | string[] | undefined }

export function getParam(params: SearchParams, key: string): string | undefined {
  const v = params[key]
  return Array.isArray(v) ? v[0] : v
}

所有页面统一 import { getParam, type SearchParams } from "@/shared/lib/search-params"

4.2 提取共享筛选组件(解决 T19, T31, T32

新建 src/shared/components/ui/filter-chips.tsx

import Link from "next/link"
import { cn } from "@/shared/lib/utils"

interface FilterChip {
  id: string
  label: string
  href: string
  active: boolean
}

export function FilterChips({ chips }: { chips: FilterChip[] }) {
  return (
    <div className="flex flex-wrap gap-2">
      {chips.map((c) => (
        <Link
          key={c.id}
          href={c.href}
          className={cn(
            "rounded-md border px-3 py-1.5 text-sm transition-colors",
            "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
            c.active
              ? "border-primary bg-primary text-primary-foreground"
              : "bg-card hover:bg-accent"
          )}
        >
          {c.label}
        </Link>
      ))}
    </div>
  )
}

4.3 统一权限校验模式(解决 T47-T50

所有教师页面入口统一:

import { getAuthContext } from "@/shared/lib/auth-guard"

export default async function XxxPage() {
  const ctx = await getAuthContext()
  // 使用 ctx.userId、ctx.dataScope 进行数据过滤
}

4.4 并行数据获取优化(解决 T20-T28

将串行 await 改为 Promise.all

// 优化前
const classes = await getTeacherClasses()
const records = await getGradeRecords({ ... })

// 优化后
const [classes, records] = await Promise.all([
  getTeacherClasses(),
  getGradeRecords({ ... }),
])

4.5 DB 访问下沉到 data-access解决 T01-T05

modules/school/data-access.ts 添加:

import "server-only"
import { db } from "@/shared/db"
import { subjects } from "@/shared/db/schema"
import { asc } from "drizzle-orm"

export async function getSubjectsOrdered(): Promise<Subject[]> {
  return db.query.subjects.findMany({
    orderBy: [asc(subjects.order), asc(subjects.name)],
  })
}

页面改为 import { getSubjectsOrdered } from "@/modules/school/data-access"


五、架构图同步建议

本次核查未修改源码,无需同步架构图。但建议在后续修复时:

  1. 若新增 shared/lib/search-params.ts,需在 005_architecture_data.json 的 shared.lib.exports 中添加
  2. 若新增 shared/components/ui/filter-chips.tsx,需在 005 的 shared.components.exports 中添加
  3. modules/school/data-access.ts 新增 getSubjectsOrdered,需在 005 的 modules.school.dataAccess 中添加

六、总结

本次核查覆盖 src/app/(dashboard)/teacher/ 下全部 45 个前端文件,共发现 64 个问题,分布如下:

严重度 数量 类别
P0 9 架构违规、权限缺失
P1 16 TypeScript、性能
P2 18 规范、可访问性
P3 21 代码质量

核心问题

  1. 架构层违规严重5 处 app 层直接访问 DB破坏三层架构
  2. 权限校验不一致:部分页面无校验,部分用 auth(),部分用 getAuthContext()
  3. 性能 waterfall 普遍9 处串行数据获取,应改为并行
  4. DRY 违规突出getParam 函数在 16 个文件中重复
  5. 可访问性缺失焦点样式、aria-label、skip link 普遍缺失

建议按 P0 → P1 → P2 → P3 顺序修复,优先解决架构与安全问题。