# 新增模块与其他模块架构审查报告 > 审查范围: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 个幽灵路由已全部修复