- Update architecture impact map, data, feature checklist, gap audit - Add audit reports for dashboard, exam-homework, grades-diagnostic, settings-profile, textbooks - Update bug reports (admin, teacher, lesson-preparation, others, shared) - Update coding standards, DR plan, design docs, and README
265 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)handleActionError(e)/safeActionCall(action, options?)/safeJsonParse(json, msg)/safeParseDate(v, field)/safeParseNumber(v, field)/escapeLikePattern(input)— Server Action 错误处理与客户端调用包装(2026-06-23 审计修复新增:位于lib/action-utils.ts,统一所有 Server Action catch 块错误处理,避免内部错误消息暴露给客户端)BusinessError/NotFoundError/ValidationError— 错误类(2026-06-23 审计修复新增:位于lib/action-utils.ts,已知业务错误基类,message 可安全返回客户端)
共享组件导出(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 分桶着色 + defs 自定义 SVG 图案) | 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提取) - Stats-service(V3-8 新增):
getExamAnalytics(cache 包装,聚合考试所有作业的已批改提交,计算平均分/及格率/分数段分布/逐题错误率与难度等级,对标智学网考试分析)+ExamAnalyticsSummary类型 - Components(V3-8 新增):
ExamAnalyticsDashboard(考试分析仪表盘:汇总卡片+分数段分布+逐题分析表)
依赖关系:
- 依赖:
shared/*、@/auth、questions(✅ P0-1 已修复:通过 data-access.createQuestionWithRelations)、classes(✅ P0-2 已修复:通过 data-access.getClassGradeIdsByClassIds)、school(✅ P1-1 已修复:通过 school data-access.getSubjectOptions/getGradeOptions)、homework(V3-8 新增:stats-service 通过homework/data-access.getHomeworkAssignmentsByExamId/getGradedSubmissionsByExamId获取作业与提交数据,合理跨模块调用) - 被依赖:
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→?? []) - ✅ V3-5/V3-8/V3-12:
exam-actions.tsx增强:V3-5 角色化菜单(按角色显示不同操作项)、V3-8 新增 analytics 菜单项(BarChart3 图标,跳转/teacher/exams/[id]/analytics)、V3-12 移动端触摸目标尺寸优化
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
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 跨模块通信) |
stats-service.ts |
- | V3-8 新增:考试分析数据聚合(getExamAnalytics + ExamAnalyticsSummary 类型) |
types.ts |
31 | 类型定义 |
hooks/use-exam-preview.ts |
295 | 预览 Hook |
utils/normalize-structure.ts |
57 | v3 新增:exam.structure 运行时校验与归一化(从 build/page.tsx 提取) |
components/exam-analytics-dashboard.tsx |
- | V3-8 新增:考试分析仪表盘组件 |
components/exam-actions.tsx |
- | V3-5/V3-8/V3-12 增强:角色化菜单+analytics 链接+移动端触摸优化 |
components/* |
19 文件 | 考试表单/组卷/预览/分析组件 |
2.3 homework(作业模块)
职责:作业全生命周期(创建/发布/作答/批改/分析)。
导出函数:
- Actions:
createHomeworkAssignmentAction/startHomeworkSubmissionAction/saveHomeworkAnswerAction/submitHomeworkAction/gradeHomeworkSubmissionAction/batchAutoGradeSubmissionsAction(V3-7 新增:批量自动批改,HOMEWORK_GRADE 权限+非管理员仅可批改自己创建的作业)(✅ 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 使用)/getHomeworkAssignmentsByExamId(V3-8 新增:按考试 ID 查作业+目标/提交/批改计数)/getGradedSubmissionsByExamId(V3-8 新增:按考试 ID 查已批改提交,按学生去重)/getStudentSubmissionResult(V3-9 新增:查学生指定作业最新提交,用于结果页)/getStudentExamResults(V3-11 新增:查学生考试结果列表,供家长端展示) - Data-access-classes:
getAssignmentIdsForStudents/getHomeworkAssignmentsWithSubject/getHomeworkAssignmentsByIds/getAssignmentTargetCounts/getHomeworkSubmissionsForStudents/getPublishedHomeworkAssignmentsWithSubject/getHomeworkSubmissionsForAssignments(P0-7 新增,供 classes 模块跨模块调用;✅ P1-1 已修复:内部不再直查 exams/subjects 表,改为调用exams/data-access.getExamSubjectIdMap+school/data-access.getSubjectNameMapByIds) - Data-access-write:11 个写操作函数(P1-2 新增 10 个从 actions 下沉 + V3-7 新增
batchAutoGradeSubmissions) - Stats-service:
getTeacherGradeTrends/getHomeworkAssignmentAnalytics/getStudentDashboardGrades(从 data-access.ts re-export 以保持向后兼容) - Components(V3-7/V3-9 新增):
HomeworkBatchGradingView(批量批改视图:勾选+一键批改+toast 反馈)/HomeworkSubmissionResult(提交后即时反馈:分数汇总+对错分布+错题预览)
依赖关系:
- 依赖:
shared/*、@/auth、exams(✅ P1-1 已修复:通过 exams data-access.getExamIdsByGradeIds/getExamSubjectIdMap/getExamWithQuestionsForHomework/getExamForProctoringCrossModule)、classes(✅ P1-1 已修复:通过 classes data-access.getStudentIdsByClassId 等 7 个函数)、school(✅ P1-1 已修复:通过 school data-access.getSubjectOptions/getSubjectNameMapByIds)、users(✅ P1-1 已修复:通过 users data-access.getUserWithRole/getUserNamesByIds) - 被依赖:
dashboard(通过 data-access,合理)、parent(通过 data-access,合理;V3-11 新增getStudentExamResults供 parent 调用)、classes(✅ P0-7 已修复:classes 通过homework/data-access-classes获取作业数据,不再反向直查 homework/exams 表)、exams(V3-8 新增:exams/stats-service.getExamAnalytics调用getHomeworkAssignmentsByExamId/getGradedSubmissionsByExamId,合理跨模块调用)
已知问题:
- ✅ 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;data-access-classes.ts内部 JOIN exams+subjects 也改为调用exams/data-access.getExamSubjectIdMap+school/data-access.getSubjectNameMapByIds,彻底消除跨模块直查 - ✅ P0-竞品已修复:新增
hooks/use-exam-countdown.ts倒计时 Hook(对标智学网/猿题库),homework-take-view.tsx集成限时/监考模式倒计时显示与到时自动提交;data-access.ts的getStudentHomeworkTakeData新增examModeConfig+startedAt字段供客户端计算截止时间 - ✅ P1-2 已修复:
DB 操作已下沉到actions.ts多处直接 DB 操作(createHomeworkAssignmentAction157 行)data-access-write.ts,actions.ts 现 239 行 - ✅ V3-7:新增
batchAutoGradeSubmissionsAction+batchAutoGradeSubmissions+HomeworkBatchGradingView,提交列表页接入批量批改 - ✅ V3-8:新增
getHomeworkAssignmentsByExamId+getGradedSubmissionsByExamId,供 exams 模块跨模块调用 - ✅ V3-9:新增
getStudentSubmissionResult+HomeworkSubmissionResult+ 路由/student/learning/assignments/[assignmentId]/result,homework-take-view.tsx提交后跳转结果页 - ✅ V3-12:
homework-take-view.tsx移动端触摸目标尺寸优化 - ✅ 2026-06-23 审计修复:data-access 层 4 个查询函数新增
scope?: DataScope参数(getHomeworkAssignments/getHomeworkAssignmentById/getHomeworkSubmissions/getHomeworkAssignmentReviewList),教师仅查看自己创建的作业,学生/家长仅查看相关作业;Server Action catch 块改用handleActionError统一错误处理
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
data-access.ts |
598+ | 作业 CRUD + 学生视角 + 批改(含 re-export stats 函数;V3-8/V3-9/V3-11 新增 4 个查询函数) |
data-access-write.ts |
285+ | 作业写操作(P1-2 新增 10 个写函数从 actions 下沉;V3-7 新增 batchAutoGradeSubmissions) |
data-access-classes.ts |
240+ | 跨模块查询封装(P0-7 新增;✅ P1-1 已修复:内部通过 exams/school data-access 获取考试科目信息,不再直查 exams/subjects 表) |
stats-service.ts |
425 | 统计分析(教师趋势/作业分析/学生仪表盘成绩) |
actions.ts |
239+ | 6 个 Server Action(P1-2 已修复,无直接 DB 操作;V3-7 新增 batchAutoGradeSubmissionsAction) |
types.ts |
186 | 类型定义 |
schema.ts |
29 | Zod 校验 |
components/homework-batch-grading-view.tsx |
- | V3-7 新增:批量批改视图(use client) |
components/homework-submission-result.tsx |
- | V3-9 新增:提交后即时反馈页 |
components/homework-take-view.tsx |
- | V3-9/V3-12 增强:提交后跳转结果页+移动端触摸优化;✅ P0-竞品:集成限时/监考模式倒计时(useExamCountdown)+ 到时自动提交 |
hooks/use-exam-countdown.ts |
122 | P0-竞品新增:考试倒计时 Hook(每秒更新、紧急状态高亮、到时回调自动提交) |
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(14 个,均为写操作;读操作由 RSC 页面直接调用 data-access):
createTextbookAction/updateTextbookAction/deleteTextbookAction/createChapterAction/updateChapterContentAction/deleteChapterAction/reorderChaptersAction/createKnowledgePointAction/updateKnowledgePointAction/deleteKnowledgePointAction/getKnowledgePointsByChapterAction/getKnowledgeGraphDataAction(✅ Task 7 新增:知识图谱数据查询,支持 structure/student-mastery/class-mastery 三种视图)/createPrerequisiteAction(✅ Task 7 新增:声明前置依赖,含循环检测)/deletePrerequisiteAction(✅ Task 7 新增:删除前置依赖) - Data-access:
getTextbooks/getTextbookById/getChaptersByTextbookId/getKnowledgePointsByChapterId/getKnowledgePointsByTextbookId/createTextbook/updateTextbook/deleteTextbook/createChapter/updateChapterContent/deleteChapter/createKnowledgePoint/updateKnowledgePoint/deleteKnowledgePoint/reorderChapters/getTextbooksDashboardStats/getKnowledgePointOptions(跨模块接口,供 questions 调用)/getTextbooksWithScope(P1-1 新增:按数据范围获取教材列表,学生端强制按年级过滤)/verifyChapterBelongsToTextbook(P0-4 新增:资源归属校验)/verifyKnowledgePointBelongsToTextbook(P0-4 新增:资源归属校验)/verifyKnowledgePointBelongsToChapter(P0-4 新增:校验知识点是否属于指定章节)/createPrerequisite(✅ Task 7 新增:创建前置依赖)/deletePrerequisite(✅ Task 7 新增:删除前置依赖)/getPrerequisiteEdgesForTextbook(✅ Task 7 新增:获取教材下所有前置依赖边,用于循环检测)/getSubjectLabelKey/getGradeLabelKey(i18n 标签键) - Data-access-graph(✅ Task 5 新增,图谱只读查询):
getKnowledgePointsWithRelations(知识点+依赖+题目数聚合查询)/getStudentKpMastery(学生掌握度)/getClassKpMastery(班级平均掌握度)/getPrerequisitesForKp/getSuccessorsForKp - Constants(✅ 新增):
SUBJECTS/GRADES/SUBJECT_COLORS/getSubjectColor/getSubjectLabelKey/getGradeLabelKey- ✅ v1 测试修复:
SUBJECTS新增Chinese学科、GRADES新增Grade 1/Grade 2,与 seed 数据对齐 - ✅ v1 测试修复:
SUBJECT_COLORS新增Chinese主题色(rose)
- ✅ v1 测试修复:
- Utils(✅ 新增,纯函数 + 单测):
sortChapters/buildChapterTree/buildChapterIndex/findChapterParent/filterKnowledgePointsByChapter/normalizeOptional/highlightKnowledgePoints/hasCycleAfterAddingEdge(Task 4 新增:循环依赖检测,DFS 算法) - Graph-layout(✅ 新增,纯函数 + 单测):
computeGraphLayout - Components(✅ Task 13 新增):
GraphNodeDetailPanel— 知识图谱节点详情侧边面板,展示知识点名称/描述/掌握度/关联题目/前置后置知识点,支持跳转与前置关系增删 - Analytics(✅ 新增):
TextbookAnalytics/TextbookAnalyticsProvider/useTextbookAnalytics
依赖关系:
- 依赖:
shared/*、@/auth、@xyflow/react(知识图谱可视化)、@dagrejs/dagre(图谱分层布局算法) - 被依赖:
questions(✅ P1-1 已修复:通过 textbooks data-access)、exams(通过类型)、dashboard(通过 data-access,P0-4 已修复) - ✅ UI 层跨模块依赖已解耦:
textbooks/components/knowledge-point-dialogs.tsx不再直接 import questions 模块,改为通过 render prop 注入创建题目入口
已知问题:
- ✅ 无跨模块 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通过 render prop 解耦,不再直接 import questions 模块 - ✅ P0 前端权限硬编码已修复:改用
usePermission().hasPermission() - ✅ P0 i18n 已基本接入:
chapter-sidebar-list.tsx/actions.ts/section-error-boundary.tsx(默认值改英文)均已接入 next-intl - ✅ P1 Server Action 资源归属校验已修复:新增
verifyChapterBelongsToTextbook/verifyKnowledgePointBelongsToTextbook - ✅ P1 data-access 数据范围过滤已修复:新增
getTextbooksWithScope,学生端强制按年级过滤 - ✅ P1 Error Boundary 已补齐:新增
section-error-boundary.tsx - ✅ P1 知识点列表/弹窗重复实现已清理:移除无调用方的
knowledge-point-panel.tsx - ✅ P1 学科/年级选项统一:抽取到
constants.ts(SUBJECTS/GRADES/SUBJECT_COLORS) - ✅ v1 测试修复:
textbook-reader.tsx移除SheetTrigger(错误置于Sheet外部导致DialogTrigger must be used within Dialog),改用受控Button+onClick打开移动端抽屉 - ✅ v1 测试修复:新增
teacher-textbook-reader.tsx客户端包装组件,解决 Server Component 向 Client Component 传递函数 prop(renderQuestionCreator)违反 Next.js 序列化约束的问题 - ✅ v1 测试修复:
scripts/seed.ts教材 subject/grade 改为英文 value(与constants.ts对齐),消除 i18nMISSING_MESSAGE错误 - ✅ P1 纯逻辑已导出并补单测:新增
utils.ts/graph-layout.ts及对应.test.ts - ⚠️ i18n 覆盖率约 95%(
chapter-sidebar-list已接入,actions.ts已接入,section-error-boundary默认值已改英文) - ⚠️ 类型断言残留 3 处
as string - ⚠️ P2 图谱方向键导航未实现
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
actions.ts |
515 | 14 个 Server Action(写操作,含 Zod 校验 + 资源归属校验 + 知识图谱查询/前置依赖管理 + class-mastery 视图) |
data-access.ts |
662 | 教材/章节/知识点 CRUD + 跨模块查询接口 + 资源归属校验 + 数据范围过滤 + 前置依赖 CRUD |
data-access-graph.ts |
207 | 知识图谱只读查询(✅ Task 5 新增:知识点关联聚合、学生/班级掌握度、前置后置知识点,标记 server-only;v2 修复:前置依赖双向 IN 过滤防跨教材污染) |
types.ts |
106 | 类型定义(含知识图谱类型:GraphViewMode/MasteryInfo/KpWithRelations/GraphNodeData/GraphEdgeData/KnowledgeGraphData/MasteryLevel) |
schema.ts |
81 | Zod 校验(含 CreatePrerequisiteSchema/DeletePrerequisiteSchema;v2 修复:refine 消息改为英文) |
constants.ts |
96 | 学科/年级常量与颜色映射(✅ 新增;v1 测试修复:新增 Chinese/Grade 1/Grade 2) |
utils.ts |
225 | 章节树构建/排序/查找等纯函数 + 循环检测(✅ 新增,含单测) |
graph-layout.ts |
121 | 知识图谱布局计算纯函数(✅ Task 8 重写为 dagre 集成,输出 React Flow 格式,含单测) |
analytics.tsx |
43 | 教材分析 Context/Provider/Hook(✅ 新增) |
hooks/use-knowledge-point-actions.ts |
49 | 知识点操作门面 Hook(v2 拆分:组合 useKpDialogState + useKpCrud,对外 API 不变) |
hooks/use-kp-dialog-state.ts |
38 | 知识点对话框状态管理 Hook(v2 新增:从 use-knowledge-point-actions 拆分) |
hooks/use-kp-crud.ts |
122 | 知识点 CRUD 操作 Hook(v2 新增:从 use-knowledge-point-actions 拆分) |
hooks/use-text-selection.ts |
57 | 文本选区捕获 Hook |
hooks/use-graph-data.ts |
76 | 知识图谱数据加载 Hook(✅ Task 11 新增:派生值模式避免 effect 中 setState;v2 修复:区分 isLoading/isRefreshing 避免 UI 闪烁) |
components/knowledge-graph.tsx |
376 | React Flow 知识图谱主组件(✅ Task 10/15 重写:全书视图 + 搜索高亮 + 关联节点高亮 + 章节着色;v2 新增:添加/删除前置依赖 Dialog) |
components/graph-kp-node.tsx |
92 | React Flow 自定义节点(✅ Task 9 新增:知识点名称+题目数徽章+掌握度进度条;v2 修复:节点宽度常量化 NODE_WIDTH) |
components/graph-prerequisite-edge.tsx |
48 | React Flow 自定义边(✅ Task 9 新增:虚线+箭头表示前置依赖;v2 修复:GraphEdgeData 类型安全转换) |
components/graph-toolbar.tsx |
93 | 图谱工具栏(✅ Task 9 新增:视图模式切换 + 搜索 + 重置视图;v2 新增:isRefreshing 轻量指示器) |
components/graph-node-detail-panel.tsx |
181 | 节点详情侧边面板(✅ Task 13 新增:描述/掌握度/关联题目/前置后置列表;v2 修复:移除未使用 textbookId prop) |
components/chapter-sidebar-list.tsx |
342 | 章节侧边栏列表(v2 修复:canEdit 默认 false + orderUpdateFailed i18n key) |
components/section-error-boundary.tsx |
71 | 章节内容错误边界(v2 修复:默认值改为空字符串 + 条件渲染) |
components/* |
16 文件 | 教材编辑/知识图谱组件(新增 section-error-boundary.tsx、5 个 graph-* 组件;teacher-textbook-reader.tsx 已移至 app 层) |
2.6 grades(成绩模块)— 标杆模块(拆分范例)
职责:成绩分析(录入/查询/统计/导出/趋势对比分析)。
导出函数:
- Actions:
getGradeRecordsAction/createGradeRecordAction(v4-P1-6 增强:成绩录入后通知学生和家长,调用notifyGradeEntered)/updateGradeRecordAction/deleteGradeRecordAction/exportGradesAction(v4-P1-12 增强:新增可选studentId参数,支持按学生导出,家长视角调用exportStudentGradeRecordsToExcel,校验 studentId 属于家长子女)/getGradeTrendAction/getClassComparisonAction/getSubjectComparisonAction/getGradeDistributionAction/getClassRankingAction/getRankingTrendAction/getGradeRecordByIdAction/getClassGradeStatsAction/getStudentGradeSummaryAction/batchCreateGradeRecordsAction(v4-P1-6 增强:批量成绩录入后通知学生和家长)/assertClassInScope(✅ P3 新增导出:班级 scope 校验工具,供 actions-analytics 复用)/saveGradeDraftAction/getGradeDraftAction/deleteGradeDraftAction(✅ v3-P2 新增:成绩录入草稿 Server Actions,分别使用 GRADE_RECORD_MANAGE/GRADE_RECORD_READ/GRADE_RECORD_MANAGE 权限) - Data-access:
getGradeRecords/getStudentGradeSummary/getClassRanking/getClassStudentsForEntry/getClassGradeStats/getClassGradeStatsWithMeta/getGradeTrend/getClassComparison/getSubjectComparison/getGradeDistribution/getRankingTrend/PaginatedGradeRecords(✅ P3 新增:分页结果接口{ records, total })/saveGradeDraft/getGradeDraft/deleteGradeDraft(✅ v3-P2 新增:成绩录入草稿 CRUD,upsert + 24 小时过期)/getExamOptionsForGrades/getSchoolWideGradeSummary(✅ v3-P2 新增:考试选项查询 + 全校各年级成绩汇总,管理员视图按年级聚合平均分/及格率/优秀率/学生数/班级数,加权平均计算全校汇总) - Types(✅ v3-P2 新增):
SchoolWideGradeSummaryItem(全校汇总按年级聚合项:gradeId/gradeName/schoolName/classCount/studentCount/averageScore/passRate/excellentRate/recordCount)/SchoolWideGradeSummary(全校汇总:grades 数组 + totals 汇总对象)/GradeDraftData(草稿数据接口:{ scores: Record<string, string>, timestamp: number },位于 data-access.ts) - Lib(✅ P1-2 新增,✅ P3 更新签名,✅ P3-26 拆分):
toNumber/normalize(位于lib/grade-utils.ts);buildScopeClassFilter(scope, currentUserId?)(P3-26 从 grade-utils.ts 迁移至lib/scope-filter.ts,P3 修复:class_membersscope 内置 studentId 过滤,需传入 currentUserId 参数) - Stats-service(✅ P1-1 新增):
computeGradeStats/computeAverageScore/buildGradeTrendPoints/computeTrendAverage/computeClassComparisonStats/computeSubjectComparisonStats/computeGradeDistribution/buildRankingTrendPoints(从 3 个 data-access 文件抽取的纯函数,使数据层专注 DB I/O,统计逻辑可独立测试) - Export(✅ v4-P1-12 新增):
exportGradeRecordsToExcel/exportClassGradeReportToExcel/exportStudentGradeRecordsToExcel(v4-P1-12 新增:导出单个学生成绩单家长视角,仅含成绩明细 + 个人统计,不含班级数据,scope 为 children 自动按 studentId 过滤)/formatDateForFile(已迁移至 shared/lib/utils) - Components(✅ P1-5 新增):
WidgetBoundary(Error Boundary + Suspense + Skeleton 组合,含 a11y 属性)/SchoolWideSummaryCard(✅ v3-P2 新增:管理员全校成绩汇总卡片,4 个统计卡片 + 各年级对比表格)/ScoreCell(✅ v4-P1-7 新增:成绩单元格组件,根据得分率着色——红<60%/黄60-84%/绿≥85%,使用语义化 Tailwind 类名避免动态拼接,fullScore<=0 时不着色)
依赖关系:
- 依赖:
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) - ✅ P3 修复(2026-06-22):
score 字段添加schema.ts缺少输入边界约束.max(1000),records 数组添加.max(500),查询 schema 补全 studentId/semester/examId 字段 - ✅ P3 修复(2026-06-22):
新增buildScopeClassFilter的class_membersscope 未应用 studentId 过滤currentUserId参数,class_membersscope 内置eq(gradeRecords.studentId, currentUserId)过滤 - ✅ P3 修复(2026-06-22):
改为 DB 层分页(limit/offset),并行查询总数与当前页数据,返回getGradeRecords内存分页(全量查询后切片)PaginatedGradeRecords { records, total } - ✅ P3 修复(2026-06-22):
包裹batchCreateGradeRecords非原子操作db.transaction确保原子性 - ✅ P3 修复(2026-06-22):
新增存在性检查,不存在时抛updateGradeRecord/deleteGradeRecord未检查记录是否存在NotFoundError("成绩记录") - ✅ P3 修复(2026-06-22):
所有函数新增getClassGradeStats/getStudentGradeSummary/getClassRanking/getClassStudentsForEntry/getClassGradeStatsWithMeta缺少 scope 过滤scope?/currentUserId?参数,应用buildScopeClassFilter;getStudentGradeSummary/getClassStudentsForEntry对class_taughtscope 校验学生/班级归属 - ✅ P3 修复(2026-06-22):
相同平均分获得相同名次,下一名次跳过占位getClassRanking未处理并列排名 - ✅ P3 修复(2026-06-22):
应用getClassComparison/getRankingTrend缺少 scope 过滤buildScopeClassFilter;getRankingTrend对class_taughtscope 校验 - ✅ P3 修复(2026-06-22):
actions.ts/actions-analytics.ts catch 块直接返回改用e.messagehandleActionError(e)统一错误处理;batchCreateGradeRecordsAction使用safeJsonParse解析 JSON - ✅ P3 修复(2026-06-22):
actions-analytics.ts 缺少 class scope 校验getGradeTrendAction/getSubjectComparisonAction/getGradeDistributionAction新增assertClassInScope校验 - ✅ P3 修复(2026-06-22):
组件直接 try/catch 调用 Server Action改用safeActionCall包装器(onError/onFinally 回调);batch-grade-entry.tsxlocalStorage 访问包裹typeof window !== "undefined"检查;区分"未录入"与"录入 0" - ✅ P3 修复(2026-06-22):
新增日期有效性检查与grade-trend-card.tsx排序未处理 NaN 日期、除法未检查 fullScore > 0fullScore > 0守卫 - ✅ P3 修复(2026-06-22):
页面文件缺少 scope 传递teacher/grades/page.tsx 使用 DB 层分页;analytics/page.tsx 添加 EmptyState;entry/page.tsx 与 stats/page.tsx 过滤 class_taught scope 班级;student/grades/page.tsx 与 parent/grades/page.tsx 传递ctx.dataScope到 data-access - ✅ v3-P2 改进(2026-06-23):新增
grade_drafts表(成绩录入草稿,userId+classId+subjectId+type 唯一键,content JSON 存储 scores,24 小时过期,schema.ts 第 1444-1469 行);新增saveGradeDraft/getGradeDraft/deleteGradeDraftdata-access 函数 + 对应 3 个 Server Actions,支持成绩批量录入草稿服务端持久化 - ✅ v3-P2 改进(2026-06-23):新增
getExamOptionsForGradesdata-access 函数,获取指定班级/科目下有成绩记录的考试列表;AnalyticsFilters组件新增exams/currentExamId/currentSemesterprops,添加学期和考试筛选 ChipNav;teacher/grades/analytics/page.tsx 新增 semester/examId 搜索参数解析 - ✅ v3-P2 改进(2026-06-23):
getGradeTrend/getGradeDistribution/getSubjectComparison/getClassComparison均新增semester和examId可选参数,支持按学期和考试筛选分析 - ✅ v3-P2 改进(2026-06-23):新增
getSchoolWideGradeSummarydata-access 函数 +SchoolWideSummaryCard组件 +SchoolWideGradeSummary/SchoolWideGradeSummaryItem类型,管理员全校成绩汇总视图(按年级聚合平均分/及格率/优秀率/学生数/班级数,加权平均计算全校汇总);admin/school/grades/insights/page.tsx 顶部新增 SchoolWideSummaryCard - ✅ v3-P2 改进(2026-06-23):parent/grades/page.tsx 为每个子女并行查询
getClassAverageTrend,渲染 GradeTrendCard - ✅ v3-P3-4 改进(2026-06-23):GradeTrendCard 新增日期范围选择器(全部/近7天/近30天/近90天),通过 nuqs
trendRangeURL 参数持久化,useEffect 中计算截止时间戳避免渲染阶段调用 Date.now() - ✅ v4-P3-4 改进(2026-06-23):GradeDistributionChart 色盲友好双重编码——每个分数段使用不同 SVG pattern(条纹/点状/交叉线/反向条纹/网格)+ 颜色,
SimpleBarChart新增defsprop 支持自定义 SVG 图案 - ✅ P3 修复(2026-06-23):
P3-26 将lib/grade-utils.ts72 行超 40 行工具函数上限buildScopeClassFilter迁移至lib/scope-filter.ts,grade-utils.ts 仅保留 toNumber/normalize(20 行) - ✅ P3 修复(2026-06-23):
P3-10 移除 export 关键字,改为内部函数stats-service.tscreateDefaultBuckets 不必要导出 - ✅ P3 修复(2026-06-23):
P3-24 新增 isGradeTrendType 类型守卫函数替代 as 断言stats-service.tsbuildGradeTrendPoints 使用 as 断言 - ✅ P3 修复(2026-06-23):
P3-6 删除局部 avg,复用 computeAverageScoreexport.ts局部 avg 函数与 stats-service.computeAverageScore 重复 - ✅ P3 修复(2026-06-23):
P3-7 改用 next-intl getTranslations,新增 export.sheets/columns/metrics i18n 键(zh-CN/en 同步)export.tsTYPE_LABELS 硬编码中文 - ✅ v4-P1-6 改进(2026-06-23):
createGradeRecordAction/batchCreateGradeRecordsAction成绩录入后通知学生和家长(调用notifyGradeEntered,内部使用parent/data-access.getParentIdsByStudentIds批量查询家长 ID),通知失败不阻断成绩录入 - ✅ v4-P1-7 改进(2026-06-23):新增
ScoreCell组件(components/score-cell.tsx),根据得分率着色(红<60%/黄60-84%/绿≥85%),使用语义化 Tailwind 类名避免动态拼接 - ✅ v4-P1-10 改进(2026-06-23):
grade-record-list.tsx使用ScoreCell替代纯文本分数展示 + 表格overflow-x-auto水平滚动 - ✅ v4-P1-12 改进(2026-06-23):
exportGradesAction新增可选studentId参数,支持按学生导出(家长视角);新增exportStudentGradeRecordsToExcel导出函数(仅含成绩明细 + 个人统计,不含班级数据);parent/grades/page.tsx 传 studentId 到 ParentExportButton;ParentExportButton 接入 exportGradesAction - ✅ v3-P3-1 改进(2026-06-23):
batch-grade-entry.tsx新增"下载模板"按钮,客户端生成 CSV 模板(含学生姓名/分数/备注列头 + BOM 支持 Excel UTF-8),教师可下载填好后粘贴到录入表格 - ✅ v3-P3-2 改进(2026-06-23):
grade-record-list.tsx新增多选复选框(全选/单选)+ 批量删除工具栏 + 批量删除确认对话框;新增bulkDeleteGradeRecordsdata-access 函数(使用 inArray 一次性删除避免 N+1)+bulkDeleteGradeRecordsActionServer Action(限制单次最多 500 条) - ✅ v4-P3-2 改进(2026-06-23):
batch-grade-entry.tsx顶部新增可折叠新手引导提示框(4 步使用说明),使用 localStorage 记住用户关闭状态避免重复显示
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
actions.ts |
670+ | 19 个 Server Action(含 Zod 校验,含 v2-P1-5 安全修复:assertClassInScope + 行级 scope 校验;P3 修复:handleActionError + safeJsonParse + scope 传递 + DB 层分页;v3-P2 新增:saveGradeDraftAction/getGradeDraftAction/deleteGradeDraftAction;v4-P1-6:createGradeRecordAction/batchCreateGradeRecordsAction 新增通知;v4-P1-12:exportGradesAction 新增 studentId 参数;v3-P3-2 新增:bulkDeleteGradeRecordsAction 批量删除) |
actions-analytics.ts |
170 | 5 个分析 Action(含 Zod 校验,P3 修复:handleActionError + assertClassInScope 校验) |
data-access.ts |
450+ | 成绩 CRUD + 统计 + 草稿(含 v2-P2-9 修复:recorderName 批量查询;P3 修复:PaginatedGradeRecords 接口 + DB 层分页 + 事务 + 存在性检查 + scope 过滤 + 并列排名;v3-P2 新增:saveGradeDraft/getGradeDraft/deleteGradeDraft + GradeDraftData 接口;v3-P3-2 新增:bulkDeleteGradeRecords 使用 inArray 批量删除) |
data-access-analytics.ts |
200+ | 趋势/对比分析(P3 修复:getClassComparison 应用 buildScopeClassFilter;v3-P2 新增:getExamOptionsForGrades/getSchoolWideGradeSummary;getGradeTrend/getClassComparison/getSubjectComparison/getGradeDistribution 新增 semester/examId 可选参数) |
data-access-ranking.ts |
83 | 排名查询(P3 修复:getRankingTrend 接受 scope 参数 + class_taught 校验) |
stats-service.ts |
285 | 统计计算纯函数(P1-1 新增:8 个纯函数 + 2 个常量 + 2 个接口;P3-10:createDefaultBuckets 改为内部函数;P3-24:buildGradeTrendPoints 使用 isGradeTrendType 类型守卫替代 as 断言) |
export.ts |
290+ | Excel 导出(v2-P1-5 修复:传递 currentUserId 到 data-access;P3 修复:适配 PaginatedGradeRecords 结构 + 传递 scope;P3-6:复用 stats-service.computeAverageScore 替代局部 avg;P3-7:硬编码中文改用 next-intl getTranslations;v4-P1-12 新增:exportStudentGradeRecordsToExcel 家长视角单学生导出) |
schema.ts |
113+ | Zod 校验(含 12 个查询 schema;P3 修复:score .max(1000) + records .max(500) + 补全查询字段;v3-P2 新增:grade_drafts 表定义第 1444-1469 行) |
lib/grade-utils.ts |
20 | 公共工具函数(toNumber/normalize;P3-26:buildScopeClassFilter 迁移至 scope-filter.ts) |
lib/scope-filter.ts |
56 | DB 行级权限过滤(buildScopeClassFilter;P3-26 从 grade-utils.ts 迁移;v2-P2-2 修复:改用 classes data-access 子查询;P3 修复:新增 currentUserId 参数) |
types.ts |
168+ | 类型定义(v3-P2 新增:SchoolWideGradeSummaryItem/SchoolWideGradeSummary) |
components/widget-boundary.tsx |
136 | Widget 边界组件(P1-5 新增,v2-P1-1 已在 3 个页面应用) |
components/school-wide-summary-card.tsx |
- | v3-P2 新增:管理员全校成绩汇总卡片(4 个统计卡片 + 各年级对比表格) |
components/score-cell.tsx |
41 | v4-P1-7 新增:成绩单元格组件,根据得分率着色(红<60%/黄60-84%/绿≥85%),使用语义化 Tailwind 类名 |
components/grade-trend-card.tsx |
69 | 趋势卡片(v2-P2-9 修复:a11y;v2-P1-4:i18n;P3 修复:NaN 日期检查 + fullScore > 0 守卫) |
components/grade-record-list.tsx |
200+ | 成绩记录列表(v2-P1-4:i18n;P3 修复:safeActionCall 包装删除操作;v4-P1-7:使用 ScoreCell;v4-P1-10:overflow-x-auto;v3-P3-2 新增:多选复选框 + 全选 + 批量删除工具栏 + 批量删除确认对话框,接入 bulkDeleteGradeRecordsAction) |
components/grade-distribution-chart.tsx |
100 | 分数分布图(v2-P1-4:i18n) |
components/subject-comparison-chart.tsx |
62 | 科目对比图(v2-P1-4:i18n) |
components/class-comparison-chart.tsx |
194 | 班级对比图(v2-P1-4:i18n;v3-P3-5 新增:显著性分析区域,基于极差和样本量的经验规则判断班级间差异,含可折叠详细分析) |
components/grade-trend-chart.tsx |
59 | 趋势图(v2-P1-4:i18n) |
components/grade-record-form.tsx |
177 | 录入表单(v2-P2-7 修复:Label htmlFor;v2-P1-4:i18n;P3 修复:safeActionCall 包装提交) |
components/batch-grade-entry.tsx |
500+ | 批量录入(v2-P2-7 修复:Label htmlFor;v2-P1-4:i18n;P3 修复:safeActionCall + localStorage 安全检查 + 区分未录入与录入 0;v3-P2 新增:接入服务端草稿 saveGradeDraftAction/getGradeDraftAction/deleteGradeDraftAction;v3-P3-1 新增:下载 CSV 录入模板按钮含学生姓名列表;v4-P3-2 新增:可折叠新手引导提示框,localStorage 记住关闭状态) |
components/grade-filters.tsx |
76 | 过滤器(v2-P1-4:i18n) |
components/student-grade-summary.tsx |
107 | 学生成绩摘要(v2-P1-4:i18n) |
components/export-button.tsx |
79 | 导出按钮(v2-P1-4:i18n;P3 修复:safeActionCall 包装导出操作) |
components/analytics-filters.tsx |
86+ | 分析过滤器(v2-P1-4:i18n;v3-P2 新增:exams/currentExamId/currentSemester props,添加学期和考试筛选 ChipNav) |
components/stats-class-selector.tsx |
40 | 统计班级选择器(v2-P1-4:i18n) |
components/grade-stats-card.tsx |
74 | 统计卡片(v2-P1-4:i18n) |
components/class-grade-report.tsx |
90 | 班级成绩报告(v2-P1-4:i18n) |
components/grade-query-filters.tsx |
96 | 查询过滤器(v2-P2-7 修复:Label htmlFor;v2-P1-4:i18n) |
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中同类非空断言清理 - ✅ P0-3 修复(2026-06-22):
拆分为 6 个文件(actions-teacher/actions-admin/actions-grade/actions-invitations/actions-schedule/actions-shared),原actions.ts974 行接近 1000 行硬上限actions.ts改为 50 行 barrel re-export - ✅ P1-1 修复(2026-06-22):
改为ctx.roles.includes("admin"/"teacher"/"student")角色硬编码hasAdminScope(ctx)/hasTeacherScope(ctx)/hasStudentScope(ctx)基于dataScope.type判断 - ✅ P1-4 修复(2026-06-22):
types.ts中 ClassHomeworkInsights 等跨领域类型保留在 classes 模块(因为是 classes 对 homework 数据的视图),添加注释说明归属决策
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
data-access.ts |
548 | 核心班级 CRUD + 邀请码 + 教师班级管理(含 re-export 向后兼容) |
data-access-stats.ts |
513 | 作业统计查询(班级/年级作业洞察,通过 homework/data-access-classes 获取数据) |
data-access-schedule.ts |
93 | 课表查询(学生/班级课表只读,P0-5 已修复:写函数已迁移至 scheduling 模块) |
data-access-students.ts |
253 | 学生相关查询(科目成绩、学生名单、学生班级,通过 homework/data-access-classes 获取数据) |
data-access-admin.ts |
406 | 管理员班级管理(管理员班级 CRUD、年级管理班级查询) |
actions.ts |
50 | Barrel re-export(P0-3 修复:从 974 行拆分为 6 个文件) |
actions-teacher.ts |
100 | 教师班级 CRUD(3 个 Action) |
actions-admin.ts |
120 | 管理员班级 CRUD(3 个 Action) |
actions-grade.ts |
110 | 年级组长班级 CRUD(3 个 Action) |
actions-invitations.ts |
502 | 邀请码与注册 + 批量操作(10 个 Action,P2-4 新增批量导入学生/批量分配教师) |
actions-schedule.ts |
90 | 班级课表 CRUD(3 个 Action) |
actions-shared.ts |
60 | 共享工具(hasAdminScope/hasTeacherScope/hasStudentScope/parseSubjectTeachers/toWeekday) |
schema.ts |
152 | Zod 校验(13 个 schema:教师/管理员/年级班级 CRUD + 课表 CRUD + 邮箱注册) |
types.ts |
201 | 类型定义(含跨领域类型说明注释,P1-4 修复) |
2.8 school(学校模块)
职责:学校/学年/部门/年级的 CRUD。
导出函数:
- Actions:
createSchoolAction/updateSchoolAction/deleteSchoolAction/createAcademicYearAction/updateAcademicYearAction/deleteAcademicYearAction/createDepartmentAction/updateDepartmentAction/deleteDepartmentAction/createGradeAction/updateGradeAction/deleteGradeAction/promoteGradesAction(编排层:权限校验 + Zod 校验 + 调用 data-access + revalidatePath + after(logAudit);promoteGradesAction年级升级,审计日志grade.promote) - Data-access:只读查询(
getSchools/getGrades/getDepartments/getAcademicYears/getStaffOptions/getGradesForStaff/getOrgTree/getSubjectOptions/getGradeOptions/getSubjectNameMapByIds(P1-1 新增:批量科目名称映射,供 homework/data-access-classes 调用))+ 写操作(create/update/delete×Department/School/Grade/AcademicYear)+promoteGrades(schoolId)年级升级(order +1 + 名称升级,辅助函数promoteGradeName)
依赖关系:
- 依赖:
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模块重复定义grade-management死模块已删除,年级 CRUD 统一由 school 模块负责 - ✅ P0-5 修复(2026-06-22):
全部 4 个组件(schools-view/grades-view/departments-view/academic-year-view)已接入school/components/*4 个组件缺少 i18nuseTranslations("school");school.jsoni18n 文件已创建并扩充 - ✅ P1-3 修复(2026-06-22):新增 school-error-boundary.tsx(class component Error Boundary + i18n + router.refresh 重试)和 school-skeleton.tsx(SchoolListSkeleton 表格骨架 + SchoolCardSkeleton 卡片骨架);4 个页面(schools/grades/departments/academic-year)均已包裹 SchoolErrorBoundary;school.json 补充 errors.boundary.* 翻译键
- ✅ P1-5 修复(2026-06-22):
拆分为组合模式:SchoolListToolbar + SchoolFormDialog + SchoolDeleteDialog + useSchoolData hookschools-view.tsx硬编码 Table+Dialog+AlertDialog - ✅ P1-6 修复(2026-06-22):新增
getSchoolsForUser(userId)/getGradesForUser(userId)权限感知查询函数,根据用户角色返回可见数据范围 - ✅ P2-1 修复(2026-06-22):抽取
use-school-datahook,将对话框状态管理逻辑与 UI 分离 - ✅ P2-2 修复(2026-06-23):新增
getOrgTree()data-access 函数(学校→年级→班级三级树,通过import("@/modules/classes/data-access")动态导入获取班级数据避免循环依赖)+OrgTreeNode类型 +OrgTreeNav组件(三级树形导航,支持搜索过滤/选中高亮/展开折叠/不同节点类型图标);school.json 补充orgTree.*翻译键 - ✅ P2-3 修复(2026-06-23):新增
promoteGradesAction+promoteGrades(schoolId)+promoteGradeName(name)辅助函数(中文数字 一→二…十二、阿拉伯数字 1→2…12 识别),按 order 降序逐条 +1 避免唯一约束冲突;含after(() => logAudit({ action: "grade.promote" }))审计日志 - ✅ P2-4 修复(2026-06-23):新增
bulkEnrollStudentsAction(CSV 批量导入学生,复用 enrollStudentByEmail)+bulkAssignSubjectTeachersAction(CSV 批量分配教师,简化实现含 TODO 待完善查找逻辑);classes/actions.ts barrel 导出 - ✅ P2-5 修复(2026-06-23):为 department/academicYear/grade 的 9 个 CRUD Action 补充
after(() => logAudit(...))审计日志(action: department.create/update/delete、academicYear.create/update/delete、grade.create/update/delete),与 school 实体审计日志策略一致
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
actions.ts |
457 | 13 个 Server Action(编排层,无 DB 直访;含 promoteGradesAction 年级升级) |
data-access.ts |
782 | 只读查询 + 12 个写操作 + promoteGrades 年级升级 + getOrgTree 组织架构树 + 跨模块查询接口 + 权限感知函数(getSchoolsForUser/getGradesForUser) |
schema.ts |
56 | Zod 校验(含 PromoteGradesSchema) |
types.ts |
103 | 类型定义(含 Insert/Update 入参类型 + OrgTreeNode) |
| components/schools-view.tsx | 132 | 学校列表容器(组合模式,P1-5 修复) |
| components/school-form-dialog.tsx | 80 | 学校创建/编辑对话框(P1-5 修复) |
| components/school-delete-dialog.tsx | 50 | 学校删除确认对话框(P1-5 修复) |
| components/school-list-toolbar.tsx | 30 | 学校列表工具栏(P1-5 修复) |
| components/school-error-boundary.tsx | 72 | 共享 Error Boundary(P1-3 修复) |
| components/school-skeleton.tsx | 69 | 共享骨架屏(P1-3 修复) |
| components/org-tree-nav.tsx | 134 | 学校→年级→班级三级树形导航(P2-2 修复:搜索过滤 + 选中高亮 + 展开折叠 + 节点类型图标) |
| hooks/use-school-data.ts | 40 | 学校数据管理 hook(P2-1 修复) |
2.8b grade-management(年级管理模块)— ✅ 已删除
2026-06-22 审计发现:该模块拥有完整的理想架构(Service 接口 + Context DI + 角色配置 + Error Boundary + Skeleton + i18n + hooks 分离),但 13 个相关页面中无任何一个导入此模块。
management/grade/*页面实际依赖classes和school模块的 data-access。2026-06-22 处置决策(P0-1/P0-2 修复):该死模块已完整删除。年级 CRUD 统一由
school模块负责(school/actions.ts+school/data-access.ts),避免两套重复实现。详见docs/architecture/audit/school-grade-class-audit-report.md。
2.9 scheduling(排课模块)
职责:自动排课算法 + 课表调整 + 排课规则管理。
导出函数:
- 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(✅ P2-2:5 个写 Action 错误消息改用getTranslations("attendance")国际化) - Data-access:
getAttendanceRecords/createAttendanceRecord/updateAttendanceRecord/deleteAttendanceRecord/getClassStudentsForAttendance/getAttendanceStats(✅ P2-1 已修复:改用 SQLCOUNT()+SUM(CASE WHEN ...)聚合查询,不再依赖getAttendanceRecords分页结果)/upsertAttendanceRules/getAttendanceRules - Data-access-stats:
getStudentAttendanceSummary(✅ P2-6 已修复:stats 改用 SQL 聚合查询,recentRecords 增加recentLimit参数默认 20,不再加载全部记录)/getClassAttendanceStats/computeStats - Import-export:
exportAttendanceRecordsToExcel(✅ P2-11 新增:Sheet1 考勤明细 + Sheet2 统计汇总,列头 i18n 化) - Components:
AttendanceSheet(批量点名表单)/AttendanceRecordList(记录列表 + 删除)/AttendanceFilters(URL 同步筛选器)/AttendanceStatsCard(单卡片统计)/AttendanceStatsCards(管理员 6 卡片总览)/AttendanceStatsClassSelector(班级筛选 ChipNav)/AttendanceRulesForm(规则配置表单)/StudentAttendanceView(学生/家长只读视图)/AttendancePageLayout(✅ P2-3 新增:页面布局骨架,admin/teacher 考勤页复用)
依赖关系:
- 依赖:
shared/*、@/auth、classes(⚠️ P1-1 未修复:getClassStudentsForAttendance仍直查classEnrollments表)、next-intl(✅ P2-2:Server Action 错误消息 i18n) - 被依赖:
parent(⚠️ 跨模块 UI 类型依赖:3 个 parent 组件直接 import@/modules/attendance/types)、app/api/export(✅ P2-11:通过exportAttendanceRecordsToExcel)
已知问题(详见 docs/architecture/audit/attendance-elective-audit-report.md):
- ✅ P2-1 已修复:
getAttendanceStats改用 SQL 聚合查询,消除分页截断导致统计失真 - ✅ P2-2 已修复:5 个写 Action 错误消息改用
getTranslations("attendance")国际化 - ✅ P2-3 已修复:新增
AttendancePageLayout组件,admin/teacher 考勤页复用布局 - ✅ P2-5 已修复:
parent-attendance-calendar.tsx增加键盘导航(ARIA grid 模式,方向键/Home/End) - ✅ P2-6 已修复:
getStudentAttendanceSummarystats 改用 SQL 聚合 + recentRecords 分页限制 - ✅ P2-11 已修复:新增
export.ts,考勤记录/统计可导出 Excel - ❌ 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 校验;P2-2:错误消息 i18n 化) |
data-access.ts |
340 | 考勤 CRUD + 班级学生查询 + 规则 upsert + 总览统计(P2-1:getAttendanceStats 改 SQL 聚合) |
data-access-stats.ts |
180 | 学生/班级考勤汇总(P2-6:stats 改 SQL 聚合 + recentRecords 分页) |
export.ts |
90 | Excel 导出(P2-11 新增:考勤明细 + 统计汇总双 Sheet) |
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 | 学生/家长视图(统计 + 最近记录) |
components/attendance-page-layout.tsx |
38 | 页面布局骨架(P2-3 新增:header/stats/filters/children 插槽) |
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)+ 消息草稿(message_drafts 表 CRUD)。
导出函数:
- Actions:
sendMessageAction/markMessageAsReadAction/deleteMessageAction/getMessagesAction/getMessageDetailAction/getRecipientsAction/getUnreadMessageCountAction/getNotificationPreferencesAction/updateNotificationPreferencesAction(✅ P1-4 已修复:通知 CRUD Action 已迁移至 notifications 模块,messaging 仅保留私信和通知偏好 Action;✅ V2-P2-13c 新增:toggleMessageStarAction星标切换 /getMessageDraftsAction草稿列表 /saveMessageDraftAction草稿保存(创建或更新)/deleteMessageDraftAction草稿删除) - Data-access:
getMessages(✅ V2-P2-13c 新增 starredOnly 星标筛选)/getMessageById/getMessageThread/createMessage/markMessageAsRead/deleteMessage/toggleMessageStar(✅ V2-P2-13c 新增:接收方星标切换)/getUnreadMessageCount/getRecipients(按 DataScope 过滤可发送对象:class_taught 教师→学生、grade_managed 年级管理员→教师/学生、all 管理员、class_members 学生→自己班级的任课教师/班主任、children 家长→孩子的班主任/任课教师;通过 classes data-access.getTeacherIdsByClassIds/getStudentActiveClassId 获取班级教师 ID)/getMessagesPageData(✅ P1-5 新增:消息首页编排函数,一次性获取消息列表和通知列表)/getMessageDetailPageData(✅ V2-P1-3 新增:消息详情页编排函数,获取详情并自动标记已读)/getMessageDrafts/createMessageDraft/updateMessageDraft/deleteMessageDraft/getMessageDraftById(✅ V2-P2-13c 新增:消息草稿 CRUD,操作 message_drafts 表) - 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) - ✅ V2-P0-2 已修复:
通知标题硬编码sendMessageAction通过getTranslations('messages')生成 i18n 通知标题(notification.messageTitle/messageTitleNoSubject) - ✅ V2-P1-2 已修复:
MessageList 客户端过滤冗余客户端过滤仅在初始数据(type=all)时执行,搜索结果已由服务端按 tab 过滤 - ✅ V2-P1-3 已修复:
消息详情页分散编排新增getMessageDetailPageData编排函数,替代 page.tsx 中after()+getMessageById+markMessageAsRead的分散编排 - ✅ V2-P1-4 已修复:
表单无服务端校验错误展示message-compose.tsx新增fieldErrors状态 +aria-invalid字段级错误展示(receiverId/subject/content) - ✅ V2-P2-1 已修复:
轮询间隔魔法数字unread-message-badge.tsx轮询间隔提取为POLL_INTERVAL_MS常量(60_000ms→ 30_000ms,✅ V2-P3 与通知组件保持一致) - ✅ V2-P2-13c 已实现:消息星标和草稿功能。
messages表新增isStarred字段(接收方可标记重要消息)+messages_receiver_starred_idx复合索引;新增message_drafts表(userId/receiverId/subject/content/parentMessageId + updatedAt/createdAt,含message_drafts_user_idx和message_drafts_user_updated_idx索引);data-access 新增toggleMessageStar/getMessageDrafts/createMessageDraft/updateMessageDraft/deleteMessageDraft/getMessageDraftById;actions 新增toggleMessageStarAction/getMessageDraftsAction/saveMessageDraftAction/deleteMessageDraftAction;getMessages支持starredOnly筛选;i18n 新增 actions.star/unstar/saveDraft/deleteDraft + messages.draftSaved/draftDeleted/starToggled + empty.noDrafts/noStarred 翻译键;迁移文件drizzle/0008_message_star_draft.sql
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
actions.ts |
~280 | 7 个私信 Server Action + 2 个通知偏好 Action(✅ P1-4:通知 CRUD Action 已迁移至 notifications 模块;✅ V2-P0-2:通知标题 i18n 化) |
data-access.ts |
~290 | 私信 CRUD + getMessagesPageData 编排函数(✅ P1-5 新增)+ getMessageDetailPageData 编排函数(✅ V2-P1-3 新增) |
schema.ts |
44 | 私信发送校验 + messageId 校验 + 通知偏好更新校验 |
types.ts |
52 | 私信类型 + re-export 通知类型(向后兼容) |
hooks/use-message-search.ts |
~60 | ✅ P1-7 新增:消息搜索 hook(防抖 + 请求竞态取消) |
组件清单:
| 组件 | 职责 |
|---|---|
components/message-list.tsx |
消息列表(✅ P1-7:使用 useMessageSearch hook + 客户端分页 UI,PAGE_SIZE=20;✅ V2-P1-2:客户端过滤仅在初始数据时执行) |
components/message-detail.tsx |
消息详情(含回复) |
components/message-compose.tsx |
撰写新消息(✅ V2-P1-4:fieldErrors + aria-invalid 字段级错误展示) |
components/unread-message-badge.tsx |
未读消息计数徽章(侧边栏,每 30 秒轮询 getUnreadMessageCountAction;✅ V2-P2-1:POLL_INTERVAL_MS 常量;✅ V2-P3:间隔从 60s 缩短为 30s 与通知一致) |
客户端行为:
message-list.tsx:客户端调用getMessagesAction搜索消息(useMessageSearch hook,400ms 防抖,请求竞态取消);V2-P1-2 优化:客户端过滤仅在初始数据(type=all)时执行unread-message-badge.tsx:每POLL_INTERVAL_MS(60_000ms)轮询getUnreadMessageCountAction刷新未读计数
2.14 notifications(通知分发模块)
职责:多渠道通知分发(SMS/Email/WeChat/InApp)+ 站内通知 CRUD + 通知偏好管理 + 通知 UI 组件。
导出函数:
- Actions:
sendNotificationAction/sendClassNotificationAction/getNotificationsAction/getUnreadNotificationCountAction/markNotificationAsReadAction/markAllNotificationsAsReadAction/archiveNotificationAction(✅ P1-4 新增:后 4 个通知 CRUD Action 从 messaging 模块迁移;✅ V2-P2-13b 新增:archiveNotificationAction 归档 Action) - Dispatcher:
sendNotification(payload)/sendBatchNotifications(payloads) - Data-access:
createNotification/getNotifications/markNotificationAsRead/markAllNotificationsAsRead/getUnreadNotificationCount/archiveNotification/unarchiveNotification/getUserContactInfo/logNotificationSend/logNotificationSendBatch(✅ P0-4 / P1-5 修复后从 messaging 迁移;✅ V2-P2-13b 新增:archiveNotification / unarchiveNotification 归档函数) - Preferences:
getNotificationPreferences/upsertNotificationPreferences(✅ P0-4 / P1-5 修复后从 messaging 迁移) - Channels:
InAppChannelSender/SmsChannelSender/EmailChannelSender/WeChatChannelSender - Components:
NotificationList/NotificationDropdown(✅ P1-4 新增:从 messaging/components 迁移;✅ V2-P2-13b:NotificationList 支持优先级 Badge 显示和归档操作;✅ V2-P2-13c:NotificationList 支持按类型筛选) - Hooks:
useNotificationStream(✅ V2-P3 新增:SSE 实时推送 + 轮询降级 Hook)、useDesktopNotifications(✅ V2-P2-13c 新增:浏览器桌面推送 Hook,支持权限管理和新通知桌面提醒) - Types:
NotificationPriority(✅ V2-P2-13b 新增:通知优先级类型 low/normal/high/urgent)
依赖关系:
- 依赖:
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) - ✅ V2-P0-1 已修复:
通知 i18n 键混在 messages.json 中新增独立的notifications.json命名空间(zh-CN/en),通知组件useTranslations从"messages"切换到"notifications";src/i18n/request.ts新增 notifications 命名空间加载 - ✅ V2-P2-1 已修复:
轮询间隔魔法数字notification-dropdown.tsx轮询间隔提取为POLL_INTERVAL_MS常量(30_000ms) - ✅ V2-P3 已优化:
30 秒轮询notification-dropdown.tsx改为 SSE 实时推送(/api/notifications/stream),SSE 不可用时自动降级为轮询(调用 Server Actions,间隔 30 秒) - ✅ V2-P2-13b 新增:通知优先级和归档功能。schema.ts
messageNotifications表新增priority(low/normal/high/urgent,默认 normal)和isArchived(默认 false)字段 + 2 个索引;types.ts 新增NotificationPriority类型;data-access.ts 新增archiveNotification/unarchiveNotification函数,getNotifications支持归档和优先级筛选(默认仅返回未归档),createNotification支持 priority;actions.ts 新增archiveNotificationAction(含 trackEvent 埋点 notification.archived);NotificationList 组件支持优先级 Badge 显示和归档按钮;i18n 新增 priority/actions.archive/messages.archiveFailed 翻译键 - ✅ V2-P2-13c 新增:通知分类筛选 + 浏览器桌面推送。NotificationList 组件新增按类型筛选按钮组(all/message/announcement/homework/grade),空状态区分"无通知"和"无筛选结果";新增
useDesktopNotificationsHook(浏览器 Notification API,支持权限管理、自动请求权限、新通知桌面推送、点击跳转);NotificationDropdown 集成桌面推送(监听新通知并触发桌面提醒,首次加载不批量推送);i18n 新增 filter.all / empty.noFilterResults / empty.noFilterResultsDesc 翻译键 - ⚠️ P1:发送日志仅 console,无
notification_logs表 - ✅ 渠道抽象优秀(接口 + 工厂 + Mock 实现)
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
dispatcher.ts |
152 | 渠道选择 + 并行分发 |
data-access.ts |
~250 | 站内通知 CRUD + 用户联系方式 + 发送日志持久化(notification_logs 表;P0-4 / P1-5 修复后新增通知 CRUD;✅ V2-P2-13b:新增归档函数 + 优先级/归档筛选) |
preferences.ts |
166 | 通知偏好 CRUD(P0-4 / P1-5 修复后从 messaging 迁移) |
actions.ts |
~300 | 7 个 Server Action(✅ P1-4:新增 4 个通知 CRUD Action;✅ V2-P2-13b:新增 archiveNotificationAction) |
types.ts |
~130 | 通知负载 + 渠道配置 + 通知记录 + 偏好类型(P0-4 / P1-5 修复后扩充;✅ V2-P2-13b:新增 NotificationPriority 类型 + priority/isArchived 字段) |
index.ts |
~80 | 对外导出入口(✅ P1-4:新增组件和 CRUD Action 导出;✅ V2-P2-13b:新增归档函数/Action/类型导出;✅ V2-P2-13c:新增 useNotificationStream / useDesktopNotifications Hook 导出) |
channels/* |
5 文件 | 4 个渠道实现 |
components/notification-list.tsx |
~190 | ✅ P1-4 新增(从 messaging 迁移):通知列表组件;✅ V2-P2-13b:支持优先级 Badge 显示和归档操作;✅ V2-P2-13c:支持按类型筛选 + 空状态区分 |
components/notification-dropdown.tsx |
~180 | ✅ P1-4 新增(从 messaging 迁移):通知下拉菜单组件;✅ V2-P3:改用 SSE 实时推送 + 轮询降级;✅ V2-P2-13c:集成 useDesktopNotifications 桌面推送 |
hooks/use-notification-stream.ts |
~195 | ✅ V2-P3 新增:SSE 实时推送 Hook(EventSource + 轮询降级) |
hooks/use-desktop-notifications.ts |
~100 | ✅ V2-P2-13c 新增:浏览器桌面推送 Hook(Notification API + 权限管理 + 点击跳转) |
组件清单:
| 组件 | 职责 |
|---|---|
components/notification-list.tsx |
通知列表(消息页底部,展示所有通知,支持标记已读;✅ V2-P0-1:useTranslations 命名空间从 "messages" 切换到 "notifications";✅ V2-P2-13b:支持优先级 Badge 显示和归档操作;✅ V2-P2-13c:支持按类型筛选 + 空状态区分"无通知"/"无筛选结果") |
components/notification-dropdown.tsx |
通知下拉菜单(站点头部,✅ V2-P3:改用 SSE 实时推送 /api/notifications/stream + 轮询降级;✅ V2-P0-1:useTranslations 命名空间切换;✅ V2-P2-1:POLL_INTERVAL_MS 常量;✅ V2-P2-13c:集成 useDesktopNotifications 桌面推送,监听新通知并触发桌面提醒) |
客户端行为:
notification-dropdown.tsx:✅ V2-P3 改用useNotificationStreamHook 消费 SSE 实时推送(/api/notifications/stream),SSE 不可用时自动降级为轮询(调用getNotificationsAction+getUnreadNotificationCountAction,间隔 30 秒);✅ V2-P2-13c 集成useDesktopNotificationsHook,通过prevNotificationIds对比检测新通知并触发桌面推送(首次加载不批量推送)hooks/use-notification-stream.ts:✅ V2-P3 新增,管理 SSE 连接生命周期(EventSource onopen/onmessage/onerror),降级时通过pollFnRef调用 Server Actions 轮询hooks/use-desktop-notifications.ts:✅ V2-P2-13c 新增,封装浏览器 Notification API(权限管理、自动请求权限、桌面推送、点击跳转回调),SSR 安全(typeof window !== "undefined"检查)
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)/toggleAnnouncementPinAction/markAnnouncementAsReadAction/getAnnouncementReadStatusAction(✅ V2-P2-13d 新增:置顶切换、已读标记、批量已读状态查询) - Data-access:
getAnnouncements(支持audience受众过滤)/getAnnouncementById/insertAnnouncement/updateAnnouncementById/deleteAnnouncementById/publishAnnouncementById/archiveAnnouncementById(后 5 个为 P1-2 新增)/getAdminAnnouncementsPageData/getEditAnnouncementPageData(✅ P1-5 新增:管理端列表页和编辑页编排函数,页面层仅调用单一函数)/toggleAnnouncementPin/markAnnouncementAsRead/isAnnouncementReadByUser/getAnnouncementReadCount/getAnnouncementReadStatusForUser(✅ V2-P2-13d 新增:置顶切换 + 已读回执 CRUD,操作announcement_reads表)
依赖关系:
- 依赖:
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) - ✅ V2-P0-2 已修复:
通知标题硬编码createAnnouncementAction/updateAnnouncementAction/publishAnnouncementAction通过getTranslations('announcements')生成 i18n 通知标题(notification.publishedTitle/publishedContent) - ✅ V2-P1-1 已修复:
AnnouncementList 客户端 useState/useMemo 过滤改为纯服务端过滤模式,Select 切换仅更新 URL?status=触发 RSC 重新渲染 - ✅ V2-P1-4 已修复:
表单无服务端校验错误展示announcement-form.tsx新增fieldErrors状态 +aria-invalid字段级错误展示(title/content/targetGradeId/targetClassId) - ✅ V2-P2-13d 已实现:公告置顶 + 已读回执。
announcements表新增isPinned字段(置顶优先排序,getAnnouncementsorderBy 改为desc(isPinned), desc(createdAt));新增announcement_reads表(唯一索引 announcementId+userId 保证幂等);新增 3 个 Server Action(toggleAnnouncementPinAction/markAnnouncementAsReadAction/getAnnouncementReadStatusAction)+ 5 个 data-access 函数(toggleAnnouncementPin/markAnnouncementAsRead/isAnnouncementReadByUser/getAnnouncementReadCount/getAnnouncementReadStatusForUser);Announcement类型新增isPinned/isReadByCurrentUser?/readCount?字段;新增AnnouncementRead类型;埋点新增announcement.pin_toggled/announcement.marked_read
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
actions.ts |
~400 | 9 个 Server Action + 通知触发逻辑 + trackEvent 埋点(P1-2 已修复,无直接 DB 操作;V2-P2-13d 新增置顶/已读/批量已读状态 3 个 Action) |
data-access.ts |
~300 | 公告 CRUD + 发布/归档 + 受众过滤 + getAdminAnnouncementsPageData / getEditAnnouncementPageData 编排函数(✅ P1-5 新增;V2-P2-13d 新增置顶切换 + 已读回执 5 个函数,操作 announcement_reads 表) |
schema.ts |
~70 | Zod 校验 + refineAudience 条件校验(✅ P1-6 新增 superRefine) |
types.ts |
~75 | 类型定义(GetAnnouncementsParams 新增 audience 字段;V2-P2-13d Announcement 新增 isPinned / isReadByCurrentUser? / readCount?,新增 AnnouncementRead 接口) |
组件清单:
| 组件 | 职责 |
|---|---|
components/announcement-list.tsx |
公告列表(用户端,支持状态筛选;✅ V2-P1-1:纯服务端过滤,Select 切换更新 URL ?status= 触发 RSC 重新渲染;✅ V3:新增 detailHrefPrefix prop 替代 detailHrefBuilder 函数 prop,解决 Next.js 16 Server→Client 序列化限制) |
components/announcement-card.tsx |
公告卡片(列表项) |
components/announcement-detail.tsx |
公告详情(只读) |
components/announcement-form.tsx |
公告表单(创建/编辑,✅ P1-6:条件校验由 schema superRefine 保证;✅ V2-P1-4:fieldErrors + aria-invalid 字段级错误展示) |
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(V3-11 增强:并行调用homework/data-access.getStudentExamResults获取考试结果)/getParentDashboardData/verifyParentChildRelation/getChildNameList(✅ v4 新增:用于详情页头部多子女切换器,一次批量查询避免 N+1) - Types(V3-11 新增):
ChildExamResultItem(单条考试结果:submissionId/examId/examTitle/assignmentId/assignmentTitle/score/maxScore/submittedAt/status);ChildDashboardData扩展examResults: ChildExamResultItem[]字段 - Components:
ParentDashboard/ChildCard/ChildDetailHeader/ChildDetailPanel(V3-11 增强:新增 exams Tab)/SiblingSwitcher/ChildHomeworkSummary/ChildHomeworkDetail(v4 新增)/ChildGradeSummary/ChildGradeDetail(v4 新增)/ChildScheduleCard/ChildExamDetail(V3-11 新增:子女考试详情视图,汇总卡片+考试列表)/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]触摸区域
V3-11 修复(家长端考试详情):
- ✅ V3-11:
parent/types.ts新增ChildExamResultItem类型;ChildDashboardData扩展examResults字段 - ✅ V3-11:
parent/data-access.tsgetChildDashboardData并行调用homework/data-access.getStudentExamResults获取子女考试结果(加入现有Promise.all) - ✅ V3-11:新增组件
ChildExamDetail(考试成绩汇总卡片:已参加考试数/平均分/最高分 + 考试成绩列表:考试标题/分数/得分率 Progress/提交时间) - ✅ V3-11:
child-detail-panel.tsx新增 exams Tab(对标智学网家长端考试详情)
依赖关系:
- 依赖:
shared/*、@/auth、classes(合理)、homework(合理;V3-11 新增getStudentExamResults调用)、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-P1-5 改进(2026-06-23):新增
getParentIdsByStudentIdsdata-access 函数,批量查询多个学生的家长 userId(去重),供 diagnostic/grades 模块通知场景调用 - ✅ v4-P1-9 改进(2026-06-23):parent/diagnostic/page.tsx 新增错误卡片展示子女诊断查询失败原因
- ✅ v4-P1-12 改进(2026-06-23):
ParentExportButton接入exportGradesAction,支持按 studentId 导出单个子女成绩;parent/grades/page.tsx 传 studentId 到 ParentExportButton - ⚠️ v4 保留:详情页 Attendance/Diagnostic Tab 为占位提示,待对应功能实现后填充
- ✅ v3-P2 改进(2026-06-23):parent/grades/page.tsx 为每个子女并行查询
getClassAverageTrend,渲染 GradeTrendCard;parent/diagnostic/page.tsx 传入practiceHrefBase={null}隐藏练习按钮 - ✅ 职责单一,正确复用其他模块 data-access
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
data-access.ts |
243+ | 子女关系 + 仪表盘数据聚合 + 关系校验 + 子女姓名列表(v4 新增 getChildNameList + buildWeeklySchedule;V3-11 getChildDashboardData 并行调用 getStudentExamResults) |
types.ts |
79+ | 类型定义(含 JSDoc,v4 新增 ChildWeeklyScheduleItem;V3-11 新增 ChildExamResultItem + ChildDashboardData.examResults) |
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 新增:成绩导出按钮(v4-P1-12 已接入 exportGradesAction,支持按 studentId 导出单个子女成绩) |
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;V3-11 新增 exams Tab) |
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-exam-detail.tsx |
- | V3-11 新增:子女考试详情视图(汇总卡片+考试列表,对标智学网家长端) |
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 |
多子女成绩聚合(v3-P2:并行查询 getClassAverageTrend + GradeTrendCard) |
/parent/diagnostic |
diagnostic/page.tsx + loading.tsx + error.tsx |
P2-5 新增:多子女学情诊断聚合(v3-P2:practiceHrefBase={null} 隐藏练习按钮) |
/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(✅ P2-2:8 个写 Action 错误消息改用getTranslations("elective")国际化) - Data-access:
getElectiveCourses/getElectiveCourseById/createElectiveCourse/updateElectiveCourse/deleteElectiveCourse/openSelection/closeSelection/buildCourseSelect/mapCourseRow/resolveCourseDisplayNames(✅ P2-7:使用 Reactcache()包装 subject/grade options 请求级去重;✅ P2-8:通过resolvers.ts接口抽象跨模块依赖)/CourseCoreRow - Data-access-operations:
selectCourse(✅ P2-9:新增checkScheduleConflict时间冲突检测;✅ P2-10:新增checkCreditLimit学分上限校验)/dropCourse/runLottery/buildLotteryRankCase(⚠️ 未导出,无法单测)/checkScheduleConflict(P2-9 新增)/checkCreditLimit(P2-10 新增) - Data-access-selections:
getCourseSelections/getStudentSelections/getStudentGradeId(✅ P2-8:通过resolvers.tsStudentGradeResolver 接口抽象)/getAvailableCoursesForStudent/resolveStudentDisplayNames(✅ P2-8:通过resolvers.tsCourseDisplayResolver 接口抽象) - Resolvers(✅ P2-8 新增):
CourseDisplayResolver/StudentGradeResolver(接口)/getCourseDisplayResolver/getStudentGradeResolver/setCourseDisplayResolver/setStudentGradeResolver/resetResolvers(可注入测试 mock) - Lib 纯函数(✅ P2-9 新增):
parseSchedule(解析时间段字符串)/isScheduleConflict(判断两个时间段是否冲突) - Import-export(✅ P2-11 新增):
exportElectiveCoursesToExcel(课程列表导出)/exportCourseSelectionsToExcel(选课名单导出) - Components:
ElectiveCourseList(课程卡片网格 + 管理操作)/ElectiveCourseForm(课程创建/编辑表单)/ElectiveFilters(nuqs 筛选栏)/StudentSelectionView(学生选课视图)/ElectivePageLayout(✅ P2-4 新增:页面布局骨架,admin/teacher 选课页复用)
依赖关系:
- 依赖:
shared/*、@/auth、school(✅ P3 已修复:通过 school data-access.getSubjectOptions/getGradeOptions 获取科目/年级名称;✅ P2-8:通过 resolvers.ts 接口抽象)、users(✅ P3 已修复:通过 users data-access.getUserNamesByIds;✅ P2-8:通过 resolvers.ts 接口抽象)、classes(通过 classes data-access.getStudentActiveGradeId;✅ P2-8:通过 resolvers.ts 接口抽象)、next-intl(✅ P2-2:Server Action 错误消息 i18n) - 被依赖:
app/api/export(✅ P2-11:通过exportElectiveCoursesToExcel/exportCourseSelectionsToExcel)
已知问题(详见 docs/architecture/audit/attendance-elective-audit-report.md):
- ✅ P2-2 已修复:8 个写 Action 错误消息改用
getTranslations("elective")国际化 - ✅ P2-4 已修复:新增
ElectivePageLayout组件,admin/teacher 选课页复用布局 - ✅ P2-7 已修复:
resolveCourseDisplayNames使用 Reactcache()包装 subject/grade options 请求级去重 - ✅ P2-8 已修复:新增
resolvers.ts,跨模块依赖(users/school/classes)通过接口抽象,支持测试注入 mock - ✅ P2-9 已修复:
selectCourse新增时间冲突检测(parseSchedule+isScheduleConflict纯函数 +checkScheduleConflict异步查询) - ✅ P2-10 已修复:
selectCourse新增学分上限校验(checkCreditLimit,MAX_CREDIT_PER_TERM=10) - ✅ P2-11 已修复:新增
export.ts,课程列表/选课名单可导出 Excel - ❌ 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(P2-2:错误消息 i18n 化) |
data-access.ts |
250 | 课程 CRUD + scope 过滤 + 共享映射函数(P2-7:cache 包装;P2-8:resolvers 接口抽象) |
data-access-operations.ts |
370 | 选课操作(P2-9:时间冲突检测;P2-10:学分上限校验;P3:事务 + FOR UPDATE 锁 + Fisher-Yates 洗牌) |
data-access-selections.ts |
149 | 选课记录查询 + 学生可选课程(P2-8:resolvers 接口抽象) |
resolvers.ts |
83 | 跨模块依赖接口抽象(P2-8 新增:CourseDisplayResolver/StudentGradeResolver + 注入/重置函数) |
export.ts |
102 | Excel 导出(P2-11 新增:课程列表 + 选课名单双函数) |
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 | 学生选课视图(已选 + 可选) |
components/elective-page-layout.tsx |
30 | 页面布局骨架(P2-4 新增:header/children 插槽) |
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) - 被依赖:无
已知问题:
- ✅ P1-2 已修复:
exam-mode-config.tsx已集成到考试表单(exam-form.tsx调用<ExamModeConfig />),移除全部 10 处as类型断言,改用useFormContext替代Controlprop 避免Control<T>不变型问题 - ✅ P1-3 已修复:
proctoring-dashboard.tsx用类型守卫函数isProctoringEventType+toProctoringEventTypes替代Object.keys(...) as ProctoringEventType[]断言 - ✅ 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 |
- | 考试模式配置(✅ P1-2 已修复:已集成到 exam-form,useFormContext 替代 Control prop,无 as 断言) |
components/proctoring-dashboard.tsx |
- | 教师监考面板(✅ P1-3 已修复:类型守卫函数替代 as 断言) |
2.22 diagnostic(学情诊断模块)
职责:知识点掌握度查询 + 诊断报告生成。
导出函数:
- Actions:
generateStudentReportAction/generateClassReportAction/publishReportAction(v4-P1-4 + v4-P1-5 增强:班级报告发布时通知全班学生 + 全班学生家长,个人报告通知学生本人 + 其家长,调用parent/data-access.getParentIdsByStudentIds批量查询家长 ID)/deleteReportAction(v2-P2-3 修复:删除死代码getDiagnosticReportsAction/getDiagnosticReportByIdAction,页面直接调用 data-access 并自行权限校验) - Data-access:
updateMasteryFromSubmission(v2-P1-8 修复:累积模式;v2-P2-5 修复:db.transaction 包裹)/getStudentMastery(P3-19 修复:移除 export,改为模块内部函数)/getStudentMasterySummary(P3-18 修复:getUserNamesByIds 与 getStudentMastery 并行查询)/getClassMasterySummary(v2-P2-4 修复:totalStudents 语义 + 班级平均掌握度按学生平均)/getKnowledgePointStats(v2-P1-7 修复:页面先查班级再传参) - Data-access-reports:
generateDiagnosticReport/generateClassDiagnosticReport(v2-P2-6 修复:校验掌握度数据;P3-27 修复:使用 DiagnosticReportError 结构化错误码)/getDiagnosticReports(P3-15 修复:支持分页 limit/offset,返回 { reports, total } 结构;v4-P1-1 增强:新增可选scope?: DataScope参数,支持 children/class_taught 行级权限过滤,class_taught 通过getStudentIdsByClassIds解析所教班级学生 ID)/getDiagnosticReportById/publishDiagnosticReport/deleteDiagnosticReport(✅ P2 已修复:使用React.cache()包装实现请求级 memoization;P3-1 修复:toNumber 从 grades 模块导入)/DiagnosticReportError(P3-27 新增:结构化错误码类) - Stats-service(✅ v2-P1-6 新增):
serializeMasteryWithKp/computeAverageMastery/classifyStrengthsWeaknesses(P3-16 修复:弱项阈值从 <60 改为 <80,消除 60-79 盲区)/buildStudentMasterySummary/aggregateClassMastery/computeKpStats/computeClassAverageMastery/buildStudentsNeedingAttention/buildClassMasterySummary/buildStudentReportContent/buildClassReportContent/computeMasteryLevel/serializeMastery(从 data-access / data-access-reports 抽取的纯统计函数) - Schema:
GenerateStudentReportSchema/GenerateClassReportSchema/PublishReportSchema/DeleteReportSchema(v2-P2-3 修复:删除死代码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;v2-P1-7 新增 getStudentActiveClassId)、users(✅ P1-1 已修复:通过 users data-access.getUserNamesByIds/getUserIdsByGradeId)、grades(P3-1 修复:通过 grades/lib/grade-utils.toNumber 复用工具函数) - 被依赖:无
已知问题:
- ✅ P1-1 已修复:
改为调用updateMasteryFromSubmission跨模块直查 4 张表exams/data-access.getExamSubmissionWithAnswers和questions/data-access.getKnowledgePointsForQuestions - ✅ P2 已修复:
已删除死代码data-access-reports.ts有未使用代码(round2+void round2) - ✅ P2 已修复:
改为updateMasteryFromSubmission循环内串行 await upsertPromise.all并行执行所有 upsert - ✅ P2 已修复:
改为两组getClassMasterySummary串行查询Promise.all并行 - ✅ P2 已修复:
改为显式getDiagnosticReports中conditions隐式any[]SQL[]类型标注 - ✅ P0-2 已修复:
改为通过data-access-reports.ts直查users表获取姓名users/data-access.getUserNamesByIds - ✅ P2-2 已修复:
Tailwind 任意值改用标准 Tailwind 类 - ✅ P2-1 已修复:
图表/表格/列表缺少 a11y ARIA 属性为 5 个图表添加role="img"+aria-label,2 个表格添加<caption>,3 个列表添加role="list",3 个图标按钮添加aria-label - ✅ P2-3 已修复:
班级报告将生成者 ID 存入schema 改为可空,班级报告studentId字段studentId置空 - ✅ v2-P1-6 已修复:
统计逻辑混在 data-access 层抽取stats-service.ts(352 行,12 个纯函数 + 2 个常量 + 4 个接口) - ✅ v2-P1-7 已修复:
页面先查getKnowledgePointStats无参调用getStudentActiveClassId再传参 - ✅ v2-P1-8 已修复:
改为累积计算(读取已有记录后累加)updateMasteryFromSubmission覆盖模式 - ✅ v2-P2-3 已修复:
死代码已删除,页面直接调用 data-accessgetDiagnosticReportsAction/getDiagnosticReportByIdAction全局零调用 - ✅ v2-P2-4 已修复:
改为实际有掌握度记录的学生数;先算学生个人平均再取平均totalStudents语义错误 + 班级平均掌握度计算偏差 - ✅ v2-P2-5 已修复:
多 upsert 无事务包裹使用db.transaction()保证原子性 - ✅ v2-P2-6 已修复:
生成报告未校验掌握度数据添加totalKnowledgePoints === 0和studentCount === 0校验 - ✅ v2-P1-4 已修复:
4 个组件 i18n 完全未接入全部接入useTranslations("diagnostic") - ✅ v2-P2-7 已修复:
添加report-list.tsx过滤器 Label 缺少htmlForhtmlFor和id - ✅ 与 grades 模块无职责重叠
- ✅ v3-P2 改进(2026-06-23):
StudentDiagnosticView新增practiceHrefBaseprop(string | null),null 时隐藏练习按钮;teacher/diagnostic/student/[studentId] 传入practiceHrefBase="/teacher/questions",parent/diagnostic 传入practiceHrefBase={null}隐藏练习按钮 - ✅ P3-1 已修复(2026-06-23):
改为从 grades 模块导入data-access-reports.ts本地定义toNumber与grades/lib/grade-utils.ts重复 - ✅ P3-15 已修复(2026-06-23):
添加 limit/offset 分页支持,返回getDiagnosticReports无分页,可能返回大量数据{ reports, total }结构,Promise.all 并行查询总数和数据 - ✅ P3-16 已修复(2026-06-23):
强弱项分类存在 60-79 盲区弱项阈值从 <60 改为 <80,确保所有知识点都被分类 - ✅ P3-17 已修复:
班级报告 strengths 无数量上限buildClassReportContent中 strengths 已限制为前 5 个(按掌握度降序) - ✅ P3-18 已修复(2026-06-23):
改为 Promise.all 并行查询getStudentMasterySummary串行查询用户名和掌握度 - ✅ P3-19 已修复(2026-06-23):
移除 export,改为模块内部函数getStudentMastery使用 export 但仅内部使用 - ✅ P3-27 已修复(2026-06-23):
改为使用generateDiagnosticReport/generateClassDiagnosticReport中throw new Error("...")直接暴露给用户DiagnosticReportError结构化错误码(继承 BusinessError) - ✅ P3-21 已修复(2026-06-23):
ClassDiagnosticViewView 按钮已有描述性 aria-label(classDiagnostic.viewAriaLabel,含学生名) - ✅ P3-22 已修复(2026-06-23):
StudentDiagnosticViewPractice 按钮添加 aria-label(studentDiagnostic.practiceAriaLabel,含知识点名) - ✅ v4-P3-6 已修复(2026-06-23):
MasteryRadarChart雷达图添加tabIndex={0}键盘可达性 +focus-visible焦点样式(原有role="img"+aria-label保留) - ✅ v4-P1-1 改进(2026-06-23):
getDiagnosticReports新增可选scope?: DataScope参数,支持 children/class_taught 行级权限过滤;teacher/diagnostic/page.tsx 传入 dataScope;student/diagnostic/page.tsx 传 status:"published" + dataScope;parent/diagnostic/page.tsx 传 status:"published" + dataScope + 错误卡片 - ✅ v4-P1-3 改进(2026-06-23):
student-diagnostic-view.tsx移除草稿回退逻辑(不再展示 draft 状态报告) - ✅ v4-P1-4 + v4-P1-5 改进(2026-06-23):
publishReportAction班级报告发布时通知全班学生 + 全班学生家长(通过getStudentIdsByClassId获取全班学生,通过parent/data-access.getParentIdsByStudentIds获取家长 ID);个人报告通知学生本人 + 其家长 - ✅ v4-P1-8 + v4-P1-11 改进(2026-06-23):
class-diagnostic-view.tsx热力图添加图例 + 表格overflow-x-auto水平滚动 - ✅ v4-P1-9 改进(2026-06-23):parent/diagnostic/page.tsx 新增错误卡片(Error Card)展示子女诊断查询失败原因
- ✅ v4-P1 数据库变更(2026-06-23):
learningDiagnosticReports表新增classId字段(varchar(128), references classes.id, onDelete: set null)+diagnostic_class_idx索引;DiagnosticReport接口新增classId: string | null字段(班级报告关联班级 ID,个人报告为 null) - ✅ v4-P1 师生关系校验(2026-06-23):teacher/diagnostic/student/[studentId]/page.tsx 新增 class_taught 师生关系校验
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
data-access.ts |
179 | 知识点掌握度查询 + 更新(v2-P1-8 累积模式;v2-P2-5 事务;v2-P2-4 语义修正;v2-P1-6 改用 stats-service 纯函数) |
data-access-reports.ts |
183+ | 诊断报告 CRUD(v2-P2-6 校验;v2-P1-6 改用 stats-service 纯函数;v4-P1-1:getDiagnosticReports 新增 scope 参数支持行级权限过滤) |
stats-service.ts |
352 | 统计计算纯函数(v2-P1-6 新增:12 个纯函数 + 2 个常量 + 4 个接口) |
actions.ts |
165+ | 4 个 Server Action(v2-P2-3 删除 2 个死代码读 Action;v4-P1-4/v4-P1-5:publishReportAction 新增全班学生 + 家长通知) |
schema.ts |
23 | Zod 校验(4 个 schema,v2-P2-3 删除 2 个死代码 schema) |
types.ts |
87 | 类型定义 |
components/class-diagnostic-view.tsx |
266 | 班级诊断视图(v2-P1-6 热力图 a11y;v2-P1-4 i18n;v4-P1-8 热力图图例;v4-P1-11 表格 overflow-x-auto) |
components/student-diagnostic-view.tsx |
225+ | 学生诊断视图(v2-P1-4 i18n;v3-P2 新增:practiceHrefBase prop,null 时隐藏练习按钮;v4-P1-3 移除草稿回退逻辑) |
components/mastery-radar-chart.tsx |
72 | 雷达图(v2-P1-4 i18n) |
components/report-list.tsx |
265 | 报告列表(v2-P2-7 Label htmlFor;v2-P1-4 i18n) |
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 标签页 - ✅ v1.1 已修复:
新增/settings页面 ErrorBoundary 触发(Functions cannot be passed directly to Client Components)actions-service.ts("use server" 文件),导出updateProfileAction+updateNotificationPreferencesAction两个 Server Action wrapper;page.tsx直接传递 Server Action 引用;SettingsService接口的getProfile/getPreferences改为可选 - ✅ v1.1 已修复:
i18n 键双重settings.前缀(MISSING_MESSAGE)role-settings-config.tsx中descriptionKey去掉settings.前缀 - ✅ v1.1 已修复:
AI 标签页 FormLabel 在 Form 上下文外使用(getFieldState null)ai-provider-settings-card.tsx中两处<FormLabel>改为<Label> - ✅ v1.1 已修复:
非管理员访问/settings?tab=ai显示空白settings-view.tsx新增resolveTab()函数,对无权限的 tab 回退到general - ✅ v1.1 已修复:
数据库通过 ALTER TABLE 添加缺失字段notification_preferences表缺少quiet_hours_*字段 - ✅ v1.1 已修复:
数据库通过 CREATE TABLE 创建system_settings表不存在
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
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 新增) |
actions-service.ts |
60 | 设置页 Server Action wrapper(v1.1 新增:updateProfileAction + updateNotificationPreferencesAction,作为 Server Action 引用传递给 Client Component,避免 Server→Client 函数传递违规) |
data-access.ts |
158 | AI Provider CRUD + 密码修改 DB 操作(P1 新增) |
data-access-system-settings.ts |
119 | system_settings 表 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()自动迁移。
架构变更(2026-06-23):数据结构从 v2 升级到 v3(课文锚点画布格式)。正文节点(textbook_content)作为画布中央核心,教学节点围绕正文组织。新增锚点系统(range/point 两种锚定方式)、占位符注入算法、11 种定制节点类型。旧数据通过
migrateV2ToV3()自动迁移。
数据结构:
- v1(已废弃,仅向后兼容读取):
{ version: 1, blocks: Block[] } - v2(已废弃,仅向后兼容读取):
{ version: 2, nodes: LessonPlanNode[]; edges: LessonPlanEdge[] } - v3(当前):
{ version: 3, textbookContentNodeId: string; nodes: AnyLessonPlanNode[]; edges: AnyLessonPlanEdge[]; anchors: NodeAnchor[] }TextbookContentNode:正文节点(draggable: false,画布中央,可缩放)LessonPlanNode:教学节点(Block+position,可拖动可连线)NodeAnchor:锚点({ id, nodeId, type: "range"|"point", start, end?, textPreview?, invalid? })AnchorEdge:锚点连线(教学节点 → 正文节点,默认 10% 透明度)FlowEdge:流程连线(教学节点 → 教学节点)
导出函数:
- Data-access(
data-access.ts):getLessonPlans/getLessonPlanById/createLessonPlan/updateLessonPlanContent/softDeleteLessonPlan/duplicateLessonPlan/getTemplateById/buildInitialContent/migrateV1ToV2/normalizeDocument(v3 规范化,兼容 v1/v2 旧数据)/buildDefaultSkeleton(v3 默认 10 节点骨架)/getTextbooksForPicker/getChaptersForPicker/findChapterById - Lib(
lib/document-migration.ts):defaultDataForType/migrateV1ToV2/migrateV2ToV3/normalizeDocument/buildInitialContent/buildDefaultSkeleton/isTextbookContentNode/isAnchorEdge/getAnchorsForNode/getActiveAnchorIds/getAnchorEdges - Lib(
lib/anchor-injector.ts):markdownToPlainText/injectPlaceholders/parseAnchoredText/toCircledNumber/getNextPointIndex/relocateAnchors/getAnchorColor - Lib(
lib/node-summary.ts):getNodeSummary/getTextbookContentSummary/getNodeColor/NODE_COLORS - Lib(
lib/rf-mappers.ts):toRfNodes(支持 textbook_content 节点)/toRfEdges(区分 anchor/flow 边透明度)/fromRfEdges - 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/getTextbooksForPickerAction/getChaptersForPickerAction
依赖关系:
- 依赖:
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默认空实现,生产环境可替换为真实埋点
架构变更(2026-06-23,本次审计修复):
- Zod schema 补全:
schema.ts新增publishLessonPlanHomeworkSchema(planId/blockId 必填,classIds 至少 1 个,availableAt/dueAt 日期格式校验),导出类型PublishLessonPlanHomeworkInput;3 个 action 文件(actions/actions-publish/actions-ai)补全 Zod 校验- data-access-versions.ts 事务修复:
revertToVersion包裹db.transaction确保原子性(回滚版本时同步更新 lessonPlans.content 与版本记录)- 统一错误处理:所有 Server Action catch 块改用
handleActionError;JSON.parse改用safeJsonParse- block-renderer 拖拽 BUG 修复:修复拖拽时节点位置计算错误
文件清单:
| 文件 | 职责 |
|---|---|
types.ts |
类型定义(含 v1/v2/v3 文档类型、TextbookContentNode、LessonPlanNode、NodeAnchor、AnchorEdge、FlowEdge、11 种 BlockData 接口) |
constants.ts |
常量定义 |
schema.ts |
Zod 验证 |
lib/document-migration.ts |
纯函数:v1→v2(migrateV1ToV2)/ v2→v3(migrateV2ToV3)/ 规范化(normalizeDocument,兼容 v1/v2/v3)/ 初始内容(buildInitialContent)/ 默认骨架(buildDefaultSkeleton,10 节点 + 正文节点)/ defaultDataForType / 工具函数(isTextbookContentNode/isAnchorEdge/getAnchorsForNode/getActiveAnchorIds/getAnchorEdges) |
lib/anchor-injector.ts |
纯函数:锚点注入算法(markdownToPlainText/injectPlaceholders/parseAnchoredText/toCircledNumber/getNextPointIndex/relocateAnchors/getAnchorColor) |
lib/node-summary.ts |
纯函数:getNodeSummary(支持 11 种节点类型)+ getTextbookContentSummary + NODE_COLORS + getNodeColor |
lib/rf-mappers.ts |
纯函数:toRfNodes(支持 textbook_content 节点 + 锚点回调)/ toRfEdges(区分 anchor/flow 边透明度)/ fromRfEdges |
config/block-registry.tsx |
配置驱动:BLOCK_REGISTRY 注册表 + BlockRenderer(switch 渲染 11 种定制节点 + textbook_content) |
providers/lesson-plan-provider.tsx |
Provider + Context(P1-5/P1-7/P2-4/V2-6):LessonPlanProvider 注入数据服务/角色配置/埋点;定义 LessonPlanDataService 接口、4 个角色配置(TEACHER/ADMIN/STUDENT/PARENT)、ROLE_CONFIGS 注册表、LessonPlanTracker 接口 + noopTracker;hooks:useLessonPlanContextSafe(返回 null 不抛错)/useLessonPlanContext/useRoleConfig/useLessonPlanService/useLessonPlanTracker/useLessonPlanTrackerSafe(V2-6 新增,返回 noopTracker 不抛错) |
services/default-data-service.ts |
默认数据服务实现:createDefaultDataService() 包装 Server Actions 为 LessonPlanDataService 实现,测试可替换为 mock |
data-access.ts |
课案 CRUD + 模板查询(migrateV1ToV2/normalizeDocument/buildInitialContent 从 lib/ 导入并 re-export 保持向后兼容;buildScopeCondition 按 scope 类型精确过滤 P0-3;V2-1:抛出 LessonPlanDataError 错误码;V2-3:mapRowToLessonPlan/mapRowToListItem/mapRowToTemplate 显式映射 + isLessonPlanStatus/isTemplateType/isTemplateScope 类型守卫) |
data-access-versions.ts |
版本管理(创建/查询/回滚/清理;V2-3:mapRowToVersion 显式映射) |
data-access-templates.ts |
个人模板 CRUD(V2-3:mapRowToTemplate 显式映射 + 类型守卫) |
data-access-knowledge.ts |
按知识点/题目反查课案(V2-3:显式字段映射替代 as unknown as) |
actions.ts |
课案 CRUD/版本/模板 Server Actions(V2-1:getTranslations i18n + 错误码捕获;V2-2:createLessonPlanAction 传入 translateTitle 翻译 SYSTEM_TEMPLATES) |
actions-publish.ts |
发布作业 Server Action(V2-1:getTranslations i18n + PUBLISH_ERROR_KEY_MAP 错误码映射) |
actions-ai.ts |
AI 知识点建议 Server Action(V2-1:i18n + 错误码) |
actions-kp.ts |
知识点选项 Server Action(V2-1:i18n + 错误码) |
publish-service.ts |
发布作业服务(编排 homework/exams/classes,通过对方 data-access 调用,无直查跨模块表;V2-1:抛出 PublishServiceError 错误码;V2-3:显式字段映射替代 as unknown as) |
ai-suggest.ts |
AI 知识点建议服务 |
seed-templates.ts |
模板种子数据 |
hooks/use-lesson-plan-editor.ts |
课案编辑器 Hook(基于 zustand,支持 nodes/edges/anchors 操作:addNode/updateNode/updateNodePosition/removeNode/connect/disconnect/setEdges/selectNode + 锚点操作 addAnchor/removeAnchor/updateAnchor + 正文节点操作 updateTextbookContent/getTextbookContentNode;实时拖动) |
components/lesson-plan-list.tsx |
课案列表(i18n 已接入) |
components/lesson-plan-card.tsx |
课案卡片(i18n 已接入;V2-6:duplicate/archive 调用 tracker.track) |
components/lesson-plan-filters.tsx |
课案筛选器(i18n 已接入;V2-5:3 个表单元素 label htmlFor 关联) |
components/lesson-plan-editor.tsx |
课案编辑器(编排 NodeEditor + NodeEditPanel,i18n 已接入;V2-6:handleManualSave 调用 tracker.track;V3:顶部工具栏显示教材/章节标题指示器) |
components/node-editor.tsx |
节点图画布(React Flow,使用 lib/rf-mappers + lib/node-summary 纯函数,i18n 已接入;V2-4:MiniMap 复用 getNodeColor;V2-5:role=application + 键盘导航配置;V3:注册 textbook_content 节点类型 + 锚点回调 + 实时拖动) |
components/node-edit-panel.tsx |
侧边内容编辑面板(配置驱动渲染 Block,通过 BlockRenderer + LessonPlanErrorBoundary 包裹,i18n 已接入;V3:处理 textbook_content 节点,教学节点类型收窄) |
components/nodes/lesson-node.tsx |
自定义教学节点组件(使用 lib/node-summary 的 getNodeSummary/getNodeColor,i18n 已接入) |
components/nodes/textbook-content-node.tsx |
正文节点组件(V3 新增):ReactMarkdown 渲染正文 + 锚点注入 + 文本选择(range 锚定)+ 点击位置(point 锚定)+ 缩放控制 + 锚点浮动菜单 |
components/lesson-plan-error-boundary.tsx |
错误边界:LessonPlanErrorBoundary 类组件,支持 fallback 和 onError 回调 |
components/lesson-plan-skeleton.tsx |
骨架屏:VersionListSkeleton/QuestionBankSkeleton/KnowledgePointSkeleton/LessonPlanListSkeleton |
components/block-renderer.tsx |
⚠️ @deprecated Block 渲染器(已被 NodeEditor 替代,保留向后兼容) |
components/template-picker.tsx |
模板选择器(i18n 已接入;V2-6:create 调用 tracker.track) |
components/version-history-drawer.tsx |
版本历史抽屉(i18n 已接入;V2-6:revert 调用 tracker.track) |
components/knowledge-point-picker.tsx |
知识点选择器(i18n 已接入) |
components/question-bank-picker.tsx |
题库选择器(i18n 已接入) |
components/inline-question-editor.tsx |
内联题目编辑器(i18n 已接入;V2-5:type/difficulty select label htmlFor 关联) |
components/publish-homework-dialog.tsx |
发布作业对话框(i18n 已接入;V2-6:publish 调用 tracker.track) |
components/blocks/rich-text-block.tsx |
富文本 Block(被 NodeEditPanel 复用,i18n 已接入) |
components/blocks/text-study-block.tsx |
课文研读 Block(被 NodeEditPanel 复用,i18n 已接入) |
components/blocks/exercise-block.tsx |
练习 Block(被 NodeEditPanel 复用,使用 router.refresh 替代 window.location.reload,i18n 已接入;V2-5:purpose select label 关联 + 题目列表 ul/li 语义化) |
components/blocks/reflection-block.tsx |
反思 Block(被 NodeEditPanel 复用,i18n 已接入) |
2.28 error-book(错题本模块)
职责:自动采集考试/作业错题,基于 SM-2 间隔重复算法科学复习,提供知识点薄弱度分析与多角色视图(学生/教师/家长/管理员)。
导出函数:
- Actions:
getErrorBookItemsAction/getErrorBookItemDetailAction/getErrorBookStatsAction/createErrorBookItemAction/updateErrorBookNoteAction/reviewErrorBookItemAction/deleteErrorBookItemAction/archiveErrorBookItemAction/collectFromSubmissionAction - Data-access:
getErrorBookItems/getErrorBookItemById/getErrorBookStats/createErrorBookItem/updateErrorBookNote/recordReview/deleteErrorBookItem/archiveErrorBookItem/collectFromExamSubmission/collectFromHomeworkSubmission/getStudentErrorBookSummaries/getTopWrongQuestionsByStudentIds/getKnowledgePointWeakness/getSubjectErrorDistribution/getStudentNameMap/getStudentIdsByClassIdList - Schema:
CreateErrorBookItemSchema/UpdateErrorBookNoteSchema/ReviewErrorBookItemSchema/CollectFromSubmissionSchema - Types:
ErrorBookItem/ErrorBookItemDetail/ErrorBookReviewRecord/ErrorBookStats/StudentErrorBookSummary/KnowledgePointWeakness/SubjectErrorDistribution - Components:
ErrorBookStatsCards/ErrorBookFilters/ErrorBookItemCard/ReviewButtons/ErrorBookDetailDialog/ErrorBookList/AddErrorBookDialog/ClassErrorOverview/TopWrongQuestions
权限点:
ERROR_BOOK_READ(error_book:read):查看错题本(student 自己、parent 子女)ERROR_BOOK_MANAGE(error_book:manage):管理错题(添加/复习/删除/归档,student)ERROR_BOOK_ANALYTICS_READ(error_book:analytics_read):查看错题分析(teacher/admin/grade_head/teaching_head)
依赖关系:
- 依赖:
shared/*(db、auth-guard、types、utils)、modules/classes(getStudentIdsByClassIds)、modules/questions(getQuestionsAction) - 被依赖:
app/(dashboard)/student/error-book、app/(dashboard)/teacher/error-book、app/(dashboard)/parent/error-book、app/(dashboard)/admin/error-book
DataScope 行级权限:
| 角色 | DataScope | 说明 |
|---|---|---|
| student | owned |
仅查看/管理自己的错题 |
| parent | children |
查看子女的错题统计 |
| teacher | class_taught |
查看所教班级学生的错题分析 |
| admin | all |
查看全校错题分析 |
| grade_head | grade_managed |
查看所管年级学生的错题分析 |
| teaching_head | grade_managed |
查看所管年级学生的错题分析 |
核心算法:SM-2 间隔重复(简化版):
- 独立纯函数模块:sm2-algorithm.ts(可替换为其他算法如 FSRS)
- 4 级评级:
again(重来)/hard(困难)/good(良好)/easy(简单) - 初始间隔:1/2/4/7 天
- 间隔增长:
again重置为 1 天;hard×1.2;good×1.5;easy×2 - 掌握度:0-5 级,
again-1,hard±0,good+1,easy+2 - 已掌握判定:掌握度 ≥5 或连续答对 ≥3 次
- 复习时间:次日早上 9 点
- 单元测试:sm2-algorithm.test.ts(39 个测试用例,全部通过)
- 导出函数:
calculateNewInterval/calculateNewMastery/deriveStatus/calculateNextReviewAt/calculateNewCorrectStreak/calculateSm2Result
自动采集机制:
collectFromExamSubmission:从考试提交记录中筛选得分 < 满分的题目,去重后批量插入collectFromHomeworkSubmission:从作业提交记录中筛选错题,去重后批量插入- 自动关联知识点(通过
questionsToKnowledgePoints表)
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
actions.ts |
~180 | 9 个 Server Actions,全部使用 requirePermission() + ActionState<T> |
data-access.ts |
~960 | 16 个数据访问函数 + 自动采集逻辑(SM-2 算法已提取到独立模块) |
sm2-algorithm.ts |
~180 | SM-2 间隔重复算法(独立纯函数模块,可替换,支持时间注入测试) |
sm2-algorithm.test.ts |
~280 | SM-2 算法单元测试(39 个测试用例,覆盖所有函数和边界条件) |
schema.ts |
~60 | 4 个 Zod 验证 schema |
types.ts |
~120 | 6 个类型定义 + 状态映射常量 + 错误标签常量 |
components/error-book-stats-cards.tsx |
~80 | 5 个统计卡片(总数/待学习/学习中/已掌握/待复习) |
components/error-book-filters.tsx |
~100 | 筛选栏(搜索/状态/来源/待复习),使用 nuqs |
components/error-book-item-card.tsx |
~150 | 错题卡片(预览/标签/笔记/掌握度/操作) |
components/review-buttons.tsx |
~80 | 4 按钮复习面板(again/hard/good/easy) |
components/error-book-detail-dialog.tsx |
~250 | 详情对话框(题目/答案/复习/笔记/历史) |
components/error-book-list.tsx |
~60 | 网格列表 |
components/add-error-book-dialog.tsx |
~180 | 手动添加对话框(题库选择 + 标签) |
components/class-error-overview.tsx |
~200 | 班级错题概览(教师/管理员视图) |
components/top-wrong-questions.tsx |
~80 | 高频错题列表(Top 10) |
路由清单:
| 路由 | 文件 | 说明 |
|---|---|---|
/student/error-book |
page.tsx + loading.tsx + error.tsx |
学生错题本(统计/筛选/列表/手动添加/详情复习) |
/teacher/error-book |
page.tsx + loading.tsx + error.tsx |
教师错题分析(班级概览/薄弱知识点/学科分布/高频错题) |
/parent/error-book |
page.tsx + loading.tsx + error.tsx |
家长错题本(子女错题统计/薄弱知识点/高频错题) |
/admin/error-book |
page.tsx + loading.tsx + error.tsx |
管理员错题分析(全校错题统计/薄弱知识点/学科分布/高频错题) |
数据库表:
| 表 | 说明 |
|---|---|
errorBookItems |
错题条目主表(18 列/4 索引/2 外键):studentId、questionId、sourceType、sourceId、studentAnswer、correctAnswer、subjectId、knowledgePointIds、status、masteryLevel、nextReviewAt、reviewInterval、reviewCount、correctStreak、note、errorTags、createdAt、updatedAt |
errorBookReviews |
复习记录表(8 列/2 索引/2 外键):itemId、studentId、result、reviewedAt、newInterval、newMasteryLevel |
i18n:
src/shared/i18n/messages/zh-CN/error-book.jsonsrc/shared/i18n/messages/en/error-book.json
导航配置:
- 6 个角色(admin/teacher/student/parent/grade_head/teaching_head)均添加「错题分析」导航项
- 图标:
BookX(lucide-react)
2.29 ai(AI 模块)— ✅ 新增 / V2 增强 / V3 安全加固+竞品对标
职责:统一 AI 能力封装,为备课、错题集、试卷、改题等业务模块提供 AI 服务。V2 增加流式响应、Markdown 渲染、全局助手、内容安全过滤、家长学情摘要、管理员使用统计、学生学习路径推荐。V3 安全加固(原子配额/苏格拉底校验/重试机制)+ 竞品对标(知识图谱集成/苏格拉底式辅导强化)。
架构定位:
- 位于
modules/层,通过shared/lib/ai调用底层 AI SDK - 通过
AiClientProvider(React Context)向客户端组件注入 Server Action 引用 - 业务模块不直接 import
ai/actions,仅通过 Context 消费 - V2:在 dashboard layout 全局注入
AiClientProvider,所有页面均可使用 AI 助手
核心导出:
| 类型 | 名称 | 文件 | 说明 |
|---|---|---|---|
| Server Actions | aiChatAction |
modules/ai/actions.ts |
AI 对话(权限:AI_CHAT) |
| Server Actions | suggestSimilarQuestionsAction |
modules/ai/actions.ts |
相似题推荐(权限:AI_CHAT + ERROR_BOOK_READ) |
| Server Actions | suggestGradingAction |
modules/ai/actions.ts |
AI 辅助批改(权限:AI_CHAT + HOMEWORK_GRADE) |
| Server Actions | generateLessonContentAction |
modules/ai/actions.ts |
备课内容生成(权限:AI_CHAT + LESSON_PLAN_READ) |
| Server Actions | generateQuestionVariantAction |
modules/ai/actions.ts |
题目变体生成(权限:AI_CHAT + EXAM_AI_GENERATE) |
| Server Actions | analyzeWeaknessAction |
modules/ai/actions.ts |
薄弱点分析(权限:AI_CHAT + ERROR_BOOK_READ) |
| Server Actions | generateChildSummaryAction |
modules/ai/actions.ts |
家长学情摘要(权限:AI_CHAT)— V2 新增 |
| Server Actions | recommendStudyPathAction |
modules/ai/actions.ts |
学习路径推荐(权限:AI_CHAT)— V2 新增 |
| Server Actions | getAiUsageStatsAction |
modules/ai/actions.ts |
AI 使用统计(权限:AI_CONFIGURE)— V2 新增 |
| SSE Route | POST /api/ai/chat/stream |
app/api/ai/chat/stream/route.ts |
流式 AI 对话(SSE)— V2 新增 |
| SSE Route | GET /api/notifications/stream |
app/api/notifications/stream/route.ts |
通知实时推送(SSE,15s 心跳 + 5min 超时)— V2-P3 新增 |
| Service | AiService |
modules/ai/types.ts |
服务端 AI 服务接口(含 8 个方法) |
| Service | AiClientService |
modules/ai/types.ts |
客户端 AI 服务接口(Server Action 引用集合) |
| Provider | AiClientProvider |
modules/ai/context/ai-client-provider.tsx |
React Context Provider,注入 AiClientService |
| Hook | useAiClient |
modules/ai/context/ai-client-provider.tsx |
消费 AiClientService(必须在 Provider 内使用) |
| Hook | useAiClientOptional |
modules/ai/context/ai-client-provider.tsx |
可选消费 AiClientService(未注入返回 null) |
| Hook | useAiChatStream |
modules/ai/hooks/use-ai-chat-stream.ts |
流式 AI 对话 Hook(SSE + AbortController)— V2 新增 |
| Hook | useAiChat |
modules/ai/hooks/use-ai-chat.ts |
非流式 AI 对话 Hook |
| Hook | useAiSuggestion |
modules/ai/hooks/use-ai-suggestion.ts |
AI 建议 Hook |
| Component | AiAssistantWidget |
modules/ai/components/ai-assistant-widget.tsx |
全局 AI 助手悬浮按钮(上下文感知)— V2 新增 |
| Component | AiChatPanel |
modules/ai/components/ai-chat-panel.tsx |
AI 对话面板(流式 + Markdown + 复制 + 停止 + 建议)— V2 增强 |
| Component | AiMarkdownRenderer |
modules/ai/components/ai-markdown-renderer.tsx |
Markdown 渲染器(GFM + 复制按钮)— V2 新增 |
| Component | AiGradingAssist |
modules/ai/components/ai-grading-assist.tsx |
AI 批改辅助(主观题预评分 + 反馈建议) |
| Component | AiErrorBookAnalysis |
modules/ai/components/ai-error-book-analysis.tsx |
错题本 AI 分析(相似题 + 薄弱点) |
| Component | AiLessonContentGenerator |
modules/ai/components/ai-lesson-content-generator.tsx |
备课内容生成器(活动/评估/讨论题/素材) |
| Component | AiQuestionVariantGenerator |
modules/ai/components/ai-question-variant-generator.tsx |
题目变体生成器(同知识点/不同难度/不同题型) |
| Component | AiChildSummary |
modules/ai/components/ai-child-summary.tsx |
家长 AI 学情摘要 — V2 新增 |
| Component | AiUsageDashboard |
modules/ai/components/ai-usage-dashboard.tsx |
管理员 AI 使用统计仪表盘 — V2 新增 |
| Component | AiStudyPath |
modules/ai/components/ai-study-path.tsx |
学生学习路径推荐 — V2 新增 |
| Component | AiErrorBoundary |
modules/ai/components/ai-error-boundary.tsx |
AI 功能错误边界 |
| Component | AiSuggestionSkeleton |
modules/ai/components/ai-skeleton.tsx |
AI 建议加载骨架屏 |
| Component | AiProviderSelector |
modules/ai/components/ai-provider-selector.tsx |
AI 服务商选择器 |
| Safety | filterUserInput |
modules/ai/services/content-safety.ts |
输入安全过滤 — V2 新增 |
| Safety | filterAiOutput |
modules/ai/services/content-safety.ts |
输出安全过滤 — V2 新增 |
| Safety | checkDailyLimit |
modules/ai/services/content-safety.ts |
每日交互限制 — V2 新增 |
| Safety | tryConsumeDailyQuota |
modules/ai/services/content-safety.ts |
原子化每日限额(防 TOCTOU 竞态)— V3 新增 |
| Safety | refundDailyQuota |
modules/ai/services/content-safety.ts |
配额回退(过滤/失败时不扣配额)— V3 新增 |
| Safety | validateSocraticOutput |
modules/ai/services/content-safety.ts |
苏格拉底式辅导输出校验(问号结尾/连续陈述句限制)— V3 新增 |
| Data Access | recordAiEvent |
modules/ai/data-access.ts |
AI 事件记录(内存存储,生产环境替换为 DB)— V3 新增 |
| Data Access | getAiUsageStats |
modules/ai/data-access.ts |
AI 使用统计聚合(真实聚合,替代硬编码零)— V3 新增 |
| Prompt | SOCRATIC_TUTOR_SYSTEM_PROMPT |
modules/ai/services/prompt-templates.ts |
苏格拉底式辅导专用提示词(3 级提示升级/禁止直接给答案)— V3 新增 |
| Util | consumeSseStream / getStreamErrorKey / removeTrailingEmptyAssistant |
modules/ai/hooks/stream-utils.ts |
SSE 流解析工具(从 hook 抽取)— V3 新增 |
集成点:
| 业务模块 | 集成组件 | 页面 | 说明 |
|---|---|---|---|
| homework | AiGradingAssist |
teacher/homework/submissions/[submissionId] |
主观题辅助评分 |
| error-book | AiErrorBookAnalysis |
student/error-book |
相似题推荐 + 薄弱点分析 |
| lesson-preparation | AiLessonContentGenerator |
teacher/lesson-plans/[planId]/edit |
备课内容生成 |
| exams | AiQuestionVariantGenerator |
teacher/exams/[id]/build |
题目变体生成 |
| 全局 | AiAssistantWidget |
app/(dashboard)/layout.tsx |
全局 AI 助手悬浮按钮(所有页面)— V2 新增 |
| parent | AiChildSummary |
parent/* |
家长学情 AI 摘要 — V2 新增 |
| admin | AiUsageDashboard |
admin/* |
AI 使用统计仪表盘 — V2 新增 |
| student | AiStudyPath |
student/* |
学习路径推荐 — V2 新增 |
依赖关系:
modules/ai→shared/lib/ai(AI SDK 封装,含流式createAiChatCompletionStream,V3 增加withRetry重试机制)modules/ai→shared/lib/auth-guard(权限校验)modules/ai→shared/lib/track-event(使用量埋点)modules/ai→shared/types/permissions(权限常量)modules/ai→shared/types/action-state(返回值类型)modules/ai→modules/textbooks/data-access-graph(知识图谱+掌握度查询)— V3 新增app/(dashboard)/layout→modules/ai(全局 Provider + Widget)— V2 新增app/api/ai/chat/stream→modules/ai/services/content-safety(SSE 端点安全过滤)— V3 加固- 业务模块 →
modules/ai/context/ai-client-provider(通过 Context 注入) - 业务模块 →
modules/ai/components/*(组合 AI 组件)
安全机制(V2 新增 / V3 加固):
- 输入过滤:
filterUserInput拦截暴力/自残/色情/PII - 输出过滤:
filterAiOutput扫描 AI 回复 - 每日限制:学生 50 次/天,教师 200 次/天,家长 30 次/天
- 学生 Socratic 模式:system prompt 强制不直接给答案
- SSE 端点权限校验:
requirePermission(AI_CHAT) - V3 原子配额:
tryConsumeDailyQuota解决 TOCTOU 竞态,refundDailyQuota过滤/失败时回退 - V3 苏格拉底校验:
validateSocraticOutput检查问号结尾+连续陈述句限制,SOCRATIC_TUTOR_SYSTEM_PROMPT3 级提示升级 - V3 SSE 端点加固:Zod 校验 + rate limit + 服务端强制 systemPrompt(忽略客户端)
- V3 重试机制:
withRetry指数退避(429/5xx,2 次重试,1s/2s/4s 延迟) - V3 知识图谱集成:
recommendStudyPathAction自动从 textbooks 模块获取图谱+掌握度注入 prompt
文件清单:
| 文件 | 行数 | 职责 |
|---|---|---|
modules/ai/types.ts |
~290 | 类型定义(8 个业务场景类型 + AiService/AiClientService,V3 新增 knowledgeGraph/textbookId) |
modules/ai/schema.ts |
~230 | Zod 验证 schema(8 个输入 + 8 个输出,V3 新增 knowledgeGraph/textbookId) |
modules/ai/actions.ts |
~400 | 9 个 Server Actions(含权限校验,V3 新增知识图谱获取) |
modules/ai/data-access.ts |
~138 | AI 事件存储+统计聚合 — V3 新增 |
modules/ai/services/ai-service.ts |
~430 | DefaultAiService 实现(8 个方法,V3 新增知识图谱上下文注入) |
modules/ai/services/prompt-templates.ts |
~250 | 9 个系统提示词模板(V3 新增 SOCRATIC_TUTOR_SYSTEM_PROMPT) |
modules/ai/services/usage-tracker.ts |
~90 | AI 使用量埋点(V3 新增 child_summary/study_path 能力) |
modules/ai/services/content-safety.ts |
~290 | 内容安全过滤(输入/输出/每日限制/原子配额/苏格拉底校验)— V2 新增 / V3 加固 |
modules/ai/context/ai-client-provider.tsx |
~62 | React Context Provider + Hooks |
modules/ai/components/ai-assistant-widget.tsx |
~170 | 全局 AI 助手悬浮按钮 — V2 新增 |
modules/ai/components/ai-chat-panel.tsx |
~305 | AI 对话面板(流式 + Markdown)— V2 增强 |
modules/ai/components/ai-markdown-renderer.tsx |
~100 | Markdown 渲染器 — V2 新增 |
modules/ai/components/ai-child-summary.tsx |
~170 | 家长学情摘要 — V2 新增 |
modules/ai/components/ai-usage-dashboard.tsx |
~180 | 管理员使用统计 — V2 新增 |
modules/ai/components/ai-study-path.tsx |
~170 | 学生学习路径 — V2 新增 |
modules/ai/components/ai-grading-assist.tsx |
173 | AI 批改辅助组件 |
modules/ai/components/ai-error-book-analysis.tsx |
246 | 错题本 AI 分析组件 |
modules/ai/components/ai-lesson-content-generator.tsx |
~187 | 备课内容生成器 |
modules/ai/components/ai-question-variant-generator.tsx |
~208 | 题目变体生成器 |
modules/ai/components/ai-error-boundary.tsx |
~88 | AI 错误边界 |
modules/ai/components/ai-skeleton.tsx |
~47 | AI 骨架屏 |
modules/ai/components/ai-provider-selector.tsx |
~129 | 服务商选择器 |
modules/ai/hooks/use-ai-chat-stream.ts |
~107 | 流式 AI 对话 Hook(V3 拆分至 ≤80 行函数体)— V2 新增 / V3 重构 |
modules/ai/hooks/stream-utils.ts |
~120 | SSE 流解析工具(consumeSseStream/getStreamErrorKey 等)— V3 新增 |
modules/ai/hooks/use-ai-chat.ts |
~57 | 非流式 AI 对话 Hook |
modules/ai/hooks/use-ai-suggestion.ts |
~72 | AI 建议 Hook |
app/api/ai/chat/stream/route.ts |
~160 | SSE 流式端点 — V2 新增 |
app/api/notifications/stream/route.ts |
~120 | 通知实时推送 SSE 端点 — V2-P3 新增 |
i18n:
- 翻译文件:
shared/i18n/messages/{locale}/ai.json - 命名空间:
ai - V2 新增键:
chat.streaming/stopGeneration/copy/clearConfirm/suggestedPrompts、grading.description/batch*、lessonPrep.description/additionalContext/insertContent、exam.variantType.*/targetDifficulty/addVariant、parent.*、admin.*、studyPath.*、widget.*、safety.*
第三部分:已知架构问题和技术债
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暂保留原位(集成到考试表单属于功能新增,不在本次修复范围)
P0-7:proxy.ts 路由权限跨角色访问漏洞 ✅ 已修复(webapp-testing 发现)
问题:proxy.ts 中 /teacher 和 /parent 路由前缀都使用 EXAM_READ 权限校验,但 student 和 parent 角色也拥有 EXAM_READ 权限,导致:
- 学生可访问
/teacher/*所有页面 - 教师可访问
/parent/*所有页面
修复方案(已实施):
/teacher改为Permissions.EXAM_CREATE(仅 teacher/admin 拥有)/parent改为Permissions.DASHBOARD_PARENT_READ(仅 parent 拥有)- 新增
DASHBOARD_ROUTE_PERMISSIONS细粒度仪表盘权限表,覆盖各角色 dashboard 路由 - 跨角色访问测试验证:teacher/student/parent 访问其他角色路由均被重定向回各自 dashboard
P0-8:list-pagination.tsx 客户端/服务端边界错误 ✅ 已修复(webapp-testing 发现)
问题:shared/components/ui/list-pagination.tsx 文件顶部声明 "use client",但导出的 computePagination() 和 paginate() 纯工具函数被 4 个服务端组件页面直接调用:
teacher/attendance/page.tsxteacher/homework/assignments/page.tsxteacher/homework/submissions/page.tsxteacher/grades/page.tsx
导致 Next.js 16 / React 19 抛出错误:Attempted to call computePagination() from the server but computePagination is on the client,页面渲染时触发 ErrorBoundary。
修复方案(已实施):
- 移除
list-pagination.tsx的"use client"指令 ListPagination组件仅使用Link、Button和图标,无需客户端交互,可作为服务端组件渲染computePagination和paginate恢复为服务端可调用的纯函数
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/getGradeOptionsgetSubjectNameMapByIds批量科目名称映射,P1-1 新增供 homework/data-access-classes 调用)users/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 实现 |
3.6 教师端全模块审计修复(2026-06-23)
本次审计修复覆盖教师端 13 个模块组、约 80+ 个文件,聚焦越权访问漏洞、错误处理一致性、类型安全与 React 性能。以下仅记录涉及架构图同步的变更(函数签名/导出/新增文件),其余纯实现修复(如 try/catch 补齐、useMemo/useCallback、key prop、as 断言清理)不在此列出。
3.6.1 新增共享文件 src/shared/lib/action-utils.ts
统一 Server Action 错误处理与客户端 Action 调用模式,避免内部错误消息暴露给客户端。
| 导出 | 类型 | 签名 | 用途 |
|---|---|---|---|
handleActionError |
function | (e: unknown) => ActionState<never> |
统一 Server Action catch 块错误处理:PermissionDeniedError/BusinessError 返回其 message,其他 Error 返回通用消息并 console.error 记录 |
safeActionCall |
function | <T>(action: () => Promise<ActionState<T>>, options?: { onError?, onFinally? }) => Promise<ActionState<T> | null> |
客户端调用 Server Action 的 try/catch/finally 包装器,防止 UI 永久卡 loading |
safeJsonParse |
function | <T>(json: string, errorMessage: string) => T |
安全 JSON.parse,失败抛 ValidationError(替代裸 JSON.parse) |
safeParseDate |
function | (value: string, fieldName: string) => Date |
校验日期字符串有效性,无效抛 ValidationError |
safeParseNumber |
function | (value: string, fieldName: string) => number |
校验数字字符串,无效抛 ValidationError |
escapeLikePattern |
function | (input: string) => string |
转义 SQL LIKE 通配符(% _ \),防止用户输入干扰模糊查询 |
BusinessError |
class | extends Error |
已知业务错误基类,message 可安全返回客户端(含可选 code) |
NotFoundError |
class | extends BusinessError |
资源不存在错误(自动生成 ${resource} 不存在 消息,code: not_found) |
ValidationError |
class | extends BusinessError |
输入校验错误(code: validation_error) |
3.6.2 grades 模块签名变更
data-access 层新增 scope 参数(P3 修复:所有查询函数应用 buildScopeClassFilter 进行行级 scope 过滤):
| 函数 | 文件 | 旧签名 | 新签名 |
|---|---|---|---|
getGradeRecords |
data-access.ts |
(params: GradeQueryParams) => Promise<GradeRecordListItem[]> |
(params: GradeQueryParams & { scope: DataScope; currentUserId?: string; limit?: number; offset?: number }) => Promise<PaginatedGradeRecords> |
getClassGradeStats |
data-access.ts |
(classId, subjectId?, examId?) => Promise<GradeStats> |
(classId, subjectId?, examId?, scope?: DataScope, currentUserId?: string) => Promise<GradeStats | null> |
getStudentGradeSummary |
data-access.ts |
(studentId) => Promise<StudentGradeSummary> |
(studentId, scope?: DataScope) => Promise<StudentGradeSummary | null>(class_taught scope 校验学生归属) |
getClassRanking |
data-access.ts |
(classId, subjectId?, examId?) => Promise<ClassRankingItem[]> |
(classId, subjectId?, examId?, scope?: DataScope, currentUserId?: string) => Promise<ClassRankingItem[]>(含并列排名处理) |
getClassStudentsForEntry |
data-access.ts |
(classId) => Promise<{id,name}[]> |
(classId, scope?: DataScope) => Promise<{id,name,email}[]>(class_taught scope 校验 classId) |
getRankingTrend |
data-access-ranking.ts |
(studentId, subjectId?, semester?) => Promise<RankingTrendResult | null> |
(studentId, subjectId?, semester?, scope?: DataScope) => Promise<RankingTrendResult | null>(class_taught scope 校验) |
lib 层签名变更:
buildScopeClassFilter(scope: DataScope, currentUserId?: string): SQL | null(新增currentUserId参数,class_membersscope 内置eq(gradeRecords.studentId, currentUserId)过滤;P3-26:从lib/grade-utils.ts迁移至lib/scope-filter.ts)
新增导出:
assertClassInScope(scope: DataScope, classId: string): string | null(actions.ts,校验 classId 是否在 scope 允许范围内,供 actions.ts 与 actions-analytics.ts 复用)PaginatedGradeRecords接口(data-access.ts,{ records: GradeRecordListItem[]; total: number },配合 DB 层分页)
3.6.3 homework 模块签名变更
data-access 层新增 scope 参数(P3 修复:教师仅查看自己创建的作业,学生/家长仅查看相关作业):
| 函数 | 文件 | 新签名 |
|---|---|---|
getHomeworkAssignments |
data-access.ts |
(params?: { creatorId?, ids?, classId?, scope?: DataScope }) => Promise<HomeworkAssignmentListItem[]> |
getHomeworkAssignmentById |
data-access.ts |
(id: string, scope?: DataScope) => Promise<HomeworkAssignmentListItem | null> |
getHomeworkSubmissions |
data-access.ts |
(params?: { assignmentId?, classId?, creatorId?, scope?: DataScope }) => Promise<HomeworkSubmissionListItem[]> |
getHomeworkAssignmentReviewList |
data-access.ts |
(params: { creatorId: string; scope?: DataScope }) => Promise<HomeworkReviewListItem[]> |
3.6.4 lesson-preparation 模块变更
新增 Zod schema(schema.ts):
publishLessonPlanHomeworkSchema:发布作业输入校验(planId/blockId 必填,classIds 至少 1 个,availableAt/dueAt 日期格式校验)- 导出类型
PublishLessonPlanHomeworkInput = z.infer<typeof publishLessonPlanHomeworkSchema>
data-access-versions.ts 事务变更:
revertToVersion包裹db.transaction确保原子性(回滚版本时同步更新 lessonPlans.content 与版本记录)createLessonPlanVersion已使用db.transaction(版本号自增 + 插入版本记录原子化)
3.6.5 其他修复(不涉及签名变更,仅记录范围)
- 越权访问修复:15+ 处页面添加
requirePermission,data-access 层添加 scope 过滤 - 功能性 BUG 修复:question-actions 删除字段名、question-columns 时间显示、textbook-card 删除按钮、block-renderer 拖拽
- 统一错误处理:所有 Server Action catch 块改用
handleActionError - 客户端 Action 调用添加 try/catch/finally(20+ 处,使用
safeActionCall) JSON.parse改用safeJsonParse(8+ 处)- Date 解析添加校验(15+ 处,使用
safeParseDate) - 数据库事务添加:
batchCreateGradeRecords、deleteChapter、revertToVersion - 性能优化:
Promise.all并行化、DB 层分页(gradesgetGradeRecords) - TypeScript
as断言清理(30+ 处,改用类型守卫) - React 性能修复:
useMemo、useCallback、keyprop、渲染期间副作用移除
附录 A:模块间依赖矩阵
行表示使用方,列表示被使用方。
✅合理依赖,❌违规直查,⟳循环依赖。 ✅ P1-1 已修复:所有跨模块直查已改为通过对方 data-access 接口。
| ↓ 使用 → | shared | auth | exams | homework | questions | textbooks | classes | school | dashboard | users | grades | messaging | notifications | lesson-prep | error-book | 其他 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| shared | - | ⟳✅已修复 | - | - | - | - | - | - | - | - | - | - | - | - | - | - |
| auth(root) | ✅ db/lib | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - |
| exams | ✅ | ✅ | - | - | ✅data-access | - | ✅data-access | ✅data-access | - | - | - | - | - | - | - | - |
| homework | ✅ | ✅ | ✅data-access | - | ✅关系 | - | ✅data-access | ✅data-access | - | ✅data-access | - | - | - | - | - | - |
| questions | ✅ | ✅ | - | - | - | ✅data-access | - | - | - | - | - | - | - | - | - | - |
| textbooks | ✅ | ✅ | - | - | ✅UI | - | - | - | - | - | - | - | - | - | - | - |
| classes | ✅ | ✅ | ✅data-access | ✅data-access | - | - | - | ✅data-access | - | - | - | - | - | - | - | ✅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 |
| error-book | ✅ | ✅ | - | - | ✅actions | - | ✅data-access | - | - | - | - | - | - | - | - | - |
附录 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<PaginatedGradeRecords>>
createGradeRecordAction(prevState: ActionState, formData: FormData): Promise<ActionState>
exportGradesAction(params: ExportGradesParams): Promise<ActionState<Buffer>>
// scheduling/actions.ts
autoScheduleAction(prevState: ActionState, formData: FormData): Promise<ActionState>
applyAutoScheduleAction(prevState: ActionState, formData: FormData): Promise<ActionState>
业务模块核心 Data-access
// exams/data-access.ts
getExams(params: GetExamsParams & { scope: DataScope }): Promise<{ items: Exam[]; total: number }>
getExamById(id: string, scope: DataScope): Promise<Exam | null>
persistExamDraft(input: ExamDraftInput): Promise<{ examId: string }>
persistAiGeneratedExamDraft(input: AiExamDraftInput): Promise<{ examId: string }>
// homework/data-access.ts
getHomeworkAssignments(params: GetHomeworkAssignmentsParams & { scope: DataScope }): Promise<{ items, total }>
getStudentHomeworkAssignments(studentId: string): Promise<StudentHomeworkAssignment[]>
getStudentDashboardGrades(studentId: string): Promise<StudentDashboardGrades>
getHomeworkAssignmentAnalytics(assignmentId: string): Promise<HomeworkAnalytics>
// classes/data-access.ts
getAdminClasses(scope: DataScope): Promise<Class[]>
getTeacherClasses(teacherId: string): Promise<Class[]>
getStudentClasses(studentId: string): Promise<Class[]>
getClassStudents(classId: string): Promise<Student[]>
getClassHomeworkInsights(classId: string): Promise<ClassHomeworkInsights> // ✅ P0-7 已修复:通过 homework/data-access-classes 获取数据
// grades/data-access.ts
getGradeRecords(params: GetGradeRecordsParams & { scope: DataScope; currentUserId?: string; limit?: number; offset?: number }): Promise<PaginatedGradeRecords>
getStudentGradeSummary(studentId: string, scope?: DataScope): Promise<StudentGradeSummary | null>
getClassRanking(classId: string, subjectId?: string, examId?: string, scope?: DataScope, currentUserId?: string): Promise<ClassRankingItem[]>
// scheduling/auto-scheduler.ts(纯函数,标杆)
findOptimalSlot(input: FindOptimalSlotInput): ScheduleSlot | null
validateSchedule(schedule: ScheduleItem[]): ValidationResult
autoSchedule(input: AutoScheduleInput): AutoScheduleResult
buildDefaultTimeSlots(): TimeSlot[]
文档维护说明
- 修改源码后:同步更新本文档对应模块章节 +
005_architecture_data.json - 新增模块:在第二部分添加模块清单 + 更新 1.1 分层架构图 + 更新附录 A 依赖矩阵
- 新增/删除导出函数:更新对应模块的"导出函数"清单 + 附录 C
- 修改依赖关系:更新 1.2 模块依赖关系图 + 附录 A 依赖矩阵
- 新增路由:更新
005_architecture_data.json的routes节点 - 新增数据库表:更新
shared/db/schema.ts分节 +005的dbTables节点
完整路由表、DevOps 脚本、E2E 测试等信息见
005_architecture_data.json。