Files
NextEdu/docs/architecture/004_architecture_impact_map.md
SpecialX 58656da983 feat(textbooks): 知识图谱功能全面重构 — 前置依赖 + dagre 布局 + React Flow 可视化 + 师生双视角
将教材模块图谱从基本无用状态升级为完整知识图谱可视化系统。

数据层:新增 knowledgePointPrerequisites 表(复合主键+双外键 cascade);新增 data-access-graph.ts(server-only)知识点关联聚合、学生/班级掌握度查询;utils.ts 新增 hasCycleAfterAddingEdge(DFS 循环依赖检测)。

业务层:3 个新 Server Action(getKnowledgeGraphDataAction 三视图模式、createPrerequisiteAction 含循环检测、deletePrerequisiteAction);graph-layout.ts 重写为 dagre 分层有向图布局。

视图层:knowledge-graph.tsx 重写为 React Flow 主组件(全书视图+搜索高亮+关联节点高亮+章节着色);4 个新组件(graph-kp-node/graph-prerequisite-edge/graph-toolbar/graph-node-detail-panel);use-graph-data.ts 派生值模式避免 effect 中 setState。

架构:严格三层架构,客户端通过 Server Action 间接访问 server-only 数据层;权限校验+ i18n 全覆盖;架构文档 004/005 同步。

测试:utils.test.ts 新增 5 个循环检测测试,graph-layout.test.ts 重写 5 个 dagre 布局测试,全部 30 个教材模块单元测试通过。

附带提交 drizzle/0005 error-book 迁移文件以保持 journal 一致性。
2026-06-23 00:13:03 +08:00

207 KiB
Raw Blame History

Next_Edu 架构影响地图

重写日期2026-06-17 目标:一次阅读即可直观理解整个项目架构 规则:源码修改后须同步更新本文档与 005_architecture_data.json 审查依据:docs/architecture/audit/ 下 4 份审查报告


目录


第一部分:全局架构概览

1.1 分层架构图

项目采用严格三层架构,依赖方向必须单向:app → modules → shared

┌─────────────────────────────────────────────────────────────────────┐
│  app/ (路由层)                                                       │
│  ─────────────────────────────────────────────────────────────────  │
│  (auth)/          login / register / privacy / terms                │
│  (dashboard)/     admin / teacher / student / parent / management   │
│  api/             REST API (auth, ai, upload, files, search, ...)   │
└──────────────────────────────┬──────────────────────────────────────┘
                               │ 调用 Server Actions / data-access
                               ▼
┌─────────────────────────────────────────────────────────────────────┐
│  modules/ (业务模块层)                                               │
│  ─────────────────────────────────────────────────────────────────  │
│  核心教学:  exams · homework · questions · textbooks · grades       │
│              · lesson-preparation                                     │
│  教学管理:  classes · school · scheduling · attendance · course-plans│
│  用户与沟通users · messaging · notifications · parent · audit      │
│  扩展功能:  elective · proctoring · diagnostic · dashboard          │
│  其他:      announcements · files · settings · auth · layout · student│
│                                                                     │
│  每个模块标准结构actions.ts (编排) → data-access.ts (DB) → schema.ts│
└──────────────────────────────┬──────────────────────────────────────┘
                               │ 依赖
                               ▼
┌─────────────────────────────────────────────────────────────────────┐
│  shared/ (基础设施层)                                                │
│  ─────────────────────────────────────────────────────────────────  │
│  db/              schema.ts (54 张表) + relations.ts + index.ts     │
│  lib/             auth-guard · permissions · ai · audit-logger ·    │
│                   change-logger · login-logger · password-policy ·  │
│                   rate-limit · excel · file-storage · ...           │
│  hooks/           use-permission · use-aria-live · ...              │
│  components/      ui/ (shadcn) · a11y/ · global-search · ...        │
│  types/           permissions · action-state                        │
└─────────────────────────────────────────────────────────────────────┘
                               ▲
                               │ 反向依赖(违规,见 1.2
┌──────────────────────────────┴──────────────────────────────────────┐
│  根模块src/auth.ts (NextAuth 配置) · src/proxy.ts (中间件)         │
│  ✅ shared/lib/{audit-logger, change-logger, auth-guard} 已通过       │
│     shared/lib/session.ts 单一入口获取 session不再直依赖 @/auth     │
│     (详见第三部分 P0-2                                              │
└─────────────────────────────────────────────────────────────────────┘

分层规则

  • app/ 只能调用 modules/ 的 Server Actions 和 data-access不直接访问 DB
  • modules/ 之间通过对方 data-access 通信,不直接查询对方表
  • shared/ 是被依赖方,不应反向依赖 app/modules/
  • src/auth.tssrc/proxy.ts 位于根目录,属于应用层

1.2 模块依赖关系图

下图展示模块间的实际依赖关系,标注依赖类型与合规性

图例

  • ───▶ 合理依赖(通过 data-access 或类型导入)
  • ═══▶ 违规依赖(直接查询对方 DB 表)
  • 循环依赖
  • ──▷ UI 组件组合(合理)

1.2.1 核心业务模块依赖

                    ┌──────────────┐
                    │  textbooks   │ ◀── 标杆模块(无跨模块 DB 访问)
                    └──────┬───────┘
                           │ ──▷ UI 组合knowledge-point-dialogs
                           │
            ┌──────────────┼──────────────┐
            │              │              │
            ▼              ▼              ▼
     ┌────────────┐  ┌──────────┐  ┌────────────┐
     │ questions  │  │  exams   │  │  homework  │
     └─────┬──────┘  └────┬─────┘  └─────┬──────┘
           │ ✅ P1-1      │ ✅ P1-1      │ ✅ P1-1 已修复
           │ 通过 textbooks│ 通过 questions│ 通过 exams/classes/
           │ data-access  │ data-access  │   school/users data-access
           │              │ 通过 classes  │
           │              │ data-access   │
           ┌─┴────────┐     │              │
           │ grades   │◀────┘ 仅外键引用(合理)
           │ (成绩)   │
           └────┬─────┘
                │ ✅ P1-1 已修复
                │ 通过 classes/school/users data-access
                ▼
           ┌──────────┐
           │ classes  │ ✅ P1-1 已修复:通过 homework/data-access-classes
           └────┬─────┘   获取作业数据,不再直查 homework/exams 表
                │ ✅ P0-1 已修复data-access.ts 已拆分为 5 文件
                │ ✅ P0-5 已修复classSchedule 写入口统一到 scheduling
                │
                    ┌─────────┼─────────┐
                    │         │         │
                    ▼         ▼         ▼
              ┌──────────┐ ┌──────┐ ┌──────────┐
              │scheduling│ │school│ │ attendance│
              └────┬─────┘ └──────┘ └──────────┘
                   │ ✅ P0-5 已修复
                   │ classSchedule 写入口统一到 scheduling
                   │ 通过 classes/data-access 校验归属
                   │ ✅ P1-1 已修复:通过 users data-access

1.2.2 扩展模块依赖

┌─────────────┐  ✅ P0-3 已修复:通过各模块 data-access    ┌──────────────┐
│ dashboard   │  获取数据getUsersDashboardStats 等)       │ users/classes│
│ (聚合层)    │──────────────────────────────────────▶│ /exams/...   │
└─────────────┘  ✅ P0-4 已修复Promise.all 并行调用     └──────────────┘

┌─────────────┐  ─── 调用 data-access合理   ┌──────────────┐
│ parent      │──────────────────────────────▶│ classes/      │
│ (聚合层)    │  ✅ P1-1 已修复:通过各模块     │ homework/grades│
└─────────────┘   data-access 获取数据          └──────────────┘

┌─────────────┐  ✅ P1-1 已修复:通过 exams/questions/      ┌──────────────┐
│ diagnostic  │  classes/users data-access 获取数据          │ exams/questions│
│             │──────────────────────────────────────▶│ /classes/users│
└─────────────┘                                       └──────────────┘

┌─────────────┐  ✅ P1-1 已修复:通过 exams/users           ┌──────────────┐
│ proctoring  │  data-access 获取数据                       │ exams/users   │
└─────────────┘──────────────────────────────────────▶└──────────────┘

┌─────────────┐  ─── 调用 notifications dispatcher     ┌──────────────┐
│ messaging   │  (通知偏好/CRUD 已迁移至 notifications│ notifications │
│             │──────────────────────────────────────▶│ (拥有         │
│             │  ✅ P0-4 / P1-5 已修复:单向依赖       │  messageNotif│
└─────────────┘                                       │  ications +  │
                                                      │  preferences)│
                                                      └──────────────┘

┌─────────────┐  ─── 调用 messaging Action       ┌──────────────┐
│ settings    │  (通知偏好表单)                 │ messaging     │
└─────────────┘──────────────────────────────▶└──────────────┘

┌─────────────────┐  ───▶ data-access合理       ┌──────────────┐
│ lesson-prep     │──────────────────────────────▶│ textbooks    │
│ (备课聚合层)    │  只读章节/知识点树              │ (章节/KP 树) │
│                 │──────────────────────────────▶└──────────────┘
│                 │──────────────────────────────▶┌──────────────┐
│                 │  创建/查询题目                  │ questions    │
│                 │──────────────────────────────▶└──────────────┘
│                 │──────────────────────────────▶┌──────────────┐
│                 │  创建 exam 草稿                │ exams        │
│                 │──────────────────────────────▶└──────────────┘
│                 │──────────────────────────────▶┌──────────────┐
│                 │  创建作业下发                  │ homework     │
│                 │──────────────────────────────▶└──────────────┘
│                 │──────────────────────────────▶┌──────────────┐
│                 │  查询教师班级                  │ classes      │
│                 │──────────────────────────────▶└──────────────┘
│                 │──────────────────────────────▶┌──────────────┐
│                 │  附件                          │ files        │
└─────────────────┘──────────────────────────────▶└──────────────┘

1.2.3 循环依赖详情 已修复

shared/lib/audit-logger.ts   ──┐
shared/lib/change-logger.ts  ──┼──▶ shared/lib/session.ts ──▶ (dynamic import) @/auth
shared/lib/auth-guard.ts     ──┘

src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
              ──▶ import { ... } from "@/shared/lib/login-logger"
              ──▶ import { ... } from "@/shared/lib/password-policy"
              ──▶ import { ... } from "@/shared/lib/rate-limit"
              ──▶ import { ... } from "@/shared/lib/role-utils"        # P1-3 拆出
              ──▶ import { ... } from "@/shared/lib/bcrypt-utils"      # P1-3 拆出
              ──▶ import { ... } from "@/shared/lib/http-utils"        # P1-3 拆出
              ──▶ import { ... } from "@/shared/lib/password-security-service"  # P1-3 拆出
              ──▶ import { db, schema } from "@/shared/db"

  ✅ 修复shared/lib/* 不再静态 import @/auth统一通过 session.ts 单一入口
     session.ts 内部使用 dynamic import("@/auth") 打破模块级静态循环
     运行时调用链保持不变,模块加载图无环

1.3 数据流向图(考试流程)

以"考试流程"为例,展示数据从创建到成绩统计的完整流向。

┌─────────────────────────────────────────────────────────────────────┐
│ 阶段 1教师创建考试                                                 │
│ ─────────────────────────────────────────────────────────────────  │
│ teacher/exams/create/page.tsx                                       │
│   └─▶ exams/actions.createExamAction                                │
│         ├─▶ requirePermission(EXAM_CREATE)  [shared/auth-guard]     │
│         ├─▶ persistExamDraft()              [exams/data-access]     │
│         │     └─▶ db.insert(exams)           [shared/db]            │
│         │     └─▶ db.insert(examQuestions)   [shared/db]            │
│         └─▶ revalidatePath("/teacher/exams")                        │
│                                                                     │
│ 数据写入exams 表 + examQuestions 表                                │
│ ✅ P0-1 已修复persistAiGeneratedExamDraft 改为调用 questions/data-access.createQuestionWithRelations不再直查 questions 表 │
└─────────────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────────┐
│ 阶段 2学生作答作业化考试                                        │
│ ─────────────────────────────────────────────────────────────────  │
│ student/learning/assignments/[assignmentId]/page.tsx                │
│   └─▶ homework/actions.startHomeworkSubmissionAction                │
│         ├─▶ requirePermission(HOMEWORK_SUBMIT)                      │
│         ├─▶ data-access-write.startHomeworkSubmission  ✅ P1-2 已修复 │
│         │     └─▶ db.insert(homeworkSubmissions)  [shared/db]       │
│         └─▶ 返回 submissionId                                       │
│                                                                     │
│   └─▶ homework/actions.saveHomeworkAnswerAction                     │
│         └─▶ data-access-write.saveHomeworkAnswer  ✅ P1-2 已修复     │
│              └─▶ db.transaction(insert homeworkAnswers) [shared/db] │
│                                                                     │
│   └─▶ homework/actions.submitHomeworkAction                         │
│         └─▶ data-access-write.submitHomework  ✅ P1-2 已修复         │
│              └─▶ db.update(homeworkSubmissions.status="submitted")  │
│                                                                     │
│ 数据写入homeworkSubmissions 表 + homeworkAnswers 表                │
│ ✅ P1-2 已修复actions 层不再直接 DB 操作,已下沉到 data-access-write│
└─────────────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────────┐
│ 阶段 3教师批改                                                     │
│ ─────────────────────────────────────────────────────────────────  │
│ teacher/homework/submissions/[submissionId]/page.tsx                │
│   └─▶ homework/actions.gradeHomeworkSubmissionAction                │
│         ├─▶ requirePermission(HOMEWORK_GRADE)                       │
│         └─▶ data-access-write.gradeHomeworkSubmission  ✅ P1-2 已修复│
│              └─▶ db.update(homeworkAnswers) 循环  [shared/db]       │
│                                                                     │
│ 数据更新homeworkAnswers.isCorrect / score / feedback               │
└─────────────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────────┐
│ 阶段 4成绩统计与诊断                                               │
│ ─────────────────────────────────────────────────────────────────  │
│ teacher/grades/page.tsx                                             │
│   └─▶ grades/data-access.getGradeRecords                            │
│         └─▶ ✅ P1-1 已修复:通过 classes/school/users data-access    │
│                                                                     │
│ teacher/diagnostic/page.tsx                                         │
│   └─▶ diagnostic/data-access.updateMasteryFromSubmission            │
│         ├─▶ ✅ P1-1 已修复:通过 exams data-access 获取提交          │
│         ├─▶ ✅ P1-1 已修复:通过 questions data-access 获取知识点    │
│         └─▶ 更新 knowledgePointMastery                              │
│                                                                     │
│ 数据读取homeworkSubmissions → grades → knowledgePointMastery        │
└─────────────────────────────────────────────────────────────────────┘

关键观察:考试流程横跨 4 个模块exams → homework → grades → diagnostic P1-1 已修复:所有跨模块查询已改为通过对方 data-access 接口,模块封装性已恢复。


1.4 核心调用链路

1.4.1 调用链路:创建考试(含 AI 出题)

[Client] exam-form.tsx
   │ FormData
   ▼
[Route]  POST /teacher/exams/create (Server Action)
   │
   ▼
[Action] exams/actions.createAiExamAction
   │
   ├─▶ requirePermission(EXAM_CREATE)
   │     └─▶ shared/lib/auth-guard.getAuthContext()
   │           ├─▶ auth()  [src/auth.ts]
   │           ├─▶ db.query.usersToRoles  [shared/db]
   │           ├─▶ db.query.classSubjectTeachers
   │           └─▶ 返回 { userId, roles, permissions, dataScope }
   │
   ├─▶ generateAiCreateDraftFromSource()
   │     └─▶ exams/ai-pipeline.ts (912 行)
   │           ├─▶ shared/lib/ai.createAiChatCompletion()
   │           │     └─▶ OpenAI SDK + db.query.aiProviders
   │           └─▶ JSON 解析 + Zod 校验
   │
   ├─▶ persistAiGeneratedExamDraft()
   │     └─▶ exams/data-access.ts
   │           ├─▶ db.insert(exams)              ✅ 合理
   │           ├─▶ db.insert(examQuestions)      ✅ 合理
   │           └─▶ questions/data-access.createQuestionWithRelations  ✅ P0-1 已修复:通过 data-access
   │
   └─▶ revalidatePath("/teacher/exams")

1.4.2 调用链路:学生提交作业

[Client] homework-take-view.tsx
   │
   ▼
[Action] homework/actions.submitHomeworkAction
   │
   ├─▶ requirePermission(HOMEWORK_SUBMIT)
   │
   ├─▶ data-access-write.submitHomework  ✅ P1-2 已修复
   │     (校验 submission 归属 + 更新状态)
   │     └─▶ db.update(homeworkSubmissions)
   │           SET status = "submitted", submittedAt = now()
   │
   └─▶ 返回 ActionState<{ submissionId }>

1.4.3 调用链路:管理员仪表盘聚合

[Route]  /admin/dashboard/page.tsx (Server Component)
   │
   ▼
[DataAccess] dashboard/data-access.getAdminDashboardData
   │
   ├─▶ users/data-access.getUsersDashboardStats()           ✅ 通过模块 data-access
   │     ├─ userCount / activeSessionsCount / userRoleCounts
   │     └─ recentUsers (含角色解析)
   ├─▶ classes/data-access.getClassesDashboardStats()       ✅ 通过模块 data-access
   │     └─ classCount
   ├─▶ textbooks/data-access.getTextbooksDashboardStats()   ✅ 通过模块 data-access
   │     └─ textbookCount / chapterCount
   ├─▶ questions/data-access.getQuestionsDashboardStats()   ✅ 通过模块 data-access
   │     └─ questionCount
   ├─▶ exams/data-access.getExamsDashboardStats(scope?)     ✅ 通过模块 data-access
   │     └─ examCount (含 scope 过滤)
   └─▶ homework/stats-service.getHomeworkDashboardStats(scope?)  ✅ 通过模块 data-access
         ├─ homeworkAssignmentCount / homeworkAssignmentPublishedCount
         └─ homeworkSubmissionCount / homeworkSubmissionToGradeCount

   ✅ P0-4 已修复dashboard 改为并行调用各模块 dashboard stats 函数,不再直查跨模块表

1.4.4 调用链路admin 路由组统一权限守卫

[Layout]  app/(dashboard)/admin/layout.tsx (Server Component)
   │
   ▼
[AuthGuard] shared/lib/auth-guard.getAuthContext()
   │
   └─▶ getSession() → 校验已登录(未登录抛 PermissionDeniedError
   │
   ▼
[Children] 各 admin/* 页面page.tsx在函数体首行调用 requirePermission(XXX)
   │
   ├─▶ /admin/school/schools         → requirePermission(SCHOOL_MANAGE)
   ├─▶ /admin/school/academic-year   → requirePermission(SCHOOL_MANAGE)
   ├─▶ /admin/school/classes         → requirePermission(SCHOOL_MANAGE)
   ├─▶ /admin/school/departments     → requirePermission(SCHOOL_MANAGE)
   ├─▶ /admin/school/grades          → requirePermission(SCHOOL_MANAGE)
   ├─▶ /admin/school/grades/insights → requirePermission(SCHOOL_MANAGE)
   ├─▶ /admin/users/import           → requirePermission(USER_MANAGE)
   ├─▶ /admin/users                  → requirePermission(USER_MANAGE)
   ├─▶ /admin/scheduling/auto        → requirePermission(SCHEDULE_AUTO)
   ├─▶ /admin/scheduling/changes     → requirePermission(SCHEDULE_ADJUST)
   ├─▶ /admin/scheduling/rules       → requirePermission(SCHEDULE_ADJUST)
   ├─▶ /admin/announcements          → requirePermission(ANNOUNCEMENT_MANAGE)
   ├─▶ /admin/announcements/[id]     → requirePermission(ANNOUNCEMENT_MANAGE)
   └─▶ /admin/audit-logs             → requirePermission(AUDIT_LOG_READ)

   ✅ P0 安全修复admin/layout.tsx 提供登录态统一守卫,
      各页面 requirePermission() 提供细粒度权限校验

   teacher/classes/* 路由权限校验2026-06-22 审计修复):
   ├─▶ /teacher/classes/my           → requirePermission(CLASS_READ)  ✅ 已修复
   ├─▶ /teacher/classes/my/[id]      → requirePermission(CLASS_READ)  ✅ 已修复
   ├─▶ /teacher/classes/schedule     → requirePermission(CLASS_READ)  ✅ 已修复
   └─▶ /teacher/classes/students     → requirePermission(CLASS_READ)  ✅ 已修复

第二部分:模块清单

每个模块包含:职责 · 导出函数 · 依赖关系 · 已知问题 · 文件清单

2.1 shared基础设施层

职责:提供全项目共享的 DB Schema、工具函数、权限系统、UI 基础组件、通用 Hooks。

导出函数(核心):

  • getAuthContext() / requirePermission(p) / requireAuth() — 认证与权限
  • resolvePermissions(roles) / resolveDataScope(userId, roles) — 权限解析
  • logAudit() / logLoginEvent() / logDataChange() — 日志记录
  • createAiChatCompletion() / parseAiChatPayload() — AI 调用
  • validatePassword() / isAccountLocked() / rateLimit() — 安全策略
  • exportToExcel() / parseExcel() / generateTemplate() — Excel 工具
  • cn() / formatDate() / formatFileSize() — 通用工具
  • getInitials(name) / formatDateForFile(d?) — 通用工具P1-c / P1-a 重构新增:从 parent/lib/utils.ts、grades/export-button.tsx 等多处重复实现抽取)
  • downloadBase64File(base64, filename, mimeType?) / downloadBlob(blob, filename) — 客户端文件下载P1-c 重构新增:从 grades/export-button、users/user-import-dialog、audit/audit-log-export-button 三处重复实现抽取,位于 lib/download.ts

共享组件导出P0-b / P1-a / P1-b / P1-c / P2-a / P2-b / P3-a / P3-b / P3-c / P3-d / 第二轮 P0-1/P0-2/P0-3/P1-1/P1-2/P1-3/P1-4 重构新增,按类别组织):

类别 组件 文件 用途 消费方数量
UI 组件 StatCard components/ui/stat-card.tsx 统计卡片(标题+数值+图标+描述+跳转+骨架屏) 8 个P1-a
UI 组件 StatItem components/ui/stat-item.tsx 紧凑统计项label+icon+value+hint用于统计面板网格 8 个P1-a
UI 组件 ChipNav components/ui/chip-nav.tsx 芯片导航组(通过 URL search params 切换筛选维度Link 跳转) 3 个P1-b
UI 组件 PageHeader components/ui/page-header.tsx 页面头部(标题+描述+icon+actions响应式布局 2 个P2-b: profile/page.tsx, settings/security/page.tsx
UI 组件 FilterBar / FilterSearchInput / FilterResetButton components/ui/filter-bar.tsx 筛选栏容器+搜索框+重置按钮统一布局壳URL 状态由各模块处理) 5 个P3-b: exam/textbook/question/audit-log/login-log filters
UI 组件 ConfirmDeleteDialog components/ui/confirm-delete-dialog.tsx 通用删除确认对话框AlertDialog 包装,支持自定义 confirmText/cancelText 5 个P0-1: announcement-detail, message-detail, course-plan-detail, grade-classes-view, students-table
UI 组件 Pagination components/ui/pagination.tsx 通用分页 UIShowing X-Y of Z + Page X of Y + 上一页/下一页按钮) 3 个P0-2: audit-log-table, login-log-table, data-change-log-table
UI 组件 EmptyTableRow components/ui/empty-table-row.tsx 表格空状态行TableRow + TableCell 居中显示空状态文案) 3 个P0-3: audit-log-table, login-log-table, data-change-log-table
UI 组件 StatusBadge components/ui/status-badge.tsx 通用状态徽章Badge + 状态→variant/label/className 映射表,修复 in_progress 颜色不一致 bug 9+ 个P1-1: audit 3 文件, grades 2 文件, student/learning/assignments, parent/child-homework-summary, student-upcoming-assignments-card, question-columns
表单字段 TextField components/form-fields/text-field.tsx 通用文本字段FormField + Input 包装,支持 text/number/password/datetime-local 类型 + value 转换器) 3 个文件 16 处P1-2: profile-settings-form 6, exam-basic-info-form 4, ai-provider-settings-card 4
表单字段 SelectField components/form-fields/select-field.tsx 通用选择字段FormField + Select 包装,支持 toSelectValue/fromSelectValue 处理 number↔string 4 个文件 8 处P1-2: exam-basic-info-form 3, ai-provider-settings-card 1, create-question-dialog 2, profile-settings-form 1
表单字段 TextareaField components/form-fields/textarea-field.tsx 通用多行文本字段FormField + Textarea 包装) 1 个P1-2: create-question-dialog
图表组件 ChartCardShell components/charts/chart-card-shell.tsx 图表卡片外壳Card+Header+EmptyState+Content 统一结构) 8 个P3-c
图表组件 TrendLineChart components/charts/trend-line-chart.tsx 趋势折线图LineChart 统一配置,支持单/多系列) 8 个P3-c: grade-trend-chart 等)
图表组件 SimpleBarChart components/charts/simple-bar-chart.tsx 柱状图BarChart 统一配置,支持单/多 Bar + Cell 分桶着色) 8 个P3-c: grade-distribution-chart 等)
图表组件 ComparisonRadarChart components/charts/comparison-radar-chart.tsx 对比雷达图RadarChart 统一配置,支持双 Radar 对比) 8 个P3-c: subject-comparison-chart, mastery-radar-chart 等)
课表组件 ScheduleList / ScheduleListItem components/schedule/schedule-list.tsx 课表列表+列表项(课程+时间+地点+班级徽章separator/card 两种变体) 3 个P3-a: student-today-schedule-card, child-schedule-card, student-schedule-view
题库组件 QuestionBankFilters components/question/question-bank-filters.tsx 题库筛选栏(搜索+题型+难度default/compact 两种布局) 2 个P3-d: exam-assembly, question-bank-picker
设置组件 SettingsView modules/settings/components/settings-view.tsx 统一设置页布局5 标签页General/Notifications/Appearance/Security/AI角色差异通过 props 注入Tab URL 持久化,登出二次确认) 4 个P2-a: admin/teacher/student/parent 设置页)

共享 Hooks 导出(第二轮 P1-4 重构新增):

Hook 文件 签名 用途 消费方
useActionMutation hooks/use-action-mutation.ts useActionMutation<T>(options?): { isWorking, mutate } 通用 Server Action mutation Hook替代 50+ 文件中重复的 setIsWorking + try/catch/finally + toast 模式 1 个示范P1-4: schools-view潜在影响 50+ 文件
useActionQuery hooks/use-action-query.ts useActionQuery<T>(action, options?): { data, loading, error, refetch } 通用 Server Action 查询 Hook替代 11 个文件中重复的 useEffect + useState(loading) + Action().then().catch().finally() 模式,内置竞态防护 1 个示范P1-4: create-question-dialog潜在影响 11 个文件

共享工具函数导出(第二轮 P1-3 重构新增):

函数 文件 签名 用途 消费方
formatDateTime lib/utils.ts formatDateTime(date, locale?): string 国际化日期+时间格式化(含小时、分钟) 4 个P1-3: lesson-plan-card, version-history-drawer, proctoring-dashboard, exam-ai-generator
formatLongDate lib/utils.ts formatLongDate(date, locale?): string 国际化长日期格式化(含星期、完整月份名),默认 zh-CNweekday=short 1 个P1-3: teacher-dashboard-header

注:SettingsView 位于 modules/settings/components/(非 shared 层),因仅被 settings 模块消费,未下沉到 shared。此处列出以完整反映本次重构的组件抽取范围。

依赖关系

  • 被依赖方:所有模块依赖 shared
  • 反向依赖已修复:shared/lib/{audit-logger, change-logger, auth-guard} 通过 shared/lib/session.ts 单一入口获取 session不再直接依赖 @/auth

已知问题

  • P0shared/lib/*@/auth 循环依赖 已修复(新增 shared/lib/session.ts 封装 session 获取3 个文件改为 import { getSession } from "@/shared/lib/session"
  • ⚠️ P1schema.ts 1111 行54 张表混合,超 1000 硬上限)
  • P1auth.ts 293 行混合 5 类职责 已拆分4 个辅助函数组迁移至 shared/lib/{role-utils,bcrypt-utils,http-utils,password-security-service}auth.ts 仅保留 NextAuth 配置)
  • P2-2 已修复:ai.ts 218 行混合 5 类职责 已拆分为 ai/ 目录payload-parser.ts/api-key-crypto.ts/provider-config.ts/client.ts/errors.ts/index.tsai.ts 保留为向后兼容的重导出文件9 行)
  • P2-4 已修复:onboarding-gate.tsx 业务逻辑泄漏到 shared 已迁移至 modules/onboarding/actions/data-access/schema/types/components引导流程改为独立路由 /onboarding + middleware 重定向 + Server Action
  • P0-2/P0-3/P0-4/P0-5/P1-1/P1-2/P1-4/P1-5 已修复v3 对标 PowerSchool/Veracross/Auth0家长绑定三因子验证邮箱+生日+手机号后4位、教师多科目循环绑定、审计日志、服务端幂等、URL query 持久化步骤、局部错误收集、家长多子女动态行、跳过机制明确化
  • v3 i18n 体系引入:采用 next-intl 4.xwithout i18n routing 模式cookie 驱动 locale 切换,字典放在 shared/i18n/messages/{locale}/,支持 zh-CN/en 两种语言,不破坏现有路由组结构
  • v3 班级邀请码体系引入(对标 Google Classroom / 钉钉教育 / 智学网):新增 class_invitation_codes 表(独立表,支持有效期/次数限制/审计/多码并存6 位字母数字(剔除歧义字符 0/O/1/I/L空间 1.13 亿rate limit 防爆破10 次/5 分钟),审计日志全链路记录,懒清理过期码

文件清单

文件 行数 职责
db/schema.ts 1111 54 张表定义(超硬上限)
db/relations.ts - 表关系定义
db/index.ts - Drizzle 客户端
lib/auth-guard.ts - 认证上下文 + 权限校验 + DataScope
lib/permissions.ts - 角色-权限映射
lib/session.ts 38 session 获取单一入口getSessionserver-onlydynamic import 打破循环)
lib/ai.ts 9 向后兼容重导出P2-2 已拆分到 ai/ 目录)
lib/ai/payload-parser.ts 78 请求负载解析
lib/ai/api-key-crypto.ts 28 API Key 加密/解密
lib/ai/provider-config.ts 61 Provider 配置查询
lib/ai/client.ts 58 AI 客户端创建与调用
lib/ai/errors.ts 8 错误格式化
lib/ai/index.ts 5 聚合导出
lib/audit-logger.ts - 操作日志(通过 session.ts 获取 sessionhttp-utils 获取 IP/UA
lib/change-logger.ts - 数据变更日志(通过 session.ts 获取 sessionhttp-utils 获取 IP
lib/login-logger.ts - 登录日志(通过 http-utils 获取 IP/UA
lib/password-policy.ts - 密码策略纯函数
lib/rate-limit.ts - 内存滑动窗口限流
lib/role-utils.ts 31 角色规范化纯函数normalizeRole / resolvePrimaryRole
lib/bcrypt-utils.ts 18 bcrypt 哈希前缀规范化纯函数
lib/http-utils.ts 44 请求头解析resolveClientIp / getUserAgentserver-only
lib/password-security-service.ts 84 密码安全 DB 操作(账户锁定/失败登录追踪server-only
lib/excel.ts - Excel 导入导出
lib/file-storage.ts - 文件存储抽象
hooks/use-permission.ts - 客户端权限 Hook
components/ui/* 34 文件 shadcn/ui 标准组件
components/ui/stat-card.tsx 95 StatCard 统计卡片P1-a 新增)
components/ui/stat-item.tsx 38 StatItem 紧凑统计项P1-a 新增)
components/ui/chip-nav.tsx 78 ChipNav 芯片导航P1-b 新增)
components/ui/page-header.tsx 44 PageHeader 页面头部P2-b 新增,含 icon 属性)
components/ui/filter-bar.tsx 124 FilterBar + FilterSearchInput + FilterResetButtonP3-b 新增)
components/charts/chart-card-shell.tsx 90 ChartCardShell 图表卡片外壳P3-c 新增)
components/charts/trend-line-chart.tsx 153 TrendLineChart 趋势折线图P3-c 新增)
components/charts/simple-bar-chart.tsx 162 SimpleBarChart 柱状图P3-c 新增)
components/charts/comparison-radar-chart.tsx 143 ComparisonRadarChart 对比雷达图P3-c 新增)
components/schedule/schedule-list.tsx 112 ScheduleList + ScheduleListItem 课表列表P3-a 新增)
components/question/question-bank-filters.tsx 137 QuestionBankFilters 题库筛选栏P3-d 新增)
lib/download.ts 47 downloadBase64File + downloadBlob 客户端下载工具P1-c 新增)
lib/utils.ts - 通用工具P1-a/P1-c 新增 getInitials + formatDateForFile
components/onboarding-gate.tsx 312 引导流程(业务泄漏) 已废弃,逻辑迁移至 modules/onboarding/P2-4 已修复)
components/global-search.tsx 221 全局搜索(业务泄漏)
types/permissions.ts 157 61 个权限点常量 + Role/DataScope/AuthContext 类型

2.2 exams考试模块

职责:考试全生命周期管理(创建/编辑/预览/发布/删除/复制)+ AI 辅助出题。

导出函数

  • ActionscreateExamAction / createAiExamAction / previewAiExamAction / regenerateAiQuestionAction / updateExamAction / deleteExamAction / duplicateExamAction / getExamPreviewAction / getSubjectsAction / getGradesAction P1-2 已修复actions 层不再直接访问 DB全部下沉到 data-access
  • Data-accessgetExams / getExamById / persistExamDraft / persistAiGeneratedExamDraft / buildExamDescription / resolveSubjectGradeNames / getExamCreatorId / updateExamWithQuestions / deleteExamById / duplicateExam / getExamPreview / getExamSubjects / getExamGrades(后 7 个为 P1-2 新增)
  • AI PipelinegenerateAiCreateDraftFromSource / generateAiPreviewData / regenerateAiQuestionByInstruction
  • UtilsnormalizeStructurev3 新增:将持久化的 exam.structure unknown JSON 运行时校验并归一化为类型安全的 ExamNode[],类型守卫模式无 as 断言,从 teacher/exams/[id]/build/page.tsx 提取)

依赖关系

  • 依赖:shared/*@/authquestions P0-1 已修复:通过 data-access.createQuestionWithRelationsclasses P0-2 已修复:通过 data-access.getClassGradeIdsByClassIdsschool P1-1 已修复:通过 school data-access.getSubjectOptions/getGradeOptions
  • 被依赖:homework(通过 sourceExamId 外键,合理)、dashboard(通过 data-accessP0-4 已修复)、proctoring P1-1 已修复:通过 exams data-accessdiagnostic P1-1 已修复:通过 exams data-access

已知问题

  • P0-1 已修复:persistAiGeneratedExamDraft 直接 insert 到 questions 改为调用 questions/data-access.createQuestionWithRelations,通过 ID 映射保持 structure 引用一致
  • P0-2 已修复:getExams/getExamById/getExamsDashboardStats 直查 classes 改为调用 classes/data-access.getClassGradeIdsByClassIds
  • P1-1 已修复:getSubjectsAction/getGradesAction 直查 subjects/grades 改为调用 school/data-access.getSubjectOptions / getGradeOptions
  • P1-2 已修复:actions.ts 832 行(超 800 建议),多处直接 DB 操作 DB 操作已下沉到 data-accessactions.ts 现 691 行
  • ⚠️ P1ai-pipeline.ts 857 行(超 800 建议),混合 4 类职责
  • P2 已修复:ai-pipeline.ts 中 3 处非空断言清理(draft.sections!.forEach → 安全守卫、aiParsed.sections!.flatMap?? []aiParsed.sections!.map?? []

文件清单

文件 行数 职责
actions.ts 691 10 个 Server ActionP1-2 已修复,无直接 DB 操作)
ai-pipeline.ts 857 AI 出题管线(超限)
data-access.ts 473 考试 CRUD含 P1-2 新增 7 个写/查询函数P0-1/P0-2 已修复:通过 questions/classes data-access 跨模块通信)
types.ts 31 类型定义
hooks/use-exam-preview.ts 295 预览 Hook
utils/normalize-structure.ts 57 v3 新增exam.structure 运行时校验与归一化(从 build/page.tsx 提取)
components/* 18 文件 考试表单/组卷/预览组件

2.3 homework作业模块

职责:作业全生命周期(创建/发布/作答/批改/分析)。

导出函数

  • ActionscreateHomeworkAssignmentAction / startHomeworkSubmissionAction / saveHomeworkAnswerAction / submitHomeworkAction / gradeHomeworkSubmissionAction P1-2 已修复actions 层不再直接访问 DB全部下沉到 data-access/data-access-write
  • Data-accessgetHomeworkAssignments / getHomeworkAssignmentById / getHomeworkSubmissions / getStudentHomeworkAssignments / getStudentHomeworkTakeData / getHomeworkAssignmentReviewList / getHomeworkSubmissionDetails / getDemoStudentUser(已迁移至 users 模块 getCurrentStudentUser,此处为 re-export 向后兼容)/ isRecord / toQuestionContent / getAssignmentMaxScoreById(后三者供 stats-service 使用)
  • Data-access-classesgetAssignmentIdsForStudents / getHomeworkAssignmentsWithSubject / getHomeworkAssignmentsByIds / getAssignmentTargetCounts / getHomeworkSubmissionsForStudents / getPublishedHomeworkAssignmentsWithSubject / getHomeworkSubmissionsForAssignmentsP0-7 新增,供 classes 模块跨模块调用,封装 homework/exams 表查询)
  • Data-access-write10 个写操作函数P1-2 新增,从 actions 下沉)
  • Stats-servicegetTeacherGradeTrends / getHomeworkAssignmentAnalytics / getStudentDashboardGrades(从 data-access.ts re-export 以保持向后兼容)

依赖关系

  • 依赖:shared/*@/authexams P1-1 已修复:通过 exams data-access.getExamIdsByGradeIds/getExamSubjectIdMap/getExamWithQuestionsForHomeworkclasses P1-1 已修复:通过 classes data-access.getStudentIdsByClassId 等 7 个函数)、school P1-1 已修复:通过 school data-access.getSubjectOptionsusers P1-1 已修复:通过 users data-access.getUserWithRole/getUserNamesByIds
  • 被依赖:dashboard(通过 data-access合理parent(通过 data-access合理classes P0-7 已修复classes 通过 homework/data-access-classes 获取作业数据,不再反向直查 homework/exams 表)

已知问题

  • P0 已解决:data-access.ts 已拆分至 598 行(原 1038 行超 1000 硬上限),统计函数迁移至 stats-service.ts
  • P0 已解决:getStudentDashboardGrades 排名计算逻辑迁移至 stats-service.ts
  • P0 已解决:getHomeworkAssignmentAnalytics 错误率统计逻辑迁移至 stats-service.ts
  • P0-7 已修复:新增 data-access-classes.ts,将 classes 模块对 homework/exams 表的直查封装为 homework 模块的导出函数,恢复三层架构
  • P1-1 已修复:5 处直查 exams 改为调用 exams/data-access.getExamIdsByGradeIds / getExamSubjectIdMap / getExamWithQuestionsForHomework
  • P1-2 已修复:actions.ts 多处直接 DB 操作(createHomeworkAssignmentAction 157 行) DB 操作已下沉到 data-access-write.tsactions.ts 现 239 行

文件清单

文件 行数 职责
data-access.ts 598 作业 CRUD + 学生视角 + 批改(含 re-export stats 函数)
data-access-write.ts 285 作业写操作P1-2 新增10 个写函数从 actions 下沉)
data-access-classes.ts 232 跨模块查询封装P0-7 新增,供 classes 模块调用,封装 homework/exams 表查询)
stats-service.ts 425 统计分析(教师趋势/作业分析/学生仪表盘成绩)
actions.ts 239 5 个 Server ActionP1-2 已修复,无直接 DB 操作)
types.ts 186 类型定义
schema.ts 29 Zod 校验

2.4 questions题库模块

职责:题库管理(题目 CRUD、知识点关联、题型支持

导出函数

  • ActionsgetQuestionsAction / createQuestionAction / updateQuestionAction / deleteQuestionAction / getKnowledgePointOptionsAction P1-2 已修复actions 层不再直接访问 DB全部下沉到 data-access
  • Data-accessgetQuestions / createQuestionWithRelations / updateQuestionById / deleteQuestionByIdRecursive / getKnowledgePointOptions(后 4 个为 P1-2 新增/迁移)

依赖关系

  • 依赖:shared/*@/authtextbooks P1-1 已修复:通过 textbooks data-access.getKnowledgePointOptions
  • 被依赖:exams(通过类型导入,合理)、textbooksUI 组合,合理)

已知问题

  • P1-2 已修复:写操作函数错放在 actions.tsinsertQuestionWithRelations / deleteQuestionRecursive 已下沉到 data-accesscreateQuestionWithRelations / updateQuestionById / deleteQuestionByIdRecursive / getKnowledgePointOptions
  • P1-1 已修复:getKnowledgePointOptionsAction 直查 textbooks 模块表 改为调用 textbooks/data-access.getKnowledgePointOptions
  • P2 已解决:data-access.ts 仅 129 行,写操作缺失 P1-2 后 data-access.ts 扩充至 260 行

文件清单

文件 行数 职责
actions.ts 149 5 个 Server ActionP1-2 已修复,无直接 DB 操作)
data-access.ts 260 题目 CRUD + 知识点选项(含 P1-2 新增 4 个写/查询函数)
schema.ts 18 Zod 校验
types.ts 34 类型定义

2.5 textbooks教材模块— 标杆模块data-access 层)

职责:教材与知识体系管理(教材/章节树形结构、知识点 CRUD、Markdown 内容编辑、知识图谱)。

导出函数

  • Actions14 个,均为写操作;读操作由 RSC 页面直接调用 data-accesscreateTextbookAction / updateTextbookAction / deleteTextbookAction / createChapterAction / updateChapterContentAction / deleteChapterAction / reorderChaptersAction / createKnowledgePointAction / updateKnowledgePointAction / deleteKnowledgePointAction / getKnowledgePointsByChapterAction / getKnowledgeGraphDataAction Task 7 新增:知识图谱数据查询,支持 structure/student-mastery/class-mastery 三种视图)/ createPrerequisiteAction Task 7 新增:声明前置依赖,含循环检测)/ deletePrerequisiteAction Task 7 新增:删除前置依赖)
  • Data-accessgetTextbooks / getTextbookById / getChaptersByTextbookId / getKnowledgePointsByChapterId / getKnowledgePointsByTextbookId / createTextbook / updateTextbook / deleteTextbook / createChapter / updateChapterContent / deleteChapter / createKnowledgePoint / updateKnowledgePoint / deleteKnowledgePoint / reorderChapters / getTextbooksDashboardStats / getKnowledgePointOptions(跨模块接口,供 questions 调用)/ getTextbooksWithScopeP1-1 新增:按数据范围获取教材列表,学生端强制按年级过滤)/ verifyChapterBelongsToTextbookP0-4 新增:资源归属校验)/ verifyKnowledgePointBelongsToTextbookP0-4 新增:资源归属校验)/ createPrerequisite Task 7 新增:创建前置依赖)/ deletePrerequisite Task 7 新增:删除前置依赖)/ getPrerequisiteEdgesForTextbook Task 7 新增:获取教材下所有前置依赖边,用于循环检测)/ getSubjectLabelKey / getGradeLabelKeyi18n 标签键)
  • Data-access-graph Task 5 新增,图谱只读查询):getKnowledgePointsWithRelations(知识点+依赖+题目数聚合查询)/ getStudentKpMastery(学生掌握度)/ getClassKpMastery(班级平均掌握度)/ getPrerequisitesForKp / getSuccessorsForKp
  • Constants 新增):SUBJECTS / GRADES / SUBJECT_COLORS / getSubjectColor / getSubjectLabelKey / getGradeLabelKey
    • v1 测试修复:SUBJECTS 新增 Chinese 学科、GRADES 新增 Grade 1/Grade 2,与 seed 数据对齐
    • v1 测试修复:SUBJECT_COLORS 新增 Chinese 主题色rose
  • Utils 新增,纯函数 + 单测):sortChapters / buildChapterTree / buildChapterIndex / findChapterParent / filterKnowledgePointsByChapter / normalizeOptional / highlightKnowledgePoints / hasCycleAfterAddingEdgeTask 4 新增循环依赖检测DFS 算法)
  • Graph-layout 新增,纯函数 + 单测):computeGraphLayout
  • Components Task 13 新增):GraphNodeDetailPanel — 知识图谱节点详情侧边面板,展示知识点名称/描述/掌握度/关联题目/前置后置知识点,支持跳转与前置关系增删
  • Analytics 新增):TextbookAnalytics / TextbookAnalyticsProvider / useTextbookAnalytics

依赖关系

  • 依赖:shared/*@/auth@xyflow/react(知识图谱可视化)、@dagrejs/dagre(图谱分层布局算法)
  • 被依赖:questions P1-1 已修复:通过 textbooks data-accessexams(通过类型)、dashboard(通过 data-accessP0-4 已修复)
  • UI 层跨模块依赖已解耦:textbooks/components/knowledge-point-dialogs.tsx 不再直接 import questions 模块,改为通过 render prop 注入创建题目入口

已知问题

  • 无跨模块 DB 访问data-access 层)
  • actions 层编排模式标杆(权限校验 → 调用 data-access → revalidatePath
  • data-access 层职责单一
  • P2 已修复:data-access.tsbyId.get(pid)!.children.push 非空断言清理为安全守卫;or(...)! 非空断言清理为条件 push
  • P0 跨模块 UI 依赖已修复:knowledge-point-dialogs.tsx 通过 render prop 解耦,不再直接 import questions 模块
  • P0 前端权限硬编码已修复:改用 usePermission().hasPermission()
  • P0 i18n 已基本接入:chapter-sidebar-list.tsx / actions.ts / section-error-boundary.tsx(默认值改英文)均已接入 next-intl
  • P1 Server Action 资源归属校验已修复:新增 verifyChapterBelongsToTextbook / verifyKnowledgePointBelongsToTextbook
  • P1 data-access 数据范围过滤已修复:新增 getTextbooksWithScope,学生端强制按年级过滤
  • P1 Error Boundary 已补齐:新增 section-error-boundary.tsx
  • P1 知识点列表/弹窗重复实现已清理:移除无调用方的 knowledge-point-panel.tsx
  • P1 学科/年级选项统一:抽取到 constants.tsSUBJECTS / GRADES / SUBJECT_COLORS
  • v1 测试修复:textbook-reader.tsx 移除 SheetTrigger(错误置于 Sheet 外部导致 DialogTrigger must be used within Dialog),改用受控 Button + onClick 打开移动端抽屉
  • v1 测试修复:新增 teacher-textbook-reader.tsx 客户端包装组件,解决 Server Component 向 Client Component 传递函数 proprenderQuestionCreator)违反 Next.js 序列化约束的问题
  • v1 测试修复:scripts/seed.ts 教材 subject/grade 改为英文 valueconstants.ts 对齐),消除 i18n MISSING_MESSAGE 错误
  • P1 纯逻辑已导出并补单测:新增 utils.ts / graph-layout.ts 及对应 .test.ts
  • ⚠️ i18n 覆盖率约 95%chapter-sidebar-list 已接入,actions.ts 已接入,section-error-boundary 默认值已改英文)
  • ⚠️ 类型断言残留 3 处 as string
  • ⚠️ P2 图谱方向键导航未实现

文件清单

文件 行数 职责
actions.ts 502 14 个 Server Action写操作含 Zod 校验 + 资源归属校验 + 知识图谱查询/前置依赖管理)
data-access.ts 586 教材/章节/知识点 CRUD + 跨模块查询接口 + 资源归属校验 + 数据范围过滤 + 前置依赖 CRUD
data-access-graph.ts 184 知识图谱只读查询( Task 5 新增:知识点关联聚合、学生/班级掌握度、前置后置知识点,标记 server-only
types.ts 94 类型定义含知识图谱类型GraphViewMode/MasteryInfo/KpWithRelations/GraphNodeData/GraphEdgeData/KnowledgeGraphData/MasteryLevel
schema.ts 62 Zod 校验(含 CreatePrerequisiteSchema/DeletePrerequisiteSchema
constants.ts 99 学科/年级常量与颜色映射( 新增v1 测试修复:新增 Chinese/Grade 1/Grade 2
utils.ts 203 章节树构建/排序/查找等纯函数 + 循环检测( 新增,含单测)
graph-layout.ts 105 知识图谱布局计算纯函数( Task 8 重写为 dagre 集成,输出 React Flow 格式,含单测)
analytics.tsx 43 教材分析 Context/Provider/Hook 新增)
hooks/use-knowledge-point-actions.ts 121 知识点操作 Hook
hooks/use-text-selection.ts 57 文本选区捕获 Hook
hooks/use-graph-data.ts 58 知识图谱数据加载 Hook Task 11 新增:派生值模式避免 effect 中 setState
components/teacher-textbook-reader.tsx 41 教师端 TextbookReader 客户端包装( v1 测试修复:解决 Server→Client 函数 prop 序列化问题)
components/knowledge-graph.tsx 249 React Flow 知识图谱主组件( Task 10/15 重写:全书视图 + 搜索高亮 + 关联节点高亮 + 章节着色)
components/graph-kp-node.tsx 80 React Flow 自定义节点( Task 9 新增:知识点名称+题目数徽章+掌握度进度条)
components/graph-prerequisite-edge.tsx 40 React Flow 自定义边( Task 9 新增:虚线+箭头表示前置依赖)
components/graph-toolbar.tsx 77 图谱工具栏( Task 9 新增:视图模式切换 + 搜索 + 重置视图)
components/graph-node-detail-panel.tsx 171 节点详情侧边面板( Task 13 新增:描述/掌握度/关联题目/前置后置列表)
components/* 17 文件 教材编辑/知识图谱组件(新增 section-error-boundary.tsxteacher-textbook-reader.tsx、5 个 graph-* 组件)

2.6 grades成绩模块— 标杆模块(拆分范例)

职责:成绩分析(录入/查询/统计/导出/趋势对比分析)。

导出函数

  • ActionsgetGradeRecordsAction / createGradeRecordAction / updateGradeRecordAction / deleteGradeRecordAction / exportGradesAction / getGradeTrendAction / getClassComparisonAction / getSubjectComparisonAction / getGradeDistributionAction / getClassRankingAction / getRankingTrendAction / getGradeRecordByIdAction / getClassGradeStatsAction / getStudentGradeSummaryAction / batchCreateGradeRecordsAction / assertClassInScope P3 新增导出:班级 scope 校验工具,供 actions-analytics 复用)
  • Data-accessgetGradeRecords / getStudentGradeSummary / getClassRanking / getClassStudentsForEntry / getClassGradeStats / getClassGradeStatsWithMeta / getGradeTrend / getClassComparison / getSubjectComparison / getGradeDistribution / getRankingTrend / PaginatedGradeRecords P3 新增:分页结果接口 { records, total }
  • Lib P1-2 新增, P3 更新签名):toNumber / normalize / buildScopeClassFilter(scope, currentUserId?)P3 修复:class_members scope 内置 studentId 过滤,需传入 currentUserId 参数)
  • Stats-service P1-1 新增):computeGradeStats / computeAverageScore / buildGradeTrendPoints / computeTrendAverage / computeClassComparisonStats / computeSubjectComparisonStats / computeGradeDistribution / buildRankingTrendPoints(从 3 个 data-access 文件抽取的纯函数,使数据层专注 DB I/O统计逻辑可独立测试
  • Components P1-5 新增):WidgetBoundaryError Boundary + Suspense + Skeleton 组合,含 a11y 属性)

依赖关系

  • 依赖:shared/*@/authclasses P1-1 已修复:通过 classes data-access.getClassExists/getClassNameById/getClassNamesByIds/getActiveStudentIdsByClassId/getStudentActiveClassId/getClassesByGradeIdschool P1-1 已修复:通过 school data-access.getSubjectOptions/getGradeOptionsusers P1-1 已修复:通过 users data-access.getUserNamesByIds
  • 被依赖:parent(通过 data-access合理dashboard

已知问题

  • P1-1 已修复:多处直查 classes/classEnrollments/subjects/users 改为调用对应模块 data-access 函数classes/school/users
  • P1-1 已修复:统计计算业务逻辑混入 data-accessgetClassGradeStats / getGradeDistribution 抽取为纯函数到 stats-service.ts,数据层专注 DB I/O
  • P1-2 已修复:toNumber/normalize/buildScopeClassFilter 在 3 个 data-access 文件中重复定义 抽取到 lib/grade-utils.ts 统一维护
  • P1-3 已修复:12 个查询/分析 Action 缺少 Zod 校验 新增 12 个查询 schemaDeleteGradeRecordSchema/GetGradeRecordByIdSchema/GradeQuerySchema/ClassGradeStatsQuerySchema/StudentGradeSummaryQuerySchema/ClassRankingQuerySchema/ExportGradesSchema/GradeTrendQuerySchema/ClassComparisonQuerySchema/SubjectComparisonQuerySchema/GradeDistributionQuerySchema/RankingTrendQuerySchema所有 Action 使用 safeParse 校验
  • P1-4 已修复:batch-grade-entry.tsx/grade-record-form.tsx/grade-distribution-chart.tsx 中存在 as 断言 改用类型守卫函数isGradeType/isSemester/isDistributionTooltipPayload
  • P1-5 已修复:teacher/grades 与 teacher/diagnostic 路由缺少 loading.tsx/error.tsx 已为 7 个路由补齐 loading.tsx + error.tsx并新增 WidgetBoundary 通用组件
  • P2-1 已修复:图表/表格/列表缺少 a11y ARIA 属性 为 4 个成绩图表添加 role="img" + aria-label2 个表格添加 <caption>3 个图标按钮添加 aria-label
  • P2-2 已修复:diagnostic 组件中存在 Tailwind 任意值 改用标准 Tailwind 类
  • P2-4 已修复:~~buildScopeClassFiltergrade_managed scope 返回 sql\1=0`(空数据)~~ 改为子查询 classId IN (SELECT id FROM classes WHERE grade_id IN (...))` 过滤所管年级的班级
  • P2-6 已修复:student/grades 和 management/grade/insights 各自重复定义 SearchParams 类型与 getParam 函数 改为统一从 @/shared/lib/search-params 导入
  • actions 层无直接 DB 访问(标杆)
  • data-access 按职责拆分为 3 个文件(标杆)
  • P2 已修复:export.tsscoreMap.get(r.studentId)! 非空断言清理为安全守卫(if (!subjMap) continue
  • P3 修复2026-06-22schema.ts 缺少输入边界约束 score 字段添加 .max(1000)records 数组添加 .max(500),查询 schema 补全 studentId/semester/examId 字段
  • P3 修复2026-06-22buildScopeClassFilterclass_members scope 未应用 studentId 过滤 新增 currentUserId 参数,class_members scope 内置 eq(gradeRecords.studentId, currentUserId) 过滤
  • P3 修复2026-06-22getGradeRecords 内存分页(全量查询后切片) 改为 DB 层分页limit/offset并行查询总数与当前页数据返回 PaginatedGradeRecords { records, total }
  • P3 修复2026-06-22batchCreateGradeRecords 非原子操作 包裹 db.transaction 确保原子性
  • P3 修复2026-06-22updateGradeRecord/deleteGradeRecord 未检查记录是否存在 新增存在性检查,不存在时抛 NotFoundError("成绩记录")
  • P3 修复2026-06-22getClassGradeStats/getStudentGradeSummary/getClassRanking/getClassStudentsForEntry/getClassGradeStatsWithMeta 缺少 scope 过滤 所有函数新增 scope?/currentUserId? 参数,应用 buildScopeClassFiltergetStudentGradeSummary/getClassStudentsForEntryclass_taught scope 校验学生/班级归属
  • P3 修复2026-06-22getClassRanking 未处理并列排名 相同平均分获得相同名次,下一名次跳过占位
  • P3 修复2026-06-22getClassComparison/getRankingTrend 缺少 scope 过滤 应用 buildScopeClassFiltergetRankingTrendclass_taught scope 校验
  • P3 修复2026-06-22actions.ts/actions-analytics.ts catch 块直接返回 e.message 改用 handleActionError(e) 统一错误处理;batchCreateGradeRecordsAction 使用 safeJsonParse 解析 JSON
  • P3 修复2026-06-22actions-analytics.ts 缺少 class scope 校验 getGradeTrendAction/getSubjectComparisonAction/getGradeDistributionAction 新增 assertClassInScope 校验
  • P3 修复2026-06-22组件直接 try/catch 调用 Server Action 改用 safeActionCall 包装器onError/onFinally 回调);batch-grade-entry.tsx localStorage 访问包裹 typeof window !== "undefined" 检查;区分"未录入"与"录入 0"
  • P3 修复2026-06-22grade-trend-card.tsx 排序未处理 NaN 日期、除法未检查 fullScore > 0 新增日期有效性检查与 fullScore > 0 守卫
  • P3 修复2026-06-22页面文件缺少 scope 传递 teacher/grades/page.tsx 使用 DB 层分页analytics/page.tsx 添加 EmptyStateentry/page.tsx 与 stats/page.tsx 过滤 class_taught scope 班级student/grades/page.tsx 与 parent/grades/page.tsx 传递 ctx.dataScope 到 data-access

文件清单

文件 行数 职责
actions.ts 396 15 个 Server Action含 Zod 校验,含 v2-P1-5 安全修复assertClassInScope + 行级 scope 校验P3 修复handleActionError + safeJsonParse + scope 传递 + DB 层分页)
actions-analytics.ts 170 5 个分析 Action含 Zod 校验P3 修复handleActionError + assertClassInScope 校验)
data-access.ts 428 成绩 CRUD + 统计(含 v2-P2-9 修复recorderName 批量查询P3 修复PaginatedGradeRecords 接口 + DB 层分页 + 事务 + 存在性检查 + scope 过滤 + 并列排名)
data-access-analytics.ts 200 趋势/对比分析P3 修复getClassComparison 应用 buildScopeClassFilter
data-access-ranking.ts 83 排名查询P3 修复getRankingTrend 接受 scope 参数 + class_taught 校验)
stats-service.ts 279 统计计算纯函数P1-1 新增8 个纯函数 + 2 个常量 + 2 个接口)
export.ts 189 Excel 导出v2-P1-5 修复:传递 currentUserId 到 data-accessP3 修复:适配 PaginatedGradeRecords 结构 + 传递 scope
schema.ts 113 Zod 校验(含 12 个查询 schemaP3 修复score .max(1000) + records .max(500) + 补全查询字段)
lib/grade-utils.ts 66 公共工具函数toNumber/normalize/buildScopeClassFilterv2-P2-2 修复:改用 classes data-access 子查询P3 修复buildScopeClassFilter 新增 currentUserId 参数)
components/widget-boundary.tsx 136 Widget 边界组件P1-5 新增v2-P1-1 已在 3 个页面应用)
components/grade-trend-card.tsx 69 趋势卡片v2-P2-9 修复a11yv2-P1-4i18nP3 修复NaN 日期检查 + fullScore > 0 守卫)
components/grade-record-list.tsx 125 成绩记录列表v2-P1-4i18nP3 修复safeActionCall 包装删除操作)
components/grade-distribution-chart.tsx 100 分数分布图v2-P1-4i18n
components/subject-comparison-chart.tsx 62 科目对比图v2-P1-4i18n
components/class-comparison-chart.tsx 58 班级对比图v2-P1-4i18n
components/grade-trend-chart.tsx 59 趋势图v2-P1-4i18n
components/grade-record-form.tsx 177 录入表单v2-P2-7 修复Label htmlForv2-P1-4i18nP3 修复safeActionCall 包装提交)
components/batch-grade-entry.tsx 435 批量录入v2-P2-7 修复Label htmlForv2-P1-4i18nP3 修复safeActionCall + localStorage 安全检查 + 区分未录入与录入 0
components/grade-filters.tsx 76 过滤器v2-P1-4i18n
components/student-grade-summary.tsx 107 学生成绩摘要v2-P1-4i18n
components/export-button.tsx 79 导出按钮v2-P1-4i18nP3 修复safeActionCall 包装导出操作)
components/analytics-filters.tsx 86 分析过滤器v2-P1-4i18n
components/stats-class-selector.tsx 40 统计班级选择器v2-P1-4i18n
components/grade-stats-card.tsx 74 统计卡片v2-P1-4i18n
components/class-grade-report.tsx 90 班级成绩报告v2-P1-4i18n
components/grade-query-filters.tsx 96 查询过滤器v2-P2-7 修复Label htmlForv2-P1-4i18n
types.ts 168 类型定义

2.7 classes班级模块— 耦合最严重

职责:班级 CRUD + 学生/教师管理 + 邀请码注册。

导出函数

  • ActionscreateTeacherClassAction / updateTeacherClassAction / deleteTeacherClassAction / createAdminClassAction / updateAdminClassAction / deleteAdminClassAction / createGradeClassAction / updateGradeClassAction / deleteGradeClassAction
  • Data-accessgetAdminClasses / getTeacherClasses / getGradeManagedClasses / getStudentClasses / getClassDetails / getClassStudents / getClassSchedule / getClassHomeworkInsights / getGradeHomeworkInsights / getStudentsSubjectScores / verifyTeacherOwnsClass / getTeacherIdsByClassIds(获取多个班级的所有教师 ID班主任 + 任课教师,跨模块接口,供 messaging 模块调用)( P0-5 已修复classSchedule 写函数 createClassScheduleItem/updateClassScheduleItem/deleteClassScheduleItem 已迁移至 scheduling/data-access-class-schedule.tsclasses 模块仅保留 classSchedule 读函数; P2 已修复:getAccessibleClassIdsForTeacher 使用 Promise.all 并行化 ownedIds 与 assignedIds 查询)
  • SchemaCreateTeacherClassSchema / UpdateTeacherClassSchema / DeleteTeacherClassSchema / CreateAdminClassSchema / UpdateAdminClassSchema / DeleteAdminClassSchema / CreateGradeClassSchema / UpdateGradeClassSchema / DeleteGradeClassSchema / CreateClassScheduleItemSchema / UpdateClassScheduleItemSchema / DeleteClassScheduleItemSchema / EnrollStudentByEmailSchema

依赖关系

  • 依赖:shared/*@/authschool P1-1 已修复:通过 school data-access.isGradeHead/isGradeManager/findGradeIdByHeadAndNamehomework P0-7 已修复:通过 homework/data-access-classes 暴露的函数获取作业数据,不再直查 homework/exams 表)
  • 被依赖:exams/homework/grades/attendance/scheduling/dashboard(通过 data-accessP0-4 已修复)/parent/course-plans/users P1-1 已修复8+ 处直查 classes 表改为通过 classes data-access/messaging(通过 data-access.getTeacherIdsByClassIds/getStudentActiveClassId支持学生/家长给班级教师发消息)

已知问题

  • P0-1 已修复:data-access.ts 已拆分为 5 个文件data-access/data-access-stats/data-access-schedule/data-access-students/data-access-admin所有文件均 ≤800 行
  • P0-5 已修复classSchedule 写函数createClassScheduleItem/updateClassScheduleItem/deleteClassScheduleItem已迁移至 scheduling/data-access-class-schedule.tsclasses 模块仅保留 classSchedule 读函数getStudentSchedule/getClassSchedule新增 verifyTeacherOwnsClass 供 scheduling 模块跨模块校验教师班级归属
  • P0-7 已修复:data-access-stats.tsdata-access-students.ts 不再直查 homeworkAssignmentQuestions/homeworkAssignmentTargets/homeworkAssignments/homeworkSubmissions/exams 表,改为调用 homework/data-access-classes.ts 暴露的函数(getAssignmentIdsForStudents/getHomeworkAssignmentsWithSubject/getHomeworkAssignmentsByIds/getAssignmentMaxScoreById/getAssignmentTargetCounts/getHomeworkSubmissionsForStudents/getPublishedHomeworkAssignmentsWithSubject/getHomeworkSubmissionsForAssignments
  • P1-1 已修复:actions.ts 直查 grades 表做权限校验 改为调用 school/data-access 函数
  • P1-1 已修复:getSessionTeacherId 在 data-access 调用 auth() 改为通过 shared/lib/auth-guard.getAuthContext() 获取
  • P2 已修复:data-access.tsidByName.get(name)! 非空断言清理为 flatMap 安全过滤;data-access-admin.ts 中同类非空断言清理
  • P0-3 修复2026-06-22actions.ts 974 行接近 1000 行硬上限 拆分为 6 个文件actions-teacher/actions-admin/actions-grade/actions-invitations/actions-schedule/actions-sharedactions.ts 改为 50 行 barrel re-export
  • P1-1 修复2026-06-22ctx.roles.includes("admin"/"teacher"/"student") 角色硬编码 改为 hasAdminScope(ctx)/hasTeacherScope(ctx)/hasStudentScope(ctx) 基于 dataScope.type 判断
  • P1-4 修复2026-06-22types.ts 中 ClassHomeworkInsights 等跨领域类型保留在 classes 模块(因为是 classes 对 homework 数据的视图),添加注释说明归属决策

文件清单

文件 行数 职责
data-access.ts 548 核心班级 CRUD + 邀请码 + 教师班级管理(含 re-export 向后兼容)
data-access-stats.ts 513 作业统计查询(班级/年级作业洞察,通过 homework/data-access-classes 获取数据)
data-access-schedule.ts 93 课表查询(学生/班级课表只读P0-5 已修复:写函数已迁移至 scheduling 模块)
data-access-students.ts 253 学生相关查询(科目成绩、学生名单、学生班级,通过 homework/data-access-classes 获取数据)
data-access-admin.ts 406 管理员班级管理(管理员班级 CRUD、年级管理班级查询
actions.ts 50 Barrel re-exportP0-3 修复:从 974 行拆分为 6 个文件)
actions-teacher.ts 100 教师班级 CRUD3 个 Action
actions-admin.ts 120 管理员班级 CRUD3 个 Action
actions-grade.ts 110 年级组长班级 CRUD3 个 Action
actions-invitations.ts 280 邀请码与注册8 个 Action
actions-schedule.ts 90 班级课表 CRUD3 个 Action
actions-shared.ts 60 共享工具hasAdminScope/hasTeacherScope/hasStudentScope/parseSubjectTeachers/toWeekday
schema.ts 152 Zod 校验13 个 schema教师/管理员/年级班级 CRUD + 课表 CRUD + 邮箱注册)
types.ts 201 类型定义含跨领域类型说明注释P1-4 修复)

2.8 school学校模块

职责:学校/学年/部门/年级的 CRUD。

导出函数

  • ActionscreateSchoolAction / updateSchoolAction / deleteSchoolAction / createAcademicYearAction / updateAcademicYearAction / deleteAcademicYearAction / createDepartmentAction / updateDepartmentAction / deleteDepartmentAction / createGradeAction / updateGradeAction / deleteGradeAction(编排层:权限校验 + Zod 校验 + 调用 data-access + revalidatePath + after(logAudit)
  • Data-access只读查询getSchools / getGrades / getDepartments / getAcademicYears / getStaffOptions / getGradesForStaff+ 写操作(create/update/delete × Department/School/Grade/AcademicYear

依赖关系

  • 依赖:shared/*@/authusers⚠️ getStaffOptions 直查 users/roles可接受
  • 被依赖:exams P1-1 已修复:通过 school data-accesshomework P1-1 已修复:通过 school data-accessgrades P1-1 已修复:通过 school data-accessquestions P1-1 已修复:通过 textbooks data-accessclasses P1-1 已修复:通过 school data-accesscourse-plans(合理)

已知问题

  • P0-8 已修复:actions.ts 不再直接导入 db 和 schema所有 DB 写操作下沉到 data-access.ts,符合三层架构
  • P2 已修复:logAudit() 通过 Next.js after() 异步非阻塞执行
  • P2 已修复:data-access.ts 中 8 处 catch 块添加 console.error 输出错误上下文getDepartments/getAcademicYears/getSchools/getGrades/getStaffOptions/getGradesForStaff/getSubjectOptions/getGradeOptions
  • ⚠️ P2审计日志不一致仅 school 实体记录department/academicYear/grade 未记录)
  • ⚠️ P2getStaffOptions/getGrades 直查 users/roles展示用可接受
  • P0-2 修复2026-06-22年级 CRUD 逻辑与 grade-management 模块重复定义 grade-management 死模块已删除,年级 CRUD 统一由 school 模块负责
  • P0-5 修复2026-06-22school/components/* 4 个组件缺少 i18n 全部 4 个组件schools-view/grades-view/departments-view/academic-year-view已接入 useTranslations("school")school.json i18n 文件已创建并扩充
  • P1-3 修复2026-06-22新增 school-error-boundary.tsxclass component Error Boundary + i18n + router.refresh 重试)和 school-skeleton.tsxSchoolListSkeleton 表格骨架 + SchoolCardSkeleton 卡片骨架4 个页面schools/grades/departments/academic-year均已包裹 SchoolErrorBoundaryschool.json 补充 errors.boundary.* 翻译键
  • P1-5 修复2026-06-22schools-view.tsx 硬编码 Table+Dialog+AlertDialog 拆分为组合模式SchoolListToolbar + SchoolFormDialog + SchoolDeleteDialog + useSchoolData hook
  • P1-6 修复2026-06-22新增 getSchoolsForUser(userId) / getGradesForUser(userId) 权限感知查询函数,根据用户角色返回可见数据范围
  • P2-1 修复2026-06-22抽取 use-school-data hook将对话框状态管理逻辑与 UI 分离

文件清单

文件 行数 职责
actions.ts 349 12 个 Server Action编排层无 DB 直访)
data-access.ts 504+ 只读查询 + 12 个写操作 + 跨模块查询接口 + 权限感知函数getSchoolsForUser/getGradesForUser
schema.ts 51 Zod 校验
types.ts 96 类型定义(含 Insert/Update 入参类型)
components/schools-view.tsx 132 学校列表容器组合模式P1-5 修复)
components/school-form-dialog.tsx 80 学校创建/编辑对话框P1-5 修复)
components/school-delete-dialog.tsx 50 学校删除确认对话框P1-5 修复)
components/school-list-toolbar.tsx 30 学校列表工具栏P1-5 修复)
components/school-error-boundary.tsx 72 共享 Error BoundaryP1-3 修复)
components/school-skeleton.tsx 69 共享骨架屏P1-3 修复)
hooks/use-school-data.ts 40 学校数据管理 hookP2-1 修复)

2.8b grade-management年级管理模块 已删除

2026-06-22 审计发现该模块拥有完整的理想架构Service 接口 + Context DI + 角色配置 + Error Boundary + Skeleton + i18n + hooks 分离),但 13 个相关页面中无任何一个导入此模块management/grade/* 页面实际依赖 classesschool 模块的 data-access。

2026-06-22 处置决策P0-1/P0-2 修复):该死模块已完整删除。年级 CRUD 统一由 school 模块负责(school/actions.ts + school/data-access.ts),避免两套重复实现。详见 docs/architecture/audit/school-grade-class-audit-report.md


2.9 scheduling排课模块

职责:自动排课算法 + 课表调整 + 排课规则管理。

导出函数

  • ActionsautoScheduleAction / applyAutoScheduleAction / getSchedulingRulesAction / updateSchedulingRulesAction / getScheduleChangesAction / createScheduleChangeAction / updateScheduleChangeAction / deleteScheduleChangeAction
  • Data-accessgetSchedulingRules / getScheduleChanges / getAdminClassesForScheduling / getTeachersForScheduling / getClassroomsForScheduling / getClassSubjectsForScheduling
  • Data-access-class-schedule P0-5 新增):createClassScheduleItem / updateClassScheduleItem / deleteClassScheduleItem(从 classes 模块迁移,含教师班级归属校验,通过 classes/data-access.verifyTeacherOwnsClass 跨模块校验)
  • Data-access低级写入insertClassScheduleItem / updateClassScheduleItemById / deleteClassScheduleItemById / replaceClassSchedule(统一 classSchedule DB 写入口)
  • 算法:findOptimalSlot / validateSchedule / autoSchedule / buildDefaultTimeSlots(纯函数,标杆)

依赖关系

  • 依赖:shared/*@/authclasses P0-5 已修复:通过 classes/data-access.verifyTeacherOwnsClass / getTeacherIdForMutations 校验教师班级归属,不再直写 classSchedule 表的写入口分散在 classes 模块)、school⚠️ 排课辅助查询,可接受)、users P1-1 已修复:通过 users data-access.getUserNamesByIds
  • 被依赖:classes/actions.ts P0-5 已修复:通过 scheduling/data-access-class-schedule 调用写函数)

已知问题

  • P0-5 已修复:applyAutoScheduleAction 直接 transaction 写 classSchedule 表(第三个写入口) 改为调用 replaceClassSchedule 统一写入口classSchedule 所有写函数统一在 scheduling 模块
  • P1-1 已修复:autoScheduleAction 直查 users 改为调用 users/data-access.getUserNamesByIds
  • ⚠️ P2actions.ts 末尾 re-export data-access 函数(反模式)
  • P2 已修复:data-access.ts 中 3 处非空断言清理(userIds[0]!rows[i]!rows[j]!auto-scheduler.ts 中 2 处非空断言清理(schedule[i]!schedule[j]!
  • auto-scheduler.ts 是算法独立化的最佳实践(纯函数、无 DB、可测试

文件清单

文件 行数 职责
auto-scheduler.ts 310 排课算法(纯函数,标杆)
actions.ts 302 8 个 Server Action
data-access.ts 398 排课辅助查询 + 规则/变更 CRUD + classSchedule 低级写入insert/update/delete/replace
data-access-class-schedule.ts 165 classSchedule 业务写入P0-5 新增,从 classes 模块迁移,含教师归属校验)
schema.ts - Zod 校验
types.ts - 类型定义(含 P0-5 迁移的 CreateClassScheduleItemInput / UpdateClassScheduleItemInput

2.10 attendance考勤模块— 结构典范

职责:考勤记录管理 + 统计分析 + 规则配置。

导出函数

  • Actions10 个):recordAttendanceAction / batchRecordAttendanceAction / updateAttendanceAction / deleteAttendanceAction / getAttendanceAction / getStudentAttendanceAction / getClassAttendanceStatsAction / getClassAttendanceForDateAction / saveAttendanceRulesAction / getAttendanceRulesAction
  • Data-accessgetAttendanceRecords / createAttendanceRecord / updateAttendanceRecord / deleteAttendanceRecord / getClassStudentsForAttendance / getAttendanceStats(管理员考勤总览页统计概览,基于 getAttendanceRecords 聚合)/ upsertAttendanceRules / getAttendanceRules
  • Data-access-statsgetStudentAttendanceSummary / getClassAttendanceStats / computeStats⚠️ 未导出,无法单测)
  • ComponentsAttendanceSheet(批量点名表单)/ AttendanceRecordList(记录列表 + 删除)/ AttendanceFiltersURL 同步筛选器)/ AttendanceStatsCard(单卡片统计)/ AttendanceStatsCards(管理员 6 卡片总览)/ AttendanceStatsClassSelector(班级筛选 ChipNav/ AttendanceRulesForm(规则配置表单)/ StudentAttendanceView(学生/家长只读视图)

依赖关系

  • 依赖:shared/*@/authclasses⚠️ P1-1 未修复:getClassStudentsForAttendance 仍直查 classEnrollments 表)
  • 被依赖:parent⚠️ 跨模块 UI 类型依赖3 个 parent 组件直接 import @/modules/attendance/types

已知问题(详见 docs/architecture/audit/attendance-elective-audit-report.md

  • P0getAttendanceStats 统计失真——调用 getAttendanceRecords(默认 pageSize=20后对 items 聚合,仅基于前 20 条记录计算总览数据
  • P0getClassStudentsForAttendance 仍直查 classEnrollments 表(架构图此前声称已修复,实际未修复)
  • P06 个读 Action 无调用方(页面绕过 Action 直接调用 data-access违反三层架构
  • P0update/delete Action 缺资源归属校验(教师 A 可修改/删除教师 B 的记录)
  • P0i18n 完全缺失(ATTENDANCE_STATUS_LABELS 硬编码英文,组件中硬编码中文)
  • P0错误边界完全缺失5 个角色目录均无 error.tsx
  • ⚠️ P1computeStats 未导出,无法单测
  • ⚠️ P1attendance-sheet.tsx 使用 window.confirm(与项目 AlertDialog 模式不一致)
  • ⚠️ P1attendance-sheet.tsx 存在 {} as Record<AttendanceStatus, number> 类型断言
  • ⚠️ P1STATUS_OPTIONS/SHORTCUTS/STYLES 常量在 types.ts 与 attendance-sheet.tsx 重复定义
  • stats 独立拆分为 data-access-stats.ts(拆分范例)
  • DataScope 完整接入 6 种 scope 类型
  • actions 层无直接 DB 访问

文件清单

文件 行数 职责
actions.ts 271 10 个 Server Action含权限校验、Zod 校验)
data-access.ts 309 考勤 CRUD + 班级学生查询 + 规则 upsert + 总览统计
data-access-stats.ts 145 学生/班级考勤汇总(拆分范例,computeStats 未导出)
schema.ts 43 Zod 校验5 个 schema
types.ts 103 类型定义 + 状态标签/颜色常量(硬编码英文)
components/attendance-sheet.tsx 353 批量点名表单(键盘快捷键、状态按钮组)
components/attendance-record-list.tsx 130 考勤记录列表 + 删除对话框
components/attendance-filters.tsx 97 URL 同步筛选器(班级/状态/日期)
components/attendance-stats-card.tsx 81 单卡片统计8 指标)
components/attendance-stats-cards.tsx 80 管理员总览 6 卡片网格(硬编码中文)
components/attendance-stats-class-selector.tsx 27 班级筛选 ChipNav
components/attendance-rules-form.tsx 148 考勤规则配置表单
components/student-attendance-view.tsx 104 学生/家长视图(统计 + 最近记录)

2.11 users用户模块

职责:用户资料管理 + 批量导入导出 + 管理员用户列表管理。

导出函数

  • ActionsgetUserProfileAction / updateUserProfileAction / importUsersAction / exportUsersAction / downloadUserTemplateAction / updateUserRoleAction / deleteUserAction
  • Data-accessgetUserProfile / getCurrentStudentUser P2-20 已修复:从 homework 模块迁移而来6 个 student 页面通过此函数获取学生身份,不再依赖 homework 模块)/ getAdminUsers(管理员用户列表分页查询,支持搜索+角色聚合)/ getAdminUserRoles(角色名列表,用于筛选下拉框)
  • Import-exportgenerateUserImportTemplate / parseUserImportData / exportUsersToExcel+ re-export batchImportUsers / UserImportResult 保持向后兼容)
  • User-servicebatchImportUsers(用户创建 + 密码哈希 + 角色分配)
  • Class-registrationregisterStudentByInvitationCode(委托 classes/data-access 完成班级注册)
  • ComponentsUserImportDialog(批量导入对话框)/ AdminUsersView(管理员用户列表客户端组件,搜索+筛选+分页+删除)

依赖关系

  • 依赖:shared/*(含 shared/lib/role-utils P2 已修复:删除本地 normalizeRoleName/resolvePrimaryRole/rolePriority,统一复用 shared/lib/role-utils.resolvePrimaryRole)、@/authclasses P1-4 已修复:通过 class-registration.ts 调用 classes/data-access.enrollStudentByInvitationCode,不再直写 classEnrollments
  • 被依赖:dashboard(通过 data-accessP0-4 已修复)、grades P1-1 已修复:通过 users data-accesshomework P1-1 已修复:通过 users data-accessscheduling P1-1 已修复:通过 users data-accessdiagnostic P1-1 已修复:通过 users data-accesselective P1-1 已修复:通过 users data-accessproctoring P1-1 已修复:通过 users data-accessparent P1-1 已修复:通过 users data-access

已知问题

  • P1 已解决:import-export.ts 四重职责已拆分为 import-export.ts(解析/生成)+ user-service.ts(用户创建)+ class-registration.ts(班级注册)
  • P1 已解决:batchImportUsers 不再跨模块直写 classEnrollments,改为调用 classes/data-access.enrollStudentByInvitationCode
  • P2 已解决:删除本地 normalizeRoleName/resolvePrimaryRole/rolePriority,统一复用 shared/lib/role-utils.resolvePrimaryRole,消除重复代码
  • P1-1 已修复:updateUserProfile 绕过 data-access 直接 DB 写 已下沉到 data-access
  • P2-20 已修复:新增 getCurrentStudentUser 函数(从 homework 模块迁移6 个 student 页面通过此函数获取学生身份,不再依赖 homework 模块
  • P2 已解决:data-access.ts 已扩充写操作updateUserProfile 已下沉)
  • ⚠️ 已知限制:AdminUsersView 客户端组件的删除操作通过 fetch("/api/admin/users/:id") 调用,对应 API 路由尚未实现(deleteUserAction Server Action 已就绪,可作为后续 API 路由的实现基础)

文件清单

文件 行数 职责
import-export.ts 157 文件解析/生成(模板生成 + 解析校验 + Excel 导出)+ re-export 向后兼容
user-service.ts 82 用户创建(批量导入 + 密码哈希 + 角色分配)
class-registration.ts 21 班级注册(委托 classes/data-access
actions.ts 218 7 个 Server Actionprofile 更新 + 模板下载/导入/导出 + 角色更新 + 删除)
data-access.ts 394 getUserProfile + 用户查询 + 管理员用户列表分页查询getAdminUsers/getAdminUserRoles
components/admin-users-view.tsx 290 管理员用户列表客户端组件(搜索+筛选+表格+分页+删除对话框)

2.12 dashboard仪表盘模块

职责:管理员/教师/学生/家长仪表盘数据聚合 + 权限校验 + i18n + 纯逻辑工具函数。

导出函数

  • ActionsgetAdminDashboardAction / getTeacherDashboardAction / getStudentDashboardAction / getParentDashboardAction(均调用 requirePermission() 校验对应 DASHBOARD_*_READ 权限)
  • Data-accessgetAdminDashboardData(并行调用 6 个模块 stats 函数)
  • Lib 纯函数:toWeekday / countStudentAssignments / sortUpcomingAssignments / filterTodaySchedule / computeTeacherMetrics / getGreetingKey
  • ComponentsAdminDashboardView / TeacherDashboardView / StudentDashboard / UserGrowthChart / DashboardGreetingHeader共享问候头部V2 抽象)/ DashboardSection(分区 Error Boundary + Suspense + 骨架屏)(均接入 next-intl i18n

依赖关系

  • 依赖:shared/*@/authclasses(通过 data-accesshomework(通过 data-accessusers(通过 data-accessparent(通过 data-access.getParentDashboardDatatextbooks/questions/exams(通过各模块 dashboard stats 函数)、rechartsnext-intl
  • 被依赖:无

权限点

  • DASHBOARD_ADMIN_READadmin
  • DASHBOARD_TEACHER_READteacher
  • DASHBOARD_STUDENT_READstudent
  • DASHBOARD_PARENT_READparent

已知问题

  • P0-4 已修复:getAdminDashboardData 改为并行调用各模块 dashboard stats 函数,不再直查跨模块表
  • P0 已修复2026-06-22所有仪表盘页面通过 actions.ts 调用 requirePermission() 进行权限校验,不再裸调 data-access
  • P0 已修复2026-06-22根重定向页 /dashboard 改用 resolvePermissions() + 权限点判断,不再 role === "xxx" 硬编码
  • P0 已修复2026-06-22所有仪表盘组件接入 next-intluseTranslations / getTranslations),翻译文件 messages/{zh-CN,en}/dashboard.json
  • P1 已修复2026-06-22业务逻辑weekday 转换、作业统计、教师指标计算、问候语时段)抽取至 lib/dashboard-utils.ts 纯函数,与 UI 分离
  • P2 已修复2026-06-22新增 components/dashboard-section.tsx,每个独立数据区块用 Error Boundary + Suspense + 骨架屏包裹,单区块崩溃/加载不波及整页5 种骨架变体stats/card/chart/table/list
  • V1 新增:AdminDashboardData 类型含 userGrowth/homeworkTrend 字段,data-access.ts 当前返回空数组占位,待后续接入真实统计
  • parent 仪表盘组件仍位于 modules/parent/components/parent-dashboard.tsx,通过 dashboard/actions.getParentDashboardAction 调用(架构决策:保留在 parent 模块以避免移动文件破坏其他 import
  • P0 已修复2026-06-22 V210 个子组件 i18n 遗漏全部补齐teacher-quick-actions / teacher-classes-card / teacher-homework-card / teacher-schedule / recent-submissions / teacher-grade-trends / student-grades-card / student-today-schedule-card / student-upcoming-assignments-card / admin-dashboard新增 ~50 个翻译键
  • P1 已修复2026-06-22 V2抽象共享组件 DashboardGreetingHeader,消除 teacher/student 头部 90% 重复代码
  • P2 已修复2026-06-22 V2为 6 个纯函数添加 31 个单元测试(tests/integration/dashboard/dashboard-utils.test.ts
  • P2 已修复2026-06-22 V2a11y 增强 — admin 表格 <caption>、teacher/student 视图语义化标签(<header> / <section aria-label> / <aside aria-label>

文件清单

文件 行数 职责
actions.ts 120 4 个 Server Action编排层requirePermission() 权限校验)
data-access.ts 49 admin 仪表盘数据聚合(并行调用各模块 stats 函数)
lib/dashboard-utils.ts 170 纯逻辑工具函数weekday / 统计 / 排序 / 指标计算 / 问候语)
types.ts 74 Admin / Teacher / Student 类型定义
components/dashboard-section.tsx 165 分区 Error Boundary + Suspense + 骨架屏5 种变体stats/card/chart/table/list
components/dashboard-greeting-header.tsx 36 共享问候头部组件V2 抽象,消除 teacher/student 头部重复)
components/admin-dashboard/admin-dashboard.tsx 267 管理员仪表盘视图i18n + a11y 表格 caption
components/admin-dashboard/user-growth-chart.tsx 50 recharts 折线图i18n
components/teacher-dashboard/*.tsx 9 文件 教师仪表盘组件i18n + a11y 语义化标签)
components/student-dashboard/*.tsx 6 文件 学生仪表盘组件i18n + a11y 语义化标签)
tests/integration/dashboard/dashboard-utils.test.ts 408 6 个纯函数的 31 个单元测试V2 新增)

2.13 messaging私信模块

职责站内私信messages 表 CRUD

导出函数

  • ActionssendMessageAction / markMessageAsReadAction / deleteMessageAction / getMessagesAction / getMessageDetailAction / getRecipientsAction / getUnreadMessageCountAction / getNotificationPreferencesAction / updateNotificationPreferencesAction P1-4 已修复:通知 CRUD Action 已迁移至 notifications 模块messaging 仅保留私信和通知偏好 Action
  • Data-accessgetMessages / getMessageById / getMessageThread / createMessage / markMessageAsRead / deleteMessage / getUnreadMessageCount / getRecipients(按 DataScope 过滤可发送对象class_taught 教师→学生、grade_managed 年级管理员→教师/学生、all 管理员、class_members 学生→自己班级的任课教师/班主任、children 家长→孩子的班主任/任课教师;通过 classes data-access.getTeacherIdsByClassIds/getStudentActiveClassId 获取班级教师 ID/ getMessagesPageData P1-5 新增:消息首页编排函数,一次性获取消息列表和通知列表)/ getMessageDetailPageData V2-P1-3 新增:消息详情页编排函数,获取详情并自动标记已读)
  • HooksuseMessageSearch P1-7 新增:消息搜索 hook含防抖和请求竞态取消
  • Notification-preferencesre-export shim实际逻辑在 notifications/preferences.ts P0-b 已修复:notification-preferences.ts 文件已删除(通知模块去重),消费方改为直接从 @/modules/notifications/preferences 导入 getNotificationPreferences / upsertNotificationPreferences

依赖关系

  • 依赖:shared/*@/authnotifications P0-4 / P1-5 已修复:通过 sendNotification dispatcher 发送通知,通知 CRUD 和偏好已迁移至 notifications 模块; P1-4 已修复:通知 UI 组件已迁移至 notifications 模块)、classes(通过 data-access.getTeacherIdsByClassIds/getStudentActiveClassId 获取班级教师 ID支持学生 class_members 和家长 children 数据范围)、users(通过 data-access.getUserNamesByIds 获取用户显示名称)
  • 被依赖:notifications 已消除反向依赖)、settings(通知偏好表单)、layout P1-4 已修复:通知下拉组件改为从 notifications 模块导入)

已知问题

  • P0-4 已修复:sendMessageAction 绕过 notifications dispatcher 直接调用 createNotification 改为调用 notifications.sendNotification,通知 CRUD 已迁移至 notifications 模块
  • P0 已修复:与 notifications 双向依赖 + 职责重叠 通知相关表messageNotifications / notificationPreferences所有权已移交 notifications 模块messaging 仅保留 messages 表
  • P1-5 已修复:同时管理 3 类数据messages + messageNotifications + notificationPreferences 仅管理 messages 表,通知相关数据由 notifications 模块管理
  • P1 已修复:通知相关 Action 使用 requireAuth() 而非 requirePermission() 通知 Action 已迁移至 notifications 模块并改为 requirePermission(Permissions.MESSAGE_READ)
  • P1 已修复:markMessageAsReadAction / deleteMessageAction / getMessageDetailAction 缺少 Zod 校验 已添加 MessageIdSchema 校验 messageId 参数
  • P1 已修复:updateNotificationPreferencesAction 缺少 Zod 校验 已添加 UpdateNotificationPreferencesSchema 校验 8 个布尔字段
  • P2 已修复:data-access.ts 中 3 处 or(...)! 非空断言清理为安全守卫(条件 push
  • P0-b 已修复:notification-preferences.ts re-export shim 文件 已删除通知模块去重8 个消费方改为直接从 @/modules/notifications/preferences 导入 getNotificationPreferences / upsertNotificationPreferences,消除 messaging 模块对通知偏好的冗余 re-export 层
  • P1 已修复:全模块零 i18n中英文案硬编码 所有组件接入 next-intluseTranslations("messages")),新增 src/shared/i18n/messages/{zh-CN,en}/messages.json 翻译字典title/description/tabs/actions/form/status/meta/notificationType/search/empty/messages/error 共 13 个命名空间);所有页面 page.tsx 使用 generateMetadata + getTranslations 替代硬编码 metadata
  • P1 已修复:缺 Error Boundary 新增 3 个 error.tsx 错误边界(/messages/messages/[id]/messages/compose),统一使用 EmptyState + i18n 错误文案 + 重试按钮
  • P2 已修复a11y 改进,message-list.tsx 添加 aria-label / aria-hidden
  • P1-4 已修复:通知组件notification-list.tsx / notification-dropdown.tsx放在 messaging/components 下,直接 import notifications 模块类型和 messaging/actions 两个组件已迁移至 notifications/components/,通知 CRUD Action 已迁移至 notifications/actions.tsmessaging 模块仅保留私信组件
  • P1-5 已修复:页面层 Promise.all 编排 messaging 和 notifications 两个模块的 data-access 新增 getMessagesPageData 编排函数,页面层仅调用单一函数
  • P1-7 已修复:消息列表客户端 useEffect + setTimeout 防抖搜索未取消已发出的请求 搜索逻辑抽离为 useMessageSearch hook含防抖 + 请求竞态取消);无分页 UI 新增客户端分页 UIPAGE_SIZE=20ChevronLeft/ChevronRight 按钮)
  • P1-9 已修复:deleteMessage 两个独立 UPDATE 无事务 改为 db.transaction 包裹 senderDeletedAt 和 receiverDeletedAt 更新,保证原子性
  • P2-11 已修复:发送/删除消息无埋点 sendMessageAction / markMessageAsReadAction / deleteMessageAction 新增 trackEvent 埋点message.sent / message.marked_read / message.deleted
  • V2-P0-2 已修复:通知标题硬编码 sendMessageAction 通过 getTranslations('messages') 生成 i18n 通知标题(notification.messageTitle / messageTitleNoSubject
  • V2-P1-2 已修复:MessageList 客户端过滤冗余 客户端过滤仅在初始数据type=all时执行搜索结果已由服务端按 tab 过滤
  • V2-P1-3 已修复:消息详情页分散编排 新增 getMessageDetailPageData 编排函数,替代 page.tsx 中 after() + getMessageById + markMessageAsRead 的分散编排
  • V2-P1-4 已修复:表单无服务端校验错误展示 message-compose.tsx 新增 fieldErrors 状态 + aria-invalid 字段级错误展示receiverId/subject/content
  • V2-P2-1 已修复:轮询间隔魔法数字 unread-message-badge.tsx 轮询间隔提取为 POLL_INTERVAL_MS 常量60_000ms

文件清单

文件 行数 职责
actions.ts ~280 7 个私信 Server Action + 2 个通知偏好 Action P1-4通知 CRUD Action 已迁移至 notifications 模块; V2-P0-2通知标题 i18n 化)
data-access.ts ~290 私信 CRUD + getMessagesPageData 编排函数( P1-5 新增)+ getMessageDetailPageData 编排函数( V2-P1-3 新增)
schema.ts 44 私信发送校验 + messageId 校验 + 通知偏好更新校验
types.ts 52 私信类型 + re-export 通知类型(向后兼容)
hooks/use-message-search.ts ~60 P1-7 新增:消息搜索 hook防抖 + 请求竞态取消)

组件清单

组件 职责
components/message-list.tsx 消息列表( P1-7使用 useMessageSearch hook + 客户端分页 UIPAGE_SIZE=20 V2-P1-2客户端过滤仅在初始数据时执行
components/message-detail.tsx 消息详情(含回复)
components/message-compose.tsx 撰写新消息( V2-P1-4fieldErrors + aria-invalid 字段级错误展示)
components/unread-message-badge.tsx 未读消息计数徽章(侧边栏,每 60 秒轮询 getUnreadMessageCountAction V2-P2-1POLL_INTERVAL_MS 常量)

客户端行为

  • message-list.tsx:客户端调用 getMessagesAction 搜索消息useMessageSearch hook400ms 防抖请求竞态取消V2-P1-2 优化客户端过滤仅在初始数据type=all时执行
  • unread-message-badge.tsx:每 POLL_INTERVAL_MS60_000ms轮询 getUnreadMessageCountAction 刷新未读计数

2.14 notifications通知分发模块

职责多渠道通知分发SMS/Email/WeChat/InApp+ 站内通知 CRUD + 通知偏好管理 + 通知 UI 组件。

导出函数

  • ActionssendNotificationAction / sendClassNotificationAction / getNotificationsAction / getUnreadNotificationCountAction / markNotificationAsReadAction / markAllNotificationsAsReadAction P1-4 新增:后 4 个通知 CRUD Action 从 messaging 模块迁移)
  • DispatchersendNotification(payload) / sendBatchNotifications(payloads)
  • Data-accesscreateNotification / getNotifications / markNotificationAsRead / markAllNotificationsAsRead / getUnreadNotificationCount / getUserContactInfo / logNotificationSend / logNotificationSendBatch P0-4 / P1-5 修复后从 messaging 迁移)
  • PreferencesgetNotificationPreferences / upsertNotificationPreferences P0-4 / P1-5 修复后从 messaging 迁移)
  • ChannelsInAppChannelSender / SmsChannelSender / EmailChannelSender / WeChatChannelSender
  • ComponentsNotificationList / NotificationDropdown P1-4 新增:从 messaging/components 迁移)

依赖关系

  • 依赖:shared/*@/authclasses P1-1 已修复:通过 classes data-access.getClassExists/getStudentIdsByClassId
  • 被依赖:messaging P0-4 / P1-5 已修复messaging 通过 sendNotification dispatcher 发送通知; P1-4 已修复:通知 UI 组件由 notifications 模块自持messaging 不再反向依赖)、layout P1-4 已修复:通知下拉组件直接从 notifications 模块导入)、app/(dashboard)/messages P1-4 已修复:通知列表组件直接从 notifications 模块导入)

已知问题

  • P0-4 已修复:不拥有任何数据,全部依赖 messaging 模块 messageNotifications 和 notificationPreferences 表所有权已从 messaging 迁移至 notifications 模块
  • P0 已修复:与 messaging 双向依赖 notifications 不再反向依赖 messagingin-app-channel 改为静态导入本地 createNotification
  • P1-1 已修复:sendClassNotificationAction 直查 classes/classEnrollments 改为调用 classes/data-access.getClassExists / getStudentIdsByClassId
  • P1-4 已修复:通知 UI 组件放在 messaging/components 下,直接 import notifications 类型和 messaging/actions notification-list.tsxnotification-dropdown.tsx 已迁移至 notifications/components/,通知 CRUD Action 已从 messaging 迁移至 notifications/actions.ts,消除 UI 层跨模块耦合
  • P2-11 已修复:通知标记已读无埋点 markNotificationAsReadAction / markAllNotificationsAsReadAction 新增 trackEvent 埋点notification.marked_read / notification.marked_all_read
  • V2-P0-1 已修复:通知 i18n 键混在 messages.json 中 新增独立的 notifications.json 命名空间zh-CN/en通知组件 useTranslations"messages" 切换到 "notifications"src/i18n/request.ts 新增 notifications 命名空间加载
  • V2-P2-1 已修复:轮询间隔魔法数字 notification-dropdown.tsx 轮询间隔提取为 POLL_INTERVAL_MS 常量30_000ms
  • ⚠️ P1发送日志仅 consolenotification_logs
  • 渠道抽象优秀(接口 + 工厂 + Mock 实现)

文件清单

文件 行数 职责
dispatcher.ts 152 渠道选择 + 并行分发
data-access.ts 177 站内通知 CRUD + 用户联系方式 + 日志P0-4 / P1-5 修复后新增通知 CRUD
preferences.ts 166 通知偏好 CRUDP0-4 / P1-5 修复后从 messaging 迁移)
actions.ts ~260 6 个 Server Action P1-4新增 4 个通知 CRUD Action
types.ts 120 通知负载 + 渠道配置 + 通知记录 + 偏好类型P0-4 / P1-5 修复后扩充)
index.ts ~75 对外导出入口( P1-4新增组件和 CRUD Action 导出)
channels/* 5 文件 4 个渠道实现
components/notification-list.tsx ~140 P1-4 新增(从 messaging 迁移):通知列表组件
components/notification-dropdown.tsx ~180 P1-4 新增(从 messaging 迁移):通知下拉菜单组件

组件清单

组件 职责
components/notification-list.tsx 通知列表(消息页底部,展示所有通知,支持标记已读; V2-P0-1useTranslations 命名空间从 "messages" 切换到 "notifications"
components/notification-dropdown.tsx 通知下拉菜单(站点头部,每 30 秒轮询 getNotificationsAction + getUnreadNotificationCountAction V2-P0-1useTranslations 命名空间切换; V2-P2-1POLL_INTERVAL_MS 常量)

客户端行为

  • notification-dropdown.tsx:每 POLL_INTERVAL_MS30_000ms轮询 getNotificationsActionpageSize=10getUnreadNotificationCountAction 刷新通知和未读计数

2.15 audit审计模块

职责:操作日志 + 登录日志 + 数据变更日志查询与导出。

导出函数

  • ActionsgetAuditLogsAction / getLoginLogsAction / getDataChangeLogsAction / exportAuditLogsAction / exportLoginLogsAction / exportDataChangeLogsAction
  • Data-accessgetAuditLogs / getLoginLogs / getDataChangeLogs / getAuditModuleOptions

依赖关系

  • 依赖:shared/*@/auth
  • 被依赖:无

已知问题

  • ⚠️ P2Excel 导出逻辑内联在 actions 层(应抽取到 export.ts
  • ⚠️ P2三个导出 Action 结构高度重复
  • P2 已修复:data-access.ts 中 6 处 catch 块添加 console.error 输出错误上下文;toIso/clampPageSize/clampPage 工具函数补齐显式返回类型
  • data-access 职责清晰,无跨模块问题

文件清单

文件 行数 职责
actions.ts 212 6 个 Server Action含内联导出
data-access.ts 260 日志查询
types.ts - 类型定义

2.16 announcements公告模块

职责:公告 CRUD + 发布/归档 + 发布通知。

导出函数

  • ActionsgetAnnouncementsAction / createAnnouncementAction / updateAnnouncementAction / deleteAnnouncementAction / publishAnnouncementAction / archiveAnnouncementAction P1-2 已修复actions 层不再直接访问 DB全部下沉到 data-access 发布公告时触发通知模块 sendBatchNotifications
  • Data-accessgetAnnouncements(支持 audience 受众过滤)/ getAnnouncementById / insertAnnouncement / updateAnnouncementById / deleteAnnouncementById / publishAnnouncementById / archiveAnnouncementById(后 5 个为 P1-2 新增)/ getAdminAnnouncementsPageData / getEditAnnouncementPageData P1-5 新增:管理端列表页和编辑页编排函数,页面层仅调用单一函数)

依赖关系

  • 依赖:shared/*@/authschool(获取年级列表)、classes(获取班级列表 + 解析受众)、users(获取目标用户 ID 列表)、notifications(发布公告时发送通知)
  • 被依赖:无

已知问题

  • P1-2 已修复:所有写操作直接在 actions 层 db.insert/update/delete,未下沉到 data-access 写操作已下沉到 data-access5 个新函数)
  • P2 已修复:getAnnouncementsAction 使用 requireAuth() 而非 requirePermission(ANNOUNCEMENT_READ) 改为 requirePermission(Permissions.ANNOUNCEMENT_READ)
  • 已修复:用户端列表页传入 audience 受众过滤school/grade/class管理端返回所有公告
  • 已修复:用户端新增公告详情页 /announcements/[id](只读模式)
  • 已修复:管理端列表页传递 classes 数据给 AdminAnnouncementsView
  • 已修复:发布公告时(publishAnnouncementAction / createAnnouncementAction 直接发布 / updateAnnouncementAction 状态变为 published触发通知模块 sendBatchNotifications
  • 已修复:新增 loading.tsx 骨架屏(用户端 + 管理端)
  • P1 已修复:全模块零 i18n中英文案硬编码 所有组件接入 next-intluseTranslations("announcements")),新增 src/shared/i18n/messages/{zh-CN,en}/announcements.json 翻译字典title/description/filter/status/type/form/actions/messages/meta/empty/error 共 11 个命名空间);所有页面 page.tsx 使用 generateMetadata + getTranslations 替代硬编码 metadata
  • P1 已修复:缺 Error Boundary 新增 4 个 error.tsx 错误边界(/announcements/announcements/[id]/admin/announcements/admin/announcements/[id]),统一使用 EmptyState + i18n 错误文案 + 重试按钮
  • P2 已修复a11y 改进,announcement-card.tsx / announcement-detail.tsx 添加 aria-label
  • P1-5 已修复:页面层 Promise.all 编排 announcements/school/classes 三个模块的 data-access 新增 getAdminAnnouncementsPageDatagetEditAnnouncementPageData 编排函数,页面层仅调用单一函数
  • P1-6 已修复:targetGradeId / targetClassId 为 optional未根据 type 做条件必填校验 CreateAnnouncementSchemaUpdateAnnouncementSchema 添加 superRefine(refineAudience),年级公告强制 targetGradeId,班级公告强制 targetClassId
  • P2-11 已修复:发布/归档/删除公告无埋点 createAnnouncementAction / updateAnnouncementAction / deleteAnnouncementAction / publishAnnouncementAction / archiveAnnouncementAction 新增 trackEvent 埋点announcement.created / announcement.updated / announcement.published / announcement.deleted / announcement.archived
  • V2-P0-2 已修复:通知标题硬编码 createAnnouncementAction / updateAnnouncementAction / publishAnnouncementAction 通过 getTranslations('announcements') 生成 i18n 通知标题(notification.publishedTitle / publishedContent
  • V2-P1-1 已修复:AnnouncementList 客户端 useState/useMemo 过滤 改为纯服务端过滤模式Select 切换仅更新 URL ?status= 触发 RSC 重新渲染
  • V2-P1-4 已修复:表单无服务端校验错误展示 announcement-form.tsx 新增 fieldErrors 状态 + aria-invalid 字段级错误展示title/content/targetGradeId/targetClassId

文件清单

文件 行数 职责
actions.ts ~330 6 个 Server Action + 通知触发逻辑 + trackEvent 埋点P1-2 已修复,无直接 DB 操作)
data-access.ts ~230 公告 CRUD + 发布/归档 + 受众过滤 + getAdminAnnouncementsPageData / getEditAnnouncementPageData 编排函数( P1-5 新增)
schema.ts ~70 Zod 校验 + refineAudience 条件校验( P1-6 新增 superRefine
types.ts ~65 类型定义(GetAnnouncementsParams 新增 audience 字段)

组件清单

组件 职责
components/announcement-list.tsx 公告列表(用户端,支持状态筛选; V2-P1-1纯服务端过滤Select 切换更新 URL ?status= 触发 RSC 重新渲染; V3新增 detailHrefPrefix prop 替代 detailHrefBuilder 函数 prop解决 Next.js 16 Server→Client 序列化限制)
components/announcement-card.tsx 公告卡片(列表项)
components/announcement-detail.tsx 公告详情(只读)
components/announcement-form.tsx 公告表单(创建/编辑, P1-6条件校验由 schema superRefine 保证; V2-P1-4fieldErrors + aria-invalid 字段级错误展示)
components/admin-announcements-view.tsx 管理端公告视图(列表 + 筛选)

2.17 files文件模块

职责:文件附件 CRUD + 批量删除 + 统计。

导出函数

  • Data-accessgetAllFileAttachments / getFileAttachmentsByOwner / getFileAttachmentById / createFileAttachment / updateFileAttachment / deleteFileAttachment / batchDeleteFileAttachments / getFileStats P2 已修复7 个读函数使用 React.cache() 包装实现请求级 memoizationgetFileAttachment / getFileAttachmentsByTarget / getFileAttachmentsByUploader / getAllFileAttachments / getFileAttachmentsWithFilters / getFileStats / getFileAttachmentsByIds

依赖关系

  • 依赖:shared/*@/auth
  • 被依赖:app/api/upload / app/api/files/[id] / app/api/files/batch-delete

已知问题

  • P2-13 已修复:所有函数 try-catch 吞错误返回空数组/null 所有 catch 块已添加 console.error 输出错误上下文
  • P2 已修复:getFileAttachmentsWithFiltersor(...)! 非空断言清理为安全守卫
  • P2 已修复:getFileAttachmentsWithFiltersconditions 隐式 any[] 改为显式 SQL[] 类型标注
  • ⚠️ P2actions.tsdata-access 被路由直接调用
  • 职责单一,不跨模块查询

文件清单

文件 行数 职责
data-access.ts 267 文件 CRUD + 批量删除 + 统计
types.ts - 类型定义
components/* 6 文件 上传/列表/预览/管理

2.18 course-plans课程计划模块

职责:课程计划 CRUD + 周计划项 CRUD + 排序。

导出函数

  • ActionsgetCoursePlansAction / getCoursePlanByIdAction / createCoursePlanAction / updateCoursePlanAction / deleteCoursePlanAction / createCoursePlanItemAction / updateCoursePlanItemAction / deleteCoursePlanItemAction / toggleCoursePlanItemCompletedAction
  • Data-access与 actions 对应

依赖关系

  • 依赖:shared/*@/authclasses合理getAdminClasses/getStaffOptionsschool合理getAcademicYears
  • 被依赖:无

已知问题

  • ⚠️ P2getSubjectOptions 直查 subjectssubjects 无独立模块,可接受)
  • P2 已修复:data-access.ts 中 3 处 catch 块添加 console.error 输出错误上下文getCoursePlans/getCoursePlanById/getSubjectOptions
  • actions 层使用 handleError/revalidatePlanPaths 辅助函数(良好范例)

文件清单

文件 行数 职责
data-access.ts 320 课程计划 + 周计划项 CRUD
actions.ts 265 9 个 Server Action
schema.ts - Zod 校验
types.ts - 类型定义

2.19 parent家长模块

职责:家长视角的子女数据聚合与展示。

导出函数

  • Data-accessgetChildren / getChildBasicInfo / getChildDashboardData / getParentDashboardData / verifyParentChildRelation / getChildNameList v4 新增:用于详情页头部多子女切换器,一次批量查询避免 N+1
  • ComponentsParentDashboard / ChildCard / ChildDetailHeader / ChildDetailPanel / SiblingSwitcher / ChildHomeworkSummary / ChildHomeworkDetailv4 新增)/ ChildGradeSummary / ChildGradeDetailv4 新增)/ ChildScheduleCard / ParentChildrenDataPage / ParentNoChildrenPage / ParentAttentionBannerv4 新增)/ ParentAttendanceWarningv4 新增)/ ParentAttendanceRateCardv4 新增)/ ParentAttendanceCalendarv4 新增)/ ParentExportButtonv4 新增)

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 多子女切换器
  • LAYOUT-P01仪表盘新增 ParentAttentionBanner 待办事项横幅
  • LAYOUT-P02ChildCard 突出 Overdue 异常(红色边框 + AlertTriangle 图标)
  • LAYOUT-P03仪表盘快捷入口改为 4 宫格大图标卡片
  • LAYOUT-P04详情页改为 Tab 布局Overview/Homework/Grades/Schedule/Attendance/Diagnostic
  • LAYOUT-P05详情页新增面包屑导航
  • LAYOUT-P07成绩趋势图 X 轴改用序号,避免日期重叠
  • LAYOUT-P08成绩页新增 ParentExportButton 导出按钮(占位)
  • LAYOUT-P09考勤页新增 ParentAttendanceCalendar 月历视图(按状态着色,支持按月切换)
  • LAYOUT-P10考勤异常高亮与 FEAT-G05 同步实现)
  • NAV-P02Grades/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 tab1 次点击到达)
  • HABIT-P03多子女切换无需返回仪表盘
  • HABIT-P06仪表盘展示未读/待办数量
  • A11Y-P02Overdue 状态增加 AlertTriangle 图标辅助
  • A11Y-P04成绩图表容器新增 aria-label
  • PERF-P01/P02骨架屏 + 错误边界
  • PERF-P03空状态新增"Contact support"引导按钮
  • PERF-P04ChildCard Link 添加 prefetch
  • MOBILE-P03移动端子女卡片改为水平滑动 Carouselsnap-x
  • MOBILE-P04作业/成绩列表项 min-h-[44px] 触摸区域

依赖关系

  • 依赖:shared/*@/authclasses(合理)、homework(合理)、grades(合理)、users(合理)、school(合理)、attendancev4 新增:考勤页复用 StudentAttendanceView⚠️ 跨模块 UI 类型依赖3 个组件直接 import @/modules/attendance/types
  • 被依赖:无

已知问题

  • ⚠️ P13 个 parent 组件(parent-attendance-warning.tsx/parent-attendance-rate-card.tsx/parent-attendance-calendar.tsx)直接 import @/modules/attendance/types,违反模块解耦原则(应通过 data-access 接口或 shared 类型抽象)
  • ⚠️ P1parent-attendance-calendar.tsx 重新定义 STATUS_DOT/STATUS_LABEL 常量,与 attendance 模块重复
  • ⚠️ P13 个 parent 组件的纯函数(buildWarnings/aggregate/rateTone/formatDateKey/parseDateKey/buildCalendarDays/isSameDay)未导出,无法单测
  • P1 已修复:app/(dashboard)/parent/children/[studentId]/page.tsx 直接访问 DB违反三层架构 改为调用 verifyParentChildRelation data-access 函数
  • P1 已修复:权限校验未加 parentId 条件,存在信息泄露风险 verifyParentChildRelation 同时按 parentId + studentId 过滤
  • P2 已修复:getChildBasicInfo 多次串行查询 改为 Promise.all 并行化,并使用 getStudentActiveClass 一次 JOIN
  • P2 已修复:getGradeOptions 全量查询效率低 改为 getGradeNameById 按 ID 查询
  • P2 已修复:buildHomeworkSummary[...assignments].sort() 不必要拷贝 改为 toSorted()
  • P2 已修复:in7Days 死代码 已删除
  • ⚠️ v4 保留:/parent/leave 为占位页,待后端实现请假审批流后接入
  • ⚠️ v4 保留:ParentExportButton 为占位,待后端实现成绩导出 Server Action 后接入
  • ⚠️ v4 保留:详情页 Attendance/Diagnostic Tab 为占位提示,待对应功能实现后填充
  • 职责单一,正确复用其他模块 data-access

文件清单

文件 行数 职责
data-access.ts 243 子女关系 + 仪表盘数据聚合 + 关系校验 + 子女姓名列表v4 新增 getChildNameList + buildWeeklySchedule
types.ts 79 类型定义(含 JSDocv4 新增 ChildWeeklyScheduleItem
components/parent-dashboard.tsx 110 仪表盘v4 重构:待办横幅 + 宫格快捷入口 + 移动端水平滑动)
components/parent-attention-banner.tsx 128 v4 新增:待办事项/异常聚合横幅(作业项直接跳转详情页 homework tab
components/parent-attendance-warning.tsx 89 v4 新增:考勤异常预警
components/parent-attendance-rate-card.tsx 105 v4 新增:考勤出勤率汇总卡片
components/parent-attendance-calendar.tsx 175 v4 新增考勤月历视图use client
components/parent-export-button.tsx 50 v4 新增:成绩导出按钮(占位)
components/child-card.tsx 148 子女卡片v4 增强:异常突出 + 趋势图标)
components/child-detail-header.tsx 78 详情页头部v4 增强:面包屑)
components/child-detail-panel.tsx 200 详情页 Tab 面板 + SiblingSwitcherv4 重写,集成 Homework/Grade Detail
components/child-homework-summary.tsx 147 作业摘要v4 增强:科目标识 + 触摸区域 + pts 单位)
components/child-homework-detail.tsx 145 v4 新增:作业详情视图(完整作业信息)
components/child-grade-summary.tsx 159 成绩趋势v4 增强:趋势图标 + aria-label
components/child-grade-detail.tsx 165 v4 新增:成绩详情视图(按科目分组分析)
components/child-schedule-card.tsx 119 课表卡片v4 增强:周课表视图)
components/parent-children-data-page.tsx 92 共享数据页v4 增强headerExtra

路由清单

路由 文件 说明
/parent/dashboard dashboard/page.tsx + loading.tsx 家长仪表盘
/parent/grades grades/page.tsx + loading.tsx 多子女成绩聚合
/parent/diagnostic diagnostic/page.tsx + loading.tsx + error.tsx P2-5 新增:多子女学情诊断聚合
/parent/attendance attendance/page.tsx + loading.tsx 多子女考勤聚合v4 新增预警横幅)
/parent/leave leave/page.tsx + loading.tsx v4 新增:请假申请(占位)
/parent/children/[studentId] children/[studentId]/page.tsx + loading.tsx 子女详情页v4 重构Tab 布局 + 多子女切换)
error.tsx error.tsx v4 新增:错误边界

2.20 elective选课模块

职责:选修课程管理 + 学生选课 + 抽签。

导出函数

  • Actions11 个):getElectiveCoursesAction / createElectiveCourseAction / updateElectiveCourseAction / deleteElectiveCourseAction / getStudentSelectionsAction / selectCourseAction / dropCourseAction / runLotteryAction / getAvailableCoursesForStudentAction / openSelectionAction / closeSelectionAction
  • Data-accessgetElectiveCourses / getElectiveCourseById / createElectiveCourse / updateElectiveCourse / deleteElectiveCourse / openSelection / closeSelection / buildCourseSelect / mapCourseRow / resolveCourseDisplayNames / CourseCoreRowP3 新增导出,供 data-access-selections 复用)
  • Data-access-operationsselectCourse / dropCourse / runLottery / buildLotteryRankCase⚠️ 未导出,无法单测)
  • Data-access-selectionsgetCourseSelections / getStudentSelections / getStudentGradeId / getAvailableCoursesForStudent
  • ComponentsElectiveCourseList(课程卡片网格 + 管理操作)/ ElectiveCourseForm(课程创建/编辑表单)/ ElectiveFiltersnuqs 筛选栏)/ StudentSelectionView(学生选课视图)

依赖关系

  • 依赖:shared/*@/authschool P3 已修复:通过 school data-access.getSubjectOptions/getGradeOptions 获取科目/年级名称,不再直查 subjects/grades 表)、users P3 已修复:通过 users data-access.getUserNamesByIds 获取教师姓名,不再直查 users 表)、classes(通过 classes data-access.getStudentActiveGradeId 获取学生年级)
  • 被依赖:无

已知问题(详见 docs/architecture/audit/attendance-elective-audit-report.md

  • P03 个读 Action 无调用方(getElectiveCoursesAction/getStudentSelectionsAction/getAvailableCoursesForStudentAction),页面绕过 Action 直接调用 data-access
  • P0update/delete/select/drop/lottery Action 缺资源归属校验(教师 A 可操作教师 B 的课程,学生可退选他人课程)
  • P0i18n 完全缺失4 组标签/颜色常量硬编码英文,组件中硬编码中文)
  • P0错误边界完全缺失3 个角色目录均无 error.tsx
  • ⚠️ P1elective-course-form.tsx 存在 v as "fcfs" | "lottery" 类型断言
  • ⚠️ P1elective-course-list.tsx 存在 null as never 类型逃逸
  • ⚠️ P1buildLotteryRankCase 未导出,无法单测
  • ⚠️ P1SELECTION_MODE_LABELS 已定义但表单未复用,硬编码 Select 选项
  • P1 已修复:buildCourseSelect 跨模块 join users/subjects/grades 表 改为只查 electiveCourses 表,通过 resolveCourseDisplayNames 调用 school/users data-access 获取显示名称
  • P1 已修复:getSubjectOptions 本地直查 subjects 表且与 school 模块重复 删除本地实现,改用 school/data-access.getSubjectOptions
  • P1 已修复:selectCourse/dropCourse 缺事务包裹 改为 db.transaction 包裹FCFS 模式下使用 FOR UPDATE 行锁防止并发超卖
  • P2 已修复:mapCourseRow 在 data-access.ts 与 data-access-selections.ts 重复定义 抽取到 data-access.ts 统一导出data-access-selections.ts 复用
  • P2 已修复:runLottery 使用 sort(() => Math.random() - 0.5) 有偏 shuffle 改为 Fisher-Yates 无偏洗牌算法
  • P2 已修复:selectCourse FCFS 并发超卖风险 使用 db.transaction + .for("update") 行锁
  • 权限校验完整ELECTIVE_MANAGE/SELECT/READ

文件清单

文件 行数 职责
actions.ts 304 11 个 Server Action
data-access.ts 250 课程 CRUD + scope 过滤 + 共享映射函数P3 重构:移除跨模块 join通过 school/users data-access 获取显示名称)
data-access-operations.ts 245 选课操作select/drop/lotteryP3 重构:事务包裹 + FOR UPDATE 锁 + Fisher-Yates 洗牌)
data-access-selections.ts 149 选课记录查询 + 学生可选课程
schema.ts 132 Zod 校验
types.ts 108 类型定义 + 4 组标签/颜色常量(硬编码英文)
components/elective-course-list.tsx 233 课程卡片网格 + 管理操作
components/elective-course-form.tsx 293 课程创建/编辑表单
components/elective-filters.tsx 49 nuqs 筛选栏(搜索 + 模式)
components/student-selection-view.tsx 250 学生选课视图(已选 + 可选)

2.21 proctoring监考模块

职责:考试监考事件记录 + 防作弊监控 + 监考面板。

导出函数

  • ActionsrecordProctoringEventAction / getProctoringDashboardAction
  • Data-accessrecordProctoringEvent / getExamSubmissionForProctoring / getExamForProctoring / getExamProctoringSummary / getStudentProctoringStatuses / getRecentProctoringEvents P2 已修复:getExamProctoringSummary 使用 Promise.all 并行化考试信息与提交记录查询、事件类型统计与学生事件统计查询;合并两次 filter 为单次循环统计 started/submitted

依赖关系

  • 依赖:shared/*@/authexams P1-1 已修复:通过 exams data-access.getExamForProctoringCrossModule/getExamSubmissionForProctoringCrossModule/getExamSubmissionsForExam/getExamTitleByIdusers P1-1 已修复:通过 users data-access.getUserNamesByIds
  • 被依赖:无

已知问题

  • P0exam-mode-config.tsx 未集成到考试表单(死代码,监考功能无法启用)
  • P0-6 已修复:事件上报存在 Server Action 与 REST API 双通道重复 删除 /api/proctoring/event REST 路由(移至 deletes/Server Action recordProctoringEventAction 为唯一规范路径
  • P1-1 已修复:跨模块直查 exams/examSubmissions/users 改为通过 exams/users data-access 函数获取数据
  • P2 已修复:actions.ts 不再直接 import dbexamSubmissionssubmission 归属校验已下沉到 data-accessrecordProctoringEventAction 改用 requirePermission(EXAM_SUBMIT) 并增加 revalidatePath
  • P2 已修复:getStudentProctoringStatuses 串行查询getUserNamesByIds 后再查事件) 改为 Promise.all 并行拉取学生姓名与事件记录

文件清单

文件 行数 职责
data-access.ts 409 事件记录 + 查询 + 摘要统计 + submission 归属校验
actions.ts 139 2 个 Server Action
types.ts 136 类型定义 + 标签常量 + 阈值常量
components/anti-cheat-monitor.tsx - 学生端防作弊监控
components/exam-mode-config.tsx - 考试模式配置(未集成
components/proctoring-dashboard.tsx - 教师监考面板

2.22 diagnostic学情诊断模块

职责:知识点掌握度查询 + 诊断报告生成。

导出函数

  • ActionsgenerateStudentReportAction / generateClassReportAction / publishReportAction / deleteReportActionv2-P2-3 修复:删除死代码 getDiagnosticReportsAction / getDiagnosticReportByIdAction,页面直接调用 data-access 并自行权限校验)
  • Data-accessupdateMasteryFromSubmissionv2-P1-8 修复累积模式v2-P2-5 修复db.transaction 包裹)/ getStudentMastery / getStudentMasterySummary / getClassMasterySummaryv2-P2-4 修复totalStudents 语义 + 班级平均掌握度按学生平均)/ getKnowledgePointStatsv2-P1-7 修复:页面先查班级再传参)
  • Data-access-reportsgenerateDiagnosticReport / generateClassDiagnosticReportv2-P2-6 修复:校验掌握度数据)/ getDiagnosticReports / getDiagnosticReportById / publishDiagnosticReport / deleteDiagnosticReport P2 已修复:使用 React.cache() 包装实现请求级 memoization
  • Stats-service v2-P1-6 新增):serializeMasteryWithKp / computeAverageMastery / classifyStrengthsWeaknesses / buildStudentMasterySummary / aggregateClassMastery / computeKpStats / computeClassAverageMastery / buildStudentsNeedingAttention / buildClassMasterySummary / buildStudentReportContent / buildClassReportContent / computeMasteryLevel / serializeMastery(从 data-access / data-access-reports 抽取的纯统计函数)
  • SchemaGenerateStudentReportSchema / GenerateClassReportSchema / PublishReportSchema / DeleteReportSchemav2-P2-3 修复:删除死代码 GetDiagnosticReportsSchema / GetDiagnosticReportByIdSchema

依赖关系

  • 依赖:shared/*@/authexams P1-1 已修复:通过 exams data-access.getExamSubmissionWithAnswersquestions P1-1 已修复:通过 questions data-access.getKnowledgePointsForQuestionsclasses P1-1 已修复:通过 classes data-access.getClassExists/getClassNameById/getActiveStudentIdsByClassIdv2-P1-7 新增 getStudentActiveClassIdusers P1-1 已修复:通过 users data-access.getUserNamesByIds/getUserIdsByGradeId
  • 被依赖:无

已知问题

  • P1-1 已修复:updateMasteryFromSubmission 跨模块直查 4 张表 改为调用 exams/data-access.getExamSubmissionWithAnswersquestions/data-access.getKnowledgePointsForQuestions
  • P2 已修复:data-access-reports.ts 有未使用代码(round2 + void round2 已删除死代码
  • P2 已修复:updateMasteryFromSubmission 循环内串行 await upsert 改为 Promise.all 并行执行所有 upsert
  • P2 已修复:getClassMasterySummary 串行查询 改为两组 Promise.all 并行
  • P2 已修复:getDiagnosticReportsconditions 隐式 any[] 改为显式 SQL[] 类型标注
  • P0-2 已修复:data-access-reports.ts 直查 users 表获取姓名 改为通过 users/data-access.getUserNamesByIds
  • P2-2 已修复:Tailwind 任意值 改用标准 Tailwind 类
  • P2-1 已修复:图表/表格/列表缺少 a11y ARIA 属性 为 5 个图表添加 role="img" + aria-label2 个表格添加 <caption>3 个列表添加 role="list"3 个图标按钮添加 aria-label
  • P2-3 已修复:班级报告将生成者 ID 存入 studentId 字段 schema 改为可空,班级报告 studentId 置空
  • v2-P1-6 已修复:统计逻辑混在 data-access 层 抽取 stats-service.ts352 行12 个纯函数 + 2 个常量 + 4 个接口)
  • v2-P1-7 已修复:getKnowledgePointStats 无参调用 页面先查 getStudentActiveClassId 再传参
  • v2-P1-8 已修复:updateMasteryFromSubmission 覆盖模式 改为累积计算(读取已有记录后累加)
  • v2-P2-3 已修复:死代码 getDiagnosticReportsAction / getDiagnosticReportByIdAction 全局零调用 已删除,页面直接调用 data-access
  • v2-P2-4 已修复:totalStudents 语义错误 + 班级平均掌握度计算偏差 改为实际有掌握度记录的学生数;先算学生个人平均再取平均
  • v2-P2-5 已修复:多 upsert 无事务包裹 使用 db.transaction() 保证原子性
  • v2-P2-6 已修复:生成报告未校验掌握度数据 添加 totalKnowledgePoints === 0studentCount === 0 校验
  • v2-P1-4 已修复:4 个组件 i18n 完全未接入 全部接入 useTranslations("diagnostic")
  • v2-P2-7 已修复:report-list.tsx 过滤器 Label 缺少 htmlFor 添加 htmlForid
  • 与 grades 模块无职责重叠

文件清单

文件 行数 职责
data-access.ts 179 知识点掌握度查询 + 更新v2-P1-8 累积模式v2-P2-5 事务v2-P2-4 语义修正v2-P1-6 改用 stats-service 纯函数)
data-access-reports.ts 160 诊断报告 CRUDv2-P2-6 校验v2-P1-6 改用 stats-service 纯函数)
stats-service.ts 352 统计计算纯函数v2-P1-6 新增12 个纯函数 + 2 个常量 + 4 个接口)
actions.ts 111 4 个 Server Actionv2-P2-3 删除 2 个死代码读 Action
schema.ts 23 Zod 校验4 个 schemav2-P2-3 删除 2 个死代码 schema
types.ts 87 类型定义
components/class-diagnostic-view.tsx 266 班级诊断视图v2-P1-6 热力图 a11yv2-P1-4 i18n
components/student-diagnostic-view.tsx 225 学生诊断视图v2-P1-4 i18n
components/mastery-radar-chart.tsx 72 雷达图v2-P1-4 i18n
components/report-list.tsx 265 报告列表v2-P2-7 Label htmlForv2-P1-4 i18n

2.23 settings设置模块

职责:系统设置(学校信息/安全策略/文件上传/通知配置)+ AI Provider 管理 + 密码修改 + 个人资料 + 主题偏好 + 通知偏好 + 个人信息页(学生/教师概览)。

导出函数

  • ActionsgetAiProvidersAction / createAiProviderAction / updateAiProviderAction / deleteAiProviderAction / testAiProviderAction
  • Actions-passwordchangePasswordAction P1 已修复:使用 requirePermission(USER_PROFILE_UPDATE) + Zod 校验 + DB 操作下沉到 data-access
  • Actions-avatarupdateUserAvatarAction / removeUserAvatarAction P2-8 新增:头像上传/删除,复用 /api/upload 路由)
  • Actions-notificationssendTestNotificationAction P2-10 新增:发送测试通知,占位实现待接入真实通知服务)
  • Actions-system-settingsgetAdminSystemSettingsAction / saveAdminSystemSettingsAction P0-3 新增:管理员系统设置 CRUD4 分类 Zod 校验)
  • Actions-securitygetSecurityCenterAction / toggleTwoFactorAction P2-9 新增2FA 状态查询/切换 + 最近登录历史)
  • Data-accessgetAiProviderSummaries / countDefaultAiProviders / getAiProviderForUpdate / updateAiProvider / createAiProvider / getUserPasswordHash / getPasswordSecurityByUserId / updateUserPassword / upsertPasswordSecurityOnPasswordChangeP1 新增,从 actions 下沉)
  • Data-access-system-settingsgetSystemSettingsByCategory / getAllSystemSettings / getSystemSetting / upsertSystemSetting / upsertSystemSettings P0-3 新增system_settings 表 CRUD键值对存储模式
  • ComponentsSettingsView统一设置页布局5 标签页 General/Notifications/Appearance/Security/AI角色差异通过 resolveRoleSettingsConfig 配置驱动 + generalExtra props 注入Tab URL 持久化;每个 TabsContent 包裹 SettingsSectionErrorBoundary + Suspense 骨架屏AI 标签页条件渲染需 AI_CONFIGURE 权限)、SettingsServiceProvider / useSettingsServiceContext 注入 SettingsService 接口,解耦组件对 users/messaging actions 的直接依赖)、SettingsSectionErrorBoundary(分区 Error Boundary局部失败不影响整页QuickLinksCard快捷链接卡片i18n 键驱动)、ProfileStudentOverview / ProfileStudentOverviewSkeleton(学生概览异步 Server Component + 骨架屏)、ProfileTeacherOverview / ProfileTeacherOverviewSkeleton(教师概览异步 Server Component + 骨架屏)、AdminSettingsView P0-3 已修复:从 mock 改为真实数据层,通过 Server Actions 加载/保存到 system_settings 表)、AvatarUpload P2-8 新增:头像上传/预览/删除客户端组件,文件验证 + i18nSecurityCenterCard P2-9 新增2FA 开关 + 最近登录历史卡片)、ThemePreferencesCard P2-11 已增强:集成 LocaleSwitcher 语言切换)
  • ConfigROLE_SETTINGS_CONFIG / resolveRoleSettingsConfig(配置驱动角色 → 设置视图映射,新增角色只需添加条目)
  • LibbuildStudentOverviewData / computeStudentStats / sortUpcomingAssignments / filterTodaySchedule / toWeekday(纯数据计算函数,与 UI 分离,便于单元测试)
  • TypesAiProviderSummary / AiProviderName / AiProviderExisting / SettingsService / ProfileService / NotificationPreferenceService(服务接口定义,用于依赖注入解耦)

依赖关系

  • 依赖:shared/*(含 shared/lib/bcrypt-utils)、@/authmessaging(页面层通过 SettingsService 接口注入,组件层不直接 importusers(页面层通过 SettingsService 接口注入)、classes / homework / dashboardProfileStudentOverview 异步组件获取学生概览数据)、notifications(页面层获取通知偏好)
  • 被依赖:无

已知问题

  • ⚠️ P2混合 5 类职责AI Provider + 密码 + 资料 + 主题 + 通知偏好)
  • P1 已修复:data-access.tsactions.ts 直接使用 db 新建 data-access.ts,所有 DB 操作已下沉
  • P1 已修复:changePasswordAction 使用 requireAuth() 无 Zod 校验 改为 requirePermission(USER_PROFILE_UPDATE) + ChangePasswordSchema Zod 校验 + 并行查询优化
  • P2 已修复:actions-password.ts 删除本地 normalizeBcryptHash,统一复用 shared/lib/bcrypt-utils.normalizeBcryptHash,消除重复代码
  • P2-a 已修复:admin/teacher/student/parent 四个设置视图重复布局 新增 SettingsView 统一设置页布局 + resolveRoleSettingsConfig 配置驱动角色路由,删除 parent-settings-view.tsx / student-settings-view.tsx / teacher-settings-view.tsx
  • parent 角色路由已修复:通过 ROLE_SETTINGS_CONFIG 配置驱动parent 用户正确渲染对应配置
  • Tab URL 持久化已修复:SettingsView 改为受控模式,通过 useSearchParams 读取 tab 参数,router.push 更新 URL
  • 登出二次确认已修复:SettingsView 的 Log out 按钮使用 AlertDialog 包裹,点击时弹出确认对话框
  • AiProviderSettingsCard 已集成:SettingsView 新增 AI 标签页,条件渲染需 AI_CONFIGURE 权限
  • password-change-form 任意值 Tailwind 类已修复:[&>div]:bg-red-500 等任意值类 Progress 组件新增 indicatorClassName prop使用标准颜色类
  • P0 已修复:notification-preferences-form.tsx 跨模块直接 import messaging/actions 改为通过 useSettingsService().notifications.updatePreferences 调用,页面层注入实现
  • P0 已修复:profile-settings-form.tsx 跨模块直接 import users/actions 改为通过 useSettingsService().profile.updateProfile 调用,页面层注入实现
  • P0 已修复:i18n 完全缺失 新增 settings.json 翻译文件zh-CN + en所有组件改用 useTranslations / getTranslations
  • P1 已修复:缺少 Error Boundary 新增 SettingsSectionErrorBoundary,每个 TabsContent + profile 页面角色概览区块均包裹
  • P1 已修复:缺少 Suspense 骨架屏 每个 TabsContent 包裹 Suspense + SettingsSectionSkeletonprofile 页面包裹 ProfileStudentOverviewSkeleton / ProfileTeacherOverviewSkeleton
  • P1 已修复:profile/page.tsx 业务逻辑与 UI 混合 抽取 buildStudentOverviewData 等纯函数到 lib/student-overview-data.ts;拆分 ProfileStudentOverview / ProfileTeacherOverview 异步组件
  • 密码修改有速率限制
  • AI Provider 操作有 AI_CONFIGURE 权限校验
  • P0-3 已修复:AdminSettingsView 为 mock 实现,无数据持久化 新增 system_settings 表(键值对存储)+ data-access-system-settings.ts + actions-system-settings.tsAdminSettingsView 改为真实数据层
  • P2-8 已修复:无头像上传 新增 updateUserAvatar data-access + actions-avatar.ts + AvatarUpload 组件,复用 /api/upload 路由
  • P2-9 已修复:无 2FA / 会话管理 新增 actions-security.ts + SecurityCenterCard 组件2FA 开关占位 + 最近登录历史来自 login_logs 表)
  • P2-10 已修复:通知偏好表单无测试通知按钮 新增 sendTestNotificationAction,每个已启用渠道旁显示测试按钮
  • P2-11 已修复:语言切换未集成到设置页 ThemePreferencesCard 集成 LocaleSwitcher 到 Appearance 标签页
  • v1.1 已修复:/settings 页面 ErrorBoundary 触发Functions cannot be passed directly to Client Components 新增 actions-service.ts"use server" 文件),导出 updateProfileAction + updateNotificationPreferencesAction 两个 Server Action wrapperpage.tsx 直接传递 Server Action 引用;SettingsService 接口的 getProfile/getPreferences 改为可选
  • v1.1 已修复:i18n 键双重 settings. 前缀MISSING_MESSAGE role-settings-config.tsxdescriptionKey 去掉 settings. 前缀
  • v1.1 已修复:AI 标签页 FormLabel 在 Form 上下文外使用getFieldState null ai-provider-settings-card.tsx 中两处 <FormLabel> 改为 <Label>
  • v1.1 已修复:非管理员访问 /settings?tab=ai 显示空白 settings-view.tsx 新增 resolveTab() 函数,对无权限的 tab 回退到 general
  • v1.1 已修复:数据库 notification_preferences 表缺少 quiet_hours_* 字段 通过 ALTER TABLE 添加缺失字段
  • v1.1 已修复:数据库 system_settings 表不存在 通过 CREATE TABLE 创建

文件清单

文件 行数 职责
actions.ts 160 AI Provider CRUD + 测试P1 已修复,无直接 DB 操作)
actions-password.ts 87 修改密码P1 已修复requirePermission + Zod + data-access
actions-avatar.ts 56 头像上传/删除P2-8 新增requirePermission + revalidatePath
actions-notifications.ts 46 发送测试通知P2-10 新增:占位实现待接入真实通知服务)
actions-system-settings.ts 186 管理员系统设置 CRUDP0-3 新增4 分类 Zod 校验 + upsert
actions-security.ts 165 2FA 状态查询/切换 + 最近登录历史P2-9 新增)
actions-service.ts 60 设置页 Server Action wrapperv1.1 新增updateProfileAction + updateNotificationPreferencesAction作为 Server Action 引用传递给 Client Component避免 Server→Client 函数传递违规)
data-access.ts 158 AI Provider CRUD + 密码修改 DB 操作P1 新增)
data-access-system-settings.ts 119 system_settings 表 CRUDP0-3 新增:键值对存储模式)
types.ts 60 类型定义AiProviderSummary + SettingsService/ProfileService/NotificationPreferenceService 接口)
config/role-settings-config.tsx 85 角色设置页配置驱动映射ROLE_SETTINGS_CONFIG + resolveRoleSettingsConfig
lib/student-overview-data.ts 150 学生概览纯数据计算buildStudentOverviewData + computeStudentStats 等,便于单测)
components/settings-view.tsx 236 SettingsView 统一设置页布局5 标签页 + Error Boundary + Suspense + i18n + SecurityCenterCard 集成)
components/settings-service-context.tsx 39 SettingsServiceProvider + useSettingsServiceContext 注入服务接口)
components/settings-section-error-boundary.tsx 64 分区 Error Boundary局部失败不影响整页
components/quick-links-card.tsx 42 快捷链接卡片i18n 键驱动)
components/profile-student-overview.tsx 91 学生概览异步 Server Component + 骨架屏
components/profile-teacher-overview.tsx 115 教师概览异步 Server Component + 骨架屏
components/admin-settings-view.tsx 425 AdminSettingsView 系统设置视图P0-3 已修复:真实数据层 + 4 个 Card + i18n
components/avatar-upload.tsx ~150 头像上传/预览/删除客户端组件P2-8 新增:文件验证 + i18n
components/security-center-card.tsx ~240 安全中心卡片P2-9 新增2FA 开关 + 最近登录历史)
components/profile-settings-form.tsx 158 个人资料表单(通过 SettingsService 注入i18n
components/notification-preferences-form.tsx ~160 通知偏好表单(通过 SettingsService 注入i18n + P2-10 测试按钮)
components/password-change-form.tsx ~130 密码修改表单i18n + a11y
components/theme-preferences-card.tsx ~80 主题偏好卡片i18n + P2-11 LocaleSwitcher 集成)
components/ai-provider-settings-card.tsx ~200 AI 服务商配置卡片i18n

2.24 auth认证 UI 模块)

职责:认证页面 UI登录/注册/布局)。

导出函数:纯 UI 组件(LoginForm / RegisterForm / AuthLayout

依赖关系

  • 依赖:shared/*
  • 被依赖:app/(auth)/*

已知问题

  • 纯 UI 模块,无 data-access/actions/types
  • 认证逻辑由 NextAuth + shared/lib/auth-guard 统一处理

文件清单

文件 职责
components/auth-layout.tsx 认证页面布局
components/login-form.tsx 登录表单
components/register-form.tsx 注册表单

2.25 layout布局模块

职责:应用骨架(侧边栏 + 顶部导航 + 导航配置 + 多角色切换)。

导出函数AppSidebar / SidebarProvider / SiteHeader + navigation 配置

依赖关系

  • 依赖:shared/hooks/use-permission@/authuseSessionmessaging(通知下拉)、shared/components/ui/select(角色切换下拉)
  • 被依赖:app/(dashboard)/layout.tsx

已知问题

  • P2 已修复:用权限反推角色 app-sidebar.tsx 改用 hasRole() 判断角色,并新增多角色切换机制(SidebarContext.currentRole/setCurrentRolenull 表示自动检测;当用户拥有多个角色时在侧边栏底部显示 Select 下拉切换)
  • navigation.ts 无幽灵路由13 个已修复)

文件清单

文件 职责
components/app-sidebar.tsx 侧边栏(根据权限渲染 + 多角色切换下拉)
components/sidebar-provider.tsx 侧边栏状态 ContextcurrentRole/setCurrentRole
components/site-header.tsx 顶部导航(含通知下拉)
config/navigation.ts 导航配置4 个角色)

2.26 student学生 UI 模块)

职责:学生端 UI 组件(课程视图 + 课表筛选/视图)。

导出函数StudentCoursesView / StudentScheduleFilters / StudentScheduleView

依赖关系

  • 依赖:shared/*
  • 被依赖:app/(dashboard)/student/*

已知问题

  • ⚠️ P2与 classes 模块的 schedule-view.tsx/schedule-filters.tsx 可能功能重叠
  • 纯 UI 模块,数据由页面通过 classes data-access 获取
  • 认证模式已统一:所有 student 页面使用 getCurrentStudentUser()users 模块)或 getAuthContext()shared 模块),不再直接调用 auth()getDemoStudentUser()

文件清单

文件 职责
components/student-courses-view.tsx 学生课程视图(含 ClassCard memo 组件 + 加入班级表单,使用 useTransition
components/student-schedule-filters.tsx 课表筛选器
components/student-schedule-view.tsx 学生课表视图

路由文件清单app/(dashboard)/student/

文件 职责
dashboard/page.tsx + loading.tsx 学生仪表盘 + 骨架屏
attendance/page.tsx + loading.tsx 学生考勤 + 骨架屏
diagnostic/page.tsx + loading.tsx 学情诊断 + 骨架屏
elective/page.tsx + loading.tsx 选课中心 + 骨架屏
grades/page.tsx + loading.tsx 我的成绩 + 骨架屏
learning/assignments/page.tsx + loading.tsx 作业列表(含 AssignmentCard 组件)+ 骨架屏
learning/assignments/[assignmentId]/page.tsx + loading.tsx 作业作答/复习 + 骨架屏
learning/courses/page.tsx + loading.tsx 课程列表 + 骨架屏
learning/textbooks/page.tsx + loading.tsx 教材列表 + 骨架屏
learning/textbooks/[id]/page.tsx + loading.tsx 教材阅读 + 骨架屏
schedule/page.tsx + loading.tsx 课表 + 骨架屏
error.tsx 路由组错误边界(提供"重试"按钮)

2.27 lesson-preparation备课模块

职责:教师备课,基于教材章节创建课案(节点图编辑器 React Flow),支持模板、版本管理、知识点标注、题目创建/拉取、作业发布。

架构变更2026-06-21编辑器从列表式BlockRenderer + @dnd-kit升级为节点图式NodeEditor + @xyflow/react。数据结构从 v1blocks 数组)升级到 v2nodes + edges 节点图),旧数据通过 migrateV1ToV2() 自动迁移。

数据结构

  • v1已废弃仅向后兼容读取{ version: 1, blocks: Block[] }
  • v2当前{ version: 2, nodes: LessonPlanNode[]; edges: LessonPlanEdge[] }
    • LessonPlanNodeBlock + position: { x, y }(画布坐标)
    • LessonPlanEdge{ id, source, target, sourceHandle?, targetHandle? }(节点间连线)

导出函数

  • Data-accessdata-access.tsgetLessonPlans / getLessonPlanById / createLessonPlan / updateLessonPlanContent / softDeleteLessonPlan / duplicateLessonPlan / getTemplateById / buildInitialContent / migrateV1ToV2v1→v2 迁移blocks 数组转换为 nodes + 线性 edges/ normalizeDocument(规范化:确保 content 为 v2 格式,兼容旧数据)
  • Data-access-versionsdata-access-versions.tsgetLessonPlanVersions / createLessonPlanVersion / getVersionContent / revertToVersion / pruneAutoVersions
  • Data-access-templatesdata-access-templates.tsgetLessonPlanTemplates / saveAsTemplate / deletePersonalTemplate
  • Data-access-knowledgedata-access-knowledge.tsgetLessonPlansByKnowledgePoint / getLessonPlansByQuestion
  • Publish-servicepublish-service.tspublishLessonPlanHomework
  • AI-suggestai-suggest.tssuggestKnowledgePoints
  • ActionsgetLessonPlansAction / getLessonPlanByIdAction / createLessonPlanAction / updateLessonPlanAction / saveLessonPlanVersionAction / getLessonPlanVersionsAction / revertLessonPlanVersionAction / deleteLessonPlanAction / duplicateLessonPlanAction / getLessonPlanTemplatesAction / saveAsTemplateAction / deleteTemplateAction / suggestKnowledgePointsAction / publishLessonPlanHomeworkAction / getKnowledgePointOptionsAction

依赖关系

  • 依赖:shared/*@/authshared/lib/ai@xyflow/react(节点图编辑器)、textbooks(只读章节/知识点树)、questions(创建/查询题目)、exams(创建 exam 草稿)、homework(创建作业下发)、classes(查询教师班级)、files(附件)
  • 被依赖:无

已知问题

  • 通过对方 data-access 调用跨模块数据,无直查跨模块表
  • data-access 按职责拆分为 4 个文件data-access/data-access-versions/data-access-templates/data-access-knowledge
  • actions 按职责拆分为 4 个文件actions/actions-publish/actions-ai/actions-kp
  • 编辑器架构升级NodeEditorReact Flow 画布)+ NodeEditPanel侧边内容编辑面板+ LessonNode自定义节点组件支持节点拖拽、连线、画布缩放
  • ⚠️ block-renderer.tsx 标记为 @deprecated已被 NodeEditor 替代,保留用于向后兼容)

架构变更2026-06-22本次审计修复

  • P0-1 跨模块直查修复publish-service.ts 不再直接 db.insert(examQuestions) 和本地实现 getStudentIdsByClassIds,改为调用 exams/data-access.addExamQuestionsclasses/data-access.getStudentIdsByClassIds,恢复三层架构约束
  • P0-2 i18n 接入:新增 shared/i18n/messages/zh-CN/lesson-preparation.jsonshared/i18n/messages/en/lesson-preparation.json,注册 lessonPreparation 命名空间到 src/i18n/request.ts17 个组件改造为 useTranslations/getTranslations
  • P0-3 DataScope 过滤修复data-access.tsbuildScopeCondition 按 scope 类型精确过滤——class_taught 增加 subjectId IN teacher.subjects AND gradeId IN teacher.gradesgrade_managed 限制 gradeId IN managedGradesclass_members/children 仅允许查看 published 课案
  • P1-1 类型安全修复as never 断言全部替换为类型守卫函数(isQuestionType/isV1Document/isV2Document+ validTypes 数组;block-renderer.tsx 使用 as ExerciseBlockData/as TextStudyBlockData 精确断言;constants.tsBLOCK_TYPE_LABELS/LESSON_PLAN_STATUS_LABELS 改为 i18n 键(BLOCK_TYPE_KEYS/LESSON_PLAN_STATUS_KEYS
  • P1 纯函数抽取:新增 lib/document-migration.tsmigrateV1ToV2/normalizeDocument/buildInitialContent使用类型守卫替代 as 断言)、lib/node-summary.tsgetNodeSummary + NODE_COLORS + getNodeColor接受翻译函数注入lib/rf-mappers.tstoRfNodes/toRfEdges/fromRfEdgesdata-access.ts 改为从 lib/ 导入并 re-export 保持向后兼容
  • P1-2/P1-3 错误边界 + 骨架屏:新增 components/lesson-plan-error-boundary.tsxLessonPlanErrorBoundary 类组件)和 components/lesson-plan-skeleton.tsxVersionListSkeleton/QuestionBankSkeleton/KnowledgePointSkeleton/LessonPlanListSkeleton
  • P1-4 阻塞式 UI 修复alert() 全部替换为 sonner toastconfirm() 全部替换为 AlertDialogshadcnwindow.location.reload 替换为 router.refresh();涉及 lesson-plan-card/version-history-drawer/inline-question-editor/text-study-block/exercise-block
  • P1-5/P1-7 多实例 + 角色配置驱动:新增 providers/lesson-plan-provider.tsxLessonPlanProvider + Context + 4 个角色配置 TEACHER/ADMIN/STUDENT/PARENT + LessonPlanDataService 接口 + LessonPlanTracker 埋点接口 + useLessonPlanContextSafe/useRoleConfig 等 hooksservices/default-data-service.ts(包装 Server Actions 为 DataService 实现lesson-plan-list/lesson-plan-card 通过 Context 注入数据服务useRoleConfig 控制按钮可见性
  • P1-8 Block 注册表:新增 config/block-registry.tsxBLOCK_REGISTRY 配置表 + getBlockComponent/isRichTextBlocknode-edit-panel.tsx 重构为配置驱动渲染,移除 if/else 链
  • P1-4 window.location.reload 修复exercise-block.tsx 改用 router.refresh() 精确刷新缓存
  • P2-1 a11y 修复5 个组件question-bank-picker/publish-homework-dialog/knowledge-point-picker/exercise-block/text-study-block添加 role="dialog"/aria-modal/aria-labelinline-question-editor 添加 role="dialog"/aria-modal/aria-label
  • P2-4 监控埋点预留providers/lesson-plan-provider.tsx 定义 LessonPlanTracker 接口 + noopTracker 默认空实现,生产环境可替换为真实埋点

文件清单

文件 职责
types.ts 类型定义(含 v1/v2 文档类型、LessonPlanNode、LessonPlanEdge
constants.ts 常量定义
schema.ts Zod 验证
lib/document-migration.ts 纯函数v1→v2 迁移migrateV1ToV2/ 规范化normalizeDocument/ 初始内容构建buildInitialContent使用类型守卫 isV1Document/isV2Document 替代 as 断言
lib/node-summary.ts 纯函数getNodeSummary接受翻译函数注入支持 i18n+ NODE_COLORS + getNodeColor
lib/rf-mappers.ts 纯函数toRfNodes/toRfEdges/fromRfEdgesLessonPlanNode/Edge ↔ React Flow Node/Edge 映射)
config/block-registry.tsx 配置驱动BLOCK_REGISTRY 注册表 + getBlockComponent/isRichTextBlocknode-edit-panel 通过配置渲染 Block
providers/lesson-plan-provider.tsx Provider + ContextP1-5/P1-7/P2-4/V2-6LessonPlanProvider 注入数据服务/角色配置/埋点;定义 LessonPlanDataService 接口、4 个角色配置TEACHER/ADMIN/STUDENT/PARENT、ROLE_CONFIGS 注册表、LessonPlanTracker 接口 + noopTrackerhooksuseLessonPlanContextSafe返回 null 不抛错)/useLessonPlanContext/useRoleConfig/useLessonPlanService/useLessonPlanTracker/useLessonPlanTrackerSafeV2-6 新增,返回 noopTracker 不抛错)
services/default-data-service.ts 默认数据服务实现createDefaultDataService() 包装 Server Actions 为 LessonPlanDataService 实现,测试可替换为 mock
data-access.ts 课案 CRUD + 模板查询migrateV1ToV2/normalizeDocument/buildInitialContent 从 lib/ 导入并 re-export 保持向后兼容buildScopeCondition 按 scope 类型精确过滤 P0-3V2-1抛出 LessonPlanDataError 错误码V2-3mapRowToLessonPlan/mapRowToListItem/mapRowToTemplate 显式映射 + isLessonPlanStatus/isTemplateType/isTemplateScope 类型守卫)
data-access-versions.ts 版本管理(创建/查询/回滚/清理V2-3mapRowToVersion 显式映射)
data-access-templates.ts 个人模板 CRUDV2-3mapRowToTemplate 显式映射 + 类型守卫)
data-access-knowledge.ts 按知识点/题目反查课案V2-3显式字段映射替代 as unknown as
actions.ts 课案 CRUD/版本/模板 Server ActionsV2-1getTranslations i18n + 错误码捕获V2-2createLessonPlanAction 传入 translateTitle 翻译 SYSTEM_TEMPLATES
actions-publish.ts 发布作业 Server ActionV2-1getTranslations i18n + PUBLISH_ERROR_KEY_MAP 错误码映射)
actions-ai.ts AI 知识点建议 Server ActionV2-1i18n + 错误码)
actions-kp.ts 知识点选项 Server ActionV2-1i18n + 错误码)
publish-service.ts 发布作业服务(编排 homework/exams/classes通过对方 data-access 调用无直查跨模块表V2-1抛出 PublishServiceError 错误码V2-3显式字段映射替代 as unknown as
ai-suggest.ts AI 知识点建议服务
seed-templates.ts 模板种子数据
hooks/use-lesson-plan-editor.ts 课案编辑器 Hook基于 zustand支持 nodes/edges 操作addNode/updateNode/updateNodePosition/removeNode/connect/disconnect/setEdges/selectNode
components/lesson-plan-list.tsx 课案列表i18n 已接入)
components/lesson-plan-card.tsx 课案卡片i18n 已接入V2-6duplicate/archive 调用 tracker.track
components/lesson-plan-filters.tsx 课案筛选器i18n 已接入V2-53 个表单元素 label htmlFor 关联)
components/lesson-plan-editor.tsx 课案编辑器(编排 NodeEditor + NodeEditPaneli18n 已接入V2-6handleManualSave 调用 tracker.track
components/node-editor.tsx 节点图画布React Flow使用 lib/rf-mappers + lib/node-summary 纯函数i18n 已接入V2-4MiniMap 复用 getNodeColorV2-5role=application + 键盘导航配置)
components/node-edit-panel.tsx 侧边内容编辑面板(配置驱动渲染 Block通过 getBlockComponent + LessonPlanErrorBoundary 包裹i18n 已接入)
components/nodes/lesson-node.tsx 自定义节点组件(使用 lib/node-summary 的 getNodeSummary/getNodeColori18n 已接入)
components/lesson-plan-error-boundary.tsx 错误边界LessonPlanErrorBoundary 类组件,支持 fallback 和 onError 回调
components/lesson-plan-skeleton.tsx 骨架屏VersionListSkeleton/QuestionBankSkeleton/KnowledgePointSkeleton/LessonPlanListSkeleton
components/block-renderer.tsx ⚠️ @deprecated Block 渲染器(已被 NodeEditor 替代,保留向后兼容)
components/template-picker.tsx 模板选择器i18n 已接入V2-6create 调用 tracker.track
components/version-history-drawer.tsx 版本历史抽屉i18n 已接入V2-6revert 调用 tracker.track
components/knowledge-point-picker.tsx 知识点选择器i18n 已接入)
components/question-bank-picker.tsx 题库选择器i18n 已接入)
components/inline-question-editor.tsx 内联题目编辑器i18n 已接入V2-5type/difficulty select label htmlFor 关联)
components/publish-homework-dialog.tsx 发布作业对话框i18n 已接入V2-6publish 调用 tracker.track
components/blocks/rich-text-block.tsx 富文本 Block被 NodeEditPanel 复用i18n 已接入)
components/blocks/text-study-block.tsx 课文研读 Block被 NodeEditPanel 复用i18n 已接入)
components/blocks/exercise-block.tsx 练习 Block被 NodeEditPanel 复用,使用 router.refresh 替代 window.location.reloadi18n 已接入V2-5purpose select label 关联 + 题目列表 ul/li 语义化)
components/blocks/reflection-block.tsx 反思 Block被 NodeEditPanel 复用i18n 已接入)

2.28 error-book错题本模块

职责:自动采集考试/作业错题,基于 SM-2 间隔重复算法科学复习,提供知识点薄弱度分析与多角色视图(学生/教师/家长/管理员)。

导出函数

  • ActionsgetErrorBookItemsAction / getErrorBookItemDetailAction / getErrorBookStatsAction / createErrorBookItemAction / updateErrorBookNoteAction / reviewErrorBookItemAction / deleteErrorBookItemAction / archiveErrorBookItemAction / collectFromSubmissionAction
  • Data-accessgetErrorBookItems / getErrorBookItemById / getErrorBookStats / createErrorBookItem / updateErrorBookNote / recordReview / deleteErrorBookItem / archiveErrorBookItem / collectFromExamSubmission / collectFromHomeworkSubmission / getStudentErrorBookSummaries / getTopWrongQuestionsByStudentIds / getKnowledgePointWeakness / getSubjectErrorDistribution / getStudentNameMap / getStudentIdsByClassIdList
  • SchemaCreateErrorBookItemSchema / UpdateErrorBookNoteSchema / ReviewErrorBookItemSchema / CollectFromSubmissionSchema
  • TypesErrorBookItem / ErrorBookItemDetail / ErrorBookReviewRecord / ErrorBookStats / StudentErrorBookSummary / KnowledgePointWeakness / SubjectErrorDistribution
  • ComponentsErrorBookStatsCards / ErrorBookFilters / ErrorBookItemCard / ReviewButtons / ErrorBookDetailDialog / ErrorBookList / AddErrorBookDialog / ClassErrorOverview / TopWrongQuestions

权限点

  • ERROR_BOOK_READerror_book:read查看错题本student 自己、parent 子女)
  • ERROR_BOOK_MANAGEerror_book:manage):管理错题(添加/复习/删除/归档student
  • ERROR_BOOK_ANALYTICS_READerror_book:analytics_read查看错题分析teacher/admin/grade_head/teaching_head

依赖关系

  • 依赖:shared/*db、auth-guard、types、utilsmodules/classesgetStudentIdsByClassIdsmodules/questionsgetQuestionsAction
  • 被依赖:app/(dashboard)/student/error-bookapp/(dashboard)/teacher/error-bookapp/(dashboard)/parent/error-bookapp/(dashboard)/admin/error-book

DataScope 行级权限

角色 DataScope 说明
student owned 仅查看/管理自己的错题
parent children 查看子女的错题统计
teacher class_taught 查看所教班级学生的错题分析
admin all 查看全校错题分析
grade_head grade_managed 查看所管年级学生的错题分析
teaching_head grade_managed 查看所管年级学生的错题分析

核心算法SM-2 间隔重复(简化版)

  • 4 级评级:again(重来)/ hard(困难)/ good(良好)/ easy(简单)
  • 初始间隔1/2/4/7 天
  • 间隔增长:again 重置为 1 天;hard ×1.2good ×1.5easy ×2
  • 掌握度0-5 级,again -1hard ±0good +1easy +2
  • 已掌握判定:掌握度 ≥5 或连续答对 ≥3 次
  • 复习时间:次日早上 9 点

自动采集机制

  • collectFromExamSubmission:从考试提交记录中筛选得分 < 满分的题目,去重后批量插入
  • collectFromHomeworkSubmission:从作业提交记录中筛选错题,去重后批量插入
  • 自动关联知识点(通过 questionsToKnowledgePoints 表)

文件清单

文件 行数 职责
actions.ts ~180 9 个 Server Actions全部使用 requirePermission() + ActionState<T>
data-access.ts ~960 16 个数据访问函数 + SM-2 算法实现 + 自动采集逻辑
schema.ts ~60 4 个 Zod 验证 schema
types.ts ~120 6 个类型定义 + 状态映射常量 + 错误标签常量
components/error-book-stats-cards.tsx ~80 5 个统计卡片(总数/待学习/学习中/已掌握/待复习)
components/error-book-filters.tsx ~100 筛选栏(搜索/状态/来源/待复习),使用 nuqs
components/error-book-item-card.tsx ~150 错题卡片(预览/标签/笔记/掌握度/操作)
components/review-buttons.tsx ~80 4 按钮复习面板again/hard/good/easy
components/error-book-detail-dialog.tsx ~250 详情对话框(题目/答案/复习/笔记/历史)
components/error-book-list.tsx ~60 网格列表
components/add-error-book-dialog.tsx ~180 手动添加对话框(题库选择 + 标签)
components/class-error-overview.tsx ~200 班级错题概览(教师/管理员视图)
components/top-wrong-questions.tsx ~80 高频错题列表Top 10

路由清单

路由 文件 说明
/student/error-book page.tsx + loading.tsx + error.tsx 学生错题本(统计/筛选/列表/手动添加/详情复习)
/teacher/error-book page.tsx + loading.tsx + error.tsx 教师错题分析(班级概览/薄弱知识点/学科分布/高频错题)
/parent/error-book page.tsx + loading.tsx + error.tsx 家长错题本(子女错题统计/薄弱知识点/高频错题)
/admin/error-book page.tsx + loading.tsx + error.tsx 管理员错题分析(全校错题统计/薄弱知识点/学科分布/高频错题)

数据库表

说明
errorBookItems 错题条目主表18 列/4 索引/2 外键studentId、questionId、sourceType、sourceId、studentAnswer、correctAnswer、subjectId、knowledgePointIds、status、masteryLevel、nextReviewAt、reviewInterval、reviewCount、correctStreak、note、errorTags、createdAt、updatedAt
errorBookReviews 复习记录表8 列/2 索引/2 外键itemId、studentId、result、reviewedAt、newInterval、newMasteryLevel

i18n

  • src/shared/i18n/messages/zh-CN/error-book.json
  • src/shared/i18n/messages/en/error-book.json

导航配置

  • 6 个角色admin/teacher/student/parent/grade_head/teaching_head均添加「错题分析」导航项
  • 图标:BookXlucide-react

第三部分:已知架构问题和技术债

3.1 P0 严重问题(必须立即修复)

P0-1文件超 1000 行硬上限3 个文件)

文件 行数 问题 拆分建议
classes/data-access.ts 2104 → 548 混入 homework/scheduling/grades 逻辑 已拆分 已拆为 5 个文件data-access.ts(548行) + data-access-stats.ts(531行) + data-access-schedule.ts(194行) + data-access-students.ts(244行) + data-access-admin.ts(406行),通过 re-export 保持向后兼容
homework/data-access.ts 1038 → 598 混入排名计算业务逻辑 已拆分 已拆为 data-access.ts(598行) + stats-service.ts(425行),统计函数迁移至 stats-service.ts
shared/db/schema.ts 1111 54 张表混合 按业务域拆分为 schema/auth.ts + schema/academic.ts + schema/exam.ts + ...,通过 index.ts 聚合

P0-2shared/lib ↔ auth 循环依赖 已修复

shared/lib/{audit-logger, change-logger, auth-guard} → @/auth → shared/lib/*

影响shared 层无法独立测试/复用;架构上基础设施不应反向依赖应用层。

修复方案(已实施):

  • 创建 shared/lib/session.ts 封装 session 获取(getSession()server-only
  • session.ts 内部使用 dynamic import("@/auth") 打破模块级静态循环
  • audit-logger.ts / change-logger.ts / auth-guard.ts 改为 import { getSession } from "@/shared/lib/session",不再直接依赖 @/auth
  • 运行时调用链保持不变,模块加载图无环

P0-3dashboard 跨模块直接查询 11 张表 已修复

dashboard/data-access.tsgetAdminDashboardData 原直查 sessions/users/usersToRoles/roles/classes/textbooks/chapters/questions/exams/homeworkAssignments/homeworkSubmissions。

修复方案(已实施):

  • 各模块新增 dashboard stats 函数:
    • users/data-access.tsgetUsersDashboardStats()userCount/activeSessionsCount/userRoleCounts/recentUsers
    • classes/data-access.tsgetClassesDashboardStats()classCount
    • textbooks/data-access.tsgetTextbooksDashboardStats()textbookCount/chapterCount
    • questions/data-access.tsgetQuestionsDashboardStats()questionCount
    • exams/data-access.tsgetExamsDashboardStats(scope?)examCount支持 scope 过滤)
    • homework/stats-service.tsgetHomeworkDashboardStats(scope?)4 个计数,支持 scope 过滤)
  • dashboard 改为并行调用:Promise.all([getUsersDashboardStats(), getClassesDashboardStats(), ...])
  • 返回值结构保持不变,调用方无需修改

P0-4messaging 绕过 notifications 直接写通知 已修复

messaging/actions.ts 原直接调用 createNotificationmessageNotifications 表,导致用户通知偏好失效、多渠道通知无效。notifications/data-access.tsin-app-channel.ts 反向依赖 messaging 模块。

修复方案(已实施):

  • messageNotifications 表的 CRUD 函数(createNotification / getNotifications / markNotificationAsRead / markAllNotificationsAsRead / getUnreadNotificationCount)从 messaging/data-access.ts 迁移到 notifications/data-access.ts
  • notificationPreferences 表的 CRUD 函数(getNotificationPreferences / upsertNotificationPreferences)从 messaging/notification-preferences.ts 迁移到新文件 notifications/preferences.ts
  • NotificationType / Notification / NotificationPreferences / UpdateNotificationPreferencesInput / CreateNotificationInput / GetNotificationsParams / PaginatedResult 类型从 messaging/types.ts 迁移到 notifications/types.ts
  • notifications/channels/in-app-channel.ts 改为静态导入本地 createNotification(不再动态 import messaging
  • notifications/dispatcher.ts 改为从 notifications/preferences.ts 导入 getNotificationPreferences(不再通过 getUserNotificationPreferences 包装器反向依赖 messaging
  • messaging 模块通过 re-export 保持向后兼容:messaging/data-access.ts re-export 通知 CRUDmessaging/notification-preferences.ts 转为 re-export shimmessaging/types.ts re-export 通知类型
  • 依赖方向变为单向messaging → notificationsmessaging 调用 sendNotification dispatcher 发送通知)

P0-5classSchedule 表三处写入口 已修复

  • classes/data-access.tscreateClassScheduleItem 等)
  • scheduling/actions.tsapplyAutoScheduleAction 直接 transaction 写入)
  • scheduling/data-access.ts(间接)

影响:数据完整性高风险。

修复方案(已实施):

  • 将 classSchedule 写函数(createClassScheduleItem / updateClassScheduleItem / deleteClassScheduleItem)从 classes/data-access-schedule.ts 迁移到新文件 scheduling/data-access-class-schedule.ts
  • classes 模块仅保留 READ 函数(getStudentSchedule / getClassSchedule),不再有任何 classSchedule 写入口
  • scheduling 模块通过 classes/data-access.verifyTeacherOwnsClass 跨模块校验教师班级归属(合理依赖)
  • classes/actions.ts 改为从 @/modules/scheduling/data-access-class-schedule 导入写函数
  • 类型 CreateClassScheduleItemInput / UpdateClassScheduleItemInputclasses/types.ts 迁移到 scheduling/types.ts
  • 所有 classSchedule DB 写入统一由 scheduling 模块管理(insertClassScheduleItem / updateClassScheduleItemById / deleteClassScheduleItemById / replaceClassSchedule

P0-6proctoring 死代码与重复实现

  • exam-mode-config.tsx 未集成到考试表单(监考功能无法启用)
  • 事件上报存在 Server Action 与 REST API 双通道重复 已修复

修复方案(已实施):

  • src/app/api/proctoring/event/route.ts 移至 deletes/api/proctoring/event/route.ts,消除事件上报的 REST API 重复通道
  • Server Action recordProctoringEventActionproctoring/actions.ts)为唯一规范路径
  • exam-mode-config.tsx 暂保留原位(集成到考试表单属于功能新增,不在本次修复范围)

P0-7proxy.ts 路由权限跨角色访问漏洞 已修复webapp-testing 发现)

问题proxy.ts/teacher/parent 路由前缀都使用 EXAM_READ 权限校验,但 studentparent 角色也拥有 EXAM_READ 权限,导致:

  • 学生可访问 /teacher/* 所有页面
  • 教师可访问 /parent/* 所有页面

修复方案(已实施):

  • /teacher 改为 Permissions.EXAM_CREATE(仅 teacher/admin 拥有)
  • /parent 改为 Permissions.DASHBOARD_PARENT_READ(仅 parent 拥有)
  • 新增 DASHBOARD_ROUTE_PERMISSIONS 细粒度仪表盘权限表,覆盖各角色 dashboard 路由
  • 跨角色访问测试验证teacher/student/parent 访问其他角色路由均被重定向回各自 dashboard

P0-8list-pagination.tsx 客户端/服务端边界错误 已修复webapp-testing 发现)

问题shared/components/ui/list-pagination.tsx 文件顶部声明 "use client",但导出的 computePagination()paginate() 纯工具函数被 4 个服务端组件页面直接调用:

  • teacher/attendance/page.tsx
  • teacher/homework/assignments/page.tsx
  • teacher/homework/submissions/page.tsx
  • teacher/grades/page.tsx

导致 Next.js 16 / React 19 抛出错误:Attempted to call computePagination() from the server but computePagination is on the client,页面渲染时触发 ErrorBoundary。

修复方案(已实施):

  • 移除 list-pagination.tsx"use client" 指令
  • ListPagination 组件仅使用 LinkButton 和图标,无需客户端交互,可作为服务端组件渲染
  • computePaginationpaginate 恢复为服务端可调用的纯函数

3.2 P1 较严重问题(短期执行)

P1-1跨模块直接 DB 查询普遍存在 已修复

被访问表 访问次数 应归属模块 主要违规者
classes 8+ classes exams/homework/grades/dashboard 已改为通过 classes data-access
classEnrollments 6+ classes homework/grades/attendance/users 已改为通过 classes data-access
users 6+ users 多个模块已改为通过 users data-access
subjects 6+ school exams/homework/questions/grades 已改为通过 school data-access
exams 5+ exams homework/grades/dashboard/classes 已改为通过 exams data-access
homeworkAssignments 5+ homework classes反向直查已改为通过 homework/data-access-classes

修复方案(已实施):

  • 各模块 data-access 暴露查询接口:
    • classes/data-accessgetClassGradeIdsByClassIds 已实现 / getClassStudentsByClassId / getActiveClassStudents / getClassExists / getClassNameById / getClassNamesByIds / getActiveStudentIdsByClassId / getStudentActiveClassId / getClassesByGradeId / verifyTeacherOwnsClass / getTeacherIdForMutations
    • exams/data-accessgetExamForHomeworkCreation 已实现(getExamIdsByGradeIds / getExamSubjectIdMap / getExamWithQuestionsForHomework / getExamSubmissionWithAnswers / getExamForProctoringCrossModule / getExamSubmissionForProctoringCrossModule / getExamSubmissionsForExam / getExamTitleById
    • school/data-accessgetSubjectOptions / getGradeOptions 已实现
    • users/data-accessgetUserNameByIds / getStudentInfo 已实现(getUserNamesByIds / getUserWithRole / getUserBasicInfo / getUserIdsByGradeId / getCurrentStudentUser
    • textbooks/data-accessgetKnowledgePointOptions 已实现
    • questions/data-accessinsertQuestionWithRelations 已通过 createQuestionWithRelations 供 exams 调用 / getKnowledgePointsForQuestions
    • homework/data-access-classes 新增 7 个函数供 classes 模块跨模块调用

P1-2actions 层混入数据访问逻辑 已修复

已完成修复2026-06-17commit 84d66364 个模块的 actions 层 DB 操作全部下沉到 data-access

模块 问题 Action 修复内容
exams updateExamAction / deleteExamAction / duplicateExamAction / getExamPreviewAction / getSubjectsAction / getGradesAction 新增 7 个 data-access 函数getExamCreatorId/updateExamWithQuestions/deleteExamById/duplicateExam/getExamPreview/getExamSubjects/getExamGradesactions.ts 831→691 行data-access.ts 374→471 行
homework createHomeworkAssignmentAction157 行)/ startHomeworkSubmissionAction / saveHomeworkAnswerAction / submitHomeworkAction / gradeHomeworkSubmissionAction 新建 data-access-write.ts285 行10 个写函数actions.ts 387→239 行
questions createQuestionAction / updateQuestionAction / deleteQuestionAction / getKnowledgePointOptionsAction 新增 4 个 data-access 函数createQuestionWithRelations/updateQuestionById/deleteQuestionByIdRecursive/getKnowledgePointOptionsactions.ts 294→149 行data-access.ts 138→260 行
announcements 所有写操作 Action 新增 5 个 data-access 函数insertAnnouncement/updateAnnouncementById/deleteAnnouncementById/publishAnnouncementById/archiveAnnouncementByIdactions.ts 242→197 行data-access.ts 120→171 行

剩余未修复模块(不在本次 P1-2 范围):

  • usersupdateUserProfileAction 直接 db.update 已下沉到 data-accessP1-1 修复)
  • schedulingapplyAutoScheduleAction / autoScheduleAction 直接 db.transaction + db.select 已改为调用 replaceClassSchedule 统一写入口;autoScheduleAction 直查 users 表已改为通过 users data-accessP0-5 / P1-1 修复)

P1-3auth.ts 混合 5 类职责 已完成

src/auth.ts 原 293 行混合NextAuth 配置 + 密码安全 DB 操作 + 角色规范化 + IP 解析 + 回调函数。

已完成拆分auth.ts 现 193 行,仅保留 NextAuth 配置):

  • 密码安全 DB 操作 → shared/lib/password-security-service.tsgetOrCreatePasswordSecurity / recordFailedLogin / resetFailedLoginserver-only
  • 角色规范化 → shared/lib/role-utils.tsnormalizeRole / resolvePrimaryRole纯函数
  • bcrypt 哈希规范化 → shared/lib/bcrypt-utils.tsnormalizeBcryptHash纯函数
  • IP 解析 → shared/lib/http-utils.tsresolveClientIpserver-only

后续可选优化(未执行,需保持 NextAuth 配置不变原则下评估):

  • authorize 回调可进一步拆分为 checkRateLimit / checkAccountLockout / verifyPassword / loadUserRoles,使 auth.ts 降至 ≤150 行

P1-4users/import-export.ts 四重职责 已完成

users/import-export.ts 原 291 行混合:导入解析 + 导出 + 用户创建(含密码哈希)+ 班级注册(跨模块写 classEnrollments

已完成拆分import-export.ts 现 157 行,仅保留文件解析/生成):

  • 用户创建(含密码哈希、角色分配)→ user-service.tsbatchImportUsers82 行server-only
  • 班级注册 → class-registration.tsregisterStudentByInvitationCode21 行server-only
  • batchImportUsers 不再直写 classEnrollments,改为调用 classes/data-access.enrollStudentByInvitationCode
  • import-export.ts 通过 re-export batchImportUsers / UserImportResult 保持向后兼容(actions.tsapp/api/export/route.ts 无需修改)

P1-5notifications 反向依赖 messaging 已完成

notifications/data-access.tsin-app-channel.ts 原反向依赖 messaging 模块的偏好和 createNotification。

已完成修复(与 P0-4 一并解决):

  • messageNotificationsnotificationPreferences 表所有权移交 notifications 模块
  • notifications/data-access.ts 不再 import messaging 模块
  • notifications/channels/in-app-channel.ts 改为静态导入本地 createNotification(不再动态 import messaging
  • notifications/dispatcher.ts 改为从本地 preferences.ts 导入偏好函数
  • messaging 模块通过 re-export 保持向后兼容

P1-6三个 logger 重复实现 IP/Header 提取 已修复

audit-logger.ts / change-logger.ts / login-logger.ts / auth.ts 四处重复实现 IP/User-Agent 提取逻辑,且实现略有差异。

修复方案(已实施):

  • shared/lib/http-utils.ts 新增 getUserAgent() 函数(与已有 resolveClientIp() 配套)
  • audit-logger.ts / change-logger.ts / login-logger.ts 改为从 @/shared/lib/http-utils 导入 resolveClientIpgetUserAgent,删除本地重复实现
  • auth.ts 已在 P1-3 中改用 resolveClientIp
  • 四处实现统一,消除不一致风险(resolveClientIpx-forwarded-for 第一段,更准确)

3.3 P2 代码质量问题(机会修复)

序号 问题 模块
P2-1 exams/ai-pipeline.ts 857 行,混合 4 类职责 exams
P2-2 exams/actions.ts 832 行(超 800 建议) 已修复P1-2 后降至 691 行) exams
P2-3 shared/lib/ai.ts 218 行,混合 5 类职责 已修复P2-2 已拆分为 ai/ 目录) shared
P2-4 onboarding-gate.tsx 业务逻辑泄漏到 shared 已修复(迁移至 modules/onboarding/,改用独立路由 + Server Action + middleware 重定向) shared/onboarding
P2-5 global-search.tsx 业务类型硬编码在 shared shared
P2-6 proxy.ts 硬编码权限字符串,未复用 Permissions 常量 已修复(改用 Permissions 常量) proxy
P2-7 useA11yId Hook 错放在 lib/ 而非 hooks/ 已修复(文件已不存在;use-aria-live.ts 已在 hooks/ 目录) shared
P2-8 schema.ts 分节编号混乱section 12 出现在 14b 之后) 已修复(重新编号为连续 1-24 shared/db
P2-9 audit/actions.ts Excel 导出逻辑内联 audit
P2-10 school 模块审计日志不一致(仅 school 实体记录) school
P2-11 announcements 死代码 void wasPublished 已修复(代码中已不存在) announcements
P2-12 announcements 权限模式不一致requireAuth vs requirePermission 已修复 announcements
P2-13 files try-catch 吞错误 已修复(所有 catch 块已添加 console.errorconditions 隐式 any[] 改为 SQL[] files
P2-14 elective runLottery 使用 Math.random 已修复(改为 Fisher-Yates 无偏洗牌) elective
P2-15 elective selectCourse FCFS 并发超卖风险 已修复db.transaction + FOR UPDATE 行锁) elective
P2-16 diagnostic 班级报告 studentId 字段复用 diagnostic
P2-17 layout 用权限反推角色 已修复(app-sidebar.tsx 改用 hasRole() 判断角色N3 新增多角色切换机制:SidebarContext.currentRole/setCurrentRole + 侧边栏底部 Select 下拉) layout
P2-18 scheduling/actions.ts 末尾 re-export data-access 已修复(移除 re-export4 个页面改为从 data-access 导入) scheduling
P2-19 ExamAssembly / ExamPreviewQuestionEditor 10 个 props exams
P2-20 homework/data-access.getDemoStudentUser 使用 auth() 而非 auth-guard 已修复(已迁移至 users/data-access.getCurrentStudentUser6 个 student 页面改用 users 模块;elective 页面改用 getAuthContext()homework 保留 re-export 向后兼容) homework

3.4 解耦优先级路线图

立即执行P0

  1. 拆分 classes/data-access.ts2104 行 → 按职责拆 3-4 个文件) 已完成(拆为 5 个文件data-access.ts 548行 + data-access-stats.ts 531行 + data-access-schedule.ts 194行 + data-access-students.ts 244行 + data-access-admin.ts 406行
  2. 拆分 homework/data-access.ts1038 行 → 分离排名逻辑) 已完成(拆为 data-access.ts 598行 + stats-service.ts 425行
  3. 修复 shared/libauth 循环依赖 已完成(新增 shared/lib/session.ts 单一入口3 个 shared/lib 文件改为通过 getSession 获取 sessiondynamic import 打破静态循环)
  4. dashboard 改为通过各模块 data-access 获取数据 已完成P0-3 修复:并行调用各模块 dashboard stats 函数)
  5. messaging 写通知改为通过 notifications dispatcher 已完成P0-4 / P1-5 修复:通知 CRUD 和偏好迁移至 notifications 模块messaging 通过 dispatcher 发送通知)
  6. 统一 classSchedule 写入口到 scheduling 模块 已完成P0-5 修复classSchedule 写函数从 classes/data-access-schedule.ts 迁移至 scheduling/data-access-class-schedule.tsclasses 模块仅保留读函数)
  7. 集成 proctoring/exam-mode-config 到考试表单 部分完成P0-6 修复:删除重复的 /api/proctoring/event REST 路由Server Action 为唯一规范路径exam-mode-config.tsx 集成属于功能新增,暂保留原位)

短期执行P1

  1. actions 层移除直接 DB 操作exams/homework/questions/announcements/users/scheduling 已完成(全部 6 个模块均已修复)
  2. 拆分 auth.ts 已完成4 个辅助函数组迁移至 shared/libauth.ts 保留 NextAuth 配置)
  3. 拆分 users/import-export.ts 已完成(拆为 import-export.ts 157行 + user-service.ts 82行 + class-registration.ts 21行班级注册改为调用 classes/data-access
  4. 消除 notifications → messaging 反向依赖 已完成P0-4 / P1-5 修复:通知表所有权迁移至 notificationsin-app-channel 改为静态导入)
  5. 提取 shared/lib/http-utils.ts 统一 IP 提取 已完成(新增 getUserAgent,三个 logger 统一复用 resolveClientIp / getUserAgent
  6. 各模块暴露跨模块查询接口(见 P1-1 已完成(所有跨模块直查已改为通过对方 data-access 接口)

中期执行P2

  1. 建立模块间数据访问规范(通过对方 data-access 或导出查询函数) 已完成P1-1 修复)
  2. schema.ts 按业务域分节 已完成P2-8 修复:重新编号为连续 1-24消除 8b/14b/乱序问题)
  3. 拆分 exams/ai-pipeline.ts
  4. 拆分 shared/lib/ai.ts 已完成P2-2commit 6588f74拆分为 ai/ 目录 6 个文件,原 ai.ts 保留为重导出)
  5. shared 层业务逻辑下沉到 modules 层
  6. 代码质量问题逐项修复( 大部分已修复React.cache 包装、Promise.all 并行化、错误吞没清理、非空断言清理、函数返回类型补齐、重复代码提取、合并 filter 遍历、Set/Map 优化、P2-6 proxy.ts 权限常量复用、P2-11 announcements 死代码清理、P2-17 layout 角色判断、P2-18 scheduling re-export 移除)

3.5 标杆实践(建议推广)

实践 模块 说明
算法纯函数化 scheduling/auto-scheduler.ts 无 DB 依赖,可独立测试,应作为算法抽取模板
stats 文件拆分 attendance/data-access-stats.ts 统计逻辑独立成文件classes 应效仿
data-access 多文件拆分 grades/data-access*.ts 按职责拆分为 3 个文件CRUD/分析/排名)
actions 辅助函数 course-plans/actions.ts handleError / revalidatePlanPaths 消除重复
actions 编排模式 textbooks/actions.ts 权限校验 → 调用 data-access → revalidatePath标杆
DataScope 接入 attendance/actions.ts 6 种数据范围完整支持
权限统一接入 school / attendance / course-plans 全部 Action 使用 requirePermission
跨模块解耦 grades 通过外键引用 exams/homework不直接访问其表
渠道抽象 notifications/channels/ 接口 + 工厂 + Mock 实现

附录 A模块间依赖矩阵

行表示使用方,列表示被使用方。 合理依赖, 违规直查, 循环依赖。 P1-1 已修复:所有跨模块直查已改为通过对方 data-access 接口。

↓ 使用 → shared auth exams homework questions textbooks classes school dashboard users grades messaging notifications lesson-prep error-book 其他
shared - 已修复 - - - - - - - - - - - - - -
auth(root) db/lib - - - - - - - - - - - - - - -
exams - - data-access - data-access data-access - - - - - - - -
homework data-access - 关系 - data-access data-access - data-access - - - - - -
questions - - - data-access - - - - - - - - - -
textbooks - - UI - - - - - - - - - - -
classes data-access data-access - - - data-access - - - - - - - schedulingP0-5data-access-class-schedule 写函数)
school - - - - - - - ⚠️可接受 - - - - - -
grades 外键 外键 - - data-access data-access - data-access - - - - - -
dashboard data-access data-access data-access data-access data-access - - data-access - - - - - -
users - - - - data-access - - - - - - - - -
messaging - - - - data-access - - - - - dispatcher - - -
notifications - - - - data-access - - - - - - - - -
attendance - - - - data-access - - - - - - - - -
scheduling - - - - data-access - - data-access - - - - - -
proctoring data-access - - - - - - data-access - - - - - -
diagnostic data-access - data-access - data-access - - data-access - - - - - -
parent - data-access - - data-access data-access - data-access data-access - - - - -
elective - - - - data-access data-access - data-access - - - - - -
course-plans - - - - - - - - - - -
audit - - - - - - - - - - - - - -
announcements - - - - - - - - - - - - -
files - - - - - - - - - - - - - -
settings - - - - - - - - - - - - -
layout - - - - - - - - - - - - -
lesson-preparation data-access data-access data-access data-access data-access - - - - - - - - files/ai
error-book - - actions - data-access - - - - - - - - -

附录 B关键参数影响链

userId

  1. auth.ts JWT callback 从 users 表查询产生,存入 JWT
  2. 通过 session.user.id 传递到所有 Server/Client Components
  3. 通过 getAuthContext().userId 传递到所有 Server Actions
  4. auth-guard.ts 中用于查询 usersToRoles(角色)和 classSubjectTeachers/gradesDataScope
  5. exams/actions.ts 中作为 creatorId 写入 exams
  6. homework/actions.ts 中作为 creatorId 写入 homeworkAssignments
  7. classes/data-access.ts 中查询 getTeacherClasses(teacherId) / getGradeManagedClasses(userId)
  8. elective/actions.ts 中作为 teacherId/studentId 用于选课过滤

examId

  1. exams/actions.createExamAction 产生CUID2写入 exams
  2. exams/data-access.getExamById(id) 读取
  3. exams/actionsupdateExamAction/deleteExamAction/duplicateExamAction 用于定位考试
  4. 传入 homework/actions.createHomeworkAssignmentActionsourceExamId 参数
  5. homeworkAssignments 表中作为外键关联到源考试
  6. homework/data-access.getHomeworkAssignmentAnalytics 用于追溯作业来源

classId

  1. classes/actionscreateTeacherClassAction/createAdminClassAction 产生
  2. classes/data-access.getClassStudents(classId) 读取学生列表
  3. classes/data-access.getClassSchedule(classId) 读取课表
  4. classes/data-access.getClassHomeworkInsights(classId) 读取作业洞察( P0-7 已修复:通过 homework/data-access-classes 获取数据)
  5. homework/data-access.getHomeworkAssignments({ classId }) 过滤作业列表
  6. auth-guard.ts 中通过 classSubjectTeachers 查询教师关联的 classIds构建 DataScope.class_taught

permission

  1. shared/types/permissions.tsPermissions 常量定义61 个权限点)
  2. shared/lib/permissions.ts 中通过 ROLE_PERMISSIONS 映射角色到权限列表
  3. auth.ts JWT callback 中通过 resolvePermissions(roleNames) 合并多角色权限,存入 JWT
  4. proxy.ts middleware 中通过 token.permissions 检查路由访问权限
  5. shared/lib/auth-guard.ts 中通过 requirePermission(permission) 在 Server Action 层断言权限
  6. shared/hooks/use-permission.ts 中通过 hasPermission(permission) 在客户端组件中条件渲染
  7. layout/config/navigation.ts 中作为 NavItem.permission 字段过滤侧边栏菜单

DataScope

  1. auth-guard.tsresolveDataScope(userId, roles) 根据用户角色和 DB 关系动态计算
  2. 支持类型:all / grade_managed / class_taught / class_members / children / owned
  3. 传递到 exams/homework/grades/attendance/elective/dashboard 的 data-access 进行行级过滤
  4. 对 parent 角色,查询 parentStudentRelations 表构建 { type: "children", childrenIds: string[] }
  5. parent/children/[studentId]/page.tsx 中通过 ctx.dataScope.childrenIds.includes(studentId) 二次校验

附录 C核心函数签名索引

完整函数签名见 005_architecture_data.json。本附录仅列出关键函数。

shared 层核心函数

// shared/lib/auth-guard.ts
getAuthContext(): Promise<AuthContext>
requirePermission(permission: Permission): Promise<AuthContext>
requireAuth(): Promise<AuthContext>
checkPermission(permission: Permission): Promise<{ allowed: boolean; ctx: AuthContext }>
resolveDataScope(userId: string, roleNames: string[]): Promise<DataScope>

// shared/lib/permissions.ts
resolvePermissions(roleNames: string[]): Permission[]

// shared/lib/audit-logger.ts
logAudit(params: LogAuditParams): Promise<void>

// shared/lib/login-logger.ts
logLoginEvent(params: LogLoginEventParams): Promise<void>

// shared/lib/change-logger.ts
logDataChange(params: LogDataChangeParams): Promise<void>

// shared/lib/session.ts (P0-2 新增session 获取单一入口)
getSession(): Promise<AppSession>

// shared/lib/http-utils.ts (P1-6 完成,统一 IP/UA 提取)
resolveClientIp(): Promise<string>
getUserAgent(): Promise<string>

// shared/lib/ai/ (P2-2 已拆分,原 ai.ts 保留为重导出)
// ai/client.ts
createAiChatCompletion(input: AiChatRequest): Promise<{ content, usage }>
// ai/payload-parser.ts
parseAiChatPayload(body: unknown): AiChatRequest
// ai/api-key-crypto.ts
encryptAiApiKey(value: string): string
decryptAiApiKey(value: string): string

// shared/lib/password-policy.ts
validatePassword(password: string): { valid: boolean; errors: string[] }
getPasswordStrength(password: string): "weak" | "medium" | "strong"
isAccountLocked(failedAttempts: number, lastFailedAt: Date | null): boolean

// shared/lib/rate-limit.ts
rateLimit(params: { key: string; limit: number; windowMs: number }): RateLimitResult
rateLimitKey(prefix: string, identifier: string): string
rateLimitHeaders(result: RateLimitResult): Record<string, string>

// shared/lib/excel.ts
exportToExcel(params: { sheets: ExcelSheet[] }): Promise<Buffer>
parseExcel(buffer: Buffer): Promise<ParsedSheet[]>
generateTemplate(params: { sheets: TemplateSheet[] }): Promise<Buffer>

// shared/lib/file-storage.ts
isAllowedMimeType(mimeType: string): boolean
generateStoragePath(originalName: string): string
formatFileSize(bytes: number): string

// shared/lib/utils.ts
cn(...inputs: ClassValue[]): string
formatDate(date: string | Date, locale?: string): string
getSearchParam(params: SearchParams, key: string): string | undefined
formatNumber(v: number | null | undefined, digits?: number): string

// shared/lib/search-params.ts (re-export from utils.ts)
getParam(params: SearchParams, key: string): string | undefined  // = getSearchParam

业务模块核心 Actions

// exams/actions.ts
createExamAction(prevState: ActionState, formData: FormData): Promise<ActionState<Exam>>
createAiExamAction(prevState: ActionState, formData: FormData): Promise<ActionState<{ examId: string }>>
updateExamAction(prevState: ActionState, formData: FormData): Promise<ActionState>
deleteExamAction(prevState: ActionState, formData: FormData): Promise<ActionState>
duplicateExamAction(prevState: ActionState, formData: FormData): Promise<ActionState<{ examId: string }>>

// homework/actions.ts
createHomeworkAssignmentAction(prevState: ActionState, formData: FormData): Promise<ActionState>
startHomeworkSubmissionAction(prevState: ActionState, formData: FormData): Promise<ActionState<{ submissionId: string }>>
saveHomeworkAnswerAction(prevState: ActionState, formData: FormData): Promise<ActionState>
submitHomeworkAction(prevState: ActionState, formData: FormData): Promise<ActionState>
gradeHomeworkSubmissionAction(prevState: ActionState, formData: FormData): Promise<ActionState>

// classes/actions.ts
createTeacherClassAction(prevState: ActionState, formData: FormData): Promise<ActionState>
createAdminClassAction(prevState: ActionState, formData: FormData): Promise<ActionState>
createGradeClassAction(prevState: ActionState, formData: FormData): Promise<ActionState>
// + update/delete 各 3 个,共 9 个

// grades/actions.ts
getGradeRecordsAction(params: GetGradeRecordsParams): Promise<ActionState<PaginatedGradeRecords>>
createGradeRecordAction(prevState: ActionState, formData: FormData): Promise<ActionState>
exportGradesAction(params: ExportGradesParams): Promise<ActionState<Buffer>>

// scheduling/actions.ts
autoScheduleAction(prevState: ActionState, formData: FormData): Promise<ActionState>
applyAutoScheduleAction(prevState: ActionState, formData: FormData): Promise<ActionState>

业务模块核心 Data-access

// exams/data-access.ts
getExams(params: GetExamsParams & { scope: DataScope }): Promise<{ items: Exam[]; total: number }>
getExamById(id: string, scope: DataScope): Promise<Exam | null>
persistExamDraft(input: ExamDraftInput): Promise<{ examId: string }>
persistAiGeneratedExamDraft(input: AiExamDraftInput): Promise<{ examId: string }>

// homework/data-access.ts
getHomeworkAssignments(params: GetHomeworkAssignmentsParams & { scope: DataScope }): Promise<{ items, total }>
getStudentHomeworkAssignments(studentId: string): Promise<StudentHomeworkAssignment[]>
getStudentDashboardGrades(studentId: string): Promise<StudentDashboardGrades>
getHomeworkAssignmentAnalytics(assignmentId: string): Promise<HomeworkAnalytics>

// classes/data-access.ts
getAdminClasses(scope: DataScope): Promise<Class[]>
getTeacherClasses(teacherId: string): Promise<Class[]>
getStudentClasses(studentId: string): Promise<Class[]>
getClassStudents(classId: string): Promise<Student[]>
getClassHomeworkInsights(classId: string): Promise<ClassHomeworkInsights>  // ✅ P0-7 已修复:通过 homework/data-access-classes 获取数据

// grades/data-access.ts
getGradeRecords(params: GetGradeRecordsParams & { scope: DataScope; currentUserId?: string; limit?: number; offset?: number }): Promise<PaginatedGradeRecords>
getStudentGradeSummary(studentId: string, scope?: DataScope): Promise<StudentGradeSummary | null>
getClassRanking(classId: string, subjectId?: string, examId?: string, scope?: DataScope, currentUserId?: string): Promise<ClassRankingItem[]>

// scheduling/auto-scheduler.ts纯函数标杆
findOptimalSlot(input: FindOptimalSlotInput): ScheduleSlot | null
validateSchedule(schedule: ScheduleItem[]): ValidationResult
autoSchedule(input: AutoScheduleInput): AutoScheduleResult
buildDefaultTimeSlots(): TimeSlot[]

文档维护说明

  • 修改源码后:同步更新本文档对应模块章节 + 005_architecture_data.json
  • 新增模块:在第二部分添加模块清单 + 更新 1.1 分层架构图 + 更新附录 A 依赖矩阵
  • 新增/删除导出函数:更新对应模块的"导出函数"清单 + 附录 C
  • 修改依赖关系:更新 1.2 模块依赖关系图 + 附录 A 依赖矩阵
  • 新增路由:更新 005_architecture_data.jsonroutes 节点
  • 新增数据库表:更新 shared/db/schema.ts 分节 + 005dbTables 节点

完整路由表、DevOps 脚本、E2E 测试等信息见 005_architecture_data.json