FEATURE: Implementado serviço de notificação para Admin e colaboradores, Adicionado validador de ticket updade, e modificado email para envio bonito
This commit is contained in:
parent
ad28159185
commit
53bdd1c5a5
@ -45,11 +45,28 @@ async function insertTicket(ticketData) {
|
||||
const [result] = await db.query(query, values)
|
||||
return result.insertId
|
||||
} catch (err) {
|
||||
logError('Erro ao inserir ticket no GLPI', err)
|
||||
logError('[GLPI][REPOSITORY]Erro ao inserir ticket no GLPI', err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async function checkTicketUpdateDate(glpiTicketId) {
|
||||
const query = `
|
||||
SELECT date_mod
|
||||
FROM glpi_tickets
|
||||
WHERE id = ?
|
||||
`
|
||||
|
||||
try {
|
||||
const [rows] = await db.query(query, [glpiTicketId])
|
||||
return rows[0]
|
||||
} catch (err) {
|
||||
logError('[GLPI][REPOSITORY]Erro ao verificar data de atualização do ticket no GLPI', err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
insertTicket
|
||||
insertTicket,
|
||||
checkTicketUpdateDate
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ function buildStatus(kind, type) {
|
||||
/**
|
||||
* Verifica se a notificaÇõÇœo jÇ foi enviada com sucesso
|
||||
*/
|
||||
async function notificationAlreadySentFunc(hubsoftTicketId, type = 'func') {
|
||||
async function notificationAlreadySent(hubsoftTicketId, type = 'func') {
|
||||
const status = buildStatus('sent', type);
|
||||
const query = `
|
||||
SELECT 1
|
||||
@ -38,12 +38,12 @@ async function notificationAlreadySentFunc(hubsoftTicketId, type = 'func') {
|
||||
/**
|
||||
* Marca tickets como pendente de notificaÇõÇœo
|
||||
*/
|
||||
async function markNotificationsAsPendingFunc(hubsoftTicketIds, type = 'func') {
|
||||
async function markNotificationsAsPending(hubsoftTicketIds, type = 'func') {
|
||||
if (!hubsoftTicketIds || hubsoftTicketIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const status = buildStatus('pending', type);
|
||||
const status = buildStatus('pending', type); // Define o status como 'pending_func' ou 'pending_adm'
|
||||
const query = `
|
||||
INSERT INTO watchdog_notifications (ticket_id, notified_at, status)
|
||||
SELECT
|
||||
@ -68,7 +68,7 @@ async function markNotificationsAsPendingFunc(hubsoftTicketIds, type = 'func') {
|
||||
/**
|
||||
* Marca tickets como notificados com sucesso
|
||||
*/
|
||||
async function markNotificationsAsSentFunc(hubsoftTicketIds, type = 'func') {
|
||||
async function markNotificationsAsSent(hubsoftTicketIds, type = 'func') {
|
||||
const ids = (hubsoftTicketIds || [])
|
||||
.map(id => Number(id))
|
||||
.filter(Number.isFinite);
|
||||
@ -99,7 +99,7 @@ async function markNotificationsAsSentFunc(hubsoftTicketIds, type = 'func') {
|
||||
/**
|
||||
* Marca tickets como falha de notificaÇõÇœo
|
||||
*/
|
||||
async function markNotificationsAsFailedFunc(hubsoftTicketIds, type = 'func') {
|
||||
async function markNotificationsAsFailed(hubsoftTicketIds, type = 'func') {
|
||||
const ids = (hubsoftTicketIds || [])
|
||||
.map(id => Number(id))
|
||||
.filter(Number.isFinite);
|
||||
@ -127,8 +127,39 @@ async function markNotificationsAsFailedFunc(hubsoftTicketIds, type = 'func') {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marca tickets como fluxo concluido
|
||||
*/
|
||||
async function markNotificationsAsCompleted(hubsoftTicketIds, type = 'func') {
|
||||
const ids = (hubsoftTicketIds || [])
|
||||
.map(id => Number(id))
|
||||
.filter(Number.isFinite);
|
||||
|
||||
async function getPendingTicketsForNotificationFunc(type = 'func') {
|
||||
if (ids.length === 0) return;
|
||||
|
||||
const status = buildStatus('completed', type);
|
||||
const query = `
|
||||
INSERT INTO watchdog_notifications (ticket_id, notified_at, status)
|
||||
SELECT
|
||||
unnest($1::bigint[]),
|
||||
NOW(),
|
||||
$2
|
||||
ON CONFLICT (ticket_id)
|
||||
DO UPDATE SET
|
||||
notified_at = EXCLUDED.notified_at,
|
||||
status = $2;
|
||||
`;
|
||||
|
||||
try {
|
||||
await db.query(query, [ids, status]);
|
||||
} catch (error) {
|
||||
logError('[WATCHDOG][REPOSITORY] Erro ao marcar notificaÇÎÇæÇÎÇÝes como concluido', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function getPendingTicketsForNotification(type = 'func') {
|
||||
const status = buildStatus('pending', type);
|
||||
const query = `
|
||||
SELECT
|
||||
@ -154,10 +185,51 @@ async function getPendingTicketsForNotificationFunc(type = 'func') {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Busca chamados com status sent_func com notified_at anterior a thresholdDate
|
||||
*/
|
||||
|
||||
async function getSentNotificationsBefore(thresholdDate, type = 'func') {
|
||||
const status = buildStatus('sent', type);
|
||||
|
||||
const query = `
|
||||
SELECT
|
||||
wn.ticket_id AS hubsoft_ticket_id,
|
||||
ht.protocolo_hub as protocolo_hub,
|
||||
ht.ticket_mundiale AS ticket_mundiale,
|
||||
wn.notified_at AS closed_at,
|
||||
sd.glpi_ticket_id AS glpi_ticket_id
|
||||
FROM watchdog_notifications wn
|
||||
INNER JOIN sync_data sd
|
||||
ON wn.ticket_id = sd.hubsoft_ticket_id
|
||||
INNER JOIN hubsoft_tickets ht
|
||||
ON wn.ticket_id = ht.id_atendimento
|
||||
WHERE wn.status = $1 AND wn.notified_at > $2;
|
||||
`;
|
||||
//
|
||||
|
||||
|
||||
try {
|
||||
const { rows } = await db.query(query, [status, thresholdDate]);
|
||||
return rows;
|
||||
} catch (error) {
|
||||
logError('Erro ao buscar notificaÇõÇœes enviadas antes da data limite', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function getTicketsPendingResponse(sinceMinutes = 30, type = 'func') {
|
||||
const thresholdDate = new Date(Date.now() - sinceMinutes * 60 * 1000);
|
||||
return getSentNotificationsBefore(thresholdDate, type);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
notificationAlreadySent: notificationAlreadySentFunc,
|
||||
markNotificationsAsSent: markNotificationsAsSentFunc,
|
||||
markNotificationsAsFailed: markNotificationsAsFailedFunc,
|
||||
markNotificationsAsPending: markNotificationsAsPendingFunc,
|
||||
getPendingTicketsForNotification: getPendingTicketsForNotificationFunc
|
||||
notificationAlreadySent,
|
||||
markNotificationsAsSent,
|
||||
markNotificationsAsFailed,
|
||||
markNotificationsAsPending,
|
||||
markNotificationsAsCompleted,
|
||||
getPendingTicketsForNotification,
|
||||
getSentNotificationsBefore,
|
||||
getTicketsPendingResponse
|
||||
};
|
||||
|
||||
@ -101,18 +101,18 @@ async function getTicketsClosedSince(thresholdDate) {
|
||||
if (process.env.HUBSOFT_MOCK_ENABLED === 'true') {
|
||||
return [
|
||||
{
|
||||
id_atendimento: 2780,
|
||||
protocolo: '20260106155510498970',
|
||||
id_atendimento: 2949,
|
||||
protocolo: '20260120133512641803',
|
||||
hubsoft_closed_at: new Date('2026-01-06T08:20:45')
|
||||
},
|
||||
{
|
||||
id_atendimento: 2769,
|
||||
protocolo: '20260105170715994323',
|
||||
id_atendimento: 2950,
|
||||
protocolo: '20260120133618108141',
|
||||
hubsoft_closed_at: new Date('2026-01-06T10:02:13')
|
||||
},
|
||||
{
|
||||
id_atendimento: 2779,
|
||||
protocolo: '20260106145016864639',
|
||||
id_atendimento: 2955,
|
||||
protocolo: '20260120134024457448',
|
||||
hubsoft_closed_at: new Date('2025-12-18T14:35:56')
|
||||
}
|
||||
]
|
||||
|
||||
@ -65,7 +65,13 @@ async function syncTicketsUseCase() {
|
||||
if (!service) continue
|
||||
|
||||
const glpiId = await service.sendToGlpi(ticket)
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
logInfo(`[USECASE] Ambiente de desenvolvimento. Ignorando notificação do ticket ${ticket.id_atendimento}`)
|
||||
continue
|
||||
}
|
||||
await notifyTicketCreated.notifyTicketCreated(ticket.id_atendimento, glpiId)
|
||||
|
||||
} catch (err) {
|
||||
logError(err, `[USECASE] Falha ao processar ticket ${ticket.id_atendimento}`)
|
||||
}
|
||||
|
||||
@ -3,67 +3,18 @@
|
||||
require('dotenv').config({ path: '.env.development' })
|
||||
|
||||
const { logError, logInfo } = require('../../../shared/utils/logger')
|
||||
const repository = require('../repository/watchdog.repository.js')
|
||||
const model = require('../model/email.model.js')
|
||||
const { notifyCollaborators } = require('../services/notifyCollaborators.service.js')
|
||||
const { notifyAdmins } = require('../services/notifyAdmins.service.js')
|
||||
|
||||
async function runWatchdog() {
|
||||
const { sentCount: sentFunc } = await notifyCollaborators({ thresholdMinutes: 31 })
|
||||
const { sentCount: sentAdm } = await notifyAdmins({ sinceMinutes: 30 })
|
||||
|
||||
logInfo('[WATCHDOG] [JOB] Coletando chamados fechados hÇ 31 minutos')
|
||||
|
||||
const thresholdDate = new Date(Date.now() - 31 * 60 * 1000)
|
||||
|
||||
const closedTickets = await repository.getClosedTicketsSince(thresholdDate)
|
||||
logInfo(`[WATCHDOG] [JOB] Encontrados ${closedTickets.length} chamados fechados`)
|
||||
|
||||
let notificationType = 'func'
|
||||
|
||||
for (const ticket of closedTickets) {
|
||||
|
||||
const hubGlpiTicket = await repository.checkTicketInHubGlpi(ticket.id_atendimento)
|
||||
|
||||
if (!hubGlpiTicket.exists) {
|
||||
logInfo(`[WATCHDOG] [JOB] Chamado ${ticket.id_atendimento} nao encontrado no HubGlpi. Ignorando.`)
|
||||
continue
|
||||
}
|
||||
|
||||
if (hubGlpiTicket.status === 'closed') {
|
||||
logInfo(`[WATCHDOG] [JOB] Chamado ${ticket.id_atendimento} ja esta fechado no HubGlpi. Ignorando.`)
|
||||
continue
|
||||
}
|
||||
if (await repository.notificationAlreadySent(ticket.id_atendimento, notificationType)) {
|
||||
logInfo(`[WATCHDOG] [JOB] NotificaÇõÇœo jÇ enviada para o chamado ${ticket.id_atendimento}.`)
|
||||
continue
|
||||
}
|
||||
await repository.markNotificationsAsPending([ticket.id_atendimento], notificationType)
|
||||
|
||||
}
|
||||
const ticketsToNotify = await repository.getPendingTicketsForNotification(notificationType)
|
||||
logInfo(`[WATCHDOG] [JOB] ${ticketsToNotify.length} chamados pendentes para notificaÇõÇœo.`)
|
||||
|
||||
if (!ticketsToNotify.length) {
|
||||
logInfo('[WATCHDOG] [JOB] Nenhum chamado pendente para notificaÇõÇœo')
|
||||
return
|
||||
}
|
||||
|
||||
const payload = await model.prepareNotificationPayload(ticketsToNotify)
|
||||
const hubsoftTicketIds = ticketsToNotify.map(t => Number(t.hubsoft_ticket_id)).filter(Number.isFinite);
|
||||
|
||||
try {
|
||||
await repository.sendClosureNotifications(payload)
|
||||
|
||||
|
||||
await repository.markNotificationsAsSent(hubsoftTicketIds, notificationType)
|
||||
} catch (err) {
|
||||
logError('[WATCHDOG] Erro ao enviar notificaÇõÇæes', err)
|
||||
await repository.markNotificationsAsFailed(hubsoftTicketIds, notificationType)
|
||||
}
|
||||
|
||||
logInfo(`[WATCHDOG] [JOB] Enviadas ${ticketsToNotify.length} notificaÇõÇæes`)
|
||||
logInfo(`[WATCHDOG] [JOB] Enviadas ${sentFunc} notificacoes para colaboradores e ${sentAdm} para ADM.`)
|
||||
}
|
||||
|
||||
|
||||
runWatchdog().catch((error) => {
|
||||
logError('[WATCHDOG] [JOB] Erro ao executar o job do Watchdog', error)
|
||||
})
|
||||
|
||||
module.exports = { runWatchdog}
|
||||
module.exports = { runWatchdog }
|
||||
|
||||
@ -1,83 +1,118 @@
|
||||
// src/modules/watchdog/model/email.model.js
|
||||
|
||||
function prepareNotificationPayload(tickets) {
|
||||
return {
|
||||
subject: buildSubject(tickets),
|
||||
bodyEmail: buildBodyEmail(tickets),
|
||||
recipients: getRecipients(),
|
||||
cc: getCc()
|
||||
};
|
||||
function prepareNotificationPayload(tickets, type = 'func') {
|
||||
const normalizedType = String(type || 'func').toLowerCase()
|
||||
const isAdm = normalizedType === 'adm'
|
||||
|
||||
return {
|
||||
subject: buildSubject(tickets, normalizedType),
|
||||
bodyEmail: isAdm ? buildBodyEmailAdm(tickets) : buildBodyEmail(tickets),
|
||||
recipients: getRecipients(normalizedType),
|
||||
cc: getCc(normalizedType)
|
||||
}
|
||||
}
|
||||
|
||||
function buildSubject(tickets) {
|
||||
|
||||
function buildSubject(tickets, type = 'func') {
|
||||
if (type === 'adm') {
|
||||
if (tickets.length === 0) {
|
||||
return '[GOLEIRO] Nenhum chamado foi agarrado';
|
||||
return '🧤⚽ [GOLEIRO ADM] Nenhum chamado foi para os pênaltis'
|
||||
}
|
||||
|
||||
return `🧤 [GOLEIRO] ${tickets.length} chamados foram agarrados`;
|
||||
|
||||
return `🚨⚽ [GOLEIRO ADM] ${tickets.length} chamados foram para os pênaltis`
|
||||
}
|
||||
|
||||
if (tickets.length === 0) {
|
||||
return '🧤 [GOLEIRO] Nenhum chamado foi agarrado'
|
||||
}
|
||||
|
||||
return `🧤 [GOLEIRO] ${tickets.length} chamados foram agarrados`
|
||||
}
|
||||
|
||||
function buildBodyEmail(tickets) {
|
||||
let body = `
|
||||
<p>🚨 <strong>Atenção!</strong></p>
|
||||
<p>O goleiro defendeu os seguintes chamados:</p>
|
||||
<p>Esses chamados foram <strong>fechados no Hubsoft</strong>, mas ainda constam como <strong>abertos no GLPI</strong>.</p>
|
||||
<p>🧤 <strong>Atenção, time!</strong></p>
|
||||
<p>O goleiro entrou em ação e defendeu os seguintes chamados:</p>
|
||||
<p>
|
||||
Esses chamados foram <strong>fechados no Hubsoft</strong>,
|
||||
mas ainda constam como <strong>abertos no GLPI</strong>.
|
||||
</p>
|
||||
<br>
|
||||
<ul>
|
||||
`;
|
||||
`
|
||||
|
||||
tickets.forEach(ticket => {
|
||||
body += `
|
||||
<li>
|
||||
Protocolo Hubsoft: <strong>${ticket.protocolo_hub}</strong><br>
|
||||
Mundiale ID: <strong>${ticket.ticket_mundiale}</strong><br>
|
||||
GLPI ID: <strong>${ticket.glpi_ticket_id ?? 'não encontrado'}</strong><br>
|
||||
Fechado em: ${formatDate(ticket.closed_at)}
|
||||
🧾 <strong>Protocolo Hubsoft:</strong> ${ticket.protocolo_hub}<br>
|
||||
🧠 <strong>Mundiale ID:</strong> ${ticket.ticket_mundiale}<br>
|
||||
🛠 <strong>GLPI ID:</strong> ${ticket.glpi_ticket_id ?? 'não encontrado'}<br>
|
||||
⏱ <strong>Fechado em:</strong> ${formatDate(ticket.closed_at)}
|
||||
</li>
|
||||
<br>
|
||||
`;
|
||||
});
|
||||
`
|
||||
})
|
||||
|
||||
body += `
|
||||
</ul>
|
||||
<br>
|
||||
<p>⚽ Favor verificar e alinhar os status no GLPI.</p>
|
||||
<p>⚠️ Favor verificar e alinhar os status no GLPI.</p>
|
||||
<p><em>Watchdog Hub × GLPI</em></p>
|
||||
`;
|
||||
`
|
||||
|
||||
return body;
|
||||
return body
|
||||
}
|
||||
|
||||
function buildBodyEmailAdm(tickets) {
|
||||
let body = `
|
||||
<p>🚨⚽ <strong>Pênalti!</strong></p>
|
||||
<p>Os seguintes chamados passaram da defesa inicial:</p>
|
||||
<p>
|
||||
Esses chamados foram <strong>fechados no Hubsoft há mais de 1 hora</strong>
|
||||
e ainda <strong>não tiveram atualização no GLPI</strong>.
|
||||
</p>
|
||||
<br>
|
||||
<ul>
|
||||
`
|
||||
|
||||
tickets.forEach(ticket => {
|
||||
body += `
|
||||
<li>
|
||||
🧾 <strong>Protocolo Hubsoft:</strong> ${ticket.protocolo_hub}<br>
|
||||
🧠 <strong>Mundiale ID:</strong> ${ticket.ticket_mundiale}<br>
|
||||
🛠 <strong>GLPI ID:</strong> ${ticket.glpi_ticket_id ?? 'não encontrado'}<br>
|
||||
⏱ <strong>Fechado em:</strong> ${formatDate(ticket.closed_at)}
|
||||
</li>
|
||||
<br>
|
||||
`
|
||||
})
|
||||
|
||||
body += `
|
||||
</ul>
|
||||
<br>
|
||||
<p>📢 Favor acionar o time responsável e alinhar os status no GLPI.</p>
|
||||
<p><em>Watchdog Hub × GLPI</em></p>
|
||||
`
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
function formatDate(value) {
|
||||
if (!value) return 'não informado';
|
||||
if (!value) return 'não informado'
|
||||
|
||||
const d = new Date(value);
|
||||
if (Number.isNaN(d.getTime())) return String(value); // fallback seguro
|
||||
const d = new Date(value)
|
||||
if (Number.isNaN(d.getTime())) return String(value)
|
||||
|
||||
return d.toLocaleString('pt-BR', { timeZone: 'America/Sao_Paulo' });
|
||||
return d.toLocaleString('pt-BR', { timeZone: 'America/Sao_Paulo' })
|
||||
}
|
||||
|
||||
|
||||
|
||||
function formatDate(date) {
|
||||
try {
|
||||
return new Date(date).toLocaleString('pt-BR');
|
||||
} catch {
|
||||
return date;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getRecipients() {
|
||||
return process.env.WATCHDOG_RECIPIENT_EMAILS?.split(',') || [];
|
||||
return process.env.WATCHDOG_RECIPIENT_EMAILS?.split(',') || []
|
||||
}
|
||||
|
||||
function getCc() {
|
||||
return process.env.WATCHDOG_CC_EMAILS?.split(',') || [];
|
||||
return process.env.WATCHDOG_CC_EMAILS?.split(',') || []
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
prepareNotificationPayload
|
||||
};
|
||||
prepareNotificationPayload
|
||||
}
|
||||
|
||||
@ -5,6 +5,8 @@ const wdRepository = require('../../../infra/db/repositories/hubglpi/watchdog.re
|
||||
const hubsoftTicketsRepo = require('../../../infra/db/repositories/hubsoft/tickets.repository.js');
|
||||
const hubglpiSyncRepo = require('../../../infra/db/repositories/hubglpi/sync.repository.js');
|
||||
const senderMail = require('../../../infra/mail/sender.js')
|
||||
const glpiRepository = require('../../../infra/db/repositories/glpi/tickets.repository.js')
|
||||
|
||||
|
||||
/**
|
||||
* Busca tickets encerrados no Hubsoft a partir de uma data
|
||||
@ -71,13 +73,34 @@ async function getPendingTicketsForNotification(type = 'func') {
|
||||
return wdRepository.getPendingTicketsForNotification(type);
|
||||
}
|
||||
|
||||
async function getTicketsPendingResponse(sinceMinutes = 30, type = 'func') {
|
||||
return wdRepository.getTicketsPendingResponse(sinceMinutes, type);
|
||||
}
|
||||
|
||||
async function checkTicketIsUpdated(glpiTicketId, sinceMinutes = 30) {
|
||||
const ticketUpdateDate = await glpiRepository.checkTicketUpdateDate(glpiTicketId);
|
||||
|
||||
if (ticketUpdateDate.date_mod < new Date(Date.now() - sinceMinutes * 60000)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function markNotificationAsCompleted(hubsoftTicketId, type = 'func') {
|
||||
return wdRepository.markNotificationsAsCompleted([hubsoftTicketId], type);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getClosedTicketsSince,
|
||||
checkTicketInHubGlpi,
|
||||
checkTicketIsUpdated,
|
||||
notificationAlreadySent,
|
||||
sendClosureNotifications,
|
||||
markNotificationsAsSent,
|
||||
markNotificationsAsFailed,
|
||||
markNotificationsAsPending,
|
||||
getPendingTicketsForNotification
|
||||
getPendingTicketsForNotification,
|
||||
getTicketsPendingResponse,
|
||||
markNotificationAsCompleted
|
||||
};
|
||||
|
||||
62
src/modules/watchdog/services/notifyAdmins.service.js
Normal file
62
src/modules/watchdog/services/notifyAdmins.service.js
Normal file
@ -0,0 +1,62 @@
|
||||
// src/modules/watchdog/services/notifyAdmins.service.js
|
||||
|
||||
const { logError, logInfo } = require('../../../shared/utils/logger')
|
||||
const repository = require('../repository/watchdog.repository.js')
|
||||
const model = require('../model/email.model.js')
|
||||
|
||||
async function notifyAdmins({ sinceMinutes = 30 } = {}) {
|
||||
logInfo(`[WATCHDOG] [ADM] Verificando chamados sem interacao ha mais de ${sinceMinutes} minutos`)
|
||||
|
||||
const ticketsPendingVerify = await repository.getTicketsPendingResponse(sinceMinutes, 'func')
|
||||
logInfo(`[WATCHDOG] [ADM] ${ticketsPendingVerify.length} chamados com notificacao enviada e pendentes de resposta.`)
|
||||
|
||||
const notificationType = 'adm'
|
||||
|
||||
for (const ticket of ticketsPendingVerify) {
|
||||
const hubsoftTicketId = Number(ticket.hubsoft_ticket_id ?? ticket.id_atendimento)
|
||||
const glpiTicketId = Number(ticket.glpi_ticket_id)
|
||||
|
||||
if (!Number.isFinite(hubsoftTicketId)) {
|
||||
logInfo('[WATCHDOG] [ADM] Ticket pendente sem id valido. Ignorando.')
|
||||
continue
|
||||
}
|
||||
|
||||
const isUpdated = await repository.checkTicketIsUpdated(glpiTicketId, sinceMinutes)
|
||||
if (isUpdated) {
|
||||
logInfo(`[WATCHDOG] [ADM] Chamado ${hubsoftTicketId} atualizado no GLPI. Encerrando fluxo.`)
|
||||
await repository.markNotificationAsCompleted(hubsoftTicketId, 'func')
|
||||
continue
|
||||
}
|
||||
|
||||
if (await repository.notificationAlreadySent(hubsoftTicketId, notificationType)) {
|
||||
logInfo(`[WATCHDOG] [ADM] Notificacao ADM ja enviada para o chamado ${hubsoftTicketId}.`)
|
||||
continue
|
||||
}
|
||||
|
||||
await repository.markNotificationsAsPending([hubsoftTicketId], notificationType)
|
||||
}
|
||||
|
||||
const ticketsToNotify = await repository.getPendingTicketsForNotification(notificationType)
|
||||
logInfo(`[WATCHDOG] [ADM] ${ticketsToNotify.length} chamados pendentes para notificacao.`)
|
||||
|
||||
if (!ticketsToNotify.length) {
|
||||
return { sentCount: 0, failedCount: 0 }
|
||||
}
|
||||
|
||||
const payload = await model.prepareNotificationPayload(ticketsToNotify, notificationType)
|
||||
const hubsoftTicketIds = ticketsToNotify
|
||||
.map(t => Number(t.hubsoft_ticket_id))
|
||||
.filter(Number.isFinite)
|
||||
|
||||
try {
|
||||
await repository.sendClosureNotifications(payload)
|
||||
await repository.markNotificationsAsSent(hubsoftTicketIds, notificationType)
|
||||
return { sentCount: ticketsToNotify.length, failedCount: 0 }
|
||||
} catch (err) {
|
||||
logError('[WATCHDOG] Erro ao enviar notificacoes para ADM', err)
|
||||
await repository.markNotificationsAsFailed(hubsoftTicketIds, notificationType)
|
||||
return { sentCount: 0, failedCount: hubsoftTicketIds.length }
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { notifyAdmins }
|
||||
58
src/modules/watchdog/services/notifyCollaborators.service.js
Normal file
58
src/modules/watchdog/services/notifyCollaborators.service.js
Normal file
@ -0,0 +1,58 @@
|
||||
// src/modules/watchdog/services/notifyCollaborators.service.js
|
||||
|
||||
const { logError, logInfo } = require('../../../shared/utils/logger')
|
||||
const repository = require('../repository/watchdog.repository.js')
|
||||
const model = require('../model/email.model.js')
|
||||
|
||||
async function notifyCollaborators({ thresholdMinutes = 31 } = {}) {
|
||||
logInfo(`[WATCHDOG] [FUNC] Coletando chamados fechados ha mais de ${thresholdMinutes} minutos`)
|
||||
|
||||
const thresholdDate = new Date(Date.now() - thresholdMinutes * 60 * 1000)
|
||||
const closedTickets = await repository.getClosedTicketsSince(thresholdDate)
|
||||
logInfo(`[WATCHDOG] [FUNC] Encontrados ${closedTickets.length} chamados fechados`)
|
||||
|
||||
const notificationType = 'func'
|
||||
|
||||
for (const ticket of closedTickets) {
|
||||
const hubGlpiTicket = await repository.checkTicketInHubGlpi(ticket.id_atendimento)
|
||||
|
||||
if (!hubGlpiTicket.exists) {
|
||||
logInfo(`[WATCHDOG] [FUNC] Chamado ${ticket.id_atendimento} nao encontrado no HubGlpi. Ignorando.`)
|
||||
continue
|
||||
}
|
||||
if (hubGlpiTicket.status === 'closed') {
|
||||
logInfo(`[WATCHDOG] [FUNC] Chamado ${ticket.id_atendimento} ja esta fechado no HubGlpi. Ignorando.`)
|
||||
continue
|
||||
}
|
||||
if (await repository.notificationAlreadySent(ticket.id_atendimento, notificationType)) {
|
||||
logInfo(`[WATCHDOG] [FUNC] Notificacao ja enviada para o chamado ${ticket.id_atendimento}.`)
|
||||
continue
|
||||
}
|
||||
|
||||
await repository.markNotificationsAsPending([ticket.id_atendimento], notificationType)
|
||||
}
|
||||
|
||||
const ticketsToNotify = await repository.getPendingTicketsForNotification(notificationType)
|
||||
logInfo(`[WATCHDOG] [FUNC] ${ticketsToNotify.length} chamados pendentes para notificacao.`)
|
||||
|
||||
if (!ticketsToNotify.length) {
|
||||
return { sentCount: 0, failedCount: 0 }
|
||||
}
|
||||
|
||||
const payload = await model.prepareNotificationPayload(ticketsToNotify, notificationType)
|
||||
const hubsoftTicketIds = ticketsToNotify
|
||||
.map(t => Number(t.hubsoft_ticket_id))
|
||||
.filter(Number.isFinite)
|
||||
|
||||
try {
|
||||
await repository.sendClosureNotifications(payload)
|
||||
await repository.markNotificationsAsSent(hubsoftTicketIds, notificationType)
|
||||
return { sentCount: ticketsToNotify.length, failedCount: 0 }
|
||||
} catch (err) {
|
||||
logError('[WATCHDOG] Erro ao enviar notificacoes para colaboradores', err)
|
||||
await repository.markNotificationsAsFailed(hubsoftTicketIds, notificationType)
|
||||
return { sentCount: 0, failedCount: hubsoftTicketIds.length }
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { notifyCollaborators }
|
||||
Loading…
Reference in New Issue
Block a user