michangarrito-backend-v2/src/modules/onboarding/entities/product-scan.entity.ts
rckrdmrd 29d68ec9b7 [MCH-006] feat: Implementar modulo onboarding inteligente
- 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>
2026-01-18 04:18:16 -06:00

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;
}