// src/models/ticketGlpiModel.js const glpiPool = require('../data/glpiDataBase'); const { logInfo, logError } = require('../utils/logger'); const LocationMappingModel = require('./locationMappingModel'); class TicketGlpiModel { // Mapeamento de status do ServiceNow para o GLPI static mapStatus(snState) { const statusMap = { 'Aguardando Atendimento': 1, 'Em Atendimento': 2, 'Em Espera': 4, 'Encerrado': 5, 'Encerrado - Omitido': 6 }; return statusMap[snState] || 1; } // Mapeamento de prioridade static mapPriority(snPriority) { const priorityMap = { '1': 5, '2': 4, '3': 3, '4': 2, '5': 1 }; return priorityMap[snPriority] || 3; } // Mapeamento de categoria static mapCategory(shortDescription) { const categoryMap = { 'Problemas com Telefonia Fixa': 5717, 'Problemas com Internet': 5707, 'Problemas com Wi-Fi/Rede Corporativa': 5707, 'Wi-fi Clientes': 5707 }; return categoryMap[shortDescription] || 5717; } // Mapeamento de SLA baseado no tipo e na descrição static mapSlasIdTtr(ticketData) { const isRequisicao = ticketData.type === 'requisicao'; const shortDesc = ticketData.short_description; const isTelefonia = shortDesc === 'Problemas com Telefonia Fixa' || shortDesc === 'Solicitações relacionadas a telefonia fixa'; const isLinkDedicado = shortDesc === 'Problemas com Internet'; const isWifi = shortDesc === 'Problemas com Wi-Fi/Rede Corporativa' || shortDesc === 'Wi-fi Clientes'; if (isRequisicao && isTelefonia) { return 37; } if (isRequisicao && (isLinkDedicado || isWifi)) { return 37; } if (!isRequisicao && (isWifi || isLinkDedicado)) { return 34; } if (!isRequisicao && isTelefonia) { return 34; } return 0; // Default se nenhum critério for atendido } static formatDescription(ticketData) { // Função auxiliar para extrair informações usando regex const extractInfo = (regex, description) => { if (!description) return null; const match = description.match(regex); return match ? match[1] : null; }; const extractEmailFromDescription = (description) => { return extractInfo(/([\w-\.]+@([\w-]+\.)+[\w-]{2,4})/i, description); }; const extractPhoneFromDescription = (description) => { return extractInfo(/(\+?(\d{2})?\s*\(?\d{2}\)?[\s-]*\d{4,5}[\s-]?\d{4})/i, description); }; const extractRamalFromDescription = (description) => { return extractInfo(/(\d{3,4})/i, description); }; // Obtém email, telefone e ramal, usando os valores do ticketData ou extraindo da descrição, se necessário. const email = ticketData.caller_email || extractEmailFromDescription(ticketData.description) || 'N/A'; const phone = ticketData.caller_phone || extractPhoneFromDescription(ticketData.description) || 'N/A'; const ramal = ticketData.caller_ramal || extractRamalFromDescription(ticketData.description) || 'N/A'; // Formata a descrição em HTML let htmlDescription = `
Campo Valor
Nome: ${ticketData.caller_id}
Email: ${email}
Telefone: ${phone}
Ramal: ${ramal}
Ticket Number (ServiceNow): ${ticketData.ticket_number || 'N/A'}

Descrição:
${ticketData.description || 'N/A'} `; return htmlDescription; } // Cria um ticket no GLPI static async createTicket(ticketData) { const connection = await glpiPool.getConnection(); try { await connection.beginTransaction(); // Buscar entity_id com base no location_id ou location_name let entityId = 1371; // Valor padrão (fallback) if (ticketData.location_id) { entityId = await LocationMappingModel.getGlpiEntityId(ticketData.location_id) || entityId; } else if (ticketData.location_name) { entityId = await LocationMappingModel.getGlpiEntityIdByName(ticketData.location_name) || entityId; } // Extrai o email da descrição, se existir e não houver email no ticketData let email = ticketData.caller_email; if (!email && ticketData.description) { const emailRegex = /[\w-\.]+@([\w-]+\.)+[\w-]{2,4}/g; const matches = ticketData.description.match(emailRegex); if (matches && matches.length > 0) { email = matches[0]; } } // Formata a descrição para HTML const formattedDescription = TicketGlpiModel.formatDescription(ticketData); // Mapeia o tipo do ticket const ticketType = ticketData.type === 'requisicao' ? 2 : 1; // Determina o valor de slas_id_ttr const slasIdTtr = TicketGlpiModel.mapSlasIdTtr(ticketData); // Prepara os dados para o GLPI const glpiTicketData = { entities_id: entityId, // Usar o entity_id mapeado name: ticketData.short_description || 'Ticket sem descrição', date: ticketData.opened_at || new Date(), date_mod: ticketData.updated_at || new Date(), status: TicketGlpiModel.mapStatus(ticketData.state || '1'), users_id_recipient: 917, requesttypes_id: 1, content: formattedDescription, urgency: 3, impact: 3, priority: TicketGlpiModel.mapPriority(ticketData.priority || '3'), type: ticketType, itilcategories_id: TicketGlpiModel.mapCategory(ticketData.short_description), global_validation: 1, date_creation: ticketData.opened_at || new Date(), slas_id_ttr: slasIdTtr }; // Constrói a query dinamicamente e insere o ticket principal const fields = Object.keys(glpiTicketData).join(', '); const placeholders = Object.keys(glpiTicketData).map(() => '?').join(', '); const values = Object.values(glpiTicketData); const ticketQuery = `INSERT INTO glpi_tickets (${fields}) VALUES (${placeholders})`; const [ticketResult] = await connection.execute(ticketQuery, values); const ticketId = ticketResult.insertId; // Adiciona o requerente await connection.execute( `INSERT INTO glpi_tickets_users (tickets_id, users_id, type, use_notification, alternative_email) VALUES (?, ?, 1, 1, '')`, [ticketId, 917] ); // Adiciona o grupo await connection.execute( `INSERT INTO glpi_groups_tickets (tickets_id, groups_id, type) VALUES (?, ?, 2)`, [ticketId, 30] ); await connection.commit(); logInfo(`✅ Ticket GLPI criado: ${ticketId}`); return ticketId; } catch (error) { await connection.rollback(); logError(error, `❌ Erro ao criar ticket GLPI`); throw error; } finally { connection.release(); } } // Verifica se um ticket já existe no GLPI static async ticketExists(snTicketNumber) { try { const query = ` SELECT id FROM glpi_tickets WHERE name LIKE ? OR content LIKE ? LIMIT 1 `; const [rows] = await glpiPool.execute(query, [ `%${snTicketNumber}%`, `%${snTicketNumber}%` ]); return rows.length > 0 ? rows[0].id : null; } catch (error) { logError(error, 'Erro ao verificar ticket existente'); return null; } } } /** * @module TicketGlpiModel * @description Este módulo fornece métodos para gerenciar tickets no GLPI. * Inclui funcionalidades para mapear status, prioridade e categoria, * formatar a descrição do ticket, determinar o SLA, criar tickets e * verificar se um ticket já existe no GLPI. * * O módulo usa expressões regulares para extrair informações da descrição do ticket, * como email, número de telefone e ramal, quando essas informações não são * fornecidas diretamente. Também formata a descrição em HTML para melhor * apresentação no GLPI. * * O módulo interage com o banco de dados GLPI através da conexão glpiPool. */ module.exports = TicketGlpiModel;