Notificacoes
Notificacoes
O sistema de notificacoes da plataforma AgrSis envia alertas por 3 canais diferentes, com controle granular tanto a nivel global (admin) quanto individual (usuario).
Canais de Envio
| Canal | Identificador | Descricao |
|---|---|---|
| Sistema (in-app) | in_app | Notificacao interna visivel no painel do usuario |
email | Email enviado via SMTP (enfileirado na queue) | |
whatsapp | Mensagem via Z-API (enfileirada com delay anti-spam) |
Tipos de Notificacao
O enum NotificationType define todos os tipos de notificacao organizados por contexto.
Notificacoes do Produtor
| Tipo | Valor | Titulo |
|---|---|---|
| Proposta recebida | proposal_received | Nova proposta recebida |
| Proposta aceita | proposal_accepted | Proposta aceita |
| Proposta rejeitada | proposal_rejected | Proposta rejeitada |
| Proposta cancelada | proposal_cancelled | Proposta cancelada |
| Prazo proximo | order_deadline_approaching | Prazo proximo do vencimento |
| Cotacao expirada | order_expired | Cotacao expirada |
| Cotacao em analise | order_in_analysis | Cotacao em analise |
| Mensagem recebida | message_received | Nova mensagem |
Notificacoes do Fornecedor
| Tipo | Valor | Titulo |
|---|---|---|
| Nova cotacao | new_order_available | Nova cotacao disponivel |
| Cotacao atualizada | order_updated | Cotacao atualizada |
| Proposta visualizada | proposal_viewed | Proposta visualizada |
| Proposta aceita | your_proposal_accepted | Sua proposta foi aceita! |
| Confirmacao pendente | award_pending_confirmation | Confirmacao de proposta vencedora |
| Negociacao confirmada | award_confirmed | Negociacao confirmada |
| Vencedora recusada | award_declined | Proposta vencedora recusada |
| Confirmacao expirada | award_confirmation_expired | Confirmacao da vencedora expirada |
| Prazo prorrogado | analysis_deadline_extended | Prazo de analise prorrogado |
| Proposta rejeitada | your_proposal_rejected | Proposta rejeitada |
| Proposta perdeu | your_proposal_lost | Outra proposta foi aceita |
| Cotacao expirada | order_deadline_expired | Cotacao expirada |
| Resposta recebida | message_response | Nova resposta |
Notificacoes de Avaliacao e Entrega
| Tipo | Valor | Titulo |
|---|---|---|
| Avaliacao disponivel | review_negotiation_available | Avaliacao disponivel |
| Lembrete avaliacao | review_negotiation_reminder | Lembrete de avaliacao |
| Entrega pendente | delivery_pending | Entrega pendente |
| Lembrete entrega | delivery_reminder | Lembrete de entrega |
| Entrega marcada | delivery_marked | Entrega realizada |
| Lembrete recebimento | receipt_reminder | Confirme o recebimento |
| Avaliar entrega | delivery_review_available | Avalie a entrega |
| Lembrete avaliar entrega | delivery_review_reminder | Lembrete: avalie a entrega |
Notificacoes de Assinatura e Pagamento
| Tipo | Valor | Titulo |
|---|---|---|
| Trial expirando | trial_expiring | Periodo de teste expirando |
| Trial expirou | trial_expired | Periodo de teste expirou |
| Pagamento confirmado | payment_confirmed | Pagamento confirmado |
| Pagamento atrasado | payment_overdue | Pagamento atrasado |
| Assinatura renovada | subscription_renewed | Assinatura renovada |
| Cobranca gerada | subscription_charge_created | Cobranca gerada |
Notificacoes do Admin
| Tipo | Valor | Titulo |
|---|---|---|
| Novo produtor | admin_new_producer_registered | Novo produtor cadastrado |
| Novo fornecedor | admin_new_supplier_registered | Novo fornecedor cadastrado |
Destinatario por Tipo
Alguns tipos de notificacao sao enviados para multiplos perfis. Os metodos isForProducer(), isForSupplier() e isForAdmin() no enum definem quais tipos se aplicam a cada perfil.
Notificacoes compartilhadas entre perfis:
award_pending_confirmation,award_confirmed,award_declined,award_confirmation_expired,analysis_deadline_extended-- enviadas tanto para produtor quanto fornecedor e adminreview_negotiation_available,review_negotiation_reminder,delivery_review_available,delivery_review_reminder-- enviadas para produtor e fornecedororder_in_analysis-- enviada para produtor e fornecedor
Configuracao Global (Admin)
O model NotificationChannelConfig (tabela notification_channel_config) permite ao admin habilitar ou desabilitar canais por tipo de notificacao.
| Campo | Tipo | Descricao |
|---|---|---|
notification_type | string | Tipo da notificacao (valor do enum) |
in_app | boolean | Canal in-app habilitado |
email | boolean | Canal email habilitado |
whatsapp | boolean | Canal WhatsApp habilitado |
Se um canal esta desabilitado globalmente, ele nao sera utilizado para nenhum usuario, independente das preferencias individuais.
Preferencias Individuais do Usuario
O model NotificationSetting (tabela notification_settings) armazena as preferencias de cada usuario por tipo de notificacao.
| Campo | Tipo | Descricao |
|---|---|---|
user_id | FK | Usuario |
notification_type | enum | Tipo da notificacao |
in_app | boolean | Receber no sistema |
email | boolean | Receber por email |
whatsapp | boolean | Receber por WhatsApp |
Valores padrao
Quando um usuario recebe uma notificacao de um tipo que ainda nao tem configuracao, o sistema cria automaticamente via getOrCreateDefault():
| Canal | Padrao |
|---|---|
| in_app | true (habilitado) |
true (habilitado) | |
false (desabilitado) |
Metodos disponiveis
shouldSendInApp()-- verifica se in-app esta habilitadoshouldSendEmail()-- verifica se email esta habilitadoshouldSendWhatsApp()-- verifica se WhatsApp esta habilitadogetEnabledChannels()-- retorna array com canais habilitadostoggleChannel(channel, enabled)-- altera um canal especifico
Fluxo de Envio
O NotificationService coordena o envio em todos os canais com a seguinte logica:
1. Verificar preferencia do usuario (NotificationSetting)
└── Se in_app desabilitado → nao cria notificacao
2. Criar registro Notification (in-app)
3. Verificar configuracao global (NotificationChannelConfig)
4. Para cada canal habilitado globalmente:
└── Verificar preferencia do usuario
├── Email habilitado? → enfileirar NotificationMail
└── WhatsApp habilitado? → enfileirar SendWhatsAppNotificationJob
Idempotencia
Para evitar notificacoes duplicadas, o sistema verifica se ja existe uma notificacao com o mesmo type e proposal_uuid antes de criar. Essa verificacao e feita especificamente para PROPOSAL_RECEIVED.
Canal Email
O email e enviado via NotificationMail (Mailable que implementa ShouldQueue).
Estrutura do email
| Componente | Descricao |
|---|---|
| Assunto | "AgrSis - {titulo}" com codigo de referencia opcional |
| Saudacao | Nome do destinatario |
| Mensagem | Texto da notificacao |
| Detalhes | Tabela com metadados (codigo da cotacao, proposta, prazo, valor, raio) |
| Botao de acao | Link para a plataforma (URL absoluta) |
| Link de opt-out | Link para /profile#notificacoes para gerenciar preferencias |
Resolucao de URLs
O NotificationService resolve URLs relativas para absolutas baseado no perfil do usuario:
| Perfil | Base URL |
|---|---|
| Admin | https://admin.agrsis.com |
| Supplier | https://supplier.agrsis.com |
| Producer (padrao) | https://producer.agrsis.com |
Canal WhatsApp
O envio e feito via SendWhatsAppNotificationJob, um Job que implementa ShouldQueue.
Configuracao do Job
| Parametro | Valor |
|---|---|
| Tentativas | 3 ($tries = 3) |
| Backoff | 10 segundos entre tentativas |
| Servico | WhatsAppService (integra com Z-API) |
Anti-spam (Delay Incremental)
O NotificationService implementa um mecanismo anti-spam para evitar envio massivo de mensagens WhatsApp em um unico request:
private static int $whatsappDispatchCount = 0;
// Cada mensagem adiciona 3 segundos de delay
$delay = self::$whatsappDispatchCount * 3;
self::$whatsappDispatchCount++;
Exemplo pratico: ao publicar uma cotacao que notifica 10 fornecedores:
- Fornecedor 1: enviado imediatamente (delay 0s)
- Fornecedor 2: enviado apos 3s
- Fornecedor 3: enviado apos 6s
- ...
- Fornecedor 10: enviado apos 27s
O contador e estatico e se reseta a cada request HTTP. Isso evita que a Z-API bloqueie o numero por envio excessivo.
Notificacoes In-App
Operacoes disponiveis
| Metodo | Descricao |
|---|---|
markAsRead(uuid, userId) | Marcar uma notificacao como lida |
markAllAsRead(userId) | Marcar todas como lidas |
getUnreadCount(userId) | Contar nao lidas |
Estrutura do registro (tabela notifications)
| Campo | Tipo | Descricao |
|---|---|---|
uuid | UUID | Identificador unico |
user_id | FK | Destinatario |
type | string | Tipo (valor do enum) |
title | string | Titulo |
message | string | Mensagem |
data | JSON | Metadados (uuids, codigos, prazos) |
link | string | URL relativa para acao |
read | boolean | Lida ou nao |
read_at | datetime | Data da leitura |
Cenarios de Notificacao
Publicacao de cotacao
Quando uma cotacao e publicada, o NotificationService::notifyOrderPublished() busca fornecedores dentro do raio de fornecimento usando calculo de distancia por coordenadas (formula de Haversine). Apenas fornecedores com:
- Endereco ativo com latitude/longitude
- Status ativo (supplier e user)
- Dentro do raio definido na cotacao
Proposta aceita
O notifyProposalAccepted() notifica:
- O fornecedor vencedor com
YOUR_PROPOSAL_ACCEPTED - Todos os outros fornecedores que enviaram proposta com
YOUR_PROPOSAL_LOST
Cancelamento/Revogacao de cotacao
O notifyOrderStatusChangedForSuppliers() notifica todos os fornecedores participantes, incluindo motivo e observacoes quando disponveis.
Adjudicacao (Award)
O fluxo de adjudicacao gera multiplas notificacoes:
notifyAwardPendingConfirmation()-- notifica vencedor, produtor e demais participantesnotifyAwardConfirmed()-- notifica produtor, vencedor e demais (encerramento)notifyAwardDeclined()-- notifica todos e reabre prazo de analisenotifyAwardConfirmationExpired()-- notifica todos sobre expiracao do prazo