diff --git a/src/models/ticketGlpiModel.js b/src/models/ticketGlpiModel.js index 6e204e4..634d7a6 100644 --- a/src/models/ticketGlpiModel.js +++ b/src/models/ticketGlpiModel.js @@ -268,7 +268,7 @@ class TicketGlpiModel { // Buscar followups (notas) de um ticket no GLPI static async getFollowupsByItemId(itemId) { try { - const query = 'SELECT 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 = ?'; const [rows] = await glpiPool.execute(query, [itemId]); return rows; } catch (error) { diff --git a/src/models/ticketSnModel.js b/src/models/ticketSnModel.js index ef94628..1a890ef 100644 --- a/src/models/ticketSnModel.js +++ b/src/models/ticketSnModel.js @@ -183,47 +183,6 @@ class TicketSnModel { } } - // Buscar todos os tickets que estão na tabela de sincronização - static async getTicketsToSync() { - const query = 'SELECT id, glpi_ticket_id FROM ticket_sync '; - const { rows } = await pool.query(query); - return rows; - } - - // Inserir um registro na tabela ticket_updates - static async insertTicketUpdate(updateData) { - const query = ` - INSERT INTO ticket_updates (ticket_sync_id, update_type, source_system, content, author, created_at_source, sync_status, sync_date, created_at, updated_at, source_id) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) - RETURNING id; - `; - const values = [ - updateData.ticket_sync_id, - 'note', - 'glpi', - updateData.content, - 'Sothis', - updateData.created_at_source, - updateData.sync_status, - new Date(), - updateData.created_at, - updateData.updated_at, - updateData.source_id - ]; - try { - const { rows } = await pool.query(query, values); - return { success: true, newId: rows[0].id }; - } catch (error) { - logError('Erro ao inserir ticket_update:', error); - return { success: false, error: error.message }; - } - } - static async updateTicketSyncStatus(ticketSyncId, glpiStatus, snStatus) { - const query = 'UPDATE ticket_sync SET glpi_sync_status = $1, sn_sync_status = $2 WHERE id = $3'; - await pool.query(query, [glpiStatus, snStatus, ticketSyncId]); - } - - } diff --git a/src/models/ticketSyncModel.js b/src/models/ticketSyncModel.js index 6042ae8..45ebb79 100644 --- a/src/models/ticketSyncModel.js +++ b/src/models/ticketSyncModel.js @@ -1,120 +1,55 @@ // src/models/ticketSyncModel.js const pool = require('../data/database'); -const { logInfo, logError } = require('../utils/logger'); +const { logError } = require('../utils/logger'); class TicketSyncModel { + + // Buscar tickets que foram criados com sucesso para sincronizar comentários + static async getTicketsToSync() { + try { + const query = 'SELECT id, glpi_ticket_id, sn_ticket_id FROM ticket_sync'; + const { rows } = await pool.query(query); + return rows; + } catch (error) { + logError(error, '❌ Erro ao buscar tickets para sincronizar'); + throw error; + } + } + + // Atualiza o status de sincronização de um ticket + static async updateStatus(ticketSyncId, glpiStatus, snStatus) { + try { + const query = 'UPDATE ticket_sync SET glpi_sync_status = $1, sn_sync_status = $2 WHERE id = $3'; + await pool.query(query, [glpiStatus, snStatus, ticketSyncId]); + } catch (error) { + logError(error, `❌ Erro ao atualizar status de sincronização para o ID: ${ticketSyncId}`); + throw error; + } + } - // Cria um registro de sincronização - static async createSyncRecord(snTicketId, glpiTicketId = null) { + // Buscar o ID de sincronização baseado no glpi_ticket_id + static async getIdSyncByGlpiId(glpiTicketId) { try { - const query = ` - INSERT INTO ticket_sync (sn_ticket_id, glpi_ticket_id) - VALUES ($1, $2) - RETURNING * - `; - const result = await pool.query(query, [snTicketId, glpiTicketId]); - logInfo(`✅ Registro de sincronização criado para ticket SN: ${snTicketId}, GLPI: ${glpiTicketId || 'null'}`); - return result.rows[0]; + const query = 'SELECT id FROM ticket_sync WHERE glpi_ticket_id = $1'; + const { rows } = await pool.query(query, [glpiTicketId]); + return rows[0] ? rows[0].id : null; } catch (error) { - logError(error, `❌ Erro ao criar registro de sincronização para ticket ${snTicketId}`); + logError(error, `❌ Erro ao buscar ID de sincronização por GLPI ID: ${glpiTicketId}`); throw error; } } - - // Atualiza o status de sincronização com o GLPI - static async updateGlpiSyncStatus(syncId, status, glpiTicketId = null) { - try { - const query = ` - UPDATE ticket_sync - SET glpi_sync_status = $1, - glpi_ticket_id = COALESCE($2, glpi_ticket_id), - last_glpi_sync = NOW() - WHERE id = $3 - RETURNING * - `; - const result = await pool.query(query, [status, glpiTicketId, syncId]); - logInfo(`✅ Status GLPI atualizado para: ${status}, sync ID: ${syncId}`); - return result.rows[0]; - } catch (error) { - logError(error, `❌ Erro ao atualizar status GLPI para sync ${syncId}`); - throw error; - } - } - - // Atualiza o status de sincronização com o ServiceNow - static async updateSnSyncStatus(syncId, status) { - try { - const query = ` - UPDATE ticket_sync - SET sn_sync_status = $1, - last_sn_sync = NOW() - WHERE id = $2 - RETURNING * - `; - const result = await pool.query(query, [status, syncId]); - logInfo(`✅ Status SN atualizado para: ${status}, sync ID: ${syncId}`); - return result.rows[0]; - } catch (error) { - logError(error, `❌ Erro ao atualizar status SN para sync ${syncId}`); - throw error; - } - } - - // Busca registro de sincronização por ticket SN - static async findBySnTicketId(snTicketId) { - try { - const query = `SELECT * FROM ticket_sync WHERE sn_ticket_id = $1`; - const result = await pool.query(query, [snTicketId]); - return result.rows[0]; - } catch (error) { - logError(error, `❌ Erro ao buscar sync por SN ticket ID: ${snTicketId}`); - return null; - } - } - - // Busca registro de sincronização por ticket GLPI - static async findByGlpiTicketId(glpiTicketId) { - try { - const query = `SELECT * FROM ticket_sync WHERE glpi_ticket_id = $1`; - const result = await pool.query(query, [glpiTicketId]); - return result.rows[0]; - } catch (error) { - logError(error, `❌ Erro ao buscar sync por GLPI ticket ID: ${glpiTicketId}`); - return null; - } - } - - // Busca todos os registros de sincronização com status pendente para GLPI - static async findPendingGlpiSync() { - try { - const query = ` - SELECT * FROM ticket_sync - WHERE glpi_sync_status = 'pending' - ORDER BY created_at - `; - const result = await pool.query(query); - return result.rows; - } catch (error) { - logError(error, '❌ Erro ao buscar sincronizações pendentes para GLPI'); - return []; - } - } - - // Busca todos os registros de sincronização com status pendente para ServiceNow - static async findPendingSnSync() { - try { - const query = ` - SELECT * FROM ticket_sync - WHERE sn_sync_status = 'pending' - ORDER BY created_at - `; - const result = await pool.query(query); - return result.rows; - } catch (error) { - logError(error, '❌ Erro ao buscar sincronizações pendentes para ServiceNow'); - return []; - } - } } +/** + * @module TicketSyncModel + * @description Este módulo fornece métodos para interagir com a tabela de sincronização de tickets (ticket_sync) no banco de dados. + * Inclui funcionalidades para buscar tickets para sincronização, atualizar o status de sincronização + * e buscar o ID de sincronização baseado no glpi_ticket_id. + * + * A função getTicketsToSync busca todos os tickets que possuem IDs tanto no GLPI quanto no ServiceNow. + * A função updateStatus atualiza os status de sincronização do GLPI e do ServiceNow para um ticket específico. + * A função getIdSyncByGlpiId retorna o ID de sincronização baseado no glpi_ticket_id. + * Essas funcionalidades são essenciais para manter a consistência e o rastreamento dos tickets entre os dois sistemas. + */ + module.exports = TicketSyncModel; \ No newline at end of file diff --git a/src/models/ticketUpdateModel.js b/src/models/ticketUpdateModel.js new file mode 100644 index 0000000..6156584 --- /dev/null +++ b/src/models/ticketUpdateModel.js @@ -0,0 +1,59 @@ +// src/models/ticketUpdateModel.js +const pool = require('../data/database'); +const { logError } = require('../utils/logger'); + +class TicketUpdateModel { + + static async insert(updateData) { + const query = ` + INSERT INTO ticket_updates (ticket_sync_id, update_type, source_system, content, author, created_at, updated_at, source_id) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + RETURNING id; + `; + const values = [ + updateData.ticket_sync_id, + 'note', + 'glpi', + updateData.content, + 'Sothis', + updateData.created_at, + updateData.updated_at, + updateData.source_id + ]; + try { + const { rows } = await pool.query(query, values); + return { success: true, newId: rows[0].id }; + } catch (error) { + logError(error, '❌ Erro ao inserir ticket_update'); + return { success: false, error: error.message }; + } + } + + static async getByTicketSyncIdAndSourceId(ticketSyncId, sourceId) { + try { + const query = ` + SELECT * FROM ticket_updates + WHERE ticket_sync_id = $1 AND source_id = $2 + `; + const { rows } = await pool.query(query, [ticketSyncId, sourceId]); + return rows[0] || null; + } catch (error) { + logError(error, `❌ Erro ao buscar update por ticket_sync_id e source_id`); + throw error; + } + } + static async getCommentSync(glpiTicketId, sourceId) { + const ticketSyncModel = require('./ticketSyncModel'); + const ticketSyncId = await ticketSyncModel.getIdSyncByGlpiId(glpiTicketId); + + + + if (!ticketSyncId) { + return null; + } + + return await this.getByTicketSyncIdAndSourceId(ticketSyncId, sourceId); + } +} + +module.exports = TicketUpdateModel; \ No newline at end of file diff --git a/src/scripts/database/scriptBD.sql b/src/scripts/database/scriptBD.sql index 18e7758..6c3a4ed 100644 --- a/src/scripts/database/scriptBD.sql +++ b/src/scripts/database/scriptBD.sql @@ -86,10 +86,7 @@ CREATE TABLE ticket_updates ( source_system VARCHAR(10) NOT NULL, -- Sistema de origem: 'sn' (ServiceNow) ou 'glpi' content TEXT, -- Conteúdo da atualização/tarefa author VARCHAR(255), -- Autor da atualização - created_at_source TIMESTAMP, -- Data e hora de criação no sistema de origem - sync_status VARCHAR(20) DEFAULT 'pending', -- Status da sincronização: 'pending', 'completed', 'error' - sync_date TIMESTAMP, -- Data e hora da sincronização - created_at TIMESTAMP DEFAULT NOW(), -- Data e hora de criação do registro + created_at TIMESTAMP, -- Data e hora de criação no sistema de origem updated_at TIMESTAMP DEFAULT NOW(), -- Data e hora da última atualização do registro source_id VARCHAR(64), -- ID da atualização no sistema de origem (ex: sys_id no ServiceNow) @@ -102,10 +99,8 @@ CREATE TABLE ticket_updates ( -- Índices para melhor performance nas consultas frequentes CREATE INDEX idx_ticket_updates_sync ON ticket_updates(ticket_sync_id); -CREATE INDEX idx_ticket_updates_status ON ticket_updates(sync_status); CREATE INDEX idx_ticket_updates_source ON ticket_updates(source_system); CREATE INDEX idx_ticket_updates_type ON ticket_updates(update_type); -CREATE INDEX idx_ticket_updates_created ON ticket_updates(created_at_source); CREATE INDEX idx_ticket_updates_source_id ON ticket_updates(source_id); diff --git a/src/services/glpiCommentService.js b/src/services/glpiCommentService.js index 1ac857f..545aedc 100644 --- a/src/services/glpiCommentService.js +++ b/src/services/glpiCommentService.js @@ -1,30 +1,74 @@ // src/services/glpiCommentService.js -// src/services/glpiService.js const TicketSnModel = require('../models/ticketSnModel'); const TicketGlpiModel = require('../models/ticketGlpiModel'); const TicketSyncModel = require('../models/ticketSyncModel'); +const TicketUpdateModel = require('../models/ticketUpdateModel'); const { logInfo, logError } = require('../utils/logger'); - +// Sincroniza comentários do GLPI para o Banco de Dados local const syncCommentsGlpi = async () => { try { + logInfo('🔄 Iniciando sincronização de comments do GLPI para o Service Now iniciada...'); - // 1. Buscar tickets que foram criados com sucesso para sincronizar comentários - const effectiveTickets = await TicketSnModel.getTicketsToSync(); + const effectiveTickets = await TicketSyncModel.getTicketsToSync(); logInfo(`🔍 ${effectiveTickets.length} tickets encontrados para sincronização de comentários`, { step: 1 }); - // 2. Para cada ticket, buscar comentários no GLPI e salvar no ServiceNow + for (const ticket of effectiveTickets) { - console.log(`Buscando comentários para o ticket GLPI ID: ${ticket.glpi_ticket_id}`); + logInfo(`Buscando comentários para o ticket GLPI ID: ${ticket.glpi_ticket_id}`); const comments = await TicketGlpiModel.getFollowupsByItemId(ticket.glpi_ticket_id); - console.log(comments); + logInfo(`📝 ${comments.length} comentários encontrados para o ticket GLPI ID: ${ticket.glpi_ticket_id}`, { step: 2 }); + for (const comment of comments) { + + const existingComment = await TicketUpdateModel.getCommentSync(ticket.glpi_ticket_id, comment.id); + + if (existingComment) { + logInfo(`Comentário ID ${comment.id} já sincronizado, pulando...`, { step: 3 }); + continue; + } + + const sync_id = await TicketSyncModel.getIdSyncByGlpiId(ticket.glpi_ticket_id); + + const commentData = { + ticket_sync_id: sync_id, + content: comment.content, + created_at: new Date(comment.date_creation), + updated_at: new Date(comment.date_mod), + source_id: comment.id, + }; + + + const insertResult = await TicketUpdateModel.insert(commentData); + if (insertResult.success) { + logInfo(`✅ Comentário ID ${comment.id} sincronizado com sucesso! 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}'.`); + } } }catch (error) { logError(error, 'GLPI Tickets'); } } + +/** + * @module glpiCommentService + * @description Este módulo fornece serviços para sincronizar comentários do GLPI para o banco de dados local. + * A função syncCommentsGlpi busca tickets que foram criados com sucesso e sincroniza seus comentários, + * garantindo que apenas novos comentários sejam inseridos no banco de dados. + * Utiliza os modelos TicketSnModel, TicketGlpiModel, TicketSyncModel e TicketUpdateModel para interagir com o banco de dados. + * Atualiza o status de sincronização após cada comentário processado. +* Inclui logging detalhado para monitorar o progresso e identificar erros durante o processo de sincronização. + */ module.exports = { syncCommentsGlpi -}; \ No newline at end of file +}; +