docs: 同步架构文档 004/005/007/audit 反映 P1-2/P2-2 解耦修复
This commit is contained in:
@@ -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-access;P2-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-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 行)。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": {
|
||||
|
||||
Reference in New Issue
Block a user