# 新增模块与其他模块架构审查报告

> 审查范围：elective / proctoring / diagnostic / notifications / dashboard / messaging / parent / settings / files / auth / layout / student
> 审查日期：2026-06-17
> 审查依据：源码全量扫描 + 架构影响地图(004/005) + 差距审计报告(007)
> 审查目标：识别职责不单一、过耦合、边界模糊、幽灵路由等问题

---

## 一、总体评估

| 维度 | 模块数 | 严重问题 | 中等问题 | 轻微问题 | 总体评价 |
|------|--------|---------|---------|---------|----------|
| 新增模块 | 4 | 3 | 5 | 3 | ⚠️ 职责基本清晰，但存在跨模块耦合与未集成代码 |
| 其他模块 | 8 | 1 | 4 | 3 | ⚠️ dashboard 跨模块直查问题突出 |
| **合计** | **12** | **4** | **9** | **6** | **需重点修复 4 项严重问题** |

### 严重问题清单（必须修复）

1. ~~**messaging 与 notifications 边界模糊、双向依赖** — 两个模块都写入 `messageNotifications` 表，notifications 反向依赖 messaging，类型系统不一致~~ ✅ 已修复（P0-5 + P1-6）
2. ~~**dashboard/data-access.ts 直查 11 张跨模块表** — 违反模块封装原则~~ ✅ 已修复（P0-4）
3. **proctoring/exam-mode-config.tsx 未集成到考试表单** — DB schema 有 examMode 等字段但无 UI 录入入口，组件成为死代码 ⚠️ 用户决定保留
4. **proctoring 事件上报存在 Server Action 与 REST API 双通道重复** — 同一逻辑两份代码

---

## 二、新增模块审查

### 2.1 elective 模块（选课管理）

#### 模块结构

| 文件 | 行数 | 职责 |
|------|------|------|
| `actions.ts` | 304 | 11 个 Server Action（CRUD + 选课 + 抽签 + 查询） |
| `data-access.ts` | 242 | 课程 CRUD + 查询 + scope 过滤 |
| `data-access-operations.ts` | 217 | 选课操作（select/drop/lottery） |
| `data-access-selections.ts` | 189 | 选课记录查询 + 学生可用课程查询 |
| `schema.ts` | 132 | Zod 校验 schema |
| `types.ts` | 108 | 类型定义 + 标签常量 |

#### 拆分为 3 个 data-access 文件是否合理？

**结论：⚠️ 拆分意图合理，但存在代码重复**

- **拆分意图合理**：operations（写操作）与 selections（读查询）职责分明，符合"按职责划分"原则
- **代码重复问题**：`data-access.ts` 与 `data-access-selections.ts` 重复定义了 `mapCourseRow` 和 `buildCourseSelect` 两个函数（共约 60 行重复代码）
  - `data-access.ts` 第 47-108 行
  - `data-access-selections.ts` 第 28-88 行
  - 两份代码完全相同，维护时易产生不一致

**建议**：
- 抽取共享的 `mapCourseRow` / `buildCourseSelect` 到 `data-access.ts` 并导出，`data-access-selections.ts` 复用
- 或合并 `data-access-selections.ts` 到 `data-access.ts`（文件仅 189 行，合并后仍在 800 行上限内）

#### 权限校验

✅ 全部 Server Action 均使用 `requirePermission`：
- 管理 Action 使用 `ELECTIVE_MANAGE`
- 选课 Action 使用 `ELECTIVE_SELECT`
- 查询 Action 使用 `ELECTIVE_READ`
- 学生查看他人选课记录有额外 dataScope 校验（actions.ts 第 283-288 行）

#### 其他发现

- ✅ schema.ts 使用 Zod transform 统一处理空字符串转 null，规范
- ⚠️ `data-access-operations.ts` 的 `runLottery` 使用 `Math.random()` 排序（第 40 行），结果不可复现，建议改用 DB 的 `ORDER BY RAND()` 或记录种子
- ⚠️ `selectCourse` 在 FCFS 模式下先查后写（第 119-128 行），存在并发超卖风险，建议加事务或乐观锁

---

### 2.2 proctoring 模块（考试监考）

#### 模块结构

| 文件 | 行数 | 职责 |
|------|------|------|
| `actions.ts` | 144 | 2 个 Server Action（上报事件 + 获取面板） |
| `data-access.ts` | 388 | 事件记录 + 查询 + 摘要统计 + 学生状态 |
| `types.ts` | 136 | 类型定义 + 标签常量 + 阈值常量 |
| `components/anti-cheat-monitor.tsx` | - | 学生端防作弊监控 |
| `components/exam-mode-config.tsx` | - | 考试模式配置表单（**未集成**） |
| `components/proctoring-dashboard.tsx` | - | 教师监考面板 |

#### 职责清晰度

**结论：⚠️ 职责基本清晰，但存在 3 个严重问题**

#### 严重问题 1：`exam-mode-config.tsx` 未集成到考试表单（死代码）⚠️ 用户决定保留

- DB schema 已有 `examMode` / `durationMinutes` / `shuffleQuestions` / `allowLateStart` / `antiCheatEnabled` 字段（schema.ts 第 457-462 行）
- `proctoring/components/exam-mode-config.tsx` 提供了完整的配置 UI 组件
- **但 `exams/components/exam-form.tsx` 并未导入该组件**（grep 确认无 `ExamModeConfig` 引用）
- `exams/components/exam-mode-selector.tsx` 是"手动组卷 vs AI 生成"的选择器，**与考试模式（homework/timed/proctored）无关**，命名易混淆
- 结果：创建考试时无法设置监考模式，proctoring 模块的 `getExamForProctoring` 读取的 `examMode` 永远是默认值 `"homework"`

**状态**：用户决定保留该组件，暂不集成也不删除。后续如需启用监考功能，可再集成到 `exam-form.tsx`。

#### 严重问题 2：事件上报存在 Server Action 与 REST API 双通道重复

- `proctoring/actions.ts` 第 58-105 行：`recordProctoringEventAction`（Server Action）
- `app/api/proctoring/event/route.ts` 第 27-90 行：POST handler（REST API）
- **两者逻辑完全相同**：都校验 submission 归属、调用 `recordProctoringEvent`
- `AntiCheatMonitor` 组件使用 Server Action（第 58 行），REST API 路由无调用方

**建议**：删除未使用的 `/api/proctoring/event` 路由，或让组件改用 REST API（适用于客户端轮询场景）

#### 中等问题 3：跨模块读取 exams 表

- `data-access.ts` 第 326-353 行：`getExamForProctoring` 直接查询 `exams` 表
- 第 178-185 行：`getExamProctoringSummary` 直接查询 `examSubmissions` 表
- 第 249-256 行：`getStudentProctoringStatuses` 直接 join `examSubmissions` 和 `users`

**建议**：可接受（监考本质是考试模块的扩展），但应在架构图中标注依赖关系

#### 其他发现

- ✅ `recordProctoringEventAction` 使用 `requireAuth()` 而非 `requirePermission`，符合"学生上报自己事件"场景
- ✅ `getProctoringDashboardAction` 使用 `EXAM_PROCTOR` 权限
- ✅ 异常阈值 `ABNORMAL_EVENT_THRESHOLD = 3` 提取为常量，便于调整
- ⚠️ `actions.ts` 第 11-13 行直接 import `db` 和 `examSubmissions`，应在 data-access 层封装 submission 校验逻辑

---

### 2.3 diagnostic 模块（学情诊断）

#### 模块结构

| 文件 | 行数 | 职责 |
|------|------|------|
| `actions.ts` | 148 | 6 个 Server Action（生成/发布/删除/查询报告） |
| `data-access.ts` | 254 | 知识点掌握度查询 + 从提交更新掌握度 |
| `data-access-reports.ts` | 202 | 诊断报告 CRUD |
| `types.ts` | 97 | 类型定义 |
| `components/` | 4 个 | 学生/班级诊断视图 + 雷达图 + 报告列表 |

#### 与 grades 模块的边界

**结论：✅ 无职责重叠，但存在跨模块耦合**

- **grades 模块**：管理 `gradeRecords` 表（分数记录），维度是"学生-班级-科目-考试"
- **diagnostic 模块**：管理 `knowledgePointMastery` 表（知识点掌握度），维度是"学生-知识点"
- 两者数据来源不同：grades 是教师录入的分数，diagnostic 是从 `submissionAnswers` 推导的正确率
- **无任何代码重叠**：grep 确认 grades 模块无 `knowledgePoint` / `mastery` / `diagnostic` 关键字

#### 中等问题 1：`data-access.ts` 跨模块直查 4 张表

- 第 87-92 行：查询 `examSubmissions` 表（属 exams 模块）
- 第 95-101 行：查询 `submissionAnswers` 表（属 exams/homework 模块）
- 第 106-112 行：查询 `questionsToKnowledgePoints` 表（属 questions 模块）
- 第 150-158 行：查询 `classEnrollments` + `classes` + `users` 表（属 classes 模块）

`updateMasteryFromSubmission` 函数（第 87-147 行）直接读取提交答案和题目-知识点关联，将 diagnostic 模块与 exams/homework/questions 模块紧耦合。

**建议**：
- 短期：在架构图中标注此依赖关系
- 长期：由 exams/homework 模块在提交评分后主动调用 diagnostic 模块的更新接口（事件驱动）

#### 轻微问题 2：`data-access-reports.ts` 有未使用代码

- 第 20 行定义 `round2` 函数
- 第 201 行 `void round2` 仅为消除 lint 警告
- **建议**：删除未使用的 `round2` 函数

#### 轻微问题 3：班级报告字段复用不当

- `data-access-reports.ts` 第 107 行：`studentId: generatedBy` — 班级报告将生成者 ID 存入 `studentId` 字段
- 注释说明"schema 要求 NOT NULL"，但这是 schema 设计缺陷的 workaround
- **建议**：修改 `learningDiagnosticReports` schema，将 `studentId` 改为可空，或增加 `classId` 字段

#### 权限校验

✅ 全部 Action 使用 `DIAGNOSTIC_MANAGE` 或 `DIAGNOSTIC_READ` 权限

---

### 2.4 notifications 模块（通知分发）

#### 模块结构

| 文件 | 行数 | 职责 |
|------|------|------|
| `actions.ts` | 119 | 2 个 Server Action（单发 + 班级群发） |
| `data-access.ts` | 86 | 用户偏好 + 联系方式 + 日志 |
| `dispatcher.ts` | 152 | 渠道选择 + 并行分发 |
| `types.ts` | 70 | 通知负载 + 渠道配置类型 |
| `index.ts` | 38 | 对外导出入口 |
| `channels/` | 5 个 | SMS/Email/WeChat/InApp 渠道实现 |

#### 严重问题 1：与 messaging 模块双向依赖、边界模糊 ✅ 已修复

~~详见下文"三、messaging vs notifications 边界分析"。~~

**已完成修复**（2026-06-17，P0-5 + P1-6）：
- messaging/actions.ts 改用 `sendNotification` from `@/modules/notifications/dispatcher`（P0-5）
- notifications/channels/in-app-channel.ts 将静态 import 改为动态 `await import("@/modules/messaging/data-access")`，打破模块级静态反向依赖（P1-6）
- 依赖方向已统一：messaging → notifications（单向）

#### 中等问题 2：`sendClassNotificationAction` 跨模块直查

- `actions.ts` 第 83-96 行：直接查询 `classes` 和 `classEnrollments` 表
- 应通过 classes 模块的 data-access 获取班级学生列表

**建议**：调用 `classes` 模块的 data-access 函数获取学生 ID 列表

#### 中等问题 3：发送日志仅 console 输出

- `data-access.ts` 第 71-77 行：`logNotificationSend` 使用 `console.info`
- 代码注释承认"当前项目无 notification_logs 表"
- **影响**：无法查询历史发送记录、无法统计发送成功率、无法排查发送失败

**建议**：新增 `notification_logs` 表，记录 channel/userId/payload/success/error/sentAt

#### 轻微问题 4：复用 MESSAGE_SEND 权限

- `actions.ts` 第 34、67 行：使用 `Permissions.MESSAGE_SEND`
- 代码注释说明"项目无独立 NOTIFICATION_SEND 权限点"
- **影响**：无法单独控制"谁能发通知"vs"谁能发私信"

**建议**：新增 `NOTIFICATION_SEND` 权限点，或确认复用是设计意图

#### 设计亮点

- ✅ 渠道抽象优秀：`NotificationChannelSender` 接口 + 工厂函数，新增渠道只需实现接口
- ✅ Mock 实现完善：SMS/Email/WeChat 均有 Mock 实现，开发环境零配置可用
- ✅ 动态 import 第三方 SDK（阿里云/腾讯云/nodemailer），避免增加构建体积
- ✅ 渠道选择逻辑清晰（dispatcher.ts 第 59-95 行）

---

## 三、messaging vs notifications 边界分析（重点）✅ 已修复

> **状态**：双向依赖与绕过 dispatcher 问题已于 2026-06-17 修复（P0-5 + P1-6）。以下为修复前的现状记录，保留作为历史参考。

### 3.1 现状对比（修复前）

| 维度 | messaging 模块 | notifications 模块 |
|------|---------------|-------------------|
| **核心职责** | 站内私信 + 站内通知列表 | 多渠道通知分发 |
| **管理的表** | `messages` + `messageNotifications` + `notificationPreferences` | 无独有表（借用 messaging 的表） |
| **写入 messageNotifications** | ✅ 直接写（`createNotification`） | ✅ 通过 in-app 渠道写 |
| **通知类型枚举** | `NotificationType = "message" \| "announcement" \| "homework" \| "grade"` | `NotificationPayload.type = "info" \| "warning" \| "error" \| "success"` |
| **UI 组件** | message-list / message-detail / message-compose / notification-dropdown / notification-list | 无 UI 组件 |
| **偏好管理** | ✅ `notification-preferences.ts` | ❌ 借用 messaging 的 |
| **渠道支持** | 仅站内 | 站内 + SMS + Email + WeChat |

### 3.2 严重问题：双向依赖与职责重叠 ✅ 已修复

#### 问题 1：notifications 反向依赖 messaging ✅ 已修复

~~notifications/data-access.ts~~
~~  → import { getNotificationPreferences } from "@/modules/messaging/notification-preferences"~~
~~  → import type { NotificationPreferences } from "@/modules/messaging/types"~~

~~notifications/channels/in-app-channel.ts~~
~~  → import { createNotification } from "@/modules/messaging/data-access"~~

**修复方案**：notifications/channels/in-app-channel.ts 将静态 import 改为动态 `await import("@/modules/messaging/data-access")`，打破模块级静态反向依赖。运行时调用链保持不变，但模块加载图无环。

#### 问题 2：messaging 绕过 notifications 直接写通知 ✅ 已修复

~~`messaging/actions.ts` 第 66-72 行：~~

```typescript
// ~~Notify the receiver about the new message~~
// ~~await createNotification({~~
// ~~  userId: input.receiverId,~~
// ~~  type: "message",~~
// ~~  ...~~
// ~~})~~
```

**修复方案**：messaging/actions.ts 改用 `sendNotification` from `@/modules/notifications/dispatcher`，通知现在会经过 dispatcher 的渠道选择逻辑，尊重用户偏好（SMS/Email/WeChat/In-App）。

#### 问题 3：类型系统不一致（保留）

- `messaging/types.ts` 第 23 行：`NotificationType = "message" | "announcement" | "homework" | "grade"`（按业务类别）
- `notifications/types.ts` 第 20 行：`type: "info" | "warning" | "error" | "success"`（按严重级别）
- `in-app-channel.ts` 第 49 行：`type: payload.type as "message" | "announcement" | "homework" | "grade"` — **强制类型转换，运行时可能写入非法值**

DB schema 中 `messageNotifications.type` 为 `varchar(128)`，虽然不会报错，但语义混乱。（P2 待统一）

#### 问题 4：notification-preferences 归属不清（保留）

- `notificationPreferences` 表的 data-access 在 messaging 模块
- 但 notifications 模块的 dispatcher 依赖此偏好决定渠道
- settings 模块的 `notification-preferences-form.tsx` 调用 `messaging/actions.ts` 的 `updateNotificationPreferencesAction`
- 三个模块都在操作同一份数据，职责归属不清（P2 待重构）

### 3.3 建议方案

**方案 A（推荐）：notifications 吞并 messaging 的通知部分**

1. 将 `messageNotifications` 表和 `notificationPreferences` 表的所有权移交给 notifications 模块
2. messaging 模块仅保留 `messages` 表（私信）
3. messaging 的 `sendMessageAction` 改为调用 `notifications.sendNotification`
4. messaging 的 UI 组件（notification-dropdown / notification-list）迁移到 notifications 模块
5. 统一 `NotificationType` 枚举，支持业务类别 + 严重级别两个维度

**方案 B：保持现状，明确依赖方向**

1. 在架构图中标注 notifications → messaging 的单向依赖
2. messaging 不再直接调用 `createNotification`，改为调用 `notifications.sendNotification`
3. 消除双向依赖，但 notifications 仍不拥有数据

---

## 四、dashboard 模块审查（重点）

### 4.1 严重问题：`data-access.ts` 直查 11 张跨模块表 ✅ 已修复

~~`dashboard/data-access.ts` 的 `getAdminDashboardData` 函数直接查询以下表~~

**已完成修复**（2026-06-17，P0-4）：dashboard/data-access.ts 从大文件降至 42 行，改为并行调用 6 个模块的 stats 函数：

```typescript
const [usersStats, classesStats, textbooksStats, questionsStats, examsStats, homeworkStats] = await Promise.all([
  getUsersDashboardStats(),
  getClassesDashboardStats(),
  getTextbooksDashboardStats(),
  getQuestionsDashboardStats(),
  getExamsDashboardStats(scope),
  getHomeworkDashboardStats(scope),
])
```

不再直接查询任何业务表，完全通过各模块 data-access 暴露的聚合查询函数获取数据。

### 4.2 学生/教师仪表盘的对比

**学生仪表盘**（`app/(dashboard)/student/dashboard/page.tsx`）：
- ✅ 正确做法：调用 `classes/data-access` 的 `getStudentClasses` / `getStudentSchedule`
- ✅ 调用 `homework/data-access` 的 `getStudentDashboardGrades` / `getStudentHomeworkAssignments`
- ✅ 不直接查询任何表

**教师仪表盘**（`app/(dashboard)/teacher/dashboard/page.tsx`）：
- ✅ 调用 `classes/data-access` 的 `getClassSchedule` / `getTeacherClasses`
- ✅ 调用 `homework/data-access` 的 `getHomeworkAssignments` / `getHomeworkSubmissions` / `getTeacherGradeTrends`
- ⚠️ 第 18-21 行直接查询 `users` 表获取教师姓名：`db.query.users.findFirst(...)` — 应使用 users 模块的 data-access

### 4.3 建议方案

**方案 A（理想）：聚合 API 模式**

为每个模块添加 `getModuleStats(scope?)` 函数，dashboard 聚合调用：
```typescript
const [userStats, classStats, textbookStats, ...] = await Promise.all([
  getUsersStats(scope),
  getClassStats(scope),
  getTextbookStats(),
  ...
])
```

**方案 B（务实）：接受 dashboard 作为跨模块聚合层**

- 在架构图中明确标注 dashboard 对所有业务模块的依赖
- 将 scope 过滤逻辑下沉到各模块的 `getStats` 函数
- 至少消除 dashboard 中重复实现的 exam/homework scope 过滤（第 31-73 行）

---

## 五、其他模块审查

### 5.1 messaging 模块

#### 模块结构

| 文件 | 行数 | 职责 |
|------|------|------|
| `actions.ts` | 245 | 9 个 Server Action（私信 + 通知 + 偏好） |
| `data-access.ts` | 252 | 私信 CRUD + 通知 CRUD + 收件人查询 |
| `notification-preferences.ts` | 166 | 通知偏好 CRUD |
| `schema.ts` | 17 | 私信发送校验 |
| `types.ts` | 108 | 私信 + 通知 + 偏好类型 |

#### 问题

- ❌ **职责过多**：同时管理私信（messages）、站内通知（messageNotifications）、通知偏好（notificationPreferences）三类数据
- ❌ **与 notifications 模块边界模糊**：详见第三节
- ⚠️ `getRecipients` 函数（第 227-251 行）根据 dataScope 查询收件人，逻辑较复杂，可考虑下沉到 users 模块

### 5.2 parent 模块

#### 模块结构

| 文件 | 行数 | 职责 |
|------|------|------|
| `data-access.ts` | 234 | 子女关系 + 子女仪表盘数据聚合 |
| `types.ts` | 57 | 类型定义 |
| `components/` | 7 个 | 子女卡片 + 详情 + 仪表盘 |

#### 评价

- ✅ **职责单一**：仅负责家长视角的子女数据聚合与展示
- ✅ **正确复用其他模块**：
  - 调用 `classes/data-access` 的 `getStudentClasses` / `getStudentSchedule`
  - 调用 `homework/data-access` 的 `getStudentDashboardGrades` / `getStudentHomeworkAssignments`
  - 调用 `grades/data-access` 的 `getStudentGradeSummary`
- ✅ 不直接查询业务表（仅查询 `parentStudentRelations` 自有表 + `users`/`classes`/`classEnrollments`/`grades` 用于基本信息）
- ⚠️ `getChildBasicInfo` 第 74-105 行多次串行查询（grade → class），可优化为 join

### 5.3 settings 模块

#### 模块结构

| 文件 | 行数 | 职责 |
|------|------|------|
| `actions.ts` | 205 | AI Provider CRUD + 测试连通性 |
| `actions-password.ts` | 113 | 修改密码 |
| `components/` | 8 个 | 通用设置 + AI 配置 + 密码 + 主题 + 通知偏好 |

#### 问题：职责混杂但可接受

settings 模块混合了 5 类职责：
1. AI Provider 管理（`actions.ts` + `ai-provider-settings-card.tsx`）
2. 密码修改（`actions-password.ts` + `password-change-form.tsx`）
3. 个人资料（`profile-settings-form.tsx`）
4. 主题偏好（`theme-preferences-card.tsx`）
5. 通知偏好表单（`notification-preferences-form.tsx` — **调用 messaging 模块的 Action**）

**评价**：
- ⚠️ AI Provider 管理与"用户设置"语义距离较远，可考虑独立为 `ai-config` 模块
- ⚠️ `notification-preferences-form.tsx` 第 14 行 import `updateNotificationPreferencesAction` from `@/modules/messaging/actions` — 跨模块 UI 依赖
- ✅ 密码修改有速率限制（`actions-password.ts` 第 33-37 行）
- ✅ AI Provider 操作有 `AI_CONFIGURE` 权限校验
- ✅ 密码修改仅要求 `requireAuth()`（自助操作），符合最小权限原则
- ⚠️ 无 `data-access.ts` 文件，`actions.ts` 直接使用 `db` — 建议抽取 data-access 层

### 5.4 files 模块

#### 模块结构

| 文件 | 行数 | 职责 |
|------|------|------|
| `data-access.ts` | 267 | 文件附件 CRUD + 批量删除 + 统计 |
| `types.ts` | - | 类型定义 |
| `components/` | 6 个 | 上传 + 列表 + 预览 + 管理 |

#### 评价

- ✅ **职责单一**：仅管理 `fileAttachments` 表
- ✅ 不跨模块查询
- ✅ 批量删除有容错处理（第 152-177 行，失败时回退到逐条删除）
- ⚠️ 所有函数都用 try-catch 吞掉错误返回空数组/null，可能掩盖真实问题
- ⚠️ 无 actions.ts 文件 — 文件上传通过 `app/api/upload/route.ts` 和 `app/api/files/[id]/route.ts` 实现，data-access 被路由直接调用

### 5.5 auth 模块

#### 模块结构

| 文件 | 职责 |
|------|------|
| `components/auth-layout.tsx` | 认证页面布局 |
| `components/login-form.tsx` | 登录表单 |
| `components/register-form.tsx` | 注册表单 |

#### 评价

- ✅ **纯 UI 模块**：无 data-access / actions / types 文件
- ✅ 认证逻辑由 NextAuth + `shared/lib/auth-guard` 统一处理
- ✅ 职责清晰

### 5.6 layout 模块

#### 模块结构

| 文件 | 职责 |
|------|------|
| `components/app-sidebar.tsx` | 侧边栏（根据权限渲染导航） |
| `components/sidebar-provider.tsx` | 侧边栏状态 Context |
| `components/site-header.tsx` | 顶部导航（含通知下拉） |
| `config/navigation.ts` | 导航配置（4 个角色） |

#### navigation.ts 幽灵路由审查

**结论：✅ 无幽灵路由**（007 报告中提到的 13 个幽灵路由已全部修复）

逐一核对导航配置中的所有 href 与 `src/app/` 下的实际页面：

| 角色 | 导航项数 | 全部存在 | 备注 |
|------|---------|---------|------|
| admin | 19 | ✅ | 包括子菜单项 |
| teacher | 22 | ✅ | 包括子菜单项 |
| student | 12 | ✅ | 包括子菜单项 |
| parent | 5 | ✅ | 包括子菜单项 |

#### 存在但未纳入导航的页面

| 路由 | 说明 | 建议 |
|------|------|------|
| `/admin/attendance` | 管理员考勤页面 | 如需管理员查看全校考勤，应加入 admin 导航 |
| `/admin/files` | 管理员文件管理 | 应加入 admin 导航 |
| `/parent/children/[studentId]` | 子女详情页 | 通过仪表盘卡片跳转，可不加入导航 |
| `/settings/security` | 安全设置子页 | 通过 settings 页 Tab 切换，无需独立导航 |
| `/profile` | 个人主页 | 通过 header 头像菜单跳转，无需独立导航 |

#### 其他发现

- ✅ `app-sidebar.tsx` 第 36-43 行根据权限动态选择角色导航配置，符合 RBAC
- ⚠️ 第 39 行判断学生逻辑：`permissions.includes(Permissions.HOMEWORK_SUBMIT) && !permissions.includes(Permissions.EXAM_CREATE)` — 用权限反推角色，不够直观，建议改用 `hasRole("student")`

### 5.7 student 模块

#### 模块结构

| 文件 | 职责 |
|------|------|
| `components/student-courses-view.tsx` | 学生课程视图 |
| `components/student-schedule-filters.tsx` | 课表筛选器 |
| `components/student-schedule-view.tsx` | 学生课表视图 |

#### 评价

- ✅ **纯 UI 模块**：无 data-access / actions / types
- ✅ 数据由 `app/(dashboard)/student/learning/courses/page.tsx` 和 `app/(dashboard)/student/schedule/page.tsx` 通过 classes 模块的 data-access 获取
- ⚠️ 与 classes 模块的 `schedule-view.tsx` / `schedule-filters.tsx` 可能存在功能重叠，建议核查

---

## 六、跨模块依赖关系图

```
dashboard ──直查──> users, classes, textbooks, questions, exams, homework (11 张表)
parent ──调用──> classes, homework, grades (data-access)
diagnostic ──直查──> examSubmissions, submissionAnswers, questionsToKnowledgePoints, classes
notifications ──依赖──> messaging (偏好 + in-app 渠道)
messaging ──绕过──> notifications (直接写 messageNotifications)
proctoring ──直查──> exams, examSubmissions, users
settings ──调用──> messaging (通知偏好 Action)
```

---

## 七、修复优先级

### P0（严重，应立即修复）

| 序号 | 问题 | 模块 | 工作量 | 影响 | 状态 |
|------|------|------|--------|------|------|
| ~~1~~ | ~~messaging 绕过 notifications 直接写通知~~ | ~~messaging~~ | ~~小~~ | ~~用户通知偏好失效，多渠道通知无效~~ | ✅ 已修复（P0-5） |
| 2 | proctoring/exam-mode-config.tsx 未集成 | proctoring | 小 | 监考功能无法启用，组件为死代码 | ⚠️ 用户决定保留 |
| 3 | proctoring 事件上报双通道重复 | proctoring | 小 | 代码重复，维护成本 | ❌ 待修复 |
| ~~4~~ | ~~notifications 反向依赖 messaging~~ | ~~notifications~~ | ~~中~~ | ~~架构耦合，难以独立演进~~ | ✅ 已修复（P1-6） |

### P1（中等问题，下个迭代修复）

| 序号 | 问题 | 模块 | 工作量 | 状态 |
|------|------|------|--------|------|
| ~~5~~ | ~~dashboard 直查 11 张跨模块表~~ | ~~dashboard~~ | ~~大~~ | ✅ 已修复（P0-4） |
| 6 | diagnostic 跨模块直查 4 张表 | diagnostic | 中 | ❌ 待修复 |
| 7 | notifications 无 notification_logs 表 | notifications | 中 | ❌ 待修复 |
| 8 | sendClassNotificationAction 直查 classes 表 | notifications | 小 | ❌ 待修复 |
| 9 | elective 两个 data-access 文件代码重复 | elective | 小 | ❌ 待修复 |
| 10 | settings/notification-preferences-form 跨模块依赖 | settings | 小 | ❌ 待修复 |

### P2（轻微问题，机会修复）

| 序号 | 问题 | 模块 |
|------|------|------|
| 11 | diagnostic/data-access-reports.ts 有未使用代码 | diagnostic |
| 12 | diagnostic 班级报告 studentId 字段复用 | diagnostic |
| 13 | elective runLottery 使用 Math.random | elective |
| 14 | teacher dashboard 直查 users 表 | dashboard |
| 15 | files 模块 try-catch 吞错误 | files |
| 16 | layout 用权限反推角色 | layout |

---

## 八、总结

### 新增模块质量

- **elective**：✅ 质量较好，拆分合理但有代码重复
- **proctoring**：⚠️ 有死代码（exam-mode-config 未集成，用户决定保留）和重复实现（双通道上报）
- **diagnostic**：✅ 与 grades 无重叠，但跨模块耦合较重
- **notifications**：✅ 渠道抽象优秀，与 messaging 的双向依赖已修复（P0-5 + P1-6）

### 重点问题回答

1. **elective 拆分为 3 个 data-access 文件是否合理？**
   合理，但需消除 `data-access.ts` 与 `data-access-selections.ts` 之间的代码重复

2. **proctoring 模块职责是否清晰？**
   基本清晰，但 `exam-mode-config.tsx` 应属于 exams 模块或集成到考试表单（用户决定保留）

3. **diagnostic 与 grades 是否有职责重叠？**
   无重叠。grades 管分数记录，diagnostic 管知识点掌握度，数据来源和维度均不同

4. **notifications 与 messaging 边界是否清晰？**
   ✅ 已修复。双向依赖通过动态 import 打破，messaging 改用 notifications/dispatcher 发送通知，依赖方向统一为 messaging → notifications

5. **dashboard 是否直查其他模块的表？**
   ✅ 已修复。`getAdminDashboardData` 改为并行调用各模块的 `get[Module]DashboardStats()` 函数，不再直接查询任何业务表

6. **settings 是否混入太多职责？**
   混合了 5 类职责，但作为"设置"聚合点尚可接受。AI Provider 管理可考虑独立

7. **navigation.ts 是否有幽灵路由？**
   无。007 报告中的 13 个幽灵路由已全部修复
