feat(exams,homework): add error collection data-access for error book integration

- Add data-access-error-collection in exams module for collecting wrong exam answers

- Add data-access-error-collection in homework module for collecting wrong homework answers

- Update exams actions, exam-ai-generator, data-access, and types

- Update homework actions and data-access-write
This commit is contained in:
SpecialX
2026-06-24 12:03:03 +08:00
parent 0cee93676b
commit f0f713ff33
8 changed files with 623 additions and 25 deletions

View File

@@ -0,0 +1,94 @@
import "server-only"
import { and, eq } from "drizzle-orm"
import { db } from "@/shared/db"
import { examQuestions, examSubmissions, exams, submissionAnswers } from "@/shared/db/schema"
/**
* 错题采集所需的答案数据(单题)
*/
export type AnswerForErrorCollection = {
questionId: string
answerContent: unknown
score: number | null
feedback: string | null
maxScore: number
}
/**
* 考试提交的错题采集数据
*/
export type ExamSubmissionDataForErrorCollection = {
examId: string
subjectId: string | null
answers: AnswerForErrorCollection[]
}
/**
* 跨模块接口:获取考试提交的错题采集数据。
*
* 供 error-book 模块调用,避免 error-book 直接查询 examSubmissions、
* submissionAnswers、examQuestions、exams 等属于 exams 模块的表。
*
* 返回该提交的所有答案(含得分、反馈、满分),由 error-book 模块
* 自行筛选错题score < maxScore并采集。
*
* @param submissionId 考试提交 ID
* @param studentId 学生 ID用于校验提交归属
* @returns 提交数据;若提交不存在或 studentId 不匹配则返回 null
*/
export async function getExamSubmissionDataForErrorCollection(
submissionId: string,
studentId: string,
): Promise<ExamSubmissionDataForErrorCollection | null> {
const submission = await db.query.examSubmissions.findFirst({
where: and(
eq(examSubmissions.id, submissionId),
eq(examSubmissions.studentId, studentId),
),
columns: { id: true, examId: true },
})
if (!submission) return null
// 并行获取考试学科、提交答案、题目满分
const [exam, answers, examQuestionScores] = await Promise.all([
db.query.exams.findFirst({
where: eq(exams.id, submission.examId),
columns: { subjectId: true },
}),
db
.select({
questionId: submissionAnswers.questionId,
answerContent: submissionAnswers.answerContent,
score: submissionAnswers.score,
feedback: submissionAnswers.feedback,
})
.from(submissionAnswers)
.where(eq(submissionAnswers.submissionId, submissionId)),
db
.select({
questionId: examQuestions.questionId,
maxScore: examQuestions.score,
})
.from(examQuestions)
.where(eq(examQuestions.examId, submission.examId)),
])
const maxScoreMap = new Map(examQuestionScores.map((q) => [q.questionId, q.maxScore ?? 0]))
const mappedAnswers: AnswerForErrorCollection[] = answers.map((a) => ({
questionId: a.questionId,
answerContent: a.answerContent,
score: a.score,
feedback: a.feedback,
maxScore: maxScoreMap.get(a.questionId) ?? 0,
}))
return {
examId: submission.examId,
subjectId: exam?.subjectId ?? null,
answers: mappedAnswers,
}
}