2025-09-11 20:57:43 -03:00
|
|
|
// src/models/ticketSnModel.js
|
2025-09-05 20:28:15 -03:00
|
|
|
const pool = require('../data/database');
|
2025-09-15 20:55:19 -03:00
|
|
|
const { logInfo, logError, logWarning } = require('../utils/logger');
|
2025-08-30 19:04:35 -03:00
|
|
|
|
2025-09-05 20:28:15 -03:00
|
|
|
class TicketSnModel {
|
2025-09-15 20:55:19 -03:00
|
|
|
|
|
|
|
|
// Salva ou atualiza um ticket no banco de dados
|
2025-09-05 20:28:15 -03:00
|
|
|
static async saveTicket(ticketData, typeTicket) {
|
2025-09-08 20:35:49 -03:00
|
|
|
const query = `
|
2025-09-05 20:28:15 -03:00
|
|
|
INSERT INTO tickets_sn
|
2025-09-15 20:55:19 -03:00
|
|
|
(ticket_number, sys_id, short_description, status, description, caller_id, caller_email,
|
2025-09-08 20:35:49 -03:00
|
|
|
location_id, location_name, ramal, telefone, opened_at, tipo)
|
2025-09-15 20:55:19 -03:00
|
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
|
2025-09-05 20:28:15 -03:00
|
|
|
ON CONFLICT (ticket_number)
|
|
|
|
|
DO UPDATE SET
|
2025-09-15 20:55:19 -03:00
|
|
|
sys_id = EXCLUDED.sys_id,
|
2025-09-05 20:28:15 -03:00
|
|
|
short_description = EXCLUDED.short_description,
|
2025-09-11 20:57:43 -03:00
|
|
|
status = EXCLUDED.status,
|
2025-09-05 20:28:15 -03:00
|
|
|
description = EXCLUDED.description,
|
2025-09-08 20:35:49 -03:00
|
|
|
caller_id = EXCLUDED.caller_id,
|
2025-09-05 20:28:15 -03:00
|
|
|
caller_email = EXCLUDED.caller_email,
|
2025-09-11 20:57:43 -03:00
|
|
|
location_id = EXCLUDED.location_id,
|
|
|
|
|
location_name = EXCLUDED.location_name,
|
2025-09-05 20:28:15 -03:00
|
|
|
ramal = EXCLUDED.ramal,
|
|
|
|
|
telefone = EXCLUDED.telefone,
|
2025-09-11 20:57:43 -03:00
|
|
|
opened_at = EXCLUDED.opened_at,
|
|
|
|
|
tipo = EXCLUDED.tipo,
|
2025-09-05 20:28:15 -03:00
|
|
|
updated_at = NOW()
|
2025-09-08 20:35:49 -03:00
|
|
|
RETURNING id, xmax = 0 as inserted`;
|
2025-08-30 19:04:35 -03:00
|
|
|
|
2025-09-15 20:55:19 -03:00
|
|
|
const values = [
|
|
|
|
|
ticketData.number?.value || null,
|
|
|
|
|
ticketData.sys_id?.value || null,
|
|
|
|
|
ticketData.short_description?.value || null,
|
|
|
|
|
ticketData.state?.display_value || null,
|
|
|
|
|
ticketData.description?.value || null,
|
|
|
|
|
ticketData.opened_by?.display_value || null,
|
|
|
|
|
ticketData.sys_created_by?.display_value || null,
|
|
|
|
|
ticketData.location?.value || null,
|
|
|
|
|
ticketData.location?.display_value || null,
|
|
|
|
|
ticketData['variables.numero_ramal']?.value || null,
|
|
|
|
|
ticketData['variables.telephone_favorecido_rh']?.value || null,
|
|
|
|
|
ticketData.opened_at?.value ? new Date(ticketData.opened_at.value) : null,
|
|
|
|
|
typeTicket
|
2025-09-05 20:28:15 -03:00
|
|
|
];
|
2025-09-15 20:55:19 -03:00
|
|
|
|
2025-09-08 20:35:49 -03:00
|
|
|
|
2025-09-11 20:57:43 -03:00
|
|
|
try {
|
|
|
|
|
const result = await pool.query(query, values);
|
2025-09-15 20:55:19 -03:00
|
|
|
const wasInserted = result.rows[0].inserted;
|
2025-09-11 20:57:43 -03:00
|
|
|
|
|
|
|
|
if (wasInserted) {
|
|
|
|
|
logInfo(`✅ NOVO ticket salvo! ID: ${result.rows[0].id}`, {
|
|
|
|
|
ticket_id: result.rows[0].id,
|
|
|
|
|
ticket_number: values[0],
|
|
|
|
|
action: 'insert'
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
logInfo(`🔄 Ticket atualizado! ID: ${result.rows[0].id}`, {
|
|
|
|
|
ticket_id: result.rows[0].id,
|
|
|
|
|
ticket_number: values[0],
|
|
|
|
|
action: 'update'
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-09-15 20:55:19 -03:00
|
|
|
|
2025-09-11 20:57:43 -03:00
|
|
|
return result.rows[0].id;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logError(error, `❌ Erro ao salvar ticket ${values[0]}`);
|
2025-09-15 20:55:19 -03:00
|
|
|
throw error;
|
2025-09-11 20:57:43 -03:00
|
|
|
}
|
|
|
|
|
}
|
2025-08-30 19:04:35 -03:00
|
|
|
|
2025-09-15 20:55:19 -03:00
|
|
|
// Busca tickets que ainda não foram sincronizados com o GLPI
|
|
|
|
|
static async getPendingTickets() {
|
2025-09-11 20:57:43 -03:00
|
|
|
try {
|
|
|
|
|
const query = `
|
|
|
|
|
SELECT
|
|
|
|
|
id,
|
|
|
|
|
ticket_number,
|
|
|
|
|
short_description,
|
|
|
|
|
description,
|
|
|
|
|
status,
|
|
|
|
|
caller_id,
|
|
|
|
|
caller_email,
|
|
|
|
|
location_id,
|
|
|
|
|
location_name,
|
|
|
|
|
ramal,
|
|
|
|
|
telefone,
|
|
|
|
|
opened_at,
|
|
|
|
|
tipo,
|
|
|
|
|
created_at,
|
|
|
|
|
updated_at
|
|
|
|
|
FROM tickets_sn
|
|
|
|
|
WHERE glpi_sync_status IS NULL
|
2025-09-15 20:55:19 -03:00
|
|
|
AND status NOT IN ('Encerrado', 'Encerrado - Omitido')
|
|
|
|
|
ORDER BY opened_at
|
2025-09-11 20:57:43 -03:00
|
|
|
`;
|
2025-09-15 20:55:19 -03:00
|
|
|
|
|
|
|
|
const result = await pool.query(query);
|
2025-09-11 20:57:43 -03:00
|
|
|
logInfo(`📋 Tickets pendentes encontrados: ${result.rows.length}`);
|
|
|
|
|
return result.rows;
|
2025-09-15 20:55:19 -03:00
|
|
|
|
2025-09-11 20:57:43 -03:00
|
|
|
} catch (error) {
|
|
|
|
|
logError(error, '❌ Erro ao buscar tickets pendentes');
|
|
|
|
|
return [];
|
2025-09-05 20:28:15 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-15 20:55:19 -03:00
|
|
|
// Atualiza o status de sincronização de um ticket específico // Inativo
|
2025-09-11 20:57:43 -03:00
|
|
|
static async updateSyncStatus(ticketId, status, glpiTicketId = null) {
|
|
|
|
|
try {
|
|
|
|
|
const query = `
|
|
|
|
|
UPDATE tickets_sn
|
|
|
|
|
SET glpi_sync_status = $1,
|
|
|
|
|
glpi_ticket_id = $2,
|
|
|
|
|
glpi_sync_date = NOW()
|
|
|
|
|
WHERE id = $3
|
|
|
|
|
RETURNING id, ticket_number
|
|
|
|
|
`;
|
2025-09-15 20:55:19 -03:00
|
|
|
|
2025-09-11 20:57:43 -03:00
|
|
|
const result = await pool.query(query, [status, glpiTicketId, ticketId]);
|
2025-09-15 20:55:19 -03:00
|
|
|
|
2025-09-11 20:57:43 -03:00
|
|
|
if (result.rows.length > 0) {
|
|
|
|
|
logInfo(`🔄 Status atualizado: ${status} para ticket ${result.rows[0].ticket_number}`, {
|
|
|
|
|
ticket_id: result.rows[0].id,
|
|
|
|
|
ticket_number: result.rows[0].ticket_number,
|
|
|
|
|
glpi_ticket_id: glpiTicketId,
|
|
|
|
|
status: status
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-09-15 20:55:19 -03:00
|
|
|
|
2025-09-11 20:57:43 -03:00
|
|
|
} catch (error) {
|
|
|
|
|
logError(error, `❌ Erro ao atualizar status de sync para ticket ${ticketId}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-05 20:28:15 -03:00
|
|
|
|
2025-09-15 20:55:19 -03:00
|
|
|
// Coleta o status de sincronização para todos os tickets (uso em testes)
|
2025-09-11 20:57:43 -03:00
|
|
|
static async getSyncStatus() {
|
|
|
|
|
try {
|
|
|
|
|
const query = `
|
|
|
|
|
SELECT
|
|
|
|
|
glpi_sync_status,
|
|
|
|
|
COUNT(*) as total,
|
|
|
|
|
COUNT(glpi_ticket_id) as synchronized
|
|
|
|
|
FROM tickets_sn
|
|
|
|
|
GROUP BY glpi_sync_status
|
|
|
|
|
`;
|
2025-09-15 20:55:19 -03:00
|
|
|
|
2025-09-11 20:57:43 -03:00
|
|
|
const result = await pool.query(query);
|
|
|
|
|
return result.rows;
|
2025-09-15 20:55:19 -03:00
|
|
|
|
2025-09-11 20:57:43 -03:00
|
|
|
} catch (error) {
|
|
|
|
|
logError(error, '❌ Erro ao verificar status de sync');
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-05 20:28:15 -03:00
|
|
|
}
|
2025-09-11 20:57:43 -03:00
|
|
|
|
2025-09-15 20:55:19 -03:00
|
|
|
/**
|
|
|
|
|
* @module TicketSnModel
|
|
|
|
|
* @description Este módulo fornece métodos para interagir com a tabela de tickets do ServiceNow (tickets_sn) no banco de dados.
|
|
|
|
|
* Inclui funcionalidades para salvar um ticket, buscar tickets pendentes para sincronização com o GLPI,
|
|
|
|
|
* atualizar o status de sincronização de um ticket, resetar o status de sincronização para testes e verificar o status geral de sincronização.
|
|
|
|
|
*
|
|
|
|
|
* A função saveTicket insere ou atualiza um ticket na tabela tickets_sn.
|
|
|
|
|
* A função getPendingTickets busca tickets que ainda não foram sincronizados com o GLPI e ignora os tickets com status 'Encerrado' ou 'Encerrado - Omitido'.
|
|
|
|
|
* A função updateSyncStatus atualiza o status de sincronização de um ticket específico.
|
|
|
|
|
* A função getSyncStatus retorna um resumo do status de sincronização dos tickets.
|
|
|
|
|
*/
|
2025-09-05 20:28:15 -03:00
|
|
|
module.exports = TicketSnModel;
|