# 架构解耦路线图

> 创建日期：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              # 班级核心 CRUD（656 行）
├── 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              # 作业 CRUD（598 行）
├── 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 84d6636），4 个模块的 actions 层 DB 操作全部下沉到 data-access：
- exams：新增 7 个 data-access 函数，actions.ts 832→691 行，data-access.ts 339→471 行
- homework：新建 data-access-write.ts（285 行，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 范围）：users（updateUserProfileAction）、scheduling（applyAutoScheduleAction/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-access）（21 行）
```

**完成状态**：2026-06-17 已完成。拆分为 3 个文件，用户创建逻辑下沉到 user-service.ts，班级注册逻辑下沉到 class-registration.ts（调用 classes/data-access），import-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 + revalidate）（P1-2 已修复 exams/homework/questions/announcements，users/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 对项目可维护性的提升

- **新人上手更快**：清晰的分层和模块边界
- **技术债可控**：已知问题有明确修复计划
- **演进路径清晰**：解耦后可独立演进各模块
