Files
NextEdu/docs/architecture/004_architecture_impact_map.md
SpecialX c45b3488c5 feat(admin): 补全 admin 模块核心功能与产品体验优化
修复 v4 报告中的 13 个产品体验问题:新增用户管理列表页和系统设置页,重组导航菜单并补充缺失入口,增加角色切换机制,Dashboard 增加快捷操作和 recharts 趋势图表,考勤增加统计概览,排课增加课表网格视图,统一 Toast 操作反馈,同步更新架构文档
2026-06-22 13:38:07 +08:00

1918 lines
143 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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() 提供细粒度权限校验
```
---
# 第二部分:模块清单
> 每个模块包含:职责 · 导出函数 · 依赖关系 · 已知问题 · 文件清单
## 2.1 shared基础设施层
**职责**:提供全项目共享的 DB Schema、工具函数、权限系统、UI 基础组件、通用 Hooks。
**导出函数**(核心):
- `getAuthContext()` / `requirePermission(p)` / `requireAuth()` — 认证与权限
- `resolvePermissions(roles)` / `resolveDataScope(userId, roles)` — 权限解析
- `logAudit()` / `logLoginEvent()` / `logDataChange()` — 日志记录
- `createAiChatCompletion()` / `parseAiChatPayload()` — AI 调用
- `validatePassword()` / `isAccountLocked()` / `rateLimit()` — 安全策略
- `exportToExcel()` / `parseExcel()` / `generateTemplate()` — Excel 工具
- `cn()` / `formatDate()` / `formatFileSize()` — 通用工具
- `getInitials(name)` / `formatDateForFile(d?)` — 通用工具P1-c / P1-a 重构新增:从 parent/lib/utils.ts、grades/export-button.tsx 等多处重复实现抽取)
- `downloadBase64File(base64, filename, mimeType?)` / `downloadBlob(blob, filename)` — 客户端文件下载P1-c 重构新增:从 grades/export-button、users/user-import-dialog、audit/audit-log-export-button 三处重复实现抽取,位于 `lib/download.ts`
**共享组件导出**P0-b / P1-a / P1-b / P1-c / P2-a / P2-b / P3-a / P3-b / P3-c / P3-d / 第二轮 P0-1/P0-2/P0-3/P1-1/P1-2/P1-3/P1-4 重构新增,按类别组织):
| 类别 | 组件 | 文件 | 用途 | 消费方数量 |
|------|------|------|------|-----------|
| **UI 组件** | `StatCard` | `components/ui/stat-card.tsx` | 统计卡片(标题+数值+图标+描述+跳转+骨架屏) | 8 个P1-a |
| **UI 组件** | `StatItem` | `components/ui/stat-item.tsx` | 紧凑统计项label+icon+value+hint用于统计面板网格 | 8 个P1-a |
| **UI 组件** | `ChipNav` | `components/ui/chip-nav.tsx` | 芯片导航组(通过 URL search params 切换筛选维度Link 跳转) | 3 个P1-b |
| **UI 组件** | `PageHeader` | `components/ui/page-header.tsx` | 页面头部(标题+描述+icon+actions响应式布局 | 2 个P2-b: profile/page.tsx, settings/security/page.tsx |
| **UI 组件** | `FilterBar` / `FilterSearchInput` / `FilterResetButton` | `components/ui/filter-bar.tsx` | 筛选栏容器+搜索框+重置按钮统一布局壳URL 状态由各模块处理) | 5 个P3-b: exam/textbook/question/audit-log/login-log filters |
| **UI 组件** | `ConfirmDeleteDialog` | `components/ui/confirm-delete-dialog.tsx` | 通用删除确认对话框AlertDialog 包装,支持自定义 confirmText/cancelText | 5 个P0-1: announcement-detail, message-detail, course-plan-detail, grade-classes-view, students-table |
| **UI 组件** | `Pagination` | `components/ui/pagination.tsx` | 通用分页 UIShowing X-Y of Z + Page X of Y + 上一页/下一页按钮) | 3 个P0-2: audit-log-table, login-log-table, data-change-log-table |
| **UI 组件** | `EmptyTableRow` | `components/ui/empty-table-row.tsx` | 表格空状态行TableRow + TableCell 居中显示空状态文案) | 3 个P0-3: audit-log-table, login-log-table, data-change-log-table |
| **UI 组件** | `StatusBadge` | `components/ui/status-badge.tsx` | 通用状态徽章Badge + 状态→variant/label/className 映射表,修复 in_progress 颜色不一致 bug | 9+ 个P1-1: audit 3 文件, grades 2 文件, student/learning/assignments, parent/child-homework-summary, student-upcoming-assignments-card, question-columns |
| **表单字段** | `TextField` | `components/form-fields/text-field.tsx` | 通用文本字段FormField + Input 包装,支持 text/number/password/datetime-local 类型 + value 转换器) | 3 个文件 16 处P1-2: profile-settings-form 6, exam-basic-info-form 4, ai-provider-settings-card 4 |
| **表单字段** | `SelectField` | `components/form-fields/select-field.tsx` | 通用选择字段FormField + Select 包装,支持 toSelectValue/fromSelectValue 处理 number↔string | 4 个文件 8 处P1-2: exam-basic-info-form 3, ai-provider-settings-card 1, create-question-dialog 2, profile-settings-form 1 |
| **表单字段** | `TextareaField` | `components/form-fields/textarea-field.tsx` | 通用多行文本字段FormField + Textarea 包装) | 1 个P1-2: create-question-dialog |
| **图表组件** | `ChartCardShell` | `components/charts/chart-card-shell.tsx` | 图表卡片外壳Card+Header+EmptyState+Content 统一结构) | 8 个P3-c |
| **图表组件** | `TrendLineChart` | `components/charts/trend-line-chart.tsx` | 趋势折线图LineChart 统一配置,支持单/多系列) | 8 个P3-c: grade-trend-chart 等) |
| **图表组件** | `SimpleBarChart` | `components/charts/simple-bar-chart.tsx` | 柱状图BarChart 统一配置,支持单/多 Bar + Cell 分桶着色) | 8 个P3-c: grade-distribution-chart 等) |
| **图表组件** | `ComparisonRadarChart` | `components/charts/comparison-radar-chart.tsx` | 对比雷达图RadarChart 统一配置,支持双 Radar 对比) | 8 个P3-c: subject-comparison-chart, mastery-radar-chart 等) |
| **课表组件** | `ScheduleList` / `ScheduleListItem` | `components/schedule/schedule-list.tsx` | 课表列表+列表项(课程+时间+地点+班级徽章separator/card 两种变体) | 3 个P3-a: student-today-schedule-card, child-schedule-card, student-schedule-view |
| **题库组件** | `QuestionBankFilters` | `components/question/question-bank-filters.tsx` | 题库筛选栏(搜索+题型+难度default/compact 两种布局) | 2 个P3-d: exam-assembly, question-bank-picker |
| **设置组件** | `SettingsView` | `modules/settings/components/settings-view.tsx` | 统一设置页布局5 标签页General/Notifications/Appearance/Security/AI角色差异通过 props 注入Tab URL 持久化,登出二次确认) | 4 个P2-a: admin/teacher/student/parent 设置页) |
**共享 Hooks 导出**(第二轮 P1-4 重构新增):
| Hook | 文件 | 签名 | 用途 | 消费方 |
|------|------|------|------|--------|
| `useActionMutation` | `hooks/use-action-mutation.ts` | `useActionMutation<T>(options?): { isWorking, mutate }` | 通用 Server Action mutation Hook替代 50+ 文件中重复的 setIsWorking + try/catch/finally + toast 模式 | 1 个示范P1-4: schools-view潜在影响 50+ 文件 |
| `useActionQuery` | `hooks/use-action-query.ts` | `useActionQuery<T>(action, options?): { data, loading, error, refetch }` | 通用 Server Action 查询 Hook替代 11 个文件中重复的 useEffect + useState(loading) + Action().then().catch().finally() 模式,内置竞态防护 | 1 个示范P1-4: create-question-dialog潜在影响 11 个文件 |
**共享工具函数导出**(第二轮 P1-3 重构新增):
| 函数 | 文件 | 签名 | 用途 | 消费方 |
|------|------|------|------|--------|
| `formatDateTime` | `lib/utils.ts` | `formatDateTime(date, locale?): string` | 国际化日期+时间格式化(含小时、分钟) | 4 个P1-3: lesson-plan-card, version-history-drawer, proctoring-dashboard, exam-ai-generator |
| `formatLongDate` | `lib/utils.ts` | `formatLongDate(date, locale?): string` | 国际化长日期格式化(含星期、完整月份名) | 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 持久化步骤、局部错误收集、家长多子女动态行、跳过机制明确化
**文件清单**
| 文件 | 行数 | 职责 |
|------|------|------|
| `db/schema.ts` | 1111 | 54 张表定义(超硬上限) |
| `db/relations.ts` | - | 表关系定义 |
| `db/index.ts` | - | Drizzle 客户端 |
| `lib/auth-guard.ts` | - | 认证上下文 + 权限校验 + DataScope |
| `lib/permissions.ts` | - | 角色-权限映射 |
| `lib/session.ts` | 38 | session 获取单一入口getSessionserver-onlydynamic import 打破循环) |
| `lib/ai.ts` | 9 | 向后兼容重导出P2-2 已拆分到 `ai/` 目录) |
| `lib/ai/payload-parser.ts` | 78 | 请求负载解析 |
| `lib/ai/api-key-crypto.ts` | 28 | API Key 加密/解密 |
| `lib/ai/provider-config.ts` | 61 | Provider 配置查询 |
| `lib/ai/client.ts` | 58 | AI 客户端创建与调用 |
| `lib/ai/errors.ts` | 8 | 错误格式化 |
| `lib/ai/index.ts` | 5 | 聚合导出 |
| `lib/audit-logger.ts` | - | 操作日志(通过 session.ts 获取 sessionhttp-utils 获取 IP/UA |
| `lib/change-logger.ts` | - | 数据变更日志(通过 session.ts 获取 sessionhttp-utils 获取 IP |
| `lib/login-logger.ts` | - | 登录日志(通过 http-utils 获取 IP/UA |
| `lib/password-policy.ts` | - | 密码策略纯函数 |
| `lib/rate-limit.ts` | - | 内存滑动窗口限流 |
| `lib/role-utils.ts` | 31 | 角色规范化纯函数normalizeRole / resolvePrimaryRole |
| `lib/bcrypt-utils.ts` | 18 | bcrypt 哈希前缀规范化纯函数 |
| `lib/http-utils.ts` | 44 | 请求头解析resolveClientIp / getUserAgentserver-only |
| `lib/password-security-service.ts` | 84 | 密码安全 DB 操作(账户锁定/失败登录追踪server-only |
| `lib/excel.ts` | - | Excel 导入导出 |
| `lib/file-storage.ts` | - | 文件存储抽象 |
| `hooks/use-permission.ts` | - | 客户端权限 Hook |
| `components/ui/*` | 34 文件 | shadcn/ui 标准组件 |
| `components/ui/stat-card.tsx` | 95 | StatCard 统计卡片P1-a 新增) |
| `components/ui/stat-item.tsx` | 38 | StatItem 紧凑统计项P1-a 新增) |
| `components/ui/chip-nav.tsx` | 78 | ChipNav 芯片导航P1-b 新增) |
| `components/ui/page-header.tsx` | 44 | PageHeader 页面头部P2-b 新增,含 icon 属性) |
| `components/ui/filter-bar.tsx` | 124 | FilterBar + FilterSearchInput + FilterResetButtonP3-b 新增) |
| `components/charts/chart-card-shell.tsx` | 90 | ChartCardShell 图表卡片外壳P3-c 新增) |
| `components/charts/trend-line-chart.tsx` | 153 | TrendLineChart 趋势折线图P3-c 新增) |
| `components/charts/simple-bar-chart.tsx` | 162 | SimpleBarChart 柱状图P3-c 新增) |
| `components/charts/comparison-radar-chart.tsx` | 143 | ComparisonRadarChart 对比雷达图P3-c 新增) |
| `components/schedule/schedule-list.tsx` | 112 | ScheduleList + ScheduleListItem 课表列表P3-a 新增) |
| `components/question/question-bank-filters.tsx` | 137 | QuestionBankFilters 题库筛选栏P3-d 新增) |
| `lib/download.ts` | 47 | downloadBase64File + downloadBlob 客户端下载工具P1-c 新增) |
| `lib/utils.ts` | - | 通用工具P1-a/P1-c 新增 getInitials + formatDateForFile |
| `components/onboarding-gate.tsx` | 312 | ~~引导流程(业务泄漏)~~ 已废弃,逻辑迁移至 `modules/onboarding/`P2-4 已修复) |
| `components/global-search.tsx` | 221 | 全局搜索(业务泄漏) |
| `types/permissions.ts` | 157 | 61 个权限点常量 + Role/DataScope/AuthContext 类型 |
---
## 2.2 exams考试模块
**职责**:考试全生命周期管理(创建/编辑/预览/发布/删除/复制)+ AI 辅助出题。
**导出函数**
- 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-accessP0-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-accessactions.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 ActionP1-2 已修复,无直接 DB 操作) |
| `ai-pipeline.ts` | 857 | AI 出题管线(超限) |
| `data-access.ts` | 473 | 考试 CRUD含 P1-2 新增 7 个写/查询函数P0-1/P0-2 已修复:通过 questions/classes data-access 跨模块通信) |
| `types.ts` | 31 | 类型定义 |
| `hooks/use-exam-preview.ts` | 295 | 预览 Hook |
| `utils/normalize-structure.ts` | 57 | v3 新增exam.structure 运行时校验与归一化(从 build/page.tsx 提取) |
| `components/*` | 18 文件 | 考试表单/组卷/预览组件 |
---
## 2.3 homework作业模块
**职责**:作业全生命周期(创建/发布/作答/批改/分析)。
**导出函数**
- 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-write10 个写操作函数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 ActionP1-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 ActionP1-2 已修复,无直接 DB 操作) |
| `data-access.ts` | 260 | 题目 CRUD + 知识点选项(含 P1-2 新增 4 个写/查询函数) |
| `schema.ts` | 18 | Zod 校验 |
| `types.ts` | 34 | 类型定义 |
---
## 2.5 textbooks教材模块— 标杆模块
**职责**:教材与知识体系管理(教材/章节树形结构、知识点 CRUD、Markdown 内容编辑、知识图谱)。
**导出函数**
- Actions`getTextbooksAction` / `getTextbookByIdAction` / `createTextbookAction` / `updateTextbookAction` / `deleteTextbookAction` / `getChaptersAction` / `createChapterAction` / `updateChapterAction` / `deleteChapterAction` / `getKnowledgePointsAction` / `createKnowledgePointAction` / `updateKnowledgePointAction` / `deleteKnowledgePointAction`
- Data-access与 actions 一一对应的 data-access 函数
**依赖关系**
- 依赖:`shared/*``@/auth`
- 被依赖:`questions`(✅ P1-1 已修复:通过 textbooks data-access`exams`(通过类型)、`dashboard`(通过 data-accessP0-4 已修复)
**已知问题**
- ✅ 无跨模块 DB 访问
- ✅ actions 层编排模式标杆(权限校验 → 调用 data-access → revalidatePath
- ✅ data-access 层职责单一
- ✅ P2 已修复:`data-access.ts``byId.get(pid)!.children.push` 非空断言清理为安全守卫;`or(...)!` 非空断言清理为条件 push
**文件清单**
| 文件 | 行数 | 职责 |
|------|------|------|
| `actions.ts` | 276 | 13 个 Server Action标杆 |
| `data-access.ts` | 428 | 教材/章节/知识点 CRUD |
| `types.ts` | 79 | 类型定义 |
| `hooks/use-knowledge-point-actions.ts` | 121 | 知识点操作 Hook |
| `components/*` | 12 文件 | 教材编辑/知识图谱组件 |
---
## 2.6 grades成绩模块— 标杆模块(拆分范例)
**职责**:成绩分析(录入/查询/统计/导出/趋势对比分析)。
**导出函数**
- Actions`getGradeRecordsAction` / `createGradeRecordAction` / `updateGradeRecordAction` / `deleteGradeRecordAction` / `exportGradesAction` / `getGradeTrendAction` / `getClassComparisonAction` / `getSubjectComparisonAction` / `getGradeDistributionAction` / `getClassRankingAction` / `getRankingTrendAction`
- Data-access`getGradeRecords` / `getStudentGradeSummary` / `getClassRanking` / `getClassStudentsForEntry` / `getClassGradeStats` / `getClassGradeStatsWithMeta` / `getGradeTrend` / `getClassComparison` / `getSubjectComparison` / `getGradeDistribution` / `getRankingTrend`
**依赖关系**
- 依赖:`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
- ⚠️ P2统计计算业务逻辑混入 data-access`getClassGradeStats` / `getGradeDistribution`
- ✅ actions 层无直接 DB 访问(标杆)
- ✅ data-access 按职责拆分为 3 个文件(标杆)
- ✅ P2 已修复:`export.ts``scoreMap.get(r.studentId)!` 非空断言清理为安全守卫(`if (!subjMap) continue`
**文件清单**
| 文件 | 行数 | 职责 |
|------|------|------|
| `actions.ts` | 312 | 6 个 Server Action |
| `actions-analytics.ts` | 133 | 5 个分析 Action |
| `data-access.ts` | 419 | 成绩 CRUD + 统计 |
| `data-access-analytics.ts` | 293 | 趋势/对比分析 |
| `data-access-ranking.ts` | 121 | 排名查询 |
| `export.ts` | 214 | Excel 导出 |
| `schema.ts` | 52 | Zod 校验 |
| `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.tsclasses 模块仅保留 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-accessP0-4 已修复)/`parent`/`course-plans`/`users`(✅ P1-1 已修复8+ 处直查 classes 表改为通过 classes data-access/`messaging`(通过 data-access.getTeacherIdsByClassIds/getStudentActiveClassId支持学生/家长给班级教师发消息)
**已知问题**
- ✅ P0-1 已修复:`data-access.ts` 已拆分为 5 个文件data-access/data-access-stats/data-access-schedule/data-access-students/data-access-admin所有文件均 ≤800 行
- ✅ P0-5 已修复classSchedule 写函数createClassScheduleItem/updateClassScheduleItem/deleteClassScheduleItem已迁移至 scheduling/data-access-class-schedule.tsclasses 模块仅保留 classSchedule 读函数getStudentSchedule/getClassSchedule新增 verifyTeacherOwnsClass 供 scheduling 模块跨模块校验教师班级归属
- ✅ P0-7 已修复:`data-access-stats.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展示用可接受
**文件清单**
| 文件 | 行数 | 职责 |
|------|------|------|
| `actions.ts` | 326 | 12 个 Server Action编排层无 DB 直访) |
| `data-access.ts` | 320 | 只读查询 + 12 个写操作CRUD |
| `schema.ts` | 51 | Zod 校验 |
| `types.ts` | 96 | 类型定义(含 Insert/Update 入参类型) |
---
## 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`getAttendanceRecordsAction` / `createAttendanceRecordAction` / `updateAttendanceRecordAction` / `deleteAttendanceRecordAction` / `getStudentAttendanceAction` / `getAttendanceStatsAction`
- Data-access`getAttendanceRecords` / `createAttendanceRecord` / `updateAttendanceRecord` / `deleteAttendanceRecord` / `getClassStudentsForAttendance` / `getAttendanceStats`(管理员考勤总览页统计概览,基于 `getAttendanceRecords` 聚合)
- Components`AttendanceStatsCards`(管理员考勤总览页 6 卡片统计概览)
**依赖关系**
- 依赖:`shared/*``@/auth``classes`(✅ P1-1 已修复:通过 classes data-access.getTeacherClasses/getAdminClasses
- 被依赖:无
**已知问题**
- ✅ P1-1 已修复:~~`getClassStudentsForAttendance` 直查 `classEnrollments`~~ 改为通过 classes data-access 获取
- ✅ stats 独立拆分为 `data-access-stats.ts`(拆分范例)
- ✅ DataScope 完整接入 6 种 scope 类型
- ✅ actions 层无直接 DB 访问
**文件清单**
| 文件 | 行数 | 职责 |
|------|------|------|
| `actions.ts` | 271 | 6 个 Server Action |
| `data-access.ts` | 309 | 考勤 CRUD + 管理员统计概览 |
| `data-access-stats.ts` | 145 | 统计逻辑(拆分范例) |
| `schema.ts` | - | Zod 校验 |
| `types.ts` | - | 类型定义 |
---
## 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-accessP0-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 Actionprofile 更新 + 模板下载/导入/导出 + 角色更新 + 删除) |
| `data-access.ts` | 394 | getUserProfile + 用户查询 + 管理员用户列表分页查询getAdminUsers/getAdminUserRoles |
| `components/admin-users-view.tsx` | 290 | 管理员用户列表客户端组件(搜索+筛选+表格+分页+删除对话框) |
---
## 2.12 dashboard仪表盘模块
**职责**:管理员/教师/学生仪表盘数据聚合 + 管理员趋势图表。
**导出函数**
- Data-access`getAdminDashboardData` / `getTeacherDashboardData` / `getStudentDashboardData`
- Components`AdminDashboardView` / `UserGrowthChart`recharts 折线图,复用于用户增长趋势与作业提交趋势)
**依赖关系**
- 依赖:`shared/*``@/auth``classes`(通过 data-access合理`homework`(通过 data-access合理`grades`(合理)、`users`/`textbooks`/`questions`/`exams`(通过各模块 dashboard stats 函数P0-4 已修复)、`recharts`UserGrowthChart
- 被依赖:无
**已知问题**
- ✅ P0-4 已修复:`getAdminDashboardData` 改为并行调用各模块 dashboard stats 函数(`getUsersDashboardStats`/`getClassesDashboardStats`/`getTextbooksDashboardStats`/`getQuestionsDashboardStats`/`getExamsDashboardStats`/`getHomeworkDashboardStats`),不再直查跨模块表
- ✅ P1-1 已修复:~~教师仪表盘直查 `users` 表获取教师姓名~~ 改为通过 users data-access 获取
- ✅ 学生/教师仪表盘正确通过各模块 data-access 获取数据
- V1 新增:`AdminDashboardData` 类型新增 `userGrowth`/`homeworkTrend` 字段(`Array<{ date: string; count: number }>``data-access.ts` 当前返回空数组占位,待后续接入真实统计
**文件清单**
| 文件 | 行数 | 职责 |
|------|------|------|
| `data-access.ts` | - | 仪表盘数据聚合P0-4 已修复,通过各模块 data-access 获取数据V1 新增 userGrowth/homeworkTrend 占位字段) |
| `types.ts` | - | 类型定义V1 新增 userGrowth/homeworkTrend 字段) |
| `components/admin-dashboard/admin-dashboard.tsx` | - | 管理员仪表盘视图V1 新增趋势图表区域:用户增长趋势 + 作业提交趋势) |
| `components/admin-dashboard/user-growth-chart.tsx` | - | recharts 折线图组件V1 新增,复用于两个趋势卡片) |
| `components/*` | 14 文件 | 三种角色仪表盘组件 |
---
## 2.13 messaging私信模块
**职责**站内私信messages 表 CRUD
**导出函数**
- Actions`sendMessageAction` / `getMessagesAction` / `getMessageAction` / `deleteMessageAction` / `getNotificationsAction` / `markNotificationReadAction` / `markAllNotificationsReadAction` / `getNotificationPreferencesAction` / `updateNotificationPreferencesAction`
- Data-access`getMessages` / `getMessageById` / `getMessageThread` / `createMessage` / `markMessageAsRead` / `deleteMessage` / `getUnreadMessageCount` / `getRecipients`(按 DataScope 过滤可发送对象class_taught 教师→学生、grade_managed 年级管理员→教师/学生、all 管理员、class_members 学生→自己班级的任课教师/班主任、children 家长→孩子的班主任/任课教师;通过 classes data-access.getTeacherIdsByClassIds/getStudentActiveClassId 获取班级教师 ID通知 CRUD 通过 re-export 从 notifications 模块重导出,保持向后兼容)
- Notification-preferences~~re-export shim实际逻辑在 `notifications/preferences.ts`~~ ✅ P0-b 已修复:`notification-preferences.ts` 文件已删除(通知模块去重),消费方改为直接从 `@/modules/notifications/preferences` 导入 `getNotificationPreferences` / `upsertNotificationPreferences`
**依赖关系**
- 依赖:`shared/*``@/auth``notifications`(✅ P0-4 / P1-5 已修复:通过 `sendNotification` dispatcher 发送通知,通知 CRUD 和偏好已迁移至 notifications 模块)、`classes`(通过 data-access.getTeacherIdsByClassIds/getStudentActiveClassId 获取班级教师 ID支持学生 class_members 和家长 children 数据范围)、`users`(通过 data-access.getUserNamesByIds 获取用户显示名称)
- 被依赖:`notifications`(✅ 已消除反向依赖)、`settings`(通知偏好表单)、`layout`(通知下拉)
**已知问题**
- ✅ P0-4 已修复:~~`sendMessageAction` 绕过 notifications dispatcher 直接调用 `createNotification`~~ 改为调用 `notifications.sendNotification`,通知 CRUD 已迁移至 notifications 模块
- ✅ P0 已修复:~~与 notifications 双向依赖 + 职责重叠~~ 通知相关表messageNotifications / notificationPreferences所有权已移交 notifications 模块messaging 仅保留 messages 表
- ✅ P1-5 已修复:~~同时管理 3 类数据messages + messageNotifications + notificationPreferences~~ 仅管理 messages 表,通知相关数据由 notifications 模块管理
- ✅ P1 已修复:~~通知相关 Action 使用 `requireAuth()` 而非 `requirePermission()`~~ 5 个通知 ActiongetNotifications / markNotificationAsRead / markAllNotificationsAsRead / getNotificationPreferences / updateNotificationPreferences已改为 `requirePermission(Permissions.MESSAGE_READ)`
- ✅ P1 已修复:~~`markMessageAsReadAction` / `deleteMessageAction` / `getMessageDetailAction` 缺少 Zod 校验~~ 已添加 `MessageIdSchema` 校验 messageId 参数
- ✅ P1 已修复:~~`updateNotificationPreferencesAction` 缺少 Zod 校验~~ 已添加 `UpdateNotificationPreferencesSchema` 校验 8 个布尔字段
- ✅ P2 已修复:`data-access.ts` 中 3 处 `or(...)!` 非空断言清理为安全守卫(条件 push
- ✅ P0-b 已修复:~~`notification-preferences.ts` re-export shim 文件~~ 已删除通知模块去重8 个消费方改为直接从 `@/modules/notifications/preferences` 导入 `getNotificationPreferences` / `upsertNotificationPreferences`,消除 messaging 模块对通知偏好的冗余 re-export 层
**文件清单**
| 文件 | 行数 | 职责 |
|------|------|------|
| `actions.ts` | 276 | 9 个 Server Action通知相关 Action 委托 notifications 模块) |
| `data-access.ts` | 199 | 私信 CRUD + re-export 通知 CRUD向后兼容 |
| ~~`notification-preferences.ts`~~ | ~~11~~ | ~~re-export shim~~ ✅ P0-b 已删除(消费方改为直接从 notifications/preferences 导入) |
| `schema.ts` | 41 | 私信发送校验 + messageId 校验 + 通知偏好更新校验 |
| `types.ts` | 72 | 私信类型 + re-export 通知类型(向后兼容) |
---
## 2.14 notifications通知分发模块
**职责**多渠道通知分发SMS/Email/WeChat/InApp+ 站内通知 CRUD + 通知偏好管理。
**导出函数**
- Actions`sendNotificationAction` / `sendClassNotificationAction`
- Dispatcher`sendNotification(payload)` / `sendBatchNotifications(payloads)`
- Data-access`createNotification` / `getNotifications` / `markNotificationAsRead` / `markAllNotificationsAsRead` / `getUnreadNotificationCount` / `getUserContactInfo` / `logNotificationSend` / `logNotificationSendBatch`(✅ P0-4 / P1-5 修复后从 messaging 迁移)
- Preferences`getNotificationPreferences` / `upsertNotificationPreferences`(✅ P0-4 / P1-5 修复后从 messaging 迁移)
- Channels`InAppChannelSender` / `SmsChannelSender` / `EmailChannelSender` / `WeChatChannelSender`
**依赖关系**
- 依赖:`shared/*``@/auth``classes`(✅ P1-1 已修复:通过 classes data-access.getClassExists/getStudentIdsByClassId
- 被依赖:`messaging`(✅ P0-4 / P1-5 已修复messaging 通过 `sendNotification` dispatcher 发送通知,通知 CRUD 和偏好通过 re-export 保持向后兼容)
**已知问题**
- ✅ P0-4 已修复:~~不拥有任何数据,全部依赖 messaging 模块~~ messageNotifications 和 notificationPreferences 表所有权已从 messaging 迁移至 notifications 模块
- ✅ P0 已修复:~~与 messaging 双向依赖~~ notifications 不再反向依赖 messagingin-app-channel 改为静态导入本地 createNotification
- ✅ P1-1 已修复:~~`sendClassNotificationAction` 直查 `classes`/`classEnrollments`~~ 改为调用 `classes/data-access.getClassExists` / `getStudentIdsByClassId`
- ⚠️ P1发送日志仅 console`notification_logs`
- ✅ 渠道抽象优秀(接口 + 工厂 + Mock 实现)
**文件清单**
| 文件 | 行数 | 职责 |
|------|------|------|
| `dispatcher.ts` | 152 | 渠道选择 + 并行分发 |
| `data-access.ts` | 177 | 站内通知 CRUD + 用户联系方式 + 日志P0-4 / P1-5 修复后新增通知 CRUD |
| `preferences.ts` | 166 | 通知偏好 CRUDP0-4 / P1-5 修复后从 messaging 迁移) |
| `actions.ts` | 119 | 2 个 Server Action |
| `types.ts` | 120 | 通知负载 + 渠道配置 + 通知记录 + 偏好类型P0-4 / P1-5 修复后扩充) |
| `index.ts` | 61 | 对外导出入口 |
| `channels/*` | 5 文件 | 4 个渠道实现 |
---
## 2.15 audit审计模块
**职责**:操作日志 + 登录日志 + 数据变更日志查询与导出。
**导出函数**
- Actions`getAuditLogsAction` / `getLoginLogsAction` / `getDataChangeLogsAction` / `exportAuditLogsAction` / `exportLoginLogsAction` / `exportDataChangeLogsAction`
- Data-access`getAuditLogs` / `getLoginLogs` / `getDataChangeLogs` / `getAuditModuleOptions`
**依赖关系**
- 依赖:`shared/*``@/auth`
- 被依赖:无
**已知问题**
- ⚠️ P2Excel 导出逻辑内联在 actions 层(应抽取到 `export.ts`
- ⚠️ P2三个导出 Action 结构高度重复
- ✅ P2 已修复:`data-access.ts` 中 6 处 catch 块添加 `console.error` 输出错误上下文;`toIso`/`clampPageSize`/`clampPage` 工具函数补齐显式返回类型
- ✅ data-access 职责清晰,无跨模块问题
**文件清单**
| 文件 | 行数 | 职责 |
|------|------|------|
| `actions.ts` | 212 | 6 个 Server Action含内联导出 |
| `data-access.ts` | 260 | 日志查询 |
| `types.ts` | - | 类型定义 |
---
## 2.16 announcements公告模块
**职责**:公告 CRUD + 发布/归档 + 发布通知。
**导出函数**
- Actions`getAnnouncementsAction` / `createAnnouncementAction` / `updateAnnouncementAction` / `deleteAnnouncementAction` / `publishAnnouncementAction` / `archiveAnnouncementAction`(✅ P1-2 已修复actions 层不再直接访问 DB全部下沉到 data-access✅ 发布公告时触发通知模块 `sendBatchNotifications`
- Data-access`getAnnouncements`(支持 `audience` 受众过滤)/ `getAnnouncementById` / `insertAnnouncement` / `updateAnnouncementById` / `deleteAnnouncementById` / `publishAnnouncementById` / `archiveAnnouncementById`(后 5 个为 P1-2 新增)
**依赖关系**
- 依赖:`shared/*``@/auth``school`(获取年级列表)、`classes`(获取班级列表 + 解析受众)、`users`(获取目标用户 ID 列表)、`notifications`(发布公告时发送通知)
- 被依赖:无
**已知问题**
- ✅ P1-2 已修复:~~所有写操作直接在 actions 层 `db.insert/update/delete`,未下沉到 data-access~~ 写操作已下沉到 data-access5 个新函数)
- ✅ P2 已修复:~~`getAnnouncementsAction` 使用 `requireAuth()` 而非 `requirePermission(ANNOUNCEMENT_READ)`~~ 改为 `requirePermission(Permissions.ANNOUNCEMENT_READ)`
- ✅ 已修复:用户端列表页传入 `audience` 受众过滤school/grade/class管理端返回所有公告
- ✅ 已修复:用户端新增公告详情页 `/announcements/[id]`(只读模式)
- ✅ 已修复:管理端列表页传递 `classes` 数据给 `AdminAnnouncementsView`
- ✅ 已修复:发布公告时(`publishAnnouncementAction` / `createAnnouncementAction` 直接发布 / `updateAnnouncementAction` 状态变为 published触发通知模块 `sendBatchNotifications`
- ✅ 已修复:新增 `loading.tsx` 骨架屏(用户端 + 管理端)
**文件清单**
| 文件 | 行数 | 职责 |
|------|------|------|
| `actions.ts` | ~270 | 6 个 Server Action + 通知触发逻辑P1-2 已修复,无直接 DB 操作) |
| `data-access.ts` | ~190 | 公告 CRUD + 发布/归档 + 受众过滤(含 P1-2 新增 5 个写函数) |
| `schema.ts` | - | Zod 校验 |
| `types.ts` | - | 类型定义(`GetAnnouncementsParams` 新增 `audience` 字段) |
---
## 2.17 files文件模块
**职责**:文件附件 CRUD + 批量删除 + 统计。
**导出函数**
- Data-access`getAllFileAttachments` / `getFileAttachmentsByOwner` / `getFileAttachmentById` / `createFileAttachment` / `updateFileAttachment` / `deleteFileAttachment` / `batchDeleteFileAttachments` / `getFileStats`(✅ P2 已修复7 个读函数使用 `React.cache()` 包装实现请求级 memoization`getFileAttachment` / `getFileAttachmentsByTarget` / `getFileAttachmentsByUploader` / `getAllFileAttachments` / `getFileAttachmentsWithFilters` / `getFileStats` / `getFileAttachmentsByIds`
**依赖关系**
- 依赖:`shared/*``@/auth`
- 被依赖:`app/api/upload` / `app/api/files/[id]` / `app/api/files/batch-delete`
**已知问题**
- ✅ P2-13 已修复:~~所有函数 try-catch 吞错误返回空数组/null~~ 所有 catch 块已添加 `console.error` 输出错误上下文
- ✅ P2 已修复:`getFileAttachmentsWithFilters``or(...)!` 非空断言清理为安全守卫
- ✅ P2 已修复:~~`getFileAttachmentsWithFilters``conditions` 隐式 `any[]`~~ 改为显式 `SQL[]` 类型标注
- ⚠️ P2`actions.ts`data-access 被路由直接调用
- ✅ 职责单一,不跨模块查询
**文件清单**
| 文件 | 行数 | 职责 |
|------|------|------|
| `data-access.ts` | 267 | 文件 CRUD + 批量删除 + 统计 |
| `types.ts` | - | 类型定义 |
| `components/*` | 6 文件 | 上传/列表/预览/管理 |
---
## 2.18 course-plans课程计划模块
**职责**:课程计划 CRUD + 周计划项 CRUD + 排序。
**导出函数**
- Actions`getCoursePlansAction` / `getCoursePlanByIdAction` / `createCoursePlanAction` / `updateCoursePlanAction` / `deleteCoursePlanAction` / `createCoursePlanItemAction` / `updateCoursePlanItemAction` / `deleteCoursePlanItemAction` / `toggleCoursePlanItemCompletedAction`
- Data-access与 actions 对应
**依赖关系**
- 依赖:`shared/*``@/auth``classes`合理getAdminClasses/getStaffOptions`school`合理getAcademicYears
- 被依赖:无
**已知问题**
- ⚠️ P2`getSubjectOptions` 直查 `subjects`subjects 无独立模块,可接受)
- ✅ P2 已修复:`data-access.ts` 中 3 处 catch 块添加 `console.error` 输出错误上下文getCoursePlans/getCoursePlanById/getSubjectOptions
- ✅ actions 层使用 `handleError`/`revalidatePlanPaths` 辅助函数(良好范例)
**文件清单**
| 文件 | 行数 | 职责 |
|------|------|------|
| `data-access.ts` | 320 | 课程计划 + 周计划项 CRUD |
| `actions.ts` | 265 | 9 个 Server Action |
| `schema.ts` | - | Zod 校验 |
| `types.ts` | - | 类型定义 |
---
## 2.19 parent家长模块
**职责**:家长视角的子女数据聚合与展示。
**导出函数**
- Data-access`getChildren` / `getChildBasicInfo` / `getChildDashboardData` / `getParentDashboardData` / `verifyParentChildRelation` / `getChildNameList`(✅ v4 新增:用于详情页头部多子女切换器,一次批量查询避免 N+1
- Components`ParentDashboard` / `ChildCard` / `ChildDetailHeader` / `ChildDetailPanel` / `SiblingSwitcher` / `ChildHomeworkSummary` / `ChildGradeSummary` / `ChildScheduleCard` / `ParentChildrenDataPage` / `ParentNoChildrenPage` / `ParentAttentionBanner`v4 新增)/ `ParentAttendanceWarning`v4 新增)/ `ParentExportButton`v4 新增)
**v4 修复(产品/UX 维度)**
- ✅ FEAT-G01新增 `/parent/leave` 请假申请占位页(含 loading.tsx
- ✅ FEAT-G02详情页 Schedule Tab 支持完整周课表(新增 `weeklySchedule` 字段 + `ChildWeeklyScheduleItem` 类型 + `buildWeeklySchedule` 函数)
- ✅ FEAT-G05考勤页新增 `ParentAttendanceWarning` 异常预警横幅(聚合缺勤/迟到/低出勤率)
- ✅ FEAT-G06详情页底部新增"Contact Teacher"快捷入口
- ✅ FEAT-G07详情页头部新增 `SiblingSwitcher` 多子女切换器
- ✅ LAYOUT-P01仪表盘新增 `ParentAttentionBanner` 待办事项横幅
- ✅ LAYOUT-P02`ChildCard` 突出 Overdue 异常(红色边框 + AlertTriangle 图标)
- ✅ LAYOUT-P03仪表盘快捷入口改为 4 宫格大图标卡片
- ✅ LAYOUT-P04详情页改为 Tab 布局Overview/Homework/Grades/Schedule/Attendance/Diagnostic
- ✅ LAYOUT-P05详情页新增面包屑导航
- ✅ LAYOUT-P07成绩趋势图 X 轴改用序号,避免日期重叠
- ✅ LAYOUT-P08成绩页新增 `ParentExportButton` 导出按钮(占位)
- ✅ NAV-P03详情页实现 `?tab=` 参数支持
- ✅ NAV-P04所有 parent 路由新增 `loading.tsx` 骨架屏 + `error.tsx` 错误边界
- ✅ DATA-P02成绩卡片新增 TrendIcon 进步/退步/持平标识
- ✅ DATA-P03排名展示新增"Top X%"百分比
- ✅ DATA-P04作业列表新增科目标识 Badge
- ✅ HABIT-P01仪表盘"一眼定位异常"能力AttentionBanner 聚合)
- ✅ HABIT-P03多子女切换无需返回仪表盘
- ✅ HABIT-P06仪表盘展示未读/待办数量
- ✅ A11Y-P02Overdue 状态增加 AlertTriangle 图标辅助
- ✅ A11Y-P04成绩图表容器新增 aria-label
- ✅ PERF-P01/P02骨架屏 + 错误边界
- ✅ PERF-P03空状态新增"Contact support"引导按钮
- ✅ PERF-P04`ChildCard` Link 添加 `prefetch`
- ✅ MOBILE-P04作业/成绩列表项 `min-h-[44px]` 触摸区域
**依赖关系**
- 依赖:`shared/*``@/auth``classes`(合理)、`homework`(合理)、`grades`(合理)、`users`(合理)、`school`(合理)、`attendance`v4 新增:考勤页复用 `StudentAttendanceView`
- 被依赖:无
**已知问题**
- ✅ P1 已修复:~~`app/(dashboard)/parent/children/[studentId]/page.tsx` 直接访问 DB违反三层架构~~ 改为调用 `verifyParentChildRelation` data-access 函数
- ✅ P1 已修复:~~权限校验未加 parentId 条件,存在信息泄露风险~~ `verifyParentChildRelation` 同时按 parentId + studentId 过滤
- ✅ P2 已修复:~~`getChildBasicInfo` 多次串行查询~~ 改为 `Promise.all` 并行化,并使用 `getStudentActiveClass` 一次 JOIN
- ✅ P2 已修复:~~`getGradeOptions` 全量查询效率低~~ 改为 `getGradeNameById` 按 ID 查询
- ✅ P2 已修复:~~`buildHomeworkSummary``[...assignments].sort()` 不必要拷贝~~ 改为 `toSorted()`
- ✅ P2 已修复:~~`in7Days` 死代码~~ 已删除
- ⚠️ v4 保留:`/parent/leave` 为占位页,待后端实现请假审批流后接入
- ⚠️ v4 保留:`ParentExportButton` 为占位,待后端实现成绩导出 Server Action 后接入
- ⚠️ v4 保留:详情页 Attendance/Diagnostic Tab 为占位提示,待对应功能实现后填充
- ✅ 职责单一,正确复用其他模块 data-access
**文件清单**
| 文件 | 行数 | 职责 |
|------|------|------|
| `data-access.ts` | 243 | 子女关系 + 仪表盘数据聚合 + 关系校验 + 子女姓名列表v4 新增 `getChildNameList` + `buildWeeklySchedule` |
| `types.ts` | 79 | 类型定义(含 JSDocv4 新增 `ChildWeeklyScheduleItem` |
| `components/parent-dashboard.tsx` | 97 | 仪表盘v4 重构:待办横幅 + 宫格快捷入口) |
| `components/parent-attention-banner.tsx` | 116 | v4 新增:待办事项/异常聚合横幅 |
| `components/parent-attendance-warning.tsx` | 89 | v4 新增:考勤异常预警 |
| `components/parent-export-button.tsx` | 50 | v4 新增:成绩导出按钮(占位) |
| `components/child-card.tsx` | 148 | 子女卡片v4 增强:异常突出 + 趋势图标) |
| `components/child-detail-header.tsx` | 78 | 详情页头部v4 增强:面包屑) |
| `components/child-detail-panel.tsx` | 187 | 详情页 Tab 面板 + SiblingSwitcherv4 重写) |
| `components/child-homework-summary.tsx` | 147 | 作业摘要v4 增强:科目标识 + 触摸区域) |
| `components/child-grade-summary.tsx` | 159 | 成绩趋势v4 增强:趋势图标 + aria-label |
| `components/child-schedule-card.tsx` | 119 | 课表卡片v4 增强:周课表视图) |
| `components/parent-children-data-page.tsx` | 92 | 共享数据页v4 增强headerExtra |
**路由清单**
| 路由 | 文件 | 说明 |
|------|------|------|
| `/parent/dashboard` | `dashboard/page.tsx` + `loading.tsx` | 家长仪表盘 |
| `/parent/grades` | `grades/page.tsx` + `loading.tsx` | 多子女成绩聚合 |
| `/parent/attendance` | `attendance/page.tsx` + `loading.tsx` | 多子女考勤聚合v4 新增预警横幅) |
| `/parent/leave` | `leave/page.tsx` + `loading.tsx` | v4 新增:请假申请(占位) |
| `/parent/children/[studentId]` | `children/[studentId]/page.tsx` + `loading.tsx` | 子女详情页v4 重构Tab 布局 + 多子女切换) |
| `error.tsx` | `error.tsx` | v4 新增:错误边界 |
---
## 2.20 elective选课模块
**职责**:选修课程管理 + 学生选课 + 抽签。
**导出函数**
- Actions`getElectiveCoursesAction` / `createElectiveCourseAction` / `updateElectiveCourseAction` / `deleteElectiveCourseAction` / `getStudentSelectionsAction` / `selectCourseAction` / `dropCourseAction` / `runLotteryAction` / `getAvailableCoursesForStudentAction`
- Data-access`getElectiveCourses` / `getElectiveCourseById` / `createElectiveCourse` / `updateElectiveCourse` / `deleteElectiveCourse` / `openSelection` / `closeSelection` / `buildCourseSelect` / `mapCourseRow` / `resolveCourseDisplayNames` / `CourseCoreRow`P3 新增导出,供 data-access-selections 复用)
- Data-access-operations`selectCourse` / `dropCourse` / `runLottery`
- Data-access-selections`getCourseSelections` / `getStudentSelections` / `getStudentGradeId` / `getAvailableCoursesForStudent`
**依赖关系**
- 依赖:`shared/*``@/auth``school`(✅ P3 已修复:通过 school data-access.getSubjectOptions/getGradeOptions 获取科目/年级名称,不再直查 subjects/grades 表)、`users`(✅ P3 已修复:通过 users data-access.getUserNamesByIds 获取教师姓名,不再直查 users 表)、`classes`(通过 classes data-access.getStudentActiveGradeId 获取学生年级)
- 被依赖:无
**已知问题**
- ✅ P1 已修复:~~`buildCourseSelect` 跨模块 join users/subjects/grades 表~~ 改为只查 electiveCourses 表,通过 `resolveCourseDisplayNames` 调用 school/users data-access 获取显示名称
- ✅ P1 已修复:~~`getSubjectOptions` 本地直查 subjects 表且与 school 模块重复~~ 删除本地实现,改用 `school/data-access.getSubjectOptions`
- ✅ P1 已修复:~~`selectCourse`/`dropCourse` 缺事务包裹~~ 改为 `db.transaction` 包裹FCFS 模式下使用 `FOR UPDATE` 行锁防止并发超卖
- ✅ P2 已修复:~~`mapCourseRow` 在 data-access.ts 与 data-access-selections.ts 重复定义~~ 抽取到 data-access.ts 统一导出data-access-selections.ts 复用
- ✅ P2 已修复:~~`runLottery` 使用 `sort(() => Math.random() - 0.5)` 有偏 shuffle~~ 改为 Fisher-Yates 无偏洗牌算法
- ✅ P2 已修复:~~`selectCourse` FCFS 并发超卖风险~~ 使用 `db.transaction` + `.for("update")` 行锁
- ✅ 权限校验完整ELECTIVE_MANAGE/SELECT/READ
**文件清单**
| 文件 | 行数 | 职责 |
|------|------|------|
| `actions.ts` | 304 | 11 个 Server Action |
| `data-access.ts` | 250 | 课程 CRUD + scope 过滤 + 共享映射函数P3 重构:移除跨模块 join通过 school/users data-access 获取显示名称) |
| `data-access-operations.ts` | 245 | 选课操作select/drop/lotteryP3 重构:事务包裹 + FOR UPDATE 锁 + Fisher-Yates 洗牌) |
| `data-access-selections.ts` | 189 | 选课记录查询 |
| `schema.ts` | 132 | Zod 校验 |
| `types.ts` | 108 | 类型定义 + 标签常量 |
---
## 2.21 proctoring监考模块
**职责**:考试监考事件记录 + 防作弊监控 + 监考面板。
**导出函数**
- Actions`recordProctoringEventAction` / `getProctoringDashboardAction`
- Data-access`recordProctoringEvent` / `getExamSubmissionForProctoring` / `getExamForProctoring` / `getExamProctoringSummary` / `getStudentProctoringStatuses` / `getRecentProctoringEvents`(✅ P2 已修复:`getExamProctoringSummary` 使用 `Promise.all` 并行化考试信息与提交记录查询、事件类型统计与学生事件统计查询;合并两次 filter 为单次循环统计 started/submitted
**依赖关系**
- 依赖:`shared/*``@/auth``exams`(✅ P1-1 已修复:通过 exams data-access.getExamForProctoringCrossModule/getExamSubmissionForProctoringCrossModule/getExamSubmissionsForExam/getExamTitleById`users`(✅ P1-1 已修复:通过 users data-access.getUserNamesByIds
- 被依赖:无
**已知问题**
- ❌ P0`exam-mode-config.tsx` 未集成到考试表单(死代码,监考功能无法启用)
- ✅ P0-6 已修复:~~事件上报存在 Server Action 与 REST API 双通道重复~~ 删除 `/api/proctoring/event` REST 路由(移至 deletes/Server Action `recordProctoringEventAction` 为唯一规范路径
- ✅ P1-1 已修复:~~跨模块直查 `exams`/`examSubmissions`/`users`~~ 改为通过 exams/users data-access 函数获取数据
- ✅ P2 已修复:`actions.ts` 不再直接 import `db``examSubmissions`submission 归属校验已下沉到 data-access`recordProctoringEventAction` 改用 `requirePermission(EXAM_SUBMIT)` 并增加 `revalidatePath`
- ✅ P2 已修复:~~`getStudentProctoringStatuses` 串行查询getUserNamesByIds 后再查事件)~~ 改为 `Promise.all` 并行拉取学生姓名与事件记录
**文件清单**
| 文件 | 行数 | 职责 |
|------|------|------|
| `data-access.ts` | 409 | 事件记录 + 查询 + 摘要统计 + submission 归属校验 |
| `actions.ts` | 139 | 2 个 Server Action |
| `types.ts` | 136 | 类型定义 + 标签常量 + 阈值常量 |
| `components/anti-cheat-monitor.tsx` | - | 学生端防作弊监控 |
| `components/exam-mode-config.tsx` | - | 考试模式配置(**未集成** |
| `components/proctoring-dashboard.tsx` | - | 教师监考面板 |
---
## 2.22 diagnostic学情诊断模块
**职责**:知识点掌握度查询 + 诊断报告生成。
**导出函数**
- Actions`generateStudentDiagnosticReportAction` / `generateClassDiagnosticReportAction` / `publishDiagnosticReportAction` / `deleteDiagnosticReportAction` / `getDiagnosticReportsAction` / `getStudentMasteryAction`
- Data-access`updateMasteryFromSubmission` / `getStudentMastery` / `getClassMasteryOverview`
- Data-access-reports`createDiagnosticReport` / `getDiagnosticReport` / `getDiagnosticReports` / `deleteDiagnosticReport` / `publishDiagnosticReport`(✅ P2 已修复:`getDiagnosticReports``getDiagnosticReportById` 使用 `React.cache()` 包装实现请求级 memoization
- Schema`GenerateStudentReportSchema` / `GenerateClassReportSchema` / `PublishReportSchema` / `DeleteReportSchema` / `GetDiagnosticReportsSchema` / `GetDiagnosticReportByIdSchema`
**依赖关系**
- 依赖:`shared/*``@/auth``exams`(✅ P1-1 已修复:通过 exams data-access.getExamSubmissionWithAnswers`questions`(✅ P1-1 已修复:通过 questions data-access.getKnowledgePointsForQuestions`classes`(✅ P1-1 已修复:通过 classes data-access.getClassExists/getClassNameById/getActiveStudentIdsByClassId`users`(✅ P1-1 已修复:通过 users data-access.getUserNamesByIds/getUserIdsByGradeId
- 被依赖:无
**已知问题**
- ✅ P1-1 已修复:~~`updateMasteryFromSubmission` 跨模块直查 4 张表(与 exams/homework/questions 紧耦合)~~ 改为调用 `exams/data-access.getExamSubmissionWithAnswers``questions/data-access.getKnowledgePointsForQuestions`
- ✅ P2 已修复:~~`data-access-reports.ts` 有未使用代码(`round2` + `void round2`~~ 已删除死代码
- ✅ P2 已修复:~~`updateMasteryFromSubmission` 循环内串行 await upsert~~ 改为 `Promise.all` 并行执行所有 upsert
- ✅ P2 已修复:~~`getClassMasterySummary` 串行查询className → studentIds → userMap → masteryRows~~ 改为两组 `Promise.all` 并行className+studentIdsuserMap+masteryRows
- ✅ P2 已修复:~~`getDiagnosticReports``conditions` 隐式 `any[]`~~ 改为显式 `SQL[]` 类型标注
- ⚠️ P2班级报告将生成者 ID 存入 `studentId` 字段schema 设计缺陷 workaround
- ✅ 与 grades 模块无职责重叠grades 管分数diagnostic 管知识点掌握度)
**文件清单**
| 文件 | 行数 | 职责 |
|------|------|------|
| `data-access.ts` | 254 | 知识点掌握度查询 + 更新 |
| `data-access-reports.ts` | 202 | 诊断报告 CRUD |
| `actions.ts` | 172 | 6 个 Server Action使用 Zod schema 校验) |
| `schema.ts` | 56 | Zod 校验6 个 schema生成/发布/删除/查询报告) |
| `types.ts` | 97 | 类型定义 |
| `components/*` | 4 文件 | 学生/班级诊断视图 + 雷达图 |
---
## 2.23 settings设置模块
**职责**:系统设置(学校信息/安全策略/文件上传/通知配置)+ AI Provider 管理 + 密码修改 + 个人资料 + 主题偏好 + 通知偏好。
**导出函数**
- Actions`getAiProvidersAction` / `createAiProviderAction` / `updateAiProviderAction` / `deleteAiProviderAction` / `testAiProviderAction`
- Actions-password`changePasswordAction`(✅ P1 已修复:使用 `requirePermission(USER_PROFILE_UPDATE)` + Zod 校验 + DB 操作下沉到 data-access
- Data-access`getAiProviderSummaries` / `countDefaultAiProviders` / `getAiProviderForUpdate` / `updateAiProvider` / `createAiProvider` / `getUserPasswordHash` / `getPasswordSecurityByUserId` / `updateUserPassword` / `upsertPasswordSecurityOnPasswordChange`P1 新增,从 actions 下沉)
- Components`SettingsView`P2-a 新增:统一设置页布局,消除 admin/teacher/student/parent 四个设置视图的重复布局5 标签页 General/Notifications/Appearance/Security/AI角色差异通过 `description` / `backHref` / `generalExtra` 三个 props 注入Tab 通过 URL `?tab=` 参数持久化AI 标签页条件渲染需 `AI_CONFIGURE` 权限;登出按钮使用 AlertDialog 二次确认4 个消费方admin/teacher/student/parent 设置页)、`ParentSettingsView`家长设置视图backHref 指向 `/parent/dashboard`,含家长专属快捷链接)、`AdminSettingsView`系统设置视图4 个 Card学校信息/安全策略/文件上传/通知配置;消费方:`/admin/settings` 页面,权限 `SETTINGS_ADMIN`
- Types`AiProviderSummary` / `AiProviderName` / `AiProviderExisting`P1 新增,从 actions.ts 迁出)
**依赖关系**
- 依赖:`shared/*`(含 `shared/lib/bcrypt-utils`,✅ P2 已修复:`actions-password.ts` 删除本地 `normalizeBcryptHash`,统一复用 `shared/lib/bcrypt-utils.normalizeBcryptHash`)、`@/auth``messaging`(通知偏好表单调用 messaging Action
- 被依赖:无
**已知问题**
- ⚠️ P2混合 5 类职责AI Provider + 密码 + 资料 + 主题 + 通知偏好)
- ✅ P1 已修复:~~无 `data-access.ts``actions.ts` 直接使用 `db`~~ 新建 `data-access.ts`,所有 DB 操作已下沉
- ✅ P1 已修复:~~`changePasswordAction` 使用 `requireAuth()` 无 Zod 校验~~ 改为 `requirePermission(USER_PROFILE_UPDATE)` + `ChangePasswordSchema` Zod 校验 + 并行查询优化
- ✅ P2 已修复:`actions-password.ts` 删除本地 `normalizeBcryptHash`,统一复用 `shared/lib/bcrypt-utils.normalizeBcryptHash`,消除重复代码
- ✅ P2-a 已修复:~~admin/teacher/student 三个设置视图重复布局~~ 新增 `SettingsView` 统一设置页布局5 标签页 + 角色差异通过 props 注入4 个设置页改为消费 `SettingsView`
- ✅ parent 角色路由已修复:~~parent 用户被错误渲染为 TeacherSettingsView~~ 新增 `ParentSettingsView``/settings` 页面增加 parent 角色分支
- ✅ Tab URL 持久化已修复:`SettingsView` 改为受控模式,通过 `useSearchParams` 读取 `tab` 参数,`router.push` 更新 URL
- ✅ 登出二次确认已修复:`SettingsView` 的 Log out 按钮使用 `AlertDialog` 包裹,点击时弹出确认对话框
- ✅ AiProviderSettingsCard 已集成:`SettingsView` 新增 AI 标签页,条件渲染需 `AI_CONFIGURE` 权限
- ✅ password-change-form 任意值 Tailwind 类已修复:~~`[&>div]:bg-red-500` 等任意值类~~ Progress 组件新增 `indicatorClassName` prop使用标准颜色类
- ⚠️ P2`notification-preferences-form.tsx` 跨模块 UI 依赖
- ✅ 密码修改有速率限制
- ✅ AI Provider 操作有 `AI_CONFIGURE` 权限校验
**文件清单**
| 文件 | 行数 | 职责 |
|------|------|------|
| `actions.ts` | 178 | AI Provider CRUD + 测试P1 已修复,无直接 DB 操作) |
| `actions-password.ts` | 107 | 修改密码P1 已修复requirePermission + Zod + data-access |
| `data-access.ts` | 175 | AI Provider CRUD + 密码修改 DB 操作P1 新增) |
| `types.ts` | 16 | 类型定义P1 新增AiProviderSummary 等) |
| `components/settings-view.tsx` | 196 | SettingsView 统一设置页布局P2-a 新增5 标签页 + props 注入角色差异 + Tab URL 持久化 + 登出二次确认 + AI 标签页) |
| `components/admin-settings-view.tsx` | 195 | AdminSettingsView 系统设置视图4 个 Card学校信息/安全策略/文件上传/通知配置,模拟保存) |
| `components/parent-settings-view.tsx` | 70 | ParentSettingsView 家长设置视图(新增,复用 SettingsView 布局) |
| `components/*` | 9 文件 | 通用设置 + AI 配置 + 密码 + 主题 + 通知偏好 + 4 角色设置视图 |
---
## 2.24 auth认证 UI 模块)
**职责**:认证页面 UI登录/注册/布局)。
**导出函数**:纯 UI 组件(`LoginForm` / `RegisterForm` / `AuthLayout`
**依赖关系**
- 依赖:`shared/*`
- 被依赖:`app/(auth)/*`
**已知问题**
- ✅ 纯 UI 模块,无 data-access/actions/types
- ✅ 认证逻辑由 NextAuth + `shared/lib/auth-guard` 统一处理
**文件清单**
| 文件 | 职责 |
|------|------|
| `components/auth-layout.tsx` | 认证页面布局 |
| `components/login-form.tsx` | 登录表单 |
| `components/register-form.tsx` | 注册表单 |
---
## 2.25 layout布局模块
**职责**:应用骨架(侧边栏 + 顶部导航 + 导航配置 + 多角色切换)。
**导出函数**`AppSidebar` / `SidebarProvider` / `SiteHeader` + `navigation` 配置
**依赖关系**
- 依赖:`shared/hooks/use-permission``@/auth`useSession`messaging`(通知下拉)、`shared/components/ui/select`(角色切换下拉)
- 被依赖:`app/(dashboard)/layout.tsx`
**已知问题**
- ✅ P2 已修复:~~用权限反推角色~~ `app-sidebar.tsx` 改用 `hasRole()` 判断角色,并新增多角色切换机制(`SidebarContext.currentRole`/`setCurrentRole`null 表示自动检测;当用户拥有多个角色时在侧边栏底部显示 `Select` 下拉切换)
- ✅ navigation.ts 无幽灵路由13 个已修复)
**文件清单**
| 文件 | 职责 |
|------|------|
| `components/app-sidebar.tsx` | 侧边栏(根据权限渲染 + 多角色切换下拉) |
| `components/sidebar-provider.tsx` | 侧边栏状态 Context`currentRole`/`setCurrentRole` |
| `components/site-header.tsx` | 顶部导航(含通知下拉) |
| `config/navigation.ts` | 导航配置4 个角色) |
---
## 2.26 student学生 UI 模块)
**职责**:学生端 UI 组件(课程视图 + 课表筛选/视图)。
**导出函数**`StudentCoursesView` / `StudentScheduleFilters` / `StudentScheduleView`
**依赖关系**
- 依赖:`shared/*`
- 被依赖:`app/(dashboard)/student/*`
**已知问题**
- ⚠️ P2与 classes 模块的 `schedule-view.tsx`/`schedule-filters.tsx` 可能功能重叠
- ✅ 纯 UI 模块,数据由页面通过 classes data-access 获取
- ✅ 认证模式已统一:所有 student 页面使用 `getCurrentStudentUser()`users 模块)或 `getAuthContext()`shared 模块),不再直接调用 `auth()``getDemoStudentUser()`
**文件清单**
| 文件 | 职责 |
|------|------|
| `components/student-courses-view.tsx` | 学生课程视图(含 `ClassCard` memo 组件 + 加入班级表单,使用 `useTransition` |
| `components/student-schedule-filters.tsx` | 课表筛选器 |
| `components/student-schedule-view.tsx` | 学生课表视图 |
**路由文件清单**`app/(dashboard)/student/`
| 文件 | 职责 |
|------|------|
| `dashboard/page.tsx` + `loading.tsx` | 学生仪表盘 + 骨架屏 |
| `attendance/page.tsx` + `loading.tsx` | 学生考勤 + 骨架屏 |
| `diagnostic/page.tsx` + `loading.tsx` | 学情诊断 + 骨架屏 |
| `elective/page.tsx` + `loading.tsx` | 选课中心 + 骨架屏 |
| `grades/page.tsx` + `loading.tsx` | 我的成绩 + 骨架屏 |
| `learning/assignments/page.tsx` + `loading.tsx` | 作业列表(含 `AssignmentCard` 组件)+ 骨架屏 |
| `learning/assignments/[assignmentId]/page.tsx` + `loading.tsx` | 作业作答/复习 + 骨架屏 |
| `learning/courses/page.tsx` + `loading.tsx` | 课程列表 + 骨架屏 |
| `learning/textbooks/page.tsx` + `loading.tsx` | 教材列表 + 骨架屏 |
| `learning/textbooks/[id]/page.tsx` + `loading.tsx` | 教材阅读 + 骨架屏 |
| `schedule/page.tsx` + `loading.tsx` | 课表 + 骨架屏 |
| `error.tsx` | 路由组错误边界(提供"重试"按钮) |
---
## 2.27 lesson-preparation备课模块
**职责**:教师备课,基于教材章节创建课案(**节点图编辑器 React Flow**),支持模板、版本管理、知识点标注、题目创建/拉取、作业发布。
> 架构变更2026-06-21编辑器从列表式BlockRenderer + @dnd-kit升级为节点图式NodeEditor + @xyflow/react。数据结构从 v1blocks 数组)升级到 v2nodes + edges 节点图),旧数据通过 `migrateV1ToV2()` 自动迁移。
**数据结构**
- v1已废弃仅向后兼容读取`{ version: 1, blocks: Block[] }`
- v2当前`{ version: 2, nodes: LessonPlanNode[]; edges: LessonPlanEdge[] }`
- `LessonPlanNode``Block` + `position: { x, y }`(画布坐标)
- `LessonPlanEdge``{ id, source, target, sourceHandle?, targetHandle? }`(节点间连线)
**导出函数**
- Data-access`data-access.ts``getLessonPlans` / `getLessonPlanById` / `createLessonPlan` / `updateLessonPlanContent` / `softDeleteLessonPlan` / `duplicateLessonPlan` / `getTemplateById` / `buildInitialContent` / `migrateV1ToV2`v1→v2 迁移blocks 数组转换为 nodes + 线性 edges/ `normalizeDocument`(规范化:确保 content 为 v2 格式,兼容旧数据)
- Data-access-versions`data-access-versions.ts``getLessonPlanVersions` / `createLessonPlanVersion` / `getVersionContent` / `revertToVersion` / `pruneAutoVersions`
- Data-access-templates`data-access-templates.ts``getLessonPlanTemplates` / `saveAsTemplate` / `deletePersonalTemplate`
- Data-access-knowledge`data-access-knowledge.ts``getLessonPlansByKnowledgePoint` / `getLessonPlansByQuestion`
- Publish-service`publish-service.ts``publishLessonPlanHomework`
- AI-suggest`ai-suggest.ts``suggestKnowledgePoints`
- Actions`getLessonPlansAction` / `getLessonPlanByIdAction` / `createLessonPlanAction` / `updateLessonPlanAction` / `saveLessonPlanVersionAction` / `getLessonPlanVersionsAction` / `revertLessonPlanVersionAction` / `deleteLessonPlanAction` / `duplicateLessonPlanAction` / `getLessonPlanTemplatesAction` / `saveAsTemplateAction` / `deleteTemplateAction` / `suggestKnowledgePointsAction` / `publishLessonPlanHomeworkAction` / `getKnowledgePointOptionsAction`
**依赖关系**
- 依赖:`shared/*``@/auth``shared/lib/ai``@xyflow/react`(节点图编辑器)、`textbooks`(只读章节/知识点树)、`questions`(创建/查询题目)、`exams`(创建 exam 草稿)、`homework`(创建作业下发)、`classes`(查询教师班级)、`files`(附件)
- 被依赖:无
**已知问题**
- ✅ 通过对方 data-access 调用跨模块数据,无直查跨模块表
- ✅ data-access 按职责拆分为 4 个文件data-access/data-access-versions/data-access-templates/data-access-knowledge
- ✅ actions 按职责拆分为 4 个文件actions/actions-publish/actions-ai/actions-kp
- ✅ 编辑器架构升级NodeEditorReact Flow 画布)+ NodeEditPanel侧边内容编辑面板+ LessonNode自定义节点组件支持节点拖拽、连线、画布缩放
- ⚠️ `block-renderer.tsx` 标记为 @deprecated(已被 NodeEditor 替代,保留用于向后兼容)
**文件清单**
| 文件 | 职责 |
|------|------|
| `types.ts` | 类型定义(含 v1/v2 文档类型、LessonPlanNode、LessonPlanEdge |
| `constants.ts` | 常量定义 |
| `schema.ts` | Zod 验证 |
| `data-access.ts` | 课案 CRUD + 模板查询 + 初始内容构建 + v1→v2 迁移migrateV1ToV2 / normalizeDocument |
| `data-access-versions.ts` | 版本管理(创建/查询/回滚/清理) |
| `data-access-templates.ts` | 个人模板 CRUD |
| `data-access-knowledge.ts` | 按知识点/题目反查课案 |
| `actions.ts` | 课案 CRUD/版本/模板 Server Actions |
| `actions-publish.ts` | 发布作业 Server Action |
| `actions-ai.ts` | AI 知识点建议 Server Action |
| `actions-kp.ts` | 知识点选项 Server Action |
| `publish-service.ts` | 发布作业服务(编排 homework/exams/classes |
| `ai-suggest.ts` | AI 知识点建议服务 |
| `seed-templates.ts` | 模板种子数据 |
| `hooks/use-lesson-plan-editor.ts` | 课案编辑器 Hook基于 zustand支持 nodes/edges 操作addNode/updateNode/updateNodePosition/removeNode/connect/disconnect/setEdges/selectNode |
| `components/lesson-plan-list.tsx` | 课案列表 |
| `components/lesson-plan-card.tsx` | 课案卡片 |
| `components/lesson-plan-filters.tsx` | 课案筛选器 |
| `components/lesson-plan-editor.tsx` | 课案编辑器(编排 NodeEditor + NodeEditPanel |
| `components/node-editor.tsx` | **节点图画布**React Flow自定义 LessonNode支持拖拽/连线/缩放) |
| `components/node-edit-panel.tsx` | **侧边内容编辑面板**(选中节点后编辑标题/数据) |
| `components/nodes/lesson-node.tsx` | **自定义节点组件**(按 BlockType 显示图标/颜色,含 Handle 连接点) |
| `components/block-renderer.tsx` | ⚠️ @deprecated Block 渲染器(已被 NodeEditor 替代,保留向后兼容) |
| `components/template-picker.tsx` | 模板选择器 |
| `components/version-history-drawer.tsx` | 版本历史抽屉 |
| `components/knowledge-point-picker.tsx` | 知识点选择器 |
| `components/question-bank-picker.tsx` | 题库选择器 |
| `components/inline-question-editor.tsx` | 内联题目编辑器 |
| `components/publish-homework-dialog.tsx` | 发布作业对话框 |
| `components/blocks/rich-text-block.tsx` | 富文本 Block被 NodeEditPanel 复用) |
| `components/blocks/text-study-block.tsx` | 课文研读 Block被 NodeEditPanel 复用) |
| `components/blocks/exercise-block.tsx` | 练习 Block被 NodeEditPanel 复用) |
| `components/blocks/reflection-block.tsx` | 反思 Block被 NodeEditPanel 复用) |
---
# 第三部分:已知架构问题和技术债
## 3.1 P0 严重问题(必须立即修复)
### P0-1文件超 1000 行硬上限3 个文件)
| 文件 | 行数 | 问题 | 拆分建议 |
|------|------|------|---------|
| `classes/data-access.ts` | ~~2104~~ → 548 | ~~混入 homework/scheduling/grades 逻辑~~ ✅ 已拆分 | 已拆为 5 个文件data-access.ts(548行) + data-access-stats.ts(531行) + data-access-schedule.ts(194行) + data-access-students.ts(244行) + data-access-admin.ts(406行),通过 re-export 保持向后兼容 |
| `homework/data-access.ts` | ~~1038~~ → 598 | ~~混入排名计算业务逻辑~~ ✅ 已拆分 | 已拆为 data-access.ts(598行) + stats-service.ts(425行),统计函数迁移至 stats-service.ts |
| `shared/db/schema.ts` | 1111 | 54 张表混合 | 按业务域拆分为 schema/auth.ts + schema/academic.ts + schema/exam.ts + ...,通过 index.ts 聚合 |
### P0-2shared/lib ↔ auth 循环依赖 ✅ 已修复
```
shared/lib/{audit-logger, change-logger, auth-guard} → @/auth → shared/lib/*
```
**影响**shared 层无法独立测试/复用;架构上基础设施不应反向依赖应用层。
**修复方案**(已实施):
- ✅ 创建 `shared/lib/session.ts` 封装 session 获取(`getSession()`server-only
-`session.ts` 内部使用 `dynamic import("@/auth")` 打破模块级静态循环
-`audit-logger.ts` / `change-logger.ts` / `auth-guard.ts` 改为 `import { getSession } from "@/shared/lib/session"`,不再直接依赖 `@/auth`
- ✅ 运行时调用链保持不变,模块加载图无环
### P0-3dashboard 跨模块直接查询 11 张表 ✅ 已修复
`dashboard/data-access.ts``getAdminDashboardData` 原直查 sessions/users/usersToRoles/roles/classes/textbooks/chapters/questions/exams/homeworkAssignments/homeworkSubmissions。
**修复方案**(已实施):
- 各模块新增 dashboard stats 函数:
- `users/data-access.ts``getUsersDashboardStats()`userCount/activeSessionsCount/userRoleCounts/recentUsers
- `classes/data-access.ts``getClassesDashboardStats()`classCount
- `textbooks/data-access.ts``getTextbooksDashboardStats()`textbookCount/chapterCount
- `questions/data-access.ts``getQuestionsDashboardStats()`questionCount
- `exams/data-access.ts``getExamsDashboardStats(scope?)`examCount支持 scope 过滤)
- `homework/stats-service.ts``getHomeworkDashboardStats(scope?)`4 个计数,支持 scope 过滤)
- dashboard 改为并行调用:`Promise.all([getUsersDashboardStats(), getClassesDashboardStats(), ...])`
- 返回值结构保持不变,调用方无需修改
### P0-4messaging 绕过 notifications 直接写通知 ✅ 已修复
`messaging/actions.ts` 原直接调用 `createNotification``messageNotifications` 表,导致用户通知偏好失效、多渠道通知无效。`notifications/data-access.ts``in-app-channel.ts` 反向依赖 messaging 模块。
**修复方案**(已实施):
-`messageNotifications` 表的 CRUD 函数(`createNotification` / `getNotifications` / `markNotificationAsRead` / `markAllNotificationsAsRead` / `getUnreadNotificationCount`)从 `messaging/data-access.ts` 迁移到 `notifications/data-access.ts`
-`notificationPreferences` 表的 CRUD 函数(`getNotificationPreferences` / `upsertNotificationPreferences`)从 `messaging/notification-preferences.ts` 迁移到新文件 `notifications/preferences.ts`
-`NotificationType` / `Notification` / `NotificationPreferences` / `UpdateNotificationPreferencesInput` / `CreateNotificationInput` / `GetNotificationsParams` / `PaginatedResult` 类型从 `messaging/types.ts` 迁移到 `notifications/types.ts`
- `notifications/channels/in-app-channel.ts` 改为静态导入本地 `createNotification`(不再动态 import messaging
- `notifications/dispatcher.ts` 改为从 `notifications/preferences.ts` 导入 `getNotificationPreferences`(不再通过 `getUserNotificationPreferences` 包装器反向依赖 messaging
- messaging 模块通过 re-export 保持向后兼容:`messaging/data-access.ts` re-export 通知 CRUD`messaging/notification-preferences.ts` 转为 re-export shim`messaging/types.ts` re-export 通知类型
- 依赖方向变为单向messaging → notificationsmessaging 调用 `sendNotification` dispatcher 发送通知)
### P0-5classSchedule 表三处写入口 ✅ 已修复
- ~~`classes/data-access.ts`createClassScheduleItem 等)~~
- ~~`scheduling/actions.ts`applyAutoScheduleAction 直接 transaction 写入)~~
- ~~`scheduling/data-access.ts`(间接)~~
**影响**:数据完整性高风险。
**修复方案**(已实施):
- 将 classSchedule 写函数(`createClassScheduleItem` / `updateClassScheduleItem` / `deleteClassScheduleItem`)从 `classes/data-access-schedule.ts` 迁移到新文件 `scheduling/data-access-class-schedule.ts`
- classes 模块仅保留 READ 函数(`getStudentSchedule` / `getClassSchedule`),不再有任何 classSchedule 写入口
- scheduling 模块通过 `classes/data-access.verifyTeacherOwnsClass` 跨模块校验教师班级归属(合理依赖)
- `classes/actions.ts` 改为从 `@/modules/scheduling/data-access-class-schedule` 导入写函数
- 类型 `CreateClassScheduleItemInput` / `UpdateClassScheduleItemInput``classes/types.ts` 迁移到 `scheduling/types.ts`
- 所有 classSchedule DB 写入统一由 scheduling 模块管理(`insertClassScheduleItem` / `updateClassScheduleItemById` / `deleteClassScheduleItemById` / `replaceClassSchedule`
### P0-6proctoring 死代码与重复实现
- `exam-mode-config.tsx` 未集成到考试表单(监考功能无法启用)
- ~~事件上报存在 Server Action 与 REST API 双通道重复~~ ✅ 已修复
**修复方案**(已实施):
-`src/app/api/proctoring/event/route.ts` 移至 `deletes/api/proctoring/event/route.ts`,消除事件上报的 REST API 重复通道
- Server Action `recordProctoringEventAction``proctoring/actions.ts`)为唯一规范路径
- `exam-mode-config.tsx` 暂保留原位(集成到考试表单属于功能新增,不在本次修复范围)
---
## 3.2 P1 较严重问题(短期执行)
### P1-1跨模块直接 DB 查询普遍存在 ✅ 已修复
| 被访问表 | 访问次数 | 应归属模块 | 主要违规者 |
|---------|---------|-----------|-----------|
| `classes` | 8+ | classes | ✅ exams/homework/grades/dashboard 已改为通过 classes data-access |
| `classEnrollments` | 6+ | classes | ✅ homework/grades/attendance/users 已改为通过 classes data-access |
| `users` | 6+ | users | ✅ 多个模块已改为通过 users data-access |
| `subjects` | 6+ | school | ✅ exams/homework/questions/grades 已改为通过 school data-access |
| `exams` | 5+ | exams | ✅ homework/grades/dashboard/classes 已改为通过 exams data-access |
| `homeworkAssignments` | 5+ | homework | ✅ classes反向直查已改为通过 homework/data-access-classes |
**修复方案**(已实施):
- ✅ 各模块 data-access 暴露查询接口:
- `classes/data-access`~~`getClassGradeIdsByClassIds`~~ ✅ 已实现 / `getClassStudentsByClassId` / `getActiveClassStudents` / `getClassExists` / `getClassNameById` / `getClassNamesByIds` / `getActiveStudentIdsByClassId` / `getStudentActiveClassId` / `getClassesByGradeId` / `verifyTeacherOwnsClass` / `getTeacherIdForMutations`
- `exams/data-access`~~`getExamForHomeworkCreation`~~ ✅ 已实现(`getExamIdsByGradeIds` / `getExamSubjectIdMap` / `getExamWithQuestionsForHomework` / `getExamSubmissionWithAnswers` / `getExamForProctoringCrossModule` / `getExamSubmissionForProctoringCrossModule` / `getExamSubmissionsForExam` / `getExamTitleById`
- `school/data-access`~~`getSubjectOptions` / `getGradeOptions`~~ ✅ 已实现
- `users/data-access`~~`getUserNameByIds` / `getStudentInfo`~~ ✅ 已实现(`getUserNamesByIds` / `getUserWithRole` / `getUserBasicInfo` / `getUserIdsByGradeId` / `getCurrentStudentUser`
- `textbooks/data-access`~~`getKnowledgePointOptions`~~ ✅ 已实现
- `questions/data-access`~~`insertQuestionWithRelations`~~ ✅ 已通过 `createQuestionWithRelations` 供 exams 调用 / `getKnowledgePointsForQuestions`
- `homework/data-access-classes`:✅ 新增 7 个函数供 classes 模块跨模块调用
### P1-2actions 层混入数据访问逻辑 ✅ 已修复
**已完成修复**2026-06-17commit 84d66364 个模块的 actions 层 DB 操作全部下沉到 data-access
| 模块 | 问题 Action | 修复内容 |
|------|------------|---------|
| exams | `updateExamAction` / `deleteExamAction` / `duplicateExamAction` / `getExamPreviewAction` / `getSubjectsAction` / `getGradesAction` | ✅ 新增 7 个 data-access 函数getExamCreatorId/updateExamWithQuestions/deleteExamById/duplicateExam/getExamPreview/getExamSubjects/getExamGradesactions.ts 831→691 行data-access.ts 374→471 行 |
| homework | `createHomeworkAssignmentAction`157 行)/ `startHomeworkSubmissionAction` / `saveHomeworkAnswerAction` / `submitHomeworkAction` / `gradeHomeworkSubmissionAction` | ✅ 新建 `data-access-write.ts`285 行10 个写函数actions.ts 387→239 行 |
| questions | `createQuestionAction` / `updateQuestionAction` / `deleteQuestionAction` / `getKnowledgePointOptionsAction` | ✅ 新增 4 个 data-access 函数createQuestionWithRelations/updateQuestionById/deleteQuestionByIdRecursive/getKnowledgePointOptionsactions.ts 294→149 行data-access.ts 138→260 行 |
| announcements | 所有写操作 Action | ✅ 新增 5 个 data-access 函数insertAnnouncement/updateAnnouncementById/deleteAnnouncementById/publishAnnouncementById/archiveAnnouncementByIdactions.ts 242→197 行data-access.ts 120→171 行 |
**剩余未修复模块**(不在本次 P1-2 范围):
- ✅ users~~`updateUserProfileAction` 直接 db.update~~ 已下沉到 data-accessP1-1 修复)
- ✅ scheduling~~`applyAutoScheduleAction` / `autoScheduleAction` 直接 db.transaction + db.select~~ 已改为调用 `replaceClassSchedule` 统一写入口;`autoScheduleAction` 直查 users 表已改为通过 users data-accessP0-5 / P1-1 修复)
### P1-3auth.ts 混合 5 类职责 ✅ 已完成
`src/auth.ts` 原 293 行混合NextAuth 配置 + 密码安全 DB 操作 + 角色规范化 + IP 解析 + 回调函数。
**已完成拆分**auth.ts 现 193 行,仅保留 NextAuth 配置):
- ✅ 密码安全 DB 操作 → `shared/lib/password-security-service.ts`getOrCreatePasswordSecurity / recordFailedLogin / resetFailedLoginserver-only
- ✅ 角色规范化 → `shared/lib/role-utils.ts`normalizeRole / resolvePrimaryRole纯函数
- ✅ bcrypt 哈希规范化 → `shared/lib/bcrypt-utils.ts`normalizeBcryptHash纯函数
- ✅ IP 解析 → `shared/lib/http-utils.ts`resolveClientIpserver-only
**后续可选优化**(未执行,需保持 NextAuth 配置不变原则下评估):
- `authorize` 回调可进一步拆分为 `checkRateLimit` / `checkAccountLockout` / `verifyPassword` / `loadUserRoles`,使 auth.ts 降至 ≤150 行
### P1-4users/import-export.ts 四重职责 ✅ 已完成
`users/import-export.ts` 原 291 行混合:导入解析 + 导出 + 用户创建(含密码哈希)+ 班级注册(跨模块写 classEnrollments
**已完成拆分**import-export.ts 现 157 行,仅保留文件解析/生成):
- ✅ 用户创建(含密码哈希、角色分配)→ `user-service.ts``batchImportUsers`82 行server-only
- ✅ 班级注册 → `class-registration.ts``registerStudentByInvitationCode`21 行server-only
-`batchImportUsers` 不再直写 `classEnrollments`,改为调用 `classes/data-access.enrollStudentByInvitationCode`
-`import-export.ts` 通过 re-export `batchImportUsers` / `UserImportResult` 保持向后兼容(`actions.ts``app/api/export/route.ts` 无需修改)
### P1-5notifications 反向依赖 messaging ✅ 已完成
`notifications/data-access.ts``in-app-channel.ts` 原反向依赖 messaging 模块的偏好和 createNotification。
**已完成修复**(与 P0-4 一并解决):
- ✅ 将 `messageNotifications``notificationPreferences` 表所有权移交 notifications 模块
-`notifications/data-access.ts` 不再 import messaging 模块
-`notifications/channels/in-app-channel.ts` 改为静态导入本地 `createNotification`(不再动态 import messaging
-`notifications/dispatcher.ts` 改为从本地 `preferences.ts` 导入偏好函数
- ✅ messaging 模块通过 re-export 保持向后兼容
### P1-6三个 logger 重复实现 IP/Header 提取 ✅ 已修复
`audit-logger.ts` / `change-logger.ts` / `login-logger.ts` / `auth.ts` 四处重复实现 IP/User-Agent 提取逻辑,且实现略有差异。
**修复方案**(已实施):
-`shared/lib/http-utils.ts` 新增 `getUserAgent()` 函数(与已有 `resolveClientIp()` 配套)
-`audit-logger.ts` / `change-logger.ts` / `login-logger.ts` 改为从 `@/shared/lib/http-utils` 导入 `resolveClientIp``getUserAgent`,删除本地重复实现
-`auth.ts` 已在 P1-3 中改用 `resolveClientIp`
- ✅ 四处实现统一,消除不一致风险(`resolveClientIp``x-forwarded-for` 第一段,更准确)
---
## 3.3 P2 代码质量问题(机会修复)
| 序号 | 问题 | 模块 |
|------|------|------|
| P2-1 | `exams/ai-pipeline.ts` 857 行,混合 4 类职责 | exams |
| ~~P2-2~~ | ~~`exams/actions.ts` 832 行(超 800 建议)~~ ✅ 已修复P1-2 后降至 691 行) | exams |
| ~~P2-3~~ | ~~`shared/lib/ai.ts` 218 行,混合 5 类职责~~ ✅ 已修复P2-2 已拆分为 `ai/` 目录) | shared |
| ~~P2-4~~ | ~~`onboarding-gate.tsx` 业务逻辑泄漏到 shared~~ ✅ 已修复(迁移至 `modules/onboarding/`,改用独立路由 + Server Action + middleware 重定向) | shared/onboarding |
| P2-5 | `global-search.tsx` 业务类型硬编码在 shared | shared |
| ~~P2-6~~ | ~~`proxy.ts` 硬编码权限字符串,未复用 Permissions 常量~~ ✅ 已修复(改用 `Permissions` 常量) | proxy |
| ~~P2-7~~ | ~~`useA11yId` Hook 错放在 lib/ 而非 hooks/~~ ✅ 已修复(文件已不存在;`use-aria-live.ts` 已在 `hooks/` 目录) | shared |
| ~~P2-8~~ | ~~`schema.ts` 分节编号混乱section 12 出现在 14b 之后)~~ ✅ 已修复(重新编号为连续 1-24 | shared/db |
| P2-9 | `audit/actions.ts` Excel 导出逻辑内联 | audit |
| P2-10 | school 模块审计日志不一致(仅 school 实体记录) | school |
| ~~P2-11~~ | ~~`announcements` 死代码 `void wasPublished`~~ ✅ 已修复(代码中已不存在) | announcements |
| ~~P2-12~~ | ~~`announcements` 权限模式不一致requireAuth vs requirePermission~~ ✅ 已修复 | announcements |
| P2-13 | ~~`files` try-catch 吞错误~~ ✅ 已修复(所有 catch 块已添加 console.errorconditions 隐式 any[] 改为 SQL[] | files |
| ~~P2-14~~ | ~~`elective` runLottery 使用 Math.random~~ ✅ 已修复(改为 Fisher-Yates 无偏洗牌) | elective |
| ~~P2-15~~ | ~~`elective` selectCourse FCFS 并发超卖风险~~ ✅ 已修复db.transaction + FOR UPDATE 行锁) | elective |
| P2-16 | `diagnostic` 班级报告 studentId 字段复用 | diagnostic |
| ~~P2-17~~ | ~~`layout` 用权限反推角色~~ ✅ 已修复(`app-sidebar.tsx` 改用 `hasRole()` 判断角色N3 新增多角色切换机制:`SidebarContext.currentRole`/`setCurrentRole` + 侧边栏底部 Select 下拉) | layout |
| ~~P2-18~~ | ~~`scheduling/actions.ts` 末尾 re-export data-access~~ ✅ 已修复(移除 re-export4 个页面改为从 `data-access` 导入) | scheduling |
| P2-19 | `ExamAssembly` / `ExamPreviewQuestionEditor` 10 个 props | exams |
| P2-20 | ~~`homework/data-access.getDemoStudentUser` 使用 `auth()` 而非 auth-guard~~ ✅ 已修复(已迁移至 `users/data-access.getCurrentStudentUser`6 个 student 页面改用 users 模块;`elective` 页面改用 `getAuthContext()`homework 保留 re-export 向后兼容) | homework |
---
## 3.4 解耦优先级路线图
### 立即执行P0
1. ~~拆分 `classes/data-access.ts`2104 行 → 按职责拆 3-4 个文件)~~ ✅ 已完成(拆为 5 个文件data-access.ts 548行 + data-access-stats.ts 531行 + data-access-schedule.ts 194行 + data-access-students.ts 244行 + data-access-admin.ts 406行
2. ~~拆分 `homework/data-access.ts`1038 行 → 分离排名逻辑)~~ ✅ 已完成(拆为 data-access.ts 598行 + stats-service.ts 425行
3. ~~修复 `shared/lib` ↔ `auth` 循环依赖~~ ✅ 已完成(新增 `shared/lib/session.ts` 单一入口3 个 shared/lib 文件改为通过 getSession 获取 sessiondynamic import 打破静态循环)
4. ~~dashboard 改为通过各模块 data-access 获取数据~~ ✅ 已完成P0-3 修复:并行调用各模块 dashboard stats 函数)
5. ~~messaging 写通知改为通过 notifications dispatcher~~ ✅ 已完成P0-4 / P1-5 修复:通知 CRUD 和偏好迁移至 notifications 模块messaging 通过 dispatcher 发送通知)
6. ~~统一 classSchedule 写入口到 scheduling 模块~~ ✅ 已完成P0-5 修复classSchedule 写函数从 classes/data-access-schedule.ts 迁移至 scheduling/data-access-class-schedule.tsclasses 模块仅保留读函数)
7. ~~集成 proctoring/exam-mode-config 到考试表单~~ 部分完成P0-6 修复:删除重复的 /api/proctoring/event REST 路由Server Action 为唯一规范路径exam-mode-config.tsx 集成属于功能新增,暂保留原位)
### 短期执行P1
8. ~~actions 层移除直接 DB 操作exams/homework/questions/announcements/users/scheduling~~ ✅ 已完成(全部 6 个模块均已修复)
9. ~~拆分 `auth.ts`~~ ✅ 已完成4 个辅助函数组迁移至 shared/libauth.ts 保留 NextAuth 配置)
10. ~~拆分 `users/import-export.ts`~~ ✅ 已完成(拆为 import-export.ts 157行 + user-service.ts 82行 + class-registration.ts 21行班级注册改为调用 classes/data-access
11. ~~消除 notifications → messaging 反向依赖~~ ✅ 已完成P0-4 / P1-5 修复:通知表所有权迁移至 notificationsin-app-channel 改为静态导入)
12. ~~提取 `shared/lib/http-utils.ts` 统一 IP 提取~~ ✅ 已完成(新增 `getUserAgent`,三个 logger 统一复用 `resolveClientIp` / `getUserAgent`
13. ~~各模块暴露跨模块查询接口(见 P1-1~~ ✅ 已完成(所有跨模块直查已改为通过对方 data-access 接口)
### 中期执行P2
14. ~~建立模块间数据访问规范(通过对方 data-access 或导出查询函数)~~ ✅ 已完成P1-1 修复)
15. ~~`schema.ts` 按业务域分节~~ ✅ 已完成P2-8 修复:重新编号为连续 1-24消除 8b/14b/乱序问题)
16. 拆分 `exams/ai-pipeline.ts`
17. ~~拆分 `shared/lib/ai.ts`~~ ✅ 已完成P2-2commit 6588f74拆分为 `ai/` 目录 6 个文件,原 ai.ts 保留为重导出)
18. shared 层业务逻辑下沉到 modules 层
19. 代码质量问题逐项修复(✅ 大部分已修复React.cache 包装、Promise.all 并行化、错误吞没清理、非空断言清理、函数返回类型补齐、重复代码提取、合并 filter 遍历、Set/Map 优化、P2-6 proxy.ts 权限常量复用、P2-11 announcements 死代码清理、P2-17 layout 角色判断、P2-18 scheduling re-export 移除)
---
## 3.5 标杆实践(建议推广)
| 实践 | 模块 | 说明 |
|------|------|------|
| 算法纯函数化 | `scheduling/auto-scheduler.ts` | 无 DB 依赖,可独立测试,应作为算法抽取模板 |
| stats 文件拆分 | `attendance/data-access-stats.ts` | 统计逻辑独立成文件classes 应效仿 |
| data-access 多文件拆分 | `grades/data-access*.ts` | 按职责拆分为 3 个文件CRUD/分析/排名) |
| actions 辅助函数 | `course-plans/actions.ts` | handleError / revalidatePlanPaths 消除重复 |
| actions 编排模式 | `textbooks/actions.ts` | 权限校验 → 调用 data-access → revalidatePath标杆 |
| DataScope 接入 | `attendance/actions.ts` | 6 种数据范围完整支持 |
| 权限统一接入 | school / attendance / course-plans | 全部 Action 使用 requirePermission |
| 跨模块解耦 | `grades` | 通过外键引用 exams/homework不直接访问其表 |
| 渠道抽象 | `notifications/channels/` | 接口 + 工厂 + Mock 实现 |
---
# 附录 A模块间依赖矩阵
> 行表示使用方,列表示被使用方。`✅` 合理依赖,`❌` 违规直查,`⟳` 循环依赖。
> ✅ P1-1 已修复:所有跨模块直查已改为通过对方 data-access 接口。
| ↓ 使用 → | shared | auth | exams | homework | questions | textbooks | classes | school | dashboard | users | grades | messaging | notifications | lesson-prep | 其他 |
|----------|--------|------|-------|----------|-----------|-----------|---------|--------|-----------|-------|--------|-----------|---------------|-------------|------|
| **shared** | - | ⟳✅已修复 | - | - | - | - | - | - | - | - | - | - | - | - | - |
| **auth(root)** | ✅ db/lib | - | - | - | - | - | - | - | - | - | - | - | - | - | - |
| **exams** | ✅ | ✅ | - | - | ✅data-access | - | ✅data-access | ✅data-access | - | - | - | - | - | - | - |
| **homework** | ✅ | ✅ | ✅data-access | - | ✅关系 | - | ✅data-access | ✅data-access | - | ✅data-access | - | - | - | - | - |
| **questions** | ✅ | ✅ | - | - | - | ✅data-access | - | - | - | - | - | - | - | - | - |
| **textbooks** | ✅ | ✅ | - | - | ✅UI | - | - | - | - | - | - | - | - | - | - |
| **classes** | ✅ | ✅ | ✅data-access | ✅data-access | - | - | - | ✅data-access | - | - | - | - | - | - | ✅schedulingP0-5data-access-class-schedule 写函数) |
| **school** | ✅ | ✅ | - | - | - | - | - | - | - | ⚠️可接受 | - | - | - | - | - |
| **grades** | ✅ | ✅ | ✅外键 | ✅外键 | - | - | ✅data-access | ✅data-access | - | ✅data-access | - | - | - | - | - |
| **dashboard** | ✅ | ✅ | ✅data-access | ✅data-access | ✅data-access | ✅data-access | ✅data-access | - | - | ✅data-access | - | - | - | - | - |
| **users** | ✅ | ✅ | - | - | - | - | ✅data-access | - | - | - | - | - | - | - | - |
| **messaging** | ✅ | ✅ | - | - | - | - | ✅data-access | - | - | - | - | - | ✅dispatcher | - | - |
| **notifications** | ✅ | ✅ | - | - | - | - | ✅data-access | - | - | - | - | - | - | - | - |
| **attendance** | ✅ | ✅ | - | - | - | - | ✅data-access | - | - | - | - | - | - | - | - |
| **scheduling** | ✅ | ✅ | - | - | - | - | ✅data-access | - | - | ✅data-access | - | - | - | - | - |
| **proctoring** | ✅ | ✅ | ✅data-access | - | - | - | - | - | - | ✅data-access | - | - | - | - | - |
| **diagnostic** | ✅ | ✅ | ✅data-access | - | ✅data-access | - | ✅data-access | - | - | ✅data-access | - | - | - | - | - |
| **parent** | ✅ | ✅ | - | ✅data-access | - | - | ✅data-access | ✅data-access | - | ✅data-access | ✅data-access | - | - | - | - |
| **elective** | ✅ | ✅ | - | - | - | - | ✅data-access | ✅data-access | - | ✅data-access | - | - | - | - | - |
| **course-plans** | ✅ | ✅ | - | - | - | - | ✅ | ✅ | - | ✅ | - | - | - | - | - |
| **audit** | ✅ | ✅ | - | - | - | - | - | - | - | - | - | - | - | - | - |
| **announcements** | ✅ | ✅ | - | - | - | - | - | ✅ | - | - | - | - | - | - | - |
| **files** | ✅ | ✅ | - | - | - | - | - | - | - | - | - | - | - | - | - |
| **settings** | ✅ | ✅ | - | - | - | - | - | - | - | - | - | ✅ | - | - | - |
| **layout** | ✅ | ✅ | - | - | - | - | - | - | - | - | - | ✅ | - | - | - |
| **lesson-preparation** | ✅ | ✅ | ✅data-access | ✅data-access | ✅data-access | ✅data-access | ✅data-access | - | - | - | - | - | - | - | ✅files/ai |
---
# 附录 B关键参数影响链
### `userId`
1.`auth.ts` JWT callback 从 `users` 表查询产生,存入 JWT
2. 通过 `session.user.id` 传递到所有 Server/Client Components
3. 通过 `getAuthContext().userId` 传递到所有 Server Actions
4.`auth-guard.ts` 中用于查询 `usersToRoles`(角色)和 `classSubjectTeachers`/`grades`DataScope
5.`exams/actions.ts` 中作为 `creatorId` 写入 `exams`
6.`homework/actions.ts` 中作为 `creatorId` 写入 `homeworkAssignments`
7.`classes/data-access.ts` 中查询 `getTeacherClasses(teacherId)` / `getGradeManagedClasses(userId)`
8.`elective/actions.ts` 中作为 `teacherId`/`studentId` 用于选课过滤
### `examId`
1.`exams/actions.createExamAction` 产生CUID2写入 `exams`
2.`exams/data-access.getExamById(id)` 读取
3.`exams/actions``updateExamAction`/`deleteExamAction`/`duplicateExamAction` 用于定位考试
4. 传入 `homework/actions.createHomeworkAssignmentAction``sourceExamId` 参数
5.`homeworkAssignments` 表中作为外键关联到源考试
6.`homework/data-access.getHomeworkAssignmentAnalytics` 用于追溯作业来源
### `classId`
1.`classes/actions``createTeacherClassAction`/`createAdminClassAction` 产生
2.`classes/data-access.getClassStudents(classId)` 读取学生列表
3.`classes/data-access.getClassSchedule(classId)` 读取课表
4.`classes/data-access.getClassHomeworkInsights(classId)` 读取作业洞察(✅ P0-7 已修复:通过 `homework/data-access-classes` 获取数据)
5.`homework/data-access.getHomeworkAssignments({ classId })` 过滤作业列表
6.`auth-guard.ts` 中通过 `classSubjectTeachers` 查询教师关联的 classIds构建 `DataScope.class_taught`
### `permission`
1.`shared/types/permissions.ts``Permissions` 常量定义61 个权限点)
2.`shared/lib/permissions.ts` 中通过 `ROLE_PERMISSIONS` 映射角色到权限列表
3.`auth.ts` JWT callback 中通过 `resolvePermissions(roleNames)` 合并多角色权限,存入 JWT
4.`proxy.ts` middleware 中通过 `token.permissions` 检查路由访问权限
5.`shared/lib/auth-guard.ts` 中通过 `requirePermission(permission)` 在 Server Action 层断言权限
6.`shared/hooks/use-permission.ts` 中通过 `hasPermission(permission)` 在客户端组件中条件渲染
7.`layout/config/navigation.ts` 中作为 `NavItem.permission` 字段过滤侧边栏菜单
### `DataScope`
1.`auth-guard.ts``resolveDataScope(userId, roles)` 根据用户角色和 DB 关系动态计算
2. 支持类型:`all` / `grade_managed` / `class_taught` / `class_members` / `children` / `owned`
3. 传递到 `exams`/`homework`/`grades`/`attendance`/`elective`/`dashboard` 的 data-access 进行行级过滤
4. 对 parent 角色,查询 `parentStudentRelations` 表构建 `{ type: "children", childrenIds: string[] }`
5.`parent/children/[studentId]/page.tsx` 中通过 `ctx.dataScope.childrenIds.includes(studentId)` 二次校验
---
# 附录 C核心函数签名索引
> 完整函数签名见 `005_architecture_data.json`。本附录仅列出关键函数。
### shared 层核心函数
```typescript
// shared/lib/auth-guard.ts
getAuthContext(): Promise<AuthContext>
requirePermission(permission: Permission): Promise<AuthContext>
requireAuth(): Promise<AuthContext>
checkPermission(permission: Permission): Promise<{ allowed: boolean; ctx: AuthContext }>
resolveDataScope(userId: string, roleNames: string[]): Promise<DataScope>
// shared/lib/permissions.ts
resolvePermissions(roleNames: string[]): Permission[]
// shared/lib/audit-logger.ts
logAudit(params: LogAuditParams): Promise<void>
// shared/lib/login-logger.ts
logLoginEvent(params: LogLoginEventParams): Promise<void>
// shared/lib/change-logger.ts
logDataChange(params: LogDataChangeParams): Promise<void>
// shared/lib/session.ts (P0-2 新增session 获取单一入口)
getSession(): Promise<AppSession>
// shared/lib/http-utils.ts (P1-6 完成,统一 IP/UA 提取)
resolveClientIp(): Promise<string>
getUserAgent(): Promise<string>
// shared/lib/ai/ (P2-2 已拆分,原 ai.ts 保留为重导出)
// ai/client.ts
createAiChatCompletion(input: AiChatRequest): Promise<{ content, usage }>
// ai/payload-parser.ts
parseAiChatPayload(body: unknown): AiChatRequest
// ai/api-key-crypto.ts
encryptAiApiKey(value: string): string
decryptAiApiKey(value: string): string
// shared/lib/password-policy.ts
validatePassword(password: string): { valid: boolean; errors: string[] }
getPasswordStrength(password: string): "weak" | "medium" | "strong"
isAccountLocked(failedAttempts: number, lastFailedAt: Date | null): boolean
// shared/lib/rate-limit.ts
rateLimit(params: { key: string; limit: number; windowMs: number }): RateLimitResult
rateLimitKey(prefix: string, identifier: string): string
rateLimitHeaders(result: RateLimitResult): Record<string, string>
// shared/lib/excel.ts
exportToExcel(params: { sheets: ExcelSheet[] }): Promise<Buffer>
parseExcel(buffer: Buffer): Promise<ParsedSheet[]>
generateTemplate(params: { sheets: TemplateSheet[] }): Promise<Buffer>
// shared/lib/file-storage.ts
isAllowedMimeType(mimeType: string): boolean
generateStoragePath(originalName: string): string
formatFileSize(bytes: number): string
// shared/lib/utils.ts
cn(...inputs: ClassValue[]): string
formatDate(date: string | Date, locale?: string): string
getSearchParam(params: SearchParams, key: string): string | undefined
formatNumber(v: number | null | undefined, digits?: number): string
// shared/lib/search-params.ts (re-export from utils.ts)
getParam(params: SearchParams, key: string): string | undefined // = getSearchParam
```
### 业务模块核心 Actions
```typescript
// exams/actions.ts
createExamAction(prevState: ActionState, formData: FormData): Promise<ActionState<Exam>>
createAiExamAction(prevState: ActionState, formData: FormData): Promise<ActionState<{ examId: string }>>
updateExamAction(prevState: ActionState, formData: FormData): Promise<ActionState>
deleteExamAction(prevState: ActionState, formData: FormData): Promise<ActionState>
duplicateExamAction(prevState: ActionState, formData: FormData): Promise<ActionState<{ examId: string }>>
// homework/actions.ts
createHomeworkAssignmentAction(prevState: ActionState, formData: FormData): Promise<ActionState>
startHomeworkSubmissionAction(prevState: ActionState, formData: FormData): Promise<ActionState<{ submissionId: string }>>
saveHomeworkAnswerAction(prevState: ActionState, formData: FormData): Promise<ActionState>
submitHomeworkAction(prevState: ActionState, formData: FormData): Promise<ActionState>
gradeHomeworkSubmissionAction(prevState: ActionState, formData: FormData): Promise<ActionState>
// classes/actions.ts
createTeacherClassAction(prevState: ActionState, formData: FormData): Promise<ActionState>
createAdminClassAction(prevState: ActionState, formData: FormData): Promise<ActionState>
createGradeClassAction(prevState: ActionState, formData: FormData): Promise<ActionState>
// + update/delete 各 3 个,共 9 个
// grades/actions.ts
getGradeRecordsAction(params: GetGradeRecordsParams): Promise<ActionState<GradeRecord[]>>
createGradeRecordAction(prevState: ActionState, formData: FormData): Promise<ActionState>
exportGradesAction(params: ExportGradesParams): Promise<ActionState<Buffer>>
// scheduling/actions.ts
autoScheduleAction(prevState: ActionState, formData: FormData): Promise<ActionState>
applyAutoScheduleAction(prevState: ActionState, formData: FormData): Promise<ActionState>
```
### 业务模块核心 Data-access
```typescript
// exams/data-access.ts
getExams(params: GetExamsParams & { scope: DataScope }): Promise<{ items: Exam[]; total: number }>
getExamById(id: string, scope: DataScope): Promise<Exam | null>
persistExamDraft(input: ExamDraftInput): Promise<{ examId: string }>
persistAiGeneratedExamDraft(input: AiExamDraftInput): Promise<{ examId: string }>
// homework/data-access.ts
getHomeworkAssignments(params: GetHomeworkAssignmentsParams & { scope: DataScope }): Promise<{ items, total }>
getStudentHomeworkAssignments(studentId: string): Promise<StudentHomeworkAssignment[]>
getStudentDashboardGrades(studentId: string): Promise<StudentDashboardGrades>
getHomeworkAssignmentAnalytics(assignmentId: string): Promise<HomeworkAnalytics>
// classes/data-access.ts
getAdminClasses(scope: DataScope): Promise<Class[]>
getTeacherClasses(teacherId: string): Promise<Class[]>
getStudentClasses(studentId: string): Promise<Class[]>
getClassStudents(classId: string): Promise<Student[]>
getClassHomeworkInsights(classId: string): Promise<ClassHomeworkInsights> // ✅ P0-7 已修复:通过 homework/data-access-classes 获取数据
// grades/data-access.ts
getGradeRecords(params: GetGradeRecordsParams & { scope: DataScope }): Promise<GradeRecord[]>
getStudentGradeSummary(studentId: string): Promise<StudentGradeSummary>
getClassRanking(classId: string, examId?: string): Promise<ClassRanking[]>
// scheduling/auto-scheduler.ts纯函数标杆
findOptimalSlot(input: FindOptimalSlotInput): ScheduleSlot | null
validateSchedule(schedule: ScheduleItem[]): ValidationResult
autoSchedule(input: AutoScheduleInput): AutoScheduleResult
buildDefaultTimeSlots(): TimeSlot[]
```
---
## 文档维护说明
- **修改源码后**:同步更新本文档对应模块章节 + `005_architecture_data.json`
- **新增模块**:在第二部分添加模块清单 + 更新 1.1 分层架构图 + 更新附录 A 依赖矩阵
- **新增/删除导出函数**:更新对应模块的"导出函数"清单 + 附录 C
- **修改依赖关系**:更新 1.2 模块依赖关系图 + 附录 A 依赖矩阵
- **新增路由**:更新 `005_architecture_data.json``routes` 节点
- **新增数据库表**:更新 `shared/db/schema.ts` 分节 + `005``dbTables` 节点
> 完整路由表、DevOps 脚本、E2E 测试等信息见 `005_architecture_data.json`。