96 lines
4.4 KiB
JavaScript
96 lines
4.4 KiB
JavaScript
|
|
const geogridService = require('../../shared/apis/geogridService.js');
|
|||
|
|
const apiConfig = require('../../shared/config/apiConfig.js');
|
|||
|
|
const logger = require('../../shared/utils/logger.js');
|
|||
|
|
const { mapWithConcurrency } = require('../../shared/utils/concurrency.js');
|
|||
|
|
|
|||
|
|
// Regra de negócio PURA: dada uma distância (em metros), define quais serviços
|
|||
|
|
// estão disponíveis. É a mesma regra de sempre — o que muda é a distância que
|
|||
|
|
// alimenta ela (agora a real de trajeto). Isolada e testável.
|
|||
|
|
function classificarPorDistancia(distancia) {
|
|||
|
|
if (distancia == null || typeof distancia !== 'number' || Number.isNaN(distancia)) {
|
|||
|
|
return { dedicado: false, naoDedicado: false };
|
|||
|
|
}
|
|||
|
|
if (distancia <= 500) return { dedicado: true, naoDedicado: true };
|
|||
|
|
if (distancia <= 1000) return { dedicado: true, naoDedicado: false };
|
|||
|
|
return { dedicado: false, naoDedicado: false }; // 1001–5000m: caixa existe, mas fora do alcance
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Núcleo de orquestração: a partir de coordenadas de origem (endereço),
|
|||
|
|
// 1) acha as N caixas mais próximas em linha reta (GeoGrid),
|
|||
|
|
// 2) calcula a distância REAL de trajeto até cada uma e pega a menor,
|
|||
|
|
// 3) classifica a viabilidade sobre a distância real (com fallback p/ reta).
|
|||
|
|
//
|
|||
|
|
// Retorna um resultado de domínio NEUTRO (sem formato de resposta HTTP):
|
|||
|
|
// { distancia, distanciaReta, distanciaReal, dedicado, naoDedicado, provedor }
|
|||
|
|
// - `distancia` é a EFETIVA usada na regra: real quando disponível, senão reta.
|
|||
|
|
async function avaliarPorCoordenadas(origem, { modo = apiConfig.geogridTrajetoModo, topN, misturarProvedores = false } = {}) {
|
|||
|
|
const { provedor, caixas } = await geogridService.consultarCaixasProximas(origem.lat, origem.lon, { topN, misturarProvedores });
|
|||
|
|
|
|||
|
|
// Sem caixa no raio: mantém o comportamento histórico ("5KM+", nada viável).
|
|||
|
|
if (!caixas || caixas.length === 0) {
|
|||
|
|
return {
|
|||
|
|
distancia: "5KM+",
|
|||
|
|
distanciaReta: null,
|
|||
|
|
distanciaReal: null,
|
|||
|
|
dedicado: false,
|
|||
|
|
naoDedicado: false,
|
|||
|
|
provedor,
|
|||
|
|
caixas: []
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Para cada caixa (na ordem do GeoGrid = por distância em linha reta),
|
|||
|
|
// calcula a distância real de trajeto e classifica individualmente.
|
|||
|
|
const detalhes = await mapWithConcurrency(caixas, apiConfig.geogridConcurrency, async (caixa) => {
|
|||
|
|
let distanciaReal = null;
|
|||
|
|
if (caixa.lat != null && caixa.lon != null) {
|
|||
|
|
try {
|
|||
|
|
const trajeto = await geogridService.consultaTrajeto(origem, caixa, modo);
|
|||
|
|
const d = Number(trajeto && trajeto.distancia);
|
|||
|
|
distanciaReal = Number.isFinite(d) ? d : null;
|
|||
|
|
} catch (err) {
|
|||
|
|
logger.warn('Trajeto falhou para uma caixa, usando linha reta nesta candidata', {
|
|||
|
|
message: err.message,
|
|||
|
|
status: err.response?.status,
|
|||
|
|
data: err.response?.data, // corpo do erro do GeoGrid (motivo do 400)
|
|||
|
|
url: err.config?.url,
|
|||
|
|
caixa: { lat: caixa.lat, lon: caixa.lon }
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
const distancia = distanciaReal ?? caixa.distanciaReta; // efetiva por caixa (real, senão reta)
|
|||
|
|
return {
|
|||
|
|
provedor: caixa.provedor,
|
|||
|
|
sigla: caixa.sigla,
|
|||
|
|
lat: caixa.lat,
|
|||
|
|
lon: caixa.lon,
|
|||
|
|
distanciaReta: caixa.distanciaReta,
|
|||
|
|
distanciaReal,
|
|||
|
|
distancia,
|
|||
|
|
...classificarPorDistancia(distancia)
|
|||
|
|
};
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Agregado (compatibilidade WordPress): menor distância efetiva entre as caixas.
|
|||
|
|
const distanciaReta = detalhes[0].distanciaReta ?? null; // caixa mais próxima em linha reta
|
|||
|
|
const reais = detalhes.map(d => d.distanciaReal).filter(d => d != null);
|
|||
|
|
const distanciaReal = reais.length ? Math.min(...reais) : null;
|
|||
|
|
|
|||
|
|
if (distanciaReal == null) {
|
|||
|
|
logger.warn('Nenhum trajeto disponível; usando distância em linha reta como fallback', { origem, distanciaReta });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const distanciaEfetiva = distanciaReal ?? distanciaReta;
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
distancia: distanciaEfetiva,
|
|||
|
|
distanciaReta,
|
|||
|
|
distanciaReal,
|
|||
|
|
...classificarPorDistancia(distanciaEfetiva),
|
|||
|
|
provedor,
|
|||
|
|
caixas: detalhes // detalhe por caixa (mais próxima em linha reta primeiro)
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
module.exports = { classificarPorDistancia, avaliarPorCoordenadas };
|