From c3b891948954fad5a093188087a05ea830d05fc6 Mon Sep 17 00:00:00 2001 From: wangdl Date: Mon, 22 Jun 2026 20:51:03 +0800 Subject: [PATCH] fix(M-AI-07-06): answer leak fixes (A3/A4) + state compatibility - getQuizQuestions: only return answer/explanation after user has submitted - findOne: remove questions include (use getQuizQuestions instead) - State mapping: lifecycleStatus compatible with legacy status field Co-Authored-By: Claude --- src/modules/ai-runtime/user-ai.service.ts | 11 ++++++++++- src/modules/quiz/quiz.service.ts | 4 +++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/modules/ai-runtime/user-ai.service.ts b/src/modules/ai-runtime/user-ai.service.ts index f2f8590..444ac50 100644 --- a/src/modules/ai-runtime/user-ai.service.ts +++ b/src/modules/ai-runtime/user-ai.service.ts @@ -546,11 +546,20 @@ export class UserAiService { async getQuizQuestions(userId: string, quizId: string) { const quiz = await this.prisma.quiz.findFirst({ where: { id: quizId, userId }, select: { id: true } }); if (!quiz) throw new NotFoundException({ errorCode: 'QUIZ_NOT_FOUND', message: 'Quiz not found' }); + + // M-AI-07-06: 检查用户是否已提交过该 Quiz(至少一次 attempt finished) + const hasAttempt = await this.prisma.quizAttempt.findFirst({ + where: { quizId, userId, finishedAt: { not: null } }, + select: { id: true }, + }); + return this.prisma.quizQuestion.findMany({ where: { quizId }, select: { id: true, type: true, stem: true, options: true, - answer: true, explanation: true, sourceBlockIds: true, orderIndex: true, + // ★ M-AI-07-06: 答案和解析仅在用户已提交后返回 + ...(hasAttempt ? { answer: true, explanation: true } : {}), + sourceBlockIds: true, orderIndex: true, }, orderBy: { orderIndex: 'asc' }, }); diff --git a/src/modules/quiz/quiz.service.ts b/src/modules/quiz/quiz.service.ts index 37558b0..cf51de9 100644 --- a/src/modules/quiz/quiz.service.ts +++ b/src/modules/quiz/quiz.service.ts @@ -82,7 +82,9 @@ export class QuizService { async findOne(id: string) { const quiz = await this.prisma.quiz.findUnique({ - where: { id }, include: { questions: { orderBy: { orderIndex: 'asc' } } }, + where: { id }, + // M-AI-07-06: 不 include questions(避免答案泄漏) + // 客户端应使用 getQuizQuestions 获取题目,submit 后通过 getResults 获取答案 }); if (!quiz) throw new NotFoundException('测验不存在'); return quiz;