Files
WhatsAppCentralizado/docs/database/README.md
Claude AI a92a7efccc Initial commit: Documentación completa del proyecto WhatsApp Centralizado
- README principal con descripción del proyecto
- Documento de diseño completo (arquitectura, DB, flujos)
- Documentación de API REST y WebSocket
- Guía del Flow Builder (30+ tipos de nodos)
- Documentación de integración con Odoo
- Guía de despliegue con Docker
- Esquema de base de datos
- Estructura de carpetas del proyecto
- Archivo .env.example con todas las variables

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 09:29:57 +00:00

453 lines
16 KiB
Markdown

# Esquema de Base de Datos
## Diagrama ER
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ users │ │ queues │ │ whatsapp_accounts│
├─────────────────┤ ├─────────────────┤ ├─────────────────┤
│ id (PK) │ │ id (PK) │ │ id (PK) │
│ email │ │ name │ │ phone_number │
│ password_hash │ │ description │ │ name │
│ name │ │ assignment_method │ status │
│ role │ │ max_per_agent │ │ session_data │
│ status │ │ sla_first_resp │ │ qr_code │
│ is_active │ │ sla_resolution │ │ created_at │
│ created_at │ │ business_hours │ └────────┬────────┘
│ updated_at │ │ fallback_flow_id│ │
└────────┬────────┘ │ is_active │ │
│ └────────┬────────┘ │
│ │ │
│ ┌────────────────────┼─────────────────────────┤
│ │ │ │
│ ▼ ▼ ▼
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ │ queue_agents │ │ conversations │ │ contacts │
│ ├─────────────────┤ ├─────────────────┤ ├─────────────────┤
│ │ id (PK) │ │ id (PK) │ │ id (PK) │
│ │ queue_id (FK)───┼──│ queue_id (FK)───│ │ phone_number │
├──│ user_id (FK) │ │ whatsapp_acc_id─┼──│ name │
│ │ is_supervisor │ │ contact_id (FK)─┼──│ email │
│ │ skills │ │ assigned_to (FK)│ │ company │
│ └─────────────────┘ │ status │ │ metadata │
│ │ priority │ │ tags │
│ │ current_flow_id │ │ odoo_partner_id │
│ │ flow_context │ │ created_at │
│ │ sla_* │ └─────────────────┘
│ │ csat_* │
│ │ created_at │
│ └────────┬────────┘
│ │
│ │
│ ▼
│ ┌─────────────────┐
│ │ messages │
│ ├─────────────────┤
│ │ id (PK) │
│ │ conversation_id │
│ │ direction │
│ │ type │
│ │ content │
└───────────────────────│ sent_by (FK) │
│ is_internal_note│
│ status │
│ created_at │
└─────────────────┘
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ flows │ │ message_templates│ │ quick_replies │
├─────────────────┤ ├─────────────────┤ ├─────────────────┤
│ id (PK) │ │ id (PK) │ │ id (PK) │
│ name │ │ name │ │ shortcut │
│ description │ │ content │ │ content │
│ trigger_type │ │ attachments │ │ attachments │
│ trigger_value │ │ variables │ │ queue_id (FK) │
│ nodes (JSONB) │ │ created_at │ │ created_by (FK) │
│ variables │ └─────────────────┘ │ created_at │
│ is_active │ └─────────────────┘
│ version │
│ created_at │ ┌─────────────────┐ ┌─────────────────┐
└─────────────────┘ │ odoo_config │ │odoo_automations │
├─────────────────┤ ├─────────────────┤
┌─────────────────┐ │ id (PK) │ │ id (PK) │
│ tags │ │ url │ │ name │
├─────────────────┤ │ database │ │ odoo_model │
│ id (PK) │ │ username │ │ odoo_trigger │
│ name │ │ api_key_enc │ │ odoo_condition │
│ color │ │ is_active │ │ template_id (FK)│
│ created_at │ │ last_sync_at │ │ is_active │
└─────────────────┘ └─────────────────┘ │ created_at │
└─────────────────┘
```
---
## Tablas Detalladas
### users
Usuarios del sistema (admins, supervisores, agentes).
| Campo | Tipo | Descripción |
|-------|------|-------------|
| id | UUID | Identificador único |
| email | VARCHAR(255) | Email único |
| password_hash | VARCHAR(255) | Hash bcrypt |
| name | VARCHAR(100) | Nombre completo |
| role | ENUM | admin, supervisor, agent |
| status | ENUM | online, offline, away, busy, lunch, on_call |
| is_active | BOOLEAN | Usuario activo |
| created_at | TIMESTAMP | Fecha creación |
| updated_at | TIMESTAMP | Última modificación |
**Índices:**
- `idx_users_email` (email)
- `idx_users_role` (role)
- `idx_users_status` (status)
---
### whatsapp_accounts
Números de WhatsApp conectados.
| Campo | Tipo | Descripción |
|-------|------|-------------|
| id | UUID | Identificador único |
| phone_number | VARCHAR(20) | Número con código país |
| name | VARCHAR(100) | Alias (ej: "Ventas") |
| status | ENUM | connected, disconnected, banned |
| session_data | JSONB | Datos de sesión Baileys |
| qr_code | TEXT | QR para reconexión |
| created_at | TIMESTAMP | Fecha creación |
**Índices:**
- `idx_wa_phone` (phone_number)
- `idx_wa_status` (status)
---
### contacts
Contactos (clientes que escriben).
| Campo | Tipo | Descripción |
|-------|------|-------------|
| id | UUID | Identificador único |
| phone_number | VARCHAR(20) | Teléfono único |
| name | VARCHAR(100) | Nombre |
| email | VARCHAR(255) | Email |
| company | VARCHAR(100) | Empresa |
| metadata | JSONB | Datos personalizados |
| tags | VARCHAR[] | Array de etiquetas |
| odoo_partner_id | INTEGER | ID en Odoo |
| created_at | TIMESTAMP | Primer contacto |
**Índices:**
- `idx_contacts_phone` UNIQUE (phone_number)
- `idx_contacts_email` (email)
- `idx_contacts_odoo` (odoo_partner_id)
- `idx_contacts_tags` GIN (tags)
---
### queues
Colas de atención.
| Campo | Tipo | Descripción |
|-------|------|-------------|
| id | UUID | Identificador único |
| name | VARCHAR(100) | Nombre de cola |
| description | TEXT | Descripción |
| assignment_method | ENUM | round_robin, least_busy, skill_based, sticky |
| max_per_agent | INTEGER | Máx. conversaciones por agente |
| sla_first_response | INTEGER | Segundos para primera respuesta |
| sla_resolution | INTEGER | Segundos para resolución |
| business_hours | JSONB | Horario de atención |
| fallback_flow_id | UUID | Flujo fuera de horario |
| is_active | BOOLEAN | Cola activa |
---
### queue_agents
Relación agentes-colas.
| Campo | Tipo | Descripción |
|-------|------|-------------|
| id | UUID | Identificador único |
| queue_id | UUID | FK a queues |
| user_id | UUID | FK a users |
| is_supervisor | BOOLEAN | Es supervisor de cola |
| skills | VARCHAR[] | Habilidades del agente |
**Índices:**
- `idx_qa_queue` (queue_id)
- `idx_qa_user` (user_id)
- `idx_qa_unique` UNIQUE (queue_id, user_id)
---
### conversations
Conversaciones.
| Campo | Tipo | Descripción |
|-------|------|-------------|
| id | UUID | Identificador único |
| whatsapp_account_id | UUID | FK a whatsapp_accounts |
| contact_id | UUID | FK a contacts |
| queue_id | UUID | FK a queues (nullable) |
| assigned_to | UUID | FK a users (nullable) |
| status | ENUM | bot, waiting, active, resolved |
| priority | ENUM | low, normal, high, urgent |
| current_flow_id | UUID | Flujo en ejecución |
| flow_context | JSONB | Estado del flujo |
| sla_first_response_at | TIMESTAMP | Hora primera respuesta |
| sla_first_response_met | BOOLEAN | SLA cumplido |
| resolved_at | TIMESTAMP | Hora resolución |
| csat_score | INTEGER | Calificación 1-5 |
| csat_feedback | TEXT | Feedback del cliente |
| last_message_at | TIMESTAMP | Último mensaje |
| created_at | TIMESTAMP | Inicio conversación |
**Índices:**
- `idx_conv_contact` (contact_id)
- `idx_conv_status` (status)
- `idx_conv_assigned` (assigned_to)
- `idx_conv_queue` (queue_id)
- `idx_conv_last_msg` (last_message_at)
- `idx_conv_created` (created_at)
---
### messages
Mensajes de conversaciones.
| Campo | Tipo | Descripción |
|-------|------|-------------|
| id | UUID | Identificador único |
| conversation_id | UUID | FK a conversations |
| direction | ENUM | inbound, outbound |
| type | ENUM | text, image, audio, video, document, buttons, list, location, contact, sticker |
| content | TEXT | Contenido del mensaje |
| media_url | VARCHAR(500) | URL de media |
| metadata | JSONB | Datos adicionales (botones, etc.) |
| sent_by | UUID | FK a users (si fue agente) |
| is_internal_note | BOOLEAN | Nota interna |
| status | ENUM | pending, sent, delivered, read, failed |
| created_at | TIMESTAMP | Fecha envío/recepción |
**Índices:**
- `idx_msg_conv` (conversation_id)
- `idx_msg_created` (created_at)
- `idx_msg_direction` (direction)
- `idx_msg_status` (status)
**Particionamiento:** Por fecha (mensual) para alto volumen.
---
### flows
Flujos de chatbot.
| Campo | Tipo | Descripción |
|-------|------|-------------|
| id | UUID | Identificador único |
| name | VARCHAR(100) | Nombre del flujo |
| description | TEXT | Descripción |
| trigger_type | ENUM | welcome, keyword, fallback, event, schedule, manual |
| trigger_value | VARCHAR(255) | Valor del trigger |
| nodes | JSONB | Definición del flujo |
| variables | JSONB | Variables del flujo |
| is_active | BOOLEAN | Flujo activo |
| version | INTEGER | Versión del flujo |
| created_at | TIMESTAMP | Fecha creación |
**Índices:**
- `idx_flows_trigger` (trigger_type, is_active)
- `idx_flows_active` (is_active)
---
### message_templates
Templates de mensajes reutilizables.
| Campo | Tipo | Descripción |
|-------|------|-------------|
| id | UUID | Identificador único |
| name | VARCHAR(100) | Nombre del template |
| content | TEXT | Contenido con variables |
| attachments | JSONB | Archivos adjuntos |
| variables | VARCHAR[] | Variables usadas |
| created_at | TIMESTAMP | Fecha creación |
---
### quick_replies
Respuestas rápidas para agentes.
| Campo | Tipo | Descripción |
|-------|------|-------------|
| id | UUID | Identificador único |
| shortcut | VARCHAR(50) | Atajo (/saludo) |
| content | TEXT | Contenido |
| attachments | JSONB | Archivos adjuntos |
| queue_id | UUID | FK a queues (opcional) |
| created_by | UUID | FK a users |
| created_at | TIMESTAMP | Fecha creación |
**Índices:**
- `idx_qr_shortcut` (shortcut)
- `idx_qr_queue` (queue_id)
---
### tags
Etiquetas para contactos.
| Campo | Tipo | Descripción |
|-------|------|-------------|
| id | UUID | Identificador único |
| name | VARCHAR(50) | Nombre único |
| color | VARCHAR(7) | Color hex (#FF5733) |
| created_at | TIMESTAMP | Fecha creación |
---
### odoo_config
Configuración de conexión Odoo.
| Campo | Tipo | Descripción |
|-------|------|-------------|
| id | UUID | Identificador único |
| url | VARCHAR(255) | URL de Odoo |
| database | VARCHAR(100) | Nombre de BD |
| username | VARCHAR(100) | Usuario API |
| api_key_encrypted | TEXT | API key encriptada |
| is_active | BOOLEAN | Conexión activa |
| last_sync_at | TIMESTAMP | Última sincronización |
| created_at | TIMESTAMP | Fecha creación |
---
### odoo_automations
Automatizaciones Odoo → WhatsApp.
| Campo | Tipo | Descripción |
|-------|------|-------------|
| id | UUID | Identificador único |
| name | VARCHAR(100) | Nombre |
| odoo_model | VARCHAR(100) | Modelo Odoo (sale.order) |
| odoo_trigger | VARCHAR(100) | Evento trigger |
| odoo_condition | JSONB | Condiciones |
| message_template_id | UUID | FK a templates |
| is_active | BOOLEAN | Automatización activa |
| created_at | TIMESTAMP | Fecha creación |
---
## Migraciones
### Crear migración
```bash
docker compose exec api-gateway alembic revision --autogenerate -m "descripcion"
```
### Aplicar migraciones
```bash
docker compose exec api-gateway alembic upgrade head
```
### Revertir migración
```bash
docker compose exec api-gateway alembic downgrade -1
```
### Ver historial
```bash
docker compose exec api-gateway alembic history
```
---
## Índices Recomendados
```sql
-- Búsqueda full-text en mensajes
CREATE INDEX idx_messages_content_fts ON messages
USING gin(to_tsvector('spanish', content));
-- Búsqueda en metadata JSONB
CREATE INDEX idx_contacts_metadata ON contacts
USING gin(metadata);
-- Queries frecuentes
CREATE INDEX idx_conv_active ON conversations (status, assigned_to)
WHERE status IN ('active', 'waiting');
CREATE INDEX idx_msg_recent ON messages (conversation_id, created_at DESC);
```
---
## Mantenimiento
### Vacuum
```sql
-- Vacuum manual
VACUUM ANALYZE conversations;
VACUUM ANALYZE messages;
-- Configurar autovacuum agresivo para messages
ALTER TABLE messages SET (
autovacuum_vacuum_scale_factor = 0.05,
autovacuum_analyze_scale_factor = 0.02
);
```
### Archivado de mensajes antiguos
```sql
-- Mover mensajes > 1 año a tabla de archivo
INSERT INTO messages_archive
SELECT * FROM messages
WHERE created_at < NOW() - INTERVAL '1 year';
DELETE FROM messages
WHERE created_at < NOW() - INTERVAL '1 year';
```
### Estadísticas
```sql
-- Tamaño de tablas
SELECT
relname as table,
pg_size_pretty(pg_total_relation_size(relid)) as size
FROM pg_catalog.pg_statio_user_tables
ORDER BY pg_total_relation_size(relid) DESC;
-- Mensajes por día
SELECT
DATE(created_at) as date,
COUNT(*) as count
FROM messages
GROUP BY DATE(created_at)
ORDER BY date DESC
LIMIT 30;
```