181 lines
7.5 KiB
JavaScript
181 lines
7.5 KiB
JavaScript
'use strict'
|
|
|
|
const Notification = require('../models/Notification')
|
|
const WhatsappCore = require('./whatsappService')
|
|
const { query } = require('../database/postgres')
|
|
|
|
const BASE_URL = process.env.APP_URL || 'http://localhost:4001'
|
|
|
|
class NotificationService {
|
|
|
|
// ── Ganhador ───────────────────────────────────────────────────────────────
|
|
|
|
static async notifyWinner(user_cpf, userName, userPhone, raffleName, ticketNumber) {
|
|
try {
|
|
await Notification.create({
|
|
user_cpf,
|
|
title: '🏆 Parabéns! Você ganhou!',
|
|
message: `Seu ticket #${ticketNumber} foi sorteado no prêmio "${raffleName}". Clique aqui para reivindicar.`,
|
|
type: 'win',
|
|
})
|
|
if (userPhone?.length >= 10) {
|
|
await WhatsappCore.sendNotification({
|
|
userId: null, phone: userPhone, type: 'winner', userCpf: user_cpf,
|
|
variables: { nome: userName || 'Cliente', numero: ticketNumber, raffleName, link: `${BASE_URL}/meus-premios` },
|
|
}).catch(console.error)
|
|
}
|
|
} catch (err) {
|
|
console.error('[Notificações] notifyWinner:', err.message)
|
|
}
|
|
}
|
|
|
|
// ── Boas-vindas ────────────────────────────────────────────────────────────
|
|
|
|
static async notifyWelcome(userId, userName, userPhone, userCpf) {
|
|
try {
|
|
if (userPhone?.length >= 10) {
|
|
await WhatsappCore.sendNotification({
|
|
userId, phone: userPhone, type: 'welcome', userCpf,
|
|
variables: { nome: userName || 'Cliente', link: `${BASE_URL}/area-do-usuario` },
|
|
}).catch(console.error)
|
|
}
|
|
} catch (err) {
|
|
console.error('[Notificações] notifyWelcome:', err.message)
|
|
}
|
|
}
|
|
|
|
// ── Pós-sorteio / Resgate ──────────────────────────────────────────────────
|
|
|
|
static async notifyClaimCreated(userPhone, userCpf, userName, raffleName, ticketNumber) {
|
|
try {
|
|
if (userPhone?.length >= 10) {
|
|
await WhatsappCore.sendNotification({
|
|
userId: null, phone: userPhone, type: 'claim_received', userCpf,
|
|
variables: { nome: userName || 'Cliente', raffleName, numero: ticketNumber },
|
|
}).catch(console.error)
|
|
}
|
|
} catch (err) {
|
|
console.error('[Notificações] notifyClaimCreated:', err.message)
|
|
}
|
|
}
|
|
|
|
static async notifyClaimApproved(userPhone, userCpf, userName, raffleName) {
|
|
try {
|
|
if (userPhone?.length >= 10) {
|
|
await WhatsappCore.sendNotification({
|
|
userId: null, phone: userPhone, type: 'claim_approved', userCpf,
|
|
variables: { nome: userName || 'Cliente', raffleName },
|
|
}).catch(console.error)
|
|
}
|
|
} catch (err) {
|
|
console.error('[Notificações] notifyClaimApproved:', err.message)
|
|
}
|
|
}
|
|
|
|
static async notifyClaimDelivered(userPhone, userCpf, userName, raffleName) {
|
|
try {
|
|
if (userPhone?.length >= 10) {
|
|
await WhatsappCore.sendNotification({
|
|
userId: null, phone: userPhone, type: 'claim_delivered', userCpf,
|
|
variables: { nome: userName || 'Cliente', raffleName },
|
|
}).catch(console.error)
|
|
}
|
|
} catch (err) {
|
|
console.error('[Notificações] notifyClaimDelivered:', err.message)
|
|
}
|
|
}
|
|
|
|
// ── Clube de Benefícios ────────────────────────────────────────────────────
|
|
// Todas as notificações de clube estão em services/clubNotificationService.js
|
|
// clubController.js usa ClubNotificationService diretamente.
|
|
|
|
// ── Redefinição de senha ───────────────────────────────────────────────────
|
|
|
|
static async notifyPasswordReset(userId, userName, userPhone, userCpf, link) {
|
|
try {
|
|
if (!userPhone || userPhone.length < 10) return
|
|
await WhatsappCore.sendNotification({
|
|
userId, phone: userPhone, type: 'password_reset', userCpf,
|
|
variables: { nome: userName || 'Cliente', link },
|
|
}).catch(console.error)
|
|
} catch (err) {
|
|
console.error('[Notificações] notifyPasswordReset:', err.message)
|
|
}
|
|
}
|
|
|
|
// ── Avisos de segurança ────────────────────────────────────────────────────
|
|
|
|
static async notifySecurityLogin(userId, userName, userPhone, userCpf) {
|
|
try {
|
|
if (!userPhone || userPhone.length < 10) return
|
|
const now = new Date()
|
|
const data = now.toLocaleDateString('pt-BR')
|
|
const hora = now.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' })
|
|
await WhatsappCore.sendNotification({
|
|
userId, phone: userPhone, type: 'security_login', userCpf,
|
|
variables: { nome: userName || 'Cliente', data, hora },
|
|
}).catch(console.error)
|
|
} catch (err) {
|
|
console.error('[Notificações] notifySecurityLogin:', err.message)
|
|
}
|
|
}
|
|
|
|
// ── Queda de preço (broadcast) ─────────────────────────────────────────────
|
|
|
|
static async notifyPriceDrop(raffleId, raffleName, newPrice) {
|
|
try {
|
|
// Todos os usuários opt-in com telefone que ainda não compraram este sorteio
|
|
const rows = await query(
|
|
`SELECT DISTINCT u.telefone, u.cpf, u.nome
|
|
FROM users u
|
|
WHERE u.telefone IS NOT NULL
|
|
AND u.whatsapp_optin = TRUE
|
|
AND u.cpf NOT IN (
|
|
SELECT DISTINCT t.usuario_cpf FROM tickets t
|
|
WHERE t.raffle_id = $1 AND t.status = 'sold'
|
|
)`,
|
|
[raffleId]
|
|
)
|
|
const link = `${BASE_URL}/sorteio`
|
|
for (const u of (rows ?? [])) {
|
|
WhatsappCore.sendNotification({
|
|
userId: null, phone: u.telefone, type: 'price_drop', userCpf: u.cpf,
|
|
variables: { raffleName, novo_valor: Number(newPrice).toFixed(2), link },
|
|
}).catch(() => {})
|
|
}
|
|
console.log(`[Notificações] price_drop enviado para ${(rows ?? []).length} usuário(s) — rifa #${raffleId}`)
|
|
} catch (err) {
|
|
console.error('[Notificações] notifyPriceDrop:', err.message)
|
|
}
|
|
}
|
|
|
|
// ── Queda de preço localizada (reservas pendentes de pagamento) ────────────
|
|
|
|
static async notifyPriceDropLocal(raffleId, raffleName, newPrice) {
|
|
try {
|
|
const rows = await query(
|
|
`SELECT DISTINCT t.usuario_telefone, t.usuario_cpf, t.usuario_nome
|
|
FROM tickets t
|
|
JOIN orders o ON o.id = t.order_id
|
|
WHERE t.raffle_id = $1
|
|
AND t.status = 'reserved'
|
|
AND o.status = 'pending'
|
|
AND t.usuario_telefone IS NOT NULL`,
|
|
[raffleId]
|
|
)
|
|
const link = `${BASE_URL}/area-do-usuario`
|
|
for (const t of (rows ?? [])) {
|
|
WhatsappCore.sendNotification({
|
|
userId: null, phone: t.usuario_telefone, type: 'price_drop_local', userCpf: t.usuario_cpf,
|
|
variables: { raffleName, novo_valor: Number(newPrice).toFixed(2), link },
|
|
}).catch(() => {})
|
|
}
|
|
console.log(`[Notificações] price_drop_local enviado para ${(rows ?? []).length} reserva(s) — rifa #${raffleId}`)
|
|
} catch (err) {
|
|
console.error('[Notificações] notifyPriceDropLocal:', err.message)
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = NotificationService
|