- Crear entidades OnboardingSession y ProductScan - Implementar OcrService con Google Vision API - Implementar WhisperService para transcripcion de audio - Crear OnboardingService con flujo completo - Agregar 12 endpoints para gestion de onboarding - Soporte para procesamiento de fotos y audios - Deteccion automatica de productos y precios - Integracion con TemplatesService para matching Sprint 5 - Inteligencia (2/2 epicas) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
104 lines
2.7 KiB
TypeScript
104 lines
2.7 KiB
TypeScript
import {
|
|
Entity,
|
|
PrimaryGeneratedColumn,
|
|
Column,
|
|
CreateDateColumn,
|
|
ManyToOne,
|
|
JoinColumn,
|
|
Index,
|
|
} from 'typeorm';
|
|
import { OnboardingSession } from './onboarding-session.entity';
|
|
|
|
export enum ScanType {
|
|
PHOTO = 'photo',
|
|
AUDIO = 'audio',
|
|
BARCODE = 'barcode',
|
|
}
|
|
|
|
export enum ScanStatus {
|
|
PENDING = 'pending',
|
|
PROCESSING = 'processing',
|
|
DETECTED = 'detected',
|
|
CONFIRMED = 'confirmed',
|
|
REJECTED = 'rejected',
|
|
FAILED = 'failed',
|
|
}
|
|
|
|
@Entity({ schema: 'catalog', name: 'product_scans' })
|
|
@Index(['sessionId', 'status'])
|
|
export class ProductScan {
|
|
@PrimaryGeneratedColumn('uuid')
|
|
id: string;
|
|
|
|
@Column({ name: 'session_id' })
|
|
sessionId: string;
|
|
|
|
@Column({ name: 'tenant_id', nullable: true })
|
|
tenantId: string;
|
|
|
|
@Column({ type: 'enum', enum: ScanType })
|
|
type: ScanType;
|
|
|
|
@Column({ type: 'enum', enum: ScanStatus, default: ScanStatus.PENDING })
|
|
status: ScanStatus;
|
|
|
|
// Input data
|
|
@Column({ name: 'media_url', type: 'text', nullable: true })
|
|
mediaUrl: string;
|
|
|
|
@Column({ name: 'media_mime_type', length: 50, nullable: true })
|
|
mediaMimeType: string;
|
|
|
|
@Column({ name: 'raw_text', type: 'text', nullable: true })
|
|
rawText: string;
|
|
|
|
// OCR/Transcription results
|
|
@Column({ name: 'detected_name', length: 150, nullable: true })
|
|
detectedName: string;
|
|
|
|
@Column({ name: 'detected_price', type: 'decimal', precision: 10, scale: 2, nullable: true })
|
|
detectedPrice: number;
|
|
|
|
@Column({ name: 'detected_barcode', length: 50, nullable: true })
|
|
detectedBarcode: string;
|
|
|
|
@Column({ name: 'confidence_score', type: 'decimal', precision: 3, scale: 2, nullable: true })
|
|
confidenceScore: number;
|
|
|
|
// Matched template
|
|
@Column({ name: 'template_id', nullable: true })
|
|
templateId: string;
|
|
|
|
@Column({ name: 'template_match_score', type: 'decimal', precision: 3, scale: 2, nullable: true })
|
|
templateMatchScore: number;
|
|
|
|
// Final product created
|
|
@Column({ name: 'product_id', nullable: true })
|
|
productId: string;
|
|
|
|
// User corrections
|
|
@Column({ name: 'user_confirmed_name', length: 150, nullable: true })
|
|
userConfirmedName: string;
|
|
|
|
@Column({ name: 'user_confirmed_price', type: 'decimal', precision: 10, scale: 2, nullable: true })
|
|
userConfirmedPrice: number;
|
|
|
|
// Metadata
|
|
@Column({ type: 'jsonb', nullable: true })
|
|
metadata: Record<string, any>;
|
|
|
|
@Column({ name: 'error_message', type: 'text', nullable: true })
|
|
errorMessage: string;
|
|
|
|
@Column({ name: 'processing_time_ms', type: 'int', nullable: true })
|
|
processingTimeMs: number;
|
|
|
|
@CreateDateColumn({ name: 'created_at' })
|
|
createdAt: Date;
|
|
|
|
// Relations
|
|
@ManyToOne(() => OnboardingSession, { onDelete: 'CASCADE' })
|
|
@JoinColumn({ name: 'session_id' })
|
|
session: OnboardingSession;
|
|
}
|