REFACTOR: Preparando WatchDog para nova feature
This commit is contained in:
parent
c1f52d741f
commit
ad28159185
232
fluxoEntidade.md
Normal file
232
fluxoEntidade.md
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
# Fluxo de Resolução e Criação de Entidades (GLPI)
|
||||||
|
|
||||||
|
## Contexto
|
||||||
|
|
||||||
|
Quando um chamado de **Implantação** chega no Hub x GLPI, precisamos garantir que exista uma **Entidade** correta no GLPI para vincular o ticket.
|
||||||
|
|
||||||
|
Hoje, a resolução tenta encontrar a entidade pelo padrão de nome baseado em:
|
||||||
|
- `codigoCliente`
|
||||||
|
- `codigoServico`
|
||||||
|
- `nomeCliente`
|
||||||
|
|
||||||
|
Caso não encontre, entra a feature de **criação automática via API (GLPI v2 + OAuth)**.
|
||||||
|
|
||||||
|
> Regra importante: **criação/alteração de entidades sempre via API**, nunca via banco do GLPI.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Convenções de Nome
|
||||||
|
|
||||||
|
### Entidade mãe (Cliente)
|
||||||
|
- **Formato:** `{codigoCliente} - {nomeCliente}`
|
||||||
|
- **Exemplo:** `12345 - ACME Telecom`
|
||||||
|
|
||||||
|
### Entidade filha (Serviço)
|
||||||
|
- **Formato:** `{codigoCliente} - {codigoServico} - {nomeCliente}`
|
||||||
|
- **Exemplo:** `12345 - 67890 - ACME Telecom`
|
||||||
|
|
||||||
|
> Observação: O `{nomeCliente}` é repetido na filha para manter legibilidade e facilitar busca manual no GLPI.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Busca de Entidades
|
||||||
|
|
||||||
|
### Busca por Serviço (Entidade filha)
|
||||||
|
Procura por entidade que contenha:
|
||||||
|
- `{codigoCliente} - {codigoServico}` (prefixo/like)
|
||||||
|
|
||||||
|
### Busca por Cliente (Entidade mãe)
|
||||||
|
Procura por entidade que contenha:
|
||||||
|
- `{codigoCliente} -` (prefixo/like)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Opção A — Criação hierárquica (Mãe + Filha quando necessário)
|
||||||
|
|
||||||
|
### Objetivo
|
||||||
|
Garantir hierarquia consistente no GLPI:
|
||||||
|
- Cliente como entidade mãe
|
||||||
|
- Serviço como entidade filha
|
||||||
|
|
||||||
|
### Fluxo (passo a passo)
|
||||||
|
|
||||||
|
1. **Tentar encontrar entidade por Serviço**
|
||||||
|
- Busca: `{codigoCliente} - {codigoServico}*`
|
||||||
|
- Se encontrou:
|
||||||
|
- ✅ usar essa entidade (filha) e finalizar
|
||||||
|
|
||||||
|
2. **Se não encontrou por Serviço: tentar encontrar entidade por Cliente**
|
||||||
|
- Busca: `{codigoCliente} -*`
|
||||||
|
- Se encontrou:
|
||||||
|
- ✅ criar **entidade filha** abaixo dessa mãe:
|
||||||
|
- Nome: `{codigoCliente} - {codigoServico} - {nomeCliente}`
|
||||||
|
- Parent: `{codigoCliente} - {nomeCliente}`
|
||||||
|
- ✅ usar a entidade criada (filha) e finalizar
|
||||||
|
|
||||||
|
3. **Se não encontrou nem por Serviço nem por Cliente**
|
||||||
|
- Criar **entidade mãe**:
|
||||||
|
- Nome: `{codigoCliente} - {nomeCliente}`
|
||||||
|
- Parent: raiz (definir entidade pai padrão, ex.: Root)
|
||||||
|
- Criar **entidade filha** abaixo da mãe:
|
||||||
|
- Nome: `{codigoCliente} - {codigoServico} - {nomeCliente}`
|
||||||
|
- Parent: `{codigoCliente} - {nomeCliente}`
|
||||||
|
- ✅ usar a entidade criada (filha) e finalizar
|
||||||
|
|
||||||
|
### Resultado final (Opção A)
|
||||||
|
- Ticket sempre fica na **entidade filha** (serviço)
|
||||||
|
- Sempre tenta manter **estrutura hierárquica** consistente
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Diagrama de Fluxo — Opção A (hierárquica: Mãe + Filha quando necessário)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[Chamado de Implantação chega do Hub] --> B[Extrair dados<br/>codigoCliente<br/>codigoServico<br/>nomeCliente]
|
||||||
|
|
||||||
|
B --> C{Existe entidade<br/>por Serviço?}
|
||||||
|
C -->|Sim| D[Usar entidade de Serviço]
|
||||||
|
D --> Z[Fim / Continua fluxo do ticket]
|
||||||
|
|
||||||
|
C -->|Não| E{Existe entidade<br/>por Cliente?}
|
||||||
|
|
||||||
|
E -->|Sim| F[Criar entidade de Serviço<br/>via API GLPI]
|
||||||
|
F --> G[Parent = Entidade Cliente<br/>Nome = codigoCliente-codigoServico-nomeCliente]
|
||||||
|
G --> Z
|
||||||
|
|
||||||
|
E -->|Não| H[Criar entidade Cliente<br/>via API GLPI]
|
||||||
|
H --> I[Parent = Contratos Ativos<br/>Nome = codigoCliente-nomeCliente]
|
||||||
|
|
||||||
|
I --> J[Criar entidade de Serviço<br/>via API GLPI]
|
||||||
|
J --> K[Parent = Entidade Cliente<br/>Nome = codigoCliente-codigoServico-nomeCliente]
|
||||||
|
K --> Z
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Opção B — Criação simplificada (sem forçar hierarquia)
|
||||||
|
|
||||||
|
### Objetivo
|
||||||
|
Criar o mínimo possível e evitar duplicação de entidades.
|
||||||
|
|
||||||
|
### Fluxo (passo a passo)
|
||||||
|
|
||||||
|
1. **Tentar encontrar entidade por Serviço**
|
||||||
|
- Busca: `{codigoCliente} - {codigoServico}*`
|
||||||
|
- Se encontrou:
|
||||||
|
- ✅ usar essa entidade e finalizar
|
||||||
|
|
||||||
|
2. **Se não encontrou por Serviço: tentar encontrar entidade por Cliente**
|
||||||
|
- Busca: `{codigoCliente} -*`
|
||||||
|
- Se encontrou:
|
||||||
|
- ✅ usar essa entidade mãe
|
||||||
|
- ❌ não cria entidade filha
|
||||||
|
- Finaliza
|
||||||
|
|
||||||
|
3. **Se não encontrou nem por Serviço nem por Cliente**
|
||||||
|
- Criar **uma entidade única (sem mãe)**:
|
||||||
|
- Nome: `{codigoCliente} - {codigoServico} - {nomeCliente}`
|
||||||
|
- Parent: raiz (ou padrão definido)
|
||||||
|
- ✅ usar essa entidade e finalizar
|
||||||
|
|
||||||
|
### Resultado final (Opção B)
|
||||||
|
- Ticket pode ficar:
|
||||||
|
- na entidade de serviço (se existir)
|
||||||
|
- ou na entidade de cliente (se existir)
|
||||||
|
- ou numa entidade única criada
|
||||||
|
- Não garante hierarquia “mãe → filha”
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Fluxo Opção B
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[Chamado de Implantação chega do Hub] --> B[Extrair dados<br/>codigoCliente<br/>codigoServico<br/>nomeCliente]
|
||||||
|
|
||||||
|
B --> C{Existe entidade<br/>por Serviço?}
|
||||||
|
C -->|Sim| D[Usar entidade de Serviço]
|
||||||
|
D --> Z[Fim / Continua fluxo do ticket]
|
||||||
|
|
||||||
|
C -->|Não| E{Existe entidade<br/>por Cliente?}
|
||||||
|
|
||||||
|
E -->|Sim| F[Usar entidade de Cliente]
|
||||||
|
F --> Z
|
||||||
|
|
||||||
|
E -->|Não| G[Criar entidade Única<br/>via API GLPI]
|
||||||
|
G --> H[Parent = Root<br/>Nome = codigoCliente-codigoServico-nomeCliente]
|
||||||
|
H --> Z
|
||||||
|
```
|
||||||
|
|
||||||
|
## Opção C — Híbrida (Implantação cria; demais apenas resolvem)
|
||||||
|
|
||||||
|
### Objetivo
|
||||||
|
- Para **Implantação**: garantir estrutura correta criando entidades quando necessário (igual Opção A).
|
||||||
|
- Para **demais tipos de chamado**: **não criar entidades**, apenas resolver a melhor entidade existente; se não houver, usar fallback **Contratos Ativos**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Fluxo (passo a passo)
|
||||||
|
|
||||||
|
#### 1) Identificar tipo do chamado
|
||||||
|
- Se `tipo == Implantação` → executar **Fluxo A (criação hierárquica)**.
|
||||||
|
- Se `tipo != Implantação` → executar **Fluxo de Resolução sem criação**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Fluxo A (quando Implantação)
|
||||||
|
Segue exatamente a **Opção A**:
|
||||||
|
1. Buscar entidade por Serviço (`{codigoCliente} - {codigoServico}%`)
|
||||||
|
2. Se não achar, buscar por Cliente (`{codigoCliente} -%`)
|
||||||
|
3. Se achar Cliente, criar Serviço abaixo do Cliente
|
||||||
|
4. Se não achar nenhum, criar Cliente (embaixo de Contratos Ativos) e depois criar Serviço abaixo do Cliente
|
||||||
|
5. Retornar sempre a **entidade de Serviço**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Fluxo de Resolução (quando NÃO é Implantação)
|
||||||
|
1. Buscar entidade por Serviço (`{codigoCliente} - {codigoServico}%`)
|
||||||
|
- Se achou: ✅ usar e finalizar
|
||||||
|
2. Se não achou, buscar entidade por Cliente (`{codigoCliente} -%`)
|
||||||
|
- Se achou: ✅ usar e finalizar
|
||||||
|
3. Se não achou nenhum dos dois:
|
||||||
|
- ✅ usar entidade **Contratos Ativos** (fallback)
|
||||||
|
- ❌ não criar nada
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Resultado final (Opção C)
|
||||||
|
- **Implantação**: sempre aponta para entidade de **Serviço**, criando Cliente/Serviço quando faltar.
|
||||||
|
- **Demais chamados**: nunca criam entidades; ficam na melhor existente (Serviço → Cliente → Contratos Ativos).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Diagrama de Fluxo — Opção C (Híbrida)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[Chega chamado do Hub] --> B[Extrair dados<br/>codigoCliente<br/>codigoServico<br/>nomeCliente<br/>tipoChamado]
|
||||||
|
|
||||||
|
B --> C{Tipo é<br/>Implantação?}
|
||||||
|
|
||||||
|
%% ========== RAMO IMPLANTAÇÃO (OPÇÃO A) ==========
|
||||||
|
C -->|Sim| IA{Existe entidade<br/>por Serviço?}
|
||||||
|
IA -->|Sim| I1[Usar entidade de Serviço] --> Z[Fim / Continua fluxo do ticket]
|
||||||
|
|
||||||
|
IA -->|Não| IB{Existe entidade<br/>por Cliente?}
|
||||||
|
IB -->|Sim| I2[Criar entidade de Serviço<br/>via API GLPI<br/>Parent = Cliente] --> I3[Usar entidade criada] --> Z
|
||||||
|
|
||||||
|
IB -->|Não| I4[Criar entidade Cliente<br/>via API GLPI<br/>Parent = Contratos Ativos] --> I5[Criar entidade de Serviço<br/>via API GLPI<br/>Parent = Cliente] --> I6[Usar entidade criada] --> Z
|
||||||
|
|
||||||
|
%% ========== RAMO NÃO IMPLANTAÇÃO (RESOLVE ONLY) ==========
|
||||||
|
C -->|Não| NA{Existe entidade<br/>por Serviço?}
|
||||||
|
NA -->|Sim| N1[Usar entidade de Serviço] --> Z
|
||||||
|
|
||||||
|
NA -->|Não| NB{Existe entidade<br/>por Cliente?}
|
||||||
|
NB -->|Sim| N2[Usar entidade de Cliente] --> Z
|
||||||
|
|
||||||
|
NB -->|Não| N3[Usar entidade fallback<br/>Contratos Ativos] --> Z
|
||||||
|
```
|
||||||
@ -3,50 +3,63 @@
|
|||||||
const db = require('../../connections/hubglpi.pg.js');
|
const db = require('../../connections/hubglpi.pg.js');
|
||||||
const { logError } = require('../../../../shared/utils/logger.js');
|
const { logError } = require('../../../../shared/utils/logger.js');
|
||||||
|
|
||||||
|
const VALID_NOTIFICATION_TYPES = new Set(['func', 'adm']);
|
||||||
|
|
||||||
|
function resolveNotificationType(type = 'func') {
|
||||||
|
const normalized = String(type || 'func').toLowerCase();
|
||||||
|
return VALID_NOTIFICATION_TYPES.has(normalized) ? normalized : 'func';
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildStatus(kind, type) {
|
||||||
|
return `${kind}_${resolveNotificationType(type)}`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifica se a notificação já foi enviada com sucesso
|
* Verifica se a notificaÇõÇœo jÇ foi enviada com sucesso
|
||||||
*/
|
*/
|
||||||
async function notificationAlreadySent(hubsoftTicketId) {
|
async function notificationAlreadySentFunc(hubsoftTicketId, type = 'func') {
|
||||||
|
const status = buildStatus('sent', type);
|
||||||
const query = `
|
const query = `
|
||||||
SELECT 1
|
SELECT 1
|
||||||
FROM watchdog_notifications
|
FROM watchdog_notifications
|
||||||
WHERE ticket_id = $1
|
WHERE ticket_id = $1
|
||||||
AND status = 'SENT'
|
AND status = $2
|
||||||
LIMIT 1;
|
LIMIT 1;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { rowCount } = await db.query(query, [hubsoftTicketId]);
|
const { rowCount } = await db.query(query, [hubsoftTicketId, status]);
|
||||||
return rowCount > 0;
|
return rowCount > 0;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logError('Erro ao verificar notificação já enviada', error);
|
logError('Erro ao verificar notificaÇõÇœo jÇ enviada', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Marca tickets como pendente de notificação
|
* Marca tickets como pendente de notificaÇõÇœo
|
||||||
*/
|
*/
|
||||||
async function markNotificationsAsPending(hubsoftTicketIds) {
|
async function markNotificationsAsPendingFunc(hubsoftTicketIds, type = 'func') {
|
||||||
if (!hubsoftTicketIds || hubsoftTicketIds.length === 0) {
|
if (!hubsoftTicketIds || hubsoftTicketIds.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const status = buildStatus('pending', type);
|
||||||
const query = `
|
const query = `
|
||||||
INSERT INTO watchdog_notifications (ticket_id, notified_at, status)
|
INSERT INTO watchdog_notifications (ticket_id, notified_at, status)
|
||||||
SELECT
|
SELECT
|
||||||
unnest($1::bigint[]),
|
unnest($1::bigint[]),
|
||||||
NOW(),
|
NOW(),
|
||||||
'pending'
|
$2
|
||||||
ON CONFLICT (ticket_id)
|
ON CONFLICT (ticket_id)
|
||||||
DO UPDATE SET
|
DO UPDATE SET
|
||||||
notified_at = EXCLUDED.notified_at,
|
notified_at = EXCLUDED.notified_at,
|
||||||
status = 'pending';
|
status = $2;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await db.query(query, [hubsoftTicketIds]);
|
await db.query(query, [hubsoftTicketIds, status]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logError('Erro ao marcar notificações como falha', error);
|
logError('Erro ao marcar notificaÇõÇæes como falha', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,65 +68,68 @@ async function markNotificationsAsPending(hubsoftTicketIds) {
|
|||||||
/**
|
/**
|
||||||
* Marca tickets como notificados com sucesso
|
* Marca tickets como notificados com sucesso
|
||||||
*/
|
*/
|
||||||
async function markNotificationsAsSent(hubsoftTicketIds) {
|
async function markNotificationsAsSentFunc(hubsoftTicketIds, type = 'func') {
|
||||||
const ids = (hubsoftTicketIds || [])
|
const ids = (hubsoftTicketIds || [])
|
||||||
.map(id => Number(id))
|
.map(id => Number(id))
|
||||||
.filter(Number.isFinite);
|
.filter(Number.isFinite);
|
||||||
|
|
||||||
if (ids.length === 0) return;
|
if (ids.length === 0) return;
|
||||||
|
|
||||||
|
const status = buildStatus('sent', type);
|
||||||
const query = `
|
const query = `
|
||||||
INSERT INTO watchdog_notifications (ticket_id, notified_at, status)
|
INSERT INTO watchdog_notifications (ticket_id, notified_at, status)
|
||||||
SELECT
|
SELECT
|
||||||
unnest($1::bigint[]),
|
unnest($1::bigint[]),
|
||||||
NOW(),
|
NOW(),
|
||||||
'sent'
|
$2
|
||||||
ON CONFLICT (ticket_id)
|
ON CONFLICT (ticket_id)
|
||||||
DO UPDATE SET
|
DO UPDATE SET
|
||||||
notified_at = EXCLUDED.notified_at,
|
notified_at = EXCLUDED.notified_at,
|
||||||
status = 'sent';
|
status = $2;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await db.query(query, [ids]);
|
await db.query(query, [ids, status]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logError('[WATCHDOG][REPOSITORY] Erro ao marcar notificações como enviadas', error);
|
logError('[WATCHDOG][REPOSITORY] Erro ao marcar notificaÇõÇæes como enviadas', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marca tickets como falha de notificação
|
* Marca tickets como falha de notificaÇõÇœo
|
||||||
*/
|
*/
|
||||||
async function markNotificationsAsFailed(hubsoftTicketIds) {
|
async function markNotificationsAsFailedFunc(hubsoftTicketIds, type = 'func') {
|
||||||
const ids = (hubsoftTicketIds || [])
|
const ids = (hubsoftTicketIds || [])
|
||||||
.map(id => Number(id))
|
.map(id => Number(id))
|
||||||
.filter(Number.isFinite);
|
.filter(Number.isFinite);
|
||||||
|
|
||||||
if (ids.length === 0) return;
|
if (ids.length === 0) return;
|
||||||
|
|
||||||
|
const status = buildStatus('failed', type);
|
||||||
const query = `
|
const query = `
|
||||||
INSERT INTO watchdog_notifications (ticket_id, notified_at, status)
|
INSERT INTO watchdog_notifications (ticket_id, notified_at, status)
|
||||||
SELECT
|
SELECT
|
||||||
unnest($1::bigint[]),
|
unnest($1::bigint[]),
|
||||||
NOW(),
|
NOW(),
|
||||||
'sent'
|
$2
|
||||||
ON CONFLICT (ticket_id)
|
ON CONFLICT (ticket_id)
|
||||||
DO UPDATE SET
|
DO UPDATE SET
|
||||||
notified_at = EXCLUDED.notified_at,
|
notified_at = EXCLUDED.notified_at,
|
||||||
status = 'failed';
|
status = $2;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await db.query(query, [ids]);
|
await db.query(query, [ids, status]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logError('[WATCHDOG][REPOSITORY] Erro ao marcar notificações como enviadas', error);
|
logError('[WATCHDOG][REPOSITORY] Erro ao marcar notificaÇõÇæes como enviadas', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function getPendingTicketsForNotification() {
|
async function getPendingTicketsForNotificationFunc(type = 'func') {
|
||||||
|
const status = buildStatus('pending', type);
|
||||||
const query = `
|
const query = `
|
||||||
SELECT
|
SELECT
|
||||||
wn.ticket_id AS hubsoft_ticket_id,
|
wn.ticket_id AS hubsoft_ticket_id,
|
||||||
@ -126,22 +142,22 @@ async function getPendingTicketsForNotification() {
|
|||||||
ON wn.ticket_id = sd.hubsoft_ticket_id
|
ON wn.ticket_id = sd.hubsoft_ticket_id
|
||||||
INNER JOIN hubsoft_tickets ht
|
INNER JOIN hubsoft_tickets ht
|
||||||
ON wn.ticket_id = ht.id_atendimento
|
ON wn.ticket_id = ht.id_atendimento
|
||||||
WHERE wn.status = 'pending';
|
WHERE wn.status = $1;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { rows } = await db.query(query);
|
const { rows } = await db.query(query, [status]);
|
||||||
return rows;
|
return rows;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logError('Erro ao buscar tickets pendentes para notificação', error);
|
logError('Erro ao buscar tickets pendentes para notificaÇõÇœo', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
notificationAlreadySent,
|
notificationAlreadySent: notificationAlreadySentFunc,
|
||||||
markNotificationsAsSent,
|
markNotificationsAsSent: markNotificationsAsSentFunc,
|
||||||
markNotificationsAsFailed,
|
markNotificationsAsFailed: markNotificationsAsFailedFunc,
|
||||||
markNotificationsAsPending,
|
markNotificationsAsPending: markNotificationsAsPendingFunc,
|
||||||
getPendingTicketsForNotification
|
getPendingTicketsForNotification: getPendingTicketsForNotificationFunc
|
||||||
};
|
};
|
||||||
|
|||||||
@ -8,9 +8,6 @@ const cancelamentoService = require('../services/cancelamento.service.js')
|
|||||||
//const sacService = require('../services/sac.service.js') //TODO
|
//const sacService = require('../services/sac.service.js') //TODO
|
||||||
const trocaTitularidadeService = require('../services/trocaTitularidade.service.js') //TODO
|
const trocaTitularidadeService = require('../services/trocaTitularidade.service.js') //TODO
|
||||||
|
|
||||||
const getAuthToken = require('../../../infra/api/hubsoft.auth.js')
|
|
||||||
|
|
||||||
|
|
||||||
const ticketShared = require('../services/createTickets.service.js')
|
const ticketShared = require('../services/createTickets.service.js')
|
||||||
const { logInfo, logError } = require('../../../shared/utils/logger.js')
|
const { logInfo, logError } = require('../../../shared/utils/logger.js')
|
||||||
|
|
||||||
|
|||||||
@ -8,38 +8,40 @@ const model = require('../model/email.model.js')
|
|||||||
|
|
||||||
async function runWatchdog() {
|
async function runWatchdog() {
|
||||||
|
|
||||||
logInfo('[WATCHDOG] [JOB] Coletando chamados fechados há 31 minutos')
|
logInfo('[WATCHDOG] [JOB] Coletando chamados fechados hÇ 31 minutos')
|
||||||
|
|
||||||
const thresholdDate = new Date(Date.now() - 31 * 60 * 1000)
|
const thresholdDate = new Date(Date.now() - 31 * 60 * 1000)
|
||||||
|
|
||||||
const closedTickets = await repository.getClosedTicketsSince(thresholdDate)
|
const closedTickets = await repository.getClosedTicketsSince(thresholdDate)
|
||||||
logInfo(`[WATCHDOG] [JOB] Encontrados ${closedTickets.length} chamados fechados`)
|
logInfo(`[WATCHDOG] [JOB] Encontrados ${closedTickets.length} chamados fechados`)
|
||||||
|
|
||||||
|
let notificationType = 'func'
|
||||||
|
|
||||||
for (const ticket of closedTickets) {
|
for (const ticket of closedTickets) {
|
||||||
|
|
||||||
const hubGlpiTicket = await repository.checkTicketInHubGlpi(ticket.id_atendimento)
|
const hubGlpiTicket = await repository.checkTicketInHubGlpi(ticket.id_atendimento)
|
||||||
|
|
||||||
if (!hubGlpiTicket.exists) {
|
if (!hubGlpiTicket.exists) {
|
||||||
logInfo(`[WATCHDOG] [JOB] Chamado ${ticket.id_atendimento} não encontrado no HubGlpi. Ignorando.`)
|
logInfo(`[WATCHDOG] [JOB] Chamado ${ticket.id_atendimento} nao encontrado no HubGlpi. Ignorando.`)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hubGlpiTicket.status === 'closed') {
|
if (hubGlpiTicket.status === 'closed') {
|
||||||
logInfo(`[WATCHDOG] [JOB] Chamado ${ticket.id_atendimento} já está fechado no HubGlpi. Ignorando.`)
|
logInfo(`[WATCHDOG] [JOB] Chamado ${ticket.id_atendimento} ja esta fechado no HubGlpi. Ignorando.`)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (await repository.notificationAlreadySent(ticket.id_atendimento)) {
|
if (await repository.notificationAlreadySent(ticket.id_atendimento, notificationType)) {
|
||||||
logInfo(`[WATCHDOG] [JOB] Notificação já enviada para o chamado ${ticket.id_atendimento}.`)
|
logInfo(`[WATCHDOG] [JOB] NotificaÇõÇœo jÇ enviada para o chamado ${ticket.id_atendimento}.`)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
await repository.markNotificationsAsPending([ticket.id_atendimento])
|
await repository.markNotificationsAsPending([ticket.id_atendimento], notificationType)
|
||||||
|
|
||||||
}
|
}
|
||||||
const ticketsToNotify = await repository.getPendingTicketsForNotification()
|
const ticketsToNotify = await repository.getPendingTicketsForNotification(notificationType)
|
||||||
logInfo(`[WATCHDOG] [JOB] ${ticketsToNotify.length} chamados pendentes para notificação.`)
|
logInfo(`[WATCHDOG] [JOB] ${ticketsToNotify.length} chamados pendentes para notificaÇõÇœo.`)
|
||||||
|
|
||||||
if (!ticketsToNotify.length) {
|
if (!ticketsToNotify.length) {
|
||||||
logInfo('[WATCHDOG] [JOB] Nenhum chamado pendente para notificação')
|
logInfo('[WATCHDOG] [JOB] Nenhum chamado pendente para notificaÇõÇœo')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,13 +52,13 @@ async function runWatchdog() {
|
|||||||
await repository.sendClosureNotifications(payload)
|
await repository.sendClosureNotifications(payload)
|
||||||
|
|
||||||
|
|
||||||
await repository.markNotificationsAsSent(hubsoftTicketIds)
|
await repository.markNotificationsAsSent(hubsoftTicketIds, notificationType)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logError('[WATCHDOG] Erro ao enviar notificações', err)
|
logError('[WATCHDOG] Erro ao enviar notificaÇõÇæes', err)
|
||||||
await repository.markNotificationsAsFailed(hubsoftTicketIds)
|
await repository.markNotificationsAsFailed(hubsoftTicketIds, notificationType)
|
||||||
}
|
}
|
||||||
|
|
||||||
logInfo(`[WATCHDOG] [JOB] Enviadas ${ticketsToNotify.length} notificações`)
|
logInfo(`[WATCHDOG] [JOB] Enviadas ${ticketsToNotify.length} notificaÇõÇæes`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -64,4 +66,4 @@ runWatchdog().catch((error) => {
|
|||||||
logError('[WATCHDOG] [JOB] Erro ao executar o job do Watchdog', error)
|
logError('[WATCHDOG] [JOB] Erro ao executar o job do Watchdog', error)
|
||||||
})
|
})
|
||||||
|
|
||||||
module.exports = { runWatchdog}
|
module.exports = { runWatchdog}
|
||||||
|
|||||||
@ -35,8 +35,8 @@ async function checkTicketInHubGlpi(hubsoftTicketId) {
|
|||||||
/**
|
/**
|
||||||
* Verifica se a notificação de encerramento já foi enviada
|
* Verifica se a notificação de encerramento já foi enviada
|
||||||
*/
|
*/
|
||||||
async function notificationAlreadySent(hubsoftTicketId) {
|
async function notificationAlreadySent(hubsoftTicketId, type = 'func') {
|
||||||
return wdRepository.notificationAlreadySent(hubsoftTicketId);
|
return wdRepository.notificationAlreadySent(hubsoftTicketId, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,26 +49,26 @@ async function sendClosureNotifications(payload) {
|
|||||||
/**
|
/**
|
||||||
* Marca notificações como enviadas com sucesso
|
* Marca notificações como enviadas com sucesso
|
||||||
*/
|
*/
|
||||||
async function markNotificationsAsSent(hubsoftTicketIds) {
|
async function markNotificationsAsSent(hubsoftTicketIds, type = 'func') {
|
||||||
return wdRepository.markNotificationsAsSent(hubsoftTicketIds);
|
return wdRepository.markNotificationsAsSent(hubsoftTicketIds, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marca notificações como falhas
|
* Marca notificações como falhas
|
||||||
*/
|
*/
|
||||||
async function markNotificationsAsFailed(hubsoftTicketIds, error = null) {
|
async function markNotificationsAsFailed(hubsoftTicketIds, type = 'func', error = null) {
|
||||||
return wdRepository.markNotificationsAsFailed(hubsoftTicketIds, error);
|
return wdRepository.markNotificationsAsFailed(hubsoftTicketIds, type, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marca notificações como pendentes
|
* Marca notificações como pendentes
|
||||||
*/
|
*/
|
||||||
async function markNotificationsAsPending(hubsoftTicketId, error = null) {
|
async function markNotificationsAsPending(hubsoftTicketId, type = 'func', error = null) {
|
||||||
return wdRepository.markNotificationsAsPending(hubsoftTicketId, error);
|
return wdRepository.markNotificationsAsPending(hubsoftTicketId, type, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getPendingTicketsForNotification() {
|
async function getPendingTicketsForNotification(type = 'func') {
|
||||||
return wdRepository.getPendingTicketsForNotification();
|
return wdRepository.getPendingTicketsForNotification(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user