Files
2026-08-14 17:35:24 -03:00

90 lines
4.5 KiB
Markdown

# Etapa 7/8 — Motor de Alertas (BullMQ)
## Contexto do projeto
Continuação do **SeConectai Vendas**. Já existem: setup, auth com RLS,
painel master completo, integrações Evolution API/e-mail com serviço
central de envio (`AlertDispatchService`), CRUD de customers/produtos, e
o módulo completo de vendas/parcelas/dashboards.
Esta é a etapa que **automatiza** o que até agora só existia como
endpoint manual: verificação de vencimentos e disparo de alertas, nos
dois níveis do sistema (master→tenant e tenant→customer).
## Tarefas
1. **Infraestrutura de filas**
- Configurar `@nestjs/bullmq` conectado ao Redis já disponível no
`docker-compose`.
- Criar duas filas: `alertas-mensalidade` (master→tenant) e
`alertas-cobranca` (tenant→customer).
- Criar um processo/worker separado do processo web principal (pode
ser o mesmo container em dev, mas documentar como se separaria em
produção — ex: `main.ts` vs `worker.ts`, ou um segundo serviço no
`docker-compose` chamado `worker` rodando o mesmo build com um
comando diferente).
2. **Módulo `alert-templates`**
- CRUD de `alert_templates`:
- Nível master (`MasterGuard`): templates padrão da plataforma
(`tenant_id = null`), tipo `mensalidade_tenant`.
- Nível tenant (`TenantGuard`, role `tenant_admin`): templates
próprios, tipo `cobranca_cliente` (e outros tipos que fizerem
sentido, ex: `venda_confirmada`, `lembrete_vencimento`). Se o
tenant não tiver template próprio para um tipo/canal, usar o
template padrão do master como fallback.
- Implementar renderização de placeholders (`{{nome}}`, `{{valor}}`,
`{{vencimento}}`, etc — listar todos os placeholders suportados por
tipo de template em um arquivo de constantes, documentado).
3. **Job diário: `mensalidade_tenant`**
- Cron (ex: todo dia às 8h, horário configurável via env) que:
1. Roda a lógica equivalente ao endpoint
`POST /master/faturas/atualizar-atrasadas` (marca faturas
vencidas como `atrasado`, tenant vira `inadimplente`).
2. Para toda `tenant_faturas` com vencimento em N dias (configurável,
ex: 3 dias antes) ou já vencida e ainda sem alerta hoje, enfileira
um job na fila `alertas-mensalidade` com os dados necessários.
- Worker da fila `alertas-mensalidade`: busca template
(`mensalidade_tenant`, canal `whatsapp` e/ou `email` conforme
configurado), renderiza, chama `AlertDispatchService`, grava
resultado em `alert_logs`, atualiza `tenant_faturas.alerta_enviado_em`.
4. **Job diário: `cobranca_cliente`** (por tenant)
- Mesmo padrão do job acima, mas iterando `sale_payments` de todos os
tenants ativos: marca atrasadas, enfileira alerta para parcelas
próximas do vencimento ou já atrasadas (respeitando
`alerta_enviado_em` para não notificar duas vezes no mesmo dia).
- Usa a instância WhatsApp e config de e-mail **do tenant dono da
venda**, não a do master.
5. **Idempotência e resiliência**
- Garantir que um mesmo job não dispare o mesmo alerta duas vezes
(checar `alerta_enviado_em` antes de enfileirar, e novamente antes
de enviar, dentro do worker, para lidar com concorrência).
- Configurar retry com backoff exponencial no BullMQ para falhas
transitórias de envio (ex: Evolution API fora do ar), com limite de
tentativas (ex: 3) e log do erro final em `alert_logs.erro`.
- Endpoint de observabilidade: `GET /master/alertas/logs` e
`GET /tenant/alertas/logs` para consultar `alert_logs` com filtros
(canal, status, período).
## Critérios de aceite
- [ ] Worker processa as duas filas de forma independente do processo
web (documentado como escalar workers separadamente)
- [ ] Template do tenant sobrepõe o padrão do master quando existe; usa
fallback quando não existe
- [ ] Placeholders são substituídos corretamente na renderização
- [ ] Job diário marca faturas/parcelas atrasadas corretamente
- [ ] Alerta de mensalidade chega no WhatsApp/e-mail do tenant em teste
controlado (vencimento simulado para "hoje")
- [ ] Alerta de cobrança chega no WhatsApp/e-mail do customer, usando a
instância/config do tenant correto (testar com 2 tenants
diferentes)
- [ ] Mesmo alerta não é disparado duas vezes no mesmo dia (testar
rodando o job duas vezes seguidas)
- [ ] Falha de envio é reprocessada com retry e, após esgotar tentativas,
fica registrada em `alert_logs` com erro
- [ ] Endpoints de consulta de logs funcionam com os filtros esperados