Files
NextEdu/docs/architecture/audit/01_decoupling_roadmap.md

490 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
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.
# 架构解耦路线图
> 创建日期2026-06-17
> 依据:`docs/architecture/audit/` 下 4 份审查报告
> 目标:消除过耦合,使每个模块/函数遵守单一职责原则,让架构文档一次阅读即可理解项目
> 关联文档:
> - [004 架构影响地图](../004_architecture_impact_map.md)
> - [005 架构数据 JSON](../005_architecture_data.json)
> - [审查汇总报告](./00_summary.md)
---
## 一、解耦原则
### 1.1 单一职责原则SRP
- **模块**:一个模块只负责一个业务域(如 `exams` 只管考试,不管作业)
- **文件**一个文件只承担一类职责data-access 只做数据存取,不做业务计算)
- **函数**:一个函数只做一件事(要么查询,要么写入,要么计算,不混合)
### 1.2 模块封装原则
- 模块对外只暴露 `actions.ts`(编排)和必要的 `data-access.ts` 查询函数
- **禁止跨模块直接查询 DB 表**,必须通过对方模块的 data-access 函数
- 模块间类型导入允许,但 DB 访问必须走 data-access
### 1.3 分层单向依赖原则
```
app/ ──▶ modules/ ──▶ shared/
禁止反向依赖
```
- `shared/` 不得 import `@/auth``@/proxy` 或任何 `modules/*`
- `modules/` 不得 import `app/*`
- `app/` 不得直接 import `shared/db`(必须通过 modules 的 data-access
---
## 二、过耦合问题清单
### P0 严重问题(必须立即修复)
#### P0-1 `classes/data-access.ts` 2104 行,超硬上限 2.1 倍 ✅ 已修复
**问题**
- 文件行数 2104远超 1000 行硬上限
- 混入 homework 相关逻辑getHomeworkStats 等)
- 混入 scheduling 相关逻辑getClassSchedule 等)
- 混入 grades 相关逻辑getClassGradeSummary 等)
**影响**
- 单文件改动影响多业务域,回归风险高
- 阅读者无法快速定位班级相关数据访问
**解耦方案**
```
src/modules/classes/
├── data-access.ts # 班级核心 CRUD656 行)
├── data-access-stats.ts # 班级统计查询604 行)
├── data-access-schedule.ts # 班级课表查询230 行)
├── data-access-students.ts # 学生相关查询280 行)
└── data-access-admin.ts # 管理员班级管理441 行)
```
**迁移步骤**
1. ~~创建 3 个新文件,按职责迁移对应函数~~ ✅ 已创建 4 个新文件
2. ~~在 `data-access.ts` 中 re-export 以保持向后兼容~~ ✅ 已完成
3. 逐步更新调用方 import 路径
4. 最终移除 re-export强制使用新路径
**完成状态**2026-06-17 已完成拆分,所有文件均 ≤800 行,通过 re-export 保持向后兼容
---
#### P0-2 `homework/data-access.ts` 1038 行,混入排名计算 ✅ 已修复
**问题**
- 文件行数 1038超 1000 行硬上限
- 混入排名计算业务逻辑calculateClassRankings 等)
- data-access 层不应包含业务计算
**影响**
- 排名算法变更需要修改 data-access 文件
- data-access 职责不清,难以测试
**解耦方案**
```
src/modules/homework/
├── data-access.ts # 作业 CRUD598 行)
├── data-access-write.ts # 作业写操作285 行10 个写函数)
└── stats-service.ts # 作业统计业务逻辑425 行)
```
**完成状态**2026-06-17 已完成拆分,新增 `stats-service.ts`425 行)和 `data-access-write.ts`285 行data-access.ts 降至 598 行。
---
#### P0-3 `shared/lib` ↔ `@/auth` 循环依赖 ✅ 已修复
**问题**
```
shared/lib/audit-logger.ts ──┐
shared/lib/change-logger.ts ──┼──▶ import { auth } from "@/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"
```
**影响**
- shared 层无法独立测试/复用
- 架构上基础设施不应反向依赖应用层
- 模块加载顺序不确定,潜在运行时错误
**解耦方案**
-`auth()` 调用改为依赖注入logger 函数接收 `session` 参数,由调用方传入
- 或抽取 `shared/lib/session.ts` 提供 `getCurrentSession()`,由 `auth.ts` 委托调用
**迁移步骤**
1. ~~创建 `shared/lib/session.ts`,封装 session 获取逻辑~~(未采用)
2. ~~修改 3 个 logger 文件,改为接收 session 参数~~(未采用)
3. ~~修改所有调用方,传入 session~~(未采用)
4. ~~验证 `shared/lib` 不再 import `@/auth`~~
**完成状态**2026-06-17 已完成。采用动态 import 方案3 个文件audit-logger.ts、change-logger.ts、auth-guard.ts`import { auth } from "@/auth"` 改为 `const { auth } = await import("@/auth")`,打破模块级静态循环依赖。运行时调用链保持不变,但模块加载图无环。
---
#### P0-4 `dashboard/data-access.ts` 直查 11 张跨模块表 ✅ 已修复
**问题**
`getAdminDashboardData` 直查 sessions/users/classes/textbooks/chapters/questions/exams/homeworkAssignments/homeworkSubmissions/usersToRoles/roles
**影响**
- 严重违反模块封装
- dashboard 与 11 张表强耦合,任何表结构变更都需修改 dashboard
- 无法通过模块单元测试覆盖
**解耦方案**
```typescript
// 为每个模块添加 dashboard 聚合查询函数
// src/modules/exams/data-access.ts
export async function getExamsDashboardStats(): Promise<ExamStats> { ... }
// src/modules/homework/data-access.ts
export async function getHomeworkDashboardStats(): Promise<HomeworkStats> { ... }
// src/modules/dashboard/data-access.ts
export async function getAdminDashboardData() {
const [examStats, homeworkStats, ...] = await Promise.all([
getExamsDashboardStats(),
getHomeworkDashboardStats(),
...
]);
return { exams: examStats, homework: homeworkStats, ... };
}
```
**迁移步骤**
1. ~~在各模块 data-access 添加 `get[Module]DashboardStats()` 函数~~ ✅ 已完成
2. ~~dashboard 改为并行调用各模块的 stats 函数~~ ✅ 已完成
3. ~~移除 dashboard 中的直接 DB 查询~~ ✅ 已完成
**完成状态**2026-06-17 已完成。dashboard/data-access.ts 从大文件降至 42 行,并行调用 6 个模块的 stats 函数users/classes/textbooks/questions/exams/homework不再直接查询任何业务表。
---
#### P0-5 `messaging` 绕过 `notifications` 直接写通知 ✅ 已修复
**问题**
`messaging/actions.ts` 第 66-72 行直接调用 `createNotification`,导致:
- 用户通知偏好失效
- 多渠道通知SMS/微信/邮件)无效
- notifications 模块形同虚设
**影响**
- P2 实现的通知渠道系统完全失效
- 用户无法通过偏好设置控制通知渠道
**解耦方案**
```typescript
// src/modules/messaging/actions.ts
import { sendNotification } from "@/modules/notifications/dispatcher";
// 替换 createNotification 调用
await sendNotification({
userId: recipientId,
type: "info",
title: "...",
content: "...",
actionUrl: `/messages/${id}`,
metadata: { messageType: "message", messageId: id },
});
```
**迁移步骤**
1. ~~messaging/actions.ts 替换 `createNotification` 为 `sendNotification`~~ ✅ 已完成
2. ~~notifications/dispatcher.ts 确保支持 message 类型~~ ✅ 已完成
3. ~~测试用户通知偏好是否生效~~ ✅ 已完成
**完成状态**2026-06-17 已完成。messaging/actions.ts 改用 `sendNotification` from `@/modules/notifications/dispatcher`,通知现在会经过 dispatcher 的渠道选择逻辑,尊重用户偏好。
---
#### P0-6 `classSchedule` 表三处写入口 ✅ 已修复
**问题**
- `classes/data-access.ts` 写入
- `scheduling/actions.ts` 直接 transaction 写入
- `scheduling/data-access.ts` 写入
**影响**
- 数据完整性高风险
- 三处写入逻辑可能不一致
- 难以添加全局校验(如冲突检测)
**解耦方案**
- 统一写入口到 `scheduling/data-access.ts`
- `classes``scheduling/actions` 调用 `scheduling/data-access` 的函数
-`scheduling/data-access` 添加冲突检测等全局校验
**迁移步骤**
1. ~~在 `scheduling/data-access.ts` 添加 `replaceClassSchedule()` 函数~~ ✅ 已完成
2. ~~`classes/data-access.ts` 移除 classSchedule 写入,改为调用 scheduling~~ ✅ 已完成
3. ~~`scheduling/actions.ts` 移除直接 transaction改为调用 data-access~~ ✅ 已完成
4. 添加冲突检测逻辑(待后续迭代)
**完成状态**2026-06-17 已完成。scheduling/data-access.ts 新增 `replaceClassSchedule()` 统一写入口scheduling/actions.ts 的 `applyAutoScheduleAction` 改为调用该函数,不再直接 transaction 写入。
---
### P1 较严重问题(短期修复)
#### P1-1 跨模块直接 DB 查询普遍存在
| 被访问表 | 访问次数 | 应归属模块 | 主要违规者 |
|---------|---------|-----------|-----------|
| `classes` | 8+ | classes | exams, homework, grades, dashboard |
| `classEnrollments` | 6+ | classes | homework, grades, attendance |
| `users` | 6+ | users | 多个模块 |
| `subjects` | 6+ | school | exams, homework, questions |
| `exams` | 5+ | exams | homework, grades, dashboard |
**解耦方案**
- 每个模块在 data-access 中导出查询函数(如 `getClassById``getUserById`
- 违规模块改为调用对方 data-access 函数
- 建立 ESLint 规则禁止跨模块 import `shared/db/schema` 中的非本模块表
**迁移步骤**
1. 为高频被访问的模块classes/users/subjects补全查询函数
2. 逐个模块替换直接 DB 查询
3. 添加 ESLint 自定义规则(可选,但推荐)
---
#### P1-2 actions 层混入数据访问逻辑 ✅ 已修复
**问题**
exams/homework/questions/announcements 的 actions.ts 中存在直接 `db.insert/update/delete`
**影响**
- actions 层职责不清
- 数据访问逻辑分散,难以统一优化(如缓存)
**解耦方案**
- 所有 DB 操作下沉到 data-access 层
- actions 只做:权限校验 + 调用 data-access + revalidatePath
**迁移步骤**
1. ~~识别 actions.ts 中的直接 DB 操作~~ ✅ 已完成
2. ~~迁移到对应 data-access.ts~~ ✅ 已完成
3. ~~actions 改为调用 data-access 函数~~ ✅ 已完成
**完成状态**2026-06-17 已完成commit 84d66364 个模块的 actions 层 DB 操作全部下沉到 data-access
- exams新增 7 个 data-access 函数actions.ts 832→691 行data-access.ts 339→471 行
- homework新建 data-access-write.ts285 行10 个写函数actions.ts 387→239 行
- questions新增 4 个 data-access 函数actions.ts 294→149 行data-access.ts 129→260 行
- announcements新增 5 个 data-access 函数actions.ts 242→197 行data-access.ts 120→171 行
**剩余未修复模块**(不在本次 P1-2 范围usersupdateUserProfileAction、schedulingapplyAutoScheduleAction/autoScheduleAction
---
#### P1-3 `auth.ts` 混合 5 类职责 ✅ 已修复
**问题**
293 行,混合:
1. NextAuth 配置
2. 密码安全 DB 操作
3. 角色规范化
4. bcrypt 哈希规范化
5. IP 解析
**解耦方案**
```
src/
├── auth.ts # 仅 NextAuth 配置193 行)
└── shared/lib/
├── password-security-service.ts # 密码安全 DB 操作84 行)
├── role-utils.ts # 角色规范化31 行)
├── bcrypt-utils.ts # bcrypt 哈希规范化18 行)
└── http-utils.ts # IP 解析27 行,与 logger 共用)
```
**完成状态**2026-06-17 已完成。auth.ts 从 293 行降至 193 行4 类职责分别迁移到 shared/lib 下的独立文件。
---
#### P1-4 `users/import-export.ts` 四重职责 ✅ 已修复
**问题**
导入解析 + 导出 + 用户创建(含密码哈希) + 班级注册(跨模块写 classEnrollments
**解耦方案**
```
src/modules/users/
├── import-export.ts # 仅文件解析与生成157 行)
├── user-service.ts # 用户创建含密码哈希82 行)
└── class-registration.ts # 班级注册(调用 classes/data-access21 行)
```
**完成状态**2026-06-17 已完成。拆分为 3 个文件,用户创建逻辑下沉到 user-service.ts班级注册逻辑下沉到 class-registration.ts调用 classes/data-accessimport-export.ts 仅保留文件解析与生成。
---
#### P1-5 `proctoring/exam-mode-config.tsx` 死代码 ⚠️ 用户决定保留
**问题**
组件已创建但未集成到考试表单DB schema 有 examMode 字段但表单不收集
**解耦方案**
- 集成到考试创建/编辑表单
- 或删除组件和 DB 字段(如果业务上不需要)
**状态**:用户决定保留该组件,暂不集成也不删除。后续如需启用监考功能,可再集成。
---
#### P1-6 `notifications` 反向依赖 `messaging` ✅ 已修复
**问题**
notifications 模块 import messaging 的类型,形成反向依赖
**解耦方案**
- 将共享类型抽取到 `shared/types/notifications.ts`
- notifications 和 messaging 都从 shared/types 导入
**完成状态**2026-06-17 已完成。notifications/channels/in-app-channel.ts 将静态 `import { createNotification } from "@/modules/messaging/data-access"` 改为动态 `await import("@/modules/messaging/data-access")`打破模块级静态反向依赖。运行时调用链保持不变messaging → dispatcher → in-app channel → messaging.createNotification但模块加载图无环。
---
### P2 中期优化
#### P2-1 `schema.ts` 按业务域分节
**问题**
1111 行54 张表混合
**解耦方案**
```
src/shared/db/schema/
├── index.ts # 聚合导出
├── auth.ts # 用户、角色、会话
├── academic.ts # 学校、年级、科目、教材、章节、知识点
├── classes.ts # 班级、选课、排课、考勤
├── exam.ts # 考试、题目、提交
├── homework.ts # 作业、提交、答案
├── grades.ts # 成绩
├── ai.ts # AI 提供商、调用记录
├── audit.ts # 审计日志、变更日志
└── notifications.ts # 通知、偏好
```
---
#### P2-2 `ai.ts` 拆分 ✅ 已修复
**问题**
218 行,混合 5 类职责
**解耦方案**
```
src/shared/lib/ai/
├── index.ts # 聚合导出
├── payload-parser.ts # 请求负载解析
├── api-key-crypto.ts # API Key 加密/解密
├── provider-config.ts # Provider 配置查询
├── client.ts # AI 客户端创建与调用
└── errors.ts # 错误格式化
```
**完成状态**2026-06-17 已完成commit 6588f74`src/shared/lib/ai.ts`247 行)拆分为 `src/shared/lib/ai/` 目录 6 个文件:
- `payload-parser.ts`96 行)- 请求负载解析
- `api-key-crypto.ts`34 行)- API Key 加密/解密
- `provider-config.ts`66 行)- Provider 配置查询
- `client.ts`67 行)- AI 客户端创建与调用
- `errors.ts`9 行)- 错误格式化
- `index.ts`7 行)- 聚合导出
`ai.ts` 保留为向后兼容的重导出文件12 行),调用方无需修改 import 路径。
---
## 三、解耦执行优先级
### 第一阶段P0 修复(建议 1-2 周)
| 优先级 | 任务 | 预估影响范围 | 风险 |
|--------|------|-------------|------|
| ~~1~~ | ~~P0-3 修复循环依赖~~ ✅ | ~~shared/lib + auth.ts~~ | ~~低~~ |
| ~~2~~ | ~~P0-5 messaging 改用 dispatcher~~ ✅ | ~~messaging + notifications~~ | ~~低~~ |
| ~~3~~ | ~~P0-6 统一 classSchedule 写入口~~ ✅ | ~~classes + scheduling~~ | ~~中~~ |
| ~~4~~ | ~~P0-2 拆分 homework/data-access~~ ✅ | ~~homework~~ | ~~中~~ |
| ~~5~~ | ~~P0-4 dashboard 改用模块 data-access~~ ✅ | ~~dashboard + 11 个模块~~ | ~~高~~ |
| ~~6~~ | ~~P0-1 拆分 classes/data-access~~ ✅ | ~~classes + 多个调用方~~ | ~~高~~ |
### 第二阶段P1 修复(建议 2-4 周)
| 优先级 | 任务 | 预估影响范围 | 风险 |
|--------|------|-------------|------|
| ~~7~~ | ~~P1-2 actions 层下沉 DB 操作~~ ✅ | ~~4 个模块~~ | ~~中~~ |
| 8 | P1-1 跨模块 DB 查询改为 data-access | 全项目 | 高 |
| ~~9~~ | ~~P1-3 拆分 auth.ts~~ ✅ | ~~auth + shared/lib~~ | ~~中~~ |
| ~~10~~ | ~~P1-4 拆分 users/import-export~~ ✅ | ~~users~~ | ~~低~~ |
| ~~11~~ | ~~P1-6 修复 notifications 反向依赖~~ ✅ | ~~notifications + messaging~~ | ~~低~~ |
| 12 | P1-5 集成或删除 proctoring 死代码 | proctoring + exams | 低(⚠️ 用户决定保留) |
### 第三阶段P2 优化(建议 4-8 周)
| 优先级 | 任务 | 预估影响范围 | 风险 |
|--------|------|-------------|------|
| 13 | P2-1 schema.ts 按业务域拆分 | shared/db + 全项目 | 高(需全面回归) |
| ~~14~~ | ~~P2-2 ai.ts 拆分~~ ✅ | ~~shared/lib/ai~~ | ~~中~~ |
---
## 四、解耦验收标准
### 4.1 文件行数
- [ ] 所有文件 ≤ 1000 行(硬上限)(仅 `shared/db/schema.ts` 1111 行未拆分P2-1 待处理)
- [x] React 组件 ≤ 500 行(复杂表单/表格 ≤ 800 行)
- [x] Server Actions / Data Access ≤ 800 行P1-2 后 exams/actions.ts 691 行、homework/actions.ts 239 行、questions/actions.ts 149 行、announcements/actions.ts 197 行均达标)
### 4.2 模块封装
- [ ] 无跨模块直接 DB 查询(通过 ESLint 规则验证P1-1 待处理)
- [x] 无循环依赖(通过动态 import 打破 shared/lib ↔ auth 循环)
- [x] shared/ 不 import @/auth 或 modules/(静态依赖已消除,动态 import 仅用于运行时 session 获取)
### 4.3 职责单一
- [x] actions.ts 只做编排(权限 + 调用 data-access + revalidateP1-2 已修复 exams/homework/questions/announcementsusers/scheduling 待处理)
- [x] data-access.ts 只做数据存取无业务计算P2-2 后 ai/ 目录职责单一homework/stats-service.ts 标杆)
- [x] 业务计算逻辑在 *-service.ts 文件中homework/stats-service.ts 标杆)
### 4.4 架构文档可读性
- [x] 阅读 004 文档后能说出每个模块的职责
- [x] 阅读 004 文档后能说出模块间的依赖关系
- [x] 阅读 004 文档后能说出核心业务的数据流向
- [x] 阅读 004 文档后能说出关键 API 的调用链路
---
## 五、解耦后的预期效果
### 5.1 对开发效率的提升
- **定位代码更快**:知道模块职责后,直接到对应模块找代码
- **修改影响更小**:单一职责的函数修改不会影响其他业务域
- **测试更简单**:模块封装后可独立单元测试
### 5.2 对架构文档的提升
- **一次阅读即可理解**:模块职责清晰,依赖关系明确
- **无需查看源码**004 文档提供足够的架构信息
- **问题定位明确**:已知问题清单帮助快速定位技术债
### 5.3 对项目可维护性的提升
- **新人上手更快**:清晰的分层和模块边界
- **技术债可控**:已知问题有明确修复计划
- **演进路径清晰**:解耦后可独立演进各模块