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

@@ -5,7 +5,7 @@
"generatedAt": "2026-06-17",
"formatVersion": "1.1",
"rule": "每次文件修改后须同步更新本文件",
"lastUpdate": "补充架构概览、模块依赖图、已知问题、数据库表清单;补全 EXAM_PROCTOR 权限与 proctoring 依赖矩阵"
"lastUpdate": "P1-2 已修复exams/homework/questions/announcements actions 层 DB 操作下沉到 data-accessP2-2 已修复ai.ts 拆分为 ai/ 目录"
},
"architectureOverview": {
"layers": [
@@ -354,7 +354,7 @@
},
{
"name": "parseAiChatPayload",
"file": "lib/ai.ts",
"file": "lib/ai/payload-parser.ts",
"signature": "parseAiChatPayload(body: unknown): AiChatRequest",
"purpose": "解析并校验AI聊天请求负载",
"deps": [
@@ -366,7 +366,7 @@
},
{
"name": "encryptAiApiKey",
"file": "lib/ai.ts",
"file": "lib/ai/api-key-crypto.ts",
"signature": "encryptAiApiKey(value: string): string",
"purpose": "AES加密AI Provider API Key",
"deps": [
@@ -378,7 +378,7 @@
},
{
"name": "decryptAiApiKey",
"file": "lib/ai.ts",
"file": "lib/ai/api-key-crypto.ts",
"signature": "decryptAiApiKey(value: string): string",
"purpose": "AES解密AI Provider API Key",
"deps": [
@@ -386,12 +386,12 @@
],
"usedBy": [
"settings/actions.ts",
"ai.ts内部"
"ai/api-key-crypto.ts内部"
]
},
{
"name": "testAiProviderConfig",
"file": "lib/ai.ts",
"file": "lib/ai/client.ts",
"signature": "testAiProviderConfig(input: { apiKey: string; baseUrl?: string; model: string }): Promise<boolean>",
"purpose": "测试AI Provider连通性(直接配置)",
"deps": [
@@ -403,7 +403,7 @@
},
{
"name": "testAiProviderById",
"file": "lib/ai.ts",
"file": "lib/ai/client.ts",
"signature": "testAiProviderById(providerId: string, overrides?: { baseUrl?: string; model?: string }): Promise<boolean>",
"purpose": "测试AI Provider连通性(按ID)",
"deps": [
@@ -416,12 +416,13 @@
},
{
"name": "createAiChatCompletion",
"file": "lib/ai.ts",
"file": "lib/ai/client.ts",
"signature": "createAiChatCompletion(input: AiChatRequest): Promise<{ content: string; usage: unknown }>",
"purpose": "调用AI模型生成聊天回复",
"deps": [
"openai",
"shared/db"
"shared/db",
"ai/provider-config.ts"
],
"usedBy": [
"exams/ai-pipeline.ts",
@@ -430,7 +431,7 @@
},
{
"name": "getAiErrorMessage",
"file": "lib/ai.ts",
"file": "lib/ai/errors.ts",
"signature": "getAiErrorMessage(v: unknown): string",
"purpose": "从AI错误中提取可读消息",
"deps": [],
@@ -2045,7 +2046,8 @@
"purpose": "更新考试(含资源归属校验)",
"deps": [
"requirePermission",
"shared/db"
"data-access.getExamCreatorId",
"data-access.updateExamWithQuestions"
],
"usedBy": [
"exam-form.tsx"
@@ -2058,7 +2060,8 @@
"purpose": "删除考试(含资源归属校验)",
"deps": [
"requirePermission",
"shared/db"
"data-access.getExamCreatorId",
"data-access.deleteExamById"
],
"usedBy": [
"exam-actions.tsx"
@@ -2071,7 +2074,8 @@
"purpose": "复制考试",
"deps": [
"requirePermission",
"shared/db"
"data-access.getExamCreatorId",
"data-access.duplicateExam"
],
"usedBy": [
"exam-actions.tsx"
@@ -2084,7 +2088,7 @@
"purpose": "获取考试预览数据",
"deps": [
"requirePermission",
"shared/db"
"data-access.getExamPreview"
],
"usedBy": [
"exam-viewer.tsx"
@@ -2097,7 +2101,7 @@
"purpose": "获取科目列表",
"deps": [
"requirePermission",
"shared/db"
"data-access.getExamSubjects"
],
"usedBy": [
"exam-form.tsx"
@@ -2110,7 +2114,7 @@
"purpose": "获取年级列表",
"deps": [
"requirePermission",
"shared/db"
"data-access.getExamGrades"
],
"usedBy": [
"exam-form.tsx"
@@ -2183,6 +2187,64 @@
"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",
"type": "type",
@@ -2585,7 +2647,7 @@
"purpose": "从已有考试创建作业",
"deps": [
"requirePermission",
"shared/db",
"data-access-write.createHomeworkAssignment",
"exams/data-access.getExams"
],
"usedBy": [
@@ -2599,7 +2661,7 @@
"purpose": "学生开始作答",
"deps": [
"requirePermission",
"shared/db"
"data-access-write.startHomeworkSubmission"
],
"usedBy": [
"homework-take-view.tsx"
@@ -2612,7 +2674,7 @@
"purpose": "保存单题答案",
"deps": [
"requirePermission",
"shared/db"
"data-access-write.saveHomeworkAnswer"
],
"usedBy": [
"homework-take-view.tsx"
@@ -2625,7 +2687,7 @@
"purpose": "提交作业",
"deps": [
"requirePermission",
"shared/db"
"data-access-write.submitHomework"
],
"usedBy": [
"homework-take-view.tsx"
@@ -2638,7 +2700,7 @@
"purpose": "教师批改作业",
"deps": [
"requirePermission",
"shared/db"
"data-access-write.gradeHomeworkSubmission"
],
"usedBy": [
"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": [
{
"name": "getTeacherGradeTrends",
@@ -3013,7 +3122,7 @@
"purpose": "创建题目(含嵌套)",
"deps": [
"requirePermission",
"shared/db"
"data-access.createQuestionWithRelations"
],
"usedBy": [
"create-question-dialog.tsx"
@@ -3026,7 +3135,7 @@
"purpose": "更新题目",
"deps": [
"requirePermission",
"shared/db"
"data-access.updateQuestionById"
],
"usedBy": [
"question-actions.tsx"
@@ -3039,7 +3148,7 @@
"purpose": "删除题目",
"deps": [
"requirePermission",
"shared/db"
"data-access.deleteQuestionByIdRecursive"
],
"usedBy": [
"question-actions.tsx"
@@ -3065,7 +3174,7 @@
"purpose": "获取知识点选项",
"deps": [
"requirePermission",
"shared/db"
"data-access.getKnowledgePointOptions"
],
"usedBy": [
"create-question-dialog.tsx"
@@ -3091,6 +3200,38 @@
"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",
"type": "type",
@@ -5351,7 +5492,7 @@
"purpose": "创建公告(草稿/已发布)",
"deps": [
"requirePermission",
"shared/db"
"data-access.insertAnnouncement"
],
"usedBy": [
"announcement-form.tsx"
@@ -5364,7 +5505,7 @@
"purpose": "更新公告",
"deps": [
"requirePermission",
"shared/db"
"data-access.updateAnnouncementById"
],
"usedBy": [
"announcement-form.tsx"
@@ -5377,7 +5518,7 @@
"purpose": "删除公告",
"deps": [
"requirePermission",
"shared/db"
"data-access.deleteAnnouncementById"
],
"usedBy": [
"announcement-detail.tsx"
@@ -5390,7 +5531,7 @@
"purpose": "发布公告",
"deps": [
"requirePermission",
"shared/db"
"data-access.publishAnnouncementById"
],
"usedBy": [
"announcement-detail.tsx"
@@ -5403,7 +5544,7 @@
"purpose": "归档公告",
"deps": [
"requirePermission",
"shared/db"
"data-access.archiveAnnouncementById"
],
"usedBy": [
"announcement-detail.tsx"
@@ -5448,6 +5589,71 @@
"usedBy": [
"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": [
@@ -11072,7 +11278,10 @@
"title": "actions 层混入数据访问逻辑",
"file": "src/modules/{exams,homework,questions,announcements}/actions.ts",
"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",
@@ -11108,6 +11317,18 @@
"lines": 1111,
"problem": "所有表定义混合在单文件,虽可接受但需分节",
"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": {