From 1ae5a18317d09f9dcc19c8a692d64fbaad77a46d Mon Sep 17 00:00:00 2001 From: "gabriel.amancio" Date: Mon, 15 Dec 2025 13:26:40 -0300 Subject: [PATCH] =?UTF-8?q?FEAT:=20Implementar=20servi=C3=A7o=20viaCep=20p?= =?UTF-8?q?ara=20consulta=20de=20CEP=20secund=C3=A1rio=20e=20tratamento=20?= =?UTF-8?q?de=20consulta=20de=20pastas=20do=20Geogrid.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ecosystem.config.js | 51 +++++++++++++++++++ .../contratacao/contratacao.service.js | 16 +++++- src/shared/apis/cepRestService.js | 3 ++ src/shared/apis/geogridService.js | 10 +++- src/shared/apis/viaCepService.js | 30 +++++++++++ 5 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 src/ecosystem.config.js create mode 100644 src/shared/apis/viaCepService.js diff --git a/src/ecosystem.config.js b/src/ecosystem.config.js new file mode 100644 index 0000000..300402c --- /dev/null +++ b/src/ecosystem.config.js @@ -0,0 +1,51 @@ +module.exports = { + apps: [ + // 🟢 --- PRIMEIRA APLICAÇÃO: API PRINCIPAL (servidor HTTP) --- + { + name: "contratacao-sothis-api", // Nome que aparecerá no PM2 + script: "./app.js", // Caminho do arquivo principal da API + + // 👇 Execução em modo "cluster" (um processo por core da máquina) + exec_mode: "cluster", + instances: "max", // "max" = usa todos os núcleos disponíveis + + // ⚙️ Variáveis de ambiente padrão (modo development) + env: { + watch: true, + NODE_ENV: "development", + PORT: 3000 // Porta usada no ambiente de desenvolvimento + }, + + // ⚙️ Variáveis de ambiente quando rodar com `--env production` + env_production: { + watch: false, + NODE_ENV: "production", + PORT: 8080 // Porta usada no ambiente de produção + } + }, + ] +}; + + +/** * @module server + * @description Ponto de entrada principal da aplicação. + * Este módulo é responsável por: + * Definir a configuração do PM2 para gerenciar a aplicação principal e o serviço de cron jobs. + * 1. A aplicação principal (`hubxglpi-api`) roda em modo cluster para lidar com requisições HTTP. Para encerrar chamados. + * 2. O serviço de cron jobs (`hubxglpi-cron`) roda em modo fork para evitar execuções duplicadas das tarefas agendadas. +*/ + +/** + * 💡 Dicas de uso: + * + * 🧪 Ambiente de desenvolvimento: + * pm2 start ecosystem.config.js --env development + * + * 🚀 Ambiente de produção: + * pm2 start ecosystem.config.js --env production + * pm2 startup systemd + * sudo env PATH=$PATH:/usr/bin /usr/local/lib/node_modules/pm2/bin/pm2 startup systemd -u desenvolvimento --hp /home/desenvolvimento + * pm2 save + * + * ✅ Após isso, o PM2 inicializa automaticamente os dois processos no boot do servidor. + */ \ No newline at end of file diff --git a/src/modules/contratacao/contratacao.service.js b/src/modules/contratacao/contratacao.service.js index 5efa559..36f395b 100644 --- a/src/modules/contratacao/contratacao.service.js +++ b/src/modules/contratacao/contratacao.service.js @@ -4,6 +4,7 @@ const cepRestService = require("../../shared/apis/cepRestService.js"); const hubsoftService = require("../../shared/apis/hubsoftService.js"); const logger = require('../../shared/utils/logger.js'); const repository = require('./contratacao.repository.js'); +// const viaCepService = require("../../shared/apis/viaCepService.js"); const {ViabilidadeModel, ClientModelPf, ClientModelPj, ProspectModel} = require('./contratacao.model'); // utilitário para ler chaves do payload tolerante a ":" e espaços @@ -41,7 +42,20 @@ async function verificarViabilidade(rawViabilidadeData) { logger.warn('CEP ou número não fornecidos na verificação de viabilidade.'); throw new ServiceError('CEP e número são obrigatórios.', 400); } - + + // // Consulta o endereço completo usando a API externa de CEP + // const addressData = await viaCepService.getConsultaCep(rawCep); + // if (!addressData) { + // logger.warn('Endereço não encontrado ou resposta inválida do serviço de CEP', { cep: rawCep, response: addressData }); + // throw new ServiceError('Endereço não encontrado para o CEP fornecido.', 404); + // } + // logger.info('Endereço obtido com sucesso via CEP', { data: addressData }); + + // const { logradouro, bairro, localidade: city, uf: state, cep } = addressData; // FIX: Usar address.data para desestruturar + // const addressString = `${logradouro}, ${rawNumero}, ${bairro}, ${city}, ${state}, ${cep}`; + // logger.info('String de endereço montada para geocodificação', { addressString }); + + const address = await cepRestService.getConsultaCep(rawCep); // FIX: Revertendo para a verificação correta da resposta do cepRestService if (!address) { diff --git a/src/shared/apis/cepRestService.js b/src/shared/apis/cepRestService.js index 7a4655b..becd00e 100644 --- a/src/shared/apis/cepRestService.js +++ b/src/shared/apis/cepRestService.js @@ -20,6 +20,9 @@ const getConsultaCep = async (rawCep) => { try { const cepRestUrl = 'https://api.cep.rest/'; const address = await axios.post(cepRestUrl, { cep }); + console.log(address.data); + + // Tratamento de resposta conforme diferentes estruturas possíveis if (address.data && address.data.code === 404) { logger.warn('CEP não encontrado na API externa', { cep, response: address.data }); diff --git a/src/shared/apis/geogridService.js b/src/shared/apis/geogridService.js index d361622..2ea3118 100644 --- a/src/shared/apis/geogridService.js +++ b/src/shared/apis/geogridService.js @@ -19,6 +19,7 @@ const consultaViabilidade = async (lat, lon) => { "itens[]": "caixa", ordenarCampos: ["distancia"], ordenarPor: ["asc"], + consultarPasta: "S", consultarIndividual: "S" }; @@ -35,8 +36,15 @@ const consultaViabilidade = async (lat, lon) => { } }); + // Extrai o primeiro registro da resposta em que a pasta.sigla seja "São Bernardo do Campo - SP", "São Paulo - SP", "Barueri - São Paulo" e "TMC" descartar os demais const registros = response.data && response.data.registros; - const primeiro = Array.isArray(registros) && registros.length ? registros[0] : null; + + const registrosFiltrados = registros.filter(r => { + const sigla = r.pasta && r.pasta.sigla; + return sigla === "São Bernardo do Campo - SP" || sigla === "São Paulo - SP" || sigla === "Barueri - São Paulo" || sigla === "TMC"; + }); + + const primeiro = registrosFiltrados[0]; // Retorna no formato esperado pelo controller (viabilidade.data.distancia) return { data: primeiro }; diff --git a/src/shared/apis/viaCepService.js b/src/shared/apis/viaCepService.js new file mode 100644 index 0000000..1ab802e --- /dev/null +++ b/src/shared/apis/viaCepService.js @@ -0,0 +1,30 @@ +const axios = require("axios"); +const logger = require('../utils/logger.js'); + +const getConsultaCep = async (rawCep) => { + logger.info('Iniciando consulta de CEP'); + if (!rawCep) { + logger.warn('CEP não fornecido para getConsultaCep'); + throw new Error("cep é obrigatório"); + } + const cep = String(rawCep).trim().replace(/\D/g, ""); + if (cep.length !== 8) { + logger.warn('CEP inválido fornecido para getConsultaCep', { cepBruto: rawCep, cepLimpo: cep }); + throw new Error("cep inválido, verifique se foram digitados apenas números"); + } + try { + const viaCepUrl = `https://viacep.com.br/ws/${cep}/json/`; + const response = await axios.get(viaCepUrl); + if (response.data.erro) { + logger.warn('CEP não encontrado na API ViaCEP', { cep }); + return null; // Controller tratará como 'Endereço não encontrado' + } else { + return response.data; + } + } catch (error) { + logger.error("Erro na chamada da API ViaCEP", { message: error.message, stack: error.stack, cep }); + throw new Error("Erro ao consultar o CEP"); + } +}; + +module.exports = { getConsultaCep }; \ No newline at end of file