WIP: Falta acertar a viabilidade no Geogrid, está trazendo postes e não apenas caixas.

This commit is contained in:
Gabriel Amancio 2025-11-14 08:16:04 -03:00
parent 55d9dbd363
commit 6ab6b209b6
9 changed files with 318 additions and 31 deletions

158
package-lock.json generated
View File

@ -9,7 +9,10 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"express": "^5.1.0"
"axios": "^1.6.0",
"cors": "^2.8.5",
"express": "^5.1.0",
"qs": "^6.11.0"
}
},
"node_modules/accepts": {
@ -25,6 +28,23 @@
"node": ">= 0.6"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/axios": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz",
"integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.4",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/body-parser": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
@ -83,6 +103,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/content-disposition": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
@ -122,6 +154,19 @@
"node": ">=6.6.0"
}
},
"node_modules/cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"license": "MIT",
"dependencies": {
"object-assign": "^4",
"vary": "^1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@ -139,6 +184,15 @@
}
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@ -207,6 +261,21 @@
"node": ">= 0.4"
}
},
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@ -281,6 +350,63 @@
"node": ">= 0.8"
}
},
"node_modules/follow-redirects": {
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/form-data": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/form-data/node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/form-data/node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@ -369,6 +495,21 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-tostringtag": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
@ -505,6 +646,15 @@
"node": ">= 0.6"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object-inspect": {
"version": "1.13.4",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
@ -570,6 +720,12 @@
"node": ">= 0.10"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
"node_modules/qs": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",

View File

@ -15,6 +15,10 @@
"license": "ISC",
"description": "",
"dependencies": {
"express": "^5.1.0"
"cors": "^2.8.5",
"express": "^5.1.0",
"qs": "^6.11.0",
"axios": "^1.6.0"
}
}

View File

@ -1,9 +1,11 @@
const express = require('express');
const routes = require('./routes/routes.js');
const cors = require('cors');
const app = express();
app.use(express.json());
app.use(cors());
app.use('/api', routes);
module.exports = app;

View File

@ -1,8 +1,14 @@
const dotenv = require("dotenv");
dotenv.config();
const key = process.env.GOOGLE_API_KEY;
const googleApiKey = process.env.GOOGLE_API_KEY;
const geogridApiUrl = process.env.GEOGRID_API_URL;
const geogridApiKey = process.env.GEOGRID_API_KEY;
const geogridApiCookie = process.env.GEOGRID_API_COOKIE;
module.exports = {
googleApiKey: key,
googleApiKey: googleApiKey,
geogridApiUrl: geogridApiUrl,
geogridApiKey: geogridApiKey,
geogridApiCookie: geogridApiCookie,
};

View File

@ -1,14 +1,83 @@
exports.handleViabilidadeNegada = (req, res) => {
const geogridService = require("../services/geogridService.js");
const googleService = require("../services/googleService.js");
const cepRestService = require("../services/cepRestService.js");
function handleViabilidadeNegada(req, res) {
// Lógica para lidar com a viabilidade negada
res.status(200).json({ message: 'Viabilidade negada processada com sucesso.' });
}
exports.handleViabilidadeAceita = (req, res) => {
function handleViabilidadeAceita(req, res) {
console.log('Recebido viabilidade aceita:', req.body);
// Lógica para lidar com a viabilidade aceita
res.status(200).json({ message: 'Viabilidade aceita' });
}
async function handleViabilidade(req, res) {
const rawCep = req.body.cep;
const rawNumero = req.body.numero;
try {
if (!rawCep && !rawNumero) return res.status(400).json({ error: 'cep e número são obrigatórios' });
if (!rawCep) return res.status(400).json({ error: 'cep é obrigatório' });
if (!rawNumero) return res.status(400).json({ error: 'número é obrigatório' });
const address = await cepRestService.getConsultaCep(rawCep, rawNumero);
if (!address) return res.status(404).json({ error: 'Endereço não encontrado para o CEP fornecido' });
const city = address.data.localidade;
const state = address.data.uf;
const numero = address.data.numero || rawNumero;
const addressString = `${address.data.logradouro}, ${numero}, ${address.data.bairro}, ${city}, ${state}, ${address.data.cep}`;
const coords = await googleService.geocodeWithGoogle(addressString);
if (!coords) return res.status(500).json({ error: 'Não foi possível obter as coordenadas do endereço' });
const viabilidade = await geogridService.consultaViabilidade(coords.lat, coords.lon);
console.log('Viabilidade retornada:', viabilidade);
const distancia = viabilidade.data.distancia;
if (distancia == null) return res.status(500).json({ error: 'Distância não retornada pela consulta de viabilidade' });
let naoDedicado;
let dedicado;
// lógica de decisão: use if/else (ajuste os limites conforme regra de negócio)
if (distancia <= 500) {
naoDedicado = true;
dedicado = true;
} else if (distancia <= 1000) {
naoDedicado = true;
dedicado = false;
} else {
naoDedicado = false;
dedicado = false;
}
return res.json({
bairro: address.data.bairro,
cidade: city,
estado: state,
logradouro: address.data.logradouro,
naoDedicado,
dedicado,
});
} catch (error) {
console.error("Erro ao processar a viabilidade:", error);
return res.status(500).json({ error: "Erro ao processar a viabilidade" });
}
}
module.exports = {
handleViabilidadeNegada,
handleViabilidadeAceita,
handleViabilidade,
};

View File

@ -5,5 +5,6 @@ const controller = require('../controllers/controller.js');
//rota de teste
router.post('/viabilidade_negada', controller.handleViabilidadeNegada);
router.post('/viabilidade_aceita', controller.handleViabilidadeAceita);
router.post('/viabilidade', controller.handleViabilidade);
module.exports = router;

View File

@ -1,26 +1,29 @@
const axios = require("axios");
const getConsultaCep = async (req, res) => {
const { cep: rawCep, numero: rawNumero } = req.query;
if (!rawCep) return res.status(400).json({ error: "cep é obrigatório" });
const getConsultaCep = async (rawCep, rawNumero) => {
if (!rawCep) {
throw new Error("cep é obrigatório");
}
const cep = String(rawCep).trim().replace(/\D/g, "");
if (cep.length !== 8) return res.status(400).json({ error: "cep inválido, verifique se foram digitados apenas números" });
if (cep.length !== 8) {
throw new Error("cep inválido, verifique se foram digitados apenas números");
}
const numero = rawNumero ? String(rawNumero).trim() : "";
try {
const cepRestUrl = 'https://api.cep.rest/';
const address = await axios.post(cepRestUrl, { cep });
if (address.data && address.data.code === 404) {
return res.status(404).json({ error: "CEP não encontrado" });
return null; // Controller tratará como 'Endereço não encontrado'
} else if (address.data && address.data.code) {
return res.status(500).json({ error: "Erro ao consultar o CEP" });
throw new Error("Erro ao consultar o CEP na API externa");
} else {
if (numero) address.data.numero = numero;
return res.json(address.data);
return address.data;
}
} catch (error) {
console.error("Erro ao consultar o CEP:", error);
return res.status(500).json({ error: "Erro ao consultar o CEP" });
throw new Error("Erro ao consultar o CEP");
}
};

View File

@ -1,2 +1,44 @@
const apiConfig = require("../config/apiConfig.js")
const axios = require("axios");
const qs = require("qs");
const consultaViabilidade = async (lat, lon) => {
const url = apiConfig.geogridApiUrl;
const apiKey = apiConfig.geogridApiKey;
const apiCookie = apiConfig.geogridApiCookie;
try {
// Monta os parâmetros como arrays para garantir serialização correta
const params = {
raio: 5000,
latitude: lat,
longitude: lon,
itens: ["caixa"],
ordenarCampos: ["distancia"],
ordenarPor: ["asc"]
};
const response = await axios.get(url, {
params,
// força a serialização do tipo `itens[]=caixa`
paramsSerializer: p => qs.stringify(p, { arrayFormat: 'brackets' }),
headers: {
'api-key': apiKey,
Cookie: apiCookie
}
});
const registros = response.data && response.data.registros;
const primeiro = Array.isArray(registros) && registros.length ? registros[0] : null;
// Retorna no formato esperado pelo controller (viabilidade.data.distancia)
return { data: primeiro };
} catch (error) {
console.error("Erro ao consultar viabilidade:", error && error.message ? error.message : error);
throw new Error("Erro ao consultar viabilidade");
}
};
module.exports = { consultaViabilidade };

View File

@ -7,25 +7,29 @@ async function geocodeWithGoogle(address) {
const key = apiConfig.googleApiKey;
if (!key) return null;
try {
const url = `https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(
address
)}&key=${encodeURIComponent(key)}`;
const r = await axios.get(url, { timeout: 10000 });
if (
r &&
r.data &&
Array.isArray(r.data.results) &&
r.data.results.length > 0
) {
const loc =
r.data.results[0].geometry && r.data.results[0].geometry.location;
if (loc && loc.lat !== undefined && loc.lng !== undefined) {
return { lat: Number(loc.lat), lon: Number(loc.lng) };
const url = 'https://maps.googleapis.com/maps/api/geocode/json';
const r = await axios.get(url, {
params: { address, key },
timeout: 10000,
});
// ensure HTTP 200
if (!r || r.status !== 200) {
console.warn(`geocodeWithGoogle unexpected status for '${address}': ${r && r.status}`);
return null;
}
if (r.data && Array.isArray(r.data.results) && r.data.results.length > 0) {
const loc = r.data.results[0].geometry && r.data.results[0].geometry.location;
const lat = loc && (loc.lat !== undefined ? Number(loc.lat) : NaN);
const lng = loc && (loc.lng !== undefined ? Number(loc.lng) : NaN);
if (!Number.isNaN(lat) && !Number.isNaN(lng)) {
return { lat, lon: lng };
}
}
return null;
} catch (e) {
console.warn(`geocodeWithGoogle error for '${address}': ${e.message}`);
console.warn(`geocodeWithGoogle error for '${address}': ${e && e.message}`, e && e.stack);
return null;
}
}