sothis-contratacao-api/src/modules/viabilidade/viabilidade.core.js
gabriel.amancio 54a81db111 WIP: O que já foi feito:
- Trazer duas caixas como alternativa.

O que falta:

- Fazer viabilidade a partir da distância do Trajeto.
- Mudar pastas autorizadas do geogrid.
2026-07-20 09:44:06 -03:00

96 lines
4.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 }; // 10015000m: 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 };