# Next_Edu 架构影响地图 > 重写日期:2026-06-17 > 目标:一次阅读即可直观理解整个项目架构 > 规则:源码修改后须同步更新本文档与 `005_architecture_data.json` > 审查依据:`docs/architecture/audit/` 下 4 份审查报告 --- ## 目录 - [第一部分:全局架构概览](#第一部分全局架构概览) - [1.1 分层架构图](#11-分层架构图) - [1.2 模块依赖关系图](#12-模块依赖关系图) - [1.3 数据流向图(考试流程)](#13-数据流向图考试流程) - [1.4 核心调用链路](#14-核心调用链路) - [第二部分:模块清单](#第二部分模块清单) - [第三部分:已知架构问题和技术债](#第三部分已知架构问题和技术债) - [附录 A:模块间依赖矩阵](#附录-a模块间依赖矩阵) - [附录 B:关键参数影响链](#附录-b关键参数影响链) - [附录 C:核心函数签名索引](#附录-c核心函数签名索引) --- # 第一部分:全局架构概览 ## 1.1 分层架构图 项目采用**严格三层架构**,依赖方向必须单向:`app → modules → shared`。 ``` ┌─────────────────────────────────────────────────────────────────────┐ │ app/ (路由层) │ │ ───────────────────────────────────────────────────────────────── │ │ (auth)/ login / register / privacy / terms │ │ (dashboard)/ admin / teacher / student / parent / management │ │ api/ REST API (auth, ai, upload, files, search, ...) │ └──────────────────────────────┬──────────────────────────────────────┘ │ 调用 Server Actions / data-access ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ modules/ (业务模块层) │ │ ───────────────────────────────────────────────────────────────── │ │ 核心教学: exams · homework · questions · textbooks · grades │ │ · lesson-preparation │ │ 教学管理: classes · school · scheduling · attendance · course-plans│ │ 用户与沟通:users · messaging · notifications · parent · audit │ │ 扩展功能: elective · proctoring · diagnostic · dashboard │ │ 其他: announcements · files · settings · auth · layout · student│ │ │ │ 每个模块标准结构:actions.ts (编排) → data-access.ts (DB) → schema.ts│ └──────────────────────────────┬──────────────────────────────────────┘ │ 依赖 ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ shared/ (基础设施层) │ │ ───────────────────────────────────────────────────────────────── │ │ db/ schema.ts (54 张表) + relations.ts + index.ts │ │ lib/ auth-guard · permissions · ai · audit-logger · │ │ change-logger · login-logger · password-policy · │ │ rate-limit · excel · file-storage · ... │ │ hooks/ use-permission · use-aria-live · ... │ │ components/ ui/ (shadcn) · a11y/ · global-search · ... │ │ types/ permissions · action-state │ └─────────────────────────────────────────────────────────────────────┘ ▲ │ 反向依赖(违规,见 1.2) ┌──────────────────────────────┴──────────────────────────────────────┐ │ 根模块:src/auth.ts (NextAuth 配置) · src/proxy.ts (中间件) │ │ ✅ shared/lib/{audit-logger, change-logger, auth-guard} 已通过 │ │ shared/lib/session.ts 单一入口获取 session,不再直依赖 @/auth │ │ (详见第三部分 P0-2) │ └─────────────────────────────────────────────────────────────────────┘ ``` **分层规则**: - `app/` 只能调用 `modules/` 的 Server Actions 和 data-access,不直接访问 DB - `modules/` 之间通过对方 data-access 通信,不直接查询对方表 - `shared/` 是被依赖方,不应反向依赖 `app/` 或 `modules/` - `src/auth.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 分桶着色) | 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(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(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.ts` 1111 行(54 张表混合,超 1000 硬上限) - ✅ P1:~~`auth.ts` 293 行混合 5 类职责~~ 已拆分(4 个辅助函数组迁移至 `shared/lib/{role-utils,bcrypt-utils,http-utils,password-security-service}`,auth.ts 仅保留 NextAuth 配置) - ✅ P2-2 已修复:~~`ai.ts` 218 行混合 5 类职责~~ 已拆分为 `ai/` 目录(payload-parser.ts/api-key-crypto.ts/provider-config.ts/client.ts/errors.ts/index.ts),原 `ai.ts` 保留为向后兼容的重导出文件(9 行) - ✅ P2-4 已修复:~~`onboarding-gate.tsx` 业务逻辑泄漏到 shared~~ 已迁移至 `modules/onboarding/`(actions/data-access/schema/types/components),引导流程改为独立路由 `/onboarding` + middleware 重定向 + Server Action - ✅ P0-2/P0-3/P0-4/P0-5/P1-1/P1-2/P1-4/P1-5 已修复(v3 对标 PowerSchool/Veracross/Auth0):家长绑定三因子验证(邮箱+生日+手机号后4位)、教师多科目循环绑定、审计日志、服务端幂等、URL query 持久化步骤、局部错误收集、家长多子女动态行、跳过机制明确化 - ✅ v3 i18n 体系引入:采用 next-intl 4.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.structure` unknown 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 已修复:~~`actions.ts` 832 行(超 800 建议),多处直接 DB 操作~~ DB 操作已下沉到 data-access,actions.ts 现 691 行 - ⚠️ P1:`ai-pipeline.ts` 857 行(超 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 模块跨模块调用,封装 homework/exams 表查询) - 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)、`classes`(✅ P1-1 已修复:通过 classes data-access.getStudentIdsByClassId 等 7 个函数)、`school`(✅ P1-1 已修复:通过 school data-access.getSubjectOptions)、`users`(✅ P1-1 已修复:通过 users data-access.getUserWithRole/getUserNamesByIds) - 被依赖:`dashboard`(通过 data-access,合理)、`parent`(通过 data-access,合理;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` - ✅ P1-2 已修复:~~`actions.ts` 多处直接 DB 操作(`createHomeworkAssignmentAction` 157 行)~~ DB 操作已下沉到 `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` | 232 | 跨模块查询封装(P0-7 新增,供 classes 模块调用,封装 homework/exams 表查询) | | `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 增强:提交后跳转结果页+移动端触摸优化 | --- ## 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 已修复:~~写操作函数错放在 `actions.ts`(`insertQuestionWithRelations` / `deleteQuestionRecursive`)~~ 已下沉到 data-access(`createQuestionWithRelations` / `updateQuestionById` / `deleteQuestionByIdRecursive` / `getKnowledgePointOptions`) - ✅ P1-1 已修复:~~`getKnowledgePointOptionsAction` 直查 textbooks 模块表~~ 改为调用 `textbooks/data-access.getKnowledgePointOptions` - ✅ P2 已解决:~~`data-access.ts` 仅 129 行,写操作缺失~~ P1-2 后 data-access.ts 扩充至 260 行 **文件清单**: | 文件 | 行数 | 职责 | |------|------|------| | `actions.ts` | 149 | 5 个 Server 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) - 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` 对齐),消除 i18n `MISSING_MESSAGE` 错误 - ✅ P1 纯逻辑已导出并补单测:新增 `utils.ts` / `graph-layout.ts` 及对应 `.test.ts` - ⚠️ i18n 覆盖率约 95%(`chapter-sidebar-list` 已接入,`actions.ts` 已接入,`section-error-boundary` 默认值已改英文) - ⚠️ 类型断言残留 3 处 `as string` - ⚠️ P2 图谱方向键导航未实现 **文件清单**: | 文件 | 行数 | 职责 | |------|------|------| | `actions.ts` | 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` / `updateGradeRecordAction` / `deleteGradeRecordAction` / `exportGradesAction` / `getGradeTrendAction` / `getClassComparisonAction` / `getSubjectComparisonAction` / `getGradeDistributionAction` / `getClassRankingAction` / `getRankingTrendAction` / `getGradeRecordByIdAction` / `getClassGradeStatsAction` / `getStudentGradeSummaryAction` / `batchCreateGradeRecordsAction` / `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, 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_members` scope 内置 studentId 过滤,需传入 currentUserId 参数) - Stats-service(✅ P1-1 新增):`computeGradeStats` / `computeAverageScore` / `buildGradeTrendPoints` / `computeTrendAverage` / `computeClassComparisonStats` / `computeSubjectComparisonStats` / `computeGradeDistribution` / `buildRankingTrendPoints`(从 3 个 data-access 文件抽取的纯函数,使数据层专注 DB I/O,统计逻辑可独立测试) - Components(✅ P1-5 新增):`WidgetBoundary`(Error Boundary + Suspense + Skeleton 组合,含 a11y 属性)/ `SchoolWideSummaryCard`(✅ v3-P2 新增:管理员全校成绩汇总卡片,4 个统计卡片 + 各年级对比表格) **依赖关系**: - 依赖:`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 已修复:~~多处直查 `classes`/`classEnrollments`/`subjects`/`users` 表~~ 改为调用对应模块 data-access 函数(classes/school/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 已修复:~~`batch-grade-entry.tsx`/`grade-record-form.tsx`/`grade-distribution-chart.tsx` 中存在 `as` 断言~~ 改用类型守卫函数(isGradeType/isSemester/isDistributionTooltipPayload) - ✅ P1-5 已修复:~~teacher/grades 与 teacher/diagnostic 路由缺少 loading.tsx/error.tsx~~ 已为 7 个路由补齐 loading.tsx + error.tsx,并新增 `WidgetBoundary` 通用组件 - ✅ P2-1 已修复:~~图表/表格/列表缺少 a11y ARIA 属性~~ 为 4 个成绩图表添加 `role="img"` + `aria-label`,2 个表格添加 ``,3 个图标按钮添加 `aria-label` - ✅ P2-2 已修复:~~diagnostic 组件中存在 Tailwind 任意值~~ 改用标准 Tailwind 类 - ✅ P2-4 已修复:~~`buildScopeClassFilter` 中 `grade_managed` scope 返回 `sql\`1=0\``(空数据)~~ 改为子查询 `classId IN (SELECT id FROM classes WHERE grade_id IN (...))` 过滤所管年级的班级 - ✅ P2-6 已修复:~~student/grades 和 management/grade/insights 各自重复定义 `SearchParams` 类型与 `getParam` 函数~~ 改为统一从 `@/shared/lib/search-params` 导入 - ✅ actions 层无直接 DB 访问(标杆) - ✅ data-access 按职责拆分为 3 个文件(标杆) - ✅ P2 已修复:`export.ts` 中 `scoreMap.get(r.studentId)!` 非空断言清理为安全守卫(`if (!subjMap) continue`) - ✅ P3 修复(2026-06-22):~~`schema.ts` 缺少输入边界约束~~ score 字段添加 `.max(1000)`,records 数组添加 `.max(500)`,查询 schema 补全 studentId/semester/examId 字段 - ✅ P3 修复(2026-06-22):~~`buildScopeClassFilter` 的 `class_members` scope 未应用 studentId 过滤~~ 新增 `currentUserId` 参数,`class_members` scope 内置 `eq(gradeRecords.studentId, currentUserId)` 过滤 - ✅ P3 修复(2026-06-22):~~`getGradeRecords` 内存分页(全量查询后切片)~~ 改为 DB 层分页(limit/offset),并行查询总数与当前页数据,返回 `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_taught` scope 校验学生/班级归属 - ✅ P3 修复(2026-06-22):~~`getClassRanking` 未处理并列排名~~ 相同平均分获得相同名次,下一名次跳过占位 - ✅ P3 修复(2026-06-22):~~`getClassComparison`/`getRankingTrend` 缺少 scope 过滤~~ 应用 `buildScopeClassFilter`;`getRankingTrend` 对 `class_taught` scope 校验 - ✅ P3 修复(2026-06-22):~~actions.ts/actions-analytics.ts catch 块直接返回 `e.message`~~ 改用 `handleActionError(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.tsx` localStorage 访问包裹 `typeof window !== "undefined"` 检查;区分"未录入"与"录入 0" - ✅ P3 修复(2026-06-22):~~`grade-trend-card.tsx` 排序未处理 NaN 日期、除法未检查 fullScore > 0~~ 新增日期有效性检查与 `fullScore > 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`/`deleteGradeDraft` data-access 函数 + 对应 3 个 Server Actions,支持成绩批量录入草稿服务端持久化 - ✅ v3-P2 改进(2026-06-23):新增 `getExamOptionsForGrades` data-access 函数,获取指定班级/科目下有成绩记录的考试列表;`AnalyticsFilters` 组件新增 `exams`/`currentExamId`/`currentSemester` props,添加学期和考试筛选 ChipNav;teacher/grades/analytics/page.tsx 新增 semester/examId 搜索参数解析 - ✅ v3-P2 改进(2026-06-23):`getGradeTrend`/`getGradeDistribution`/`getSubjectComparison`/`getClassComparison` 均新增 `semester` 和 `examId` 可选参数,支持按学期和考试筛选分析 - ✅ v3-P2 改进(2026-06-23):新增 `getSchoolWideGradeSummary` data-access 函数 + `SchoolWideSummaryCard` 组件 + `SchoolWideGradeSummary`/`SchoolWideGradeSummaryItem` 类型,管理员全校成绩汇总视图(按年级聚合平均分/及格率/优秀率/学生数/班级数,加权平均计算全校汇总);admin/school/grades/insights/page.tsx 顶部新增 SchoolWideSummaryCard - ✅ v3-P2 改进(2026-06-23):parent/grades/page.tsx 为每个子女并行查询 `getClassAverageTrend`,渲染 GradeTrendCard - ✅ P3 修复(2026-06-23):~~`lib/grade-utils.ts` 72 行超 40 行工具函数上限~~ P3-26 将 `buildScopeClassFilter` 迁移至 `lib/scope-filter.ts`,grade-utils.ts 仅保留 toNumber/normalize(20 行) - ✅ P3 修复(2026-06-23):~~`stats-service.ts` createDefaultBuckets 不必要导出~~ P3-10 移除 export 关键字,改为内部函数 - ✅ P3 修复(2026-06-23):~~`stats-service.ts` buildGradeTrendPoints 使用 as 断言~~ P3-24 新增 isGradeTrendType 类型守卫函数替代 as 断言 - ✅ P3 修复(2026-06-23):~~`export.ts` 局部 avg 函数与 stats-service.computeAverageScore 重复~~ P3-6 删除局部 avg,复用 computeAverageScore - ✅ P3 修复(2026-06-23):~~`export.ts` TYPE_LABELS 硬编码中文~~ P3-7 改用 next-intl getTranslations,新增 export.sheets/columns/metrics i18n 键(zh-CN/en 同步) **文件清单**: | 文件 | 行数 | 职责 | |------|------|------| | `actions.ts` | 396+ | 18 个 Server Action(含 Zod 校验,含 v2-P1-5 安全修复:assertClassInScope + 行级 scope 校验;P3 修复:handleActionError + safeJsonParse + scope 传递 + DB 层分页;v3-P2 新增:saveGradeDraftAction/getGradeDraftAction/deleteGradeDraftAction) | | `actions-analytics.ts` | 170 | 5 个分析 Action(含 Zod 校验,P3 修复:handleActionError + assertClassInScope 校验) | | `data-access.ts` | 428+ | 成绩 CRUD + 统计 + 草稿(含 v2-P2-9 修复:recorderName 批量查询;P3 修复:PaginatedGradeRecords 接口 + DB 层分页 + 事务 + 存在性检查 + scope 过滤 + 并列排名;v3-P2 新增:saveGradeDraft/getGradeDraft/deleteGradeDraft + GradeDraftData 接口) | | `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` | 209 | Excel 导出(v2-P1-5 修复:传递 currentUserId 到 data-access;P3 修复:适配 PaginatedGradeRecords 结构 + 传递 scope;P3-6:复用 stats-service.computeAverageScore 替代局部 avg;P3-7:硬编码中文改用 next-intl getTranslations) | | `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/grade-trend-card.tsx` | 69 | 趋势卡片(v2-P2-9 修复:a11y;v2-P1-4:i18n;P3 修复:NaN 日期检查 + fullScore > 0 守卫) | | `components/grade-record-list.tsx` | 125 | 成绩记录列表(v2-P1-4:i18n;P3 修复:safeActionCall 包装删除操作) | | `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` | 58 | 班级对比图(v2-P1-4:i18n) | | `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` | 435+ | 批量录入(v2-P2-7 修复:Label htmlFor;v2-P1-4:i18n;P3 修复:safeActionCall + localStorage 安全检查 + 区分未录入与录入 0;v3-P2 新增:接入服务端草稿 saveGradeDraftAction/getGradeDraftAction/deleteGradeDraftAction) | | `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):~~`actions.ts` 974 行接近 1000 行硬上限~~ 拆分为 6 个文件(actions-teacher/actions-admin/actions-grade/actions-invitations/actions-schedule/actions-shared),原 `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`)+ 写操作(`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.js `after()` 异步非阻塞执行 - ✅ 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):~~`school/components/*` 4 个组件缺少 i18n~~ 全部 4 个组件(schools-view/grades-view/departments-view/academic-year-view)已接入 `useTranslations("school")`;`school.json` i18n 文件已创建并扩充 - ✅ P1-3 修复(2026-06-22):新增 school-error-boundary.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):~~`schools-view.tsx` 硬编码 Table+Dialog+AlertDialog~~ 拆分为组合模式:SchoolListToolbar + SchoolFormDialog + SchoolDeleteDialog + useSchoolData hook - ✅ P1-6 修复(2026-06-22):新增 `getSchoolsForUser(userId)` / `getGradesForUser(userId)` 权限感知查询函数,根据用户角色返回可见数据范围 - ✅ P2-1 修复(2026-06-22):抽取 `use-school-data` hook,将对话框状态管理逻辑与 UI 分离 - ✅ 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` - Data-access:`getAttendanceRecords` / `createAttendanceRecord` / `updateAttendanceRecord` / `deleteAttendanceRecord` / `getClassStudentsForAttendance` / `getAttendanceStats`(管理员考勤总览页统计概览,基于 `getAttendanceRecords` 聚合)/ `upsertAttendanceRules` / `getAttendanceRules` - Data-access-stats:`getStudentAttendanceSummary` / `getClassAttendanceStats` / `computeStats`(⚠️ 未导出,无法单测) - Components:`AttendanceSheet`(批量点名表单)/ `AttendanceRecordList`(记录列表 + 删除)/ `AttendanceFilters`(URL 同步筛选器)/ `AttendanceStatsCard`(单卡片统计)/ `AttendanceStatsCards`(管理员 6 卡片总览)/ `AttendanceStatsClassSelector`(班级筛选 ChipNav)/ `AttendanceRulesForm`(规则配置表单)/ `StudentAttendanceView`(学生/家长只读视图) **依赖关系**: - 依赖:`shared/*`、`@/auth`、`classes`(⚠️ P1-1 未修复:`getClassStudentsForAttendance` 仍直查 `classEnrollments` 表) - 被依赖:`parent`(⚠️ 跨模块 UI 类型依赖:3 个 parent 组件直接 import `@/modules/attendance/types`) **已知问题**(详见 `docs/architecture/audit/attendance-elective-audit-report.md`): - ❌ P0:`getAttendanceStats` 统计失真——调用 `getAttendanceRecords`(默认 pageSize=20)后对 `items` 聚合,仅基于前 20 条记录计算总览数据 - ❌ P0:`getClassStudentsForAttendance` 仍直查 `classEnrollments` 表(架构图此前声称已修复,实际未修复) - ❌ P0:6 个读 Action 无调用方(页面绕过 Action 直接调用 data-access),违反三层架构 - ❌ P0:update/delete Action 缺资源归属校验(教师 A 可修改/删除教师 B 的记录) - ❌ P0:i18n 完全缺失(`ATTENDANCE_STATUS_LABELS` 硬编码英文,组件中硬编码中文) - ❌ P0:错误边界完全缺失(5 个角色目录均无 `error.tsx`) - ⚠️ P1:`computeStats` 未导出,无法单测 - ⚠️ P1:`attendance-sheet.tsx` 使用 `window.confirm`(与项目 AlertDialog 模式不一致) - ⚠️ P1:`attendance-sheet.tsx` 存在 `{} as Record` 类型断言 - ⚠️ P1:`STATUS_OPTIONS`/`SHORTCUTS`/`STYLES` 常量在 types.ts 与 attendance-sheet.tsx 重复定义 - ✅ stats 独立拆分为 `data-access-stats.ts`(拆分范例) - ✅ DataScope 完整接入 6 种 scope 类型 - ✅ actions 层无直接 DB 访问 **文件清单**: | 文件 | 行数 | 职责 | |------|------|------| | `actions.ts` | 271 | 10 个 Server Action(含权限校验、Zod 校验) | | `data-access.ts` | 309 | 考勤 CRUD + 班级学生查询 + 规则 upsert + 总览统计 | | `data-access-stats.ts` | 145 | 学生/班级考勤汇总(拆分范例,`computeStats` 未导出) | | `schema.ts` | 43 | Zod 校验(5 个 schema) | | `types.ts` | 103 | 类型定义 + 状态标签/颜色常量(硬编码英文) | | `components/attendance-sheet.tsx` | 353 | 批量点名表单(键盘快捷键、状态按钮组) | | `components/attendance-record-list.tsx` | 130 | 考勤记录列表 + 删除对话框 | | `components/attendance-filters.tsx` | 97 | URL 同步筛选器(班级/状态/日期) | | `components/attendance-stats-card.tsx` | 81 | 单卡片统计(8 指标) | | `components/attendance-stats-cards.tsx` | 80 | 管理员总览 6 卡片网格(硬编码中文) | | `components/attendance-stats-class-selector.tsx` | 27 | 班级筛选 ChipNav | | `components/attendance-rules-form.tsx` | 148 | 考勤规则配置表单 | | `components/student-attendance-view.tsx` | 104 | 学生/家长视图(统计 + 最近记录) | --- ## 2.11 users(用户模块) **职责**:用户资料管理 + 批量导入导出 + 管理员用户列表管理。 **导出函数**: - Actions:`getUserProfileAction` / `updateUserProfileAction` / `importUsersAction` / `exportUsersAction` / `downloadUserTemplateAction` / `updateUserRoleAction` / `deleteUserAction` - Data-access:`getUserProfile` / `getCurrentStudentUser`(✅ P2-20 已修复:从 homework 模块迁移而来,6 个 student 页面通过此函数获取学生身份,不再依赖 homework 模块)/ `getAdminUsers`(管理员用户列表分页查询,支持搜索+角色聚合)/ `getAdminUserRoles`(角色名列表,用于筛选下拉框) - Import-export:`generateUserImportTemplate` / `parseUserImportData` / `exportUsersToExcel`(+ re-export `batchImportUsers` / `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 已修复:~~`updateUserProfile` 绕过 data-access 直接 DB 写~~ 已下沉到 data-access - ✅ P2-20 已修复:新增 `getCurrentStudentUser` 函数(从 homework 模块迁移),6 个 student 页面通过此函数获取学生身份,不再依赖 homework 模块 - ✅ P2 已解决:`data-access.ts` 已扩充写操作(updateUserProfile 已下沉) - ⚠️ 已知限制:`AdminUsersView` 客户端组件的删除操作通过 `fetch("/api/admin/users/:id")` 调用,对应 API 路由尚未实现(`deleteUserAction` Server Action 已就绪,可作为后续 API 路由的实现基础) **文件清单**: | 文件 | 行数 | 职责 | |------|------|------| | `import-export.ts` | 157 | 文件解析/生成(模板生成 + 解析校验 + Excel 导出)+ re-export 向后兼容 | | `user-service.ts` | 82 | 用户创建(批量导入 + 密码哈希 + 角色分配) | | `class-registration.ts` | 21 | 班级注册(委托 classes/data-access) | | `actions.ts` | 218 | 7 个 Server 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 表格 ``、teacher/student 视图语义化标签(`
` / `
` / `