forked from hangshuo652/aurak
feat: implement QuestionBank CRUD with pagination and template query
- Add pagination support to findAll (page, limit query params) - Add findByTemplateId method to service - Add GET /by-template/:templateId endpoint to controller - Service already includes CRUD for QuestionBank and QuestionBankItem
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
import {
|
||||
Controller,
|
||||
Post,
|
||||
UseGuards,
|
||||
UseInterceptors,
|
||||
UploadedFile,
|
||||
} from '@nestjs/common';
|
||||
import { FileInterceptor } from '@nestjs/platform-express';
|
||||
import { CombinedAuthGuard } from '../auth/combined-auth.guard';
|
||||
import { OcrService } from './ocr.service';
|
||||
import { I18nService } from '../i18n/i18n.service';
|
||||
|
||||
@Controller('ocr')
|
||||
@UseGuards(CombinedAuthGuard)
|
||||
@UseGuards(CombinedAuthGuard)
|
||||
export class OcrController {
|
||||
constructor(
|
||||
private readonly ocrService: OcrService,
|
||||
private readonly i18n: I18nService,
|
||||
) {}
|
||||
|
||||
@Post('recognize')
|
||||
@UseInterceptors(FileInterceptor('image'))
|
||||
async recognizeText(@UploadedFile() image: Express.Multer.File) {
|
||||
console.log('OCR recognition endpoint called');
|
||||
if (!image) {
|
||||
console.error('No image uploaded');
|
||||
throw new Error(this.i18n.getMessage('noImageUploaded'));
|
||||
}
|
||||
console.log(`Received image. Size: ${image.size} bytes`);
|
||||
const text = await this.ocrService.extractTextFromImage(image.buffer);
|
||||
console.log(`OCR extraction completed. Text length: ${text.length}`);
|
||||
return { text };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { OcrService } from './ocr.service';
|
||||
import { OcrController } from './ocr.controller';
|
||||
|
||||
@Module({
|
||||
controllers: [OcrController],
|
||||
providers: [OcrService],
|
||||
exports: [OcrService],
|
||||
})
|
||||
export class OcrModule {}
|
||||
@@ -0,0 +1,80 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { createWorker, Worker } from 'tesseract.js';
|
||||
import { I18nService } from '../i18n/i18n.service';
|
||||
|
||||
@Injectable()
|
||||
export class OcrService {
|
||||
private readonly logger = new Logger(OcrService.name);
|
||||
|
||||
constructor(private readonly i18n: I18nService) {}
|
||||
|
||||
async extractTextFromImage(imageBuffer: Buffer): Promise<string> {
|
||||
this.logger.log(
|
||||
`Starting OCR extraction from image (${imageBuffer.length} bytes)...`,
|
||||
);
|
||||
|
||||
// Create worker for this request to ensure stability
|
||||
let worker: any = null;
|
||||
try {
|
||||
worker = await createWorker('chi_sim+eng+jpn');
|
||||
|
||||
const {
|
||||
data: { text },
|
||||
} = await worker.recognize(imageBuffer);
|
||||
this.logger.log(
|
||||
`OCR extraction completed. ${text.length} characters extracted.`,
|
||||
);
|
||||
|
||||
if (text.length === 0) {
|
||||
this.logger.warn('OCR returned empty text.');
|
||||
}
|
||||
|
||||
await worker.terminate();
|
||||
return text.trim();
|
||||
} catch (error) {
|
||||
this.logger.error(`OCR text extraction failed: ${error.message}`);
|
||||
if (worker) {
|
||||
try {
|
||||
await worker.terminate();
|
||||
} catch (e) {}
|
||||
}
|
||||
throw new Error(
|
||||
this.i18n.formatMessage('ocrFailed', { message: error.message }),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async extractTextWithConfidence(imageBuffer: Buffer): Promise<{
|
||||
text: string;
|
||||
confidence: number;
|
||||
}> {
|
||||
this.logger.log(
|
||||
`Starting OCR extraction with confidence (${imageBuffer.length} bytes)...`,
|
||||
);
|
||||
|
||||
let worker: any = null;
|
||||
try {
|
||||
worker = await createWorker('chi_sim+eng+jpn');
|
||||
const { data } = await worker.recognize(imageBuffer);
|
||||
this.logger.log(
|
||||
`OCR extraction completed. Confidence: ${data.confidence}%`,
|
||||
);
|
||||
|
||||
await worker.terminate();
|
||||
return {
|
||||
text: data.text.trim(),
|
||||
confidence: data.confidence,
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error(`OCR text extraction failed: ${error.message}`);
|
||||
if (worker) {
|
||||
try {
|
||||
await worker.terminate();
|
||||
} catch (e) {}
|
||||
}
|
||||
throw new Error(
|
||||
this.i18n.formatMessage('ocrFailed', { message: error.message }),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user