FEAT: Adiciona suporte à consulta de viabilidade por geolocalização e refatora lógica de processamento de CSV
This commit is contained in:
parent
e5b530f9a4
commit
e24db52732
@ -21,5 +21,6 @@ const apiConfig = {
|
||||
};
|
||||
|
||||
const apiViabilidadeUrl = process.env.API_VIABILIDADE_URL;
|
||||
const apiUrlBase = process.env.API_URL_BASE;
|
||||
|
||||
module.exports = { apiConfig, apiViabilidadeUrl };
|
||||
module.exports = { apiConfig, apiViabilidadeUrl, apiUrlBase };
|
||||
@ -1,4 +1,4 @@
|
||||
const { consultarViabilidade } = require('../service/viabilidadeService');
|
||||
const { consultarViabilidade, discoverDataType } = require('../service/viabilidadeService');
|
||||
const { processCsvFile, countValidLines } = require('../service/csvService');
|
||||
const { getJob, createJob } = require('../service/jobStore.service');
|
||||
const fs = require('fs');
|
||||
@ -18,6 +18,21 @@ async function consultarViabilidadeController(req, res) {
|
||||
}
|
||||
}
|
||||
|
||||
// Controlador para consultar viabilidade via geolocalização
|
||||
async function consultarViaGeolocalizacaoController(req, res) {
|
||||
try {
|
||||
const data = req.body;
|
||||
data.source = 'viabiliza.sothis.com.br';
|
||||
const result = await consultarViabilidade(data);
|
||||
res.json(result);
|
||||
} catch (error) {
|
||||
console.error("Erro ao consultar viabilidade por geolocalização:", error && (error.message || error));
|
||||
res.status(500).json({ error: "Erro ao consultar viabilidade por geolocalização" });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function uploadCsvFile(req, res) {
|
||||
try {
|
||||
// validação simples: verifica se multer populou req.file
|
||||
@ -28,6 +43,12 @@ async function uploadCsvFile(req, res) {
|
||||
const filePath = req.file.path;
|
||||
const originalName = req.file.originalname || req.file.filename || 'input.csv';
|
||||
|
||||
// Verifica o tipo de dados do CSV
|
||||
const dataType = await discoverDataType(filePath);
|
||||
if (dataType === 'unknown') {
|
||||
return res.status(400).json({ error: 'Formato de CSV inválido. Deve conter colunas CEP e Número ou Latitude e Longitude.' });
|
||||
}
|
||||
|
||||
// Conta as linhas válidas primeiro
|
||||
const total = await countValidLines(filePath);
|
||||
if (total === 0) {
|
||||
@ -151,4 +172,4 @@ async function downloadModelController(req, res) {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { consultarViabilidadeController, uploadCsvFile, getJobController, downloadCsvController, downloadModelController };
|
||||
module.exports = { consultarViabilidadeController, uploadCsvFile, getJobController, downloadCsvController, downloadModelController, consultarViaGeolocalizacaoController };
|
||||
@ -10,6 +10,9 @@ const upload = multer({ dest: path.join(__dirname, '..', 'uploads') });
|
||||
// Rota para consultar viabilidade
|
||||
router.post('/viabilidade', viabilidadeController.consultarViabilidadeController);
|
||||
|
||||
// Rota para consultar por geolocalização
|
||||
router.post('/geolocalizacao', viabilidadeController.consultarViaGeolocalizacaoController);
|
||||
|
||||
// rota de upload agora usa multer.single('csvfile')
|
||||
router.post('/upload', upload.single('csvfile'), viabilidadeController.uploadCsvFile);
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
const { consultarViabilidade } = require('./viabilidadeService');
|
||||
const { consultarViabilidade, discoverDataType } = require('./viabilidadeService');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const readline = require('readline');
|
||||
@ -11,8 +11,9 @@ const {
|
||||
failJob
|
||||
} = require('./jobStore.service');
|
||||
|
||||
// conta linhas válidas no CSV (com CEP e Número)
|
||||
// conta linhas válidas no CSV (com CEP e Número ou Latitude e Longitude)
|
||||
async function countValidLines(inputPath) {
|
||||
const dataType = await discoverDataType(inputPath);
|
||||
const instream = fs.createReadStream(inputPath, { encoding: 'utf8' });
|
||||
const rl = readline.createInterface({ input: instream, crlfDelay: Infinity });
|
||||
|
||||
@ -20,6 +21,8 @@ async function countValidLines(inputPath) {
|
||||
let headers = [];
|
||||
let idxCep = -1;
|
||||
let idxNumero = -1;
|
||||
let idxLatitude = -1;
|
||||
let idxLongitude = -1;
|
||||
let total = 0;
|
||||
|
||||
for await (const rawLine of rl) {
|
||||
@ -31,15 +34,22 @@ async function countValidLines(inputPath) {
|
||||
const lower = headers.map(h => h.toLowerCase());
|
||||
idxCep = lower.indexOf('cep');
|
||||
idxNumero = lower.indexOf('numero');
|
||||
idxLatitude = lower.indexOf('latitude');
|
||||
idxLongitude = lower.indexOf('longitude');
|
||||
isHeader = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
const cols = line.split(';').map(c => c.trim());
|
||||
if (dataType === 'cep') {
|
||||
const cep = idxCep >= 0 ? String(cols[idxCep] || '').replace(/\D/g, '') : '';
|
||||
const numero = idxNumero >= 0 ? cols[idxNumero] : '';
|
||||
|
||||
if (cep && numero) total++;
|
||||
} else if (dataType === 'geolocalizacao') {
|
||||
const latitude = idxLatitude >= 0 ? parseFloat(cols[idxLatitude]) : NaN;
|
||||
const longitude = idxLongitude >= 0 ? parseFloat(cols[idxLongitude]) : NaN;
|
||||
if (!isNaN(latitude) && !isNaN(longitude)) total++;
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
@ -48,6 +58,7 @@ async function countValidLines(inputPath) {
|
||||
// nova função: processa CSV linha a linha, chama consultarViabilidade e gera CSV de saída
|
||||
// Recebe jobId já criado no controller
|
||||
async function processCsvFile(jobId, inputPath, originalName) {
|
||||
const dataType = await discoverDataType(inputPath);
|
||||
const total = await countValidLines(inputPath);
|
||||
// Job já criado no controller
|
||||
// const jobId = createJob(total);
|
||||
@ -64,6 +75,8 @@ async function processCsvFile(jobId, inputPath, originalName) {
|
||||
let headers = [];
|
||||
let idxCep = -1;
|
||||
let idxNumero = -1;
|
||||
let idxLatitude = -1;
|
||||
let idxLongitude = -1;
|
||||
|
||||
for await (const rawLine of rl) {
|
||||
const line = rawLine.replace(/\r$/, ''); // normalize CRLF
|
||||
@ -78,6 +91,8 @@ async function processCsvFile(jobId, inputPath, originalName) {
|
||||
const lower = headers.map(h => h.toLowerCase());
|
||||
idxCep = lower.indexOf('cep');
|
||||
idxNumero = lower.indexOf('numero');
|
||||
idxLatitude = lower.indexOf('latitude');
|
||||
idxLongitude = lower.indexOf('longitude');
|
||||
|
||||
// se não encontrar, tenta variações comuns
|
||||
const idx = lower.indexOf('codigo postal');
|
||||
@ -95,6 +110,8 @@ async function processCsvFile(jobId, inputPath, originalName) {
|
||||
.map(c => c.trim())
|
||||
.filter(c => c !== '');
|
||||
|
||||
let dataToSend = {};
|
||||
if (dataType === 'cep') {
|
||||
const cepRaw = (idxCep >= 0 && cols[idxCep]) ? cols[idxCep] : '';
|
||||
const cep = String(cepRaw).replace(/\D/g, ''); // keep digits only
|
||||
const numero = (idxNumero >= 0 && cols[idxNumero]) ? cols[idxNumero] : '';
|
||||
@ -102,13 +119,25 @@ async function processCsvFile(jobId, inputPath, originalName) {
|
||||
if (!cep || !numero) {
|
||||
continue; // pula linha inválida
|
||||
}
|
||||
dataToSend = { cep, numero };
|
||||
} else if (dataType === 'geolocalizacao') {
|
||||
const latitude = (idxLatitude >= 0 && cols[idxLatitude]) ? parseFloat(cols[idxLatitude]) : NaN;
|
||||
const longitude = (idxLongitude >= 0 && cols[idxLongitude]) ? parseFloat(cols[idxLongitude]) : NaN;
|
||||
|
||||
if (isNaN(latitude) || isNaN(longitude)) {
|
||||
continue; // pula linha inválida
|
||||
}
|
||||
dataToSend = { latitude, longitude };
|
||||
} else {
|
||||
continue; // tipo desconhecido, pula
|
||||
}
|
||||
|
||||
try {
|
||||
const viab = await consultarViabilidade({ cep, numero });
|
||||
const viab = await consultarViabilidade(dataToSend);
|
||||
|
||||
const distancia = viab.distancia ?? (viab.raw && (viab.raw.distancia || viab.raw.distance)) ?? '';
|
||||
|
||||
const endereco = `${(viab.logradouro) || ''}, ${(viab.bairro) || ''}, ${(viab.cidade) || ''}/${(viab.estado) || ''}, ${(viab.cep) || ''}`.trim().replace(/^[, ]+|[, ]+$/g, '');
|
||||
const endereco = viab.endereco;
|
||||
|
||||
if (viab.naoDedicado) {
|
||||
var naoDedicado = "Viavel";
|
||||
|
||||
@ -1,11 +1,20 @@
|
||||
const axios = require('axios');
|
||||
const { apiConfig, apiViabilidadeUrl } = require('../config/apiConfig');
|
||||
const fs = require('fs');
|
||||
const readline = require('readline');
|
||||
const { apiConfig, apiViabilidadeUrl, apiUrlBase } = require('../config/apiConfig');
|
||||
|
||||
const DEFAULT_TIMEOUT = (apiConfig && apiConfig.timeoutMs) || 10000;
|
||||
|
||||
async function consultarViabilidade(data) {
|
||||
try {
|
||||
const response = await axios.post(apiViabilidadeUrl, data, {
|
||||
const dataType = await discoverDataType(data);
|
||||
let endpoint = apiUrlBase;
|
||||
if (dataType === 'geolocalizacao') {
|
||||
endpoint += 'viabilidade/lat-long';
|
||||
} else {
|
||||
endpoint += 'viabilidade';
|
||||
}
|
||||
const response = await axios.post(endpoint, data, {
|
||||
timeout: DEFAULT_TIMEOUT,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
@ -16,4 +25,42 @@ async function consultarViabilidade(data) {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { consultarViabilidade };
|
||||
|
||||
// Preciso de uma função para verificar se os dados vindos são de CEP ou de geolocalização
|
||||
async function discoverDataType(input) {
|
||||
if (typeof input === 'string') {
|
||||
// Trata como filePath
|
||||
const instream = fs.createReadStream(input, { encoding: 'utf8' });
|
||||
const rl = readline.createInterface({ input: instream, crlfDelay: Infinity });
|
||||
|
||||
let headers = [];
|
||||
for await (const rawLine of rl) {
|
||||
const line = rawLine.replace(/\r$/, '');
|
||||
if (!line.trim()) continue;
|
||||
headers = line.split(';').map(h => h.trim().toLowerCase());
|
||||
break;
|
||||
}
|
||||
rl.close();
|
||||
|
||||
if (headers.includes('cep') && headers.includes('numero')) {
|
||||
return 'cep';
|
||||
} else if (headers.includes('latitude') && headers.includes('longitude')) {
|
||||
return 'geolocalizacao';
|
||||
} else {
|
||||
return 'unknown';
|
||||
}
|
||||
} else if (typeof input === 'object') {
|
||||
// Trata como objeto de dados
|
||||
if (input.cep && input.numero) {
|
||||
return 'cep';
|
||||
} else if (input.latitude && input.longitude) {
|
||||
return 'geolocalizacao';
|
||||
} else {
|
||||
return 'unknown';
|
||||
}
|
||||
} else {
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { consultarViabilidade, discoverDataType };
|
||||
Loading…
Reference in New Issue
Block a user