fix(quota): remove duplicate quiz_generation reserve in router
Some checks failed
Deploy API Server / build-and-unit (push) Successful in 39s
Deploy API Server / current-integration (push) Successful in 33s
Deploy API Server / backward-compat (push) Successful in 0s
Deploy API Server / deploy (push) Has been cancelled

Quota reserve is now centralized in UserAiController.createAnalysisJob.
Router no longer needs QuotaGuardService injection.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
wangdl 2026-06-26 20:19:39 +08:00
parent 35e134d9e6
commit 6a5ab31ddd
2 changed files with 2 additions and 15 deletions

View File

@ -4,7 +4,6 @@ import { FeatureFlagService } from '../config/feature-flag.service';
import { QuizGenerationSnapshotBuilder } from '../ai-job/quiz-generation-snapshot-builder';
import { AiJobCreationService } from '../ai-job/ai-job-creation.service';
import { UserAiService } from './user-ai.service';
import { QuotaGuardService } from '../membership/quota-guard.service';
import * as crypto from 'crypto';
function dto(overrides?: any) {
@ -42,10 +41,6 @@ describe('QuizExecutionRouter', () => {
})),
};
mockLegacy = { createAnalysisJob: jest.fn() };
const mockQuotaGuard = {
check: jest.fn().mockResolvedValue({ allowed: true, quotaType: 'quiz_generation', limit: 30, used: 0, remaining: 30, resetAt: null, upgradeAvailable: false }),
buildError: jest.fn().mockReturnValue({ errorCode: 'QUOTA_EXCEEDED', message: '', quotaType: 'quiz_generation', limit: 30, used: 30, remaining: 0, resetAt: null, upgradeAvailable: true }),
};
const module: TestingModule = await Test.createTestingModule({
providers: [
@ -54,7 +49,6 @@ describe('QuizExecutionRouter', () => {
{ provide: QuizGenerationSnapshotBuilder, useValue: mockSnapshot },
{ provide: AiJobCreationService, useValue: mockCreation },
{ provide: UserAiService, useValue: mockLegacy },
{ provide: QuotaGuardService, useValue: mockQuotaGuard },
],
}).compile();

View File

@ -1,4 +1,4 @@
import { Injectable, Logger, BadRequestException } from '@nestjs/common';
import { Injectable, Logger } from '@nestjs/common';
import * as crypto from 'crypto';
import { FeatureFlagService } from '../config/feature-flag.service';
import { QuizGenerationSnapshotBuilder } from '../ai-job/quiz-generation-snapshot-builder';
@ -6,7 +6,6 @@ import type { QuizGenerationSnapshotInput } from '../ai-job/quiz-generation-snap
import { AiJobCreationService } from '../ai-job/ai-job-creation.service';
import { UserAiService } from './user-ai.service';
import type { CreateAnalysisJobDto } from './user-ai.dto';
import { QuotaGuardService } from '../membership/quota-guard.service';
/**
* M-AI-07-05 / M-AI-07-CLEANUP: Quiz Execution Router
@ -35,16 +34,10 @@ export class QuizExecutionRouter {
private readonly snapshotBuilder: QuizGenerationSnapshotBuilder,
private readonly creationService: AiJobCreationService,
private readonly legacyService: UserAiService,
private readonly quotaGuard: QuotaGuardService,
) {}
async generateQuiz(userId: string, dto: CreateAnalysisJobDto) {
// M-MEMBER-01-08: quiz_generation 接入统一额度(参考集成)
const quotaOpId = dto.idempotencyKey ?? `quiz_gen:${dto.targetType}:${dto.targetId}:${Date.now()}`;
const check = await this.quotaGuard.check(userId, 'quiz_generation', quotaOpId);
if (!check.allowed) {
throw new BadRequestException(this.quotaGuard.buildError(check));
}
// M-MEMBER-01-CLOSE-01A: 额度预占已由 UserAiController 集中处理,此处不再重复 reserve
const useUnified = await this.shouldUseUnified(userId);