You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
196 lines
6.7 KiB
Python
196 lines
6.7 KiB
Python
from typing import Optional, List, Dict, Any
|
|
from datetime import datetime
|
|
import hashlib
|
|
import re
|
|
|
|
import httpx
|
|
from fastapi import HTTPException
|
|
|
|
from app.core.settings import settings
|
|
from app.services.fakerapi_client import FakerApiClient
|
|
|
|
|
|
def normalize_cpf(value: str) -> str:
|
|
return re.sub(r"\D", "", value or "")
|
|
|
|
|
|
def _parse_float(value: Any, default: float = 0.0) -> float:
|
|
if value is None:
|
|
return default
|
|
if isinstance(value, (int, float)):
|
|
return float(value)
|
|
text = str(value).replace("R$", "").replace(" ", "")
|
|
text = text.replace(".", "").replace(",", ".") if "," in text else text
|
|
try:
|
|
return float(text)
|
|
except Exception:
|
|
return default
|
|
|
|
|
|
def _stable_int(seed_text: str) -> int:
|
|
digest = hashlib.sha256(seed_text.encode("utf-8")).hexdigest()
|
|
return int(digest[:16], 16)
|
|
|
|
|
|
def _cpf_from_any(value: Any) -> str:
|
|
as_int = _stable_int(str(value)) % (10**11)
|
|
return str(as_int).zfill(11)
|
|
|
|
|
|
async def _fetch_faker_products(count: int) -> List[Dict[str, Any]]:
|
|
client = FakerApiClient()
|
|
try:
|
|
return await client.fetch_resource("products", quantity=count)
|
|
except httpx.HTTPStatusError as exc:
|
|
status_code = exc.response.status_code if exc.response is not None else 502
|
|
request_url = str(exc.request.url) if exc.request is not None else "desconhecida"
|
|
raise HTTPException(
|
|
status_code=502,
|
|
detail=f"FakerAPI retornou HTTP {status_code} em '{request_url}'.",
|
|
)
|
|
except httpx.RequestError as exc:
|
|
raise HTTPException(
|
|
status_code=502,
|
|
detail=(
|
|
"Falha de rede ao acessar FakerAPI (products). "
|
|
f"{exc.__class__.__name__}: {exc}. "
|
|
"Verifique egress/NAT do Cloud Run e resolucao DNS."
|
|
),
|
|
)
|
|
except Exception:
|
|
raise HTTPException(
|
|
status_code=502,
|
|
detail="Falha de integracao com FakerAPI ao consultar products.",
|
|
)
|
|
|
|
|
|
async def _fetch_faker_persons(count: int) -> List[Dict[str, Any]]:
|
|
client = FakerApiClient()
|
|
try:
|
|
return await client.fetch_resource("persons", quantity=count)
|
|
except httpx.HTTPStatusError as exc:
|
|
status_code = exc.response.status_code if exc.response is not None else 502
|
|
request_url = str(exc.request.url) if exc.request is not None else "desconhecida"
|
|
raise HTTPException(
|
|
status_code=502,
|
|
detail=f"FakerAPI retornou HTTP {status_code} em '{request_url}'.",
|
|
)
|
|
except httpx.RequestError as exc:
|
|
raise HTTPException(
|
|
status_code=502,
|
|
detail=(
|
|
"Falha de rede ao acessar FakerAPI (persons). "
|
|
f"{exc.__class__.__name__}: {exc}. "
|
|
"Verifique egress/NAT do Cloud Run e resolucao DNS."
|
|
),
|
|
)
|
|
except Exception:
|
|
raise HTTPException(
|
|
status_code=502,
|
|
detail="Falha de integracao com FakerAPI ao consultar persons.",
|
|
)
|
|
|
|
|
|
async def consultar_estoque(preco_max: float, categoria: Optional[str] = None) -> List[Dict[str, Any]]:
|
|
raw = await _fetch_faker_products(settings.fakerapi_products_quantity)
|
|
registros: List[Dict[str, Any]] = []
|
|
|
|
for item in raw:
|
|
categories = item.get("categories")
|
|
if isinstance(categories, list) and categories:
|
|
category_value = str(categories[0])
|
|
else:
|
|
category_value = str(item.get("category") or "geral")
|
|
|
|
registro = {
|
|
"id": item.get("id"),
|
|
"modelo": item.get("name") or item.get("title") or "Veiculo",
|
|
"categoria": category_value.lower(),
|
|
"preco": _parse_float(item.get("price"), 0.0),
|
|
}
|
|
registros.append(registro)
|
|
|
|
categoria_norm = categoria.lower() if categoria else None
|
|
return [
|
|
r for r in registros
|
|
if _parse_float(r.get("preco"), 0.0) <= preco_max
|
|
and (categoria_norm is None or str(r.get("categoria", "")).lower() == categoria_norm)
|
|
]
|
|
|
|
|
|
async def validar_cliente_venda(cpf: str, valor_veiculo: float) -> Dict[str, Any]:
|
|
cpf_norm = normalize_cpf(cpf)
|
|
raw = await _fetch_faker_persons(settings.fakerapi_persons_quantity)
|
|
|
|
registros: List[Dict[str, Any]] = []
|
|
for item in raw:
|
|
person_id = item.get("id") or item.get("email") or item.get("firstname")
|
|
generated_cpf = _cpf_from_any(person_id)
|
|
entropy = _stable_int(f"{generated_cpf}:{settings.fakerapi_seed}")
|
|
limite = float(30000 + (entropy % 150000))
|
|
score = int(300 + (entropy % 550))
|
|
possui_restricao = (entropy % 7 == 0)
|
|
nome = f"{item.get('firstname', '')} {item.get('lastname', '')}".strip() or "Cliente"
|
|
|
|
registros.append(
|
|
{
|
|
"cpf": generated_cpf,
|
|
"nome": nome,
|
|
"score": score,
|
|
"limite_credito": limite,
|
|
"possui_restricao": possui_restricao,
|
|
}
|
|
)
|
|
|
|
cliente = next((r for r in registros if normalize_cpf(r.get("cpf", "")) == cpf_norm), None)
|
|
if not cliente:
|
|
entropy = _stable_int(f"{cpf_norm}:{settings.fakerapi_seed}")
|
|
cliente = {
|
|
"cpf": cpf_norm,
|
|
"nome": "Cliente Faker",
|
|
"score": int(300 + (entropy % 550)),
|
|
"limite_credito": float(30000 + (entropy % 150000)),
|
|
"possui_restricao": (entropy % 7 == 0),
|
|
}
|
|
|
|
limite = _parse_float(cliente.get("limite_credito", 0), 0.0)
|
|
restricao = bool(cliente.get("possui_restricao", False))
|
|
aprovado = (not restricao) and (valor_veiculo <= limite)
|
|
return {
|
|
"aprovado": aprovado,
|
|
"cpf": cpf_norm,
|
|
"nome": cliente.get("nome"),
|
|
"score": cliente.get("score"),
|
|
"limite_credito": limite,
|
|
"possui_restricao": restricao,
|
|
"valor_veiculo": valor_veiculo,
|
|
}
|
|
|
|
|
|
async def avaliar_veiculo_troca(modelo: str, ano: int, km: int) -> Dict[str, Any]:
|
|
ano_atual = datetime.now().year
|
|
idade = max(0, ano_atual - ano)
|
|
base = 80000.0
|
|
valor = base * (0.85 ** idade) - (km * 0.03)
|
|
valor = max(5000.0, valor)
|
|
return {
|
|
"modelo": modelo,
|
|
"ano": ano,
|
|
"km": km,
|
|
"valor_estimado_troca": round(valor, 2),
|
|
}
|
|
|
|
|
|
async def agendar_revisao(placa: str, data_hora: str) -> Dict[str, Any]:
|
|
raise HTTPException(
|
|
status_code=503,
|
|
detail="FakerAPI nao suporta escrita/persistencia. Endpoint disponivel apenas para leitura de dados ficticios.",
|
|
)
|
|
|
|
|
|
async def cancelar_pedido(numero_pedido: str, motivo: str) -> Dict[str, Any]:
|
|
raise HTTPException(
|
|
status_code=503,
|
|
detail="FakerAPI nao suporta cancelamento persistente de pedidos. Endpoint indisponivel neste modo.",
|
|
)
|