FEAT: classificação de pastas por prefixo (s-/p-/sub) e provedor por caixa
- Pastas classificadas por prefixo da sigla: "s-" => Sothis (rede própria), "p-<nome>" => parceiro (provedor = nome após o "-"); segmento "sub" após "-" prefixa "(Subterrâneo)" no provedor (ex.: "s-sub-Barueri" => "(Subterrâneo) Sothis"). - Siglas fora da convenção deixam de ser elegíveis. - Seleção mista do lote traz apenas autorizadas + parceiros, com provedor por caixa. - Classificação não depende mais de GEOGRID_AUTHORIZED_SIGLAS_PASTAS. - Testes: classificarSigla e seleção mista/prioridade cobrindo a nova regra. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
54a81db111
commit
69225fae3b
@ -31,48 +31,58 @@ const fetchRegistros = async (lat, lon) => {
|
|||||||
|
|
||||||
logger.info("Resposta do GeoGrid", {
|
logger.info("Resposta do GeoGrid", {
|
||||||
totalRegistros: registros.length,
|
totalRegistros: registros.length,
|
||||||
siglas: registros.map(r => r.pasta?.sigla),
|
siglas: registros.map(r => r.pasta?.sigla)
|
||||||
autorizadas: apiConfig.geogridAuthorizedSiglasPastas
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return registros;
|
return registros;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Seleção de registros por prioridade: autorizados (rede própria) > parceiros > nenhum.
|
// Classificação da sigla da pasta por convenção de PREFIXO:
|
||||||
// Retorna a LISTA ordenada (por distância, como veio do GeoGrid) + o provedor resolvido.
|
// "s-..." => Sothis (rede própria)
|
||||||
// Elegibilidade de um registro: rede própria (sigla autorizada) ou parceiro.
|
// "p-<nome>..." => Parceiro; o provedor é o nome que vem depois do "-"
|
||||||
// Siglas fora disso (sub-projetos, etc.) NÃO são consideradas viabilidade.
|
// segmento "sub" após "-" => subterrâneo; prefixa "(Subterrâneo) " no provedor
|
||||||
const ehAutorizado = (sigla) => apiConfig.geogridAuthorizedSiglasPastas.includes(sigla);
|
// Exemplos: "s-Barueri" => "Sothis"; "p-Vivo" => "Vivo"; "s-sub-Barueri" => "(Subterrâneo) Sothis".
|
||||||
const ehParceiro = (sigla) => !!sigla && (sigla.toLowerCase().includes("parceiro -") || sigla.toLowerCase().includes("parceiros - outros"));
|
// Qualquer outra coisa => não elegível (tipo "nenhum").
|
||||||
const ehElegivel = (registro) => {
|
const classificarSigla = (sigla) => {
|
||||||
const sigla = registro && registro.pasta && registro.pasta.sigla;
|
const partes = String(sigla || '').split('-').map(p => p.trim()).filter(Boolean);
|
||||||
return ehAutorizado(sigla) || ehParceiro(sigla);
|
const prefixo = (partes[0] || '').toLowerCase();
|
||||||
|
const subterraneo = partes.slice(1).some(p => p.toLowerCase() === 'sub');
|
||||||
|
const resto = partes.slice(1).filter(p => p.toLowerCase() !== 'sub');
|
||||||
|
|
||||||
|
let tipo, nome;
|
||||||
|
if (prefixo === 's') {
|
||||||
|
tipo = 'autorizado';
|
||||||
|
nome = 'Sothis';
|
||||||
|
} else if (prefixo === 'p') {
|
||||||
|
tipo = 'parceiro';
|
||||||
|
nome = resto.join(' ') || 'Parceiro';
|
||||||
|
} else {
|
||||||
|
return { tipo: 'nenhum', provedor: sigla || 'Desconhecido' };
|
||||||
|
}
|
||||||
|
return { tipo, provedor: subterraneo ? `(Subterrâneo) ${nome}` : nome };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ehAutorizado = (sigla) => classificarSigla(sigla).tipo === 'autorizado';
|
||||||
|
const ehParceiro = (sigla) => classificarSigla(sigla).tipo === 'parceiro';
|
||||||
|
const ehElegivel = (registro) => classificarSigla(registro && registro.pasta && registro.pasta.sigla).tipo !== 'nenhum';
|
||||||
|
const provedorDoRegistro = (registro) => classificarSigla(registro && registro.pasta && registro.pasta.sigla).provedor;
|
||||||
|
|
||||||
const selecionarRegistros = (registros) => {
|
const selecionarRegistros = (registros) => {
|
||||||
const autorizados = registros.filter(r => ehAutorizado(r.pasta && r.pasta.sigla));
|
const autorizados = registros.filter(r => ehAutorizado(r.pasta && r.pasta.sigla));
|
||||||
if (autorizados.length > 0) {
|
if (autorizados.length > 0) {
|
||||||
return { provedor: "Sothis", tipo: "autorizado", registros: autorizados };
|
return { provedor: provedorDoRegistro(autorizados[0]), tipo: "autorizado", registros: autorizados };
|
||||||
}
|
}
|
||||||
|
|
||||||
const parceiros = registros.filter(r => ehParceiro(r.pasta && r.pasta.sigla));
|
const parceiros = registros.filter(r => ehParceiro(r.pasta && r.pasta.sigla));
|
||||||
if (parceiros.length > 0) {
|
if (parceiros.length > 0) {
|
||||||
return { provedor: parceiros[0].pasta.sigla, tipo: "parceiro", registros: parceiros };
|
return { provedor: provedorDoRegistro(parceiros[0]), tipo: "parceiro", registros: parceiros };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { provedor: "Nenhum provedor disponível", tipo: "nenhum", registros: [] };
|
return { provedor: "Nenhum provedor disponível", tipo: "nenhum", registros: [] };
|
||||||
};
|
};
|
||||||
|
|
||||||
// Normaliza um registro do GeoGrid para o que o core precisa: coordenadas da
|
// Normaliza um registro do GeoGrid para o que o core precisa: coordenadas da
|
||||||
// caixa (destino do trajeto) + distância em linha reta + sigla da pasta.
|
// caixa (destino do trajeto) + distância em linha reta + sigla + provedor resolvido.
|
||||||
// Resolve o provedor de UM registro: rede própria (sigla autorizada) => "Sothis";
|
|
||||||
// caso contrário, a própria sigla da pasta (parceiro/sub-projeto/etc.).
|
|
||||||
const provedorDoRegistro = (registro) => {
|
|
||||||
const sigla = registro?.pasta?.sigla;
|
|
||||||
if (apiConfig.geogridAuthorizedSiglasPastas.includes(sigla)) return "Sothis";
|
|
||||||
return sigla || "Desconhecido";
|
|
||||||
};
|
|
||||||
|
|
||||||
const mapCaixa = (registro) => {
|
const mapCaixa = (registro) => {
|
||||||
const lat = Number(registro?.latitude);
|
const lat = Number(registro?.latitude);
|
||||||
const lon = Number(registro?.longitude);
|
const lon = Number(registro?.longitude);
|
||||||
@ -159,4 +169,4 @@ const consultaViabilidade = async (lat, lon) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = { consultaViabilidade, consultarCaixasProximas, consultaTrajeto, mapCaixa };
|
module.exports = { consultaViabilidade, consultarCaixasProximas, consultaTrajeto, mapCaixa, classificarSigla };
|
||||||
|
|||||||
@ -1,6 +1,3 @@
|
|||||||
// Define siglas autorizadas ANTES de carregar apiConfig (que lê o env no require).
|
|
||||||
process.env.GEOGRID_AUTHORIZED_SIGLAS_PASTAS = 'São Paulo - SP, TMC';
|
|
||||||
|
|
||||||
const test = require('node:test');
|
const test = require('node:test');
|
||||||
const assert = require('node:assert/strict');
|
const assert = require('node:assert/strict');
|
||||||
|
|
||||||
@ -156,19 +153,29 @@ test('core retorna detalhe por caixa (caixas[]) com provedor por caixa', async (
|
|||||||
assert.equal(r.caixas[1].naoDedicado, false); // 900 > 500
|
assert.equal(r.caixas[1].naoDedicado, false); // 900 > 500
|
||||||
});
|
});
|
||||||
|
|
||||||
test('seleção MISTA: só autorizadas + parceiros (ignora siglas inelegíveis)', async () => {
|
test('classificarSigla: convenção de prefixos s-/p-/sub', () => {
|
||||||
|
assert.deepEqual(geogridService.classificarSigla('s-Barueri'), { tipo: 'autorizado', provedor: 'Sothis' });
|
||||||
|
assert.deepEqual(geogridService.classificarSigla('p-Vivo'), { tipo: 'parceiro', provedor: 'Vivo' });
|
||||||
|
assert.deepEqual(geogridService.classificarSigla('s-sub-Barueri'), { tipo: 'autorizado', provedor: '(Subterrâneo) Sothis' });
|
||||||
|
assert.deepEqual(geogridService.classificarSigla('p-sub-Vivo'), { tipo: 'parceiro', provedor: '(Subterrâneo) Vivo' });
|
||||||
|
assert.equal(geogridService.classificarSigla('Sub Alameda Santos').tipo, 'nenhum');
|
||||||
|
assert.equal(geogridService.classificarSigla('São Paulo - SP').tipo, 'nenhum'); // convenção antiga não é mais elegível
|
||||||
|
assert.equal(geogridService.classificarSigla('S-SP').provedor, 'Sothis'); // case-insensitive
|
||||||
|
});
|
||||||
|
|
||||||
|
test('seleção MISTA: só s-/p- (ignora siglas fora da convenção), com subterrâneo', async () => {
|
||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
const orig = axios.get;
|
const orig = axios.get;
|
||||||
axios.get = async () => ({ data: { registros: [
|
axios.get = async () => ({ data: { registros: [
|
||||||
{ latitude: '0', longitude: '0', distancia: '50', pasta: { sigla: 'Sub Alameda Santos' } }, // NÃO elegível (mais próxima)
|
{ latitude: '0', longitude: '0', distancia: '50', pasta: { sigla: 'Sub Alameda Santos' } }, // NÃO elegível (mais próxima)
|
||||||
{ latitude: '1', longitude: '1', distancia: '100', pasta: { sigla: 'Parceiro - X' } }, // parceiro
|
{ latitude: '1', longitude: '1', distancia: '100', pasta: { sigla: 'p-Vivo' } }, // parceiro
|
||||||
{ latitude: '2', longitude: '2', distancia: '200', pasta: { sigla: 'São Paulo - SP' } } // autorizada => Sothis
|
{ latitude: '2', longitude: '2', distancia: '200', pasta: { sigla: 's-sub-Barueri' } } // Sothis subterrâneo
|
||||||
] } });
|
] } });
|
||||||
try {
|
try {
|
||||||
const r = await realConsultarCaixasProximas(0, 0, { topN: 2, misturarProvedores: true });
|
const r = await realConsultarCaixasProximas(0, 0, { topN: 2, misturarProvedores: true });
|
||||||
assert.equal(r.caixas.length, 2); // sub-alameda excluída
|
assert.equal(r.caixas.length, 2); // sub-alameda excluída
|
||||||
assert.equal(r.caixas[0].provedor, 'Parceiro - X');
|
assert.equal(r.caixas[0].provedor, 'Vivo');
|
||||||
assert.equal(r.caixas[1].provedor, 'Sothis');
|
assert.equal(r.caixas[1].provedor, '(Subterrâneo) Sothis');
|
||||||
} finally {
|
} finally {
|
||||||
axios.get = orig;
|
axios.get = orig;
|
||||||
}
|
}
|
||||||
@ -178,13 +185,13 @@ test('seleção PADRÃO (WordPress): prioriza Sothis e ignora parceiro mais pró
|
|||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
const orig = axios.get;
|
const orig = axios.get;
|
||||||
axios.get = async () => ({ data: { registros: [
|
axios.get = async () => ({ data: { registros: [
|
||||||
{ latitude: '1', longitude: '1', distancia: '100', pasta: { sigla: 'Parceiro - X' } },
|
{ latitude: '1', longitude: '1', distancia: '100', pasta: { sigla: 'p-Vivo' } },
|
||||||
{ latitude: '2', longitude: '2', distancia: '200', pasta: { sigla: 'São Paulo - SP' } }
|
{ latitude: '2', longitude: '2', distancia: '200', pasta: { sigla: 's-Barueri' } }
|
||||||
] } });
|
] } });
|
||||||
try {
|
try {
|
||||||
const r = await realConsultarCaixasProximas(0, 0, { topN: 2 });
|
const r = await realConsultarCaixasProximas(0, 0, { topN: 2 });
|
||||||
assert.equal(r.provedor, 'Sothis');
|
assert.equal(r.provedor, 'Sothis');
|
||||||
assert.equal(r.caixas.length, 1); // só a autorizada
|
assert.equal(r.caixas.length, 1); // só a autorizada (Sothis)
|
||||||
assert.equal(r.caixas[0].provedor, 'Sothis');
|
assert.equal(r.caixas[0].provedor, 'Sothis');
|
||||||
} finally {
|
} finally {
|
||||||
axios.get = orig;
|
axios.get = orig;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user