feat(quota): add OCR/Vision pre-check at document import controller
Some checks failed
Deploy API Server / build-and-unit (push) Successful in 40s
Deploy API Server / current-integration (push) Successful in 32s
Deploy API Server / backward-compat (push) Successful in 0s
Deploy API Server / deploy (push) Has been cancelled

Document Import: EXEMPT_BY_CONTRACT (no independent import quota — constrained
by file_size_bytes + storage_bytes + ocr_pages + vision_pages).
OCR/Vision pre-check ensures user has quota before creating import.
Per-page metering requires RAG Worker-side changes (Python).

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
wangdl 2026-06-26 20:57:19 +08:00
parent 1d6a04a8b3
commit ac7a2201ab
2 changed files with 18 additions and 3 deletions

View File

@ -1,20 +1,33 @@
import { Controller, Get, Post, Param, HttpCode, HttpStatus, Body } from '@nestjs/common'; import { Controller, Get, Post, Param, HttpCode, HttpStatus, Body, BadRequestException } from '@nestjs/common';
import { ApiTags, ApiOperation } from '@nestjs/swagger'; import { ApiTags, ApiOperation } from '@nestjs/swagger';
import { DocumentImportService } from './document-import.service'; import { DocumentImportService } from './document-import.service';
import { CreateImportDto } from './dto/create-import.dto'; import { CreateImportDto } from './dto/create-import.dto';
import { QuotaGuardService } from '../membership/quota-guard.service';
import { CurrentUser } from '../../common/decorators/current-user.decorator'; import { CurrentUser } from '../../common/decorators/current-user.decorator';
import type { UserPayload } from '../../common/types'; import type { UserPayload } from '../../common/types';
@ApiTags('document-import') @ApiTags('document-import')
@Controller('imports') @Controller('imports')
export class DocumentImportController { export class DocumentImportController {
constructor(private readonly service: DocumentImportService) {} constructor(
private readonly service: DocumentImportService,
private readonly quotaGuard: QuotaGuardService,
) {}
@Post() @Post()
@HttpCode(HttpStatus.CREATED) @HttpCode(HttpStatus.CREATED)
@ApiOperation({ summary: '创建导入任务' }) @ApiOperation({ summary: '创建导入任务' })
async createImport(@CurrentUser() user: UserPayload, @Body() dto: CreateImportDto) { async createImport(@CurrentUser() user: UserPayload, @Body() dto: CreateImportDto) {
return this.service.createImport({ ...dto, userId: user.id }); const userId = user.id;
// M-MEMBER-01-CLOSE-01D: OCR/Vision 预检——导入创建时检查可用额度
// 实际逐页计量由 RAG Worker 侧完成
const opId = `import:${userId}:${Date.now()}`;
const ocrCheck = await this.quotaGuard.check(userId, 'ocr_pages', `${opId}:ocr`, 0);
const visionCheck = await this.quotaGuard.check(userId, 'vision_pages', `${opId}:vision`, 0);
if (!ocrCheck.allowed || !visionCheck.allowed) {
throw new BadRequestException({ errorCode: 'QUOTA_EXCEEDED', message: 'OCR/Vision quota exceeded', quotaType: !ocrCheck.allowed ? 'ocr_pages' : 'vision_pages', limit: !ocrCheck.allowed ? ocrCheck.limit : visionCheck.limit, used: !ocrCheck.allowed ? ocrCheck.used : visionCheck.used, remaining: 0, upgradeAvailable: true });
}
return this.service.createImport({ ...dto, userId });
} }
@Get(':id/status') @Get(':id/status')

View File

@ -4,8 +4,10 @@ import { AdminImportsController } from './admin-imports.controller';
import { DocumentImportService } from './document-import.service'; import { DocumentImportService } from './document-import.service';
import { DocumentImportRepository } from './document-import.repository'; import { DocumentImportRepository } from './document-import.repository';
import { PrismaService } from '../../infrastructure/database/prisma.service'; import { PrismaService } from '../../infrastructure/database/prisma.service';
import { MembershipModule } from '../membership/membership.module';
@Module({ @Module({
imports: [MembershipModule],
controllers: [DocumentImportController, AdminImportsController], controllers: [DocumentImportController, AdminImportsController],
providers: [DocumentImportService, DocumentImportRepository, PrismaService], providers: [DocumentImportService, DocumentImportRepository, PrismaService],
exports: [DocumentImportService, DocumentImportRepository], exports: [DocumentImportService, DocumentImportRepository],