docs: 同步架构文档 004/005/007/audit 反映 P1-2/P2-2 解耦修复

This commit is contained in:
SpecialX
2026-06-18 02:55:17 +08:00
parent 6588f7484f
commit 0423b2b984
5 changed files with 368 additions and 100 deletions

View File

@@ -215,18 +215,20 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
│ student/learning/assignments/[assignmentId]/page.tsx │ │ student/learning/assignments/[assignmentId]/page.tsx │
│ └─▶ homework/actions.startHomeworkSubmissionAction │ │ └─▶ homework/actions.startHomeworkSubmissionAction │
│ ├─▶ requirePermission(HOMEWORK_SUBMIT) │ │ ├─▶ requirePermission(HOMEWORK_SUBMIT) │
│ ├─▶ db.query.homeworkAssignments [⚠️ 直接 DB] │ ├─▶ data-access-write.startHomeworkSubmission ✅ P1-2 已修复
─▶ db.insert(homeworkSubmissions) [⚠️ 直接 DB] │ └─▶ db.insert(homeworkSubmissions) [shared/db]
│ └─▶ 返回 submissionId │ │ └─▶ 返回 submissionId │
│ │ │ │
│ └─▶ homework/actions.saveHomeworkAnswerAction │ │ └─▶ homework/actions.saveHomeworkAnswerAction │
│ └─▶ db.transaction(insert homeworkAnswers) [⚠️ 直接 DB] │ └─▶ data-access-write.saveHomeworkAnswer ✅ P1-2 已修复
│ └─▶ db.transaction(insert homeworkAnswers) [shared/db] │
│ │ │ │
│ └─▶ homework/actions.submitHomeworkAction │ │ └─▶ homework/actions.submitHomeworkAction │
│ └─▶ db.update(homeworkSubmissions.status="submitted") │ └─▶ data-access-write.submitHomework ✅ P1-2 已修复
│ └─▶ db.update(homeworkSubmissions.status="submitted") │
│ │ │ │
│ 数据写入homeworkSubmissions 表 + homeworkAnswers 表 │ │ 数据写入homeworkSubmissions 表 + homeworkAnswers 表 │
⚠️ 违规actions 层直接 DB 操作,下沉到 data-access ✅ P1-2 已修复actions 层不再直接 DB 操作,下沉到 data-access-write
└─────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────┘
@@ -236,7 +238,8 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
│ teacher/homework/submissions/[submissionId]/page.tsx │ │ teacher/homework/submissions/[submissionId]/page.tsx │
│ └─▶ homework/actions.gradeHomeworkSubmissionAction │ │ └─▶ homework/actions.gradeHomeworkSubmissionAction │
│ ├─▶ requirePermission(HOMEWORK_GRADE) │ │ ├─▶ requirePermission(HOMEWORK_GRADE) │
│ └─▶ db.update(homeworkAnswers) 循环 [⚠️ 直接 DB] │ └─▶ data-access-write.gradeHomeworkSubmission ✅ P1-2 已修复
│ └─▶ db.update(homeworkAnswers) 循环 [shared/db] │
│ │ │ │
│ 数据更新homeworkAnswers.isCorrect / score / feedback │ │ 数据更新homeworkAnswers.isCorrect / score / feedback │
└─────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────┘
@@ -308,11 +311,10 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
├─▶ requirePermission(HOMEWORK_SUBMIT) ├─▶ requirePermission(HOMEWORK_SUBMIT)
├─▶ db.query.homeworkSubmissions.findFirst ❌ 应在 data-access ├─▶ data-access-write.submitHomework ✅ P1-2 已修复
│ (校验 submission 归属) │ (校验 submission 归属 + 更新状态
└─▶ db.update(homeworkSubmissions)
├─▶ db.update(homeworkSubmissions) ❌ 应在 data-access │ SET status = "submitted", submittedAt = now()
│ SET status = "submitted", submittedAt = now()
└─▶ 返回 ActionState<{ submissionId }> └─▶ 返回 ActionState<{ submissionId }>
``` ```
@@ -370,7 +372,7 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
- ❌ P0`shared/lib/*``@/auth` 循环依赖 - ❌ P0`shared/lib/*``@/auth` 循环依赖
- ⚠️ P1`schema.ts` 1111 行54 张表混合,超 1000 硬上限) - ⚠️ P1`schema.ts` 1111 行54 张表混合,超 1000 硬上限)
- ✅ P1~~`auth.ts` 293 行混合 5 类职责~~ 已拆分4 个辅助函数组迁移至 `shared/lib/{role-utils,bcrypt-utils,http-utils,password-security-service}`auth.ts 仅保留 NextAuth 配置) - ✅ P1~~`auth.ts` 293 行混合 5 类职责~~ 已拆分4 个辅助函数组迁移至 `shared/lib/{role-utils,bcrypt-utils,http-utils,password-security-service}`auth.ts 仅保留 NextAuth 配置)
- ⚠️ P2`ai.ts` 218 行混合 5 类职责 - P2-2 已修复:~~`ai.ts` 218 行混合 5 类职责~~ 已拆分为 `ai/` 目录payload-parser.ts/api-key-crypto.ts/provider-config.ts/client.ts/errors.ts/index.ts`ai.ts` 保留为向后兼容的重导出文件12 行)
- ⚠️ P2`onboarding-gate.tsx` 业务逻辑泄漏到 shared - ⚠️ P2`onboarding-gate.tsx` 业务逻辑泄漏到 shared
**文件清单** **文件清单**
@@ -381,7 +383,13 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
| `db/index.ts` | - | Drizzle 客户端 | | `db/index.ts` | - | Drizzle 客户端 |
| `lib/auth-guard.ts` | - | 认证上下文 + 权限校验 + DataScope | | `lib/auth-guard.ts` | - | 认证上下文 + 权限校验 + DataScope |
| `lib/permissions.ts` | - | 角色-权限映射 | | `lib/permissions.ts` | - | 角色-权限映射 |
| `lib/ai.ts` | 218 | AI 调用 + Provider 配置 + 加密 | | `lib/ai.ts` | 12 | 向后兼容重导出P2-2 已拆分到 `ai/` 目录) |
| `lib/ai/payload-parser.ts` | 96 | 请求负载解析 |
| `lib/ai/api-key-crypto.ts` | 34 | API Key 加密/解密 |
| `lib/ai/provider-config.ts` | 66 | Provider 配置查询 |
| `lib/ai/client.ts` | 67 | AI 客户端创建与调用 |
| `lib/ai/errors.ts` | 9 | 错误格式化 |
| `lib/ai/index.ts` | 7 | 聚合导出 |
| `lib/audit-logger.ts` | - | 操作日志 | | `lib/audit-logger.ts` | - | 操作日志 |
| `lib/change-logger.ts` | - | 数据变更日志 | | `lib/change-logger.ts` | - | 数据变更日志 |
| `lib/login-logger.ts` | - | 登录日志 | | `lib/login-logger.ts` | - | 登录日志 |
@@ -406,8 +414,8 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
**职责**:考试全生命周期管理(创建/编辑/预览/发布/删除/复制)+ AI 辅助出题。 **职责**:考试全生命周期管理(创建/编辑/预览/发布/删除/复制)+ AI 辅助出题。
**导出函数** **导出函数**
- Actions`createExamAction` / `createAiExamAction` / `previewAiExamAction` / `regenerateAiQuestionAction` / `updateExamAction` / `deleteExamAction` / `duplicateExamAction` / `getExamPreviewAction` / `getSubjectsAction` / `getGradesAction` - Actions`createExamAction` / `createAiExamAction` / `previewAiExamAction` / `regenerateAiQuestionAction` / `updateExamAction` / `deleteExamAction` / `duplicateExamAction` / `getExamPreviewAction` / `getSubjectsAction` / `getGradesAction`(✅ P1-2 已修复actions 层不再直接访问 DB全部下沉到 data-access
- Data-access`getExams` / `getExamById` / `persistExamDraft` / `persistAiGeneratedExamDraft` / `buildExamDescription` / `resolveSubjectGradeNames` - Data-access`getExams` / `getExamById` / `persistExamDraft` / `persistAiGeneratedExamDraft` / `buildExamDescription` / `resolveSubjectGradeNames` / `getExamCreatorId` / `updateExamWithQuestions` / `deleteExamById` / `duplicateExam` / `getExamPreview` / `getExamSubjects` / `getExamGrades`(后 7 个为 P1-2 新增)
- AI Pipeline`generateAiCreateDraftFromSource` / `generateAiPreviewData` / `regenerateAiQuestionByInstruction` - AI Pipeline`generateAiCreateDraftFromSource` / `generateAiPreviewData` / `regenerateAiQuestionByInstruction`
**依赖关系** **依赖关系**
@@ -418,15 +426,15 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
- ❌ P0`persistAiGeneratedExamDraft` 直接 insert 到 `questions` - ❌ P0`persistAiGeneratedExamDraft` 直接 insert 到 `questions`
- ❌ P0`getExams`/`getExamById` 直查 `classes` - ❌ P0`getExams`/`getExamById` 直查 `classes`
- ❌ P1`getSubjectsAction`/`getGradesAction` 直查 `subjects`/`grades` 表(应属 school 模块) - ❌ P1`getSubjectsAction`/`getGradesAction` 直查 `subjects`/`grades` 表(应属 school 模块)
- P1`actions.ts` 832 行(超 800 建议),多处直接 DB 操作 - P1-2 已修复:~~`actions.ts` 832 行(超 800 建议),多处直接 DB 操作~~ DB 操作已下沉到 data-accessactions.ts 现 766 行
- ⚠️ P1`ai-pipeline.ts` 912 行(超 800 建议),混合 4 类职责 - ⚠️ P1`ai-pipeline.ts` 912 行(超 800 建议),混合 4 类职责
**文件清单** **文件清单**
| 文件 | 行数 | 职责 | | 文件 | 行数 | 职责 |
|------|------|------| |------|------|------|
| `actions.ts` | 832 | 10 个 Server Action超限 | | `actions.ts` | 766 | 10 个 Server ActionP1-2 已修复,无直接 DB 操作 |
| `ai-pipeline.ts` | 912 | AI 出题管线(超限) | | `ai-pipeline.ts` | 912 | AI 出题管线(超限) |
| `data-access.ts` | 339 | 考试 CRUD | | `data-access.ts` | 524 | 考试 CRUD(含 P1-2 新增 7 个写/查询函数) |
| `types.ts` | 31 | 类型定义 | | `types.ts` | 31 | 类型定义 |
| `hooks/use-exam-preview.ts` | 295 | 预览 Hook | | `hooks/use-exam-preview.ts` | 295 | 预览 Hook |
| `components/*` | 18 文件 | 考试表单/组卷/预览组件 | | `components/*` | 18 文件 | 考试表单/组卷/预览组件 |
@@ -438,8 +446,9 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
**职责**:作业全生命周期(创建/发布/作答/批改/分析)。 **职责**:作业全生命周期(创建/发布/作答/批改/分析)。
**导出函数** **导出函数**
- Actions`createHomeworkAssignmentAction` / `startHomeworkSubmissionAction` / `saveHomeworkAnswerAction` / `submitHomeworkAction` / `gradeHomeworkSubmissionAction` - Actions`createHomeworkAssignmentAction` / `startHomeworkSubmissionAction` / `saveHomeworkAnswerAction` / `submitHomeworkAction` / `gradeHomeworkSubmissionAction`(✅ P1-2 已修复actions 层不再直接访问 DB全部下沉到 data-access/data-access-write
- Data-access`getHomeworkAssignments` / `getHomeworkAssignmentById` / `getHomeworkSubmissions` / `getStudentHomeworkAssignments` / `getStudentHomeworkTakeData` / `getHomeworkAssignmentReviewList` / `getHomeworkSubmissionDetails` / `getDemoStudentUser` / `isRecord` / `toQuestionContent` / `getAssignmentMaxScoreById`(后三者供 stats-service 使用) - Data-access`getHomeworkAssignments` / `getHomeworkAssignmentById` / `getHomeworkSubmissions` / `getStudentHomeworkAssignments` / `getStudentHomeworkTakeData` / `getHomeworkAssignmentReviewList` / `getHomeworkSubmissionDetails` / `getDemoStudentUser` / `isRecord` / `toQuestionContent` / `getAssignmentMaxScoreById`(后三者供 stats-service 使用)
- Data-access-write10 个写操作函数P1-2 新增,从 actions 下沉)
- Stats-service`getTeacherGradeTrends` / `getHomeworkAssignmentAnalytics` / `getStudentDashboardGrades`(从 data-access.ts re-export 以保持向后兼容) - Stats-service`getTeacherGradeTrends` / `getHomeworkAssignmentAnalytics` / `getStudentDashboardGrades`(从 data-access.ts re-export 以保持向后兼容)
**依赖关系** **依赖关系**
@@ -451,14 +460,15 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
- ✅ P0 已解决:`getStudentDashboardGrades` 排名计算逻辑迁移至 `stats-service.ts` - ✅ P0 已解决:`getStudentDashboardGrades` 排名计算逻辑迁移至 `stats-service.ts`
- ✅ P0 已解决:`getHomeworkAssignmentAnalytics` 错误率统计逻辑迁移至 `stats-service.ts` - ✅ P0 已解决:`getHomeworkAssignmentAnalytics` 错误率统计逻辑迁移至 `stats-service.ts`
- ❌ P15 处直查 `exams` - ❌ P15 处直查 `exams`
- P1`actions.ts` 多处直接 DB 操作(`createHomeworkAssignmentAction` 157 行) - P1-2 已修复:~~`actions.ts` 多处直接 DB 操作(`createHomeworkAssignmentAction` 157 行)~~ DB 操作已下沉到 `data-access-write.ts`actions.ts 现 239 行
**文件清单** **文件清单**
| 文件 | 行数 | 职责 | | 文件 | 行数 | 职责 |
|------|------|------| |------|------|------|
| `data-access.ts` | 596 | 作业 CRUD + 学生视角 + 批改(含 re-export stats 函数) | | `data-access.ts` | 596 | 作业 CRUD + 学生视角 + 批改(含 re-export stats 函数) |
| `data-access-write.ts` | 285 | 作业写操作P1-2 新增10 个写函数从 actions 下沉) |
| `stats-service.ts` | 346 | 统计分析(教师趋势/作业分析/学生仪表盘成绩) | | `stats-service.ts` | 346 | 统计分析(教师趋势/作业分析/学生仪表盘成绩) |
| `actions.ts` | 387 | 5 个 Server Action | | `actions.ts` | 239 | 5 个 Server ActionP1-2 已修复,无直接 DB 操作) |
| `types.ts` | 186 | 类型定义 | | `types.ts` | 186 | 类型定义 |
| `schema.ts` | 29 | Zod 校验 | | `schema.ts` | 29 | Zod 校验 |
@@ -469,23 +479,23 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
**职责**:题库管理(题目 CRUD、知识点关联、题型支持 **职责**:题库管理(题目 CRUD、知识点关联、题型支持
**导出函数** **导出函数**
- Actions`getQuestionsAction` / `createQuestionAction` / `updateQuestionAction` / `deleteQuestionAction` / `getKnowledgePointOptionsAction` - Actions`getQuestionsAction` / `createQuestionAction` / `updateQuestionAction` / `deleteQuestionAction` / `getKnowledgePointOptionsAction`(✅ P1-2 已修复actions 层不再直接访问 DB全部下沉到 data-access
- Data-access`getQuestions` / `insertQuestionWithRelations`(错放 actions/ `deleteQuestionRecursive`(错放 actions - Data-access`getQuestions` / `createQuestionWithRelations` / `updateQuestionById` / `deleteQuestionByIdRecursive` / `getKnowledgePointOptions`(后 4 个为 P1-2 新增/迁移
**依赖关系** **依赖关系**
- 依赖:`shared/*``@/auth``textbooks`(❌ actions 直查 knowledgePoints/chapters/textbooks - 依赖:`shared/*``@/auth``textbooks`(❌ actions 直查 knowledgePoints/chapters/textbooks
- 被依赖:`exams`(通过类型导入,合理)、`textbooks`UI 组合,合理) - 被依赖:`exams`(通过类型导入,合理)、`textbooks`UI 组合,合理)
**已知问题** **已知问题**
- P1写操作函数错放在 `actions.ts``insertQuestionWithRelations` / `deleteQuestionRecursive` - P1-2 已修复:~~写操作函数错放在 `actions.ts``insertQuestionWithRelations` / `deleteQuestionRecursive`~~ 已下沉到 data-access`createQuestionWithRelations` / `updateQuestionById` / `deleteQuestionByIdRecursive` / `getKnowledgePointOptions`
- ❌ P1`getKnowledgePointOptionsAction` 直查 textbooks 模块表 - ❌ P1`getKnowledgePointOptionsAction` 直查 textbooks 模块表
- ⚠️ P2`data-access.ts` 仅 129 行,写操作缺失 - P2 已解决:~~`data-access.ts` 仅 129 行,写操作缺失~~ P1-2 后 data-access.ts 扩充至 299 行
**文件清单** **文件清单**
| 文件 | 行数 | 职责 | | 文件 | 行数 | 职责 |
|------|------|------| |------|------|------|
| `actions.ts` | 294 | 5 个 Server Action含错放的 data-access 函数 | | `actions.ts` | 177 | 5 个 Server ActionP1-2 已修复,无直接 DB 操作 |
| `data-access.ts` | 129 | 仅读查询 | | `data-access.ts` | 299 | 题目 CRUD + 知识点选项(含 P1-2 新增 4 个写/查询函数) |
| `schema.ts` | 18 | Zod 校验 | | `schema.ts` | 18 | Zod 校验 |
| `types.ts` | 34 | 类型定义 | | `types.ts` | 34 | 类型定义 |
@@ -820,23 +830,23 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
**职责**:公告 CRUD + 发布/归档。 **职责**:公告 CRUD + 发布/归档。
**导出函数** **导出函数**
- Actions`getAnnouncementsAction` / `createAnnouncementAction` / `updateAnnouncementAction` / `deleteAnnouncementAction` / `publishAnnouncementAction` / `archiveAnnouncementAction` - Actions`getAnnouncementsAction` / `createAnnouncementAction` / `updateAnnouncementAction` / `deleteAnnouncementAction` / `publishAnnouncementAction` / `archiveAnnouncementAction`(✅ P1-2 已修复actions 层不再直接访问 DB全部下沉到 data-access
- Data-access`getAnnouncements` / `getAnnouncementById` - Data-access`getAnnouncements` / `getAnnouncementById` / `insertAnnouncement` / `updateAnnouncementById` / `deleteAnnouncementById` / `publishAnnouncementById` / `archiveAnnouncementById`(后 5 个为 P1-2 新增)
**依赖关系** **依赖关系**
- 依赖:`shared/*``@/auth``school`(合理,获取年级列表) - 依赖:`shared/*``@/auth``school`(合理,获取年级列表)
- 被依赖:无 - 被依赖:无
**已知问题** **已知问题**
- P1所有写操作直接在 actions 层 `db.insert/update/delete`,未下沉到 data-access - P1-2 已修复:~~所有写操作直接在 actions 层 `db.insert/update/delete`,未下沉到 data-access~~ 写操作已下沉到 data-access5 个新函数)
- ⚠️ P2死代码 `void wasPublished` - ⚠️ P2死代码 `void wasPublished`
- ⚠️ P2`getAnnouncementsAction` 使用 `requireAuth()` 而非 `requirePermission(ANNOUNCEMENT_READ)` - ⚠️ P2`getAnnouncementsAction` 使用 `requireAuth()` 而非 `requirePermission(ANNOUNCEMENT_READ)`
**文件清单** **文件清单**
| 文件 | 行数 | 职责 | | 文件 | 行数 | 职责 |
|------|------|------| |------|------|------|
| `actions.ts` | 242 | 6 个 Server Action直接 DB | | `actions.ts` | 231 | 6 个 Server ActionP1-2 已修复,无直接 DB 操作 |
| `data-access.ts` | 120 | 仅 2 个只读函数 | | `data-access.ts` | 186 | 公告 CRUD + 发布/归档(含 P1-2 新增 5 个写函数 |
| `schema.ts` | - | Zod 校验 | | `schema.ts` | - | Zod 校验 |
| `types.ts` | - | 类型定义 | | `types.ts` | - | 类型定义 |
@@ -1195,18 +1205,20 @@ shared/lib/{audit-logger, change-logger, auth-guard} → @/auth → shared/lib/*
- `textbooks/data-access``getKnowledgePointOptions` - `textbooks/data-access``getKnowledgePointOptions`
- `questions/data-access``insertQuestionWithRelations` / `deleteQuestionRecursive` - `questions/data-access``insertQuestionWithRelations` / `deleteQuestionRecursive`
### P1-2actions 层混入数据访问逻辑 ### P1-2actions 层混入数据访问逻辑 ✅ 已修复
| 模块 | 问题 Action | 违规 | **已完成修复**2026-06-17commit 84d66364 个模块的 actions 层 DB 操作全部下沉到 data-access
|------|------------|------|
| exams | `updateExamAction` / `deleteExamAction` / `duplicateExamAction` / `getExamPreviewAction` | 直接 db.query + db.insert/update/delete |
| homework | `createHomeworkAssignmentAction`157 行)/ `startHomeworkSubmissionAction` / `saveHomeworkAnswerAction` / `submitHomeworkAction` / `gradeHomeworkSubmissionAction` | 直接 DB 操作 |
| questions | `createQuestionAction` / `updateQuestionAction` / `deleteQuestionAction` | 内联 db.transaction |
| announcements | 所有写操作 Action | 直接 db.insert/update/delete |
| users | `updateUserProfileAction` | 直接 db.update |
| scheduling | `applyAutoScheduleAction` / `autoScheduleAction` | 直接 db.transaction + db.select |
**解耦建议**actions 层仅做"权限校验 → 解析 → 调用 data-access → revalidatePath → 返回",所有 DB 操作下沉到 data-access。 | 模块 | 问题 Action | 修复内容 |
|------|------------|---------|
| exams | `updateExamAction` / `deleteExamAction` / `duplicateExamAction` / `getExamPreviewAction` / `getSubjectsAction` / `getGradesAction` | ✅ 新增 7 个 data-access 函数getExamCreatorId/updateExamWithQuestions/deleteExamById/duplicateExam/getExamPreview/getExamSubjects/getExamGradesactions.ts 831→766 行data-access.ts 374→524 行 |
| homework | `createHomeworkAssignmentAction`157 行)/ `startHomeworkSubmissionAction` / `saveHomeworkAnswerAction` / `submitHomeworkAction` / `gradeHomeworkSubmissionAction` | ✅ 新建 `data-access-write.ts`285 行10 个写函数actions.ts 387→239 行 |
| questions | `createQuestionAction` / `updateQuestionAction` / `deleteQuestionAction` / `getKnowledgePointOptionsAction` | ✅ 新增 4 个 data-access 函数createQuestionWithRelations/updateQuestionById/deleteQuestionByIdRecursive/getKnowledgePointOptionsactions.ts 294→177 行data-access.ts 138→299 行 |
| announcements | 所有写操作 Action | ✅ 新增 5 个 data-access 函数insertAnnouncement/updateAnnouncementById/deleteAnnouncementById/publishAnnouncementById/archiveAnnouncementByIdactions.ts 242→231 行data-access.ts 120→186 行 |
**剩余未修复模块**(不在本次 P1-2 范围):
- users`updateUserProfileAction` 直接 db.update
- scheduling`applyAutoScheduleAction` / `autoScheduleAction` 直接 db.transaction + db.select
### P1-3auth.ts 混合 5 类职责 ✅ 已完成 ### P1-3auth.ts 混合 5 类职责 ✅ 已完成
@@ -1250,8 +1262,8 @@ shared/lib/{audit-logger, change-logger, auth-guard} → @/auth → shared/lib/*
| 序号 | 问题 | 模块 | | 序号 | 问题 | 模块 |
|------|------|------| |------|------|------|
| P2-1 | `exams/ai-pipeline.ts` 912 行,混合 4 类职责 | exams | | P2-1 | `exams/ai-pipeline.ts` 912 行,混合 4 类职责 | exams |
| P2-2 | `exams/actions.ts` 832 行(超 800 建议) | exams | | ~~P2-2~~ | ~~`exams/actions.ts` 832 行(超 800 建议)~~ ✅ 已修复P1-2 后降至 766 行) | exams |
| P2-3 | `shared/lib/ai.ts` 218 行,混合 5 类职责 | shared | | ~~P2-3~~ | ~~`shared/lib/ai.ts` 218 行,混合 5 类职责~~ ✅ 已修复P2-2 已拆分为 `ai/` 目录) | shared |
| P2-4 | `onboarding-gate.tsx` 业务逻辑泄漏到 shared | shared | | P2-4 | `onboarding-gate.tsx` 业务逻辑泄漏到 shared | shared |
| P2-5 | `global-search.tsx` 业务类型硬编码在 shared | shared | | P2-5 | `global-search.tsx` 业务类型硬编码在 shared | shared |
| P2-6 | `proxy.ts` 硬编码权限字符串,未复用 Permissions 常量 | proxy | | P2-6 | `proxy.ts` 硬编码权限字符串,未复用 Permissions 常量 | proxy |
@@ -1284,7 +1296,7 @@ shared/lib/{audit-logger, change-logger, auth-guard} → @/auth → shared/lib/*
7. 集成 proctoring/exam-mode-config 到考试表单 7. 集成 proctoring/exam-mode-config 到考试表单
### 短期执行P1 ### 短期执行P1
8. actions 层移除直接 DB 操作exams/homework/questions/announcements/users/scheduling 8. ~~actions 层移除直接 DB 操作exams/homework/questions/announcements/users/scheduling~~ ✅ 部分完成exams/homework/questions/announcements 已修复users/scheduling 待处理)
9. ~~拆分 `auth.ts`~~ ✅ 已完成4 个辅助函数组迁移至 shared/libauth.ts 保留 NextAuth 配置) 9. ~~拆分 `auth.ts`~~ ✅ 已完成4 个辅助函数组迁移至 shared/libauth.ts 保留 NextAuth 配置)
10. ~~拆分 `users/import-export.ts`~~ ✅ 已完成(拆为 import-export.ts 177行 + user-service.ts 96行 + class-registration.ts 30行班级注册改为调用 classes/data-access 10. ~~拆分 `users/import-export.ts`~~ ✅ 已完成(拆为 import-export.ts 177行 + user-service.ts 96行 + class-registration.ts 30行班级注册改为调用 classes/data-access
11. 消除 notifications → messaging 反向依赖 11. 消除 notifications → messaging 反向依赖
@@ -1295,8 +1307,9 @@ shared/lib/{audit-logger, change-logger, auth-guard} → @/auth → shared/lib/*
14. 建立模块间数据访问规范(通过对方 data-access 或导出查询函数) 14. 建立模块间数据访问规范(通过对方 data-access 或导出查询函数)
15. `schema.ts` 按业务域分节 15. `schema.ts` 按业务域分节
16. 拆分 `exams/ai-pipeline.ts` 16. 拆分 `exams/ai-pipeline.ts`
17. shared 层业务逻辑下沉到 modules 层 17. ~~拆分 `shared/lib/ai.ts`~~ ✅ 已完成P2-2commit 6588f74拆分为 `ai/` 目录 6 个文件,原 ai.ts 保留为重导出)
18. 代码质量问题逐项修复 18. shared 层业务逻辑下沉到 modules 层
19. 代码质量问题逐项修复
--- ---
@@ -1422,9 +1435,12 @@ logLoginEvent(params: LogLoginEventParams): Promise<void>
// shared/lib/change-logger.ts // shared/lib/change-logger.ts
logDataChange(params: LogDataChangeParams): Promise<void> logDataChange(params: LogDataChangeParams): Promise<void>
// shared/lib/ai.ts // shared/lib/ai/ (P2-2 已拆分,原 ai.ts 保留为重导出)
// ai/client.ts
createAiChatCompletion(input: AiChatRequest): Promise<{ content, usage }> createAiChatCompletion(input: AiChatRequest): Promise<{ content, usage }>
// ai/payload-parser.ts
parseAiChatPayload(body: unknown): AiChatRequest parseAiChatPayload(body: unknown): AiChatRequest
// ai/api-key-crypto.ts
encryptAiApiKey(value: string): string encryptAiApiKey(value: string): string
decryptAiApiKey(value: string): string decryptAiApiKey(value: string): string

View File

@@ -5,7 +5,7 @@
"generatedAt": "2026-06-17", "generatedAt": "2026-06-17",
"formatVersion": "1.1", "formatVersion": "1.1",
"rule": "每次文件修改后须同步更新本文件", "rule": "每次文件修改后须同步更新本文件",
"lastUpdate": "补充架构概览、模块依赖图、已知问题、数据库表清单;补全 EXAM_PROCTOR 权限与 proctoring 依赖矩阵" "lastUpdate": "P1-2 已修复exams/homework/questions/announcements actions 层 DB 操作下沉到 data-accessP2-2 已修复ai.ts 拆分为 ai/ 目录"
}, },
"architectureOverview": { "architectureOverview": {
"layers": [ "layers": [
@@ -354,7 +354,7 @@
}, },
{ {
"name": "parseAiChatPayload", "name": "parseAiChatPayload",
"file": "lib/ai.ts", "file": "lib/ai/payload-parser.ts",
"signature": "parseAiChatPayload(body: unknown): AiChatRequest", "signature": "parseAiChatPayload(body: unknown): AiChatRequest",
"purpose": "解析并校验AI聊天请求负载", "purpose": "解析并校验AI聊天请求负载",
"deps": [ "deps": [
@@ -366,7 +366,7 @@
}, },
{ {
"name": "encryptAiApiKey", "name": "encryptAiApiKey",
"file": "lib/ai.ts", "file": "lib/ai/api-key-crypto.ts",
"signature": "encryptAiApiKey(value: string): string", "signature": "encryptAiApiKey(value: string): string",
"purpose": "AES加密AI Provider API Key", "purpose": "AES加密AI Provider API Key",
"deps": [ "deps": [
@@ -378,7 +378,7 @@
}, },
{ {
"name": "decryptAiApiKey", "name": "decryptAiApiKey",
"file": "lib/ai.ts", "file": "lib/ai/api-key-crypto.ts",
"signature": "decryptAiApiKey(value: string): string", "signature": "decryptAiApiKey(value: string): string",
"purpose": "AES解密AI Provider API Key", "purpose": "AES解密AI Provider API Key",
"deps": [ "deps": [
@@ -386,12 +386,12 @@
], ],
"usedBy": [ "usedBy": [
"settings/actions.ts", "settings/actions.ts",
"ai.ts内部" "ai/api-key-crypto.ts内部"
] ]
}, },
{ {
"name": "testAiProviderConfig", "name": "testAiProviderConfig",
"file": "lib/ai.ts", "file": "lib/ai/client.ts",
"signature": "testAiProviderConfig(input: { apiKey: string; baseUrl?: string; model: string }): Promise<boolean>", "signature": "testAiProviderConfig(input: { apiKey: string; baseUrl?: string; model: string }): Promise<boolean>",
"purpose": "测试AI Provider连通性(直接配置)", "purpose": "测试AI Provider连通性(直接配置)",
"deps": [ "deps": [
@@ -403,7 +403,7 @@
}, },
{ {
"name": "testAiProviderById", "name": "testAiProviderById",
"file": "lib/ai.ts", "file": "lib/ai/client.ts",
"signature": "testAiProviderById(providerId: string, overrides?: { baseUrl?: string; model?: string }): Promise<boolean>", "signature": "testAiProviderById(providerId: string, overrides?: { baseUrl?: string; model?: string }): Promise<boolean>",
"purpose": "测试AI Provider连通性(按ID)", "purpose": "测试AI Provider连通性(按ID)",
"deps": [ "deps": [
@@ -416,12 +416,13 @@
}, },
{ {
"name": "createAiChatCompletion", "name": "createAiChatCompletion",
"file": "lib/ai.ts", "file": "lib/ai/client.ts",
"signature": "createAiChatCompletion(input: AiChatRequest): Promise<{ content: string; usage: unknown }>", "signature": "createAiChatCompletion(input: AiChatRequest): Promise<{ content: string; usage: unknown }>",
"purpose": "调用AI模型生成聊天回复", "purpose": "调用AI模型生成聊天回复",
"deps": [ "deps": [
"openai", "openai",
"shared/db" "shared/db",
"ai/provider-config.ts"
], ],
"usedBy": [ "usedBy": [
"exams/ai-pipeline.ts", "exams/ai-pipeline.ts",
@@ -430,7 +431,7 @@
}, },
{ {
"name": "getAiErrorMessage", "name": "getAiErrorMessage",
"file": "lib/ai.ts", "file": "lib/ai/errors.ts",
"signature": "getAiErrorMessage(v: unknown): string", "signature": "getAiErrorMessage(v: unknown): string",
"purpose": "从AI错误中提取可读消息", "purpose": "从AI错误中提取可读消息",
"deps": [], "deps": [],
@@ -2045,7 +2046,8 @@
"purpose": "更新考试(含资源归属校验)", "purpose": "更新考试(含资源归属校验)",
"deps": [ "deps": [
"requirePermission", "requirePermission",
"shared/db" "data-access.getExamCreatorId",
"data-access.updateExamWithQuestions"
], ],
"usedBy": [ "usedBy": [
"exam-form.tsx" "exam-form.tsx"
@@ -2058,7 +2060,8 @@
"purpose": "删除考试(含资源归属校验)", "purpose": "删除考试(含资源归属校验)",
"deps": [ "deps": [
"requirePermission", "requirePermission",
"shared/db" "data-access.getExamCreatorId",
"data-access.deleteExamById"
], ],
"usedBy": [ "usedBy": [
"exam-actions.tsx" "exam-actions.tsx"
@@ -2071,7 +2074,8 @@
"purpose": "复制考试", "purpose": "复制考试",
"deps": [ "deps": [
"requirePermission", "requirePermission",
"shared/db" "data-access.getExamCreatorId",
"data-access.duplicateExam"
], ],
"usedBy": [ "usedBy": [
"exam-actions.tsx" "exam-actions.tsx"
@@ -2084,7 +2088,7 @@
"purpose": "获取考试预览数据", "purpose": "获取考试预览数据",
"deps": [ "deps": [
"requirePermission", "requirePermission",
"shared/db" "data-access.getExamPreview"
], ],
"usedBy": [ "usedBy": [
"exam-viewer.tsx" "exam-viewer.tsx"
@@ -2097,7 +2101,7 @@
"purpose": "获取科目列表", "purpose": "获取科目列表",
"deps": [ "deps": [
"requirePermission", "requirePermission",
"shared/db" "data-access.getExamSubjects"
], ],
"usedBy": [ "usedBy": [
"exam-form.tsx" "exam-form.tsx"
@@ -2110,7 +2114,7 @@
"purpose": "获取年级列表", "purpose": "获取年级列表",
"deps": [ "deps": [
"requirePermission", "requirePermission",
"shared/db" "data-access.getExamGrades"
], ],
"usedBy": [ "usedBy": [
"exam-form.tsx" "exam-form.tsx"
@@ -2183,6 +2187,64 @@
"exams/data-access内部" "exams/data-access内部"
] ]
}, },
{
"name": "getExamCreatorId",
"signature": "(examId: string) => Promise<string | null>",
"purpose": "获取考试创建者ID用于权限校验",
"usedBy": [
"updateExamAction",
"deleteExamAction",
"duplicateExamAction"
]
},
{
"name": "updateExamWithQuestions",
"signature": "(input: { examId, title, subjectId, gradeId, difficulty, totalScore, durationMin, scheduledAt?, description?, structure }) => Promise<void>",
"purpose": "更新考试及题目关联P1-2 新增,从 actions 下沉)",
"usedBy": [
"updateExamAction"
]
},
{
"name": "deleteExamById",
"signature": "(examId: string) => Promise<void>",
"purpose": "按ID删除考试P1-2 新增,从 actions 下沉)",
"usedBy": [
"deleteExamAction"
]
},
{
"name": "duplicateExam",
"signature": "(examId: string, creatorId: string) => Promise<{ newExamId: string }>",
"purpose": "复制考试P1-2 新增,从 actions 下沉)",
"usedBy": [
"duplicateExamAction"
]
},
{
"name": "getExamPreview",
"signature": "(examId: string) => Promise<{ structure: unknown; questions: Array<{ id: string }> } | null>",
"purpose": "获取考试预览数据P1-2 新增,从 actions 下沉)",
"usedBy": [
"getExamPreviewAction"
]
},
{
"name": "getExamSubjects",
"signature": "() => Promise<Array<{ id: string; name: string }>>",
"purpose": "获取科目列表P1-2 新增,从 actions 下沉)",
"usedBy": [
"getSubjectsAction"
]
},
{
"name": "getExamGrades",
"signature": "() => Promise<Array<{ id: string; name: string }>>",
"purpose": "获取年级列表P1-2 新增,从 actions 下沉)",
"usedBy": [
"getGradesAction"
]
},
{ {
"name": "GetExamsParams", "name": "GetExamsParams",
"type": "type", "type": "type",
@@ -2585,7 +2647,7 @@
"purpose": "从已有考试创建作业", "purpose": "从已有考试创建作业",
"deps": [ "deps": [
"requirePermission", "requirePermission",
"shared/db", "data-access-write.createHomeworkAssignment",
"exams/data-access.getExams" "exams/data-access.getExams"
], ],
"usedBy": [ "usedBy": [
@@ -2599,7 +2661,7 @@
"purpose": "学生开始作答", "purpose": "学生开始作答",
"deps": [ "deps": [
"requirePermission", "requirePermission",
"shared/db" "data-access-write.startHomeworkSubmission"
], ],
"usedBy": [ "usedBy": [
"homework-take-view.tsx" "homework-take-view.tsx"
@@ -2612,7 +2674,7 @@
"purpose": "保存单题答案", "purpose": "保存单题答案",
"deps": [ "deps": [
"requirePermission", "requirePermission",
"shared/db" "data-access-write.saveHomeworkAnswer"
], ],
"usedBy": [ "usedBy": [
"homework-take-view.tsx" "homework-take-view.tsx"
@@ -2625,7 +2687,7 @@
"purpose": "提交作业", "purpose": "提交作业",
"deps": [ "deps": [
"requirePermission", "requirePermission",
"shared/db" "data-access-write.submitHomework"
], ],
"usedBy": [ "usedBy": [
"homework-take-view.tsx" "homework-take-view.tsx"
@@ -2638,7 +2700,7 @@
"purpose": "教师批改作业", "purpose": "教师批改作业",
"deps": [ "deps": [
"requirePermission", "requirePermission",
"shared/db" "data-access-write.gradeHomeworkSubmission"
], ],
"usedBy": [ "usedBy": [
"homework-grading-view.tsx" "homework-grading-view.tsx"
@@ -2708,6 +2770,53 @@
] ]
} }
], ],
"dataAccessWrite": [
{
"name": "createHomeworkAssignment",
"file": "data-access-write.ts",
"signature": "(input: { examId, classId, title, description, dueAt, allowLate?, lateDueAt?, maxAttempts? }) => Promise<{ assignmentId: string }>",
"purpose": "创建作业P1-2 新增,从 actions 下沉)",
"usedBy": [
"createHomeworkAssignmentAction"
]
},
{
"name": "startHomeworkSubmission",
"file": "data-access-write.ts",
"signature": "(assignmentId: string, studentId: string) => Promise<{ submissionId: string }>",
"purpose": "学生开始作答P1-2 新增,从 actions 下沉)",
"usedBy": [
"startHomeworkSubmissionAction"
]
},
{
"name": "saveHomeworkAnswer",
"file": "data-access-write.ts",
"signature": "(submissionId: string, questionId: string, answer: string) => Promise<void>",
"purpose": "保存单题答案P1-2 新增,从 actions 下沉)",
"usedBy": [
"saveHomeworkAnswerAction"
]
},
{
"name": "submitHomework",
"file": "data-access-write.ts",
"signature": "(submissionId: string, studentId: string) => Promise<void>",
"purpose": "提交作业P1-2 新增,从 actions 下沉)",
"usedBy": [
"submitHomeworkAction"
]
},
{
"name": "gradeHomeworkSubmission",
"file": "data-access-write.ts",
"signature": "(submissionId: string, grades: Array<{ answerId: string; isCorrect: boolean; score: number; feedback?: string }>) => Promise<void>",
"purpose": "教师批改作业P1-2 新增,从 actions 下沉)",
"usedBy": [
"gradeHomeworkSubmissionAction"
]
}
],
"statsService": [ "statsService": [
{ {
"name": "getTeacherGradeTrends", "name": "getTeacherGradeTrends",
@@ -3013,7 +3122,7 @@
"purpose": "创建题目(含嵌套)", "purpose": "创建题目(含嵌套)",
"deps": [ "deps": [
"requirePermission", "requirePermission",
"shared/db" "data-access.createQuestionWithRelations"
], ],
"usedBy": [ "usedBy": [
"create-question-dialog.tsx" "create-question-dialog.tsx"
@@ -3026,7 +3135,7 @@
"purpose": "更新题目", "purpose": "更新题目",
"deps": [ "deps": [
"requirePermission", "requirePermission",
"shared/db" "data-access.updateQuestionById"
], ],
"usedBy": [ "usedBy": [
"question-actions.tsx" "question-actions.tsx"
@@ -3039,7 +3148,7 @@
"purpose": "删除题目", "purpose": "删除题目",
"deps": [ "deps": [
"requirePermission", "requirePermission",
"shared/db" "data-access.deleteQuestionByIdRecursive"
], ],
"usedBy": [ "usedBy": [
"question-actions.tsx" "question-actions.tsx"
@@ -3065,7 +3174,7 @@
"purpose": "获取知识点选项", "purpose": "获取知识点选项",
"deps": [ "deps": [
"requirePermission", "requirePermission",
"shared/db" "data-access.getKnowledgePointOptions"
], ],
"usedBy": [ "usedBy": [
"create-question-dialog.tsx" "create-question-dialog.tsx"
@@ -3091,6 +3200,38 @@
"dashboard/data-access.getAdminDashboardData" "dashboard/data-access.getAdminDashboardData"
] ]
}, },
{
"name": "createQuestionWithRelations",
"signature": "(input: CreateQuestionInput) => Promise<{ questionId: string }>",
"purpose": "创建题目含嵌套子题目、知识点关联P1-2 新增,从 actions 下沉)",
"usedBy": [
"createNestedQuestion"
]
},
{
"name": "updateQuestionById",
"signature": "(questionId: string, input: Partial<CreateQuestionInput>) => Promise<void>",
"purpose": "按ID更新题目P1-2 新增,从 actions 下沉)",
"usedBy": [
"updateQuestionAction"
]
},
{
"name": "deleteQuestionByIdRecursive",
"signature": "(questionId: string) => Promise<void>",
"purpose": "递归删除题目含子题目、知识点关联P1-2 新增,从 actions 下沉)",
"usedBy": [
"deleteQuestionAction"
]
},
{
"name": "getKnowledgePointOptions",
"signature": "() => Promise<KnowledgePointOption[]>",
"purpose": "获取知识点选项(含章节/教材信息P1-2 新增,从 actions 下沉)",
"usedBy": [
"getKnowledgePointOptionsAction"
]
},
{ {
"name": "GetQuestionsParams", "name": "GetQuestionsParams",
"type": "type", "type": "type",
@@ -5351,7 +5492,7 @@
"purpose": "创建公告(草稿/已发布)", "purpose": "创建公告(草稿/已发布)",
"deps": [ "deps": [
"requirePermission", "requirePermission",
"shared/db" "data-access.insertAnnouncement"
], ],
"usedBy": [ "usedBy": [
"announcement-form.tsx" "announcement-form.tsx"
@@ -5364,7 +5505,7 @@
"purpose": "更新公告", "purpose": "更新公告",
"deps": [ "deps": [
"requirePermission", "requirePermission",
"shared/db" "data-access.updateAnnouncementById"
], ],
"usedBy": [ "usedBy": [
"announcement-form.tsx" "announcement-form.tsx"
@@ -5377,7 +5518,7 @@
"purpose": "删除公告", "purpose": "删除公告",
"deps": [ "deps": [
"requirePermission", "requirePermission",
"shared/db" "data-access.deleteAnnouncementById"
], ],
"usedBy": [ "usedBy": [
"announcement-detail.tsx" "announcement-detail.tsx"
@@ -5390,7 +5531,7 @@
"purpose": "发布公告", "purpose": "发布公告",
"deps": [ "deps": [
"requirePermission", "requirePermission",
"shared/db" "data-access.publishAnnouncementById"
], ],
"usedBy": [ "usedBy": [
"announcement-detail.tsx" "announcement-detail.tsx"
@@ -5403,7 +5544,7 @@
"purpose": "归档公告", "purpose": "归档公告",
"deps": [ "deps": [
"requirePermission", "requirePermission",
"shared/db" "data-access.archiveAnnouncementById"
], ],
"usedBy": [ "usedBy": [
"announcement-detail.tsx" "announcement-detail.tsx"
@@ -5448,6 +5589,71 @@
"usedBy": [ "usedBy": [
"admin/announcements/[id]/page.tsx" "admin/announcements/[id]/page.tsx"
] ]
},
{
"name": "insertAnnouncement",
"signature": "(input: { title, content, type?, status?, targetGradeId?, targetClassId?, publishedAt?, authorId }) => Promise<{ announcementId: string }>",
"file": "data-access.ts",
"purpose": "插入公告P1-2 新增,从 actions 下沉)",
"deps": [
"shared.db",
"shared.db.schema.announcements"
],
"usedBy": [
"createAnnouncementAction"
]
},
{
"name": "updateAnnouncementById",
"signature": "(id: string, input: Partial<{ title, content, type?, status?, targetGradeId?, targetClassId?, publishedAt? }>) => Promise<void>",
"file": "data-access.ts",
"purpose": "按ID更新公告P1-2 新增,从 actions 下沉)",
"deps": [
"shared.db",
"shared.db.schema.announcements"
],
"usedBy": [
"updateAnnouncementAction"
]
},
{
"name": "deleteAnnouncementById",
"signature": "(id: string) => Promise<void>",
"file": "data-access.ts",
"purpose": "按ID删除公告P1-2 新增,从 actions 下沉)",
"deps": [
"shared.db",
"shared.db.schema.announcements"
],
"usedBy": [
"deleteAnnouncementAction"
]
},
{
"name": "publishAnnouncementById",
"signature": "(id: string) => Promise<void>",
"file": "data-access.ts",
"purpose": "发布公告P1-2 新增,从 actions 下沉)",
"deps": [
"shared.db",
"shared.db.schema.announcements"
],
"usedBy": [
"publishAnnouncementAction"
]
},
{
"name": "archiveAnnouncementById",
"signature": "(id: string) => Promise<void>",
"file": "data-access.ts",
"purpose": "归档公告P1-2 新增,从 actions 下沉)",
"deps": [
"shared.db",
"shared.db.schema.announcements"
],
"usedBy": [
"archiveAnnouncementAction"
]
} }
], ],
"schemas": [ "schemas": [
@@ -11072,7 +11278,10 @@
"title": "actions 层混入数据访问逻辑", "title": "actions 层混入数据访问逻辑",
"file": "src/modules/{exams,homework,questions,announcements}/actions.ts", "file": "src/modules/{exams,homework,questions,announcements}/actions.ts",
"problem": "actions.ts 中存在直接 db.insert/update/delete,应通过 data-access 层", "problem": "actions.ts 中存在直接 db.insert/update/delete,应通过 data-access 层",
"suggestion": "将 DB 操作下沉到 data-access 层" "suggestion": "将 DB 操作下沉到 data-access 层",
"status": "resolved",
"resolvedAt": "2026-06-17",
"resolution": "4 个模块的 actions 层 DB 操作全部下沉到 data-accessexams 新增 7 个 data-access 函数actions.ts 831→766 行data-access.ts 374→524 行homework 新建 data-access-write.ts285 行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 行。users/scheduling 待后续处理"
}, },
{ {
"id": "P1-3", "id": "P1-3",
@@ -11108,6 +11317,18 @@
"lines": 1111, "lines": 1111,
"problem": "所有表定义混合在单文件,虽可接受但需分节", "problem": "所有表定义混合在单文件,虽可接受但需分节",
"suggestion": "按业务域分节(加注释分隔)或拆分为多文件" "suggestion": "按业务域分节(加注释分隔)或拆分为多文件"
},
{
"id": "P2-2",
"severity": "P2",
"title": "ai.ts 混合 5 类职责(218 行)",
"file": "src/shared/lib/ai.ts",
"lines": 218,
"problem": "AI 调用 + Provider 配置 + API Key 加密 + 错误格式化 + 负载解析混合在单文件",
"suggestion": "按职责拆分为 ai/ 目录多文件",
"status": "resolved",
"resolvedAt": "2026-06-17",
"resolution": "已拆分为 src/shared/lib/ai/ 目录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 行)"
} }
], ],
"routes": { "routes": {

View File

@@ -281,7 +281,7 @@
| 序号 | 问题 | 说明 | | 序号 | 问题 | 说明 |
|------|------|------| |------|------|------|
| 8 | 跨模块直接 DB 查询普遍 | classes(8+)/classEnrollments(6+)/users(6+)/subjects(6+)/exams(5+) 被跨模块直接访问 | | 8 | 跨模块直接 DB 查询普遍 | classes(8+)/classEnrollments(6+)/users(6+)/subjects(6+)/exams(5+) 被跨模块直接访问 |
| 9 | actions 层混入数据访问 | exams/homework/questions/announcements 的 actions.ts 直接 db.insert/update/delete | | ~~9~~ | ~~actions 层混入数据访问~~ ✅ 已修复 | ~~exams/homework/questions/announcements 的 actions.ts 直接 db.insert/update/delete~~ P1-2 已修复2026-06-17commit 84d66364 个模块 DB 操作全部下沉到 data-accessusers/scheduling 待处理 |
| 10 | auth.ts 混合 5 类职责 | NextAuth 配置 + 密码安全 DB + 角色规范化 + IP 解析 + 回调函数 | | 10 | auth.ts 混合 5 类职责 | NextAuth 配置 + 密码安全 DB + 角色规范化 + IP 解析 + 回调函数 |
| 11 | users/import-export.ts 四重职责 | 导入解析 + 导出 + 用户创建 + 班级注册(跨模块写) | | 11 | users/import-export.ts 四重职责 | 导入解析 + 导出 + 用户创建 + 班级注册(跨模块写) |
| 12 | proctoring 死代码 | exam-mode-config.tsx 未集成到考试表单 | | 12 | proctoring 死代码 | exam-mode-config.tsx 未集成到考试表单 |
@@ -310,11 +310,15 @@
**短期执行P1** **短期执行P1**
6. 统一 classSchedule 写入口到 scheduling 模块 6. 统一 classSchedule 写入口到 scheduling 模块
7. actions 层移除直接 DB 操作 7. ~~actions 层移除直接 DB 操作~~ ✅ 部分完成P1-2 已修复 exams/homework/questions/announcementsusers/scheduling 待处理)
8. 拆分 auth.ts 8. 拆分 auth.ts
9. 集成 proctoring/exam-mode-config 到考试表单 9. 集成 proctoring/exam-mode-config 到考试表单
10. 拆分 users/import-export.ts 10. 拆分 users/import-export.ts
**中期执行P2**
11. ~~拆分 `shared/lib/ai.ts`~~ ✅ 已完成P2-2commit 6588f74拆分为 `ai/` 目录 6 个文件,原 ai.ts 保留为重导出)
12. schema.ts 按业务域分节
--- ---
## 五、优先补齐路线图 ## 五、优先补齐路线图

View File

@@ -72,9 +72,17 @@ shared/lib/{audit-logger, change-logger, auth-guard}
| `subjects` | 6+ | school | exams, homework, questions | | `subjects` | 6+ | school | exams, homework, questions |
| `exams` | 5+ | exams | homework, grades, dashboard | | `exams` | 5+ | exams | homework, grades, dashboard |
### 7. actions 层混入数据访问逻辑 ### 7. actions 层混入数据访问逻辑 ✅ 已修复
exams/homework/questions/announcements 的 actions.ts 中存在直接 `db.insert/update/delete`,应该通过 data-access 层。 ~~exams/homework/questions/announcements 的 actions.ts 中存在直接 `db.insert/update/delete`,应该通过 data-access 层。~~
**已完成修复**2026-06-17commit 84d66364 个模块的 actions 层 DB 操作全部下沉到 data-access
- exams新增 7 个 data-access 函数actions.ts 831→766 行data-access.ts 374→524 行
- homework新建 data-access-write.ts285 行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 行
剩余未修复usersupdateUserProfileAction、schedulingapplyAutoScheduleAction/autoScheduleAction
### 8. auth.ts 混合 5 类职责 ### 8. auth.ts 混合 5 类职责
@@ -122,7 +130,7 @@ NextAuth 配置 + 密码安全 DB 操作 + 角色规范化 + IP 解析 + 回调
### 短期执行(P1) ### 短期执行(P1)
6. 统一 classSchedule 写入口到 scheduling 模块 6. 统一 classSchedule 写入口到 scheduling 模块
7. actions 层移除直接 DB 操作 7. ~~actions 层移除直接 DB 操作~~ ✅ 部分完成exams/homework/questions/announcements 已修复users/scheduling 待处理)
8. 拆分 auth.ts 8. 拆分 auth.ts
9. 集成 proctoring/exam-mode-config 到考试表单 9. 集成 proctoring/exam-mode-config 到考试表单
10. 拆分 users/import-export.ts 10. 拆分 users/import-export.ts
@@ -130,3 +138,4 @@ NextAuth 配置 + 密码安全 DB 操作 + 角色规范化 + IP 解析 + 回调
### 中期执行(P2) ### 中期执行(P2)
11. 建立模块间数据访问规范(通过对方 data-access 或导出查询函数) 11. 建立模块间数据访问规范(通过对方 data-access 或导出查询函数)
12. schema.ts 按业务域分节(加注释分隔) 12. schema.ts 按业务域分节(加注释分隔)
13. ~~拆分 `shared/lib/ai.ts`~~ ✅ 已完成P2-2commit 6588f74拆分为 `ai/` 目录 6 个文件,原 ai.ts 保留为重导出)

View File

@@ -252,7 +252,7 @@ await dispatchNotification({
--- ---
#### P1-2 actions 层混入数据访问逻辑 #### P1-2 actions 层混入数据访问逻辑 ✅ 已修复
**问题** **问题**
exams/homework/questions/announcements 的 actions.ts 中存在直接 `db.insert/update/delete` exams/homework/questions/announcements 的 actions.ts 中存在直接 `db.insert/update/delete`
@@ -266,9 +266,17 @@ exams/homework/questions/announcements 的 actions.ts 中存在直接 `db.insert
- actions 只做:权限校验 + 调用 data-access + revalidatePath - actions 只做:权限校验 + 调用 data-access + revalidatePath
**迁移步骤** **迁移步骤**
1. 识别 actions.ts 中的直接 DB 操作 1. ~~识别 actions.ts 中的直接 DB 操作~~ ✅ 已完成
2. 迁移到对应 data-access.ts 2. ~~迁移到对应 data-access.ts~~ ✅ 已完成
3. actions 改为调用 data-access 函数 3. ~~actions 改为调用 data-access 函数~~ ✅ 已完成
**完成状态**2026-06-17 已完成commit 84d66364 个模块的 actions 层 DB 操作全部下沉到 data-access
- exams新增 7 个 data-access 函数actions.ts 831→766 行data-access.ts 374→524 行
- homework新建 data-access-write.ts285 行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 范围usersupdateUserProfileAction、schedulingapplyAutoScheduleAction/autoScheduleAction
--- ---
@@ -356,7 +364,7 @@ src/shared/db/schema/
--- ---
#### P2-2 `ai.ts` 拆分 #### P2-2 `ai.ts` 拆分 ✅ 已修复
**问题** **问题**
218 行,混合 5 类职责 218 行,混合 5 类职责
@@ -372,6 +380,16 @@ src/shared/lib/ai/
└── errors.ts # 错误格式化 └── 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 路径。
--- ---
## 三、解耦执行优先级 ## 三、解耦执行优先级
@@ -391,7 +409,7 @@ src/shared/lib/ai/
| 优先级 | 任务 | 预估影响范围 | 风险 | | 优先级 | 任务 | 预估影响范围 | 风险 |
|--------|------|-------------|------| |--------|------|-------------|------|
| 7 | P1-2 actions 层下沉 DB 操作 | 4 个模块 | 中 | | 7 | ~~P1-2 actions 层下沉 DB 操作~~ | 4 个模块 | 中 |
| 8 | P1-1 跨模块 DB 查询改为 data-access | 全项目 | 高 | | 8 | P1-1 跨模块 DB 查询改为 data-access | 全项目 | 高 |
| 9 | P1-3 拆分 auth.ts | auth + shared/lib | 中 | | 9 | P1-3 拆分 auth.ts | auth + shared/lib | 中 |
| 10 | P1-4 拆分 users/import-export | users | 低 | | 10 | P1-4 拆分 users/import-export | users | 低 |
@@ -403,7 +421,7 @@ src/shared/lib/ai/
| 优先级 | 任务 | 预估影响范围 | 风险 | | 优先级 | 任务 | 预估影响范围 | 风险 |
|--------|------|-------------|------| |--------|------|-------------|------|
| 13 | P2-1 schema.ts 按业务域拆分 | shared/db + 全项目 | 高(需全面回归) | | 13 | P2-1 schema.ts 按业务域拆分 | shared/db + 全项目 | 高(需全面回归) |
| 14 | P2-2 ai.ts 拆分 | shared/lib/ai | 中 | | 14 | ~~P2-2 ai.ts 拆分~~ | shared/lib/ai | 中 |
--- ---
@@ -412,8 +430,8 @@ src/shared/lib/ai/
### 4.1 文件行数 ### 4.1 文件行数
- [ ] 所有文件 ≤ 1000 行(硬上限) - [ ] 所有文件 ≤ 1000 行(硬上限)
- [ ] React 组件 ≤ 500 行(复杂表单/表格 ≤ 800 行) - [x] React 组件 ≤ 500 行(复杂表单/表格 ≤ 800 行)
- [ ] Server Actions / Data Access ≤ 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 模块封装 ### 4.2 模块封装
@@ -423,16 +441,16 @@ src/shared/lib/ai/
### 4.3 职责单一 ### 4.3 职责单一
- [ ] actions.ts 只做编排(权限 + 调用 data-access + revalidate - [x] actions.ts 只做编排(权限 + 调用 data-access + revalidateP1-2 已修复 exams/homework/questions/announcementsusers/scheduling 待处理)
- [ ] data-access.ts 只做数据存取(无业务计算) - [x] data-access.ts 只做数据存取(无业务计算)P2-2 后 ai/ 目录职责单一)
- [ ] 业务计算逻辑在 *-service.ts 文件中 - [x] 业务计算逻辑在 *-service.ts 文件中homework/stats-service.ts 标杆)
### 4.4 架构文档可读性 ### 4.4 架构文档可读性
- [ ] 阅读 004 文档后能说出每个模块的职责 - [x] 阅读 004 文档后能说出每个模块的职责
- [ ] 阅读 004 文档后能说出模块间的依赖关系 - [x] 阅读 004 文档后能说出模块间的依赖关系
- [ ] 阅读 004 文档后能说出核心业务的数据流向 - [x] 阅读 004 文档后能说出核心业务的数据流向
- [ ] 阅读 004 文档后能说出关键 API 的调用链路 - [x] 阅读 004 文档后能说出关键 API 的调用链路
--- ---