fix(quota): remove duplicate quiz_generation reserve in router
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:
parent
35e134d9e6
commit
6a5ab31ddd
@ -4,7 +4,6 @@ import { FeatureFlagService } from '../config/feature-flag.service';
|
|||||||
import { QuizGenerationSnapshotBuilder } from '../ai-job/quiz-generation-snapshot-builder';
|
import { QuizGenerationSnapshotBuilder } from '../ai-job/quiz-generation-snapshot-builder';
|
||||||
import { AiJobCreationService } from '../ai-job/ai-job-creation.service';
|
import { AiJobCreationService } from '../ai-job/ai-job-creation.service';
|
||||||
import { UserAiService } from './user-ai.service';
|
import { UserAiService } from './user-ai.service';
|
||||||
import { QuotaGuardService } from '../membership/quota-guard.service';
|
|
||||||
import * as crypto from 'crypto';
|
import * as crypto from 'crypto';
|
||||||
|
|
||||||
function dto(overrides?: any) {
|
function dto(overrides?: any) {
|
||||||
@ -42,10 +41,6 @@ describe('QuizExecutionRouter', () => {
|
|||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
mockLegacy = { createAnalysisJob: jest.fn() };
|
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({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [
|
providers: [
|
||||||
@ -54,7 +49,6 @@ describe('QuizExecutionRouter', () => {
|
|||||||
{ provide: QuizGenerationSnapshotBuilder, useValue: mockSnapshot },
|
{ provide: QuizGenerationSnapshotBuilder, useValue: mockSnapshot },
|
||||||
{ provide: AiJobCreationService, useValue: mockCreation },
|
{ provide: AiJobCreationService, useValue: mockCreation },
|
||||||
{ provide: UserAiService, useValue: mockLegacy },
|
{ provide: UserAiService, useValue: mockLegacy },
|
||||||
{ provide: QuotaGuardService, useValue: mockQuotaGuard },
|
|
||||||
],
|
],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Injectable, Logger, BadRequestException } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import * as crypto from 'crypto';
|
import * as crypto from 'crypto';
|
||||||
import { FeatureFlagService } from '../config/feature-flag.service';
|
import { FeatureFlagService } from '../config/feature-flag.service';
|
||||||
import { QuizGenerationSnapshotBuilder } from '../ai-job/quiz-generation-snapshot-builder';
|
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 { AiJobCreationService } from '../ai-job/ai-job-creation.service';
|
||||||
import { UserAiService } from './user-ai.service';
|
import { UserAiService } from './user-ai.service';
|
||||||
import type { CreateAnalysisJobDto } from './user-ai.dto';
|
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
|
* M-AI-07-05 / M-AI-07-CLEANUP: Quiz Execution Router
|
||||||
@ -35,16 +34,10 @@ export class QuizExecutionRouter {
|
|||||||
private readonly snapshotBuilder: QuizGenerationSnapshotBuilder,
|
private readonly snapshotBuilder: QuizGenerationSnapshotBuilder,
|
||||||
private readonly creationService: AiJobCreationService,
|
private readonly creationService: AiJobCreationService,
|
||||||
private readonly legacyService: UserAiService,
|
private readonly legacyService: UserAiService,
|
||||||
private readonly quotaGuard: QuotaGuardService,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async generateQuiz(userId: string, dto: CreateAnalysisJobDto) {
|
async generateQuiz(userId: string, dto: CreateAnalysisJobDto) {
|
||||||
// M-MEMBER-01-08: quiz_generation 接入统一额度(参考集成)
|
// M-MEMBER-01-CLOSE-01A: 额度预占已由 UserAiController 集中处理,此处不再重复 reserve
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
const useUnified = await this.shouldUseUnified(userId);
|
const useUnified = await this.shouldUseUnified(userId);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user