476 lines
16 KiB
Markdown
476 lines
16 KiB
Markdown
# 架构解耦路线图
|
||
|
||
> 创建日期: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(目标 ≤800 行)
|
||
├── ranking-service.ts # 排名计算业务逻辑
|
||
└── stats-service.ts # 作业统计业务逻辑
|
||
```
|
||
|
||
**迁移步骤**:
|
||
1. 抽取 `calculateClassRankings` 到 `ranking-service.ts`
|
||
2. 抽取统计相关函数到 `stats-service.ts`
|
||
3. data-access 只保留 CRUD + 简单查询
|
||
|
||
---
|
||
|
||
#### 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`
|
||
|
||
---
|
||
|
||
#### 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 查询
|
||
|
||
---
|
||
|
||
#### P0-5 `messaging` 绕过 `notifications` 直接写通知
|
||
|
||
**问题**:
|
||
`messaging/actions.ts` 第 66-72 行直接调用 `createNotification`,导致:
|
||
- 用户通知偏好失效
|
||
- 多渠道通知(SMS/微信/邮件)无效
|
||
- notifications 模块形同虚设
|
||
|
||
**影响**:
|
||
- P2 实现的通知渠道系统完全失效
|
||
- 用户无法通过偏好设置控制通知渠道
|
||
|
||
**解耦方案**:
|
||
```typescript
|
||
// src/modules/messaging/actions.ts
|
||
import { dispatchNotification } from "@/modules/notifications/dispatcher";
|
||
|
||
// 替换 createNotification 调用
|
||
await dispatchNotification({
|
||
userId: recipientId,
|
||
type: "message",
|
||
title: "...",
|
||
content: "...",
|
||
channels: ["in-app"], // 由 dispatcher 根据用户偏好决定实际渠道
|
||
});
|
||
```
|
||
|
||
**迁移步骤**:
|
||
1. messaging/actions.ts 替换 `createNotification` 为 `dispatchNotification`
|
||
2. notifications/dispatcher.ts 确保支持 message 类型
|
||
3. 测试用户通知偏好是否生效
|
||
|
||
---
|
||
|
||
#### 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` 添加 `upsertClassSchedule()` 函数
|
||
2. `classes/data-access.ts` 移除 classSchedule 写入,改为调用 scheduling
|
||
3. `scheduling/actions.ts` 移除直接 transaction,改为调用 data-access
|
||
4. 添加冲突检测逻辑
|
||
|
||
---
|
||
|
||
### 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 831→766 行,data-access.ts 374→524 行
|
||
- homework:新建 data-access-write.ts(285 行,10 个写函数),actions.ts 387→239 行
|
||
- questions:新增 4 个 data-access 函数,actions.ts 294→177 行,data-access.ts 138→299 行
|
||
- announcements:新增 5 个 data-access 函数,actions.ts 242→231 行,data-access.ts 120→186 行
|
||
|
||
**剩余未修复模块**(不在本次 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 配置(目标 ≤150 行)
|
||
└── shared/lib/
|
||
├── password-security-service.ts # 密码安全 DB 操作
|
||
├── role-utils.ts # 角色规范化
|
||
├── bcrypt-utils.ts # bcrypt 哈希规范化
|
||
└── http-utils.ts # IP 解析(与 logger 共用)
|
||
```
|
||
|
||
---
|
||
|
||
#### P1-4 `users/import-export.ts` 四重职责
|
||
|
||
**问题**:
|
||
导入解析 + 导出 + 用户创建(含密码哈希) + 班级注册(跨模块写 classEnrollments)
|
||
|
||
**解耦方案**:
|
||
```
|
||
src/modules/users/
|
||
├── import-export.ts # 仅文件解析与生成
|
||
├── user-service.ts # 用户创建(含密码哈希)
|
||
└── class-registration.ts # 班级注册(调用 classes/data-access)
|
||
```
|
||
|
||
---
|
||
|
||
#### 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 导入
|
||
|
||
---
|
||
|
||
### 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 行(硬上限)
|
||
- [x] React 组件 ≤ 500 行(复杂表单/表格 ≤ 800 行)
|
||
- [x] Server Actions / Data Access ≤ 800 行(P1-2 后 exams/actions.ts 766 行、homework/actions.ts 239 行、questions/actions.ts 177 行、announcements/actions.ts 231 行均达标)
|
||
|
||
### 4.2 模块封装
|
||
|
||
- [ ] 无跨模块直接 DB 查询(通过 ESLint 规则验证)
|
||
- [ ] 无循环依赖(通过 `madge` 工具验证)
|
||
- [ ] shared/ 不 import @/auth 或 modules/
|
||
|
||
### 4.3 职责单一
|
||
|
||
- [x] actions.ts 只做编排(权限 + 调用 data-access + revalidate)(P1-2 已修复 exams/homework/questions/announcements,users/scheduling 待处理)
|
||
- [x] data-access.ts 只做数据存取(无业务计算)(P2-2 后 ai/ 目录职责单一)
|
||
- [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 对项目可维护性的提升
|
||
|
||
- **新人上手更快**:清晰的分层和模块边界
|
||
- **技术债可控**:已知问题有明确修复计划
|
||
- **演进路径清晰**:解耦后可独立演进各模块
|