WIP: Lógica de sincronização bidirecional criada.
- Tabelas de updates criada e adicionado ao script - Tabela de marca d'água criada - Fluxo de coleta de mensagens do HubSoft criado - Fluxo de coleta de comentários do GLPI criado.
This commit is contained in:
parent
4d931bdddf
commit
fe4462c323
@ -39,9 +39,9 @@ HUBGLPI_DB_PASSWORD=Ut@2S@$M9Xs@@W
|
||||
# BANCO DE DADOS FINAL - GLPI (MySQL - Desenvolvimento)
|
||||
# ==============================================================================
|
||||
GLPI_DB_TYPE=mysql
|
||||
GLPI_DB_HOST=177.73.177.32
|
||||
GLPI_DB_HOST=177.73.177.44
|
||||
GLPI_DB_PORT=3306
|
||||
GLPI_DB_USER=snglpi
|
||||
GLPI_DB_PASSWORD=j2633669
|
||||
GLPI_DB_USER=desenvolvimento
|
||||
GLPI_DB_PASSWORD=Ut@2S@$M9Xs@@W
|
||||
GLPI_DB_NAME=glpi_data
|
||||
GLPI_DB_CHARSET=utf8mb4
|
||||
|
||||
31
src/controller/commentController.js
Normal file
31
src/controller/commentController.js
Normal file
@ -0,0 +1,31 @@
|
||||
// src/controller/commentController.js
|
||||
const commentService = require('../services/commentService.js');
|
||||
const { logInfo, logError, logWarning } = require('../utils/logger.js');
|
||||
|
||||
/**
|
||||
* Controller para lidar com o webhook de novo comentário do GLPI.
|
||||
* @param {import('express').Request} req - O objeto de requisição do Express.
|
||||
* @param {import('express').Response} res - O objeto de resposta do Express.
|
||||
*/
|
||||
async function handleNewComment(req, res) {
|
||||
const body = req.body;
|
||||
const glpiTicketId = body?.item?.items_id;
|
||||
const commentContent = body?.item?.content;
|
||||
|
||||
if (!glpiTicketId || !commentContent) {
|
||||
logWarning('Webhook de novo comentário recebido com dados incompletos.', body);
|
||||
return res.status(400).json({ error: 'Dados do comentário ou ID do ticket ausentes.' });
|
||||
}
|
||||
|
||||
logInfo(`Webhook de novo comentário recebido para o ticket GLPI ID ${glpiTicketId}.`);
|
||||
|
||||
try {
|
||||
await commentService.syncGlpiCommentToHubsoft(glpiTicketId, commentContent);
|
||||
res.status(200).json({ status: 'success', message: 'Comentário recebido e processado.' });
|
||||
} catch (error) {
|
||||
logError(`Erro ao processar webhook de comentário para o ticket GLPI ID ${glpiTicketId}:`, error);
|
||||
res.status(500).json({ status: 'error', message: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { handleNewComment };
|
||||
19
src/cron.js
19
src/cron.js
@ -3,23 +3,34 @@ loadEnv();
|
||||
|
||||
const cron = require('node-cron');
|
||||
const { processaAtendimentos } = require('./controller/processController.js');
|
||||
const commentService = require('./services/commentService.js'); // 1. Importar o novo serviço
|
||||
const { logInfo, logError } = require('./utils/logger.js');
|
||||
|
||||
let isCronRunning = false;
|
||||
|
||||
logInfo('⏰ Agendando cron job para processar atendimentos a cada 5 minutos.');
|
||||
logInfo('⏰ Agendando cron job para processar atendimentos a cada 1 minuto.');
|
||||
|
||||
cron.schedule('*/5 * * * *', async () => {
|
||||
cron.schedule('* * * * *', async () => {
|
||||
if (isCronRunning) {
|
||||
logInfo('CRON: Tentativa de início, mas o processo anterior ainda está em execução. Pulando esta rodada.');
|
||||
return;
|
||||
}
|
||||
|
||||
isCronRunning = true;
|
||||
logInfo('CRON: Iniciando processamento de atendimentos...');
|
||||
logInfo('CRON: Iniciando ciclo de sincronização...');
|
||||
try {
|
||||
// --- Tarefa 1: Sincronizar criação de tickets ---
|
||||
logInfo('CRON (Etapa 1/2): Processando criação de tickets...');
|
||||
await processaAtendimentos();
|
||||
logInfo('CRON: Processamento concluído com sucesso.');
|
||||
logInfo('CRON (Etapa 1/2): Criação de tickets concluída.');
|
||||
|
||||
// --- Tarefa 2: Sincronizar comentários ---
|
||||
logInfo('CRON (Etapa 2/2): Processando sincronização de comentários...');
|
||||
await commentService.syncHubsoftCommentsToLocalDB();
|
||||
await commentService.sendPendingCommentsToGlpi(); // E outras direções se necessário
|
||||
logInfo('CRON (Etapa 2/2): Sincronização de comentários concluída.');
|
||||
|
||||
logInfo('CRON: Ciclo de sincronização concluído com sucesso.');
|
||||
} catch (error) {
|
||||
logError('CRON: Erro durante o processamento de atendimentos.', error);
|
||||
} finally {
|
||||
|
||||
130
src/model/commentModel.js
Normal file
130
src/model/commentModel.js
Normal file
@ -0,0 +1,130 @@
|
||||
const { logError, logInfo } = require('../utils/logger');
|
||||
const pool = require('../data/hubglpiDataBase');
|
||||
|
||||
class CommentModel {
|
||||
/**
|
||||
* Busca o timestamp da última execução de um job de sincronização.
|
||||
* @param {string} jobName - O nome do job (ex: 'hubsoft_comments_sync').
|
||||
* @returns {Promise<Date>} O timestamp da última execução.
|
||||
*/
|
||||
static async getLastRunTimestamp(jobName) {
|
||||
const query = 'SELECT last_run_timestamp FROM sync_control WHERE job_name = $1';
|
||||
try {
|
||||
const { rows } = await pool.query(query, [jobName]);
|
||||
if (rows.length === 0) {
|
||||
throw new Error(`Job de controle '${jobName}' não encontrado na tabela sync_control.`);
|
||||
}
|
||||
return rows[0].last_run_timestamp;
|
||||
} catch (error) {
|
||||
logError(`Erro ao buscar último timestamp para o job '${jobName}':`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atualiza o timestamp da última execução de um job de sincronização.
|
||||
* @param {string} jobName - O nome do job.
|
||||
* @param {Date} timestamp - O novo timestamp.
|
||||
*/
|
||||
static async updateLastRunTimestamp(jobName, timestamp) {
|
||||
const query = 'UPDATE sync_control SET last_run_timestamp = $1 WHERE job_name = $2';
|
||||
try {
|
||||
await pool.query(query, [timestamp, jobName]);
|
||||
} catch (error) {
|
||||
logError(`Erro ao atualizar o timestamp para o job '${jobName}':`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insere um novo comentário na fila de sincronização.
|
||||
* Usa ON CONFLICT para evitar duplicatas com base no sistema de origem e ID do comentário.
|
||||
* @param {object} commentData - Dados do comentário.
|
||||
* @param {number} commentData.hubsoftAtendimentoId - ID do atendimento no HubSoft.
|
||||
* @param {string} commentData.sourceSystem - 'hubsoft' ou 'glpi'.
|
||||
* @param {string} commentData.sourceCommentId - ID do comentário na origem.
|
||||
* @param {string} commentData.content - Conteúdo do comentário.
|
||||
* @param {string} [commentData.author] - Autor do comentário.
|
||||
*/
|
||||
static async insertComment(commentData) {
|
||||
// Primeiro, precisamos do ID do sync_data correspondente ao atendimento do HubSoft.
|
||||
const syncDataQuery = 'SELECT id FROM sync_data WHERE hubsoft_ticket_id = $1';
|
||||
const syncDataRes = await pool.query(syncDataQuery, [commentData.hubsoftAtendimentoId]);
|
||||
|
||||
if (syncDataRes.rows.length === 0) {
|
||||
logInfo(`Nenhum registro de sincronização encontrado para o atendimento HubSoft ID ${commentData.hubsoftAtendimentoId}. Comentário não será inserido.`);
|
||||
return false;
|
||||
}
|
||||
const syncDataId = syncDataRes.rows[0].id;
|
||||
|
||||
const insertQuery = `
|
||||
INSERT INTO sync_comments (
|
||||
sync_data_id, source_system, source_comment_id, content, author
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
ON CONFLICT (source_system, source_comment_id) DO NOTHING;
|
||||
`;
|
||||
const values = [
|
||||
syncDataId,
|
||||
commentData.sourceSystem,
|
||||
commentData.sourceCommentId,
|
||||
commentData.content,
|
||||
commentData.author
|
||||
];
|
||||
|
||||
try {
|
||||
await pool.query(insertQuery, values);
|
||||
return true;
|
||||
} catch (error) {
|
||||
logError('Erro ao inserir comentário em sync_comments:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Busca comentários pendentes para um sistema de destino específico.
|
||||
* @param {string} destinationSystem - O sistema de destino ('glpi' ou 'hubsoft').
|
||||
* @returns {Promise<Array<object>>} Uma lista de comentários pendentes.
|
||||
*/
|
||||
static async getPendingCommentsForDestination(destinationSystem) {
|
||||
const sourceSystem = destinationSystem === 'glpi' ? 'hubsoft' : 'glpi';
|
||||
const query = `
|
||||
SELECT sc.id, sc.content, sc.author, sd.glpi_ticket_id, sd.hubsoft_ticket_id
|
||||
FROM sync_comments sc
|
||||
JOIN sync_data sd ON sc.sync_data_id = sd.id
|
||||
WHERE sc.source_system = $1
|
||||
AND sc.sync_status = 'pending_sync'
|
||||
AND sc.sync_attempts < 5; -- Evita retentativas infinitas
|
||||
`;
|
||||
const { rows } = await pool.query(query, [sourceSystem]);
|
||||
return rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Atualiza o status de sincronização de um comentário.
|
||||
* @param {number} commentId - O ID do comentário em sync_comments.
|
||||
* @param {string} status - O novo status ('synced', 'sync_error').
|
||||
* @param {string|null} destinationId - O ID do comentário no sistema de destino.
|
||||
* @param {string|null} errorMessage - A mensagem de erro, se houver.
|
||||
*/
|
||||
static async updateCommentSyncStatus(commentId, status, destinationId = null, errorMessage = null) {
|
||||
const query = `
|
||||
UPDATE sync_comments
|
||||
SET
|
||||
sync_status = $1,
|
||||
destination_comment_id = $2,
|
||||
error_message = $3,
|
||||
sync_attempts = sync_attempts + 1,
|
||||
updated_at = NOW()
|
||||
WHERE id = $4;
|
||||
`;
|
||||
try {
|
||||
await pool.query(query, [status, destinationId, errorMessage, commentId]);
|
||||
} catch (error) {
|
||||
logError(`Erro ao atualizar status do comentário ID ${commentId}:`, error);
|
||||
// Não relançamos o erro para não parar o loop de sincronização.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CommentModel;
|
||||
0
src/model/commentSyncModel.js
Normal file
0
src/model/commentSyncModel.js
Normal file
@ -123,7 +123,20 @@ class GlpiModel {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
static async insertComment(commentData) {
|
||||
const query = `
|
||||
INSERT INTO glpi_tickets_comments (tickets_id, content, date_creation)
|
||||
VALUES (?, ?, ?)
|
||||
`;
|
||||
const values = [commentData.tickets_id, commentData.content, new Date()];
|
||||
try {
|
||||
const [rows] = await pool.query(query, values);
|
||||
return rows && rows.insertId ? { id: rows.insertId } : null;
|
||||
}
|
||||
catch (err) {
|
||||
logError(`Erro ao inserir comentário no GLPI: ${err}`);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GlpiModel;
|
||||
module.exports = GlpiModel;
|
||||
|
||||
@ -49,6 +49,27 @@ const validateMensagensByAtendimento = async (id_atendimento) => {
|
||||
return rows;
|
||||
}
|
||||
|
||||
const getNewMessagesFromDB = async (lastFetchTimestamp) => {
|
||||
// Busca novas mensagens e o id_atendimento correspondente
|
||||
const query = `
|
||||
SELECT
|
||||
m.id_atendimento_mensagem,
|
||||
m.id_atendimento,
|
||||
m.mensagem,
|
||||
m.data_cadastro
|
||||
FROM
|
||||
atendimento_mensagem AS m
|
||||
INNER JOIN
|
||||
atendimento AS a ON m.id_atendimento = a.id_atendimento
|
||||
WHERE
|
||||
m.data_cadastro > $1
|
||||
AND a.id_tipo_atendimento = 4
|
||||
AND a.id_usuario_abertura = 248;
|
||||
`;
|
||||
const { rows } = await pool.query(query, [lastFetchTimestamp]);
|
||||
return rows;
|
||||
}
|
||||
|
||||
const updateFechaAtendimento = async (id_atendimento, closingMessage) => {
|
||||
const query = `
|
||||
UPDATE atendimento
|
||||
@ -76,5 +97,6 @@ module.exports = {
|
||||
getAtendimentosFromDB,
|
||||
validateAtendimentoStatus,
|
||||
validateMensagensByAtendimento,
|
||||
updateFechaAtendimento // Exportando a função
|
||||
updateFechaAtendimento, // Exportando a função
|
||||
getNewMessagesFromDB
|
||||
};
|
||||
@ -1,8 +1,13 @@
|
||||
const { Router } = require('express');
|
||||
const ticketController = require('./controller/closureController.js');
|
||||
const closureController = require('./controller/closureController.js');
|
||||
const commentController = require('./controller/commentController'); // Novo
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.post('/close-ticket', ticketController.closeTicket);
|
||||
router.post('/webhook/close-ticket', closureController.closeTicket);
|
||||
router.post('/webhook/new-comment', commentController.handleNewComment); // Nova rota
|
||||
|
||||
|
||||
|
||||
|
||||
module.exports = router;
|
||||
@ -31,14 +31,6 @@ CREATE TABLE hubsoft_tickets (
|
||||
-- =============================================
|
||||
|
||||
CREATE TYPE source_last_enum AS ENUM ('hubsoft', 'glpi');
|
||||
CREATE TYPE status_sync_enum AS ENUM (
|
||||
'pending_create',
|
||||
'created_glpi',
|
||||
'pending_close',
|
||||
'closed_glpi',
|
||||
'sync_error',
|
||||
'need_update'
|
||||
);
|
||||
|
||||
CREATE TABLE sync_data (
|
||||
id SERIAL PRIMARY KEY,
|
||||
@ -54,8 +46,43 @@ CREATE TABLE sync_data (
|
||||
UNIQUE (hubsoft_ticket_id, glpi_ticket_id)
|
||||
);
|
||||
|
||||
-- =============================================
|
||||
-- TABELA: sync_comments
|
||||
-- DESCRIÇÃO: Armazena o estado de comentarios entre HubSoft e GLPI
|
||||
-- =============================================
|
||||
|
||||
|
||||
CREATE TABLE sync_comments (
|
||||
id SERIAL PRIMARY KEY,
|
||||
sync_data_id INTEGER NOT NULL REFERENCES sync_data(id),
|
||||
source_system VARCHAR(20) NOT NULL, -- 'hubsoft' ou 'glpi'
|
||||
source_comment_id VARCHAR(255) NOT NULL, -- ID do comentário no sistema de origem
|
||||
destination_comment_id VARCHAR(255), -- ID do comentário no sistema de destino
|
||||
content TEXT NOT NULL,
|
||||
author VARCHAR(255), -- Nome do autor do comentário (opcional, mas útil)
|
||||
sync_status VARCHAR(50) NOT NULL DEFAULT 'pending_sync', -- ex: 'pending_sync', 'synced', 'sync_error'
|
||||
sync_attempts INTEGER DEFAULT 0,
|
||||
error_message TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Índice para evitar duplicatas e otimizar buscas
|
||||
CREATE UNIQUE INDEX idx_unique_source_comment ON sync_comments(source_system, source_comment_id);
|
||||
CREATE INDEX idx_sync_status ON sync_comments(sync_status, sync_attempts);
|
||||
|
||||
|
||||
-- =============================================
|
||||
-- TABELA: sync_data
|
||||
-- DESCRIÇÃO: Armazena o estado de comentarios entre HubSoft e GLPI
|
||||
-- =============================================
|
||||
|
||||
CREATE TABLE sync_control (
|
||||
job_name VARCHAR(100) PRIMARY KEY,
|
||||
last_run_timestamp TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
|
||||
-- Inserir o registro inicial para o nosso novo cron de comentários
|
||||
INSERT INTO sync_control (job_name, last_run_timestamp) VALUES ('hubsoft_comments_sync', '2024-01-01 00:00:00');
|
||||
|
||||
|
||||
|
||||
115
src/services/commentService.js
Normal file
115
src/services/commentService.js
Normal file
@ -0,0 +1,115 @@
|
||||
// src/services/commentService.js
|
||||
const hubsoftModel = require('../model/hubsoftModel.js');
|
||||
const commentModel = require('../model/commentModel.js'); // a ser criado
|
||||
const glpiModel = require('../model/glpiModel.js');
|
||||
const hubglpiModel = require('../model/hubglpiModel.js');
|
||||
const hubsoftService = require('./hubsoftService.js');
|
||||
const { sanitizeGLPIComment } = require('../utils/commentSanitizer.js');
|
||||
const { logInfo, logError, logWarning } = require('../utils/logger');
|
||||
|
||||
/**
|
||||
* Busca novos comentários no HubSoft e os salva no banco intermediário.
|
||||
*/
|
||||
async function syncHubsoftCommentsToLocalDB() {
|
||||
logInfo('Iniciando sincronização de comentários do HubSoft...');
|
||||
const lastRun = await commentModel.getLastRunTimestamp('hubsoft_comments_sync');
|
||||
|
||||
const newMessages = await hubsoftModel.getNewMessagesFromDB(lastRun);
|
||||
if (newMessages.length === 0) {
|
||||
logInfo('Nenhum comentário novo encontrado no HubSoft.');
|
||||
return;
|
||||
}
|
||||
|
||||
logInfo(`Encontrados ${newMessages.length} novos comentários no HubSoft.`);
|
||||
|
||||
for (const message of newMessages) {
|
||||
try {
|
||||
// A função insertComment deve ter uma cláusula ON CONFLICT para não duplicar
|
||||
const commentInserted = await commentModel.insertComment({
|
||||
hubsoftAtendimentoId: message.id_atendimento,
|
||||
sourceSystem: 'hubsoft',
|
||||
sourceCommentId: message.id_atendimento_mensagem,
|
||||
content: message.mensagem,
|
||||
// author: ... se disponível
|
||||
});
|
||||
if (commentInserted){
|
||||
logInfo(`Comentário HubSoft ID ${message.id_atendimento_mensagem} salvo para sincronização.`);
|
||||
}else{
|
||||
logInfo(`Chamado HubSoft ID ${message.id_atendimento} não possui registro de sincronização. Comentário ignorado.`);
|
||||
}
|
||||
} catch (error) {
|
||||
// Se o erro for de violação de chave única, apenas ignoramos.
|
||||
if (error.code !== '23505') { // Código de erro do PostgreSQL para unique_violation
|
||||
logError(`Erro ao salvar comentário HubSoft ID ${message.id_atendimento_mensagem}:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Atualiza o timestamp da última execução
|
||||
await commentModel.updateLastRunTimestamp('hubsoft_comments_sync', new Date());
|
||||
logInfo('Sincronização de comentários do HubSoft para o banco local concluída.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Envia comentários pendentes do banco local para o GLPI.
|
||||
*/
|
||||
async function sendPendingCommentsToGlpi() {
|
||||
// Busca comentários com origem 'hubsoft' que estão pendentes de envio para o GLPI
|
||||
const pendingComments = await commentModel.getPendingCommentsForDestination('glpi');
|
||||
|
||||
if (pendingComments.length === 0) {
|
||||
return; // Nenhum comentário para processar
|
||||
}
|
||||
|
||||
logInfo(`Encontrados ${pendingComments.length} comentários pendentes para envio ao GLPI.`);
|
||||
|
||||
for (const comment of pendingComments) {
|
||||
try {
|
||||
// 1. Inserir o comentário diretamente no banco de dados do GLPI
|
||||
const newGlpiComment = await glpiModel.insertComment({
|
||||
tickets_id: comment.glpi_ticket_id, // ID do ticket no GLPI
|
||||
content: comment.content,
|
||||
// Outros campos necessários, como autor, data, etc.
|
||||
});
|
||||
|
||||
// Sucesso: atualiza o status no nosso banco
|
||||
await commentModel.updateCommentSyncStatus(comment.id, 'synced', newGlpiComment.id);
|
||||
logInfo(`Comentário ID ${comment.id} sincronizado com sucesso para o GLPI. Novo ID de comentário no GLPI: ${newGlpiComment.id}`);
|
||||
} catch (error) {
|
||||
// Falha: atualiza o status para erro e incrementa a tentativa no nosso banco
|
||||
await commentModel.updateCommentSyncStatus(comment.id, 'sync_error', null, error.message);
|
||||
logError(`Falha ao sincronizar comentário ID ${comment.id} para o GLPI:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recebe um novo comentário do GLPI (via webhook) e o envia para o HubSoft.
|
||||
* @param {number} glpiTicketId - O ID do ticket no GLPI.
|
||||
* @param {string} rawContent - O conteúdo bruto do comentário.
|
||||
*/
|
||||
async function syncGlpiCommentToHubsoft(glpiTicketId, rawContent) {
|
||||
// 1. Encontrar o registro de sincronização para obter o ID do HubSoft
|
||||
const syncRecord = await hubglpiModel.getIdByGlpiID(glpiTicketId);
|
||||
|
||||
if (!syncRecord || !syncRecord.hubsoftId) {
|
||||
logWarning(`Recebido comentário para o ticket GLPI ID ${glpiTicketId}, mas não há registro de sincronização correspondente. Ignorando.`);
|
||||
// Não lançamos um erro, pois o comentário pode ser de um ticket não sincronizado.
|
||||
return;
|
||||
}
|
||||
|
||||
const hubsoftTicketId = syncRecord.hubsoftId;
|
||||
|
||||
// 2. Sanitizar o comentário (remover HTML, etc.)
|
||||
const sanitizedContent = sanitizeGLPIComment({ content: rawContent });
|
||||
|
||||
// 3. Enviar o comentário para a API do HubSoft
|
||||
logInfo(`Enviando comentário do GLPI Ticket ${glpiTicketId} para o HubSoft Atendimento ${hubsoftTicketId}.`);
|
||||
await hubsoftService.addMensagem(hubsoftTicketId, sanitizedContent);
|
||||
|
||||
// Opcional: Salvar o comentário no banco intermediário (sync_comments) para ter um log.
|
||||
// Isso pode ser útil para auditoria, mas não é estritamente necessário para o envio.
|
||||
logInfo(`Comentário do GLPI Ticket ${glpiTicketId} enviado com sucesso para o HubSoft.`);
|
||||
}
|
||||
|
||||
module.exports = { syncHubsoftCommentsToLocalDB, sendPendingCommentsToGlpi, syncGlpiCommentToHubsoft };
|
||||
@ -77,7 +77,39 @@ const closeAtendimento = async (id_atendimento, closingMessage) => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Adiciona uma nova mensagem a um atendimento no HubSoft.
|
||||
* @param {number} id_atendimento - O ID do atendimento no HubSoft.
|
||||
* @param {string} mensagem - O conteúdo da mensagem a ser adicionada.
|
||||
* @returns {Promise<object>} A resposta da API do HubSoft.
|
||||
*/
|
||||
async function addMensagem(id_atendimento, mensagem) {
|
||||
// 1. Obter o token de autenticação
|
||||
const token = await getAuthToken();
|
||||
|
||||
// 2. Construir a URL completa do endpoint
|
||||
const url = `${apiConfig.hubsoft.baseUrl}/integracao/atendimento/adicionar_mensagem/${id_atendimento}`;
|
||||
const payload = { mensagem };
|
||||
|
||||
try {
|
||||
logInfo(`Enviando nova mensagem para o atendimento HubSoft ID ${id_atendimento}...`);
|
||||
// 3. Usar a instância 'axios' e passar o token no header
|
||||
const response = await axios.post(url, payload, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
logInfo(`Resposta da API HubSoft para adição de mensagem: ${JSON.stringify(response.data)}`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
logError(`Erro ao adicionar mensagem no atendimento HubSoft ID ${id_atendimento}:`, error.response?.data || error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
updateAtendimentoStatus,
|
||||
closeAtendimento
|
||||
closeAtendimento,
|
||||
addMensagem
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user