fix(M-AI-07-06): answer leak fixes (A3/A4) + state compatibility
Some checks failed
Deploy API Server / build-and-unit (push) Successful in 37s
Deploy API Server / current-integration (push) Successful in 30s
Deploy API Server / backward-compat (push) Successful in 0s
Deploy API Server / deploy (push) Failing after 49s

- 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 <noreply@anthropic.com>
This commit is contained in:
wangdl 2026-06-22 20:51:03 +08:00
parent 40e772cc3a
commit c3b8919489
2 changed files with 13 additions and 2 deletions

View File

@ -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' },
});

View File

@ -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;