# 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`) **共享组件导出**(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` 提取) **依赖关系**: - 依赖:`shared/*`、`@/auth`、`questions`(✅ P0-1 已修复:通过 data-access.createQuestionWithRelations)、`classes`(✅ P0-2 已修复:通过 data-access.getClassGradeIdsByClassIds)、`school`(✅ P1-1 已修复:通过 school data-access.getSubjectOptions/getGradeOptions) - 被依赖:`homework`(通过 sourceExamId 外键,合理)、`dashboard`(通过 data-access,P0-4 已修复)、`proctoring`(✅ P1-1 已修复:通过 exams data-access)、`diagnostic`(✅ P1-1 已修复:通过 exams data-access) **已知问题**: - ✅ P0-1 已修复:~~`persistAiGeneratedExamDraft` 直接 insert 到 `questions` 表~~ 改为调用 `questions/data-access.createQuestionWithRelations`,通过 ID 映射保持 structure 引用一致 - ✅ P0-2 已修复:~~`getExams`/`getExamById`/`getExamsDashboardStats` 直查 `classes` 表~~ 改为调用 `classes/data-access.getClassGradeIdsByClassIds` - ✅ P1-1 已修复:~~`getSubjectsAction`/`getGradesAction` 直查 `subjects`/`grades` 表~~ 改为调用 `school/data-access.getSubjectOptions` / `getGradeOptions` - ✅ P1-2 已修复:~~`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` → `?? []`) **文件清单**: | 文件 | 行数 | 职责 | |------|------|------| | `actions.ts` | 691 | 10 个 Server Action(P1-2 已修复,无直接 DB 操作) | | `ai-pipeline.ts` | 857 | AI 出题管线(超限) | | `data-access.ts` | 473 | 考试 CRUD(含 P1-2 新增 7 个写/查询函数,P0-1/P0-2 已修复:通过 questions/classes data-access 跨模块通信) | | `types.ts` | 31 | 类型定义 | | `hooks/use-exam-preview.ts` | 295 | 预览 Hook | | `utils/normalize-structure.ts` | 57 | v3 新增:exam.structure 运行时校验与归一化(从 build/page.tsx 提取) | | `components/*` | 18 文件 | 考试表单/组卷/预览组件 | --- ## 2.3 homework(作业模块) **职责**:作业全生命周期(创建/发布/作答/批改/分析)。 **导出函数**: - Actions:`createHomeworkAssignmentAction` / `startHomeworkSubmissionAction` / `saveHomeworkAnswerAction` / `submitHomeworkAction` / `gradeHomeworkSubmissionAction`(✅ P1-2 已修复:actions 层不再直接访问 DB,全部下沉到 data-access/data-access-write) - Data-access:`getHomeworkAssignments` / `getHomeworkAssignmentById` / `getHomeworkSubmissions` / `getStudentHomeworkAssignments` / `getStudentHomeworkTakeData` / `getHomeworkAssignmentReviewList` / `getHomeworkSubmissionDetails` / `getDemoStudentUser`(已迁移至 users 模块 `getCurrentStudentUser`,此处为 re-export 向后兼容)/ `isRecord` / `toQuestionContent` / `getAssignmentMaxScoreById`(后三者供 stats-service 使用) - Data-access-classes:`getAssignmentIdsForStudents` / `getHomeworkAssignmentsWithSubject` / `getHomeworkAssignmentsByIds` / `getAssignmentTargetCounts` / `getHomeworkSubmissionsForStudents` / `getPublishedHomeworkAssignmentsWithSubject` / `getHomeworkSubmissionsForAssignments`(P0-7 新增,供 classes 模块跨模块调用,封装 homework/exams 表查询) - Data-access-write:10 个写操作函数(P1-2 新增,从 actions 下沉) - Stats-service:`getTeacherGradeTrends` / `getHomeworkAssignmentAnalytics` / `getStudentDashboardGrades`(从 data-access.ts re-export 以保持向后兼容) **依赖关系**: - 依赖:`shared/*`、`@/auth`、`exams`(✅ P1-1 已修复:通过 exams data-access.getExamIdsByGradeIds/getExamSubjectIdMap/getExamWithQuestionsForHomework)、`classes`(✅ P1-1 已修复:通过 classes data-access.getStudentIdsByClassId 等 7 个函数)、`school`(✅ P1-1 已修复:通过 school data-access.getSubjectOptions)、`users`(✅ P1-1 已修复:通过 users data-access.getUserWithRole/getUserNamesByIds) - 被依赖:`dashboard`(通过 data-access,合理)、`parent`(通过 data-access,合理)、`classes`(✅ P0-7 已修复:classes 通过 `homework/data-access-classes` 获取作业数据,不再反向直查 homework/exams 表) **已知问题**: - ✅ P0 已解决:`data-access.ts` 已拆分至 598 行(原 1038 行超 1000 硬上限),统计函数迁移至 `stats-service.ts` - ✅ P0 已解决:`getStudentDashboardGrades` 排名计算逻辑迁移至 `stats-service.ts` - ✅ P0 已解决:`getHomeworkAssignmentAnalytics` 错误率统计逻辑迁移至 `stats-service.ts` - ✅ P0-7 已修复:新增 `data-access-classes.ts`,将 classes 模块对 homework/exams 表的直查封装为 homework 模块的导出函数,恢复三层架构 - ✅ P1-1 已修复:~~5 处直查 `exams` 表~~ 改为调用 `exams/data-access.getExamIdsByGradeIds` / `getExamSubjectIdMap` / `getExamWithQuestionsForHomework` - ✅ P1-2 已修复:~~`actions.ts` 多处直接 DB 操作(`createHomeworkAssignmentAction` 157 行)~~ DB 操作已下沉到 `data-access-write.ts`,actions.ts 现 239 行 **文件清单**: | 文件 | 行数 | 职责 | |------|------|------| | `data-access.ts` | 598 | 作业 CRUD + 学生视角 + 批改(含 re-export stats 函数) | | `data-access-write.ts` | 285 | 作业写操作(P1-2 新增,10 个写函数从 actions 下沉) | | `data-access-classes.ts` | 232 | 跨模块查询封装(P0-7 新增,供 classes 模块调用,封装 homework/exams 表查询) | | `stats-service.ts` | 425 | 统计分析(教师趋势/作业分析/学生仪表盘成绩) | | `actions.ts` | 239 | 5 个 Server Action(P1-2 已修复,无直接 DB 操作) | | `types.ts` | 186 | 类型定义 | | `schema.ts` | 29 | Zod 校验 | --- ## 2.4 questions(题库模块) **职责**:题库管理(题目 CRUD、知识点关联、题型支持)。 **导出函数**: - Actions:`getQuestionsAction` / `createQuestionAction` / `updateQuestionAction` / `deleteQuestionAction` / `getKnowledgePointOptionsAction`(✅ P1-2 已修复:actions 层不再直接访问 DB,全部下沉到 data-access) - Data-access:`getQuestions` / `createQuestionWithRelations` / `updateQuestionById` / `deleteQuestionByIdRecursive` / `getKnowledgePointOptions`(后 4 个为 P1-2 新增/迁移) **依赖关系**: - 依赖:`shared/*`、`@/auth`、`textbooks`(✅ P1-1 已修复:通过 textbooks data-access.getKnowledgePointOptions) - 被依赖:`exams`(通过类型导入,合理)、`textbooks`(UI 组合,合理) **已知问题**: - ✅ P1-2 已修复:~~写操作函数错放在 `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(10 个,均为写操作;读操作由 RSC 页面直接调用 data-access):`createTextbookAction` / `updateTextbookAction` / `deleteTextbookAction` / `createChapterAction` / `updateChapterContentAction` / `deleteChapterAction` / `reorderChaptersAction` / `createKnowledgePointAction` / `updateKnowledgePointAction` / `deleteKnowledgePointAction` - Data-access:`getTextbooks` / `getTextbookById` / `getChaptersByTextbookId` / `getKnowledgePointsByChapterId` / `getKnowledgePointsByTextbookId` / `createTextbook` / `updateTextbook` / `deleteTextbook` / `createChapter` / `updateChapterContent` / `deleteChapter` / `createKnowledgePoint` / `updateKnowledgePoint` / `deleteKnowledgePoint` / `reorderChapters` / `getTextbooksDashboardStats` / `getKnowledgePointOptions`(跨模块接口,供 questions 调用) **依赖关系**: - 依赖:`shared/*`、`@/auth` - 被依赖:`questions`(✅ P1-1 已修复:通过 textbooks data-access)、`exams`(通过类型)、`dashboard`(通过 data-access,P0-4 已修复) - ⚠️ UI 层跨模块依赖:`textbooks/components/knowledge-point-dialogs.tsx` 直接 import `questions/components/create-question-dialog`(P0 待解耦,详见 [textbooks-audit-report.md](audit/textbooks-audit-report.md)) **已知问题**: - ✅ 无跨模块 DB 访问(data-access 层) - ✅ actions 层编排模式标杆(权限校验 → 调用 data-access → revalidatePath) - ✅ data-access 层职责单一 - ✅ P2 已修复:`data-access.ts` 中 `byId.get(pid)!.children.push` 非空断言清理为安全守卫;`or(...)!` 非空断言清理为条件 push - ⚠️ P0 跨模块 UI 依赖:`knowledge-point-dialogs.tsx` 直接 import questions 模块组件 - ⚠️ P0 前端权限硬编码:`canEdit={true}` 按路由写死,未用 `usePermission().hasPermission()` - ⚠️ P0 全模块零 i18n:中英文文案硬编码,未接入 next-intl - ⚠️ P1 Server Action 未校验资源归属(chapterId 是否属于 textbookId) - ⚠️ P1 data-access 缺数据范围过滤(学生端未按年级过滤) - ⚠️ P1 缺 Error Boundary(无 error.tsx) - ⚠️ P1 知识点列表/弹窗存在重复实现(knowledge-point-panel.tsx 无调用方) - ⚠️ P1 学科/年级选项硬编码三处且彼此不一致 - ⚠️ P1 纯逻辑未导出,零单测 **文件清单**: | 文件 | 行数 | 职责 | |------|------|------| | `actions.ts` | 317 | 10 个 Server Action(写操作) | | `data-access.ts` | 514 | 教材/章节/知识点 CRUD + 跨模块查询接口 | | `types.ts` | 45 | 类型定义 | | `schema.ts` | 64 | Zod 校验 | | `hooks/use-knowledge-point-actions.ts` | 121 | 知识点操作 Hook | | `hooks/use-text-selection.ts` | 57 | 文本选区捕获 Hook | | `components/*` | 11 文件 | 教材编辑/知识图谱组件 | --- ## 2.6 grades(成绩模块)— 标杆模块(拆分范例) **职责**:成绩分析(录入/查询/统计/导出/趋势对比分析)。 **导出函数**: - Actions:`getGradeRecordsAction` / `createGradeRecordAction` / `updateGradeRecordAction` / `deleteGradeRecordAction` / `exportGradesAction` / `getGradeTrendAction` / `getClassComparisonAction` / `getSubjectComparisonAction` / `getGradeDistributionAction` / `getClassRankingAction` / `getRankingTrendAction` / `getGradeRecordByIdAction` / `getClassGradeStatsAction` / `getStudentGradeSummaryAction` / `batchCreateGradeRecordsAction` - Data-access:`getGradeRecords` / `getStudentGradeSummary` / `getClassRanking` / `getClassStudentsForEntry` / `getClassGradeStats` / `getClassGradeStatsWithMeta` / `getGradeTrend` / `getClassComparison` / `getSubjectComparison` / `getGradeDistribution` / `getRankingTrend` - Lib(✅ P1-2 新增):`toNumber` / `normalize` / `buildScopeClassFilter`(从 3 个 data-access 文件抽取的公共工具函数) - Stats-service(✅ P1-1 新增):`computeGradeStats` / `computeAverageScore` / `buildGradeTrendPoints` / `computeTrendAverage` / `computeClassComparisonStats` / `computeSubjectComparisonStats` / `computeGradeDistribution` / `buildRankingTrendPoints`(从 3 个 data-access 文件抽取的纯函数,使数据层专注 DB I/O,统计逻辑可独立测试) - Components(✅ P1-5 新增):`WidgetBoundary`(Error Boundary + Suspense + Skeleton 组合,含 a11y 属性) **依赖关系**: - 依赖:`shared/*`、`@/auth`、`classes`(✅ P1-1 已修复:通过 classes data-access.getClassExists/getClassNameById/getClassNamesByIds/getActiveStudentIdsByClassId/getStudentActiveClassId/getClassesByGradeId)、`school`(✅ P1-1 已修复:通过 school data-access.getSubjectOptions/getGradeOptions)、`users`(✅ P1-1 已修复:通过 users data-access.getUserNamesByIds) - 被依赖:`parent`(通过 data-access,合理)、`dashboard` **已知问题**: - ✅ P1-1 已修复:~~多处直查 `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`) **文件清单**: | 文件 | 行数 | 职责 | |------|------|------| | `actions.ts` | 359 | 7 个 Server Action(含 Zod 校验) | | `actions-analytics.ts` | 175 | 5 个分析 Action(含 Zod 校验) | | `data-access.ts` | 361 | 成绩 CRUD + 统计 | | `data-access-analytics.ts` | 266 | 趋势/对比分析 | | `data-access-ranking.ts` | 96 | 排名查询 | | `stats-service.ts` | - | 统计计算纯函数(P1-1 新增:8 个纯函数 + 2 个常量 + 2 个接口) | | `export.ts` | 214 | Excel 导出 | | `schema.ts` | 100 | Zod 校验(含 12 个查询 schema) | | `lib/grade-utils.ts` | 46 | 公共工具函数(toNumber/normalize/buildScopeClassFilter) | | `components/widget-boundary.tsx` | - | Widget 边界组件(P1-5 新增) | | `types.ts` | - | 类型定义 | --- ## 2.7 classes(班级模块)— 耦合最严重 **职责**:班级 CRUD + 学生/教师管理 + 邀请码注册。 **导出函数**: - Actions:`createTeacherClassAction` / `updateTeacherClassAction` / `deleteTeacherClassAction` / `createAdminClassAction` / `updateAdminClassAction` / `deleteAdminClassAction` / `createGradeClassAction` / `updateGradeClassAction` / `deleteGradeClassAction` - Data-access:`getAdminClasses` / `getTeacherClasses` / `getGradeManagedClasses` / `getStudentClasses` / `getClassDetails` / `getClassStudents` / `getClassSchedule` / `getClassHomeworkInsights` / `getGradeHomeworkInsights` / `getStudentsSubjectScores` / `verifyTeacherOwnsClass` / `getTeacherIdsByClassIds`(获取多个班级的所有教师 ID:班主任 + 任课教师,跨模块接口,供 messaging 模块调用)(✅ P0-5 已修复:classSchedule 写函数 createClassScheduleItem/updateClassScheduleItem/deleteClassScheduleItem 已迁移至 scheduling/data-access-class-schedule.ts,classes 模块仅保留 classSchedule 读函数;✅ P2 已修复:`getAccessibleClassIdsForTeacher` 使用 `Promise.all` 并行化 ownedIds 与 assignedIds 查询) - Schema:`CreateTeacherClassSchema` / `UpdateTeacherClassSchema` / `DeleteTeacherClassSchema` / `CreateAdminClassSchema` / `UpdateAdminClassSchema` / `DeleteAdminClassSchema` / `CreateGradeClassSchema` / `UpdateGradeClassSchema` / `DeleteGradeClassSchema` / `CreateClassScheduleItemSchema` / `UpdateClassScheduleItemSchema` / `DeleteClassScheduleItemSchema` / `EnrollStudentByEmailSchema` **依赖关系**: - 依赖:`shared/*`、`@/auth`、`school`(✅ P1-1 已修复:通过 school data-access.isGradeHead/isGradeManager/findGradeIdByHeadAndName)、`homework`(✅ P0-7 已修复:通过 `homework/data-access-classes` 暴露的函数获取作业数据,不再直查 homework/exams 表) - 被依赖:`exams`/`homework`/`grades`/`attendance`/`scheduling`/`dashboard`(通过 data-access,P0-4 已修复)/`parent`/`course-plans`/`users`(✅ P1-1 已修复:8+ 处直查 classes 表改为通过 classes data-access)/`messaging`(通过 data-access.getTeacherIdsByClassIds/getStudentActiveClassId,支持学生/家长给班级教师发消息) **已知问题**: - ✅ P0-1 已修复:`data-access.ts` 已拆分为 5 个文件(data-access/data-access-stats/data-access-schedule/data-access-students/data-access-admin),所有文件均 ≤800 行 - ✅ P0-5 已修复:classSchedule 写函数(createClassScheduleItem/updateClassScheduleItem/deleteClassScheduleItem)已迁移至 scheduling/data-access-class-schedule.ts,classes 模块仅保留 classSchedule 读函数(getStudentSchedule/getClassSchedule);新增 verifyTeacherOwnsClass 供 scheduling 模块跨模块校验教师班级归属 - ✅ P0-7 已修复:`data-access-stats.ts` 和 `data-access-students.ts` 不再直查 `homeworkAssignmentQuestions`/`homeworkAssignmentTargets`/`homeworkAssignments`/`homeworkSubmissions`/`exams` 表,改为调用 `homework/data-access-classes.ts` 暴露的函数(`getAssignmentIdsForStudents`/`getHomeworkAssignmentsWithSubject`/`getHomeworkAssignmentsByIds`/`getAssignmentMaxScoreById`/`getAssignmentTargetCounts`/`getHomeworkSubmissionsForStudents`/`getPublishedHomeworkAssignmentsWithSubject`/`getHomeworkSubmissionsForAssignments`) - ✅ P1-1 已修复:~~`actions.ts` 直查 `grades` 表做权限校验~~ 改为调用 `school/data-access` 函数 - ✅ P1-1 已修复:~~`getSessionTeacherId` 在 data-access 调用 `auth()`~~ 改为通过 `shared/lib/auth-guard.getAuthContext()` 获取 - ✅ P2 已修复:`data-access.ts` 中 `idByName.get(name)!` 非空断言清理为 `flatMap` 安全过滤;`data-access-admin.ts` 中同类非空断言清理 **文件清单**: | 文件 | 行数 | 职责 | |------|------|------| | `data-access.ts` | 548 | 核心班级 CRUD + 邀请码 + 教师班级管理(含 re-export 向后兼容) | | `data-access-stats.ts` | 513 | 作业统计查询(班级/年级作业洞察,通过 homework/data-access-classes 获取数据) | | `data-access-schedule.ts` | 93 | 课表查询(学生/班级课表只读,P0-5 已修复:写函数已迁移至 scheduling 模块) | | `data-access-students.ts` | 253 | 学生相关查询(科目成绩、学生名单、学生班级,通过 homework/data-access-classes 获取数据) | | `data-access-admin.ts` | 406 | 管理员班级管理(管理员班级 CRUD、年级管理班级查询) | | `actions.ts` | 785 | 17 个 Server Action(三组重复,使用 Zod schema 校验) | | `schema.ts` | 152 | Zod 校验(13 个 schema:教师/管理员/年级班级 CRUD + 课表 CRUD + 邮箱注册) | | `types.ts` | 201 | 类型定义(含跨领域类型污染) | --- ## 2.8 school(学校模块) **职责**:学校/学年/部门/年级的 CRUD。 **导出函数**: - Actions:`createSchoolAction` / `updateSchoolAction` / `deleteSchoolAction` / `createAcademicYearAction` / `updateAcademicYearAction` / `deleteAcademicYearAction` / `createDepartmentAction` / `updateDepartmentAction` / `deleteDepartmentAction` / `createGradeAction` / `updateGradeAction` / `deleteGradeAction`(编排层:权限校验 + Zod 校验 + 调用 data-access + revalidatePath + after(logAudit)) - Data-access:只读查询(`getSchools` / `getGrades` / `getDepartments` / `getAcademicYears` / `getStaffOptions` / `getGradesForStaff`)+ 写操作(`create/update/delete` × `Department/School/Grade/AcademicYear`) **依赖关系**: - 依赖:`shared/*`、`@/auth`、`users`(⚠️ `getStaffOptions` 直查 users/roles,可接受) - 被依赖:`exams`(✅ P1-1 已修复:通过 school data-access)、`homework`(✅ P1-1 已修复:通过 school data-access)、`grades`(✅ P1-1 已修复:通过 school data-access)、`questions`(✅ P1-1 已修复:通过 textbooks data-access)、`classes`(✅ P1-1 已修复:通过 school data-access)、`course-plans`(合理) **已知问题**: - ✅ P0-8 已修复:`actions.ts` 不再直接导入 `db` 和 schema,所有 DB 写操作下沉到 `data-access.ts`,符合三层架构 - ✅ P2 已修复:`logAudit()` 通过 Next.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` 模块重复定义,两套实现并存 - ⚠️ P0-5(2026-06-22 审计发现):`school/components/*` 4 个组件均缺少 i18n(`schools-view.tsx` 已修复,其余 3 个待修复);缺少 Error Boundary / Skeleton - ✅ P0-5 部分修复(2026-06-22):新增 `school.json` i18n 文件,`schools-view.tsx` 接入 `useTranslations("school")` **文件清单**: | 文件 | 行数 | 职责 | |------|------|------| | `actions.ts` | 349 | 12 个 Server Action(编排层,无 DB 直访) | | `data-access.ts` | 504 | 只读查询 + 12 个写操作 + 跨模块查询接口(`isGradeHead`/`isGradeManager`/`findGradeIdByHeadAndName`/`getGradeNameById`/`getSubjectNameById`) | | `schema.ts` | 51 | Zod 校验 | | `types.ts` | 96 | 类型定义(含 Insert/Update 入参类型) | --- ## 2.8b grade-management(年级管理模块)— ⚠️ 死模块 > **2026-06-22 审计发现**:该模块拥有完整的理想架构(Service 接口 + Context DI + 角色配置 + Error Boundary + Skeleton + i18n + hooks 分离),但 **13 个相关页面中无任何一个导入此模块**。`management/grade/*` 页面实际依赖 `classes` 和 `school` 模块的 data-access。详见 `docs/architecture/audit/school-grade-class-audit-report.md`。 **职责**:年级 CRUD + 年级作业洞察(重构版,对标理想架构模式)。 **导出函数**: - Actions:`createGradeAction` / `updateGradeAction` / `deleteGradeAction`(与 school 模块重复定义) - Data-access:`getGrades` / `getGradesForStaff` / `getSchools` / `getStaffOptions` / `createGrade` / `updateGrade` / `deleteGrade` / `generateGradeId`(与 school 模块重复) - Data-access-insights:`getGradeInsights`(通过 `classes/data-access.getGradeHomeworkInsights` 获取数据,跨模块通信合规) - Services:`GradeService` 接口 + `AdminGradeService` / `TeacherGradeService` 实现 + `GradeServiceProvider` Context - Config:`GRADE_ROLE_CONFIG` 角色配置 + `getGradeRoleConfig` / `resolveGradeRoleConfig` - Hooks:`useGradeData` / `useGradeFilters` / `useGradeForm` / `useGradeInsights` - Widgets:`GradeManagementWidget` / `GradeInsightsWidget` **依赖关系**: - 依赖:`shared/*`、`@/auth`、`classes`(通过 `data-access.getGradeHomeworkInsights`,合规) - 被依赖:⚠️ **无任何模块或页面依赖此模块** **已知问题**: - ⚠️ P0-1:模块完全未被使用(死模块),所有页面使用 school/classes 模块的 data-access - ⚠️ P0-2:年级 CRUD 逻辑与 school 模块重复定义 **文件清单**: | 文件 | 行数 | 职责 | |------|------|------| | `actions.ts` | 213 | 3 个 Server Action(含审计日志,比 school 模块版本更完善) | | `data-access.ts` | 238 | 年级 CRUD + 查询(与 school 模块重复) | | `data-access-insights.ts` | 75 | 年级洞察(适配 classes 模块数据) | | `types.ts` | 149 | 类型定义(含 GradeService 接口、角色配置、埋点接口) | | `services/*.tsx` | 4 文件 | GradeService 接口 + 实现 + Context DI | | `config/role-config.ts` | 66 | 角色配置驱动设计 | | `hooks/*.ts` | 4 文件 | 数据/筛选/表单/洞察 hooks | | `widgets/*.tsx` | 2 文件 | 管理面板 + 洞察面板 | | `components/*.tsx` | 11 文件 | 表格/工具栏/对话框/骨架屏/错误边界/空状态 | --- ## 2.9 scheduling(排课模块) **职责**:自动排课算法 + 课表调整 + 排课规则管理。 **导出函数**: - Actions:`autoScheduleAction` / `applyAutoScheduleAction` / `getSchedulingRulesAction` / `updateSchedulingRulesAction` / `getScheduleChangesAction` / `createScheduleChangeAction` / `updateScheduleChangeAction` / `deleteScheduleChangeAction` - Data-access:`getSchedulingRules` / `getScheduleChanges` / `getAdminClassesForScheduling` / `getTeachersForScheduling` / `getClassroomsForScheduling` / `getClassSubjectsForScheduling` - Data-access-class-schedule(✅ P0-5 新增):`createClassScheduleItem` / `updateClassScheduleItem` / `deleteClassScheduleItem`(从 classes 模块迁移,含教师班级归属校验,通过 `classes/data-access.verifyTeacherOwnsClass` 跨模块校验) - Data-access(低级写入):`insertClassScheduleItem` / `updateClassScheduleItemById` / `deleteClassScheduleItemById` / `replaceClassSchedule`(统一 classSchedule DB 写入口) - 算法:`findOptimalSlot` / `validateSchedule` / `autoSchedule` / `buildDefaultTimeSlots`(纯函数,标杆) **依赖关系**: - 依赖:`shared/*`、`@/auth`、`classes`(✅ P0-5 已修复:通过 `classes/data-access.verifyTeacherOwnsClass` / `getTeacherIdForMutations` 校验教师班级归属,不再直写 classSchedule 表的写入口分散在 classes 模块)、`school`(⚠️ 排课辅助查询,可接受)、`users`(✅ P1-1 已修复:通过 users data-access.getUserNamesByIds) - 被依赖:`classes/actions.ts`(✅ P0-5 已修复:通过 `scheduling/data-access-class-schedule` 调用写函数) **已知问题**: - ✅ P0-5 已修复:~~`applyAutoScheduleAction` 直接 transaction 写 `classSchedule` 表(第三个写入口)~~ 改为调用 `replaceClassSchedule` 统一写入口;classSchedule 所有写函数统一在 scheduling 模块 - ✅ P1-1 已修复:~~`autoScheduleAction` 直查 `users` 表~~ 改为调用 `users/data-access.getUserNamesByIds` - ⚠️ P2:`actions.ts` 末尾 re-export data-access 函数(反模式) - ✅ P2 已修复:`data-access.ts` 中 3 处非空断言清理(`userIds[0]!`、`rows[i]!`、`rows[j]!`);`auto-scheduler.ts` 中 2 处非空断言清理(`schedule[i]!`、`schedule[j]!`) - ✅ `auto-scheduler.ts` 是算法独立化的最佳实践(纯函数、无 DB、可测试) **文件清单**: | 文件 | 行数 | 职责 | |------|------|------| | `auto-scheduler.ts` | 310 | 排课算法(纯函数,标杆) | | `actions.ts` | 302 | 8 个 Server Action | | `data-access.ts` | 398 | 排课辅助查询 + 规则/变更 CRUD + classSchedule 低级写入(insert/update/delete/replace) | | `data-access-class-schedule.ts` | 165 | classSchedule 业务写入(P0-5 新增,从 classes 模块迁移,含教师归属校验) | | `schema.ts` | - | Zod 校验 | | `types.ts` | - | 类型定义(含 P0-5 迁移的 CreateClassScheduleItemInput / UpdateClassScheduleItemInput) | --- ## 2.10 attendance(考勤模块)— 结构典范 **职责**:考勤记录管理 + 统计分析 + 规则配置。 **导出函数**: - Actions(10 个):`recordAttendanceAction` / `batchRecordAttendanceAction` / `updateAttendanceAction` / `deleteAttendanceAction` / `getAttendanceAction` / `getStudentAttendanceAction` / `getClassAttendanceStatsAction` / `getClassAttendanceForDateAction` / `saveAttendanceRulesAction` / `getAttendanceRulesAction` - Data-access:`getAttendanceRecords` / `createAttendanceRecord` / `updateAttendanceRecord` / `deleteAttendanceRecord` / `getClassStudentsForAttendance` / `getAttendanceStats`(管理员考勤总览页统计概览,基于 `getAttendanceRecords` 聚合)/ `upsertAttendanceRules` / `getAttendanceRules` - Data-access-stats:`getStudentAttendanceSummary` / `getClassAttendanceStats` / `computeStats`(⚠️ 未导出,无法单测) - Components:`AttendanceSheet`(批量点名表单)/ `AttendanceRecordList`(记录列表 + 删除)/ `AttendanceFilters`(URL 同步筛选器)/ `AttendanceStatsCard`(单卡片统计)/ `AttendanceStatsCards`(管理员 6 卡片总览)/ `AttendanceStatsClassSelector`(班级筛选 ChipNav)/ `AttendanceRulesForm`(规则配置表单)/ `StudentAttendanceView`(学生/家长只读视图) **依赖关系**: - 依赖:`shared/*`、`@/auth`、`classes`(⚠️ P1-1 未修复:`getClassStudentsForAttendance` 仍直查 `classEnrollments` 表) - 被依赖:`parent`(⚠️ 跨模块 UI 类型依赖:3 个 parent 组件直接 import `@/modules/attendance/types`) **已知问题**(详见 `docs/architecture/audit/attendance-elective-audit-report.md`): - ❌ P0:`getAttendanceStats` 统计失真——调用 `getAttendanceRecords`(默认 pageSize=20)后对 `items` 聚合,仅基于前 20 条记录计算总览数据 - ❌ P0:`getClassStudentsForAttendance` 仍直查 `classEnrollments` 表(架构图此前声称已修复,实际未修复) - ❌ P0:6 个读 Action 无调用方(页面绕过 Action 直接调用 data-access),违反三层架构 - ❌ P0:update/delete Action 缺资源归属校验(教师 A 可修改/删除教师 B 的记录) - ❌ P0:i18n 完全缺失(`ATTENDANCE_STATUS_LABELS` 硬编码英文,组件中硬编码中文) - ❌ P0:错误边界完全缺失(5 个角色目录均无 `error.tsx`) - ⚠️ P1:`computeStats` 未导出,无法单测 - ⚠️ P1:`attendance-sheet.tsx` 使用 `window.confirm`(与项目 AlertDialog 模式不一致) - ⚠️ P1:`attendance-sheet.tsx` 存在 `{} as Record` 类型断言 - ⚠️ 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 视图语义化标签(`
` / `
` / `