FEAT: Comentarios GLPI atualizados no banco de dados
- Criado modelos individuais por tabela do banco de dados - Sincronização com tabela ticket_sync após cada comentário - Atualização da tabela ticket_updates para conter apenas infos necessárias - glpiCommentService criado para gerir toda operação.
This commit is contained in:
parent
bbc618d496
commit
2960292066
@ -268,7 +268,7 @@ class TicketGlpiModel {
|
|||||||
// 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 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]);
|
const [rows] = await glpiPool.execute(query, [itemId]);
|
||||||
return rows;
|
return rows;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -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]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,120 +1,55 @@
|
|||||||
// src/models/ticketSyncModel.js
|
// src/models/ticketSyncModel.js
|
||||||
const pool = require('../data/database');
|
const pool = require('../data/database');
|
||||||
const { logInfo, logError } = require('../utils/logger');
|
const { logError } = require('../utils/logger');
|
||||||
|
|
||||||
class TicketSyncModel {
|
class TicketSyncModel {
|
||||||
|
|
||||||
// Cria um registro de sincronização
|
// Buscar tickets que foram criados com sucesso para sincronizar comentários
|
||||||
static async createSyncRecord(snTicketId, glpiTicketId = null) {
|
static async getTicketsToSync() {
|
||||||
try {
|
try {
|
||||||
const query = `
|
const query = 'SELECT id, glpi_ticket_id, sn_ticket_id FROM ticket_sync';
|
||||||
INSERT INTO ticket_sync (sn_ticket_id, glpi_ticket_id)
|
const { rows } = await pool.query(query);
|
||||||
VALUES ($1, $2)
|
return rows;
|
||||||
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];
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logError(error, `❌ Erro ao criar registro de sincronização para ticket ${snTicketId}`);
|
logError(error, '❌ Erro ao buscar tickets para sincronizar');
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Atualiza o status de sincronização com o GLPI
|
// Atualiza o status de sincronização de um ticket
|
||||||
static async updateGlpiSyncStatus(syncId, status, glpiTicketId = null) {
|
static async updateStatus(ticketSyncId, glpiStatus, snStatus) {
|
||||||
try {
|
try {
|
||||||
const query = `
|
const query = 'UPDATE ticket_sync SET glpi_sync_status = $1, sn_sync_status = $2 WHERE id = $3';
|
||||||
UPDATE ticket_sync
|
await pool.query(query, [glpiStatus, snStatus, ticketSyncId]);
|
||||||
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) {
|
} catch (error) {
|
||||||
logError(error, `❌ Erro ao atualizar status GLPI para sync ${syncId}`);
|
logError(error, `❌ Erro ao atualizar status de sincronização para o ID: ${ticketSyncId}`);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Atualiza o status de sincronização com o ServiceNow
|
// Buscar o ID de sincronização baseado no glpi_ticket_id
|
||||||
static async updateSnSyncStatus(syncId, status) {
|
static async getIdSyncByGlpiId(glpiTicketId) {
|
||||||
try {
|
try {
|
||||||
const query = `
|
const query = 'SELECT id FROM ticket_sync WHERE glpi_ticket_id = $1';
|
||||||
UPDATE ticket_sync
|
const { rows } = await pool.query(query, [glpiTicketId]);
|
||||||
SET sn_sync_status = $1,
|
return rows[0] ? rows[0].id : null;
|
||||||
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) {
|
} catch (error) {
|
||||||
logError(error, `❌ Erro ao atualizar status SN para sync ${syncId}`);
|
logError(error, `❌ Erro ao buscar ID de sincronização por GLPI ID: ${glpiTicketId}`);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
|
||||||
// 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.exports = TicketSyncModel;
|
module.exports = TicketSyncModel;
|
||||||
59
src/models/ticketUpdateModel.js
Normal file
59
src/models/ticketUpdateModel.js
Normal file
@ -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;
|
||||||
@ -86,10 +86,7 @@ CREATE TABLE ticket_updates (
|
|||||||
source_system VARCHAR(10) NOT NULL, -- Sistema de origem: 'sn' (ServiceNow) ou 'glpi'
|
source_system VARCHAR(10) NOT NULL, -- Sistema de origem: 'sn' (ServiceNow) ou 'glpi'
|
||||||
content TEXT, -- Conteúdo da atualização/tarefa
|
content TEXT, -- Conteúdo da atualização/tarefa
|
||||||
author VARCHAR(255), -- Autor da atualização
|
author VARCHAR(255), -- Autor da atualização
|
||||||
created_at_source TIMESTAMP, -- Data e hora de criação no sistema de origem
|
created_at 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
|
|
||||||
updated_at TIMESTAMP DEFAULT NOW(), -- Data e hora da última atualização do registro
|
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)
|
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
|
-- Índices para melhor performance nas consultas frequentes
|
||||||
CREATE INDEX idx_ticket_updates_sync ON ticket_updates(ticket_sync_id);
|
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_source ON ticket_updates(source_system);
|
||||||
CREATE INDEX idx_ticket_updates_type ON ticket_updates(update_type);
|
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);
|
CREATE INDEX idx_ticket_updates_source_id ON ticket_updates(source_id);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,30 +1,74 @@
|
|||||||
// src/services/glpiCommentService.js
|
// src/services/glpiCommentService.js
|
||||||
// src/services/glpiService.js
|
|
||||||
const TicketSnModel = require('../models/ticketSnModel');
|
const TicketSnModel = require('../models/ticketSnModel');
|
||||||
const TicketGlpiModel = require('../models/ticketGlpiModel');
|
const TicketGlpiModel = require('../models/ticketGlpiModel');
|
||||||
const TicketSyncModel = require('../models/ticketSyncModel');
|
const TicketSyncModel = require('../models/ticketSyncModel');
|
||||||
|
const TicketUpdateModel = require('../models/ticketUpdateModel');
|
||||||
const { logInfo, logError } = require('../utils/logger');
|
const { logInfo, logError } = require('../utils/logger');
|
||||||
|
|
||||||
|
|
||||||
|
// Sincroniza comentários do GLPI para o Banco de Dados local
|
||||||
const syncCommentsGlpi = async () => {
|
const syncCommentsGlpi = async () => {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
logInfo('🔄 Iniciando sincronização de comments do GLPI para o Service Now iniciada...');
|
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 TicketSyncModel.getTicketsToSync();
|
||||||
const effectiveTickets = await TicketSnModel.getTicketsToSync();
|
|
||||||
logInfo(`🔍 ${effectiveTickets.length} tickets encontrados para sincronização de comentários`, { step: 1 });
|
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) {
|
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);
|
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) {
|
}catch (error) {
|
||||||
logError(error, 'GLPI Tickets');
|
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 = {
|
module.exports = {
|
||||||
syncCommentsGlpi
|
syncCommentsGlpi
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user