WIP
This commit is contained in:
parent
3b6d78152d
commit
4f7a699f92
@ -1,6 +1,6 @@
|
||||
// src/app.js
|
||||
const { snServices } = require('./controllers/servicenowController');
|
||||
const { glpiServices } = require('./controllers/glpiController');
|
||||
const { processTicketsController } = require('./controllers/processTicketsController');
|
||||
const { processCommentsController } = require('./controllers/processCommentsController');
|
||||
const { logInfo } = require('./utils/logger');
|
||||
|
||||
logInfo('Aplicação iniciada', {
|
||||
@ -10,8 +10,8 @@ logInfo('Aplicação iniciada', {
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
await snServices();
|
||||
await glpiServices();
|
||||
await processTicketsController();
|
||||
await processCommentsController();
|
||||
} catch (error) {
|
||||
logInfo('Erro na aplicação:', error);
|
||||
}
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
// src/controllers/glpiController.js
|
||||
const { syncTicketsToGlpi } = require('../services/glpiTicketService');
|
||||
const { syncCommentsGlpitoSN } = require('../services/glpiCommentService');
|
||||
|
||||
const glpiServices = async () => {
|
||||
//await syncTicketsToGlpi();
|
||||
await syncCommentsGlpitoSN();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
glpiServices
|
||||
}
|
||||
12
src/controllers/processCommentsController.js
Normal file
12
src/controllers/processCommentsController.js
Normal file
@ -0,0 +1,12 @@
|
||||
const {fetchCommentsFromServiceNow } = require('../services/servicenowService');
|
||||
const { syncCommentsGlpitoSN } = require('../services/glpiCommentService');
|
||||
|
||||
|
||||
const processCommentsController = async () => {
|
||||
await fetchCommentsFromServiceNow();
|
||||
await syncCommentsGlpitoSN();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
processCommentsController
|
||||
}
|
||||
19
src/controllers/processTicketsController.js
Normal file
19
src/controllers/processTicketsController.js
Normal file
@ -0,0 +1,19 @@
|
||||
// src/controllers/glpiController.js
|
||||
const { syncTicketsToGlpi } = require('../services/glpiTicketService');
|
||||
const { fetchTicketsFromServiceNow, fetchRequestsFromServiceNow } = require('../services/servicenowService');
|
||||
|
||||
|
||||
const processTicketsController = async () => {
|
||||
|
||||
//Busca Incidentes e Requisições do Service Now e salva no banco intermediario
|
||||
await fetchTicketsFromServiceNow();
|
||||
await fetchRequestsFromServiceNow();
|
||||
|
||||
//Sincroniza os tickets com o GLPI
|
||||
await syncTicketsToGlpi();
|
||||
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
processTicketsController
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
const { fetchTicketsFromServiceNow, fetchRequestsFromServiceNow, fetchCommentsFromServiceNow } = require('../services/servicenowService');
|
||||
|
||||
const snServices = async () => {
|
||||
//await fetchTicketsFromServiceNow();
|
||||
//await fetchRequestsFromServiceNow();
|
||||
await fetchCommentsFromServiceNow(626)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
snServices
|
||||
}
|
||||
@ -252,6 +252,7 @@ class TicketGlpiModel {
|
||||
SELECT id FROM glpi_tickets
|
||||
WHERE name LIKE ? OR content LIKE ?
|
||||
LIMIT 1
|
||||
|
||||
`;
|
||||
|
||||
const [rows] = await glpiPool.execute(query, [
|
||||
|
||||
@ -24,8 +24,7 @@ class TicketSnModel {
|
||||
ramal = EXCLUDED.ramal,
|
||||
telefone = EXCLUDED.telefone,
|
||||
opened_at = EXCLUDED.opened_at,
|
||||
tipo = EXCLUDED.tipo,
|
||||
updated_at = NOW()
|
||||
tipo = EXCLUDED.tipo
|
||||
RETURNING id, xmax = 0 as inserted`;
|
||||
|
||||
const values = [
|
||||
@ -92,8 +91,7 @@ class TicketSnModel {
|
||||
ts.updated_at
|
||||
FROM tickets_sn ts
|
||||
LEFT JOIN ticket_sync tsync ON ts.id = tsync.sn_ticket_id
|
||||
WHERE tsync.id IS NULL
|
||||
AND ts.status NOT IN ('Encerrado', 'Encerrado - Omitido')
|
||||
WHERE tsync.glpi_sync_status = 'pending_check'
|
||||
ORDER BY ts.opened_at
|
||||
`;
|
||||
|
||||
|
||||
@ -1,9 +1,33 @@
|
||||
// src/models/ticketSyncModel.js
|
||||
const { query } = require('winston');
|
||||
const pool = require('../data/database');
|
||||
const { logError } = require('../utils/logger');
|
||||
|
||||
class TicketSyncModel {
|
||||
|
||||
// Cria um registro de sincronização para um ticket
|
||||
static async createSyncRecord(data) {
|
||||
try {
|
||||
const query = `INSERT INTO ticket_sync (
|
||||
sn_ticket_id,
|
||||
glpi_ticket_id,
|
||||
last_sn_sync,
|
||||
last_glpi_sync,
|
||||
created_at,
|
||||
updated_at,
|
||||
sn_sync_status,
|
||||
glpi_sync_status
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id`;
|
||||
|
||||
await pool.query(query, data);
|
||||
|
||||
} catch (error) {
|
||||
logError(error, '❌ Erro ao criar registro de sincronização');
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Buscar tickets que foram criados com sucesso para sincronizar comentários
|
||||
static async getTicketsToSync() {
|
||||
try {
|
||||
@ -51,7 +75,15 @@ class TicketSyncModel {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
static async getByTicketNumber(ticketNumber) {
|
||||
try {
|
||||
const query = 'SELECT * FROM ticket_sync WHERE ticket_number = $1';
|
||||
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -8,7 +8,6 @@ const { logInfo, logError } = require('../utils/logger');
|
||||
const syncTicketsToGlpi = async () => {
|
||||
try {
|
||||
logInfo('🔄 Iniciando sincronização para GLPI...');
|
||||
|
||||
// 1. Buscar tickets pendentes
|
||||
const pendingTickets = await TicketSnModel.getPendingTickets();
|
||||
|
||||
@ -34,21 +33,32 @@ const syncTicketsToGlpi = async () => {
|
||||
|
||||
// Processar um ticket individual
|
||||
const processSingleTicket = async (ticket) => {
|
||||
let syncRecord = null;
|
||||
|
||||
try {
|
||||
logInfo(`🔍 Processando ticket: ${ticket.ticket_number}`);
|
||||
|
||||
// 1. Verificar se já existe no GLPI (evitar duplicatas)
|
||||
// 1. Verificar se já existe no GLPI
|
||||
const existingTicket = await TicketGlpiModel.ticketExists(ticket.ticket_number);
|
||||
if (existingTicket) {
|
||||
logInfo(`⏭️ Ticket já existe no GLPI: ${existingTicket}`);
|
||||
// Criar registro de sincronização indicando que já existe
|
||||
syncRecord = await TicketSyncModel.createSyncRecord(ticket.id, existingTicket);
|
||||
await TicketSyncModel.updateGlpiSyncStatus(syncRecord.id, 'duplicate', existingTicket);
|
||||
const idSyncRecord = await TicketSyncModel.getByTicketNumber(ticket.ticket_number);
|
||||
|
||||
|
||||
if (existingTicket && idSyncRecord) {
|
||||
await TicketSyncModel.updateGlpiSyncStatus(idSyncRecord.id, 'synced', existingTicket);
|
||||
await TicketSyncModel.updateSnSyncStatus(idSyncRecord.id, 'synced');
|
||||
return;
|
||||
}else if (existingTicket && !idSyncRecord) {
|
||||
await TicketSyncModel.createSyncRecord({
|
||||
sn_ticket_id: ticket.id,
|
||||
glpi_ticket_id: existingTicket,
|
||||
sn_sync_status: 'synced',
|
||||
glpi_sync_status: 'synced',
|
||||
last_glpi_sync: new Date()
|
||||
});
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 2. Converter dados para formato GLPI
|
||||
const glpiData = {
|
||||
ticket_number: ticket.ticket_number,
|
||||
@ -71,8 +81,7 @@ const processSingleTicket = async (ticket) => {
|
||||
const glpiTicketId = await TicketGlpiModel.createTicket(glpiData);
|
||||
|
||||
// 4. Criar registro de sincronização
|
||||
syncRecord = await TicketSyncModel.createSyncRecord(ticket.id, glpiTicketId);
|
||||
await TicketSyncModel.updateGlpiSyncStatus(syncRecord.id, 'completed', glpiTicketId);
|
||||
await TicketSyncModel.updateGlpiSyncStatus(idSyncRecord.id, 'synced', glpiTicketId);
|
||||
|
||||
logInfo(`✅ Ticket ${ticket.ticket_number} sincronizado! GLPI ID: ${glpiTicketId}`);
|
||||
|
||||
@ -81,12 +90,12 @@ const processSingleTicket = async (ticket) => {
|
||||
|
||||
// Se temos um registro de sync, atualizar o status para error
|
||||
if (syncRecord) {
|
||||
await TicketSyncModel.updateGlpiSyncStatus(syncRecord.id, 'error');
|
||||
await TicketSyncModel.updateGlpiSyncStatus(idSyncRecord.id, 'error');
|
||||
} else {
|
||||
// Se não temos um registro, criar um com status error
|
||||
try {
|
||||
syncRecord = await TicketSyncModel.createSyncRecord(ticket.id);
|
||||
await TicketSyncModel.updateGlpiSyncStatus(syncRecord.id, 'error');
|
||||
await TicketSyncModel.updateGlpiSyncStatus(idSyncRecord.id, 'error');
|
||||
} catch (innerError) {
|
||||
logError(innerError, `❌ Erro ao registrar falha de sincronização para ticket ${ticket.id}`);
|
||||
}
|
||||
|
||||
@ -15,14 +15,39 @@ const fetchTicketsFromServiceNow = async () => {
|
||||
auth: apiConfig.servicenowAuthentication.auth
|
||||
});
|
||||
|
||||
// comentado para evitar muitos arquivos
|
||||
//fs.writeFileSync('servicenow_tickets.json', JSON.stringify(ticketsResponse.data, null, 2));
|
||||
//logInfo('💾 Backup JSON de incidentes salvo', { count: ticketsResponse.data.result.length });
|
||||
|
||||
// Salvar os tickets no banco de dados
|
||||
logInfo('💾 Iniciando salvamento de incidentes no banco...');
|
||||
for (const ticket of ticketsResponse.data.result) {
|
||||
await TicketSnModel.saveTicket(ticket, 'incidente');
|
||||
|
||||
|
||||
|
||||
//Insere no banco de dados como Incidente
|
||||
const idTableTicketSn =await TicketSnModel.saveTicket(ticket, 'incidente');
|
||||
|
||||
//Salvando atualização no banco intermediario e defininfo sync status
|
||||
const ticketStatus = ticket.state?.display_value;
|
||||
|
||||
const dataSync = {
|
||||
glpi_ticket_id: null,
|
||||
sn_ticket_id: idTableTicketSn,
|
||||
last_sn_sync: new Date(),
|
||||
last_glpi_sync: null,
|
||||
created_at: null,
|
||||
updated_at: null
|
||||
}
|
||||
|
||||
if (ticketStatus === 'Encerrado' || ticketStatus === 'Encerrado - Omitido') {
|
||||
dataSync.sn_sync_status ='closed'
|
||||
dataSync.glpi_sync_status = 'ignored'
|
||||
} else {
|
||||
dataSync.sn_sync_status ='synced'
|
||||
dataSync.glpi_sync_status = 'pending_check'
|
||||
}
|
||||
|
||||
await TicketSyncModel.createSyncRecord(dataSync);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
logSync('ServiceNow', ticketsResponse.data.result.length, 'incidentes');
|
||||
@ -40,18 +65,38 @@ const fetchRequestsFromServiceNow = async () => {
|
||||
auth: apiConfig.servicenowAuthentication.auth
|
||||
});
|
||||
|
||||
// Comentado para evitar muitos arquivos
|
||||
//fs.writeFileSync('servicenow_requests.json', JSON.stringify(requestsResponse.data, null, 2));
|
||||
//logInfo('💾 Backup JSON de requisições salvo', { count: requestsResponse.data.result.length });
|
||||
|
||||
// Salvar as requisições no banco de dados
|
||||
logInfo('💾 Iniciando salvamento de requisições no banco...');
|
||||
for (const request of requestsResponse.data.result) {
|
||||
await TicketSnModel.saveTicket(request, 'requisicao');
|
||||
|
||||
|
||||
//Salvando a requisicao no banco intermediario
|
||||
const idTableTicketSn = await TicketSnModel.saveTicket(request, 'requisicao');
|
||||
|
||||
const dataSync = {
|
||||
glpi_ticket_id: null,
|
||||
sn_ticket_id: idTableTicketSn,
|
||||
last_sn_sync: new Date(),
|
||||
last_glpi_sync: null,
|
||||
created_at: null,
|
||||
updated_at: null
|
||||
}
|
||||
|
||||
if (ticketStatus === 'Encerrado' || ticketStatus === 'Encerrado - Omitido') {
|
||||
dataSync.sn_sync_status ='closed'
|
||||
dataSync.glpi_sync_status = 'ignored'
|
||||
} else {
|
||||
dataSync.sn_sync_status ='collected'
|
||||
dataSync.glpi_sync_status = 'pending_check'
|
||||
}
|
||||
|
||||
//Cria apenas se não existir
|
||||
const exists = await TicketSyncModel.getIdSyncBySnId(idTableTicketSn);
|
||||
if (!exists) await TicketSyncModel.createSyncRecord(dataSync);
|
||||
|
||||
|
||||
}
|
||||
|
||||
logSync('ServiceNow', requestsResponse.data.result.length, 'requisições');
|
||||
|
||||
} catch (error) {
|
||||
logError(error, 'SERVICE NOW Requests');
|
||||
}
|
||||
@ -204,11 +249,21 @@ const syncCommentToServiceNow = async (ticketId, comment) => {
|
||||
// Deve-se atentar para a paginação da API do ServiceNow
|
||||
// para fins de teste, gerar um arquivo JSON com os comentários de um ticket específico
|
||||
|
||||
const fetchCommentsFromServiceNow = async (ticketId) => {
|
||||
const fetchCommentsFromServiceNow = async () => {
|
||||
try {
|
||||
|
||||
const ticketsSyncList = await TicketSyncModel.getTicketsToSync();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
console.log(`Iniciando busca de comentários do ServiceNow para o ticket SN Ticket: ${ticketId}`);
|
||||
|
||||
//Coleta o sys id no bando intermediario para busca no service now
|
||||
const sys_id = await getTicketSysId(ticketId);
|
||||
|
||||
//Se não encontrar é impossível atualizar
|
||||
if (!sys_id) {
|
||||
logError('sys_id não encontrado para ticket', { ticketId });
|
||||
return [];
|
||||
@ -237,6 +292,7 @@ const fetchCommentsFromServiceNow = async (ticketId) => {
|
||||
|
||||
const comments = response?.data?.result || [];
|
||||
|
||||
|
||||
if (!Array.isArray(comments) || comments.length === 0) {
|
||||
break; // não há mais comentários
|
||||
}
|
||||
@ -264,30 +320,31 @@ const fetchCommentsFromServiceNow = async (ticketId) => {
|
||||
// Salva no arquivo
|
||||
fs.writeFileSync(filename, JSON.stringify(allComments, null, 2));
|
||||
|
||||
|
||||
//Trata os dados para inserir no banco intermediario apenas os tickets que não são do sys_created_by: "SOTHIS.CAOA"
|
||||
//Trata os dados para inserir no banco intermediario a
|
||||
// as os tickets que não são do sys_created_by: "SOTHIS.CAOA"
|
||||
const filteredComments = allComments.filter(comment => comment.sys_created_by !== 'SOTHIS.CAOA');
|
||||
|
||||
|
||||
|
||||
// Insere cada comment no banco intermediario
|
||||
for (const comment of filteredComments) {
|
||||
|
||||
|
||||
const existingComment = await TicketUpdateModel.getCommentSync(ticketId, comment.sys_id);
|
||||
|
||||
|
||||
if (existingComment) {
|
||||
logInfo(`Comentário: ${comment.sys_id} já existe no banco de dados`, { step: 3 });
|
||||
continue;
|
||||
} else {
|
||||
|
||||
|
||||
console.log(comment);
|
||||
|
||||
const ticketSnId = await TicketSyncModel.getIdSyncBySnId(ticketId);
|
||||
const ticketSyncId = await TicketSyncModel.getIdSyncBySnId(ticketId);
|
||||
|
||||
|
||||
const commentData = {
|
||||
ticket_sync_id: parseInt(ticketSnId),
|
||||
ticket_sync_id: ticketSyncId,
|
||||
source_system: 'servicenow',
|
||||
content: comment.value,
|
||||
created_at: new Date(comment.sys_created_on),
|
||||
@ -299,9 +356,22 @@ const fetchCommentsFromServiceNow = async (ticketId) => {
|
||||
|
||||
commentInserted = await TicketUpdateModel.insert(commentData);
|
||||
|
||||
if (!ticketSyncId) {
|
||||
logError(`❌ TicketSyncId não encontrado para o ticket SN ID: ${ticketId}`, { step: 4 });
|
||||
continue;
|
||||
}
|
||||
|
||||
const commentInserted = await TicketUpdateModel.insert(commentData);
|
||||
|
||||
if (commentInserted.success) {
|
||||
// Insere Glpi status como pending e SN status como synced
|
||||
await TicketSyncModel.updateStatus(ticketGlpiId.id, 'pending', 'synced');
|
||||
// Insere Glpi status como
|
||||
// ding e SN status como synced
|
||||
await TicketSyncModel.updateStatus(ticketGlpiId.id, '
|
||||
ding', 'synced');
|
||||
// Atualiza o status para indicar que o SN está sincronizado e o GLPI está
|
||||
// dente de atualização
|
||||
await TicketSyncModel.updateStatus(ticketSyncId, '
|
||||
ding', 'synced');
|
||||
logInfo(`✅ Comentário ID ${comment.sys_id} inserido no banco! Novo ID: ${commentInserted.newId}`, { step: 4 });
|
||||
} else {
|
||||
logError(`❌ Erro ao inserir comentário ID ${comment.sys_id} no banco: ${commentInserted.error}`, { step: 4 });
|
||||
|
||||
Loading…
Reference in New Issue
Block a user