🗂️ Esquema de Base de Datos
La base de datos de Rial AI está modelada de manera relacional en PostgreSQL y representa las relaciones entre compañías, marcas, usuarios, prendas, proyectos de generación, imágenes, modelos virtuales, publicaciones y control de créditos.
📊 Estructura General
Todos los modelos en nuestra base de datos siguen un patrón consistente que incluye:
id: Identificador incremental para uso interno del desarrolladorpublicId: UUID (identificador único) que se puede compartir con el usuario y frontend de forma seguracreatedAt: Timestamp de creación del registroupdatedAt: Timestamp de última actualización
¿Por qué dos IDs? El
idinterno es secuencial y optimizado para consultas de base de datos, mientras que elpublicIdes un UUID que no revela información sobre la cantidad de registros y es seguro para exponer públicamente.
🏢 Modelos de Organización
Company (Compañía)
Representa a una empresa que puede tener múltiples marcas.
model Company {
id Int @id @default(autoincrement())
publicId String @unique @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
name String @unique
logo String
credits Int @default(0) // deprecado
brands Brand[]
users User[]
folders ProjectFolder[]
models Model[]
}Campos Clave
name: Nombre único de la compañíalogo: URL del logo corporativocredits: Saldo actual de créditos (default: 0)- Nota: Por ahora no manejamos activamente el sistema de créditos
Relaciones
brands[]: Una compañía puede tener múltiples marcasusers[]: Una compañía puede tener múltiples usuariosfolders[]: Carpetas de proyectos de la compañíamodels[]: Modelos virtuales asociados a la compañía
Brand (Marca)
Cada marca pertenece a una compañía y contiene sus propias prendas, proyectos, usuarios y modelos virtuales. El par (name, companyId) es único.
model Brand {
id Int @id @default(autoincrement())
publicId String @unique @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
name String
logo String
companyId Int
company Company @relation(fields: [companyId], references: [id])
garments Garment[]
projects Project[]
users User[] // N:M con User
models Model[] // N:M con Model
settings BrandSettings @relation(fields: [settingsId], references: [id])
settingsId Int @unique
}
// Índice único: (name, companyId)
model BrandSettings {
id Int @id @default(autoincrement())
publicId String @unique @default(uuid())
brand Brand?
outputPoses PoseSetting[] @relation("OutputPoses")
inputPoses PoseSetting[] @relation("InputPoses")
imageValidations Json?
aliases String[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model PoseSetting {
id Int @id @default(autoincrement())
publicId String @unique @default(uuid())
pose Pose?
garmentView GarmentView?
suffix String?
suffixRule SuffixRule?
projectType ProjectType
inputBrandSettingsId Int?
outputBrandSettingsId Int?
inputBrandSettings BrandSettings? @relation("InputPoses", fields: [inputBrandSettingsId], references: [id])
outputBrandSettings BrandSettings? @relation("OutputPoses", fields: [outputBrandSettingsId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
enum SuffixRule {
HIGHEST // Toma el sufijo más alto disponible
LOWEST // Toma el sufijo más bajo disponible
}Campos Clave
name: Nombre de la marca (único por compañía)logo: URL del logo de la marcacompanyId: Referencia a la compañía propietariasettingsId: Configuración de poses e inputs de la marca (1:1)
BrandSettings — Configuración de la marca
| Campo | Tipo | Descripción |
|---|---|---|
outputPoses | PoseSetting[] | Poses de salida configuradas para esta marca |
inputPoses | PoseSetting[] | Poses de entrada (imágenes de prenda) configuradas |
imageValidations | Json? | Reglas de validación de imágenes de entrada |
aliases | String[] | Alias de nombre de marca para mapeo externo |
PoseSetting — Configuración de pose por tipo de proyecto
Define cómo se mapean poses de entrada y salida para un tipo de proyecto específico.
| Campo | Tipo | Descripción |
|---|---|---|
pose | Pose? | Pose objetivo |
garmentView | GarmentView? | Vista de prenda asociada |
suffix | String? | Sufijo de nombre de archivo |
suffixRule | SuffixRule? | Regla para elegir sufijo (HIGHEST/LOWEST) |
projectType | ProjectType | Tipo de proyecto al que aplica |
Ejemplo de BrandSettings
Supongamos una marca que:
- El cliente sube imágenes de prenda con sufijos
_front,_backy_zoomen el nombre del archivo - Se quieren generar poses
AMERICAN_FRONTyFULLBODY_FRONTpara proyectosBATCH, yAMERICAN_FRONTparaPRODUCT - Se acepta que si no encuentra sufijo exacto, el sistema asigne la vista
FRONTusando el criterioHIGHEST - Las imágenes deben ser JPG o PNG, y tener al menos 800×800 px
{
"aliases": ["lacroix", "c. lacroix"],
"imageValidations": {
"minWidth": 800,
"minHeight": 800,
"allowedExtensions": ["jpg", "jpeg", "png"]
},
"inputPoses": [
{ "garmentView": "BACK", "suffix": "_back", "projectType": "BATCH" },
{ "garmentView": "ZOOM", "suffix": "_zoom", "projectType": "BATCH" }
],
"outputPoses": [
{ "pose": "AMERICAN_FRONT", "suffix": "001", "projectType": "BATCH" },
{ "pose": "FULLBODY_FRONT", "suffix": "002", "projectType": "BATCH" },
{ "pose": "AMERICAN_FRONT", "suffix": "001", "projectType": "PRODUCT" }
]
}Cómo se usa en la práctica:
aliases— cuando el sistema recibe un Excel con el nombre"c. lacroix", lo resuelve a la marca correcta aunque no coincida exactamente conname.inputPoses— al subir el archivocamiseta_front.jpg, el sistema identifica que es vistaFRONTporque termina en_front.outputPoses— para un proyectoBATCH, el worker genera dos imágenes por SKU (AMERICAN_FRONTnombrada…_001.jpgyFULLBODY_FRONTnombrada…_002.jpg).imageValidations— al procesar el Excel de entrada, se rechaza cualquier imagen menor a 800×800 px o que no sea JPG/PNG.
Relaciones
company: Pertenece a una compañíausers[]: Usuarios asignados a la marcagarments[]: Prendas de la marcaprojects[]: Proyectos de generaciónmodels[]: Modelos virtuales personalizadossettings: Configuración de poses e inputs (1:1 conBrandSettings)
User (Usuario)
Representa a un miembro de la plataforma con diferentes niveles de acceso, permisos granulares y asociación a marcas mediante relación N:M.
model User {
id Int @id @default(autoincrement())
publicId String @unique @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
nickname String @default("")
email String @unique
phoneNumber String?
notificationsEnabled NotificationTemplateType[]
role UserRole @default(COMPANY_ADMIN)
companyId Int?
sessionId String @unique
transactions CreditTransaction[]
company Company? @relation(fields: [companyId], references: [id])
approvals GeneratedImage[]
apiKey ApiKey?
brands Brand[]
permissions RolePermission[]
allowedModelSexes Gender[]
job Job[] @relation("UserJob")
chatMessages ChatMessage[]
}
enum UserRole {
ADMIN
COMPANY_ADMIN
BRAND_ADMIN
}
enum RolePermission { // Permisos del usuario
PROJECT_CREATOR // Puede crear proyectos
REVIEWER // Puede revisar imágenes
DOWNLOADER // Puede descargar imágenes
PERMISSION_ADMIN // Puede manejar permisos de otros usuarios
VIEWER // Puede ver el detalle de las imágenes
}Campos Clave
email: Email único del usuario (usado para autenticación)nickname: Nombre para mostrar (Actualmente usado cuando se envían notificaciones)phoneNumber: Teléfono (opcional, usado para el envío de notificaciones)notificationsEnabled: Tipos de notificación que el usuario tiene habilitadosrole: Nivel de acceso del usuarioADMIN: Acceso completo a toda la plataformaCOMPANY_ADMIN: Administrador de una compañía específicaBRAND_ADMIN: Administrador de marcas en específico
permissions: Permisos granularesallowedModelSexes: Géneros de modelo permitidos para este usuariosessionId: Identificador único de sesión (relacionado con autenticación Supabase)companyId: Referencia a compañía (para usuarios de tipo COMPANY_ADMIN y BRAND_ADMIN)
Relaciones
company: Compañía asociada (para usuarios de tipo BRAND_ADMIN o COMPANY_ADMIN)brands[]: Marcas a las que tiene acceso (para usuarios de tipo BRAND_ADMIN)apiKey: API key asociada si existe (opcional)approvals[]: Imágenes generadas que revisó este usuariojob[]: Jobs asíncronos creados por el usuariochatMessages[]: Mensajes de chat enviados por el usuario
ApiKey (Clave de API)
Almacena claves de API para autenticación de integraciones. Cada usuario puede tener como máximo una API key.
model ApiKey {
id Int @id @default(autoincrement())
publicId String @unique
name String
prefix String @unique // Primeros caracteres visibles para identificación
hash String // Hash de la clave (nunca se almacena en claro)
ownerId Int @unique
owner User @relation(fields: [ownerId], references: [id])
isDisabled Boolean @default(false)
lastUsedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}Campos Clave
name: Nombre descriptivo de la clave (ej. “Integración Excel”)prefix: Prefijo visible de la clave para que el usuario identifique cuál eshash: Hash de la clave secreta (no se almacena el valor en claro)ownerId: Usuario propietario (relación 1:1)isDisabled: Si está deshabilitada no se puede usar para autenticaciónlastUsedAt: Última vez que se usó la clave (auditoría)
Relaciones
owner: Usuario propietario de la API key
👕 Gestión de Prendas
Garment (Prenda)
Representa las prendas subidas por las marcas para ser utilizadas en los proyectos de generación.
model Garment {
id Int @id @default(autoincrement())
publicId String @unique @default(uuid())
name String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
season String?
type GarmentType
brandId Int?
brand Brand? @relation(fields: [brandId], references: [id])
images GarmentImage[]
projects ProjectImage[] // N:M: prendas usadas en una entrada de proyecto
result GeneratedImage[] // N:M: imágenes generadas que incluyen esta prenda
}
enum GarmentType {
FOOTWEAR // Zapatos, botas, sandalias
UPPER_TRUNK // Camisetas, blusas, chaquetas
LOWER_TRUNK // Pantalones, faldas, shorts
FULL_BODY // Conjuntos completos
FACESWAP // Imágenes para proyectos de tipo faceswap
BYPASS // Imagen en proyecto FACESWAP que no necesita generación
PRODUCT // Imágenes de producto para proyectos PRODUCT
}Campos Clave
name: Nombre descriptivo de la prendatype: Categoría de la prendaFOOTWEAR: Zapatos, botas, sandalias, etc.UPPER_TRUNK: Camisetas, blusas, chaquetas, etc.LOWER_TRUNK: Pantalones, faldas, shorts, etc.FULL_BODY: Conjuntos completosFACESWAP: Imágenes para proyectos de tipo faceswapBYPASS: Prenda que se pasa directamente sin procesamientoPRODUCT: Imágenes de producto para proyectos PRODUCT
season: Temporada asociada (opcional) - ej: “Verano 2024”, “Otoño/Invierno 2024”brandId: Referencia opcional a la marca propietaria
Relaciones
brand: Marca propietaria de la prenda (opcional)images[]: Imágenes asociadas a la prendaprojects[]: Entradas de proyecto (ProjectImage) donde se utiliza la prenda (N:M)result[]: Imágenes generadas que incluyen esta prenda (N:M)
GarmentImage (Imagen de Prenda)
Almacena las imágenes originales de las prendas subidas por las marcas.
model GarmentImage {
id Int @id @default(autoincrement())
publicId String @unique @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
name String
url String
garmentId Int
garment Garment @relation(fields: [garmentId], references: [id])
viewType GarmentView?
}
enum GarmentView {
FRONT
BACK
ZOOM
OTHER
}Campos Clave
name: Nombre del archivo de imagenurl: URL de la imagen almacenada en Google Cloud StorageviewType: Vista de la prenda que muestra la imagen (FRONT,BACK,ZOOM,OTHER)garmentId: Referencia a la prenda asociada
Relaciones
garment: Prenda a la que pertenece la imagen
🎯 Sistema de Proyectos
ProjectFolder (Carpeta de Proyectos)
Agrupa proyectos bajo un nombre, tipo, temporada y descripción. La compañía organiza su trabajo en carpetas; cada carpeta tiene estados visibles para admin y para cliente.
model ProjectFolder {
id Int @id @default(autoincrement())
publicId String @unique @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
name String
projectType ProjectType
season String?
description String?
adminStatus AdminStatus @default(CREATING)
clientStatus ClientStatus @default(CREATING)
companyId Int
company Company @relation(fields: [companyId], references: [id])
projects Project[]
notifications Notification[]
}
// Índice único: (companyId, name)
enum ProjectType {
BATCH
FACESWAP
PRODUCT
}
enum AdminStatus {
CREATING
IN_PROGRESS
WAITING_APPROVAL
REJECTED
COMPLETED
CANCELLED
UNPUBLISHED
}
enum ClientStatus {
CREATING
IN_PROGRESS
PENDING_APPROVAL
IN_REVIEW
PENDING_DOWNLOAD
COMPLETED
CANCELLED
}Campos Clave
name: Nombre de la carpeta (único por compañía)projectType: Tipo de caso/proyectoBATCH: Generación masiva de prendas individualesFACESWAP: Reemplazo de rostrosPRODUCT: Generación de imágenes de producto
season: Temporada asociada (opcional)description: Descripción de la carpetaadminStatus: Estado visible para administradores (flujo interno)clientStatus: Estado visible para el cliente (flujo de aprobación/descarga)
Relaciones
company: Compañía propietaria de la carpetaprojects[]: Proyectos contenidos en la carpetanotifications[]: Notificaciones asociadas a esta carpeta
Project (Proyecto)
Un proyecto define una generación concreta dentro de una carpeta: opciones en JSON, marca y carpeta. No tiene nombre propio; el nombre, temporada y descripción están en la carpeta.
model Project {
id Int @id @default(autoincrement())
publicId String @unique @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
adminStatus AdminStatus @default(CREATING)
clientStatus ClientStatus @default(CREATING)
statusRank Int @default(99) // Deprecado
statusRankAdmin Int @default(99) // Deprecado
brandId Int
options Json
folderId Int
brand Brand @relation(fields: [brandId], references: [id])
folder ProjectFolder @relation(fields: [folderId], references: [id])
images GeneratedImage[]
uploads ProjectImage[]
models Model[]
chats Chat[]
}
// Índice único: (options, brandId, folderId)Campos Clave
adminStatus/clientStatus: Estados del proyecto (mismos enums que ProjectFolder)options: Configuración en JSON con parámetros específicos del tipo de proyectobrandId: Marca propietaria del proyectofolderId: Carpeta a la que pertenece el proyecto
Relaciones
brand: Marca propietaria del proyectofolder: Carpeta que agrupa este proyectouploads[]: Imágenes de entrada (ProjectImage)images[]: Imágenes generadas en el proyectomodels[]: Modelos virtuales asignados al proyecto (N:M)chats[]: Hilos de chat asociados al proyecto
Tipos de options — ProjectOptions
El campo options es un JSON tipado en src/types/projectOptions.ts. Su forma varía según el tipo de proyecto:
type ProjectOptions =
| FaceswapProjectOptions
| BatchProjectOptions
| ProductProjectOptions;CommonProjectOptions — base compartida para FACESWAP y BATCH
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
gender | ProjectGender | No | Género del modelo |
age | number | No | Edad del modelo |
enum ProjectGender {
MALE // Hombre
FEMALE // Mujer
UNISEX // Unisex
OTHER // Otro
}FaceswapProjectOptions — proyectos FACESWAP
Extiende CommonProjectOptions sin campos adicionales.
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
gender | ProjectGender | No | Género del modelo |
age | number | No | Edad del modelo |
BatchProjectOptions — proyectos BATCH
Extiende CommonProjectOptions con poses y tipo de medio.
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
gender | ProjectGender | No | Género del modelo |
age | number | No | Edad del modelo |
poses | Pose[] | Sí | Lista de poses a generar |
mediaType | MediaType | Sí | Tipo de medio de salida |
ProductProjectOptions — proyectos de producto
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
poses | Pose[] | Sí | Lista de poses a generar |
mediaType | MediaType | No | Tipo de medio de salida |
Enums auxiliares
enum Pose {
AMERICAN_FRONT // Plano americano, frente
AMERICAN_BACK // Plano americano, espalda
AMERICAN_SIDE // Plano americano, lateral
FULLBODY_FRONT // Cuerpo completo, frente
FULLBODY_BACK // Cuerpo completo, espalda
FULLBODY_SIDE // Cuerpo completo, lateral
ZOOM // Zoom / detalle
}
enum MediaType {
IMAGE // Imagen estática
VIDEO // Video
}ProjectImage (SKU)
Representa una entrada de generación dentro de un proyecto, es decir, un SKU. Tiene estados separados para vista admin y cliente.
model ProjectImage {
id Int @id @default(autoincrement())
publicId String @unique @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
settings Json
projectId Int
project Project @relation(fields: [projectId], references: [id])
images GeneratedImage[]
garments Garment[] // N:M: prendas usadas en esta entrada
SKU String
adminStatus AdminStatus @default(IN_PROGRESS)
clientStatus ClientStatus @default(IN_PROGRESS)
chats Chat[]
}Campos Clave
settings: Configuración específica en JSON — su forma varía según el tipo de proyecto (ver abajo)SKU: Código de identificación del productoadminStatus: Estado para administradores (mismo enum AdminStatus)clientStatus: Estado para el cliente (mismo enum ClientStatus)projectId: Referencia al proyecto
Tipos de settings — ProjectImageSettings
El campo settings está tipado en src/types/projectImageSettings.ts:
type ProjectImageSettings =
| BatchProjectImageSettings
| ProductProjectImageSettings;BatchProjectImageSettings — proyectos BATCH
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
fit | string | No | Fit de la prenda (ej. baggy) |
comment | string | No | Comentario o instrucción libre para el generador |
backgroundColor | string | No | Color de fondo (ej. "#FFFFFF") |
ProductProjectImageSettings — proyectos PRODUCT
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
description | string | No | Descripción del producto para el generador |
material | string | No | Material de la prenda (ej. "100% algodón") |
dimensions | string | No | Dimensiones del producto (ej. "30x40cm") |
Relaciones
project: Proyecto al que pertenecegarments[]: Prendas utilizadas en esta generación (N:M)images[]: Imágenes generadas para esta entradachats[]: Hilos de chat asociados a este SKU
🖼️ Gestión de Imágenes
GeneratedImage (Imagen Generada)
Almacena las imágenes generadas por IA con sistema de versionado, aprobación y relación N:M con prendas.
model GeneratedImage {
id Int @id @default(autoincrement())
publicId String @unique @default(uuid())
name String
url String?
status ImageStatus @default(PENDING)
reason String?
uploadId Int
projectId Int
project Project @relation(fields: [projectId], references: [id])
upload ProjectImage @relation(fields: [uploadId], references: [id])
previous GeneratedImage? @relation("PreviousImages", fields: [previousId], references: [id])
previousId Int? @unique
next GeneratedImage? @relation("PreviousImages")
isFinal Boolean @default(true)
version Int @default(1)
garments Garment[] // N:M: prendas que aparecen en esta imagen
approvedBy User? @relation("Approvals", fields: [approvedById], references: [id])
approvedById Int?
approvedAt DateTime?
generatedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
pipelineStatus PipelineStatus @default(PENDING)
pipelineInformation Json?
revisionInformation Json?
revisionStatus RevisionStatus @default(PENDING)
pose Pose?
}
enum ImageStatus {
BYPASS
PENDING
INCOMPLETE
UNPUBLISHED
PENDING_APPROVAL
APPROVED
REJECTED
PENDING_REGENERATION
CANCELLED
}
enum RevisionStatus {
PENDING
GENERATING
PENDING_APPROVAL
EDITING
FIXING
PRE_APPROVED
APPROVED
ERROR
}
enum PipelineStatus {
PENDING
QUEUED
PROCESSING
FINISHED
COMPLETED
ERROR
}Campos Clave
name: Nombre del archivo o imagenurl: URL de la imagen generada en GCS (opcional — puede estar vacía mientras el pipeline no terminó)status: Estado en el flujo de aprobación del clientereason: Razón de rechazo (si estado es REJECTED)isFinal: Indica si es la versión finalversion: Número de versión (para versionado)previousId: Referencia a imagen anterior (cadena de versiones)approvedById/approvedAt: Usuario y timestamp de aprobacióngeneratedAt: Timestamp de cuando el pipeline generó la imagenpipelineStatus: Estado interno del pipeline de generaciónpipelineInformation: Metadata JSON del pipeline (IDs externos, logs, etc.)revisionStatus: Estado del flujo de revisión interno (equipo Rial)revisionInformation: Metadata JSON del proceso de revisiónpose: Pose de la imagen generada (enumPose)
Sistema de Versionado y Rechazo
Las imágenes pueden tener múltiples versiones encadenadas via previousId → next. Cuando se solicita una mejora o corrección:
- Se crea una nueva
GeneratedImageconversion = anterior + 1 - Se establece
previousIdapuntando a la versión anterior - La imagen anterior pasa a tener
isFinal = false; la nueva queda conisFinal = true
Flujo de rechazo
Cuando una imagen es rechazada por el cliente:
- La imagen rechazada queda con
status = REJECTEDyreasoncon el motivo - Se crea una nueva
GeneratedImagecon:status = PENDING_REGENERATION— indica que está en cola para ser regeneradaversion = anterior + 1previousIdapuntando a la imagen rechazada
El estado
PENDING_REGENERATIONactúa como marcador de que existe una nueva versión pendiente de generar como consecuencia de un rechazo.
Relaciones
project: Proyecto al que perteneceupload: SKU al cual se relaciona (ProjectImage)previous/next: Cadena de versionesgarments[]: Prendas que aparecen en esta imagen (N:M)approvedBy: Usuario que aprobó la imagen
👤 Modelos
Model (Modelo)
Representa los modelos del catálogo Rial. Pertenecen a una compañía y se asocian a marcas mediante relación N:M (un modelo puede usarse en varias marcas).
model Model {
id Int @id @default(autoincrement())
publicId String @unique @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
name String
gender Gender
age Int
image String
companyId Int?
company Company? @relation(fields: [companyId], references: [id])
brands Brand[] // N:M con Brand
projects Project[] // N:M con Project
baseImages ModelImage[]
status ModelStatus @default(ACTIVE)
}
model ModelImage {
id Int @id @default(autoincrement())
publicId String @unique @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
url String
pose Pose?
isPreview Boolean @default(false)
modelId Int
model Model @relation(fields: [modelId], references: [id])
}
enum ModelStatus {
ACTIVE
DEPRECATED
}
enum Gender {
MALE
FEMALE
OTHER
}Campos Clave
name: Nombre del modelogender: Género del modeloage: Edad aproximada del modeloimage: URL de la imagen de referencia del modelocompanyId: Referencia a la compañía (opcional; si es null, el modelo puede ser público)status: Estado del modelo —ACTIVEdisponible para uso,DEPRECATEDretirado
ModelImage — Imágenes base del modelo
Cada modelo puede tener múltiples imágenes base, una por pose.
| Campo | Tipo | Descripción |
|---|---|---|
url | String | URL de la imagen en GCS |
pose | Pose? | Pose que representa esta imagen |
isPreview | Boolean | Si es la imagen de vista previa del modelo |
Tipos de Modelos
- Los modelos se asocian a marcas mediante la relación N:M (
brands[]). Una compañía tiene sus modelos y cada modelo puede estar en una o más marcas.
Relaciones
company: Compañía a la que pertenece el modelo (opcional)brands[]: Marcas en las que está disponible este modelo (N:M)projects[]: Proyectos donde se usa este modelo (N:M)baseImages[]: Imágenes base del modelo por pose
🔔 Notificaciones
Notification (Notificación)
Registra notificaciones enviadas a usuarios (email, WhatsApp, etc.), asociadas a una carpeta de proyectos y generadas a partir de una plantilla.
model Notification {
id Int @id @default(autoincrement())
publicId String @unique @default(uuid())
title String
description String
link String
folderId Int?
folder ProjectFolder? @relation(fields: [folderId], references: [id])
isRead Boolean @default(false)
templateId Int
template NotificationTemplate @relation(fields: [templateId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}Campos Clave
title: Título de la notificacióndescription: Cuerpo o descripción del mensajelink: Enlace asociado de rial-ai.comfolderId: Al caso que se relaciona la notificación (si es que aplica)isRead: Si el usuario ya leyó la notificacióntemplateId: Plantilla con la que se generó el mensaje
Relaciones
folder: Carpeta de proyectos asociada (opcional)template: Plantilla de notificación usada
NotificationTemplate (Plantilla de Notificación)
Define plantillas reutilizables para notificaciones (email, WhatsApp). El publicId identifica el tipo de evento (enum).
model NotificationTemplate {
id Int @id @default(autoincrement())
publicId NotificationTemplateType @unique
title String
body String
variables String[]
twilioTemplateSid String
role UserRole
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
notifications Notification[]
}
enum NotificationTemplateType {
ADMIN_PROJECT_CREATED // Notificación para administrador cuando se crea un proyecto/caso nuevo
ADMIN_NEW_REJECTIONS // Notificación para administrador cuando se rechazan imágenes
COMPANY_PROJECT_CREATED // Notificación para cliente cuando se termina de crear un proyecto nuevo
COMPANY_PROJECT_PENDING_APPROVAL // Notificación para cliente cuando se generan nuevas imágenes
COMPANY_PROJECT_READY_FOR_DOWNLOAD // Notificación para cliente cuando se completa/aprueba un caso/proyecto
}Campos Clave
publicId: Tipo de notificación (enum); identifica el evento que dispara esta plantillatitle/body: Texto de la plantillavariables: Lista de nombres de variables que se reemplazan al enviar (ej. nombre de carpeta, link)twilioTemplateSid: ID de la plantilla en Twilio para mensajes WhatsApprole: Rol del usuario al que va dirigida (ADMIN, COMPANY_ADMIN, BRAND_ADMIN)
Relaciones
notifications[]: Notificaciones generadas con esta plantilla
💬 Sistema de Chat
Chat
Hilo de conversación asociado a un proyecto o a un SKU específico para que exista una revisión cruzada dentro del mismo equipo de RIAL. Los chats sin resolver bloquean la publicación de una carpeta de proyectos.
model Chat {
id Int @id @default(autoincrement())
publicId String @unique @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
isResolved Boolean @default(false)
resolvedAt DateTime?
projectId Int
projectImageId Int?
project Project @relation(fields: [projectId], references: [id])
projectImage ProjectImage? @relation(fields: [projectImageId], references: [id])
messages ChatMessage[]
}Campos Clave
isResolved: Indica si el hilo fue resuelto — los chats sin resolver bloquean la publicaciónresolvedAt: Timestamp de cuándo se marcó como resueltoprojectId: Proyecto al que pertenece el chatprojectImageId: SKU específico al que aplica el comentario (opcional — puede ser a nivel de proyecto)
Relaciones
project: Proyecto asociado al chatprojectImage: SKU específico (opcional)messages[]: Mensajes del hilo
ChatMessage (Mensaje de Chat)
Mensaje individual dentro de un hilo de chat.
model ChatMessage {
id Int @id @default(autoincrement())
publicId String @unique @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
content String
chatId Int
userId Int
chat Chat @relation(fields: [chatId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id])
}Campos Clave
content: Texto del mensajechatId: Hilo al que pertenece el mensajeuserId: Usuario que envió el mensaje (puede ser ADMIN o BRAND_ADMIN)
⚙️ Sistema de Jobs
Jobs asíncronos que representan tareas de larga duración ejecutadas por los workers. El frontend hace polling para mostrar el progreso en tiempo real.
Job
model Job {
id Int @id @default(autoincrement())
publicId String @unique @default(uuid())
status JobStatus @default(PENDING)
errorMessage String?
userId Int
user User @relation("UserJob", fields: [userId], references: [id])
createdAt DateTime @default(now())
startedAt DateTime?
finishedAt DateTime?
type JobType
payload Json // Filtros y parámetros de entrada del job
result Json? // Resultado final (ej: signed URLs de descarga)
progress Int @default(0)
totalBatches Int?
currentBatch Int @default(0)
batches JobBatch[]
}
enum JobStatus {
PENDING // Creado, esperando worker
RUNNING // Worker procesando
COMPLETED // Finalizado exitosamente
FAILED // Error irrecuperable
RETRYING // Reintentando tras fallo parcial
CANCELLED // Cancelado manualmente
}
enum JobType {
IMAGE_DOWNLOAD // Descarga de imágenes (ZIP con signed URLs de GCS)
REPORT_GENERATION // Generación de reporte Excel de imágenes
METRICS_CALCULATION // Cálculo de métricas de admin
FACESWAP_RECOGNITION // Reconocimiento facial para proyectos FACESWAP
}Campos Clave
status: Estado actual del job (el frontend hace polling hastaCOMPLETEDoFAILED)type: Qué tipo de tarea realizapayload: Filtros y parámetros de entrada enviados al workerresult: Resultado devuelto por el worker (para descargas: array designedUrls)progress: Porcentaje de avance (0-100)totalBatches/currentBatch: Para jobs procesados en lotes
Relaciones
user: Usuario que creó el jobbatches[]: Lotes de procesamiento del job
JobBatch (Lote de Job)
Divide un job grande en lotes para procesamiento paralelo o incremental.
model JobBatch {
id Int @id @default(autoincrement())
jobId Int
job Job @relation(fields: [jobId], references: [id], onDelete: Cascade)
batchNumber Int
status JobBatchStatus @default(PENDING)
result Json?
errorMessage String?
startedAt DateTime?
finishedAt DateTime?
createdAt DateTime @default(now())
}
enum JobBatchStatus {
PENDING // Esperando procesamiento
RUNNING // Procesando
DONE // Completado exitosamente
FAILED // Error en este lote
}📰 Gestión de Contenido
Post (Publicación)
Sistema de publicaciones y contenido de prensa para el sitio web y blog.
model Post {
id Int @id @default(autoincrement())
publicId String @unique @default(uuid())
title String
description String
publicationDate DateTime?
image String
file String
tags String[]
type PostType
isPublished Boolean @default(false)
authorName String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
enum PostType {
PUBLICATION
PRESS
}Campos Clave
title: Título de la publicacióndescription: Contenido o descripción del postpublicationDate: Fecha de publicación programadaimage: URL de imagen destacadafile: URL del archivo asociadotags: Array de etiquetas para categorizacióntype: Tipo de publicaciónPUBLICATION: Artículos del blogPRESS: Notas de prensa
isPublished: Estado de publicaciónauthorName: Nombre del autor
🔗 Diagrama de Relaciones
El diagrama de relaciones se encuentra en el siguiente enlace: Diagrama de Relaciones .
