P1-1: 抽取 stats-service.ts,将 8 个统计计算纯函数从 data-access 层分离 P1-5: 创建 WidgetBoundary 组件 + 补齐 teacher 路由 loading.tsx/error.tsx (14 文件) P1-6: 同步架构图文档 004/005,新增 stats-service 与 widget-boundary 节点 P2-1: 补充 a11y ARIA 属性(5 图表 role=img + aria-label,2 表格 caption,3 列表 role=list,3 按钮 aria-label) P2-3: 修复班级报告 studentId 字段语义错误(schema 改为可空 + 迁移 + 代码适配) P2-4: 修复 grade_managed scope 返回空数据(改为子查询 classes 表按 gradeId 过滤) P2-5: 新增 /parent/diagnostic/ 页面(多子女学情诊断聚合 + loading + error) P2-6: 统一 SearchParams 工具(student/grades 和 management/grade/insights 改用 @/shared/lib/search-params)
180 KiB
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,不直接访问 DBmodules/之间通过对方 data-access 通信,不直接查询对方表shared/是被依赖方,不应反向依赖app/或modules/src/auth.ts和src/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 |
通用分页 UI(Showing 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-CN,weekday=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
已知问题:
- ✅ P0:
已修复(新增shared/lib/*↔@/auth循环依赖shared/lib/session.ts封装 session 获取,3 个文件改为import { getSession } from "@/shared/lib/session") - ⚠️ P1:
schema.ts1111 行(54 张表混合,超 1000 硬上限) - ✅ P1:
已拆分(4 个辅助函数组迁移至auth.ts293 行混合 5 类职责shared/lib/{role-utils,bcrypt-utils,http-utils,password-security-service},auth.ts 仅保留 NextAuth 配置) - ✅ P2-2 已修复:
已拆分为ai.ts218 行混合 5 类职责ai/目录(payload-parser.ts/api-key-crypto.ts/provider-config.ts/client.ts/errors.ts/index.ts),原ai.ts保留为向后兼容的重导出文件(9 行) - ✅ P2-4 已修复:
已迁移至onboarding-gate.tsx业务逻辑泄漏到 sharedmodules/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.x(without 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 获取单一入口(getSession,server-only,dynamic 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 获取 session,http-utils 获取 IP/UA) |
lib/change-logger.ts |
- | 数据变更日志(通过 session.ts 获取 session,http-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 / getUserAgent,server-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 + FilterResetButton(P3-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 辅助出题。
导出函数:
- Actions:
createExamAction/createAiExamAction/previewAiExamAction/regenerateAiQuestionAction/updateExamAction/deleteExamAction/duplicateExamAction/getExamPreviewAction/getSubjectsAction/getGradesAction(✅ P1-2 已修复:actions 层不再直接访问 DB,全部下沉到 data-access) - Data-access:
getExams/getExamById/persistExamDraft/persistAiGeneratedExamDraft/buildExamDescription/resolveSubjectGradeNames/getExamCreatorId/updateExamWithQuestions/deleteExamById/duplicateExam/getExamPreview/getExamSubjects/getExamGrades(后 7 个为 P1-2 新增) - AI Pipeline:
generateAiCreateDraftFromSource/generateAiPreviewData/regenerateAiQuestionByInstruction - Utils:
normalizeStructure(v3 新增:将持久化的exam.structureunknown JSON 运行时校验并归一化为类型安全的ExamNode[],类型守卫模式无as断言,从teacher/exams/[id]/build/page.tsx提取)
依赖关系:
- 依赖:
shared/*、@/auth、questions(✅ P0-1 已修复:通过 data-access.createQuestionWithRelations)、classes(✅ P0-2 已修复:通过 data-access.getClassGradeIdsByClassIds)、school(✅ P1-1 已修复:通过 school data-access.getSubjectOptions/getGradeOptions) - 被依赖:
homework(通过 sourceExamId 外键,合理)、dashboard(通过 data-access,P0-4 已修复)、proctoring(✅ P1-1 已修复:通过 exams data-access)、diagnostic(✅ 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 已修复:
DB 操作已下沉到 data-access,actions.ts 现 691 行actions.ts832 行(超 800 建议),多处直接 DB 操作 - ⚠️ P1:
ai-pipeline.ts857 行(超 800 建议),混合 4 类职责 - ✅ P2 已修复:
ai-pipeline.ts中 3 处非空断言清理(draft.sections!.forEach→ 安全守卫、aiParsed.sections!.flatMap→?? []、aiParsed.sections!.map→?? [])
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
actions.ts |
691 | 10 个 Server Action(P1-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(作业模块)
职责:作业全生命周期(创建/发布/作答/批改/分析)。
导出函数:
- Actions:
createHomeworkAssignmentAction/startHomeworkSubmissionAction/saveHomeworkAnswerAction/submitHomeworkAction/gradeHomeworkSubmissionAction(✅ P1-2 已修复:actions 层不再直接访问 DB,全部下沉到 data-access/data-access-write) - Data-access:
getHomeworkAssignments/getHomeworkAssignmentById/getHomeworkSubmissions/getStudentHomeworkAssignments/getStudentHomeworkTakeData/getHomeworkAssignmentReviewList/getHomeworkSubmissionDetails/getDemoStudentUser(已迁移至 users 模块getCurrentStudentUser,此处为 re-export 向后兼容)/isRecord/toQuestionContent/getAssignmentMaxScoreById(后三者供 stats-service 使用) - Data-access-classes:
getAssignmentIdsForStudents/getHomeworkAssignmentsWithSubject/getHomeworkAssignmentsByIds/getAssignmentTargetCounts/getHomeworkSubmissionsForStudents/getPublishedHomeworkAssignmentsWithSubject/getHomeworkSubmissionsForAssignments(P0-7 新增,供 classes 模块跨模块调用,封装 homework/exams 表查询) - Data-access-write:10 个写操作函数(P1-2 新增,从 actions 下沉)
- Stats-service:
getTeacherGradeTrends/getHomeworkAssignmentAnalytics/getStudentDashboardGrades(从 data-access.ts re-export 以保持向后兼容)
依赖关系:
- 依赖:
shared/*、@/auth、exams(✅ P1-1 已修复:通过 exams data-access.getExamIdsByGradeIds/getExamSubjectIdMap/getExamWithQuestionsForHomework)、classes(✅ P1-1 已修复:通过 classes data-access.getStudentIdsByClassId 等 7 个函数)、school(✅ P1-1 已修复:通过 school data-access.getSubjectOptions)、users(✅ 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 已修复:
DB 操作已下沉到actions.ts多处直接 DB 操作(createHomeworkAssignmentAction157 行)data-access-write.ts,actions.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 Action(P1-2 已修复,无直接 DB 操作) |
types.ts |
186 | 类型定义 |
schema.ts |
29 | Zod 校验 |
2.4 questions(题库模块)
职责:题库管理(题目 CRUD、知识点关联、题型支持)。
导出函数:
- Actions:
getQuestionsAction/createQuestionAction/updateQuestionAction/deleteQuestionAction/getKnowledgePointOptionsAction(✅ P1-2 已修复:actions 层不再直接访问 DB,全部下沉到 data-access) - Data-access:
getQuestions/createQuestionWithRelations/updateQuestionById/deleteQuestionByIdRecursive/getKnowledgePointOptions(后 4 个为 P1-2 新增/迁移)
依赖关系:
- 依赖:
shared/*、@/auth、textbooks(✅ P1-1 已修复:通过 textbooks data-access.getKnowledgePointOptions) - 被依赖:
exams(通过类型导入,合理)、textbooks(UI 组合,合理)
已知问题:
- ✅ P1-2 已修复:
写操作函数错放在已下沉到 data-access(actions.ts(insertQuestionWithRelations/deleteQuestionRecursive)createQuestionWithRelations/updateQuestionById/deleteQuestionByIdRecursive/getKnowledgePointOptions) - ✅ P1-1 已修复:
改为调用getKnowledgePointOptionsAction直查 textbooks 模块表textbooks/data-access.getKnowledgePointOptions - ✅ P2 已解决:
P1-2 后 data-access.ts 扩充至 260 行data-access.ts仅 129 行,写操作缺失
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
actions.ts |
149 | 5 个 Server Action(P1-2 已修复,无直接 DB 操作) |
data-access.ts |
260 | 题目 CRUD + 知识点选项(含 P1-2 新增 4 个写/查询函数) |
schema.ts |
18 | Zod 校验 |
types.ts |
34 | 类型定义 |
2.5 textbooks(教材模块)— 标杆模块(data-access 层)
职责:教材与知识体系管理(教材/章节树形结构、知识点 CRUD、Markdown 内容编辑、知识图谱)。
导出函数:
- Actions(10 个,均为写操作;读操作由 RSC 页面直接调用 data-access):
createTextbookAction/updateTextbookAction/deleteTextbookAction/createChapterAction/updateChapterContentAction/deleteChapterAction/reorderChaptersAction/createKnowledgePointAction/updateKnowledgePointAction/deleteKnowledgePointAction - Data-access:
getTextbooks/getTextbookById/getChaptersByTextbookId/getKnowledgePointsByChapterId/getKnowledgePointsByTextbookId/createTextbook/updateTextbook/deleteTextbook/createChapter/updateChapterContent/deleteChapter/createKnowledgePoint/updateKnowledgePoint/deleteKnowledgePoint/reorderChapters/getTextbooksDashboardStats/getKnowledgePointOptions(跨模块接口,供 questions 调用)
依赖关系:
- 依赖:
shared/*、@/auth - 被依赖:
questions(✅ P1-1 已修复:通过 textbooks data-access)、exams(通过类型)、dashboard(通过 data-access,P0-4 已修复) - ⚠️ UI 层跨模块依赖:
textbooks/components/knowledge-point-dialogs.tsx直接 importquestions/components/create-question-dialog(P0 待解耦,详见 textbooks-audit-report.md)
已知问题:
- ✅ 无跨模块 DB 访问(data-access 层)
- ✅ actions 层编排模式标杆(权限校验 → 调用 data-access → revalidatePath)
- ✅ data-access 层职责单一
- ✅ P2 已修复:
data-access.ts中byId.get(pid)!.children.push非空断言清理为安全守卫;or(...)!非空断言清理为条件 push - ⚠️ P0 跨模块 UI 依赖:
knowledge-point-dialogs.tsx直接 import questions 模块组件 - ⚠️ P0 前端权限硬编码:
canEdit={true}按路由写死,未用usePermission().hasPermission() - ⚠️ P0 全模块零 i18n:中英文文案硬编码,未接入 next-intl
- ⚠️ P1 Server Action 未校验资源归属(chapterId 是否属于 textbookId)
- ⚠️ P1 data-access 缺数据范围过滤(学生端未按年级过滤)
- ⚠️ P1 缺 Error Boundary(无 error.tsx)
- ⚠️ P1 知识点列表/弹窗存在重复实现(knowledge-point-panel.tsx 无调用方)
- ⚠️ P1 学科/年级选项硬编码三处且彼此不一致
- ⚠️ P1 纯逻辑未导出,零单测
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
actions.ts |
317 | 10 个 Server Action(写操作) |
data-access.ts |
514 | 教材/章节/知识点 CRUD + 跨模块查询接口 |
types.ts |
45 | 类型定义 |
schema.ts |
64 | Zod 校验 |
hooks/use-knowledge-point-actions.ts |
121 | 知识点操作 Hook |
hooks/use-text-selection.ts |
57 | 文本选区捕获 Hook |
components/* |
11 文件 | 教材编辑/知识图谱组件 |
2.6 grades(成绩模块)— 标杆模块(拆分范例)
职责:成绩分析(录入/查询/统计/导出/趋势对比分析)。
导出函数:
- Actions:
getGradeRecordsAction/createGradeRecordAction/updateGradeRecordAction/deleteGradeRecordAction/exportGradesAction/getGradeTrendAction/getClassComparisonAction/getSubjectComparisonAction/getGradeDistributionAction/getClassRankingAction/getRankingTrendAction/getGradeRecordByIdAction/getClassGradeStatsAction/getStudentGradeSummaryAction/batchCreateGradeRecordsAction - Data-access:
getGradeRecords/getStudentGradeSummary/getClassRanking/getClassStudentsForEntry/getClassGradeStats/getClassGradeStatsWithMeta/getGradeTrend/getClassComparison/getSubjectComparison/getGradeDistribution/getRankingTrend - Lib(✅ P1-2 新增):
toNumber/normalize/buildScopeClassFilter(从 3 个 data-access 文件抽取的公共工具函数) - Stats-service(✅ P1-1 新增):
computeGradeStats/computeAverageScore/buildGradeTrendPoints/computeTrendAverage/computeClassComparisonStats/computeSubjectComparisonStats/computeGradeDistribution/buildRankingTrendPoints(从 3 个 data-access 文件抽取的纯函数,使数据层专注 DB I/O,统计逻辑可独立测试) - Components(✅ P1-5 新增):
WidgetBoundary(Error Boundary + Suspense + Skeleton 组合,含 a11y 属性)
依赖关系:
- 依赖:
shared/*、@/auth、classes(✅ P1-1 已修复:通过 classes data-access.getClassExists/getClassNameById/getClassNamesByIds/getActiveStudentIdsByClassId/getStudentActiveClassId/getClassesByGradeId)、school(✅ P1-1 已修复:通过 school data-access.getSubjectOptions/getGradeOptions)、users(✅ P1-1 已修复:通过 users data-access.getUserNamesByIds) - 被依赖:
parent(通过 data-access,合理)、dashboard
已知问题:
- ✅ P1-1 已修复:
多处直查改为调用对应模块 data-access 函数(classes/school/users)classes/classEnrollments/subjects/users表 - ✅ P1-1 已修复:
统计计算业务逻辑混入 data-access(抽取为纯函数到getClassGradeStats/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 个查询 schema(DeleteGradeRecordSchema/GetGradeRecordByIdSchema/GradeQuerySchema/ClassGradeStatsQuerySchema/StudentGradeSummaryQuerySchema/ClassRankingQuerySchema/ExportGradesSchema/GradeTrendQuerySchema/ClassComparisonQuerySchema/SubjectComparisonQuerySchema/GradeDistributionQuerySchema/RankingTrendQuerySchema),所有 Action 使用 safeParse 校验 - ✅ P1-4 已修复:
改用类型守卫函数(isGradeType/isSemester/isDistributionTooltipPayload)batch-grade-entry.tsx/grade-record-form.tsx/grade-distribution-chart.tsx中存在as断言 - ✅ P1-5 已修复:
teacher/grades 与 teacher/diagnostic 路由缺少 loading.tsx/error.tsx已为 7 个路由补齐 loading.tsx + error.tsx,并新增WidgetBoundary通用组件 - ✅ P2-1 已修复:
图表/表格/列表缺少 a11y ARIA 属性为 4 个成绩图表添加role="img"+aria-label,2 个表格添加<caption>,3 个图标按钮添加aria-label - ✅ P2-2 已修复:
diagnostic 组件中存在 Tailwind 任意值改用标准 Tailwind 类 - ✅ P2-4 已修复:~~
buildScopeClassFilter中grade_managedscope 返回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.ts中scoreMap.get(r.studentId)!非空断言清理为安全守卫(if (!subjMap) continue)
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
actions.ts |
359 | 7 个 Server Action(含 Zod 校验) |
actions-analytics.ts |
175 | 5 个分析 Action(含 Zod 校验) |
data-access.ts |
361 | 成绩 CRUD + 统计 |
data-access-analytics.ts |
266 | 趋势/对比分析 |
data-access-ranking.ts |
96 | 排名查询 |
stats-service.ts |
- | 统计计算纯函数(P1-1 新增:8 个纯函数 + 2 个常量 + 2 个接口) |
export.ts |
214 | Excel 导出 |
schema.ts |
100 | Zod 校验(含 12 个查询 schema) |
lib/grade-utils.ts |
46 | 公共工具函数(toNumber/normalize/buildScopeClassFilter) |
components/widget-boundary.tsx |
- | Widget 边界组件(P1-5 新增) |
types.ts |
- | 类型定义 |
2.7 classes(班级模块)— 耦合最严重
职责:班级 CRUD + 学生/教师管理 + 邀请码注册。
导出函数:
- Actions:
createTeacherClassAction/updateTeacherClassAction/deleteTeacherClassAction/createAdminClassAction/updateAdminClassAction/deleteAdminClassAction/createGradeClassAction/updateGradeClassAction/deleteGradeClassAction - Data-access:
getAdminClasses/getTeacherClasses/getGradeManagedClasses/getStudentClasses/getClassDetails/getClassStudents/getClassSchedule/getClassHomeworkInsights/getGradeHomeworkInsights/getStudentsSubjectScores/verifyTeacherOwnsClass/getTeacherIdsByClassIds(获取多个班级的所有教师 ID:班主任 + 任课教师,跨模块接口,供 messaging 模块调用)(✅ P0-5 已修复:classSchedule 写函数 createClassScheduleItem/updateClassScheduleItem/deleteClassScheduleItem 已迁移至 scheduling/data-access-class-schedule.ts,classes 模块仅保留 classSchedule 读函数;✅ P2 已修复:getAccessibleClassIdsForTeacher使用Promise.all并行化 ownedIds 与 assignedIds 查询) - Schema:
CreateTeacherClassSchema/UpdateTeacherClassSchema/DeleteTeacherClassSchema/CreateAdminClassSchema/UpdateAdminClassSchema/DeleteAdminClassSchema/CreateGradeClassSchema/UpdateGradeClassSchema/DeleteGradeClassSchema/CreateClassScheduleItemSchema/UpdateClassScheduleItemSchema/DeleteClassScheduleItemSchema/EnrollStudentByEmailSchema
依赖关系:
- 依赖:
shared/*、@/auth、school(✅ P1-1 已修复:通过 school data-access.isGradeHead/isGradeManager/findGradeIdByHeadAndName)、homework(✅ P0-7 已修复:通过homework/data-access-classes暴露的函数获取作业数据,不再直查 homework/exams 表) - 被依赖:
exams/homework/grades/attendance/scheduling/dashboard(通过 data-access,P0-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.ts,classes 模块仅保留 classSchedule 读函数(getStudentSchedule/getClassSchedule);新增 verifyTeacherOwnsClass 供 scheduling 模块跨模块校验教师班级归属
- ✅ P0-7 已修复:
data-access-stats.ts和data-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.ts中idByName.get(name)!非空断言清理为flatMap安全过滤;data-access-admin.ts中同类非空断言清理
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
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 |
785 | 17 个 Server Action(三组重复,使用 Zod schema 校验) |
schema.ts |
152 | Zod 校验(13 个 schema:教师/管理员/年级班级 CRUD + 课表 CRUD + 邮箱注册) |
types.ts |
201 | 类型定义(含跨领域类型污染) |
2.8 school(学校模块)
职责:学校/学年/部门/年级的 CRUD。
导出函数:
- Actions:
createSchoolAction/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/*、@/auth、users(⚠️getStaffOptions直查 users/roles,可接受) - 被依赖:
exams(✅ P1-1 已修复:通过 school data-access)、homework(✅ P1-1 已修复:通过 school data-access)、grades(✅ P1-1 已修复:通过 school data-access)、questions(✅ P1-1 已修复:通过 textbooks data-access)、classes(✅ P1-1 已修复:通过 school data-access)、course-plans(合理)
已知问题:
- ✅ P0-8 已修复:
actions.ts不再直接导入db和 schema,所有 DB 写操作下沉到data-access.ts,符合三层架构 - ✅ P2 已修复:
logAudit()通过 Next.jsafter()异步非阻塞执行 - ✅ P2 已修复:
data-access.ts中 8 处 catch 块添加console.error输出错误上下文(getDepartments/getAcademicYears/getSchools/getGrades/getStaffOptions/getGradesForStaff/getSubjectOptions/getGradeOptions) - ⚠️ P2:审计日志不一致(仅 school 实体记录,department/academicYear/grade 未记录)
- ⚠️ P2:
getStaffOptions/getGrades直查 users/roles(展示用,可接受) - ⚠️ P0-2(2026-06-22 审计发现):年级 CRUD 逻辑与
grade-management模块重复定义,两套实现并存 - ⚠️ P0-5(2026-06-22 审计发现):
school/components/*4 个组件均缺少 i18n(schools-view.tsx已修复,其余 3 个待修复);缺少 Error Boundary / Skeleton - ✅ P0-5 部分修复(2026-06-22):新增
school.jsoni18n 文件,schools-view.tsx接入useTranslations("school")
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
actions.ts |
349 | 12 个 Server Action(编排层,无 DB 直访) |
data-access.ts |
504 | 只读查询 + 12 个写操作 + 跨模块查询接口(isGradeHead/isGradeManager/findGradeIdByHeadAndName/getGradeNameById/getSubjectNameById) |
schema.ts |
51 | Zod 校验 |
types.ts |
96 | 类型定义(含 Insert/Update 入参类型) |
2.8b grade-management(年级管理模块)— ⚠️ 死模块
2026-06-22 审计发现:该模块拥有完整的理想架构(Service 接口 + Context DI + 角色配置 + Error Boundary + Skeleton + i18n + hooks 分离),但 13 个相关页面中无任何一个导入此模块。
management/grade/*页面实际依赖classes和school模块的 data-access。详见docs/architecture/audit/school-grade-class-audit-report.md。
职责:年级 CRUD + 年级作业洞察(重构版,对标理想架构模式)。
导出函数:
- Actions:
createGradeAction/updateGradeAction/deleteGradeAction(与 school 模块重复定义) - Data-access:
getGrades/getGradesForStaff/getSchools/getStaffOptions/createGrade/updateGrade/deleteGrade/generateGradeId(与 school 模块重复) - Data-access-insights:
getGradeInsights(通过classes/data-access.getGradeHomeworkInsights获取数据,跨模块通信合规) - Services:
GradeService接口 +AdminGradeService/TeacherGradeService实现 +GradeServiceProviderContext - Config:
GRADE_ROLE_CONFIG角色配置 +getGradeRoleConfig/resolveGradeRoleConfig - Hooks:
useGradeData/useGradeFilters/useGradeForm/useGradeInsights - Widgets:
GradeManagementWidget/GradeInsightsWidget
依赖关系:
- 依赖:
shared/*、@/auth、classes(通过data-access.getGradeHomeworkInsights,合规) - 被依赖:⚠️ 无任何模块或页面依赖此模块
已知问题:
- ⚠️ P0-1:模块完全未被使用(死模块),所有页面使用 school/classes 模块的 data-access
- ⚠️ P0-2:年级 CRUD 逻辑与 school 模块重复定义
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
actions.ts |
213 | 3 个 Server Action(含审计日志,比 school 模块版本更完善) |
data-access.ts |
238 | 年级 CRUD + 查询(与 school 模块重复) |
data-access-insights.ts |
75 | 年级洞察(适配 classes 模块数据) |
types.ts |
149 | 类型定义(含 GradeService 接口、角色配置、埋点接口) |
services/*.tsx |
4 文件 | GradeService 接口 + 实现 + Context DI |
config/role-config.ts |
66 | 角色配置驱动设计 |
hooks/*.ts |
4 文件 | 数据/筛选/表单/洞察 hooks |
widgets/*.tsx |
2 文件 | 管理面板 + 洞察面板 |
components/*.tsx |
11 文件 | 表格/工具栏/对话框/骨架屏/错误边界/空状态 |
2.9 scheduling(排课模块)
职责:自动排课算法 + 课表调整 + 排课规则管理。
导出函数:
- Actions:
autoScheduleAction/applyAutoScheduleAction/getSchedulingRulesAction/updateSchedulingRulesAction/getScheduleChangesAction/createScheduleChangeAction/updateScheduleChangeAction/deleteScheduleChangeAction - Data-access:
getSchedulingRules/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/*、@/auth、classes(✅ 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 - ⚠️ P2:
actions.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(考勤模块)— 结构典范
职责:考勤记录管理 + 统计分析 + 规则配置。
导出函数:
- Actions(10 个):
recordAttendanceAction/batchRecordAttendanceAction/updateAttendanceAction/deleteAttendanceAction/getAttendanceAction/getStudentAttendanceAction/getClassAttendanceStatsAction/getClassAttendanceForDateAction/saveAttendanceRulesAction/getAttendanceRulesAction - Data-access:
getAttendanceRecords/createAttendanceRecord/updateAttendanceRecord/deleteAttendanceRecord/getClassStudentsForAttendance/getAttendanceStats(管理员考勤总览页统计概览,基于getAttendanceRecords聚合)/upsertAttendanceRules/getAttendanceRules - Data-access-stats:
getStudentAttendanceSummary/getClassAttendanceStats/computeStats(⚠️ 未导出,无法单测) - Components:
AttendanceSheet(批量点名表单)/AttendanceRecordList(记录列表 + 删除)/AttendanceFilters(URL 同步筛选器)/AttendanceStatsCard(单卡片统计)/AttendanceStatsCards(管理员 6 卡片总览)/AttendanceStatsClassSelector(班级筛选 ChipNav)/AttendanceRulesForm(规则配置表单)/StudentAttendanceView(学生/家长只读视图)
依赖关系:
- 依赖:
shared/*、@/auth、classes(⚠️ P1-1 未修复:getClassStudentsForAttendance仍直查classEnrollments表) - 被依赖:
parent(⚠️ 跨模块 UI 类型依赖:3 个 parent 组件直接 import@/modules/attendance/types)
已知问题(详见 docs/architecture/audit/attendance-elective-audit-report.md):
- ❌ P0:
getAttendanceStats统计失真——调用getAttendanceRecords(默认 pageSize=20)后对items聚合,仅基于前 20 条记录计算总览数据 - ❌ P0:
getClassStudentsForAttendance仍直查classEnrollments表(架构图此前声称已修复,实际未修复) - ❌ P0:6 个读 Action 无调用方(页面绕过 Action 直接调用 data-access),违反三层架构
- ❌ P0:update/delete Action 缺资源归属校验(教师 A 可修改/删除教师 B 的记录)
- ❌ P0:i18n 完全缺失(
ATTENDANCE_STATUS_LABELS硬编码英文,组件中硬编码中文) - ❌ P0:错误边界完全缺失(5 个角色目录均无
error.tsx) - ⚠️ P1:
computeStats未导出,无法单测 - ⚠️ P1:
attendance-sheet.tsx使用window.confirm(与项目 AlertDialog 模式不一致) - ⚠️ P1:
attendance-sheet.tsx存在{} as Record<AttendanceStatus, number>类型断言 - ⚠️ P1:
STATUS_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(用户模块)
职责:用户资料管理 + 批量导入导出 + 管理员用户列表管理。
导出函数:
- Actions:
getUserProfileAction/updateUserProfileAction/importUsersAction/exportUsersAction/downloadUserTemplateAction/updateUserRoleAction/deleteUserAction - Data-access:
getUserProfile/getCurrentStudentUser(✅ P2-20 已修复:从 homework 模块迁移而来,6 个 student 页面通过此函数获取学生身份,不再依赖 homework 模块)/getAdminUsers(管理员用户列表分页查询,支持搜索+角色聚合)/getAdminUserRoles(角色名列表,用于筛选下拉框) - Import-export:
generateUserImportTemplate/parseUserImportData/exportUsersToExcel(+ re-exportbatchImportUsers/UserImportResult保持向后兼容) - User-service:
batchImportUsers(用户创建 + 密码哈希 + 角色分配) - Class-registration:
registerStudentByInvitationCode(委托 classes/data-access 完成班级注册) - Components:
UserImportDialog(批量导入对话框)/AdminUsersView(管理员用户列表客户端组件,搜索+筛选+分页+删除)
依赖关系:
- 依赖:
shared/*(含shared/lib/role-utils,✅ P2 已修复:删除本地normalizeRoleName/resolvePrimaryRole/rolePriority,统一复用shared/lib/role-utils.resolvePrimaryRole)、@/auth、classes(✅ P1-4 已修复:通过class-registration.ts调用classes/data-access.enrollStudentByInvitationCode,不再直写 classEnrollments) - 被依赖:
dashboard(通过 data-access,P0-4 已修复)、grades(✅ P1-1 已修复:通过 users data-access)、homework(✅ P1-1 已修复:通过 users data-access)、scheduling(✅ P1-1 已修复:通过 users data-access)、diagnostic(✅ P1-1 已修复:通过 users data-access)、elective(✅ P1-1 已修复:通过 users data-access)、proctoring(✅ P1-1 已修复:通过 users data-access)、parent(✅ 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 已修复:
已下沉到 data-accessupdateUserProfile绕过 data-access 直接 DB 写 - ✅ P2-20 已修复:新增
getCurrentStudentUser函数(从 homework 模块迁移),6 个 student 页面通过此函数获取学生身份,不再依赖 homework 模块 - ✅ P2 已解决:
data-access.ts已扩充写操作(updateUserProfile 已下沉) - ⚠️ 已知限制:
AdminUsersView客户端组件的删除操作通过fetch("/api/admin/users/:id")调用,对应 API 路由尚未实现(deleteUserActionServer 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 Action(profile 更新 + 模板下载/导入/导出 + 角色更新 + 删除) |
data-access.ts |
394 | getUserProfile + 用户查询 + 管理员用户列表分页查询(getAdminUsers/getAdminUserRoles) |
components/admin-users-view.tsx |
290 | 管理员用户列表客户端组件(搜索+筛选+表格+分页+删除对话框) |
2.12 dashboard(仪表盘模块)
职责:管理员/教师/学生/家长仪表盘数据聚合 + 权限校验 + i18n + 纯逻辑工具函数。
导出函数:
- Actions:
getAdminDashboardAction/getTeacherDashboardAction/getStudentDashboardAction/getParentDashboardAction(均调用requirePermission()校验对应DASHBOARD_*_READ权限) - Data-access:
getAdminDashboardData(并行调用 6 个模块 stats 函数) - Lib 纯函数:
toWeekday/countStudentAssignments/sortUpcomingAssignments/filterTodaySchedule/computeTeacherMetrics/getGreetingKey - Components:
AdminDashboardView/TeacherDashboardView/StudentDashboard/UserGrowthChart/DashboardGreetingHeader(共享问候头部,V2 抽象)/DashboardSection(分区 Error Boundary + Suspense + 骨架屏)(均接入 next-intl i18n)
依赖关系:
- 依赖:
shared/*、@/auth、classes(通过 data-access)、homework(通过 data-access)、users(通过 data-access)、parent(通过 data-access.getParentDashboardData)、textbooks/questions/exams(通过各模块 dashboard stats 函数)、recharts、next-intl - 被依赖:无
权限点:
DASHBOARD_ADMIN_READ(admin)DASHBOARD_TEACHER_READ(teacher)DASHBOARD_STUDENT_READ(student)DASHBOARD_PARENT_READ(parent)
已知问题:
- ✅ 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-intl(
useTranslations/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 V2):10 个子组件 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 V2):a11y 增强 — 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)。
导出函数:
- Actions:
sendMessageAction/markMessageAsReadAction/deleteMessageAction/getMessagesAction/getMessageDetailAction/getRecipientsAction/getUnreadMessageCountAction/getNotificationPreferencesAction/updateNotificationPreferencesAction(✅ P1-4 已修复:通知 CRUD Action 已迁移至 notifications 模块,messaging 仅保留私信和通知偏好 Action) - Data-access:
getMessages/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 新增:消息首页编排函数,一次性获取消息列表和通知列表) - Hooks:
useMessageSearch(✅ P1-7 新增:消息搜索 hook,含防抖和请求竞态取消) - Notification-preferences:
re-export shim(实际逻辑在✅ P0-b 已修复:notifications/preferences.ts)notification-preferences.ts文件已删除(通知模块去重),消费方改为直接从@/modules/notifications/preferences导入getNotificationPreferences/upsertNotificationPreferences
依赖关系:
- 依赖:
shared/*、@/auth、notifications(✅ P0-4 / P1-5 已修复:通过sendNotificationdispatcher 发送通知,通知 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 直接调用createNotificationnotifications.sendNotification,通知 CRUD 已迁移至 notifications 模块 - ✅ P0 已修复:
与 notifications 双向依赖 + 职责重叠通知相关表(messageNotifications / notificationPreferences)所有权已移交 notifications 模块,messaging 仅保留 messages 表 - ✅ P1-5 已修复:
同时管理 3 类数据(messages + messageNotifications + notificationPreferences)仅管理 messages 表,通知相关数据由 notifications 模块管理 - ✅ P1 已修复:
通知相关 Action 使用通知 Action 已迁移至 notifications 模块并改为requireAuth()而非requirePermission()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 已修复:
已删除(通知模块去重),8 个消费方改为直接从notification-preferences.tsre-export shim 文件@/modules/notifications/preferences导入getNotificationPreferences/upsertNotificationPreferences,消除 messaging 模块对通知偏好的冗余 re-export 层 - ✅ P1 已修复:
全模块零 i18n,中英文案硬编码所有组件接入 next-intl(useTranslations("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.ts,messaging 模块仅保留私信组件 - ✅ P1-5 已修复:
页面层新增Promise.all编排 messaging 和 notifications 两个模块的 data-accessgetMessagesPageData编排函数,页面层仅调用单一函数 - ✅ P1-7 已修复:
消息列表客户端搜索逻辑抽离为useEffect+setTimeout防抖搜索未取消已发出的请求useMessageSearchhook(含防抖 + 请求竞态取消);无分页 UI新增客户端分页 UI(PAGE_SIZE=20,ChevronLeft/ChevronRight 按钮) - ✅ P1-9 已修复:
改为deleteMessage两个独立 UPDATE 无事务db.transaction包裹 senderDeletedAt 和 receiverDeletedAt 更新,保证原子性 - ✅ P2-11 已修复:
发送/删除消息无埋点sendMessageAction/markMessageAsReadAction/deleteMessageAction新增trackEvent埋点(message.sent / message.marked_read / message.deleted)
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
actions.ts |
~260 | 7 个私信 Server Action + 2 个通知偏好 Action(✅ P1-4:通知 CRUD Action 已迁移至 notifications 模块) |
data-access.ts |
~270 | 私信 CRUD + getMessagesPageData 编排函数(✅ P1-5 新增) |
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 + 客户端分页 UI,PAGE_SIZE=20) |
components/message-detail.tsx |
消息详情(含回复) |
components/message-compose.tsx |
撰写新消息 |
components/unread-message-badge.tsx |
未读消息计数徽章(侧边栏,每 60 秒轮询 getUnreadMessageCountAction) |
客户端行为:
message-list.tsx:客户端调用getMessagesAction搜索消息(useMessageSearch hook,400ms 防抖,请求竞态取消)unread-message-badge.tsx:每 60 秒轮询getUnreadMessageCountAction刷新未读计数
2.14 notifications(通知分发模块)
职责:多渠道通知分发(SMS/Email/WeChat/InApp)+ 站内通知 CRUD + 通知偏好管理 + 通知 UI 组件。
导出函数:
- Actions:
sendNotificationAction/sendClassNotificationAction/getNotificationsAction/getUnreadNotificationCountAction/markNotificationAsReadAction/markAllNotificationsAsReadAction(✅ P1-4 新增:后 4 个通知 CRUD Action 从 messaging 模块迁移) - Dispatcher:
sendNotification(payload)/sendBatchNotifications(payloads) - Data-access:
createNotification/getNotifications/markNotificationAsRead/markAllNotificationsAsRead/getUnreadNotificationCount/getUserContactInfo/logNotificationSend/logNotificationSendBatch(✅ P0-4 / P1-5 修复后从 messaging 迁移) - Preferences:
getNotificationPreferences/upsertNotificationPreferences(✅ P0-4 / P1-5 修复后从 messaging 迁移) - Channels:
InAppChannelSender/SmsChannelSender/EmailChannelSender/WeChatChannelSender - Components:
NotificationList/NotificationDropdown(✅ P1-4 新增:从 messaging/components 迁移)
依赖关系:
- 依赖:
shared/*、@/auth、classes(✅ P1-1 已修复:通过 classes data-access.getClassExists/getStudentIdsByClassId) - 被依赖:
messaging(✅ P0-4 / P1-5 已修复:messaging 通过sendNotificationdispatcher 发送通知;✅ 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 不再反向依赖 messaging,in-app-channel 改为静态导入本地 createNotification - ✅ P1-1 已修复:
改为调用sendClassNotificationAction直查classes/classEnrollmentsclasses/data-access.getClassExists/getStudentIdsByClassId - ✅ P1-4 已修复:
通知 UI 组件放在 messaging/components 下,直接 import notifications 类型和 messaging/actionsnotification-list.tsx和notification-dropdown.tsx已迁移至notifications/components/,通知 CRUD Action 已从 messaging 迁移至notifications/actions.ts,消除 UI 层跨模块耦合 - ✅ P2-11 已修复:
通知标记已读无埋点markNotificationAsReadAction/markAllNotificationsAsReadAction新增trackEvent埋点(notification.marked_read / notification.marked_all_read) - ⚠️ P1:发送日志仅 console,无
notification_logs表 - ✅ 渠道抽象优秀(接口 + 工厂 + Mock 实现)
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
dispatcher.ts |
152 | 渠道选择 + 并行分发 |
data-access.ts |
177 | 站内通知 CRUD + 用户联系方式 + 日志(P0-4 / P1-5 修复后新增通知 CRUD) |
preferences.ts |
166 | 通知偏好 CRUD(P0-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 |
通知列表(消息页底部,展示所有通知,支持标记已读) |
components/notification-dropdown.tsx |
通知下拉菜单(站点头部,每 30 秒轮询 getNotificationsAction + getUnreadNotificationCountAction) |
客户端行为:
notification-dropdown.tsx:每 30 秒轮询getNotificationsAction(pageSize=10)和getUnreadNotificationCountAction刷新通知和未读计数
2.15 audit(审计模块)
职责:操作日志 + 登录日志 + 数据变更日志查询与导出。
导出函数:
- Actions:
getAuditLogsAction/getLoginLogsAction/getDataChangeLogsAction/exportAuditLogsAction/exportLoginLogsAction/exportDataChangeLogsAction - Data-access:
getAuditLogs/getLoginLogs/getDataChangeLogs/getAuditModuleOptions
依赖关系:
- 依赖:
shared/*、@/auth - 被依赖:无
已知问题:
- ⚠️ P2:Excel 导出逻辑内联在 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 + 发布/归档 + 发布通知。
导出函数:
- Actions:
getAnnouncementsAction/createAnnouncementAction/updateAnnouncementAction/deleteAnnouncementAction/publishAnnouncementAction/archiveAnnouncementAction(✅ P1-2 已修复:actions 层不再直接访问 DB,全部下沉到 data-access;✅ 发布公告时触发通知模块sendBatchNotifications) - Data-access:
getAnnouncements(支持audience受众过滤)/getAnnouncementById/insertAnnouncement/updateAnnouncementById/deleteAnnouncementById/publishAnnouncementById/archiveAnnouncementById(后 5 个为 P1-2 新增)/getAdminAnnouncementsPageData/getEditAnnouncementPageData(✅ P1-5 新增:管理端列表页和编辑页编排函数,页面层仅调用单一函数)
依赖关系:
- 依赖:
shared/*、@/auth、school(获取年级列表)、classes(获取班级列表 + 解析受众)、users(获取目标用户 ID 列表)、notifications(发布公告时发送通知) - 被依赖:无
已知问题:
- ✅ P1-2 已修复:
所有写操作直接在 actions 层写操作已下沉到 data-access(5 个新函数)db.insert/update/delete,未下沉到 data-access - ✅ 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-intl(useTranslations("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-accessgetAdminAnnouncementsPageData和getEditAnnouncementPageData编排函数,页面层仅调用单一函数 - ✅ P1-6 已修复:
targetGradeId/targetClassId为 optional,未根据type做条件必填校验CreateAnnouncementSchema和UpdateAnnouncementSchema添加superRefine(refineAudience),年级公告强制targetGradeId,班级公告强制targetClassId - ✅ P2-11 已修复:
发布/归档/删除公告无埋点createAnnouncementAction/updateAnnouncementAction/deleteAnnouncementAction/publishAnnouncementAction/archiveAnnouncementAction新增trackEvent埋点(announcement.created / announcement.updated / announcement.published / announcement.deleted / announcement.archived)
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
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 |
公告列表(用户端,支持状态筛选) |
components/announcement-card.tsx |
公告卡片(列表项) |
components/announcement-detail.tsx |
公告详情(只读) |
components/announcement-form.tsx |
公告表单(创建/编辑,✅ P1-6:条件校验由 schema superRefine 保证) |
components/admin-announcements-view.tsx |
管理端公告视图(列表 + 筛选) |
2.17 files(文件模块)
职责:文件附件 CRUD + 批量删除 + 统计。
导出函数:
- Data-access:
getAllFileAttachments/getFileAttachmentsByOwner/getFileAttachmentById/createFileAttachment/updateFileAttachment/deleteFileAttachment/batchDeleteFileAttachments/getFileStats(✅ P2 已修复:7 个读函数使用React.cache()包装实现请求级 memoization:getFileAttachment/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 已修复:
getFileAttachmentsWithFilters中or(...)!非空断言清理为安全守卫 - ✅ P2 已修复:
改为显式getFileAttachmentsWithFilters中conditions隐式any[]SQL[]类型标注 - ⚠️ P2:无
actions.ts,data-access 被路由直接调用 - ✅ 职责单一,不跨模块查询
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
data-access.ts |
267 | 文件 CRUD + 批量删除 + 统计 |
types.ts |
- | 类型定义 |
components/* |
6 文件 | 上传/列表/预览/管理 |
2.18 course-plans(课程计划模块)
职责:课程计划 CRUD + 周计划项 CRUD + 排序。
导出函数:
- Actions:
getCoursePlansAction/getCoursePlanByIdAction/createCoursePlanAction/updateCoursePlanAction/deleteCoursePlanAction/createCoursePlanItemAction/updateCoursePlanItemAction/deleteCoursePlanItemAction/toggleCoursePlanItemCompletedAction - Data-access:与 actions 对应
依赖关系:
- 依赖:
shared/*、@/auth、classes(合理,getAdminClasses/getStaffOptions)、school(合理,getAcademicYears) - 被依赖:无
已知问题:
- ⚠️ P2:
getSubjectOptions直查subjects表(subjects 无独立模块,可接受) - ✅ 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-access:
getChildren/getChildBasicInfo/getChildDashboardData/getParentDashboardData/verifyParentChildRelation/getChildNameList(✅ v4 新增:用于详情页头部多子女切换器,一次批量查询避免 N+1) - Components:
ParentDashboard/ChildCard/ChildDetailHeader/ChildDetailPanel/SiblingSwitcher/ChildHomeworkSummary/ChildHomeworkDetail(v4 新增)/ChildGradeSummary/ChildGradeDetail(v4 新增)/ChildScheduleCard/ParentChildrenDataPage/ParentNoChildrenPage/ParentAttentionBanner(v4 新增)/ParentAttendanceWarning(v4 新增)/ParentAttendanceRateCard(v4 新增)/ParentAttendanceCalendar(v4 新增)/ParentExportButton(v4 新增)
v4 修复(产品/UX 维度):
- ✅ FEAT-G01:新增
/parent/leave请假申请占位页(含 loading.tsx) - ✅ FEAT-G02:详情页 Schedule Tab 支持完整周课表(新增
weeklySchedule字段 +ChildWeeklyScheduleItem类型 +buildWeeklySchedule函数) - ✅ FEAT-G03:详情页 Grades Tab 新增
ChildGradeDetail按科目分组展示(平均分、趋势、最近成绩) - ✅ FEAT-G04:详情页 Homework Tab 新增
ChildHomeworkDetail展示完整作业信息(状态、截止、提交时间、尝试次数) - ✅ FEAT-G05:考勤页新增
ParentAttendanceWarning异常预警横幅(聚合缺勤/迟到/低出勤率) - ✅ FEAT-G06:详情页底部新增"Contact Teacher"快捷入口
- ✅ FEAT-G07:详情页头部新增
SiblingSwitcher多子女切换器 - ✅ LAYOUT-P01:仪表盘新增
ParentAttentionBanner待办事项横幅 - ✅ LAYOUT-P02:
ChildCard突出 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-P02:Grades/Attendance 页面描述明确职责(多子女对比 vs 单子女详情)
- ✅ NAV-P03:详情页实现
?tab=参数支持 - ✅ NAV-P04:所有 parent 路由新增
loading.tsx骨架屏 +error.tsx错误边界 - ✅ DATA-P02:成绩卡片新增 TrendIcon 进步/退步/持平标识
- ✅ DATA-P03:排名展示新增"Top X%"百分比
- ✅ DATA-P04:作业列表新增科目标识 Badge
- ✅ DATA-P05:作业分数显示新增"pts"单位
- ✅ DATA-P06:考勤页新增
ParentAttendanceRateCard出勤率汇总卡片 - ✅ HABIT-P01:仪表盘"一眼定位异常"能力(AttentionBanner 聚合)
- ✅ HABIT-P02:待办横幅作业项直接跳转详情页 homework tab(1 次点击到达)
- ✅ HABIT-P03:多子女切换无需返回仪表盘
- ✅ HABIT-P06:仪表盘展示未读/待办数量
- ✅ A11Y-P02:Overdue 状态增加 AlertTriangle 图标辅助
- ✅ A11Y-P04:成绩图表容器新增 aria-label
- ✅ PERF-P01/P02:骨架屏 + 错误边界
- ✅ PERF-P03:空状态新增"Contact support"引导按钮
- ✅ PERF-P04:
ChildCardLink 添加prefetch - ✅ MOBILE-P03:移动端子女卡片改为水平滑动 Carousel(snap-x)
- ✅ MOBILE-P04:作业/成绩列表项
min-h-[44px]触摸区域
依赖关系:
- 依赖:
shared/*、@/auth、classes(合理)、homework(合理)、grades(合理)、users(合理)、school(合理)、attendance(v4 新增:考勤页复用StudentAttendanceView;⚠️ 跨模块 UI 类型依赖:3 个组件直接 import@/modules/attendance/types) - 被依赖:无
已知问题:
- ⚠️ P1:3 个 parent 组件(
parent-attendance-warning.tsx/parent-attendance-rate-card.tsx/parent-attendance-calendar.tsx)直接 import@/modules/attendance/types,违反模块解耦原则(应通过 data-access 接口或 shared 类型抽象) - ⚠️ P1:parent-attendance-calendar.tsx 重新定义
STATUS_DOT/STATUS_LABEL常量,与 attendance 模块重复 - ⚠️ P1:3 个 parent 组件的纯函数(
buildWarnings/aggregate/rateTone/formatDateKey/parseDateKey/buildCalendarDays/isSameDay)未导出,无法单测 - ✅ P1 已修复:
改为调用app/(dashboard)/parent/children/[studentId]/page.tsx直接访问 DB(违反三层架构)verifyParentChildRelationdata-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 | 类型定义(含 JSDoc,v4 新增 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 面板 + SiblingSwitcher(v4 重写,集成 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(选课模块)
职责:选修课程管理 + 学生选课 + 抽签。
导出函数:
- Actions(11 个):
getElectiveCoursesAction/createElectiveCourseAction/updateElectiveCourseAction/deleteElectiveCourseAction/getStudentSelectionsAction/selectCourseAction/dropCourseAction/runLotteryAction/getAvailableCoursesForStudentAction/openSelectionAction/closeSelectionAction - Data-access:
getElectiveCourses/getElectiveCourseById/createElectiveCourse/updateElectiveCourse/deleteElectiveCourse/openSelection/closeSelection/buildCourseSelect/mapCourseRow/resolveCourseDisplayNames/CourseCoreRow(P3 新增导出,供 data-access-selections 复用) - Data-access-operations:
selectCourse/dropCourse/runLottery/buildLotteryRankCase(⚠️ 未导出,无法单测) - Data-access-selections:
getCourseSelections/getStudentSelections/getStudentGradeId/getAvailableCoursesForStudent - Components:
ElectiveCourseList(课程卡片网格 + 管理操作)/ElectiveCourseForm(课程创建/编辑表单)/ElectiveFilters(nuqs 筛选栏)/StudentSelectionView(学生选课视图)
依赖关系:
- 依赖:
shared/*、@/auth、school(✅ 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):
- ❌ P0:3 个读 Action 无调用方(
getElectiveCoursesAction/getStudentSelectionsAction/getAvailableCoursesForStudentAction),页面绕过 Action 直接调用 data-access - ❌ P0:update/delete/select/drop/lottery Action 缺资源归属校验(教师 A 可操作教师 B 的课程,学生可退选他人课程)
- ❌ P0:i18n 完全缺失(4 组标签/颜色常量硬编码英文,组件中硬编码中文)
- ❌ P0:错误边界完全缺失(3 个角色目录均无
error.tsx) - ⚠️ P1:
elective-course-form.tsx存在v as "fcfs" | "lottery"类型断言 - ⚠️ P1:
elective-course-list.tsx存在null as never类型逃逸 - ⚠️ P1:
buildLotteryRankCase未导出,无法单测 - ⚠️ P1:
SELECTION_MODE_LABELS已定义但表单未复用,硬编码 Select 选项 - ✅ P1 已修复:
改为只查 electiveCourses 表,通过buildCourseSelect跨模块 join users/subjects/grades 表resolveCourseDisplayNames调用 school/users data-access 获取显示名称 - ✅ P1 已修复:
删除本地实现,改用getSubjectOptions本地直查 subjects 表且与 school 模块重复school/data-access.getSubjectOptions - ✅ P1 已修复:
改为selectCourse/dropCourse缺事务包裹db.transaction包裹,FCFS 模式下使用FOR UPDATE行锁防止并发超卖 - ✅ P2 已修复:
抽取到 data-access.ts 统一导出,data-access-selections.ts 复用mapCourseRow在 data-access.ts 与 data-access-selections.ts 重复定义 - ✅ P2 已修复:
改为 Fisher-Yates 无偏洗牌算法runLottery使用sort(() => Math.random() - 0.5)有偏 shuffle - ✅ P2 已修复:
使用selectCourseFCFS 并发超卖风险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/lottery,P3 重构:事务包裹 + 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(监考模块)
职责:考试监考事件记录 + 防作弊监控 + 监考面板。
导出函数:
- Actions:
recordProctoringEventAction/getProctoringDashboardAction - Data-access:
recordProctoringEvent/getExamSubmissionForProctoring/getExamForProctoring/getExamProctoringSummary/getStudentProctoringStatuses/getRecentProctoringEvents(✅ P2 已修复:getExamProctoringSummary使用Promise.all并行化考试信息与提交记录查询、事件类型统计与学生事件统计查询;合并两次 filter 为单次循环统计 started/submitted)
依赖关系:
- 依赖:
shared/*、@/auth、exams(✅ P1-1 已修复:通过 exams data-access.getExamForProctoringCrossModule/getExamSubmissionForProctoringCrossModule/getExamSubmissionsForExam/getExamTitleById)、users(✅ P1-1 已修复:通过 users data-access.getUserNamesByIds) - 被依赖:无
已知问题:
- ❌ P0:
exam-mode-config.tsx未集成到考试表单(死代码,监考功能无法启用) - ✅ P0-6 已修复:
事件上报存在 Server Action 与 REST API 双通道重复删除/api/proctoring/eventREST 路由(移至 deletes/),Server ActionrecordProctoringEventAction为唯一规范路径 - ✅ P1-1 已修复:
跨模块直查改为通过 exams/users data-access 函数获取数据exams/examSubmissions/users - ✅ P2 已修复:
actions.ts不再直接 importdb和examSubmissions,submission 归属校验已下沉到 data-access;recordProctoringEventAction改用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(学情诊断模块)
职责:知识点掌握度查询 + 诊断报告生成。
导出函数:
- Actions:
generateStudentDiagnosticReportAction/generateClassDiagnosticReportAction/publishDiagnosticReportAction/deleteDiagnosticReportAction/getDiagnosticReportsAction/getStudentMasteryAction - Data-access:
updateMasteryFromSubmission/getStudentMastery/getClassMasteryOverview - Data-access-reports:
createDiagnosticReport/getDiagnosticReport/getDiagnosticReports/deleteDiagnosticReport/publishDiagnosticReport(✅ P2 已修复:getDiagnosticReports和getDiagnosticReportById使用React.cache()包装实现请求级 memoization) - Schema:
GenerateStudentReportSchema/GenerateClassReportSchema/PublishReportSchema/DeleteReportSchema/GetDiagnosticReportsSchema/GetDiagnosticReportByIdSchema
依赖关系:
- 依赖:
shared/*、@/auth、exams(✅ P1-1 已修复:通过 exams data-access.getExamSubmissionWithAnswers)、questions(✅ P1-1 已修复:通过 questions data-access.getKnowledgePointsForQuestions)、classes(✅ P1-1 已修复:通过 classes data-access.getClassExists/getClassNameById/getActiveStudentIdsByClassId)、users(✅ P1-1 已修复:通过 users data-access.getUserNamesByIds/getUserIdsByGradeId) - 被依赖:无
已知问题:
- ✅ P1-1 已修复:
改为调用updateMasteryFromSubmission跨模块直查 4 张表(与 exams/homework/questions 紧耦合)exams/data-access.getExamSubmissionWithAnswers和questions/data-access.getKnowledgePointsForQuestions - ✅ P2 已修复:
已删除死代码data-access-reports.ts有未使用代码(round2+void round2) - ✅ P2 已修复:
改为updateMasteryFromSubmission循环内串行 await upsertPromise.all并行执行所有 upsert - ✅ P2 已修复:
改为两组getClassMasterySummary串行查询(className → studentIds → userMap → masteryRows)Promise.all并行(className+studentIds,userMap+masteryRows) - ✅ P2 已修复:
改为显式getDiagnosticReports中conditions隐式any[]SQL[]类型标注 - ✅ P0-2 已修复:
改为通过data-access-reports.ts直查users表获取姓名users/data-access.getUserNamesByIds跨模块接口 - ✅ P2-2 已修复:
改用标准 Tailwind 类(w-44/max-w-32/text-xs/h-96/max-w-lg)class-diagnostic-view.tsx/student-diagnostic-view.tsx/mastery-radar-chart.tsx中存在 Tailwind 任意值 - ✅ P2-1 已修复:
图表/表格/列表缺少 a11y ARIA 属性为 5 个图表添加role="img"+aria-label,2 个表格添加<caption>,3 个列表添加role="list",3 个图标按钮添加aria-label - ✅ P2-3 已修复:
班级报告将生成者 ID 存入schemastudentId字段(schema 设计缺陷 workaround)learningDiagnosticReports.studentId改为可空,班级报告studentId置空,读取逻辑适配 null - ✅ 与 grades 模块无职责重叠(grades 管分数,diagnostic 管知识点掌握度)
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
data-access.ts |
254 | 知识点掌握度查询 + 更新 |
data-access-reports.ts |
202 | 诊断报告 CRUD |
actions.ts |
172 | 6 个 Server Action(使用 Zod schema 校验) |
schema.ts |
56 | Zod 校验(6 个 schema:生成/发布/删除/查询报告) |
types.ts |
97 | 类型定义 |
components/* |
4 文件 | 学生/班级诊断视图 + 雷达图 |
2.23 settings(设置模块)
职责:系统设置(学校信息/安全策略/文件上传/通知配置)+ AI Provider 管理 + 密码修改 + 个人资料 + 主题偏好 + 通知偏好 + 个人信息页(学生/教师概览)。
导出函数:
- Actions:
getAiProvidersAction/createAiProviderAction/updateAiProviderAction/deleteAiProviderAction/testAiProviderAction - Actions-password:
changePasswordAction(✅ P1 已修复:使用requirePermission(USER_PROFILE_UPDATE)+ Zod 校验 + DB 操作下沉到 data-access) - Actions-avatar:
updateUserAvatarAction/removeUserAvatarAction(✅ P2-8 新增:头像上传/删除,复用/api/upload路由) - Actions-notifications:
sendTestNotificationAction(✅ P2-10 新增:发送测试通知,占位实现待接入真实通知服务) - Actions-system-settings:
getAdminSystemSettingsAction/saveAdminSystemSettingsAction(✅ P0-3 新增:管理员系统设置 CRUD,4 分类 Zod 校验) - Actions-security:
getSecurityCenterAction/toggleTwoFactorAction(✅ P2-9 新增:2FA 状态查询/切换 + 最近登录历史) - Data-access:
getAiProviderSummaries/countDefaultAiProviders/getAiProviderForUpdate/updateAiProvider/createAiProvider/getUserPasswordHash/getPasswordSecurityByUserId/updateUserPassword/upsertPasswordSecurityOnPasswordChange(P1 新增,从 actions 下沉) - Data-access-system-settings:
getSystemSettingsByCategory/getAllSystemSettings/getSystemSetting/upsertSystemSetting/upsertSystemSettings(✅ P0-3 新增:system_settings 表 CRUD,键值对存储模式) - Components:
SettingsView(统一设置页布局,5 标签页 General/Notifications/Appearance/Security/AI;角色差异通过resolveRoleSettingsConfig配置驱动 +generalExtraprops 注入;Tab URL 持久化;每个 TabsContent 包裹SettingsSectionErrorBoundary+Suspense骨架屏;AI 标签页条件渲染需AI_CONFIGURE权限)、SettingsServiceProvider/useSettingsService(Context 注入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 新增:头像上传/预览/删除客户端组件,文件验证 + i18n)、SecurityCenterCard(✅ P2-9 新增:2FA 开关 + 最近登录历史卡片)、ThemePreferencesCard(✅ P2-11 已增强:集成LocaleSwitcher语言切换) - Config:
ROLE_SETTINGS_CONFIG/resolveRoleSettingsConfig(配置驱动角色 → 设置视图映射,新增角色只需添加条目) - Lib:
buildStudentOverviewData/computeStudentStats/sortUpcomingAssignments/filterTodaySchedule/toWeekday(纯数据计算函数,与 UI 分离,便于单元测试) - Types:
AiProviderSummary/AiProviderName/AiProviderExisting/SettingsService/ProfileService/NotificationPreferenceService(服务接口定义,用于依赖注入解耦)
依赖关系:
- 依赖:
shared/*(含shared/lib/bcrypt-utils)、@/auth、messaging(页面层通过SettingsService接口注入,组件层不直接 import)、users(页面层通过SettingsService接口注入)、classes/homework/dashboard(ProfileStudentOverview 异步组件获取学生概览数据)、notifications(页面层获取通知偏好) - 被依赖:无
已知问题:
- ⚠️ P2:混合 5 类职责(AI Provider + 密码 + 资料 + 主题 + 通知偏好)
- ✅ P1 已修复:
无新建data-access.ts,actions.ts直接使用dbdata-access.ts,所有 DB 操作已下沉 - ✅ P1 已修复:
改为changePasswordAction使用requireAuth()无 Zod 校验requirePermission(USER_PROFILE_UPDATE)+ChangePasswordSchemaZod 校验 + 并行查询优化 - ✅ 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 类已修复:
Progress 组件新增[&>div]:bg-red-500等任意值类indicatorClassNameprop,使用标准颜色类 - ✅ P0 已修复:
改为通过notification-preferences-form.tsx跨模块直接 import messaging/actionsuseSettingsService().notifications.updatePreferences调用,页面层注入实现 - ✅ P0 已修复:
改为通过profile-settings-form.tsx跨模块直接 import users/actionsuseSettingsService().profile.updateProfile调用,页面层注入实现 - ✅ P0 已修复:
i18n 完全缺失新增settings.json翻译文件(zh-CN + en),所有组件改用useTranslations/getTranslations - ✅ P1 已修复:
缺少 Error Boundary新增SettingsSectionErrorBoundary,每个 TabsContent + profile 页面角色概览区块均包裹 - ✅ P1 已修复:
缺少 Suspense 骨架屏每个 TabsContent 包裹Suspense+SettingsSectionSkeleton;profile 页面包裹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.ts,AdminSettingsView 改为真实数据层 - ✅ P2-8 已修复:
无头像上传新增updateUserAvatardata-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 标签页
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
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 | 管理员系统设置 CRUD(P0-3 新增:4 分类 Zod 校验 + upsert) |
actions-security.ts |
165 | 2FA 状态查询/切换 + 最近登录历史(P2-9 新增) |
data-access.ts |
158 | AI Provider CRUD + 密码修改 DB 操作(P1 新增) |
data-access-system-settings.ts |
119 | system_settings 表 CRUD(P0-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 + useSettingsService(Context 注入服务接口) |
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、@/auth(useSession)、messaging(通知下拉)、shared/components/ui/select(角色切换下拉) - 被依赖:
app/(dashboard)/layout.tsx
已知问题:
- ✅ P2 已修复:
用权限反推角色app-sidebar.tsx改用hasRole()判断角色,并新增多角色切换机制(SidebarContext.currentRole/setCurrentRole,null 表示自动检测;当用户拥有多个角色时在侧边栏底部显示Select下拉切换) - ✅ navigation.ts 无幽灵路由(13 个已修复)
文件清单:
| 文件 | 职责 |
|---|---|
components/app-sidebar.tsx |
侧边栏(根据权限渲染 + 多角色切换下拉) |
components/sidebar-provider.tsx |
侧边栏状态 Context(含 currentRole/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)。数据结构从 v1(blocks 数组)升级到 v2(nodes + edges 节点图),旧数据通过
migrateV1ToV2()自动迁移。
数据结构:
- v1(已废弃,仅向后兼容读取):
{ version: 1, blocks: Block[] } - v2(当前):
{ version: 2, nodes: LessonPlanNode[]; edges: LessonPlanEdge[] }LessonPlanNode:Block+position: { x, y }(画布坐标)LessonPlanEdge:{ id, source, target, sourceHandle?, targetHandle? }(节点间连线)
导出函数:
- Data-access(
data-access.ts):getLessonPlans/getLessonPlanById/createLessonPlan/updateLessonPlanContent/softDeleteLessonPlan/duplicateLessonPlan/getTemplateById/buildInitialContent/migrateV1ToV2(v1→v2 迁移:blocks 数组转换为 nodes + 线性 edges)/normalizeDocument(规范化:确保 content 为 v2 格式,兼容旧数据) - Data-access-versions(
data-access-versions.ts):getLessonPlanVersions/createLessonPlanVersion/getVersionContent/revertToVersion/pruneAutoVersions - Data-access-templates(
data-access-templates.ts):getLessonPlanTemplates/saveAsTemplate/deletePersonalTemplate - Data-access-knowledge(
data-access-knowledge.ts):getLessonPlansByKnowledgePoint/getLessonPlansByQuestion - Publish-service(
publish-service.ts):publishLessonPlanHomework - AI-suggest(
ai-suggest.ts):suggestKnowledgePoints - Actions:
getLessonPlansAction/getLessonPlanByIdAction/createLessonPlanAction/updateLessonPlanAction/saveLessonPlanVersionAction/getLessonPlanVersionsAction/revertLessonPlanVersionAction/deleteLessonPlanAction/duplicateLessonPlanAction/getLessonPlanTemplatesAction/saveAsTemplateAction/deleteTemplateAction/suggestKnowledgePointsAction/publishLessonPlanHomeworkAction/getKnowledgePointOptionsAction
依赖关系:
- 依赖:
shared/*、@/auth、shared/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)
- ✅ 编辑器架构升级:NodeEditor(React Flow 画布)+ NodeEditPanel(侧边内容编辑面板)+ LessonNode(自定义节点组件),支持节点拖拽、连线、画布缩放
- ⚠️
block-renderer.tsx标记为 @deprecated(已被 NodeEditor 替代,保留用于向后兼容)
架构变更(2026-06-22,本次审计修复):
- P0-1 跨模块直查修复:
publish-service.ts不再直接db.insert(examQuestions)和本地实现getStudentIdsByClassIds,改为调用exams/data-access.addExamQuestions和classes/data-access.getStudentIdsByClassIds,恢复三层架构约束- P0-2 i18n 接入:新增
shared/i18n/messages/zh-CN/lesson-preparation.json和shared/i18n/messages/en/lesson-preparation.json,注册lessonPreparation命名空间到src/i18n/request.ts,17 个组件改造为useTranslations/getTranslations- P0-3 DataScope 过滤修复:
data-access.ts的buildScopeCondition按 scope 类型精确过滤——class_taught增加subjectId IN teacher.subjects AND gradeId IN teacher.grades,grade_managed限制gradeId IN managedGrades,class_members/children仅允许查看published课案- P1-1 类型安全修复:
as never断言全部替换为类型守卫函数(isQuestionType/isV1Document/isV2Document)+validTypes数组;block-renderer.tsx使用as ExerciseBlockData/as TextStudyBlockData精确断言;constants.ts的BLOCK_TYPE_LABELS/LESSON_PLAN_STATUS_LABELS改为 i18n 键(BLOCK_TYPE_KEYS/LESSON_PLAN_STATUS_KEYS)- P1 纯函数抽取:新增
lib/document-migration.ts(migrateV1ToV2/normalizeDocument/buildInitialContent,使用类型守卫替代 as 断言)、lib/node-summary.ts(getNodeSummary + NODE_COLORS + getNodeColor,接受翻译函数注入)、lib/rf-mappers.ts(toRfNodes/toRfEdges/fromRfEdges),data-access.ts 改为从 lib/ 导入并 re-export 保持向后兼容- P1-2/P1-3 错误边界 + 骨架屏:新增
components/lesson-plan-error-boundary.tsx(LessonPlanErrorBoundary 类组件)和components/lesson-plan-skeleton.tsx(VersionListSkeleton/QuestionBankSkeleton/KnowledgePointSkeleton/LessonPlanListSkeleton)- P1-4 阻塞式 UI 修复:
alert()全部替换为sonnertoast;confirm()全部替换为AlertDialog(shadcn);window.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.tsx(LessonPlanProvider + Context + 4 个角色配置 TEACHER/ADMIN/STUDENT/PARENT + LessonPlanDataService 接口 + LessonPlanTracker 埋点接口 + useLessonPlanContextSafe/useRoleConfig 等 hooks)和services/default-data-service.ts(包装 Server Actions 为 DataService 实现),lesson-plan-list/lesson-plan-card 通过 Context 注入数据服务,useRoleConfig 控制按钮可见性- P1-8 Block 注册表:新增
config/block-registry.tsx(BLOCK_REGISTRY 配置表 + getBlockComponent/isRichTextBlock),node-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-label;inline-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/fromRfEdges(LessonPlanNode/Edge ↔ React Flow Node/Edge 映射) |
config/block-registry.tsx |
配置驱动:BLOCK_REGISTRY 注册表 + getBlockComponent/isRichTextBlock,node-edit-panel 通过配置渲染 Block |
providers/lesson-plan-provider.tsx |
Provider + Context(P1-5/P1-7/P2-4):LessonPlanProvider 注入数据服务/角色配置/埋点;定义 LessonPlanDataService 接口、4 个角色配置(TEACHER/ADMIN/STUDENT/PARENT)、ROLE_CONFIGS 注册表、LessonPlanTracker 接口 + noopTracker;hooks:useLessonPlanContextSafe(返回 null 不抛错)/useLessonPlanContext/useRoleConfig/useLessonPlanService/useLessonPlanTracker |
services/default-data-service.ts |
默认数据服务实现:createDefaultDataService() 包装 Server Actions 为 LessonPlanDataService 实现,测试可替换为 mock |
data-access.ts |
课案 CRUD + 模板查询(migrateV1ToV2/normalizeDocument/buildInitialContent 从 lib/ 导入并 re-export 保持向后兼容;buildScopeCondition 按 scope 类型精确过滤 P0-3) |
data-access-versions.ts |
版本管理(创建/查询/回滚/清理) |
data-access-templates.ts |
个人模板 CRUD |
data-access-knowledge.ts |
按知识点/题目反查课案 |
actions.ts |
课案 CRUD/版本/模板 Server Actions |
actions-publish.ts |
发布作业 Server Action |
actions-ai.ts |
AI 知识点建议 Server Action |
actions-kp.ts |
知识点选项 Server Action |
publish-service.ts |
发布作业服务(编排 homework/exams/classes,通过对方 data-access 调用,无直查跨模块表) |
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 已接入) |
components/lesson-plan-filters.tsx |
课案筛选器(i18n 已接入) |
components/lesson-plan-editor.tsx |
课案编辑器(编排 NodeEditor + NodeEditPanel,i18n 已接入) |
components/node-editor.tsx |
节点图画布(React Flow,使用 lib/rf-mappers + lib/node-summary 纯函数,i18n 已接入) |
components/node-edit-panel.tsx |
侧边内容编辑面板(配置驱动渲染 Block,通过 getBlockComponent + LessonPlanErrorBoundary 包裹,i18n 已接入) |
components/nodes/lesson-node.tsx |
自定义节点组件(使用 lib/node-summary 的 getNodeSummary/getNodeColor,i18n 已接入) |
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 已接入) |
components/version-history-drawer.tsx |
版本历史抽屉(i18n 已接入) |
components/knowledge-point-picker.tsx |
知识点选择器(i18n 已接入) |
components/question-bank-picker.tsx |
题库选择器(i18n 已接入) |
components/inline-question-editor.tsx |
内联题目编辑器(i18n 已接入) |
components/publish-homework-dialog.tsx |
发布作业对话框(i18n 已接入) |
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.reload,i18n 已接入) |
components/blocks/reflection-block.tsx |
反思 Block(被 NodeEditPanel 复用,i18n 已接入) |
第三部分:已知架构问题和技术债
3.1 P0 严重问题(必须立即修复)
P0-1:文件超 1000 行硬上限(3 个文件)
| 文件 | 行数 | 问题 | 拆分建议 |
|---|---|---|---|
classes/data-access.ts |
已拆为 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 |
已拆为 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-2:shared/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-3:dashboard 跨模块直接查询 11 张表 ✅ 已修复
dashboard/data-access.ts 的 getAdminDashboardData 原直查 sessions/users/usersToRoles/roles/classes/textbooks/chapters/questions/exams/homeworkAssignments/homeworkSubmissions。
修复方案(已实施):
- 各模块新增 dashboard stats 函数:
users/data-access.ts→getUsersDashboardStats()(userCount/activeSessionsCount/userRoleCounts/recentUsers)classes/data-access.ts→getClassesDashboardStats()(classCount)textbooks/data-access.ts→getTextbooksDashboardStats()(textbookCount/chapterCount)questions/data-access.ts→getQuestionsDashboardStats()(questionCount)exams/data-access.ts→getExamsDashboardStats(scope?)(examCount,支持 scope 过滤)homework/stats-service.ts→getHomeworkDashboardStats(scope?)(4 个计数,支持 scope 过滤)
- dashboard 改为并行调用:
Promise.all([getUsersDashboardStats(), getClassesDashboardStats(), ...]) - 返回值结构保持不变,调用方无需修改
P0-4:messaging 绕过 notifications 直接写通知 ✅ 已修复
messaging/actions.ts 原直接调用 createNotification 写 messageNotifications 表,导致用户通知偏好失效、多渠道通知无效。notifications/data-access.ts 和 in-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.tsre-export 通知 CRUD,messaging/notification-preferences.ts转为 re-export shim,messaging/types.tsre-export 通知类型 - 依赖方向变为单向:messaging → notifications(messaging 调用
sendNotificationdispatcher 发送通知)
P0-5:classSchedule 表三处写入口 ✅ 已修复
classes/data-access.ts(createClassScheduleItem 等)scheduling/actions.ts(applyAutoScheduleAction 直接 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/UpdateClassScheduleItemInput从classes/types.ts迁移到scheduling/types.ts - 所有 classSchedule DB 写入统一由 scheduling 模块管理(
insertClassScheduleItem/updateClassScheduleItemById/deleteClassScheduleItemById/replaceClassSchedule)
P0-6:proctoring 死代码与重复实现
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
recordProctoringEventAction(proctoring/actions.ts)为唯一规范路径 exam-mode-config.tsx暂保留原位(集成到考试表单属于功能新增,不在本次修复范围)
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-access:✅ 已实现 /getClassGradeIdsByClassIdsgetClassStudentsByClassId/getActiveClassStudents/getClassExists/getClassNameById/getClassNamesByIds/getActiveStudentIdsByClassId/getStudentActiveClassId/getClassesByGradeId/verifyTeacherOwnsClass/getTeacherIdForMutationsexams/data-access:✅ 已实现(getExamForHomeworkCreationgetExamIdsByGradeIds/getExamSubjectIdMap/getExamWithQuestionsForHomework/getExamSubmissionWithAnswers/getExamForProctoringCrossModule/getExamSubmissionForProctoringCrossModule/getExamSubmissionsForExam/getExamTitleById)school/data-access:✅ 已实现getSubjectOptions/getGradeOptionsusers/data-access:✅ 已实现(getUserNameByIds/getStudentInfogetUserNamesByIds/getUserWithRole/getUserBasicInfo/getUserIdsByGradeId/getCurrentStudentUser)textbooks/data-access:✅ 已实现getKnowledgePointOptionsquestions/data-access:✅ 已通过insertQuestionWithRelationscreateQuestionWithRelations供 exams 调用 /getKnowledgePointsForQuestionshomework/data-access-classes:✅ 新增 7 个函数供 classes 模块跨模块调用
P1-2:actions 层混入数据访问逻辑 ✅ 已修复
已完成修复(2026-06-17,commit 84d6636):4 个模块的 actions 层 DB 操作全部下沉到 data-access:
| 模块 | 问题 Action | 修复内容 |
|---|---|---|
| exams | updateExamAction / deleteExamAction / duplicateExamAction / getExamPreviewAction / getSubjectsAction / getGradesAction |
✅ 新增 7 个 data-access 函数(getExamCreatorId/updateExamWithQuestions/deleteExamById/duplicateExam/getExamPreview/getExamSubjects/getExamGrades),actions.ts 831→691 行,data-access.ts 374→471 行 |
| homework | createHomeworkAssignmentAction(157 行)/ startHomeworkSubmissionAction / saveHomeworkAnswerAction / submitHomeworkAction / gradeHomeworkSubmissionAction |
✅ 新建 data-access-write.ts(285 行,10 个写函数),actions.ts 387→239 行 |
| questions | createQuestionAction / updateQuestionAction / deleteQuestionAction / getKnowledgePointOptionsAction |
✅ 新增 4 个 data-access 函数(createQuestionWithRelations/updateQuestionById/deleteQuestionByIdRecursive/getKnowledgePointOptions),actions.ts 294→149 行,data-access.ts 138→260 行 |
| announcements | 所有写操作 Action | ✅ 新增 5 个 data-access 函数(insertAnnouncement/updateAnnouncementById/deleteAnnouncementById/publishAnnouncementById/archiveAnnouncementById),actions.ts 242→197 行,data-access.ts 120→171 行 |
剩余未修复模块(不在本次 P1-2 范围):
- ✅ users:
已下沉到 data-access(P1-1 修复)updateUserProfileAction直接 db.update - ✅ scheduling:
已改为调用applyAutoScheduleAction/autoScheduleAction直接 db.transaction + db.selectreplaceClassSchedule统一写入口;autoScheduleAction直查 users 表已改为通过 users data-access(P0-5 / P1-1 修复)
P1-3:auth.ts 混合 5 类职责 ✅ 已完成
src/auth.ts 原 293 行混合:NextAuth 配置 + 密码安全 DB 操作 + 角色规范化 + IP 解析 + 回调函数。
已完成拆分(auth.ts 现 193 行,仅保留 NextAuth 配置):
- ✅ 密码安全 DB 操作 →
shared/lib/password-security-service.ts(getOrCreatePasswordSecurity / recordFailedLogin / resetFailedLogin,server-only) - ✅ 角色规范化 →
shared/lib/role-utils.ts(normalizeRole / resolvePrimaryRole,纯函数) - ✅ bcrypt 哈希规范化 →
shared/lib/bcrypt-utils.ts(normalizeBcryptHash,纯函数) - ✅ IP 解析 →
shared/lib/http-utils.ts(resolveClientIp,server-only)
后续可选优化(未执行,需保持 NextAuth 配置不变原则下评估):
authorize回调可进一步拆分为checkRateLimit/checkAccountLockout/verifyPassword/loadUserRoles,使 auth.ts 降至 ≤150 行
P1-4:users/import-export.ts 四重职责 ✅ 已完成
users/import-export.ts 原 291 行混合:导入解析 + 导出 + 用户创建(含密码哈希)+ 班级注册(跨模块写 classEnrollments)。
已完成拆分(import-export.ts 现 157 行,仅保留文件解析/生成):
- ✅ 用户创建(含密码哈希、角色分配)→
user-service.ts(batchImportUsers,82 行,server-only) - ✅ 班级注册 →
class-registration.ts(registerStudentByInvitationCode,21 行,server-only) - ✅
batchImportUsers不再直写classEnrollments,改为调用classes/data-access.enrollStudentByInvitationCode - ✅
import-export.ts通过 re-exportbatchImportUsers/UserImportResult保持向后兼容(actions.ts和app/api/export/route.ts无需修改)
P1-5:notifications 反向依赖 messaging ✅ 已完成
notifications/data-access.ts 和 in-app-channel.ts 原反向依赖 messaging 模块的偏好和 createNotification。
已完成修复(与 P0-4 一并解决):
- ✅ 将
messageNotifications和notificationPreferences表所有权移交 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导入resolveClientIp和getUserAgent,删除本地重复实现 - ✅
auth.ts已在 P1-3 中改用resolveClientIp - ✅ 四处实现统一,消除不一致风险(
resolveClientIp取x-forwarded-for第一段,更准确)
3.3 P2 代码质量问题(机会修复)
| 序号 | 问题 | 模块 |
|---|---|---|
| P2-1 | exams/ai-pipeline.ts 857 行,混合 4 类职责 |
exams |
exams/actions.ts 832 行(超 800 建议) |
exams | |
shared/lib/ai.ts 218 行,混合 5 类职责ai/ 目录) |
shared | |
onboarding-gate.tsx 业务逻辑泄漏到 sharedmodules/onboarding/,改用独立路由 + Server Action + middleware 重定向) |
shared/onboarding | |
| P2-5 | global-search.tsx 业务类型硬编码在 shared |
shared |
proxy.ts 硬编码权限字符串,未复用 Permissions 常量Permissions 常量) |
proxy | |
useA11yId Hook 错放在 lib/ 而非 hooks/use-aria-live.ts 已在 hooks/ 目录) |
shared | |
schema.ts 分节编号混乱(section 12 出现在 14b 之后) |
shared/db | |
| P2-9 | audit/actions.ts Excel 导出逻辑内联 |
audit |
| P2-10 | school 模块审计日志不一致(仅 school 实体记录) | school |
announcements 死代码 void wasPublished |
announcements | |
announcements 权限模式不一致(requireAuth vs requirePermission) |
announcements | |
| P2-13 | files try-catch 吞错误 |
files |
elective runLottery 使用 Math.random |
elective | |
elective selectCourse FCFS 并发超卖风险 |
elective | |
| P2-16 | diagnostic 班级报告 studentId 字段复用 |
diagnostic |
layout 用权限反推角色app-sidebar.tsx 改用 hasRole() 判断角色;N3 新增多角色切换机制:SidebarContext.currentRole/setCurrentRole + 侧边栏底部 Select 下拉) |
layout | |
scheduling/actions.ts 末尾 re-export data-accessdata-access 导入) |
scheduling | |
| P2-19 | ExamAssembly / ExamPreviewQuestionEditor 10 个 props |
exams |
| P2-20 | homework/data-access.getDemoStudentUser 使用 auth() 而非 auth-guardusers/data-access.getCurrentStudentUser,6 个 student 页面改用 users 模块;elective 页面改用 getAuthContext();homework 保留 re-export 向后兼容) |
homework |
3.4 解耦优先级路线图
立即执行(P0)
拆分✅ 已完成(拆为 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行)classes/data-access.ts(2104 行 → 按职责拆 3-4 个文件)拆分✅ 已完成(拆为 data-access.ts 598行 + stats-service.ts 425行)homework/data-access.ts(1038 行 → 分离排名逻辑)修复✅ 已完成(新增shared/lib↔auth循环依赖shared/lib/session.ts单一入口,3 个 shared/lib 文件改为通过 getSession 获取 session,dynamic import 打破静态循环)dashboard 改为通过各模块 data-access 获取数据✅ 已完成(P0-3 修复:并行调用各模块 dashboard stats 函数)messaging 写通知改为通过 notifications dispatcher✅ 已完成(P0-4 / P1-5 修复:通知 CRUD 和偏好迁移至 notifications 模块,messaging 通过 dispatcher 发送通知)统一 classSchedule 写入口到 scheduling 模块✅ 已完成(P0-5 修复:classSchedule 写函数从 classes/data-access-schedule.ts 迁移至 scheduling/data-access-class-schedule.ts,classes 模块仅保留读函数)集成 proctoring/exam-mode-config 到考试表单部分完成(P0-6 修复:删除重复的 /api/proctoring/event REST 路由,Server Action 为唯一规范路径;exam-mode-config.tsx 集成属于功能新增,暂保留原位)
短期执行(P1)
actions 层移除直接 DB 操作(exams/homework/questions/announcements/users/scheduling)✅ 已完成(全部 6 个模块均已修复)拆分✅ 已完成(4 个辅助函数组迁移至 shared/lib,auth.ts 保留 NextAuth 配置)auth.ts拆分✅ 已完成(拆为 import-export.ts 157行 + user-service.ts 82行 + class-registration.ts 21行,班级注册改为调用 classes/data-access)users/import-export.ts消除 notifications → messaging 反向依赖✅ 已完成(P0-4 / P1-5 修复:通知表所有权迁移至 notifications,in-app-channel 改为静态导入)提取✅ 已完成(新增shared/lib/http-utils.ts统一 IP 提取getUserAgent,三个 logger 统一复用resolveClientIp/getUserAgent)各模块暴露跨模块查询接口(见 P1-1)✅ 已完成(所有跨模块直查已改为通过对方 data-access 接口)
中期执行(P2)
建立模块间数据访问规范(通过对方 data-access 或导出查询函数)✅ 已完成(P1-1 修复)✅ 已完成(P2-8 修复:重新编号为连续 1-24,消除 8b/14b/乱序问题)schema.ts按业务域分节- 拆分
exams/ai-pipeline.ts 拆分✅ 已完成(P2-2,commit 6588f74,拆分为shared/lib/ai.tsai/目录 6 个文件,原 ai.ts 保留为重导出)- shared 层业务逻辑下沉到 modules 层
- 代码质量问题逐项修复(✅ 大部分已修复: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 | 其他 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 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 | - | - | - | - | - | - | ✅scheduling(P0-5:data-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 |
附录 B:关键参数影响链
userId
- 由
auth.tsJWT callback 从users表查询产生,存入 JWT - 通过
session.user.id传递到所有 Server/Client Components - 通过
getAuthContext().userId传递到所有 Server Actions - 在
auth-guard.ts中用于查询usersToRoles(角色)和classSubjectTeachers/grades(DataScope) - 在
exams/actions.ts中作为creatorId写入exams表 - 在
homework/actions.ts中作为creatorId写入homeworkAssignments表 - 在
classes/data-access.ts中查询getTeacherClasses(teacherId)/getGradeManagedClasses(userId) - 在
elective/actions.ts中作为teacherId/studentId用于选课过滤
examId
- 由
exams/actions.createExamAction产生(CUID2),写入exams表 - 被
exams/data-access.getExamById(id)读取 - 被
exams/actions的updateExamAction/deleteExamAction/duplicateExamAction用于定位考试 - 传入
homework/actions.createHomeworkAssignmentAction的sourceExamId参数 - 在
homeworkAssignments表中作为外键关联到源考试 - 被
homework/data-access.getHomeworkAssignmentAnalytics用于追溯作业来源
classId
- 由
classes/actions的createTeacherClassAction/createAdminClassAction产生 - 被
classes/data-access.getClassStudents(classId)读取学生列表 - 被
classes/data-access.getClassSchedule(classId)读取课表 - 被
classes/data-access.getClassHomeworkInsights(classId)读取作业洞察(✅ P0-7 已修复:通过homework/data-access-classes获取数据) - 被
homework/data-access.getHomeworkAssignments({ classId })过滤作业列表 - 在
auth-guard.ts中通过classSubjectTeachers查询教师关联的 classIds,构建DataScope.class_taught
permission
- 由
shared/types/permissions.ts的Permissions常量定义(61 个权限点) - 在
shared/lib/permissions.ts中通过ROLE_PERMISSIONS映射角色到权限列表 - 在
auth.tsJWT callback 中通过resolvePermissions(roleNames)合并多角色权限,存入 JWT - 在
proxy.tsmiddleware 中通过token.permissions检查路由访问权限 - 在
shared/lib/auth-guard.ts中通过requirePermission(permission)在 Server Action 层断言权限 - 在
shared/hooks/use-permission.ts中通过hasPermission(permission)在客户端组件中条件渲染 - 在
layout/config/navigation.ts中作为NavItem.permission字段过滤侧边栏菜单
DataScope
- 由
auth-guard.ts的resolveDataScope(userId, roles)根据用户角色和 DB 关系动态计算 - 支持类型:
all/grade_managed/class_taught/class_members/children/owned - 传递到
exams/homework/grades/attendance/elective/dashboard的 data-access 进行行级过滤 - 对 parent 角色,查询
parentStudentRelations表构建{ type: "children", childrenIds: string[] } - 在
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<GradeRecord[]>>
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 }): Promise<GradeRecord[]>
getStudentGradeSummary(studentId: string): Promise<StudentGradeSummary>
getClassRanking(classId: string, examId?: string): Promise<ClassRanking[]>
// 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.json的routes节点 - 新增数据库表:更新
shared/db/schema.ts分节 +005的dbTables节点
完整路由表、DevOps 脚本、E2E 测试等信息见
005_architecture_data.json。