♻️ refactor(api-routes): reorganizar rotas em pacote app/api/routes

main
parent 5a5d7f547e
commit f061657ad3

@ -1,161 +0,0 @@
from typing import List, Dict, Any
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from app.api.schemas import (
ChatRequest,
ChatResponse,
ConsultarEstoqueRequest,
ValidarClienteVendaRequest,
AvaliarVeiculoTrocaRequest,
AgendarRevisaoRequest,
CancelarPedidoRequest,
)
from app.db.database import SessionLocal
from app.services.orquestrador_service import OrquestradorService
from app.services.handlers import (
consultar_estoque,
validar_cliente_venda,
avaliar_veiculo_troca,
agendar_revisao,
cancelar_pedido,
)
router = APIRouter()
def get_db():
"""Fornece uma sessao de banco para a request e garante o fechamento."""
db = SessionLocal()
try:
yield db
finally:
db.close()
def _db_error_detail(exc: SQLAlchemyError) -> str:
"""Converte erros de banco em mensagens amigaveis para a API."""
text = str(exc).lower()
# Heuristica para identificar falhas no MySQL de tools.
tools_markers = ("tools", "tool")
if any(marker in text for marker in tools_markers):
return "Servico temporariamente indisponivel: banco MySQL (tools) inacessivel."
# Heuristica para identificar falhas no MySQL (base ficticia).
mysql_mock_markers = ("mock", "vehicles", "customers", "orders", "review_schedules")
if any(marker in text for marker in mysql_mock_markers):
return "Servico temporariamente indisponivel: banco MySQL (dados ficticios) inacessivel."
mysql_generic_markers = ("mysql", "pymysql", "(2003", "3306")
if any(marker in text for marker in mysql_generic_markers):
return "Servico temporariamente indisponivel: banco MySQL inacessivel."
return "Servico temporariamente indisponivel: erro de acesso ao banco de dados."
'''
# Removido momentaniamente para teste do Vertex IA
@router.post("/chat", response_model=ChatResponse)
async def chat(request: ChatRequest, db: Session = Depends(get_db)):
service = OrquestradorService(db)
result = await service.handle_message(
message=request.message,
user_id=request.user_id,
)
return ChatResponse(response=result)
'''
@router.post("/chat", response_model=ChatResponse)
async def chat(request: ChatRequest, db: Session = Depends(get_db)):
"""Processa mensagem do usuario via orquestrador e retorna resposta do chat."""
try:
service = OrquestradorService(db)
result = await service.handle_message(
message=request.message,
user_id=request.user_id,
)
return ChatResponse(response=result)
except SQLAlchemyError as exc:
raise HTTPException(
status_code=503,
detail=_db_error_detail(exc),
)
except ValueError as exc:
# Erros de configuracao de Vertex (regiao/projeto/modelo)
raise HTTPException(status_code=500, detail=f"Configuracao invalida do Vertex AI: {exc}")
except RuntimeError as exc:
raise HTTPException(status_code=503, detail=f"Falha temporaria no LLM/Vertex AI: {exc}")
@router.post("/mock/consultar-estoque")
async def consultar_estoque_endpoint(
body: ConsultarEstoqueRequest,
) -> List[Dict[str, Any]]:
"""Consulta estoque de veiculos mock com filtros opcionais."""
try:
return await consultar_estoque(
preco_max=body.preco_max,
categoria=body.categoria,
ordenar_preco=body.ordenar_preco,
limite=body.limite,
)
except SQLAlchemyError as exc:
raise HTTPException(status_code=503, detail=_db_error_detail(exc))
@router.post("/mock/validar-cliente-venda")
async def validar_cliente_venda_endpoint(
body: ValidarClienteVendaRequest,
) -> Dict[str, Any]:
"""Valida elegibilidade de cliente para uma venda."""
try:
return await validar_cliente_venda(
cpf=body.cpf,
valor_veiculo=body.valor_veiculo,
)
except SQLAlchemyError as exc:
raise HTTPException(status_code=503, detail=_db_error_detail(exc))
@router.post("/mock/avaliar-veiculo-troca")
async def avaliar_veiculo_troca_endpoint(
body: AvaliarVeiculoTrocaRequest,
) -> Dict[str, Any]:
"""Avalia valor de troca de um veiculo com base em modelo, ano e quilometragem."""
return await avaliar_veiculo_troca(
modelo=body.modelo,
ano=body.ano,
km=body.km,
)
@router.post("/mock/agendar-revisao")
async def agendar_revisao_endpoint(
body: AgendarRevisaoRequest,
) -> Dict[str, Any]:
"""Agenda revisao para uma placa em data/hora informada."""
try:
return await agendar_revisao(
placa=body.placa,
data_hora=body.data_hora,
user_id=body.user_id,
)
except SQLAlchemyError as exc:
raise HTTPException(status_code=503, detail=_db_error_detail(exc))
@router.post("/mock/cancelar-pedido")
async def cancelar_pedido_endpoint(
body: CancelarPedidoRequest,
) -> Dict[str, Any]:
"""Cancela pedido de venda existente registrando o motivo."""
try:
return await cancelar_pedido(
numero_pedido=body.numero_pedido,
motivo=body.motivo,
user_id=body.user_id,
)
except SQLAlchemyError as exc:
raise HTTPException(status_code=503, detail=_db_error_detail(exc))

@ -0,0 +1,11 @@
from fastapi import APIRouter
from app.api.routes.chat import router as chat_router
from app.api.routes.mock import router as mock_router
from app.api.routes.tools import router as tool_router
router = APIRouter()
router.include_router(chat_router)
router.include_router(mock_router)
__all__ = ["router", "tool_router"]

@ -0,0 +1,31 @@
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from app.api.routes.dependencies import db_error_detail, get_db
from app.api.schemas import ChatRequest, ChatResponse
from app.services.orquestrador_service import OrquestradorService
router = APIRouter(tags=["Chat"])
@router.post("/chat", response_model=ChatResponse)
async def chat(request: ChatRequest, db: Session = Depends(get_db)):
"""Processa mensagem do usuario via orquestrador e retorna resposta do chat."""
try:
service = OrquestradorService(db)
result = await service.handle_message(
message=request.message,
user_id=request.user_id,
)
return ChatResponse(response=result)
except SQLAlchemyError as exc:
raise HTTPException(
status_code=503,
detail=db_error_detail(exc),
)
except ValueError as exc:
# Erros de configuracao de Vertex (regiao/projeto/modelo)
raise HTTPException(status_code=500, detail=f"Configuracao invalida do Vertex AI: {exc}")
except RuntimeError as exc:
raise HTTPException(status_code=503, detail=f"Falha temporaria no LLM/Vertex AI: {exc}")

@ -0,0 +1,33 @@
from sqlalchemy.exc import SQLAlchemyError
from app.db.database import SessionLocal
def get_db():
"""Fornece uma sessao de banco para a request e garante o fechamento."""
db = SessionLocal()
try:
yield db
finally:
db.close()
def db_error_detail(exc: SQLAlchemyError) -> str:
"""Converte erros de banco em mensagens amigaveis para a API."""
text = str(exc).lower()
# Heuristica para identificar falhas no MySQL de tools.
tools_markers = ("tools", "tool")
if any(marker in text for marker in tools_markers):
return "Servico temporariamente indisponivel: banco MySQL (tools) inacessivel."
# Heuristica para identificar falhas no MySQL (base ficticia).
mysql_mock_markers = ("mock", "vehicles", "customers", "orders", "review_schedules")
if any(marker in text for marker in mysql_mock_markers):
return "Servico temporariamente indisponivel: banco MySQL (dados ficticios) inacessivel."
mysql_generic_markers = ("mysql", "pymysql", "(2003", "3306")
if any(marker in text for marker in mysql_generic_markers):
return "Servico temporariamente indisponivel: banco MySQL inacessivel."
return "Servico temporariamente indisponivel: erro de acesso ao banco de dados."

@ -0,0 +1,111 @@
from typing import Any, Dict, List
from fastapi import APIRouter, HTTPException
from sqlalchemy.exc import SQLAlchemyError
from app.api.routes.dependencies import db_error_detail
from app.api.schemas import (
AgendarRevisaoRequest,
AvaliarVeiculoTrocaRequest,
CancelarPedidoRequest,
ConsultarEstoqueRequest,
RealizarPedidoRequest,
ValidarClienteVendaRequest,
)
from app.services.handlers import (
agendar_revisao,
avaliar_veiculo_troca,
cancelar_pedido,
consultar_estoque,
realizar_pedido,
validar_cliente_venda,
)
router = APIRouter(prefix="/mock", tags=["Mock"])
@router.post("/consultar-estoque")
async def consultar_estoque_endpoint(
body: ConsultarEstoqueRequest,
) -> List[Dict[str, Any]]:
"""Consulta estoque de veiculos mock com filtros opcionais."""
try:
return await consultar_estoque(
preco_max=body.preco_max,
categoria=body.categoria,
ordenar_preco=body.ordenar_preco,
limite=body.limite,
)
except SQLAlchemyError as exc:
raise HTTPException(status_code=503, detail=db_error_detail(exc))
@router.post("/validar-cliente-venda")
async def validar_cliente_venda_endpoint(
body: ValidarClienteVendaRequest,
) -> Dict[str, Any]:
"""Valida elegibilidade de cliente para uma venda."""
try:
return await validar_cliente_venda(
cpf=body.cpf,
valor_veiculo=body.valor_veiculo,
)
except SQLAlchemyError as exc:
raise HTTPException(status_code=503, detail=db_error_detail(exc))
@router.post("/avaliar-veiculo-troca")
async def avaliar_veiculo_troca_endpoint(
body: AvaliarVeiculoTrocaRequest,
) -> Dict[str, Any]:
"""Avalia valor de troca de um veiculo com base em modelo, ano e quilometragem."""
return await avaliar_veiculo_troca(
modelo=body.modelo,
ano=body.ano,
km=body.km,
)
@router.post("/agendar-revisao")
async def agendar_revisao_endpoint(
body: AgendarRevisaoRequest,
) -> Dict[str, Any]:
"""Agenda revisao para uma placa em data/hora informada."""
try:
return await agendar_revisao(
placa=body.placa,
data_hora=body.data_hora,
user_id=body.user_id,
)
except SQLAlchemyError as exc:
raise HTTPException(status_code=503, detail=db_error_detail(exc))
@router.post("/cancelar-pedido")
async def cancelar_pedido_endpoint(
body: CancelarPedidoRequest,
) -> Dict[str, Any]:
"""Cancela pedido de venda existente registrando o motivo."""
try:
return await cancelar_pedido(
numero_pedido=body.numero_pedido,
motivo=body.motivo,
user_id=body.user_id,
)
except SQLAlchemyError as exc:
raise HTTPException(status_code=503, detail=db_error_detail(exc))
@router.post("/realizar-pedido")
async def realizar_pedido_endpoint(
body: RealizarPedidoRequest,
) -> Dict[str, Any]:
"""Cria um pedido de compra para cliente aprovado no valor informado."""
try:
return await realizar_pedido(
cpf=body.cpf,
valor_veiculo=body.valor_veiculo,
user_id=body.user_id,
)
except SQLAlchemyError as exc:
raise HTTPException(status_code=503, detail=db_error_detail(exc))

@ -1,30 +1,21 @@
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from app.db.database import SessionLocal
from app.repositories.tool_repository import ToolRepository
from app.api.routes.dependencies import get_db
from app.api.schemas import ToolCreate, ToolResponse
from app.repositories.tool_repository import ToolRepository
router = APIRouter(prefix="/tools", tags=["Tools"])
# Dependency para abrir e fechar conexão automaticamente
def get_db():
"""Fornece sessao para operacoes de tools e fecha conexao ao final da request."""
db = SessionLocal()
try:
yield db
finally:
db.close()
@router.post("/", response_model=ToolResponse) # Desenvolver uma tela de cadastro e atualização
@router.post("/", response_model=ToolResponse)
def create_tool(tool: ToolCreate, db: Session = Depends(get_db)):
"""Cria uma nova tool persistida no banco."""
repo = ToolRepository(db)
return repo.create(
name=tool.name,
description=tool.description,
parameters=tool.parameters
parameters=tool.parameters,
)
@ -42,7 +33,7 @@ def get_tool(tool_id: int, db: Session = Depends(get_db)):
tool = repo.get_by_id(tool_id)
if not tool:
raise HTTPException(status_code=404, detail="Tool não encontrada")
raise HTTPException(status_code=404, detail="Tool nao encontrada")
return tool
@ -54,6 +45,6 @@ def delete_tool(tool_id: int, db: Session = Depends(get_db)):
tool = repo.delete(tool_id)
if not tool:
raise HTTPException(status_code=404, detail="Tool não encontrada")
raise HTTPException(status_code=404, detail="Tool nao encontrada")
return {"message": "Tool removida com sucesso"}

@ -1,7 +1,6 @@
from fastapi import FastAPI
from app.api.routes import router
from app.api.tool_routes import router as tool_router
from app.api.routes import router, tool_router
from app.core.settings import settings
from app.db.database import Base, engine
from app.db.mock_database import MockBase, mock_engine

Loading…
Cancel
Save