viabiliza/service/viabilidadeService.js
gabriel.amancio 801d3fa90d REFAC: consumo do endpoint /lote, dedup de helpers e colunas por caixa
- csvService/viabilidadeService passam a consumir o endpoint /viabilidade/lote
  da API em blocos, no lugar de 1 requisição HTTP por linha.
- Helpers de detecção de formato/cabeçalho extraídos para fileFormat.js (remove
  duplicação entre os dois serviços).
- Planilha de saída: 4 colunas por caixa (Provedor, Não Dedicado, Dedicado,
  Distância Trajeto) para as 2 caixas mais próximas; Caixa 2 replica a Caixa 1
  quando não há segunda caixa / sem cobertura.
- parseCoordinate passa a aceitar direção O (Oeste => negativo) e L (Leste).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 15:49:29 -03:00

107 lines
3.4 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const axios = require('axios');
const fs = require('fs');
const readline = require('readline');
const XLSX = require('xlsx');
const { apiUrlBase } = require('../config/apiConfig');
const {
normalizeHeader,
isExcelFile,
detectDelimiter,
hasCepHeader,
hasAddressOrNumberHeader,
hasGeoHeaders
} = require('./fileFormat');
function findHeaderRow(rows) {
return rows.find(row => {
const hasCepNumero = hasCepHeader(row) && hasAddressOrNumberHeader(row);
const hasGeo = hasGeoHeaders(row);
return hasCepNumero || hasGeo;
}) || [];
}
function readExcelHeaders(filePath) {
const workbook = XLSX.readFile(filePath, { cellDates: false, raw: false });
const firstSheetName = workbook.SheetNames[0];
if (!firstSheetName) return [];
const rows = XLSX.utils.sheet_to_json(workbook.Sheets[firstSheetName], {
header: 1,
blankrows: true,
defval: ''
});
return findHeaderRow(rows).map(normalizeHeader);
}
async function readDelimitedHeaders(filePath) {
const instream = fs.createReadStream(filePath, { encoding: 'utf8' });
const rl = readline.createInterface({ input: instream, crlfDelay: Infinity });
for await (const rawLine of rl) {
const line = rawLine.replace(/^/, '').replace(/\r$/, '');
if (!line.trim()) continue;
const headers = line.split(detectDelimiter(line)).map(normalizeHeader);
if ((hasCepHeader(headers) && hasAddressOrNumberHeader(headers)) || hasGeoHeaders(headers)) {
rl.close();
return headers;
}
}
rl.close();
return [];
}
// Verifica se os dados vindos são de CEP ou de geolocalização (aceita caminho de arquivo ou objeto).
async function discoverDataType(input) {
if (typeof input === 'string') {
const headers = isExcelFile(input)
? readExcelHeaders(input)
: await readDelimitedHeaders(input);
if (hasCepHeader(headers) && hasAddressOrNumberHeader(headers)) return 'cep';
if (hasGeoHeaders(headers)) return 'geolocalizacao';
return 'unknown';
}
if (input && typeof input === 'object') {
if (input.cep && input.numero) return 'cep';
if (input.latitude !== undefined && input.longitude !== undefined) return 'geolocalizacao';
return 'unknown';
}
return 'unknown';
}
// Consulta individual (manual / uma linha). Escolhe o endpoint conforme o tipo dos dados.
async function consultarViabilidade(data) {
const dataType = await discoverDataType(data);
const endpoint = apiUrlBase + (dataType === 'geolocalizacao' ? 'viabilidade/lat-long' : 'viabilidade');
const response = await axios.post(endpoint, data, {
// A API agora faz geocode + GeoGrid + trajeto (várias chamadas externas);
// o cep-promise também pode ser lento. 30s evita timeout prematuro.
timeout: 30000,
headers: { 'Content-Type': 'application/json' }
});
return response.data;
}
// Consulta em LOTE: envia um array de itens ({cep,numero} ou {latitude,longitude}) de uma só vez.
// Retorna o array `resultados` da API, alinhado por índice ao array enviado.
async function consultarViabilidadeLote(itens, modo) {
const endpoint = apiUrlBase + 'viabilidade/lote';
const body = { itens };
if (modo) body.modo = modo;
const response = await axios.post(endpoint, body, {
timeout: 180000,
headers: { 'Content-Type': 'application/json' }
});
const data = response.data || {};
return Array.isArray(data.resultados) ? data.resultados : [];
}
module.exports = { consultarViabilidade, consultarViabilidadeLote, discoverDataType };