FEAT: Sistema de sincroanização de tickets refeito para melhor sincronia e garantia dos dados
This commit is contained in:
parent
4f7a699f92
commit
f7eaf34e7d
@ -1,4 +1,5 @@
|
|||||||
// src/app.js
|
// src/app.js
|
||||||
|
require('dotenv').config();
|
||||||
const { processTicketsController } = require('./controllers/processTicketsController');
|
const { processTicketsController } = require('./controllers/processTicketsController');
|
||||||
const { processCommentsController } = require('./controllers/processCommentsController');
|
const { processCommentsController } = require('./controllers/processCommentsController');
|
||||||
const { logInfo } = require('./utils/logger');
|
const { logInfo } = require('./utils/logger');
|
||||||
@ -10,7 +11,7 @@ logInfo('Aplicação iniciada', {
|
|||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
try {
|
try {
|
||||||
await processTicketsController();
|
//await processTicketsController();
|
||||||
await processCommentsController();
|
await processCommentsController();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logInfo('Erro na aplicação:', error);
|
logInfo('Erro na aplicação:', error);
|
||||||
|
|||||||
@ -1,12 +1,19 @@
|
|||||||
const {fetchCommentsFromServiceNow } = require('../services/servicenowService');
|
const {fetchCommentsFromServiceNow } = require('../services/servicenowService');
|
||||||
const { syncCommentsGlpitoSN } = require('../services/glpiCommentService');
|
const { syncCommentsGlpitoSN, syncCommentsSNtoGlpi } = require('../services/glpiCommentService');
|
||||||
|
const TicketSyncModel = require('../models/ticketSyncModel');
|
||||||
|
|
||||||
|
|
||||||
const processCommentsController = async () => {
|
const processCommentsController = async () => {
|
||||||
await fetchCommentsFromServiceNow();
|
|
||||||
await syncCommentsGlpitoSN();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const ticketsSynced = await TicketSyncModel.getTicketsSynced();
|
||||||
|
|
||||||
|
for (ticket of ticketsSynced){
|
||||||
|
const comments = await fetchCommentsFromServiceNow(ticket);
|
||||||
|
if(comments.length > 0){await syncCommentsSNtoGlpi(comments, ticket);}
|
||||||
|
await syncCommentsGlpitoSN(ticket);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
module.exports = {
|
module.exports = {
|
||||||
processCommentsController
|
processCommentsController
|
||||||
}
|
}
|
||||||
@ -249,27 +249,27 @@ class TicketGlpiModel {
|
|||||||
static async ticketExists(snTicketNumber) {
|
static async ticketExists(snTicketNumber) {
|
||||||
try {
|
try {
|
||||||
const query = `
|
const query = `
|
||||||
SELECT id FROM glpi_tickets
|
SELECT id FROM glpi_tickets WHERE name LIKE ? OR content LIKE ? LIMIT 1
|
||||||
WHERE name LIKE ? OR content LIKE ?
|
|
||||||
LIMIT 1
|
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const [rows] = await glpiPool.execute(query, [
|
const values = [
|
||||||
`%${snTicketNumber}%`,
|
`%${snTicketNumber}%`,
|
||||||
`%${snTicketNumber}%`
|
`%${snTicketNumber}%`
|
||||||
]);
|
];
|
||||||
|
|
||||||
|
|
||||||
|
const [rows] = await glpiPool.execute(query,values);
|
||||||
|
|
||||||
return rows.length > 0 ? rows[0].id : null;
|
return rows.length > 0 ? rows[0].id : null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logError(error, 'Erro ao verificar ticket existente');
|
logError(`Erro ao verificar ticket existente: ${error}`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Buscar followups (notas) de um ticket no GLPI
|
// Buscar followups (notas) de um ticket no GLPI
|
||||||
static async getFollowupsByItemId(itemId) {
|
static async getFollowupsByItemId(itemId) {
|
||||||
try {
|
try {
|
||||||
const query = 'SELECT id, content, date, date_creation, date_mod FROM glpi_itilfollowups WHERE items_id = ?';
|
const query = 'SELECT id, content, date, date_creation, date_mod FROM glpi_itilfollowups WHERE items_id = ? AND users_id != 971';
|
||||||
const [rows] = await glpiPool.execute(query, [itemId]);
|
const [rows] = await glpiPool.execute(query, [itemId]);
|
||||||
return rows;
|
return rows;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -278,9 +278,20 @@ class TicketGlpiModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
static async insertComment(comment, items_id) {
|
||||||
|
try {
|
||||||
|
const query = 'INSERT INTO glpi_itilfollowups(itemtype, items_id, date, users_id, content, date_creation, date_mod, timeline_position) VALUES(?, ?, ?, ?, ?, ?, ?, ?) RETURNING id';
|
||||||
|
const values = ['Ticket', items_id, comment.created_at, 971, comment.content , comment.created_at, comment.created_at, 1];
|
||||||
|
|
||||||
|
const [rows] = await glpiPool.execute(query, values);
|
||||||
|
return rows;
|
||||||
|
}catch(error){
|
||||||
|
logError(`Erro ao inserir o ticket: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @module TicketGlpiModel
|
* @module TicketGlpiModel
|
||||||
* @description Este módulo fornece métodos para gerenciar tickets no GLPI.
|
* @description Este módulo fornece métodos para gerenciar tickets no GLPI.
|
||||||
|
|||||||
@ -88,7 +88,8 @@ class TicketSnModel {
|
|||||||
ts.opened_at,
|
ts.opened_at,
|
||||||
ts.tipo,
|
ts.tipo,
|
||||||
ts.created_at,
|
ts.created_at,
|
||||||
ts.updated_at
|
ts.updated_at,
|
||||||
|
tsync.glpi_ticket_id
|
||||||
FROM tickets_sn ts
|
FROM tickets_sn ts
|
||||||
LEFT JOIN ticket_sync tsync ON ts.id = tsync.sn_ticket_id
|
LEFT JOIN ticket_sync tsync ON ts.id = tsync.sn_ticket_id
|
||||||
WHERE tsync.glpi_sync_status = 'pending_check'
|
WHERE tsync.glpi_sync_status = 'pending_check'
|
||||||
|
|||||||
@ -8,6 +8,8 @@ class TicketSyncModel {
|
|||||||
// Cria um registro de sincronização para um ticket
|
// Cria um registro de sincronização para um ticket
|
||||||
static async createSyncRecord(data) {
|
static async createSyncRecord(data) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
|
||||||
const query = `INSERT INTO ticket_sync (
|
const query = `INSERT INTO ticket_sync (
|
||||||
sn_ticket_id,
|
sn_ticket_id,
|
||||||
glpi_ticket_id,
|
glpi_ticket_id,
|
||||||
@ -19,7 +21,20 @@ class TicketSyncModel {
|
|||||||
glpi_sync_status
|
glpi_sync_status
|
||||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id`;
|
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id`;
|
||||||
|
|
||||||
await pool.query(query, data);
|
const values = [
|
||||||
|
data.sn_ticket_id,
|
||||||
|
data.glpi_ticket,
|
||||||
|
data.last_sn_sync,
|
||||||
|
data.last_glpi_sync,
|
||||||
|
data.created_at,
|
||||||
|
data.updated_at,
|
||||||
|
data.sn_sync_status,
|
||||||
|
data.glpi_sync_status
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
await pool.query(query, values);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logError(error, '❌ Erro ao criar registro de sincronização');
|
logError(error, '❌ Erro ao criar registro de sincronização');
|
||||||
@ -29,10 +44,11 @@ class TicketSyncModel {
|
|||||||
|
|
||||||
|
|
||||||
// Buscar tickets que foram criados com sucesso para sincronizar comentários
|
// Buscar tickets que foram criados com sucesso para sincronizar comentários
|
||||||
static async getTicketsToSync() {
|
static async getTicketsToSync(glpi_sync_status, sn_sync_status) {
|
||||||
try {
|
try {
|
||||||
const query = 'SELECT id, glpi_ticket_id, sn_ticket_id FROM ticket_sync';
|
const query = 'SELECT id, glpi_ticket_id, sn_ticket_id FROM ticket_sync WHERE glpi_sync_status = $1 AND sn_sync_status = $2';
|
||||||
const { rows } = await pool.query(query);
|
const values =[glpi_sync_status , sn_sync_status];
|
||||||
|
const { rows } = await pool.query(query, values);
|
||||||
return rows;
|
return rows;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logError(error, '❌ Erro ao buscar tickets para sincronizar');
|
logError(error, '❌ Erro ao buscar tickets para sincronizar');
|
||||||
@ -43,15 +59,24 @@ class TicketSyncModel {
|
|||||||
// Atualiza o status de sincronização de um ticket
|
// Atualiza o status de sincronização de um ticket
|
||||||
static async updateStatus(ticketSyncId, glpiStatus, snStatus) {
|
static async updateStatus(ticketSyncId, glpiStatus, snStatus) {
|
||||||
try {
|
try {
|
||||||
const query = 'UPDATE ticket_sync SET glpi_sync_status = $1, sn_sync_status = $2 WHERE id = $3';
|
const query = 'UPDATE ticket_sync SET glpi_sync_status = $1, sn_sync_status = $2 WHERE sn_ticket_id = $3';
|
||||||
await pool.query(query, [glpiStatus, snStatus, ticketSyncId]);
|
await pool.query(query, [glpiStatus, snStatus, ticketSyncId]);
|
||||||
|
} catch (error) {
|
||||||
|
logError(`${error} ❌ Erro ao atualizar status de sincronização para o ID: ${ticketSyncId}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async updateStatusAndGlpiTicket(ticketSyncId, glpiStatus, snStatus, glpiTicketId) {
|
||||||
|
try {
|
||||||
|
const query = 'UPDATE ticket_sync SET glpi_sync_status = $1, sn_sync_status = $2, glpi_ticket_id = $4 WHERE sn_ticket_id = $3';
|
||||||
|
await pool.query(query, [glpiStatus, snStatus, ticketSyncId, glpiTicketId]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logError(error, `❌ Erro ao atualizar status de sincronização para o ID: ${ticketSyncId}`);
|
logError(error, `❌ Erro ao atualizar status de sincronização para o ID: ${ticketSyncId}`);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Buscar o ID de sincronização baseado no glpi_ticket_id
|
// Buscar o ID de sincronização baseado no glpi_ticket_id
|
||||||
static async getIdSyncByGlpiId(glpiTicketId) {
|
static async getIdSyncByGlpiId(glpiTicketId) {
|
||||||
try {
|
try {
|
||||||
@ -77,14 +102,38 @@ class TicketSyncModel {
|
|||||||
}
|
}
|
||||||
static async getByTicketNumber(ticketNumber) {
|
static async getByTicketNumber(ticketNumber) {
|
||||||
try {
|
try {
|
||||||
const query = 'SELECT * FROM ticket_sync WHERE ticket_number = $1';
|
console.log(ticketNumber)
|
||||||
|
const query = 'SELECT * FROM ticket_sync WHERE sn_ticket_id = $1';
|
||||||
|
const { rows } = await pool.query(query, [ticketNumber]);
|
||||||
|
return rows[0] || null;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
logError(`${error}, ❌ Erro ao buscar ticket ${ticketNumber}}` )
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Buscar tickets que estão sincronizados
|
||||||
|
static async getTicketsSynced() {
|
||||||
|
try {
|
||||||
|
const query = `
|
||||||
|
SELECT
|
||||||
|
tsync.id,
|
||||||
|
tsync.glpi_ticket_id,
|
||||||
|
tsync.sn_ticket_id,
|
||||||
|
tsn.sys_id
|
||||||
|
FROM ticket_sync tsync
|
||||||
|
JOIN tickets_sn tsn ON tsync.sn_ticket_id = tsn.id
|
||||||
|
WHERE tsync.glpi_sync_status = 'synced' AND tsync.sn_sync_status = 'synced'`;
|
||||||
|
const { rows } = await pool.query(query);
|
||||||
|
return rows;
|
||||||
|
} catch (error) {
|
||||||
|
logError(error, '❌ Erro ao buscar tickets para sincronizar');
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
// src/models/ticketUpdateModel.js
|
// src/models/ticketUpdateModel.js
|
||||||
|
const ticketSyncModel = require('./ticketSyncModel');
|
||||||
const pool = require('../data/database');
|
const pool = require('../data/database');
|
||||||
const { logError } = require('../utils/logger');
|
const { logError } = require('../utils/logger');
|
||||||
|
|
||||||
|
|
||||||
class TicketUpdateModel {
|
class TicketUpdateModel {
|
||||||
|
|
||||||
static async insert(updateData) {
|
static async insert(updateData) {
|
||||||
@ -17,7 +19,7 @@ class TicketUpdateModel {
|
|||||||
created_at = $6,
|
created_at = $6,
|
||||||
updated_at = $7,
|
updated_at = $7,
|
||||||
destiny_id = $9
|
destiny_id = $9
|
||||||
RETURNING id;
|
RETURNING *;
|
||||||
`;
|
`;
|
||||||
const values = [
|
const values = [
|
||||||
updateData.ticket_sync_id,
|
updateData.ticket_sync_id,
|
||||||
@ -42,7 +44,7 @@ class TicketUpdateModel {
|
|||||||
//Buscar o ID de sincronização baseado no source_id
|
//Buscar o ID de sincronização baseado no source_id
|
||||||
static async getIdSyncBySourceId(sourceId) {
|
static async getIdSyncBySourceId(sourceId) {
|
||||||
try {
|
try {
|
||||||
const query = 'SELECT id FROM ticket_updates WHERE source_id = $1';
|
const query = 'SELECT tsync.id FROM ticket_updates tupd JOIN ticket_sync tsync ON tupd.ticket_sync_id = tsync.id WHERE source_id = $1';
|
||||||
const { rows } = await pool.query(query, [sourceId]);
|
const { rows } = await pool.query(query, [sourceId]);
|
||||||
return rows[0] ? rows[0].id : null;
|
return rows[0] ? rows[0].id : null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -65,10 +67,9 @@ class TicketUpdateModel {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static async getCommentSync(glpiTicketId, sourceId) {
|
static async getCommentSync(sourceId) {
|
||||||
const ticketSyncModel = require('./ticketSyncModel');
|
|
||||||
const ticketSyncId = await TicketUpdateModel.getIdSyncBySourceId(sourceId);
|
|
||||||
|
|
||||||
|
const ticketSyncId = await TicketUpdateModel.getIdSyncBySourceId(sourceId);
|
||||||
if (!ticketSyncId) {
|
if (!ticketSyncId) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,123 +6,110 @@ const TicketUpdateModel = require('../models/ticketUpdateModel');
|
|||||||
const { logInfo, logError } = require('../utils/logger');
|
const { logInfo, logError } = require('../utils/logger');
|
||||||
const { existingCommentInServiceNow, syncCommentToServiceNow } = require('./servicenowService');
|
const { existingCommentInServiceNow, syncCommentToServiceNow } = require('./servicenowService');
|
||||||
const { sanitizeGLPIComment } = require('../utils/commentSanitizer');
|
const { sanitizeGLPIComment } = require('../utils/commentSanitizer');
|
||||||
|
const { log } = require('winston');
|
||||||
|
|
||||||
// Sincroniza comentários do GLPI para o Banco de Dados local
|
// Sincroniza comentários do GLPI para o Banco de Dados local
|
||||||
const syncCommentsGlpitoSN = async () => {
|
const syncCommentsGlpitoSN = async (ticket) => {
|
||||||
try {
|
try {
|
||||||
|
logInfo('🔄 Iniciando sincronização de comments do GLPI para o Service Now...');
|
||||||
logInfo('🔄 Iniciando sincronização de comments do GLPI para o Service Now...');
|
|
||||||
const effectiveTickets = await TicketSyncModel.getTicketsToSync();
|
|
||||||
logInfo(`🔍 ${effectiveTickets.length} tickets encontrados para sincronização de comentários`, { step: 1 });
|
|
||||||
|
|
||||||
for (const ticket of effectiveTickets) {
|
const comments = await TicketGlpiModel.getFollowupsByItemId(ticket.glpi_ticket_id);
|
||||||
|
if (comments.length === 0) {
|
||||||
|
logInfo(`Nenhum comentário encontrado no GLPI para o ticket GLPI ID: ${ticket.glpi_ticket_id}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
logInfo(`Buscando comentários para o ticket GLPI ID: ${ticket.glpi_ticket_id}`);
|
logInfo(`📝 ${comments.length} comentários encontrados (GLPI ID: ${ticket.glpi_ticket_id})`);
|
||||||
const comments = await TicketGlpiModel.getFollowupsByItemId(ticket.glpi_ticket_id);
|
await TicketSyncModel.updateStatus(ticket.glpi_ticket_id, 'comment_collected', 'pending_cmt_check');
|
||||||
logInfo(`📝 ${comments.length} comentários encontrados para o ticket GLPI ID: ${ticket.glpi_ticket_id}`, { step: 2 });
|
|
||||||
|
|
||||||
for (const comment of comments) {
|
for (const comment of comments) {
|
||||||
|
try {
|
||||||
|
const sanitized = sanitizeGLPIComment(comment);
|
||||||
|
const existingComment = await TicketUpdateModel.getCommentSync(ticket.glpi_ticket_id, comment.id);
|
||||||
|
const existsInSN = await existingCommentInServiceNow(ticket.sn_ticket_id, sanitized);
|
||||||
|
const syncId = await TicketSyncModel.getIdSyncByGlpiId(ticket.glpi_ticket_id);
|
||||||
|
|
||||||
// DEBUG: Mostra antes e depois
|
// Caso 1: já sincronizado
|
||||||
logInfo(`🔍 Processando comentário GLPI ID: ${comment.id}`);
|
if (existingComment && existsInSN) {
|
||||||
logInfo(`📝 CONTEÚDO ORIGINAL: ${comment.content.substring(0, 200)}...`);
|
logInfo(`🔁 Comentário ${comment.id} já sincronizado.`);
|
||||||
|
await TicketSyncModel.updateStatus(ticket.glpi_ticket_id, 'synced', 'synced');
|
||||||
// Sanitiza o comentário
|
continue;
|
||||||
const sanitizedContent = sanitizeGLPIComment(comment);
|
|
||||||
|
|
||||||
logInfo(`✨ CONTEÚDO SANITIZADO: ${sanitizedContent.substring(0, 200)}...`);
|
|
||||||
|
|
||||||
const existingComment = await TicketUpdateModel.getCommentSync(ticket.glpi_ticket_id, comment.id);
|
|
||||||
|
|
||||||
const commentExistsInSN = await existingCommentInServiceNow(
|
|
||||||
ticket.sn_ticket_id,
|
|
||||||
sanitizedContent // ✅ Usa o conteúdo sanitizado
|
|
||||||
);
|
|
||||||
|
|
||||||
if (existingComment && commentExistsInSN) {
|
|
||||||
logInfo(`Comentário ID ${comment.id} já sincronizado no banco de dados e no ServiceNow`, { step: 3 });
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!existingComment && commentExistsInSN) {
|
|
||||||
// Só insere no banco, pois já está no ServiceNow
|
|
||||||
const sync_id = await TicketSyncModel.getIdSyncByGlpiId(ticket.glpi_ticket_id);
|
|
||||||
const commentData = {
|
|
||||||
ticket_sync_id: sync_id,
|
|
||||||
source_system: 'glpi',
|
|
||||||
content: sanitizedContent, // ✅ Usa o conteúdo sanitizado
|
|
||||||
created_at: new Date(comment.date_creation),
|
|
||||||
author: 'Sothis',
|
|
||||||
updated_at: new Date(comment.date_mod),
|
|
||||||
source_id: comment.id,
|
|
||||||
destiny_id: commentExistsInSN.destiny_id
|
|
||||||
};
|
|
||||||
const insertResult = await TicketUpdateModel.insert(commentData);
|
|
||||||
if (insertResult.success) {
|
|
||||||
logInfo(`✅ Comentário ID ${comment.id} inserido no banco! Novo ID: ${insertResult.newId}`, { step: 4 });
|
|
||||||
} else {
|
|
||||||
logError(`❌ Erro ao inserir comentário ID ${comment.id} no banco: ${insertResult.error}`, { step: 4 });
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existingComment && !commentExistsInSN) {
|
|
||||||
logInfo(`Comentário ID ${comment.id} já existe no banco, mas não no ServiceNow. Sincronizando...`, { step: 3 });
|
|
||||||
// Só sincroniza no ServiceNow
|
|
||||||
const snCommentId = await syncCommentToServiceNow(ticket.sn_ticket_id, sanitizedContent); // ✅ Usa o conteúdo sanitizado
|
|
||||||
logInfo(`✅ Comentário ID ${comment.id} sincronizado no ServiceNow!`, { step: 4 });
|
|
||||||
const updateData = await TicketUpdateModel.updateDestinyId(comment.id, snCommentId);
|
|
||||||
if (updateData && updateData.success) {
|
|
||||||
logInfo(`✅ ID do comentário de destino atualizado no banco: ${snCommentId}`, { step: 4 });
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Se não existe em nenhum dos dois, sincroniza nos dois normalmente
|
|
||||||
const snCommentId = await syncCommentToServiceNow(ticket.sn_ticket_id, sanitizedContent); // ✅ Usa o conteúdo sanitizado
|
|
||||||
const sync_id = await TicketSyncModel.getIdSyncByGlpiId(ticket.glpi_ticket_id);
|
|
||||||
const commentData = {
|
|
||||||
ticket_sync_id: sync_id,
|
|
||||||
source_system: 'glpi',
|
|
||||||
content: sanitizedContent, // ✅ Usa o conteúdo sanitizado
|
|
||||||
created_at: new Date(comment.date_creation),
|
|
||||||
updated_at: new Date(comment.date_mod),
|
|
||||||
source_id: comment.id,
|
|
||||||
destiny_id: snCommentId
|
|
||||||
};
|
|
||||||
const insertResult = await TicketUpdateModel.insert(commentData);
|
|
||||||
if (insertResult.success) {
|
|
||||||
logInfo(`✅ Comentário ID ${comment.id} sincronizado no banco e no ServiceNow! Novo ID: ${insertResult.newId}`, { step: 4 });
|
|
||||||
} else {
|
|
||||||
logError(`❌ Erro ao sincronizar comentário ID ${comment.id}: ${insertResult.error}`, { step: 4 });
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasError = !insertResult.success;
|
|
||||||
const snStatus = 'pending';
|
|
||||||
const glpiStatus = hasError ? 'Error' : 'updated';
|
|
||||||
await TicketSyncModel.updateStatus(ticket.id, glpiStatus, snStatus);
|
|
||||||
logInfo(`Status de sincronização para o ticket ID ${ticket.id} atualizado para GLPI: '${glpiStatus}', SN: '${snStatus}'.`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logInfo('✅ Sincronização de comentários do GLPI -> ServiceNow concluída!');
|
// Caso 2: está no SN mas não no banco
|
||||||
} catch (error) {
|
if (!existingComment && existsInSN) {
|
||||||
logError(error, 'GLPI Tickets');
|
await TicketUpdateModel.insert({
|
||||||
|
ticket_sync_id: syncId,
|
||||||
|
source_system: 'glpi',
|
||||||
|
content: sanitized,
|
||||||
|
created_at: new Date(comment.date_creation),
|
||||||
|
updated_at: new Date(comment.date_mod),
|
||||||
|
source_id: comment.id,
|
||||||
|
destiny_id: existsInSN.destiny_id
|
||||||
|
});
|
||||||
|
await TicketSyncModel.updateStatus(ticket.glpi_ticket_id, 'synced', 'synced');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Caso 3: existe no banco mas falta no SN
|
||||||
|
if (existingComment && !existsInSN) {
|
||||||
|
await TicketSyncModel.updateStatus(ticket.glpi_ticket_id, 'synced', 'pending_cmt_sync');
|
||||||
|
const snCommentId = await syncCommentToServiceNow(ticket.sn_ticket_id, sanitized);
|
||||||
|
await TicketUpdateModel.updateDestinyId(comment.id, snCommentId);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Caso 4: não existe em nenhum lugar → sincroniza ambos
|
||||||
|
await TicketSyncModel.updateStatus(ticket.glpi_ticket_id, 'pending_cmt_sync', 'pending_cmt_sync');
|
||||||
|
const snCommentId = await syncCommentToServiceNow(ticket.sn_ticket_id, sanitized);
|
||||||
|
await TicketUpdateModel.insert({
|
||||||
|
ticket_sync_id: syncId,
|
||||||
|
source_system: 'glpi',
|
||||||
|
content: sanitized,
|
||||||
|
created_at: new Date(comment.date_creation),
|
||||||
|
updated_at: new Date(comment.date_mod),
|
||||||
|
source_id: comment.id,
|
||||||
|
destiny_id: snCommentId
|
||||||
|
});
|
||||||
|
await TicketSyncModel.updateStatus(ticket.glpi_ticket_id, 'synced', 'synced');
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
logError(`Erro ao sincronizar comentário ${comment.id}: ${err}`);
|
||||||
|
await TicketSyncModel.updateStatus(ticket.glpi_ticket_id, 'error', 'error');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const syncCommentsSNtoGlpi = async () => {
|
logInfo('✅ Sincronização de comentários GLPI → SN concluída!');
|
||||||
try {
|
} catch (error) {
|
||||||
logInfo('🔄 Iniciando sincronização de comments do Service Now para o GLPI...');
|
logError(`Erro geral na sincronização: ${error}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const syncCommentsSNtoGlpi = async (comments, ticket) => {
|
||||||
|
logInfo('🔄 Iniciando sincronização de comments do ServiceNow para o GLPI...');
|
||||||
|
|
||||||
|
for (const comment of comments) {
|
||||||
|
try {
|
||||||
|
const commentId = await TicketGlpiModel.insertComment(comment, ticket.glpi_ticket_id);
|
||||||
|
|
||||||
|
await TicketSyncModel.updateStatus(ticket.sn_ticket_id, 'synced', 'synced');
|
||||||
|
await TicketUpdateModel.updateDestinyId(comment.sys_id, commentId);
|
||||||
|
|
||||||
|
logInfo(`✅ Comentário inserido com sucesso no GLPI para o chamado: ${ticket.glpi_ticket_id}`);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logError(`⚠️ Erro ao processar comentário do ticket ${ticket.sn_ticket_id}: ${error}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await TicketSyncModel.updateStatus(ticket.sn_ticket_id, 'synced', 'error');
|
||||||
|
} catch (err2) {
|
||||||
|
logError(`Falha ao atualizar status de erro do ticket ${ticket.sn_ticket_id}: ${err2}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (error) {
|
};
|
||||||
logError(error, 'ServiceNow Comments');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -37,29 +37,25 @@ const processSingleTicket = async (ticket) => {
|
|||||||
try {
|
try {
|
||||||
logInfo(`🔍 Processando ticket: ${ticket.ticket_number}`);
|
logInfo(`🔍 Processando ticket: ${ticket.ticket_number}`);
|
||||||
|
|
||||||
// 1. Verificar se já existe no GLPI
|
// 1. Verifica se o ticket já possui um ID do GLPI no nosso banco de sincronização.
|
||||||
const existingTicket = await TicketGlpiModel.ticketExists(ticket.ticket_number);
|
if (ticket.glpi_ticket_id) {
|
||||||
const idSyncRecord = await TicketSyncModel.getByTicketNumber(ticket.ticket_number);
|
logInfo(`✅ Ticket ${ticket.ticket_number} já possui GLPI ID (${ticket.glpi_ticket_id}) registrado. Apenas atualizando status.`);
|
||||||
|
await TicketSyncModel.updateStatus(ticket.id, 'synced', 'synced');
|
||||||
|
|
||||||
if (existingTicket && idSyncRecord) {
|
|
||||||
await TicketSyncModel.updateGlpiSyncStatus(idSyncRecord.id, 'synced', existingTicket);
|
|
||||||
await TicketSyncModel.updateSnSyncStatus(idSyncRecord.id, 'synced');
|
|
||||||
return;
|
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. Se não tem ID local, verifica se o ticket existe no GLPI (buscando pelo número).
|
||||||
|
const existingTicket = await TicketGlpiModel.ticketExists(ticket.ticket_number);
|
||||||
|
|
||||||
// 2. Converter dados para formato GLPI
|
if (existingTicket) {
|
||||||
|
logInfo(`✅ Ticket ${ticket.ticket_number} encontrado no GLPI. Atualizando registro local com o ID: ${existingTicket}`);
|
||||||
|
// Atualiza nosso registro de sincronização com o ID do GLPI encontrado.
|
||||||
|
await TicketSyncModel.updateStatusAndGlpiTicket(ticket.id, 'synced', 'synced', existingTicket);
|
||||||
|
logInfo(`✅ Ticket ${ticket.ticket_number} já existe no GLPI! GLPI ID: ${existingTicket}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Converter dados para formato GLPI
|
||||||
const glpiData = {
|
const glpiData = {
|
||||||
ticket_number: ticket.ticket_number,
|
ticket_number: ticket.ticket_number,
|
||||||
short_description: ticket.short_description,
|
short_description: ticket.short_description,
|
||||||
@ -80,8 +76,8 @@ const processSingleTicket = async (ticket) => {
|
|||||||
// 3. Inserir no GLPI
|
// 3. Inserir no GLPI
|
||||||
const glpiTicketId = await TicketGlpiModel.createTicket(glpiData);
|
const glpiTicketId = await TicketGlpiModel.createTicket(glpiData);
|
||||||
|
|
||||||
// 4. Criar registro de sincronização
|
// 4. Atualizar registro de sincronização
|
||||||
await TicketSyncModel.updateGlpiSyncStatus(idSyncRecord.id, 'synced', glpiTicketId);
|
await TicketSyncModel.updateStatus(ticket.id, 'synced', 'synced');
|
||||||
|
|
||||||
logInfo(`✅ Ticket ${ticket.ticket_number} sincronizado! GLPI ID: ${glpiTicketId}`);
|
logInfo(`✅ Ticket ${ticket.ticket_number} sincronizado! GLPI ID: ${glpiTicketId}`);
|
||||||
|
|
||||||
@ -89,16 +85,10 @@ const processSingleTicket = async (ticket) => {
|
|||||||
logError(error, `❌ Erro no ticket ${ticket.ticket_number}`);
|
logError(error, `❌ Erro no ticket ${ticket.ticket_number}`);
|
||||||
|
|
||||||
// Se temos um registro de sync, atualizar o status para error
|
// Se temos um registro de sync, atualizar o status para error
|
||||||
if (syncRecord) {
|
try {
|
||||||
await TicketSyncModel.updateGlpiSyncStatus(idSyncRecord.id, 'error');
|
await TicketSyncModel.updateStatus(idSyncRecord.id, 'error', 'synced');
|
||||||
} else {
|
} catch (innerError) {
|
||||||
// Se não temos um registro, criar um com status error
|
|
||||||
try {
|
|
||||||
syncRecord = await TicketSyncModel.createSyncRecord(ticket.id);
|
|
||||||
await TicketSyncModel.updateGlpiSyncStatus(idSyncRecord.id, 'error');
|
|
||||||
} catch (innerError) {
|
|
||||||
logError(innerError, `❌ Erro ao registrar falha de sincronização para ticket ${ticket.id}`);
|
logError(innerError, `❌ Erro ao registrar falha de sincronização para ticket ${ticket.id}`);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -18,10 +18,8 @@ const fetchTicketsFromServiceNow = async () => {
|
|||||||
logInfo('💾 Iniciando salvamento de incidentes no banco...');
|
logInfo('💾 Iniciando salvamento de incidentes no banco...');
|
||||||
for (const ticket of ticketsResponse.data.result) {
|
for (const ticket of ticketsResponse.data.result) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Insere no banco de dados como Incidente
|
//Insere no banco de dados como Incidente
|
||||||
const idTableTicketSn =await TicketSnModel.saveTicket(ticket, 'incidente');
|
const idTableTicketSn = await TicketSnModel.saveTicket(ticket, 'incidente');
|
||||||
|
|
||||||
//Salvando atualização no banco intermediario e defininfo sync status
|
//Salvando atualização no banco intermediario e defininfo sync status
|
||||||
const ticketStatus = ticket.state?.display_value;
|
const ticketStatus = ticket.state?.display_value;
|
||||||
@ -42,12 +40,10 @@ const fetchTicketsFromServiceNow = async () => {
|
|||||||
dataSync.sn_sync_status ='synced'
|
dataSync.sn_sync_status ='synced'
|
||||||
dataSync.glpi_sync_status = 'pending_check'
|
dataSync.glpi_sync_status = 'pending_check'
|
||||||
}
|
}
|
||||||
|
//Cria apenas se não existir
|
||||||
await TicketSyncModel.createSyncRecord(dataSync);
|
const exists = await TicketSyncModel.getIdSyncBySnId(idTableTicketSn);
|
||||||
|
if (!exists) await TicketSyncModel.createSyncRecord(dataSync);
|
||||||
|
else await TicketSyncModel.updateStatus(exists, 'synced', 'pending_check');
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logSync('ServiceNow', ticketsResponse.data.result.length, 'incidentes');
|
logSync('ServiceNow', ticketsResponse.data.result.length, 'incidentes');
|
||||||
@ -82,7 +78,7 @@ const fetchRequestsFromServiceNow = async () => {
|
|||||||
updated_at: null
|
updated_at: null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ticketStatus === 'Encerrado' || ticketStatus === 'Encerrado - Omitido') {
|
if (request.state?.display_value === 'Encerrado' || request.state?.display_value === 'Encerrado - Omitido') {
|
||||||
dataSync.sn_sync_status ='closed'
|
dataSync.sn_sync_status ='closed'
|
||||||
dataSync.glpi_sync_status = 'ignored'
|
dataSync.glpi_sync_status = 'ignored'
|
||||||
} else {
|
} else {
|
||||||
@ -103,7 +99,6 @@ const fetchRequestsFromServiceNow = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Função para obter o sys_id de um ticket pela tabela de sincronização do banco de dados local
|
// Função para obter o sys_id de um ticket pela tabela de sincronização do banco de dados local
|
||||||
|
|
||||||
const getTicketSysId = async (ticketId) => {
|
const getTicketSysId = async (ticketId) => {
|
||||||
try {
|
try {
|
||||||
const ticket = await TicketSnModel.findById(ticketId);
|
const ticket = await TicketSnModel.findById(ticketId);
|
||||||
@ -114,6 +109,7 @@ const getTicketSysId = async (ticketId) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Função para obter o tipo de um ticket pela tabela de sincronização do banco de dados local
|
||||||
const getTicketTypeById = async (ticketId) => {
|
const getTicketTypeById = async (ticketId) => {
|
||||||
try {
|
try {
|
||||||
const ticket = await TicketSnModel.findById(ticketId);
|
const ticket = await TicketSnModel.findById(ticketId);
|
||||||
@ -129,21 +125,6 @@ const getTicketTypeById = async (ticketId) => {
|
|||||||
// Função para obter o código de um comentário dentro de um ticket do ServiceNow
|
// Função para obter o código de um comentário dentro de um ticket do ServiceNow
|
||||||
const getCommentId = async (sysId, comment) => {
|
const getCommentId = async (sysId, comment) => {
|
||||||
try {
|
try {
|
||||||
// Exemplo de requisição GET para buscar comentários:
|
|
||||||
|
|
||||||
// GET https://caoadev.service-now.com/api/now/table/sys_journal_field?sysparm_query=element_id=<SYS_ID_DO_INCIDENTE>^element=comments^ORDERBYDESCsys_created_on&sysparm_fields=sys_id,element,value,sys_created_on,sys_created_by,element_id&sysparm_limit=1
|
|
||||||
|
|
||||||
// exemplo de resposta da API:
|
|
||||||
/*
|
|
||||||
"result": [
|
|
||||||
{
|
|
||||||
"sys_id": "SYS_ID_DO_COMENTARIO",
|
|
||||||
"sys_created_on": "2025-10-02 12:24:19",
|
|
||||||
"element_id": "SYS_ID_DO_INCIDENTE",
|
|
||||||
"value": "Teste fora do código",
|
|
||||||
"sys_created_by": "SOTHIS.CAOA",
|
|
||||||
"element": "comments"
|
|
||||||
}*/
|
|
||||||
|
|
||||||
const url = `${apiConfig.snTableJournalConfig.baseUrl}?sysparm_query=element_id=${sysId}^element=comments^ORDERBYDESCsys_created_on&sysparm_fields=sys_id,element,value,sys_created_on,sys_created_by,element_id`;
|
const url = `${apiConfig.snTableJournalConfig.baseUrl}?sysparm_query=element_id=${sysId}^element=comments^ORDERBYDESCsys_created_on&sysparm_fields=sys_id,element,value,sys_created_on,sys_created_by,element_id`;
|
||||||
|
|
||||||
@ -166,15 +147,9 @@ const getCommentId = async (sysId, comment) => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Função para criar um comentário em um ticket no ServiceNow
|
// Função para criar um comentário em um ticket no ServiceNow
|
||||||
const createCommentInServiceNow = async (sysId, comment, ticketType) => {
|
const createCommentInServiceNow = async (sysId, comment, ticketType) => {
|
||||||
try {
|
try {
|
||||||
// Exemplo de requisição PATCH para atualizar um ticket:
|
|
||||||
// curl -u user:pass \
|
|
||||||
// -H "Content-Type: application/json" \
|
|
||||||
// -X PATCH "https://SEU_INSTANCE.service-now.com/api/now/table/incident/{SYS_ID_DO_INCIDENTE}" \
|
|
||||||
// -d '{"comments":"Estamos analisando seu chamado. Retorno em breve."}'
|
|
||||||
let url = '';
|
let url = '';
|
||||||
let payload = {};
|
let payload = {};
|
||||||
if (ticketType == 'incidente') {
|
if (ticketType == 'incidente') {
|
||||||
@ -219,6 +194,7 @@ const createCommentInServiceNow = async (sysId, comment, ticketType) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Função para criar um comentário
|
||||||
const syncCommentToServiceNow = async (ticketId, comment) => {
|
const syncCommentToServiceNow = async (ticketId, comment) => {
|
||||||
logInfo(`Iniciando sincronização do comentário para o ticket SN Ticket: ${ticketId}`);
|
logInfo(`Iniciando sincronização do comentário para o ticket SN Ticket: ${ticketId}`);
|
||||||
const snSysId = await getTicketSysId(ticketId);
|
const snSysId = await getTicketSysId(ticketId);
|
||||||
@ -233,7 +209,7 @@ const syncCommentToServiceNow = async (ticketId, comment) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Função que verifica se o comentário já existe no ServiceNow antes de criar
|
// Função que verifica se o comentário já existe no ServiceNow antes de criar
|
||||||
const existingCommentInServiceNow = async (ticketId, comment) => {
|
const existingCommentInServiceNow = async (ticketId, comment) => {
|
||||||
try {
|
try {
|
||||||
const sys_id = await getTicketSysId(ticketId);
|
const sys_id = await getTicketSysId(ticketId);
|
||||||
const commentExists = await getCommentId(sys_id, comment);
|
const commentExists = await getCommentId(sys_id, comment);
|
||||||
@ -248,26 +224,15 @@ const syncCommentToServiceNow = async (ticketId, comment) => {
|
|||||||
// Função para buscar todos os comentários de tickets no ServiceNow a partir do ticket id no banco local
|
// Função para buscar todos os comentários de tickets no ServiceNow a partir do ticket id no banco local
|
||||||
// Deve-se atentar para a paginação da API do ServiceNow
|
// 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
|
// para fins de teste, gerar um arquivo JSON com os comentários de um ticket específico
|
||||||
|
const fetchCommentsFromServiceNow = async (ticket) => {
|
||||||
const fetchCommentsFromServiceNow = async () => {
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
logInfo(`Iniciando busca de comentários do ServiceNow para o ticket SN Ticket: ${ticket.glpi_ticket_id}`);
|
||||||
|
|
||||||
const ticketsSyncList = await TicketSyncModel.getTicketsToSync();
|
await TicketSyncModel.updateStatus(ticket.sn_ticket_id, 'synced', 'pending_cmt_check');
|
||||||
|
|
||||||
|
const sys_id = ticket.sys_id;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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 [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const allComments = [];
|
const allComments = [];
|
||||||
const pageSize = 100;
|
const pageSize = 100;
|
||||||
@ -283,8 +248,7 @@ const fetchCommentsFromServiceNow = async () => {
|
|||||||
sysparm_limit: pageSize,
|
sysparm_limit: pageSize,
|
||||||
sysparm_offset: offset
|
sysparm_offset: offset
|
||||||
};
|
};
|
||||||
|
logInfo(`Buscando página ${page} de comentários do ticket SN Ticket: ${ticket.glpi_ticket_id}`);
|
||||||
logInfo(`Buscando página ${page} de comentários do ticket SN Ticket: ${ticketId}`, { ticketId, page });
|
|
||||||
const response = await axios.get(apiConfig.snTableJournalConfig.baseUrl, {
|
const response = await axios.get(apiConfig.snTableJournalConfig.baseUrl, {
|
||||||
auth: apiConfig.servicenowAuthentication.auth,
|
auth: apiConfig.servicenowAuthentication.auth,
|
||||||
params
|
params
|
||||||
@ -295,7 +259,7 @@ const fetchCommentsFromServiceNow = async () => {
|
|||||||
|
|
||||||
if (!Array.isArray(comments) || comments.length === 0) {
|
if (!Array.isArray(comments) || comments.length === 0) {
|
||||||
break; // não há mais comentários
|
break; // não há mais comentários
|
||||||
}
|
}
|
||||||
|
|
||||||
allComments.push(...comments);
|
allComments.push(...comments);
|
||||||
|
|
||||||
@ -312,39 +276,35 @@ const fetchCommentsFromServiceNow = async () => {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logInfo(`Total de comentários encontrados para o ticket SN Ticket ${ticketId}: ${allComments.length} comentários`, { ticketId, count: allComments.length });
|
|
||||||
|
// Traz apenas os tickets que não são do sys_created_by: "SOTHIS.CAOA"
|
||||||
// salvar em arquivo para análise
|
|
||||||
const filename = `servicenow_comments_ticket_${ticketId}.json`;
|
|
||||||
|
|
||||||
// Salva no arquivo
|
|
||||||
fs.writeFileSync(filename, JSON.stringify(allComments, null, 2));
|
|
||||||
|
|
||||||
//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');
|
const filteredComments = allComments.filter(comment => comment.sys_created_by !== 'SOTHIS.CAOA');
|
||||||
|
if (filteredComments.length === 0) {
|
||||||
|
TicketSyncModel.updateStatus(ticket.sn_ticket_id, 'synced ', 'synced');
|
||||||
|
logInfo(`Nenhum comentário encontrado no Service Now para o ticket GLPI ID: ${ticket.glpi_ticket_id}`);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
logInfo(`Encontrado ${filteredComments.length} comentários para o ticket SN Ticket: ${ticket.glpi_ticket_id}`)
|
||||||
|
|
||||||
|
|
||||||
|
const commentsInserted = [];
|
||||||
// Insere cada comment no banco intermediario
|
// Insere cada comment no banco intermediario
|
||||||
for (const comment of filteredComments) {
|
for (const comment of filteredComments) {
|
||||||
|
|
||||||
const existingComment = await TicketUpdateModel.getCommentSync(ticketId, comment.sys_id);
|
await TicketSyncModel.updateStatus(ticket.sn_ticket_id, 'synced', 'pendind_cmt_sync');
|
||||||
|
|
||||||
|
|
||||||
|
const existingComment = await TicketUpdateModel.getCommentSync(comment.sys_id);
|
||||||
|
|
||||||
if (existingComment) {
|
if (existingComment) {
|
||||||
|
TicketSyncModel.updateStatus(ticket.sn_ticket_id, 'synced', 'synced');
|
||||||
logInfo(`Comentário: ${comment.sys_id} já existe no banco de dados`, { step: 3 });
|
logInfo(`Comentário: ${comment.sys_id} já existe no banco de dados`, { step: 3 });
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
console.log(comment);
|
|
||||||
|
|
||||||
const ticketSnId = await TicketSyncModel.getIdSyncBySnId(ticketId);
|
|
||||||
const ticketSyncId = await TicketSyncModel.getIdSyncBySnId(ticketId);
|
|
||||||
|
|
||||||
|
|
||||||
const commentData = {
|
const commentData = {
|
||||||
ticket_sync_id: parseInt(ticketSnId),
|
ticket_sync_id: ticket.id,
|
||||||
ticket_sync_id: ticketSyncId,
|
|
||||||
source_system: 'servicenow',
|
source_system: 'servicenow',
|
||||||
content: comment.value,
|
content: comment.value,
|
||||||
created_at: new Date(comment.sys_created_on),
|
created_at: new Date(comment.sys_created_on),
|
||||||
@ -354,38 +314,22 @@ const fetchCommentsFromServiceNow = async () => {
|
|||||||
destiny_id: null
|
destiny_id: null
|
||||||
};
|
};
|
||||||
|
|
||||||
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);
|
const commentInserted = await TicketUpdateModel.insert(commentData);
|
||||||
|
|
||||||
if (commentInserted.success) {
|
if (commentInserted.success) {
|
||||||
// Insere Glpi status como
|
await TicketSyncModel.updateStatus(ticket.sn_ticket_id, 'pendind_cmt_sync', 'synced');
|
||||||
// ding e SN status como synced
|
commentsInserted.push(commentInserted);
|
||||||
await TicketSyncModel.updateStatus(ticketGlpiId.id, '
|
logInfo(`✅ Comentário ID ${comment.sys_id} do ${comment.source_system} inserido no banco!`, { step: 4 });
|
||||||
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 {
|
} else {
|
||||||
logError(`❌ Erro ao inserir comentário ID ${comment.sys_id} no banco: ${commentInserted.error}`, { step: 4 });
|
logError(`❌ Erro ao inserir comentário ID ${comment.sys_id} no banco: ${commentInserted.error}`, { step: 4 });
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ticketSyncId) {
|
|
||||||
logError(`❌ TicketSyncId não encontrado para o ticket GLPI ID: ${ticketId}`, { step: 4 });
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logInfo(`💾 Backup JSON de comentários salvo: ${filename}`, { count: allComments.length });
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logError(error, '🚨Falha ao buscar comentários do ServiceNow');
|
logError(error, '🚨Falha ao buscar comentários do ServiceNow');
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user