557 lines
24 KiB
Markdown
557 lines
24 KiB
Markdown
# Sistema de Lembretes Automáticos — WhatsApp, E-mail, SMS
|
||
|
||
> Agência PSI — Arquitetura completa
|
||
> Data: 2026-03-21
|
||
> Autor: Leonardo Nohama
|
||
|
||
---
|
||
|
||
## Sumário
|
||
|
||
1. [Decisão de Provedor WhatsApp](#1-decisão-de-provedor-whatsapp)
|
||
2. [Modelagem do Banco de Dados](#2-modelagem-do-banco-de-dados)
|
||
3. [Lógica de Agendamento (Cron + Edge Functions)](#3-lógica-de-agendamento)
|
||
4. [Integração Evolution API (MVP)](#4-integração-evolution-api-mvp)
|
||
5. [Integração API Oficial Meta (Escala)](#5-integração-api-oficial-meta)
|
||
6. [Frontend: Telas de Configuração](#6-frontend-telas-de-configuração)
|
||
7. [LGPD e Boas Práticas](#7-lgpd-e-boas-práticas)
|
||
8. [Diagrama do Fluxo Completo](#8-diagrama-do-fluxo-completo)
|
||
9. [Checklist de Produção](#9-checklist-de-produção)
|
||
|
||
---
|
||
|
||
## 1. Decisão de Provedor WhatsApp
|
||
|
||
### Comparativo de Provedores
|
||
|
||
| Critério | Evolution API | WPPConnect | Z-API (grátis) | Z-API Pro | Twilio (Meta oficial) | 360dialog (Meta oficial) | Zenvia (Meta oficial) |
|
||
|----------|--------------|------------|----------------|-----------|----------------------|-------------------------|----------------------|
|
||
| **Tipo** | Não-oficial (baileys) | Não-oficial | Não-oficial | Não-oficial (infra deles) | API Oficial Meta | API Oficial Meta | API Oficial Meta |
|
||
| **Custo mensal** | R$ 0 (self-hosted) | R$ 0 (self-hosted) | R$ 0 (limite de msgs) | R$ 99–299/mês | ~R$ 0.30/msg (conversa) | €49/mês + msg | R$ 0.15–0.40/msg |
|
||
| **Setup** | 2–4h (Docker) | 4–8h | 30min (SaaS) | 1h (SaaS) | 1–2 semanas (aprovação) | 3–5 dias | 3–5 dias |
|
||
| **Risco de ban** | **MÉDIO-ALTO** | ALTO | MÉDIO | MÉDIO | **ZERO** | **ZERO** | **ZERO** |
|
||
| **Templates Meta** | ❌ Não suporta | ❌ | ❌ | ❌ | ✅ Obrigatório | ✅ Obrigatório | ✅ Obrigatório |
|
||
| **Webhooks status** | ✅ Completo | ✅ Parcial | ✅ | ✅ | ✅ Completo | ✅ Completo | ✅ Completo |
|
||
| **Multi-instância** | ✅ Nativo | ❌ Manual | ❌ | ✅ | ✅ Via WABA | ✅ | ✅ |
|
||
| **Uptime SLA** | Depende de você | Depende de você | 99.5% | 99.5% | 99.95% | 99.9% | 99.9% |
|
||
| **Escalabilidade** | ~500 msgs/dia seguro | ~300 msgs/dia | ~200 msgs/dia | ~2000 msgs/dia | Ilimitado | Ilimitado | Ilimitado |
|
||
|
||
### Recomendação
|
||
|
||
**MVP (0–100 clínicas):** Evolution API self-hosted
|
||
- Custo zero, setup rápido, suficiente para validar o produto
|
||
- Cada terapeuta conecta seu próprio número via QR Code
|
||
- Limite prático: ~500 mensagens/dia por número
|
||
- Mitigação de ban: mensagens personalizadas (não genéricas), intervalos entre envios,
|
||
máximo 2 lembretes por sessão
|
||
|
||
**Escala (100+ clínicas):** Migrar para API Oficial da Meta via 360dialog ou Twilio
|
||
- Zero risco de banimento
|
||
- Templates aprovados pela Meta = entrega garantida
|
||
- Custo previsível por conversa (~R$ 0.25–0.40 por conversa de 24h)
|
||
- Suporte a botões interativos (confirmar/cancelar)
|
||
|
||
**Estratégia de migração:** O sistema será projetado com abstração de provedor desde o início.
|
||
A tabela `notification_channels` registra qual provedor cada tenant usa. Trocar de Evolution
|
||
para Meta oficial = mudar o `provider` e credenciais, sem alterar a fila ou templates.
|
||
|
||
---
|
||
|
||
## 2. Modelagem do Banco de Dados
|
||
|
||
> **Nota de integração:** O sistema existente já possui:
|
||
> - `email_templates_global` / `email_templates_tenant` → serão estendidos (não duplicados)
|
||
> - `notifications` → continuam para notificações in-app (realtime)
|
||
> - `profiles.notify_reminders` → será respeitado como opt-out global
|
||
> - `TEMPLATE_CHANNELS` em emailTemplateConstants.js → já prevê whatsapp/sms
|
||
|
||
### Relação entre tabelas novas e existentes
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ EXISTENTES (não alterar) │
|
||
│ │
|
||
│ email_templates_global ──→ templates de email (11 seeds) │
|
||
│ email_templates_tenant ──→ overrides por tenant/owner │
|
||
│ notifications ──→ notificações in-app (realtime) │
|
||
│ profiles ──→ notify_reminders, notify_system_email│
|
||
│ agenda_eventos ──→ sessões com patient_id, inicio_em │
|
||
│ patients ──→ nome_completo, telefone, email │
|
||
├─────────────────────────────────────────────────────────────────┤
|
||
│ NOVAS TABELAS │
|
||
│ │
|
||
│ notification_channels ──→ config WhatsApp/SMS por tenant │
|
||
│ notification_templates ──→ templates multi-canal (wpp/sms) │
|
||
│ notification_queue ──→ fila de envio │
|
||
│ notification_logs ──→ histórico completo │
|
||
│ notification_preferences ──→ opt-in/opt-out por paciente │
|
||
│ notification_schedules ──→ regras de quando disparar │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 3. Lógica de Agendamento
|
||
|
||
### Fluxo Completo
|
||
|
||
```
|
||
pg_cron (a cada 5 min)
|
||
│
|
||
├──→ populate_notification_queue() ← PL/pgSQL function
|
||
│ Busca agenda_eventos com inicio_em futuro,
|
||
│ cruza com notification_schedules ativas,
|
||
│ verifica notification_preferences do paciente,
|
||
│ insere na notification_queue com idempotency_key
|
||
│
|
||
└──→ HTTP call → Edge Function: process-notification-queue
|
||
│
|
||
├── Busca itens pendentes (status = 'pendente', scheduled_at <= now())
|
||
├── Marca como 'processando' (lock otimista via updated_at)
|
||
├── Resolve variáveis do template
|
||
├── Despacha para o provedor correto:
|
||
│ ├── WhatsApp → Evolution API ou Meta API
|
||
│ ├── Email → Resend / SendGrid / SMTP
|
||
│ └── SMS → Zenvia / Twilio
|
||
├── Atualiza status → 'enviado' ou 'falhou'
|
||
├── Insere em notification_logs
|
||
└── Em caso de falha: agenda retry exponencial
|
||
```
|
||
|
||
### Retry Exponencial
|
||
|
||
```
|
||
Tentativa 1: imediato
|
||
Tentativa 2: +5 minutos
|
||
Tentativa 3: +15 minutos
|
||
Tentativa 4: +60 minutos
|
||
Tentativa 5: +4 horas
|
||
Máximo: 5 tentativas → marca como 'falhou' definitivamente
|
||
```
|
||
|
||
### Prevenção de Duplicatas
|
||
|
||
1. **Idempotency key** = `{agenda_evento_id}:{schedule_key}:{canal}:{data_sessao}`
|
||
2. **UNIQUE constraint** na notification_queue sobre idempotency_key
|
||
3. **Lock otimista** no processamento: `UPDATE ... WHERE status = 'pendente' AND updated_at = ?`
|
||
4. **pg_cron não overlap**: usa `pg_try_advisory_lock()` no populate
|
||
|
||
---
|
||
|
||
## 4. Integração Evolution API (MVP)
|
||
|
||
### Setup Docker
|
||
|
||
```yaml
|
||
# docker-compose.evolution.yml
|
||
version: '3.8'
|
||
services:
|
||
evolution-api:
|
||
image: atendai/evolution-api:latest
|
||
ports:
|
||
- "8080:8080"
|
||
environment:
|
||
- AUTHENTICATION_API_KEY=sua_chave_global_aqui
|
||
- DATABASE_PROVIDER=postgresql
|
||
- DATABASE_CONNECTION_URI=postgresql://user:pass@host:5432/evolution
|
||
- WEBHOOK_GLOBAL_URL=https://seu-dominio.com/api/webhooks/evolution
|
||
- WEBHOOK_GLOBAL_ENABLED=true
|
||
- WEBHOOK_EVENTS_STATUS_INSTANCE=true
|
||
- WEBHOOK_EVENTS_MESSAGES_UPSERT=true
|
||
- WEBHOOK_EVENTS_SEND_MESSAGE=true
|
||
volumes:
|
||
- evolution_data:/evolution/store
|
||
restart: unless-stopped
|
||
|
||
volumes:
|
||
evolution_data:
|
||
```
|
||
|
||
### Endpoints Necessários
|
||
|
||
```
|
||
Base URL: https://evolution.seudominio.com
|
||
|
||
POST /instance/create → criar instância para o tenant
|
||
GET /instance/connect/{name} → obter QR Code para conectar número
|
||
GET /instance/connectionState/{name} → verificar status da conexão
|
||
POST /message/sendText/{name} → enviar mensagem de texto
|
||
POST /message/sendMedia/{name} → enviar com mídia (opcional)
|
||
DELETE /instance/delete/{name} → remover instância
|
||
```
|
||
|
||
### Payload de Envio
|
||
|
||
```json
|
||
// POST /message/sendText/{instance_name}
|
||
{
|
||
"number": "5516999887766",
|
||
"text": "Olá Ana Clara! 👋\n\nLembrete: você tem sessão amanhã, 21/03, às 14:00 com Dra. Beatriz Costa.\n\n📍 Online via Google Meet\n🔗 https://meet.google.com/abc-defg-hij\n\nPara confirmar, responda OK.\nPara cancelar, responda CANCELAR.\n\nAgência PSI"
|
||
}
|
||
```
|
||
|
||
### Webhook de Status
|
||
|
||
```json
|
||
// POST /api/webhooks/evolution (recebido do Evolution)
|
||
{
|
||
"event": "messages.update",
|
||
"instance": "clinica_abc",
|
||
"data": {
|
||
"key": {
|
||
"remoteJid": "5516999887766@s.whatsapp.net",
|
||
"id": "3EB0A0B6F..."
|
||
},
|
||
"update": {
|
||
"status": 3 // 1=pendente, 2=enviado ao servidor, 3=entregue, 4=lido
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### Credenciais no Supabase
|
||
|
||
Armazenadas na tabela `notification_channels.credentials` como JSONB criptografado:
|
||
|
||
```json
|
||
{
|
||
"api_url": "https://evolution.seudominio.com",
|
||
"api_key": "chave_global_evolution",
|
||
"instance_name": "clinica_dr_beatriz",
|
||
"connected_number": "5516999887766",
|
||
"connection_status": "open"
|
||
}
|
||
```
|
||
|
||
> A criptografia das credenciais usa `pgcrypto` com chave armazenada como
|
||
> variável de ambiente do Supabase (Vault). Veja a função SQL `encrypt_credentials()`.
|
||
|
||
---
|
||
|
||
## 5. Integração API Oficial Meta
|
||
|
||
### Template de Lembrete para Aprovação
|
||
|
||
Nome: `session_reminder_v1`
|
||
Categoria: `UTILITY`
|
||
Idioma: `pt_BR`
|
||
|
||
```
|
||
HEADER: 📋 Lembrete de Sessão
|
||
BODY: Olá {{1}}! Sua sessão com {{2}} está agendada para {{3}} às {{4}}.
|
||
Modalidade: {{5}}
|
||
BUTTONS:
|
||
[quick_reply] ✅ Confirmar presença
|
||
[quick_reply] ❌ Preciso cancelar
|
||
FOOTER: Agência PSI — Tecnologia aplicada à escuta
|
||
```
|
||
|
||
### Envio via Graph API
|
||
|
||
```
|
||
POST https://graph.facebook.com/v19.0/{phone_number_id}/messages
|
||
Authorization: Bearer {access_token}
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"messaging_product": "whatsapp",
|
||
"to": "5516999887766",
|
||
"type": "template",
|
||
"template": {
|
||
"name": "session_reminder_v1",
|
||
"language": { "code": "pt_BR" },
|
||
"components": [
|
||
{
|
||
"type": "body",
|
||
"parameters": [
|
||
{ "type": "text", "text": "Ana Clara" },
|
||
{ "type": "text", "text": "Dra. Beatriz Costa" },
|
||
{ "type": "text", "text": "21/03/2026" },
|
||
{ "type": "text", "text": "14:00" },
|
||
{ "type": "text", "text": "Online" }
|
||
]
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
### Webhook de Status (Meta)
|
||
|
||
```json
|
||
// POST /api/webhooks/meta-whatsapp
|
||
{
|
||
"entry": [{
|
||
"changes": [{
|
||
"value": {
|
||
"statuses": [{
|
||
"id": "wamid.HBgN...",
|
||
"status": "delivered", // sent, delivered, read, failed
|
||
"timestamp": "1711036800",
|
||
"recipient_id": "5516999887766",
|
||
"errors": []
|
||
}]
|
||
}
|
||
}]
|
||
}]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6. Frontend: Telas de Configuração
|
||
|
||
### 6.1 Configuração de Canal (`ConfiguracoesCanaisPage.vue`)
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────┐
|
||
│ 📡 Canais de Notificação │
|
||
├─────────────────────────────────────────────────┤
|
||
│ │
|
||
│ ┌──────────────────────────────────────────┐ │
|
||
│ │ 💬 WhatsApp [Ativo ✅] │ │
|
||
│ │ Provedor: Evolution API │ │
|
||
│ │ Número: +55 16 99988-7766 │ │
|
||
│ │ Status: 🟢 Conectado │ │
|
||
│ │ │ │
|
||
│ │ [Reconectar] [Ver QR Code] [Testar] │ │
|
||
│ └──────────────────────────────────────────┘ │
|
||
│ │
|
||
│ ┌──────────────────────────────────────────┐ │
|
||
│ │ 📧 E-mail [Ativo ✅] │ │
|
||
│ │ Provedor: Resend │ │
|
||
│ │ Remetente: clinica@drbeat... │ │
|
||
│ │ Status: 🟢 Verificado │ │
|
||
│ │ │ │
|
||
│ │ [Configurar SMTP] [Testar] │ │
|
||
│ └──────────────────────────────────────────┘ │
|
||
│ │
|
||
│ ┌──────────────────────────────────────────┐ │
|
||
│ │ 📱 SMS [Inativo ⬜] │ │
|
||
│ │ Não configurado │ │
|
||
│ │ │ │
|
||
│ │ [Ativar] │ │
|
||
│ └──────────────────────────────────────────┘ │
|
||
│ │
|
||
└─────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 6.2 Templates (`ConfiguracoesNotifTemplatesPage.vue`)
|
||
|
||
- Lista de templates por domínio (Sessão, Triagem, Sistema)
|
||
- Preview ao vivo com variáveis mock (já existe no sistema)
|
||
- Cada template pode ser personalizado por canal (email / whatsapp / sms)
|
||
- Herança: se o tenant não customizou, mostra o template global com badge "Padrão"
|
||
|
||
### 6.3 Regras de Envio (`ConfiguracoesNotifRegrasPage.vue`)
|
||
|
||
```
|
||
┌──────────────────────────────────────────────┐
|
||
│ ⏰ Regras de Envio de Lembretes │
|
||
├──────────────────────────────────────────────┤
|
||
│ │
|
||
│ Lembrete de Sessão │
|
||
│ ├── 24 horas antes [WhatsApp ✅] [Email ✅] │
|
||
│ ├── 2 horas antes [WhatsApp ✅] [Email ⬜] │
|
||
│ └── 30 min antes [WhatsApp ⬜] [Email ⬜] │
|
||
│ │
|
||
│ Confirmação de Sessão │
|
||
│ ├── Imediata (ao criar) [Email ✅] │
|
||
│ └── Imediata [WhatsApp ✅] │
|
||
│ │
|
||
│ Cancelamento │
|
||
│ └── Imediata [WhatsApp ✅] [Email ✅]│
|
||
│ │
|
||
│ Boas-vindas (novo paciente) │
|
||
│ └── Imediata [WhatsApp ✅] [Email ✅]│
|
||
│ │
|
||
│ ⚙️ Horário permitido: 08:00 – 20:00 │
|
||
│ 📅 Não enviar: Domingos e feriados │
|
||
│ │
|
||
│ [Salvar configurações] │
|
||
└──────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 6.4 Logs de Envio (`ConfiguracoesNotifLogsPage.vue`)
|
||
|
||
- Tabela paginada com filtros por canal, status, período
|
||
- Detalhes por envio: template usado, variáveis resolvidas, resposta do provedor
|
||
- Estatísticas: total enviado, entregue, lido, falhou (últimos 30 dias)
|
||
- Export CSV
|
||
|
||
### 6.5 Opt-out pelo Paciente
|
||
|
||
- **WhatsApp:** Paciente responde "SAIR" → webhook captura → atualiza `notification_preferences`
|
||
- **Email:** Link "Cancelar inscrição" no rodapé → página pública de opt-out
|
||
- **Portal do paciente (futuro):** Toggle na área logada do paciente
|
||
|
||
---
|
||
|
||
## 7. LGPD e Boas Práticas
|
||
|
||
### Coleta de Consentimento
|
||
|
||
1. **Cadastro externo** (CadastroPacienteExterno.vue): já tem checkbox LGPD
|
||
2. **Cadastro pelo terapeuta**: adicionar checkbox "Paciente autoriza receber lembretes por WhatsApp/E-mail"
|
||
3. **Primeiro lembrete**: incluir mensagem "Responda SAIR a qualquer momento para parar de receber mensagens"
|
||
|
||
### Armazenamento Seguro
|
||
|
||
- Telefone do paciente: armazenado na tabela `patients.telefone` (já existe)
|
||
- Credenciais do provedor: `notification_channels.credentials` criptografado com `pgcrypto`
|
||
- Chave de criptografia: Supabase Vault (variável de ambiente, nunca no código)
|
||
|
||
### Retenção de Logs
|
||
|
||
- `notification_logs`: reter por **2 anos** (exigência legal para comprovação de comunicação)
|
||
- `notification_queue`: limpar itens processados após **90 dias** (via pg_cron)
|
||
- `notification_preferences`: manter enquanto o paciente estiver ativo
|
||
|
||
### Opt-out Imediato
|
||
|
||
- Resposta "SAIR" no WhatsApp → webhook → `notification_preferences.whatsapp_opt_in = false`
|
||
- Efeito imediato: todas as mensagens pendentes na fila para aquele paciente são canceladas
|
||
- Trigger SQL: ao atualizar opt-out, cancela itens pendentes na queue
|
||
|
||
---
|
||
|
||
## 8. Diagrama do Fluxo Completo
|
||
|
||
```
|
||
┌──────────────┐
|
||
│ agenda_eventos│ ← terapeuta cria/edita sessão
|
||
└──────┬───────┘
|
||
│
|
||
▼
|
||
┌──────────────────────────────────────┐
|
||
│ pg_cron: populate_notification_queue │ ← roda a cada 5 min
|
||
│ │
|
||
│ 1. Busca sessões com inicio_em │
|
||
│ entre agora e +48h │
|
||
│ 2. Cruza com notification_schedules │
|
||
│ (ex: 24h antes, 2h antes) │
|
||
│ 3. Verifica: │
|
||
│ - notification_preferences (opt-in)│
|
||
│ - notification_channels (canal ativo)│
|
||
│ - profiles.notify_reminders │
|
||
│ - agenda_eventos.status ≠ cancelado│
|
||
│ 4. Gera idempotency_key │
|
||
│ 5. INSERT INTO notification_queue │
|
||
│ ON CONFLICT DO NOTHING │
|
||
└──────────────┬───────────────────────┘
|
||
│
|
||
▼
|
||
┌──────────────────────────────────────┐
|
||
│ pg_cron: chama Edge Function │ ← roda a cada 5 min (offset 2min)
|
||
│ POST /functions/v1/process-notif-queue│
|
||
└──────────────┬───────────────────────┘
|
||
│
|
||
▼
|
||
┌──────────────────────────────────────┐
|
||
│ Edge Function: process-notif-queue │
|
||
│ │
|
||
│ 1. SELECT ... FROM notification_queue│
|
||
│ WHERE status = 'pendente' │
|
||
│ AND scheduled_at <= now() │
|
||
│ LIMIT 50 │
|
||
│ FOR UPDATE SKIP LOCKED │
|
||
│ │
|
||
│ 2. Para cada item: │
|
||
│ a. Marca 'processando' │
|
||
│ b. Resolve template + variáveis │
|
||
│ c. Despacha para provedor: │
|
||
│ ┌──────────────────────┐ │
|
||
│ │ channel = 'whatsapp' │ │
|
||
│ │ → Evolution API │ │
|
||
│ │ ou Meta Graph API │ │
|
||
│ ├──────────────────────┤ │
|
||
│ │ channel = 'email' │ │
|
||
│ │ → Resend / SMTP │ │
|
||
│ ├──────────────────────┤ │
|
||
│ │ channel = 'sms' │ │
|
||
│ │ → Zenvia / Twilio │ │
|
||
│ └──────────────────────┘ │
|
||
│ d. Atualiza status │
|
||
│ e. INSERT notification_logs │
|
||
│ │
|
||
│ 3. Itens com falha: │
|
||
│ attempts += 1 │
|
||
│ next_retry_at = exponential │
|
||
│ status = attempts >= 5 │
|
||
│ ? 'falhou' : 'pendente' │
|
||
└──────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌──────────────────────────────────────┐
|
||
│ Webhook Handler │
|
||
│ POST /functions/v1/notif-webhook │
|
||
│ │
|
||
│ Recebe status do provedor: │
|
||
│ - Evolution: messages.update │
|
||
│ - Meta: webhook de status │
|
||
│ - Email: bounce/delivery events │
|
||
│ │
|
||
│ Atualiza notification_logs: │
|
||
│ - delivered_at, read_at, failed_at │
|
||
│ - provider_status, provider_response │
|
||
│ │
|
||
│ Se resposta = "SAIR": │
|
||
│ → UPDATE notification_preferences │
|
||
│ SET whatsapp_opt_in = false │
|
||
│ → CANCEL pendentes na queue │
|
||
└──────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 9. Checklist de Produção
|
||
|
||
### Infraestrutura
|
||
- [ ] Subir Evolution API (Docker) em VPS dedicada
|
||
- [ ] Configurar domínio + SSL para Evolution API
|
||
- [ ] Configurar Supabase Vault com chave de criptografia
|
||
- [ ] Habilitar extensão `pgcrypto` no Supabase
|
||
- [ ] Habilitar extensão `pg_cron` no Supabase
|
||
- [ ] Configurar DNS para webhook (ex: `webhooks.agenciapsi.com.br`)
|
||
|
||
### Banco de Dados
|
||
- [ ] Rodar migration: tabelas de notificação
|
||
- [ ] Rodar migration: functions PL/pgSQL (populate queue, encrypt/decrypt)
|
||
- [ ] Configurar pg_cron jobs (populate + process)
|
||
- [ ] Verificar RLS policies em todas as tabelas
|
||
- [ ] Seed: notification_schedules padrão (24h, 2h)
|
||
- [ ] Seed: notification_templates padrão (whatsapp + sms)
|
||
|
||
### Edge Functions
|
||
- [ ] Deploy: `process-notif-queue`
|
||
- [ ] Deploy: `notif-webhook`
|
||
- [ ] Configurar secrets: `EVOLUTION_API_KEY`, `ENCRYPTION_KEY`
|
||
- [ ] Testar com payload simulado
|
||
|
||
### Frontend
|
||
- [ ] Tela de configuração de canais
|
||
- [ ] Tela de templates (estender ConfiguracoesEmailTemplatesPage existente)
|
||
- [ ] Tela de regras de envio
|
||
- [ ] Tela de logs
|
||
- [ ] Adicionar consentimento no cadastro de paciente
|
||
- [ ] Adicionar opt-out no rodapé de emails
|
||
|
||
### Testes
|
||
- [ ] Teste E2E: criar sessão → verificar queue populada → verificar envio
|
||
- [ ] Teste: opt-out WhatsApp → verificar cancelamento na queue
|
||
- [ ] Teste: retry após falha → verificar exponential backoff
|
||
- [ ] Teste: idempotency → rodar populate 2x → verificar sem duplicatas
|
||
- [ ] Teste: tenant isolation → verificar RLS
|
||
- [ ] Teste de carga: 1000 mensagens na queue → medir throughput
|
||
|
||
### Monitoramento
|
||
- [ ] Alerta se queue > 500 itens pendentes
|
||
- [ ] Alerta se taxa de falha > 10% em 1h
|
||
- [ ] Dashboard de métricas (envios/dia, taxa de entrega, tempo médio de processamento)
|
||
- [ ] Log de erros no Supabase Logs
|
||
|
||
---
|
||
|
||
*Documento gerado como parte da arquitetura do sistema Agência PSI.*
|
||
*As implementações SQL e JavaScript estão nos arquivos separados referenciados abaixo.*
|