Ajuste main.py

This commit is contained in:
Kenio 2025-11-03 15:18:26 -03:00
parent f8b97d2907
commit 2cd63069be
108 changed files with 5387 additions and 144 deletions

View file

@ -1,6 +1,6 @@
# abstracts/repository.py
from sqlalchemy.orm import Session
from database.postgres import SessionLocal
from database.mysql import SessionLocal
class BaseRepository:

View file

@ -1,20 +1,60 @@
import json
import os
import re
from pathlib import Path
from types import SimpleNamespace
class Config:
"""Classe responsável por carregar arquivos JSON e substituir variáveis de ambiente."""
@staticmethod
def _resolve_env_vars(value):
"""
Substitui placeholders ${VAR} por valores das variáveis de ambiente.
Funciona recursivamente para dicionários, listas e strings.
"""
if isinstance(value, str):
# Procura padrões como ${VAR_NAME}
pattern = re.compile(r"\$\{([^}^{]+)\}")
matches = pattern.findall(value)
for var in matches:
env_value = os.getenv(var)
if env_value is None:
raise ValueError(f"Variável de ambiente '{var}' não definida.")
value = value.replace(f"${{{var}}}", env_value)
return value
elif isinstance(value, dict):
return {k: Config._resolve_env_vars(v) for k, v in value.items()}
elif isinstance(value, list):
return [Config._resolve_env_vars(v) for v in value]
return value
@staticmethod
def get(name: str):
# Caminho absoluto do arquivo atual
"""
Carrega um arquivo JSON de configuração e substitui variáveis de ambiente.
Args:
name (str): Nome do arquivo dentro de /config (ex: "database/mysql.json")
Returns:
SimpleNamespace: Objeto com os valores resolvidos acessíveis via ponto.
"""
base_dir = Path(__file__).resolve().parent
config_path = base_dir.parent.parent / "config" / name
# Caminho absoluto para o config.json (subindo dois níveis e entrando em config/)
config_path = base_dir.parent.parent / 'config' / name
# Lê o arquivo JSON
with open(config_path, "r", encoding="utf-8") as f:
data = json.load(f)
# Carrega o JSON como objeto acessível por ponto
with open(config_path, 'r') as f:
config = json.load(f, object_hook=lambda d: SimpleNamespace(**d))
# Substitui variáveis de ambiente
resolved_data = Config._resolve_env_vars(data)
return config
# Retorna como objeto acessível por ponto
return json.loads(
json.dumps(resolved_data), object_hook=lambda d: SimpleNamespace(**d)
)

View file

@ -3,7 +3,6 @@ from actions.config.config import Config
from typing import Optional, Any, Type
class DynamicImport:
def __init__(self) -> None:
@ -18,7 +17,7 @@ class DynamicImport:
def set_table(self, table: str):
self.table = table
def service(self, name: str, class_name : str) -> Type[Any]:
def service(self, name: str, class_name: str) -> Type[Any]:
try:
# Define o nome do Módulo
module_file = f"{name}"

View file

@ -6,7 +6,7 @@ from passlib.context import CryptContext
# Cria uma instância do contexto de criptografia
# O esquema usado é 'bcrypt', que é seguro e amplamente aceito
# O parâmetro 'deprecated="auto"' marca versões antigas como inseguras, se aplicável
CRYPTO = CryptContext(schemes=['bcrypt'], deprecated='auto')
CRYPTO = CryptContext(schemes=["bcrypt"], deprecated="auto")
class Security:
@ -19,21 +19,19 @@ class Security:
"""
return CRYPTO.identify(senha)
# Verifica se uma senha fornecida corresponde ao hash armazenado
def verify_senha_api(plain_senha_api: str, hashed_senha_api: str) -> bool:
def verify_password(plain_senha_api: str, hashed_senha_api: str) -> bool:
"""
Compara a senha fornecida em texto puro com o hash armazenado.
:param plain_senha_api: Senha digitada pelo usuário
:param hashed_senha_api: Hash da senha armazenado no banco de dados
:return: True se corresponder, False se não
"""
return CRYPTO.verify(plain_senha_api, hashed_senha_api)
# Gera o hash de uma senha fornecida
def hash_senha_api(plain_senha_api: str) -> str:
def hash_password(plain_senha_api: str) -> str:
"""
Gera e retorna o hash da senha fornecida.

View file

@ -0,0 +1,15 @@
{
"host": "${DB_HOST}",
"port": "${DB_PORT}",
"name": "${DB_NAME}",
"user": "${DB_USER}",
"password": "${DB_PASSWORD}",
"aeskey": "${AES_KEY}",
"charset": "utf8mb4",
"pool": {
"pre_ping": true,
"size": 5,
"max_overflow": 10
},
"debug": false
}

50
database/mysql.py Normal file
View file

@ -0,0 +1,50 @@
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base
from actions.config.config import Config
# === BASE ORM ===
# Essa base é herdada por todos os modelos (tabelas) do SQLAlchemy.
Base = declarative_base()
def get_database_settings():
"""
e retorna as configurações do arquivo database/mysql.json,
com substituição automática das variáveis de ambiente (${VAR}).
"""
return Config.get("database/mysql.json")
def get_mysql_engine():
"""
Cria e retorna a engine de conexão com o banco MySQL.
A engine é responsável por gerenciar o pool de conexões.
"""
db = get_database_settings()
# === Monta a string DSN (Data Source Name) ===
dsn = (
f"mysql+pymysql://{db.user}:{db.password}@"
f"{db.host}:{db.port}/{db.name}?charset={db.charset}"
)
# === Cria a engine SQLAlchemy ===
engine = create_engine(
dsn,
echo=bool(getattr(db, "debug", False)), # Exibe SQLs no log se habilitado
pool_pre_ping=bool(db.pool.pre_ping), # Testa conexões antes de usar
pool_size=int(db.pool.size), # Tamanho do pool de conexões
max_overflow=int(db.pool.max_overflow), # Conexões extras permitidas
connect_args={"connect_timeout": 10}, # Timeout de conexão
)
return engine
# === Engine global ===
# Criada uma única vez durante o ciclo de vida da aplicação.
engine = get_mysql_engine()
# === Sessão ORM ===
# Cada request da aplicação cria uma instância de SessionLocal.
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

40
main.py
View file

@ -3,30 +3,36 @@ import os
import sys
# Adiciona o diretório atual (onde está o main.py) ao sys.path
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
# Importa a classe principal do FastAPI
from fastapi import FastAPI, Request
from pathlib import Path
# Importa o middleware de CORS
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import Response
from starlette.middleware.base import BaseHTTPMiddleware
# Importa middleware de captura de erros junto ao banco de dados
from middlewares.error_handler import database_error_handler
# Importa o roteador principal da API versão 1
from packages.v1.api import api_router
from packages.v1.system.service.startup_check_service import \
StartupCheckService
from packages.v1.system.service.startup_check_service import StartupCheckService
# Importa as configurações globais da aplicação
from actions.log.log import Log
from actions.config.config import Config
from actions.system.handlers import register_exception_handlers
config = Config.get('app.json')
config = Config.get("app.json")
# Instancia o app FastAPI com um título personalizado
app = FastAPI(title='Mirror | Orius')
app = FastAPI(title="Mirror | Orius")
# Adiciona o middleware global de erro
# app.middleware("http")(database_error_handler)
# Controle de erros personalizados
register_exception_handlers(app)
@ -40,6 +46,7 @@ app.add_middleware(
allow_headers=["*"],
)
@app.on_event("startup")
async def on_startup():
@ -49,20 +56,21 @@ async def on_startup():
# Exibe o amarzenamento do servidor
print(startupCheckService.execute())
@app.middleware("http")
async def log_tempo_requisicao(request: Request, call_next):
# Ação responsavel por registrar o log de requisição
# Ação responsavel por registrar o log de requisição
log = Log()
config = Config.get('app.json')
config = Config.get("app.json")
# Obtem os dados da requisição
log_data = {
"method": request.method,
"url": str(request.url),
"headers": dict(request.headers)
"headers": dict(request.headers),
}
# Gera o nome do arquivo
file = Path(config.log.request.path) / config.log.request.name
@ -74,16 +82,18 @@ async def log_tempo_requisicao(request: Request, call_next):
return response
# Inclui as rotas da versão 1 da API com prefixo definido em settings (ex: /api/v1)
app.include_router(api_router, prefix=config.url)
# Executa o servidor com Uvicorn se este arquivo for executado diretamente
if __name__ == '__main__':
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"main:app", # Caminho do app para execução
host="0.0.0.0", # Disponibiliza a aplicação externamente
port=8000, # Porta padrão
log_level='info', # Define o nível de log para desenvolvimento
reload=True # Ativa auto-reload durante desenvolvimento
"main:app", # Caminho do app para execução
host="0.0.0.0", # Disponibiliza a aplicação externamente
port=8000, # Porta padrão
log_level="info", # Define o nível de log para desenvolvimento
reload=True, # Ativa auto-reload durante desenvolvimento
)

View file

@ -0,0 +1,58 @@
# middlewares/error_handler.py
from fastapi import Request
from fastapi.responses import JSONResponse
from sqlalchemy.exc import OperationalError, IntegrityError
import pymysql
async def database_error_handler(request: Request, call_next):
"""
Middleware para capturar erros de banco de dados e retornar respostas JSON amigáveis.
"""
try:
response = await call_next(request)
return response
except OperationalError as e:
# Erros de conexão (ex: Access denied, banco inexistente)
return JSONResponse(
status_code=500,
content={
"success": False,
"error": "Database connection failed",
"details": str(e.orig) if hasattr(e, "orig") else str(e),
},
)
except IntegrityError as e:
# Erros de integridade (ex: unique constraint, foreign key)
return JSONResponse(
status_code=400,
content={
"success": False,
"error": "Database integrity error",
"details": str(e.orig) if hasattr(e, "orig") else str(e),
},
)
except pymysql.err.OperationalError as e:
# Erro específico do PyMySQL (ex: senha errada)
return JSONResponse(
status_code=500,
content={
"success": False,
"error": "MySQL Operational Error",
"details": str(e),
},
)
except Exception as e:
# Qualquer outro erro genérico
return JSONResponse(
status_code=500,
content={
"success": False,
"error": "Internal Server Error",
"details": str(e),
},
)

View file

@ -0,0 +1,13 @@
from packages.v1.administrativo.schemas.ato_documento_schema import AtoDocumentoSchema
from packages.v1.administrativo.repositories.ato_documento.ato_documento_delete_repository import (
DeleteRepository,
)
class DeleteAction:
def execute(self, ato_documento_schema: AtoDocumentoSchema):
delete_repository = DeleteRepository()
return delete_repository.execute(ato_documento_schema)

View file

@ -0,0 +1,18 @@
from abstracts.action import BaseAction
from packages.v1.administrativo.repositories.ato_documento.ato_documento_index_repository import (
IndexRepository,
)
class IndexAction(BaseAction):
def execute(self):
# Instânciamento do repositório sql
index_repository = IndexRepository()
# Execução do sql
response = index_repository.execute()
# Retorno da informação
return response

View file

@ -0,0 +1,15 @@
from packages.v1.administrativo.schemas.ato_documento_schema import (
AtoDocumentoSaveSchema,
)
from packages.v1.administrativo.repositories.ato_documento.ato_documento_save_repository import (
SaveRepository,
)
class SaveAction:
def execute(self, ato_documento_schema: AtoDocumentoSaveSchema):
save_repository = SaveRepository()
return save_repository.execute(ato_documento_schema)

View file

@ -0,0 +1,19 @@
from abstracts.action import BaseAction
from packages.v1.administrativo.schemas.ato_documento_schema import AtoDocumentoSchema
from packages.v1.administrativo.repositories.ato_documento.ato_documento_show_repository import (
ShowRepository,
)
class ShowAction(BaseAction):
def execute(self, ato_documento_schema: AtoDocumentoSchema):
# Instânciamento do repositório sql
show_repository = ShowRepository()
# Execução do sql
response = show_repository.execute(ato_documento_schema)
# Retorno da informação
return response

View file

@ -0,0 +1,17 @@
from packages.v1.administrativo.schemas.ato_documento_schema import (
AtoDocumentoUpdateSchema,
)
from packages.v1.administrativo.repositories.ato_documento.ato_documento_update_repository import (
UpdateRepository,
)
class UpdateAction:
def execute(
self, ato_documento_id: int, ato_documento_schema: AtoDocumentoUpdateSchema
):
update_repository = UpdateRepository()
return update_repository.execute(ato_documento_id, ato_documento_schema)

View file

@ -0,0 +1,16 @@
from packages.v1.administrativo.schemas.ato_documento_schema import AtoDocumentoSchema
from packages.v1.administrativo.repositories.ato_documento.ato_principal_show_repository import (
ShowRepository,
)
class ShowAction:
# O método execute espera um schema que contenha o ID do principal
def execute(self, ato_documento_schema: AtoDocumentoSchema):
# Instânciamento do repositório
show_repository = ShowRepository()
# Execução do repositório
return show_repository.execute(ato_documento_schema)

View file

@ -0,0 +1,13 @@
from packages.v1.administrativo.schemas.ato_parte_schema import AtoParteSchema
from packages.v1.administrativo.repositories.ato_parte.ato_parte_delete_repository import (
DeleteRepository,
)
class DeleteAction:
def execute(self, ato_parte_schema: AtoParteSchema):
delete_repository = DeleteRepository()
return delete_repository.execute(ato_parte_schema)

View file

@ -0,0 +1,18 @@
from abstracts.action import BaseAction
from packages.v1.administrativo.repositories.ato_parte.ato_parte_index_repository import (
IndexRepository,
)
class IndexAction(BaseAction):
def execute(self):
# Instânciamento do repositório sql
index_repository = IndexRepository()
# Execução do sql
response = index_repository.execute()
# Retorno da informação
return response

View file

@ -0,0 +1,15 @@
from packages.v1.administrativo.schemas.ato_parte_schema import (
AtoParteSaveSchema,
)
from packages.v1.administrativo.repositories.ato_parte.ato_parte_save_repository import (
SaveRepository,
)
class SaveAction:
def execute(self, ato_parte_schema: AtoParteSaveSchema):
save_repository = SaveRepository()
return save_repository.execute(ato_parte_schema)

View file

@ -0,0 +1,19 @@
from abstracts.action import BaseAction
from packages.v1.administrativo.schemas.ato_parte_schema import AtoParteSchema
from packages.v1.administrativo.repositories.ato_parte.ato_parte_show_repository import (
ShowRepository,
)
class ShowAction(BaseAction):
def execute(self, ato_parte_schema: AtoParteSchema):
# Instânciamento do repositório sql
show_repository = ShowRepository()
# Execução do sql
response = show_repository.execute(ato_parte_schema)
# Retorno da informação
return response

View file

@ -0,0 +1,15 @@
from packages.v1.administrativo.schemas.ato_parte_schema import (
AtoParteUpdateSchema,
)
from packages.v1.administrativo.repositories.ato_parte.ato_parte_update_repository import (
UpdateRepository,
)
class UpdateAction:
def execute(self, ato_parte_id: int, ato_parte_schema: AtoParteUpdateSchema):
update_repository = UpdateRepository()
return update_repository.execute(ato_parte_id, ato_parte_schema)

View file

@ -0,0 +1,16 @@
from packages.v1.administrativo.schemas.ato_parte_schema import AtoParteSchema
from packages.v1.administrativo.repositories.ato_parte.ato_principal_show_repository import (
ShowRepository,
)
class AtoPartePrincipalShowAction:
# O método execute espera um schema que contenha o ID do principal
def execute(self, ato_parte_schema: AtoParteSchema):
# Instânciamento do repositório
show_repository = ShowRepository()
# Execução do repositório
return show_repository.execute(ato_parte_schema)

View file

@ -0,0 +1,13 @@
from packages.v1.administrativo.schemas.ato_principal_schema import AtoPrincipalIdSchema
from packages.v1.administrativo.repositories.ato_principal.ato_principal_delete_repository import (
DeleteRepository,
)
class DeleteAction:
def execute(self, ato_principal_schema: AtoPrincipalIdSchema):
delete_repository = DeleteRepository()
return delete_repository.execute(ato_principal_schema)

View file

@ -1,13 +1,15 @@
from abstracts.action import BaseAction
from packages.v1.administrativo.repositories.ato_principal.ato_principal_index_repository import AtoPrincipalIndexRepository
from packages.v1.administrativo.repositories.ato_principal.ato_principal_index_repository import (
IndexRepository,
)
class AtoPrincipalIndexAction(BaseAction):
class IndexAction(BaseAction):
def execute(self):
# Instânciamento de repositório
ato_principal_index_repository = AtoPrincipalIndexRepository()
ato_principal_index_repository = IndexRepository()
# Retorna todos produtos
return ato_principal_index_repository.execute()
return ato_principal_index_repository.execute()

View file

@ -0,0 +1,15 @@
from packages.v1.administrativo.schemas.ato_principal_schema import (
AtoPrincipalSaveSchema,
)
from packages.v1.administrativo.repositories.ato_principal.ato_principal_save_repository import (
SaveRepository,
)
class SaveAction:
def execute(self, ato_principal_schema: AtoPrincipalSaveSchema):
save_repository = SaveRepository()
return save_repository.execute(ato_principal_schema)

View file

@ -0,0 +1,19 @@
from typing import List
from packages.v1.administrativo.schemas.ato_principal_schema import (
AtoPrincipalSaveSchema,
)
from packages.v1.administrativo.repositories.ato_principal.ato_principal_save_multiple_repository import (
SaveMultipleRepository,
)
class SaveMultipleAction:
def execute(self, atos_principais: List[AtoPrincipalSaveSchema]):
save_repository = SaveMultipleRepository()
# A lista completa é passada diretamente para o Repository.
# O Repository é a única camada que deve iterar.
results = save_repository.execute(atos_principais)
return results

View file

@ -0,0 +1,19 @@
from abstracts.action import BaseAction
from packages.v1.administrativo.schemas.ato_principal_schema import AtoPrincipalSchema
from packages.v1.administrativo.repositories.ato_principal.ato_principal_show_repository import (
ShowRepository,
)
class ShowAction(BaseAction):
def execute(self, ato_principal_schema: AtoPrincipalSchema):
# Instânciamento do repositório sql
show_repository = ShowRepository()
# Execução do sql
response = show_repository.execute(ato_principal_schema)
# Retorno da informação
return response

View file

@ -0,0 +1,17 @@
from packages.v1.administrativo.schemas.ato_principal_schema import (
AtoPrincipalUpdateSchema,
)
from packages.v1.administrativo.repositories.ato_principal.ato_principal_update_repository import (
UpdateRepository,
)
class UpdateAction:
def execute(
self, ato_principal_id: int, ato_principal_schema: AtoPrincipalUpdateSchema
):
update_repository = UpdateRepository()
return update_repository.execute(ato_principal_id, ato_principal_schema)

View file

@ -0,0 +1,13 @@
from packages.v1.administrativo.schemas.usuario_schema import UsuarioIdSchema
from packages.v1.administrativo.repositories.usuario.usuario_delete_repository import (
DeleteRepository,
)
class DeleteAction:
def execute(self, usuario_schema: UsuarioIdSchema):
delete_repository = DeleteRepository()
return delete_repository.execute(usuario_schema)

View file

@ -0,0 +1,16 @@
from abstracts.action import BaseAction
from packages.v1.administrativo.schemas.usuario_schema import UsuarioAuthenticateSchema
from packages.v1.administrativo.repositories.usuario.usuario_get_by_authenticate_repository import (
GetByAuthenticateRepository,
)
class GetByAuthenticateAction(BaseAction):
def execute(self, usuario_authenticate_schema: UsuarioAuthenticateSchema):
# Instânciamento do repositório de busca pelo authenticate
get_by_authenticate_repository = GetByAuthenticateRepository()
# Execução do repositório
return get_by_authenticate_repository.execute(usuario_authenticate_schema)

View file

@ -0,0 +1,16 @@
from abstracts.action import BaseAction
from packages.v1.administrativo.schemas.usuario_schema import UsuarioEmailSchema
from packages.v1.administrativo.repositories.usuario.usuario_get_by_email_repository import (
GetByUsuarioEmailRepository,
)
class GetByUsuarioEmailAction(BaseAction):
def execute(self, usuario_schema=UsuarioEmailSchema):
# Importação do repositório
get_by_email_repository = GetByUsuarioEmailRepository()
# Execução do repositório
return get_by_email_repository.execute(usuario_schema)

View file

@ -0,0 +1,15 @@
from packages.v1.administrativo.schemas.usuario_schema import UsuarioSchema
from packages.v1.administrativo.repositories.usuario.usuario_get_by_usuario_id_repository import (
GetByUsuarioIdRepository,
)
class GetByUsuarioIdAction:
def execute(self, usuario_schema=UsuarioSchema):
# Importação do repositório
get_by_usuario_id_repository = GetByUsuarioIdRepository()
# Execução do repositório
return get_by_usuario_id_repository.execute(usuario_schema)

View file

@ -0,0 +1,18 @@
from abstracts.action import BaseAction
from packages.v1.administrativo.repositories.usuario.usuario_index_repository import (
IndexRepository,
)
class IndexAction(BaseAction):
def execute(self):
# Instânciamento do repositório sql
index_repository = IndexRepository()
# Execução do sql
response = index_repository.execute()
# Retorno da informação
return response

View file

@ -0,0 +1,13 @@
from packages.v1.administrativo.schemas.usuario_schema import UsuarioSaveSchema
from packages.v1.administrativo.repositories.usuario.usuario_save_repository import (
SaveRepository,
)
class SaveAction:
def execute(self, usuario_schema: UsuarioSaveSchema):
save_repository = SaveRepository()
return save_repository.execute(usuario_schema)

View file

@ -0,0 +1,19 @@
from abstracts.action import BaseAction
from packages.v1.administrativo.schemas.usuario_schema import UsuarioSchema
from packages.v1.administrativo.repositories.usuario.usuario_show_repository import (
ShowRepository,
)
class ShowAction(BaseAction):
def execute(self, usuario_schema: UsuarioSchema):
# Instânciamento do repositório sql
show_repository = ShowRepository()
# Execução do sql
response = show_repository.execute(usuario_schema)
# Retorno da informação
return response

View file

@ -0,0 +1,13 @@
from packages.v1.administrativo.schemas.usuario_schema import UsuarioUpdateSchema
from packages.v1.administrativo.repositories.usuario.usuario_update_repository import (
UpdateRepository,
)
class UpdateAction:
def execute(self, usuario_id: int, usuario_schema: UsuarioUpdateSchema):
save_repository = UpdateRepository()
return save_repository.execute(usuario_id, usuario_schema)

View file

@ -0,0 +1,113 @@
from actions.dynamic_import.dynamic_import import DynamicImport
from packages.v1.administrativo.services.ato_documento.ato_documento_index_service import (
IndexService,
)
from packages.v1.administrativo.services.ato_documento.ato_documento_save_service import (
SaveService,
)
from packages.v1.administrativo.services.ato_documento.ato_documento_show_service import (
ShowService,
)
from packages.v1.administrativo.services.ato_documento.ato_principal_show_service import (
ShowAtoPrincipalService,
)
from packages.v1.administrativo.services.ato_documento.ato_documento_update_service import (
UpdateService,
)
from packages.v1.administrativo.services.ato_documento.ato_documento_delete_service import (
DeleteService,
)
from packages.v1.administrativo.schemas.ato_documento_schema import (
AtoDocumentoSchema,
AtoDocumentoSaveSchema,
AtoDocumentoUpdateSchema,
AtoDocumentoIdSchema,
)
class AtoDocumentoController:
def __init__(self):
# Action responsável por carregar as services de acodo com o estado
self.dynamic_import = DynamicImport()
# Define o pacote que deve ser carregado
self.dynamic_import.set_package("administrativo")
# Define a tabela que o pacote pertence
self.dynamic_import.set_table("ato_documento")
pass
# Lista todos os documentos
def index(self):
# Instânciamento da classe service
index_service = IndexService()
# Lista todos os documentos
return {
"message": "Documentos localizados com sucesso",
"data": index_service.execute(),
}
# Busca um documento especifico pelo ID do ato principal
def showAtoPrincipal(self, ato_documento_schema: AtoDocumentoSchema):
# Instânciamento da classe desejada
show_service = ShowAtoPrincipalService()
# Busca e retorna o documento desejado
return {
"message": "Documento localizado com sucesso",
"data": show_service.execute(ato_documento_schema),
}
# Busca um documento especifico pelo ID
def show(self, ato_documento_schema: AtoDocumentoSchema):
# Instânciamento da classe desejada
show_service = ShowService()
# Busca e retorna o documento desejado
return {
"message": "Documento localizado com sucesso",
"data": show_service.execute(ato_documento_schema),
}
# Cadastra um novo documento
def save(self, ato_documento_schema: AtoDocumentoSaveSchema):
# Instânciamento da classe desejada
save_service = SaveService()
# Busca e retorna o documento desejado
return {
"message": "Documento salvo com sucesso",
"data": save_service.execute(ato_documento_schema),
}
# Atualiza os dados de um documento
def update(
self, ato_documento_id: int, ato_documento_schema: AtoDocumentoUpdateSchema
):
# Instânciamento da classe desejada
update_service = UpdateService()
# Busca e retorna o documento desejado
return {
"message": "Documento atualizado com sucesso",
"data": update_service.execute(ato_documento_id, ato_documento_schema),
}
# Exclui um documento
def delete(self, ato_documento_schema: AtoDocumentoSchema):
# Instânciamento da classe desejada
delete_service = DeleteService()
# Busca e retorna o documento desejado
return {
"message": "Documento removido com sucesso",
"data": delete_service.execute(ato_documento_schema),
}

View file

@ -0,0 +1,111 @@
from actions.dynamic_import.dynamic_import import DynamicImport
from packages.v1.administrativo.services.ato_parte.ato_parte_index_service import (
IndexService,
)
from packages.v1.administrativo.services.ato_parte.ato_parte_save_service import (
SaveService,
)
from packages.v1.administrativo.services.ato_parte.ato_parte_show_service import (
ShowService,
)
from packages.v1.administrativo.services.ato_parte.ato_principal_show_service import (
AtoPrincipalShowService,
)
from packages.v1.administrativo.services.ato_parte.ato_parte_update_service import (
UpdateService,
)
from packages.v1.administrativo.services.ato_parte.ato_parte_delete_service import (
DeleteService,
)
from packages.v1.administrativo.schemas.ato_parte_schema import (
AtoParteSchema,
AtoParteSaveSchema,
AtoParteUpdateSchema,
AtoParteIdSchema,
)
class AtoParteController:
def __init__(self):
# Action responsável por carregar as services de acodo com o estado
self.dynamic_import = DynamicImport()
# Define o pacote que deve ser carregado
self.dynamic_import.set_package("administrativo")
# Define a tabela que o pacote pertence
self.dynamic_import.set_table("ato_parte")
pass
# Lista todas as partes
def index(self):
# Instânciamento da classe service
index_service = IndexService()
# Lista todas as partes
return {
"message": "Partes localizadas com sucesso",
"data": index_service.execute(),
}
# Busca uma parte especifica pelo ID do ato principal
def showAtoPrincipal(self, ato_parte_schema: AtoParteSchema):
# Instânciamento da classe desejada
show_service = AtoPrincipalShowService()
# Busca e retorna a parte desejada
return {
"message": "Parte localizada com sucesso",
"data": show_service.execute(ato_parte_schema),
}
# Busca uma parte especifica pelo ID
def show(self, ato_parte_schema: AtoParteSchema):
# Instânciamento da classe desejada
show_service = ShowService()
# Busca e retorna a parte desejada
return {
"message": "Parte localizada com sucesso",
"data": show_service.execute(ato_parte_schema),
}
# Cadastra uma nova parte
def save(self, ato_parte_schema: AtoParteSaveSchema):
# Instânciamento da classe desejada
save_service = SaveService()
# Busca e retorna a parte desejada
return {
"message": "Parte salva com sucesso",
"data": save_service.execute(ato_parte_schema),
}
# Atualiza os dados de uma parte
def update(self, ato_parte_id: int, ato_parte_schema: AtoParteUpdateSchema):
# Instânciamento da classe desejada
update_service = UpdateService()
# Busca e retorna a parte desejada
return {
"message": "Parte atualizada com sucesso",
"data": update_service.execute(ato_parte_id, ato_parte_schema),
}
# Exclui uma parte
def delete(self, ato_parte_schema: AtoParteSchema):
# Instânciamento da classe desejada
delete_service = DeleteService()
# Busca e retorna a parte desejada
return {
"message": "Parte removida com sucesso",
"data": delete_service.execute(ato_parte_schema),
}

View file

@ -1,20 +1,124 @@
# Importação de bibliotecas
from actions.dynamic_import.dynamic_import import DynamicImport
from packages.v1.administrativo.services.ato_principal.go.ato_principal_index_service import AtoPrincipalIndexService
# Importa os serviços com o prefixo 'ato_principal'
from packages.v1.administrativo.services.ato_principal.ato_principal_index_service import (
IndexService,
)
from packages.v1.administrativo.services.ato_principal.ato_principal_save_service import (
SaveService,
)
from packages.v1.administrativo.services.ato_principal.ato_principal_save_multiple_service import (
SaveMultipleService,
)
from packages.v1.administrativo.services.ato_principal.ato_principal_show_service import (
ShowService,
)
from packages.v1.administrativo.services.ato_principal.ato_principal_update_service import (
UpdateService,
)
from packages.v1.administrativo.services.ato_principal.ato_principal_delete_service import (
DeleteService,
)
# Importa os Schemas com o prefixo 'ato_principal'
from packages.v1.administrativo.schemas.ato_principal_schema import (
AtoPrincipalSchema,
AtoPrincipalSaveSchema,
AtoPrincipalUpdateSchema,
AtoPrincipalIdSchema,
)
# Mantendo o padrão de nome de classe
class AtoPrincipalController:
"""
Controller responsável pelas operações CRUD da entidade Ato Principal.
"""
def __init__(self):
# Action responsável por carregar as services de acodo com o estado
self.dynamic_import = DynamicImport()
# Define o pacote que deve ser carregado
self.dynamic_import.set_package("administrativo")
# Define a tabela que o pacote pertence (agora 'ato_principal')
self.dynamic_import.set_table("ato_principal")
pass
# Lista todos os atos principais
def index(self):
# Importação da classe desejad
ato_principal_index_service = AtoPrincipalIndexService()
# Instânciamento da classe service
index_service = IndexService()
# Intânciamento da classe service
self.index_service = ato_principal_index_service
# Lista todos os produtos
# Lista todos os atos principais
return {
'message' : 'Registros localizados com sucesso',
'data': self.index_service.execute()
}
"message": "Atos Principais localizados com sucesso",
"data": index_service.execute(),
}
# Busca um ato principal especifica pelo ID
# Note: O método 'showAtoPrincipal' foi removido por não se aplicar
# à entidade principal.
def show(self, ato_principal_schema: AtoPrincipalSchema):
# Instânciamento da classe desejada
show_service = ShowService()
# Busca e retorna o ato principal desejado
return {
"message": "Ato Principal localizado com sucesso",
"data": show_service.execute(ato_principal_schema),
}
# Cadastra um novo ato principal
def save(self, ato_principal_schema: AtoPrincipalSaveSchema):
# Instânciamento da classe desejada
save_service = SaveService()
# Salva e retorna o ato principal
return {
"message": "Ato Principal salvo com sucesso",
"data": save_service.execute(ato_principal_schema),
}
# Cadastra múltiplos itens
def save_multiple(self, atos_principais: list[AtoPrincipalSaveSchema]):
# A lista completa é passada diretamente para o Service.
# O Service e o Action também devem ser corrigidos para parar de iterar.
save_service = SaveMultipleService()
responses = save_service.execute(atos_principais)
return {
"message": "Processamento de múltiplos atos concluído",
"results": responses, # O Service já retorna a lista de resultados
}
# Atualiza os dados de um ato principal
def update(
self, ato_principal_id: int, ato_principal_schema: AtoPrincipalUpdateSchema
):
# Instânciamento da classe desejada
update_service = UpdateService()
# Atualiza e retorna o ato principal
return {
"message": "Ato Principal atualizado com sucesso",
"data": update_service.execute(ato_principal_id, ato_principal_schema),
}
# Exclui um ato principal
def delete(self, ato_principal_schema: AtoPrincipalSchema):
# Instânciamento da classe desejada
delete_service = DeleteService()
# Exclui e retorna a parte desejada
return {
"message": "Ato Principal removido com sucesso",
"data": delete_service.execute(ato_principal_schema),
}

View file

@ -0,0 +1,145 @@
from actions.dynamic_import.dynamic_import import DynamicImport
from packages.v1.administrativo.services.usuario.usuario_authenticate_service import (
AuthenticateService,
)
from packages.v1.administrativo.services.usuario.usuario_me_service import (
MeService,
)
from packages.v1.administrativo.services.usuario.usuario_index_service import (
IndexService,
)
from packages.v1.administrativo.services.usuario.usuario_save_service import (
SaveService,
)
from packages.v1.administrativo.services.usuario.usuario_get_email_service import (
GetEmailService,
)
from packages.v1.administrativo.services.usuario.usuario_show_service import (
ShowService,
)
from packages.v1.administrativo.services.usuario.usuario_update_service import (
UpdateService,
)
from packages.v1.administrativo.services.usuario.usuario_delete_service import (
DeleteService,
)
from packages.v1.administrativo.schemas.usuario_schema import (
UsuarioSchema,
UsuarioAuthenticateSchema,
UsuarioSaveSchema,
UsuarioUpdateSchema,
UsuarioEmailSchema,
UsuarioIdSchema,
)
class UsuarioController:
def __init__(self):
# Action responsável por carregar as services de acodo com o estado
self.dynamic_import = DynamicImport()
# Define o pacote que deve ser carregado
self.dynamic_import.set_package("administrativo")
# Define a tabela que o pacote pertence
self.dynamic_import.set_table("usuario")
pass
# Efetua o acesso junto ao sistema por um determinado usuário
def authenticate(self, user_authenticate_schema: UsuarioAuthenticateSchema):
# Importação de service de Authenticate
authenticate_service = AuthenticateService()
# Retorna o usuário logado
return {
"message": "Usuário localizado com sucesso",
"data": {"token": authenticate_service.execute(user_authenticate_schema)},
}
# Carrega os dados do usuário logado
def me(self, current_user):
# Instânciamento da service
me_service = MeService()
# Retorna o usuário logado
return {
"message": "Usuário localizado com sucesso",
"data": me_service.execute(current_user),
}
# Lista todos os usuários
def index(self):
# Instânciamento da classe service
indexService = IndexService()
# Lista todos os usuários
return {
"message": "Usuários localizados com sucesso",
"data": indexService.execute(),
}
# Busca um usuário especifico pelo ID
def show(self, usuario_schema: UsuarioSchema):
# Instânciamento da classe desejada
show_service = ShowService()
# Busca e retorna o usuário desejado
return {
"message": "Usuário localizado com sucesso",
"data": show_service.execute(usuario_schema),
}
# Busca um usuário especifico pelo e-mail
def getEmail(self, usuario_schema: UsuarioEmailSchema):
# Instânciamento da classe desejada
get_email_service = GetEmailService()
# Busca e retorna o usuário desejado
return {
"message": "E-mail localizado com sucesso",
"data": get_email_service.execute(
usuario_schema, True
), # True para retornar a mensagem de erro caso não localize o usuario
}
# Cadastra um novo usuário
def save(self, usuario_schema: UsuarioSaveSchema):
# Instânciamento da classe desejada
save_service = SaveService()
# Busca e retorna o usuário desejado
return {
"message": "Usuário salvo com sucesso",
"data": save_service.execute(usuario_schema),
}
# Atualiza os dados de um usuário
def update(self, usuario_id: int, usuario_schema: UsuarioUpdateSchema):
# Instânciamento da classe desejada
update_service = UpdateService()
# Busca e retorna o usuário desejado
return {
"message": "Usuário atualizado com sucesso",
"data": update_service.execute(usuario_id, usuario_schema),
}
# Exclui um usuário
def delete(self, usuario_schema: UsuarioIdSchema):
# Instânciamento da classe desejada
delete_service = DeleteService()
# Busca e retorna o usuário desejado
return {
"message": "Usuário removido com sucesso",
"data": delete_service.execute(usuario_schema),
}

View file

@ -0,0 +1,133 @@
# Importação de bibliotecas
from typing import Optional
from fastapi import APIRouter, Depends, status
from actions.jwt.get_current_user import get_current_user
from packages.v1.administrativo.controllers.ato_documento_controller import (
AtoDocumentoController,
)
from packages.v1.administrativo.schemas.ato_documento_schema import (
AtoDocumentoSchema,
AtoDocumentoSaveSchema,
AtoDocumentoUpdateSchema,
AtoDocumentoIdSchema,
)
# Inicializa o roteador para as rotas de ato_documento
router = APIRouter()
# Instânciamento do controller desejado
ato_documento_controller = AtoDocumentoController()
# Lista todos os documentos
@router.get(
"/",
status_code=status.HTTP_200_OK,
summary="Lista todos os documentos cadastrados",
response_description="Lista todos os documentos cadastrados",
)
async def index(current_user: dict = Depends(get_current_user)):
# Busca todos os documentos cadastrados
response = ato_documento_controller.index()
# Retorna os dados localizados
return response
# Localiza um documento pelo ato_principal_id
@router.get(
"/ato_principal/{ato_principal_id}",
status_code=status.HTTP_200_OK,
summary="Busca um registro em especifico pelo ID do ato principal",
response_description="Busca um registro em especifico",
)
async def showAtoPrincipal(
ato_principal_id: int, current_user: dict = Depends(get_current_user)
):
# Cria o schema com os dados recebidos
ato_documento_schema = AtoDocumentoSchema(ato_principal_id=ato_principal_id)
# Busca um documento especifico pelo ID
response = ato_documento_controller.showAtoPrincipal(ato_documento_schema)
# Retorna os dados localizados
return response
# Localiza um documento pelo ID
@router.get(
"/{ato_documento_id}",
status_code=status.HTTP_200_OK,
summary="Busca um registro em especifico pelo ID do documento",
response_description="Busca um registro em especifico",
)
async def show(ato_documento_id: int, current_user: dict = Depends(get_current_user)):
# Cria o schema com os dados recebidos
ato_documento_schema = AtoDocumentoSchema(ato_documento_id=ato_documento_id)
# Busca um documento especifico pelo ID
response = ato_documento_controller.show(ato_documento_schema)
# Retorna os dados localizados
return response
# Cadastro de documentos
@router.post(
"/",
status_code=status.HTTP_200_OK,
summary="Cadastra um documento",
response_description="Cadastra um documento",
)
async def save(
ato_documento_schema: AtoDocumentoSaveSchema,
current_user: dict = Depends(get_current_user),
):
# Efetua o cadastro do documento junto ao banco de dados
response = ato_documento_controller.save(ato_documento_schema)
# Retorna os dados localizados
return response
# Atualiza os dados de documento
@router.put(
"/{ato_documento_id}",
status_code=status.HTTP_200_OK,
summary="Atualiza um documento",
response_description="Atualiza um documento",
)
async def update(
ato_documento_id: int,
ato_documento_schema: AtoDocumentoUpdateSchema,
current_user: dict = Depends(get_current_user),
):
# Efetua a atualização dos dados do documento
response = ato_documento_controller.update(ato_documento_id, ato_documento_schema)
# Retorna os dados localizados
return response
# Exclui um determinado documento
@router.delete(
"/{ato_documento_id}",
status_code=status.HTTP_200_OK,
summary="Remove um documento",
response_description="Remove um documento",
)
async def delete(ato_documento_id: int, current_user: dict = Depends(get_current_user)):
# Cria o schema com os dados recebidos
ato_documento_schema = AtoDocumentoSchema(ato_documento_id=ato_documento_id)
# Efetua a exclusão de um determinado documento
response = ato_documento_controller.delete(ato_documento_schema)
# Retorna os dados localizados
return response

View file

@ -0,0 +1,133 @@
# Importação de bibliotecas
from typing import Optional
from fastapi import APIRouter, Depends, status
from actions.jwt.get_current_user import get_current_user
from packages.v1.administrativo.controllers.ato_parte_controller import (
AtoParteController,
)
from packages.v1.administrativo.schemas.ato_parte_schema import (
AtoParteSchema,
AtoParteSaveSchema,
AtoParteUpdateSchema,
AtoParteIdSchema,
)
# Inicializa o roteador para as rotas de ato_parte
router = APIRouter()
# Instânciamento do controller desejado
ato_parte_controller = AtoParteController()
# Lista todas as partes
@router.get(
"/",
status_code=status.HTTP_200_OK,
summary="Lista todas as partes cadastradas",
response_description="Lista todas as partes cadastradas",
)
async def index(current_user: dict = Depends(get_current_user)):
# Busca todas as partes cadastradas
response = ato_parte_controller.index()
# Retorna os dados localizados
return response
# Localiza uma parte pelo ato_principal_id
@router.get(
"/ato_principal/{ato_principal_id}",
status_code=status.HTTP_200_OK,
summary="Busca um registro em especifico pelo ID do ato principal",
response_description="Busca um registro em especifico",
)
async def showAtoPrincipal(
ato_principal_id: int, current_user: dict = Depends(get_current_user)
):
# Cria o schema com os dados recebidos
ato_parte_schema = AtoParteSchema(ato_principal_id=ato_principal_id)
# Busca uma parte especifica pelo ID do ato principal
response = ato_parte_controller.showAtoPrincipal(ato_parte_schema)
# Retorna os dados localizados
return response
# Localiza uma parte pelo ID
@router.get(
"/{ato_parte_id}",
status_code=status.HTTP_200_OK,
summary="Busca um registro em especifico pelo ID da parte",
response_description="Busca um registro em especifico",
)
async def show(ato_parte_id: int, current_user: dict = Depends(get_current_user)):
# Cria o schema com os dados recebidos
ato_parte_schema = AtoParteSchema(ato_parte_id=ato_parte_id)
# Busca uma parte especifica pelo ID
response = ato_parte_controller.show(ato_parte_schema)
# Retorna os dados localizados
return response
# Cadastro de partes
@router.post(
"/",
status_code=status.HTTP_200_OK,
summary="Cadastra uma parte",
response_description="Cadastra uma parte",
)
async def save(
ato_parte_schema: AtoParteSaveSchema,
current_user: dict = Depends(get_current_user),
):
# Efetua o cadastro da parte junto ao banco de dados
response = ato_parte_controller.save(ato_parte_schema)
# Retorna os dados localizados
return response
# Atualiza os dados de uma parte
@router.put(
"/{ato_parte_id}",
status_code=status.HTTP_200_OK,
summary="Atualiza uma parte",
response_description="Atualiza uma parte",
)
async def update(
ato_parte_id: int,
ato_parte_schema: AtoParteUpdateSchema,
current_user: dict = Depends(get_current_user),
):
# Efetua a atualização dos dados da parte
response = ato_parte_controller.update(ato_parte_id, ato_parte_schema)
# Retorna os dados localizados
return response
# Exclui uma determinada parte
@router.delete(
"/{ato_parte_id}",
status_code=status.HTTP_200_OK,
summary="Remove uma parte",
response_description="Remove uma parte",
)
async def delete(ato_parte_id: int, current_user: dict = Depends(get_current_user)):
# Cria o schema com os dados recebidos
ato_parte_schema = AtoParteSchema(ato_parte_id=ato_parte_id)
# Efetua a exclusão de uma determinada parte
response = ato_parte_controller.delete(ato_parte_schema)
# Retorna os dados localizados
return response

View file

@ -1,22 +1,132 @@
# Importação de bibliotecas
from typing import List
from typing import Optional
from fastapi import APIRouter, Body, Depends, status
from actions.jwt.get_current_user import get_current_user
from packages.v1.administrativo.controllers.ato_principal_controller import AtoPrincipalController
from packages.v1.administrativo.controllers.ato_principal_controller import (
AtoPrincipalController,
)
from packages.v1.administrativo.schemas.ato_principal_schema import (
AtoPrincipalSchema,
AtoPrincipalSaveSchema,
AtoPrincipalUpdateSchema,
AtoPrincipalIdSchema,
)
# Inicializar o roteaodr para as rotas de produtos
# Inicializa o roteador para as rotas de ato principal
router = APIRouter()
# Instãnciamento do controller desejado
# Instânciamento do controller desejado
ato_principal_controller = AtoPrincipalController()
@router.get("/",
status_code=status.HTTP_200_OK,
summary="Busca itens com filtros opcionais",
response_description="Lista de itens encontrados com base nos critérios de busca.")
async def index():
# Busca todos os produtos cadastrados
# Lista todos os atos principais
@router.get(
"/",
status_code=status.HTTP_200_OK,
summary="Lista todos os atos principais cadastrados",
response_description="Lista todos os atos principais cadastrados",
)
async def index(current_user: dict = Depends(get_current_user)):
# Busca todos os atos principais cadastrados
response = ato_principal_controller.index()
# Retornar os dados localizados
return response
# Retorna os dados localizados
return response
# Localiza um ato principal pelo ID
@router.get(
"/{ato_principal_id}",
status_code=status.HTTP_200_OK,
summary="Busca um registro em especifico pelo ID do ato principal",
response_description="Busca um registro em especifico",
)
async def show(ato_principal_id: int, current_user: dict = Depends(get_current_user)):
# Cria o schema com os dados recebidos
ato_principal_schema = AtoPrincipalIdSchema(ato_principal_id=ato_principal_id)
# Busca um ato principal especifico pelo ID
response = ato_principal_controller.show(ato_principal_schema)
# Retorna os dados localizados
return response
# Cadastro de múltiplos itens
@router.post(
"/batch",
status_code=status.HTTP_200_OK,
summary="Cadastra múltiplos atos principais",
response_description="Cadastra vários atos principais de uma vez",
)
async def save_multiple(
atos_principais: List[AtoPrincipalSaveSchema],
current_user: dict = Depends(get_current_user),
):
# A lista completa (List[AtoPrincipalSaveSchema]) é passada
# DIRETAMENTE para o controller (que a passará ao Service, Action e Repository).
# O loop de iteração deve estar APENAS no Repository.
responses = ato_principal_controller.save_multiple(atos_principais)
return {"success": True, "data": responses}
# Cadastro de ato principal
@router.post(
"/",
status_code=status.HTTP_200_OK,
summary="Cadastra um ato principal",
response_description="Cadastra um ato principal",
)
async def save(
ato_principal_schema: AtoPrincipalSaveSchema,
current_user: dict = Depends(get_current_user),
):
# Efetua o cadastro do ato principal junto ao banco de dados
response = ato_principal_controller.save(ato_principal_schema)
# Retorna os dados localizados
return response
# Atualiza os dados de ato principal
@router.put(
"/{ato_principal_id}",
status_code=status.HTTP_200_OK,
summary="Atualiza um ato principal",
response_description="Atualiza um ato principal",
)
async def update(
ato_principal_id: int,
ato_principal_schema: AtoPrincipalUpdateSchema,
current_user: dict = Depends(get_current_user),
):
# Efetua a atualização dos dados de ato principal
response = ato_principal_controller.update(ato_principal_id, ato_principal_schema)
# Retorna os dados localizados
return response
# Exclui um determinado ato principal
@router.delete(
"/{ato_principal_id}",
status_code=status.HTTP_200_OK,
summary="Remove um ato principal",
response_description="Remove um ato principal",
)
async def delete(ato_principal_id: int, current_user: dict = Depends(get_current_user)):
# Cria o schema com os dados recebidos
ato_principal_schema = AtoPrincipalIdSchema(ato_principal_id=ato_principal_id)
# Efetua a exclusão de um determinado ato principal
response = ato_principal_controller.delete(ato_principal_schema)
# Retorna os dados localizados
return response

View file

@ -0,0 +1,164 @@
# Importação de bibliotecas
from typing import Optional
from fastapi import APIRouter, Body, Depends, status
from actions.jwt.get_current_user import get_current_user
from packages.v1.administrativo.controllers.usuario_controller import UsuarioController
from packages.v1.administrativo.schemas.usuario_schema import (
UsuarioSchema,
UsuarioAuthenticateSchema,
UsuarioSaveSchema,
UsuarioUpdateSchema,
UsuarioEmailSchema,
UsuarioIdSchema,
)
# Inicializa o roteador para as rotas de usuário
router = APIRouter()
# Instânciamento do controller desejado
user_controller = UsuarioController()
# Autenticação de usuário
@router.post(
"/authenticate",
status_code=status.HTTP_200_OK,
summary="Cria o token de acesso do usuário",
response_description="Retorna o token de acesso do usuário",
)
async def index(user_authenticate_schema: UsuarioAuthenticateSchema):
# Efetua a autenticação de um usuário junto ao sistema
response = user_controller.authenticate(user_authenticate_schema)
# Retorna os dados localizados
return response
# Dados do usuário logado
@router.get(
"/me",
status_code=status.HTTP_200_OK,
summary="Retorna os dados do usuário que efetuou o login",
response_description="Dados do usuário que efetuou o login",
)
async def me(current_user: dict = Depends(get_current_user)):
# Busca os dados do usuário logado
response = user_controller.me(current_user)
# Retorna os dados localizados
return response
# Lista todos os usuários
@router.get(
"/",
status_code=status.HTTP_200_OK,
summary="Lista todos os usuário cadastrados",
response_description="Lista todos os usuário cadastrados",
)
async def index(current_user: dict = Depends(get_current_user)):
# Busca todos os usuários cadastrados
response = user_controller.index()
# Retorna os dados localizados
return response
# Localiza um usuário pelo email
@router.get(
"/email/{email}",
status_code=status.HTTP_200_OK,
summary="Busca um registro em especifico por e-mail informado",
response_description="Busca um registro em especifico",
)
async def getEmail(email: str, current_user: dict = Depends(get_current_user)):
# Cria o schema com os dados recebidos
usuario_schema = UsuarioEmailSchema(email=email)
print(usuario_schema)
# Busca um usuário especifico pelo e-mail
response = user_controller.getEmail(usuario_schema)
# Retorna os dados localizados
return response
# Localiza um usuário pelo ID
@router.get(
"/{usuario_id}",
status_code=status.HTTP_200_OK,
summary="Busca um registro em especifico pelo ID do usuário",
response_description="Busca um registro em especifico",
)
async def show(usuario_id: int, current_user: dict = Depends(get_current_user)):
# Cria o schema com os dados recebidos
usuario_schema = UsuarioIdSchema(usuario_id=usuario_id)
# Busca um usuário especifico pelo ID
response = user_controller.show(usuario_schema)
# Retorna os dados localizados
return response
# Cadastro de usuários
@router.post(
"/",
status_code=status.HTTP_200_OK,
summary="Cadastra um usuário",
response_description="Cadastra um usuário",
)
async def save(
usuario_schema: UsuarioSaveSchema, current_user: dict = Depends(get_current_user)
):
# Efetua o cadastro do usuário junto ao banco de dados
response = user_controller.save(usuario_schema)
# Retorna os dados localizados
return response
# Atualiza os dados de usuário
@router.put(
"/{usuario_id}",
status_code=status.HTTP_200_OK,
summary="Atualiza um usuário",
response_description="Atualiza um usuário",
)
async def update(
usuario_id: int,
usuario_schema: UsuarioUpdateSchema,
current_user: dict = Depends(get_current_user),
):
# Efetua a atualização dos dados de usuário
response = user_controller.update(usuario_id, usuario_schema)
# Retorna os dados localizados
return response
# Exclui um determinado usuário
@router.delete(
"/{usuario_id}",
status_code=status.HTTP_200_OK,
summary="Remove um usuário",
response_description="Remove um usuário",
)
async def delete(usuario_id: int, current_user: dict = Depends(get_current_user)):
# Cria o schema com os dados recebidos
usuario_schema = UsuarioIdSchema(usuario_id=usuario_id)
# Efetua a exclusão de um determinado usuário
response = user_controller.delete(usuario_schema)
# Retorna os dados localizados
return response

View file

@ -0,0 +1,62 @@
# packages/v1/administrativo/models/ato_documento_model.py
# Gerado a partir da DDL da tabela 'ato_documento'
from sqlalchemy import (
Column,
BigInteger,
String,
DateTime,
Text, # Necessário para o campo 'url'
ForeignKey, # Necessário para a chave estrangeira
)
from sqlalchemy.sql import func
# Importa Base do MySQL (assumindo que o caminho 'database.mysql' é o correto)
from database.mysql import Base
class AtoDocumento(Base):
"""
Representa o modelo da tabela 'ato_documento' no banco de dados MySQL.
Mapeia a DDL fornecida.
"""
__tablename__ = "ato_documento"
# ato_documento_id bigint unsigned NOT NULL AUTO_INCREMENT, PRIMARY KEY
ato_documento_id = Column(BigInteger, primary_key=True, autoincrement=True)
# ato_principal_id bigint NOT NULL, FOREIGN KEY
# Mapeamento da chave estrangeira
ato_principal_id = Column(
BigInteger, ForeignKey("ato_principal.ato_principal_id"), nullable=False
)
# url text NOT NULL (URL pública HTTPS do documento)
url = Column(Text, nullable=False)
# nome_documento varchar(255) NOT NULL (Nome do arquivo PDF)
nome_documento = Column(String(255), nullable=False)
# tipo_documento varchar(50) NOT NULL (Tipo textual do documento)
tipo_documento = Column(String(50), nullable=False)
# created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
created_at = Column(DateTime, server_default=func.now(), nullable=False)
# updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
updated_at = Column(
DateTime,
server_default=func.now(), # Default CURRENT_TIMESTAMP
onupdate=func.now(), # ON UPDATE CURRENT_TIMESTAMP
nullable=False,
)
def __repr__(self):
"""Representação legível do objeto."""
return (
f"<AtoDocumento(ato_documento_id={self.ato_documento_id}, "
f"ato_principal_id={self.ato_principal_id}, "
f"tipo_documento='{self.tipo_documento}')>"
)

View file

@ -0,0 +1,61 @@
# packages/v1/administrativo/models/ato_parte_model.py
# Gerado a partir da DDL da tabela 'ato_parte'
from sqlalchemy import (
Column,
BigInteger,
String,
DateTime,
ForeignKey,
)
from sqlalchemy.sql import func
# Importa Base do MySQL (assumindo que o caminho 'database.mysql' é o correto)
from database.mysql import Base
class AtoParte(Base):
"""
Representa o modelo da tabela 'ato_parte' no banco de dados MySQL.
Mapeia a DDL fornecida.
"""
__tablename__ = "ato_parte"
# ato_parte_id bigint NOT NULL AUTO_INCREMENT, PRIMARY KEY
ato_parte_id = Column(BigInteger, primary_key=True, autoincrement=True)
# ato_principal_id bigint NOT NULL, FOREIGN KEY
# Mapeamento da chave estrangeira
ato_principal_id = Column(
BigInteger, ForeignKey("ato_principal.ato_principal_id"), nullable=False
)
# nome varchar(255) NOT NULL (Nome completo da parte envolvida no ato.)
nome = Column(String(255), nullable=False)
# telefone varchar(20) DEFAULT NULL (Telefone da parte com DDI e DDD.)
telefone = Column(String(20), nullable=True)
# cpf_cnpj varchar(20) NOT NULL (CPF ou CNPJ da parte, contendo apenas números.)
cpf_cnpj = Column(String(20), nullable=False)
# created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
created_at = Column(DateTime, server_default=func.now(), nullable=False)
# updated_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
updated_at = Column(
DateTime,
server_default=func.now(), # Default CURRENT_TIMESTAMP
onupdate=func.now(), # ON UPDATE CURRENT_TIMESTAMP
nullable=False,
)
def __repr__(self):
"""Representação legível do objeto."""
return (
f"<AtoParte(ato_parte_id={self.ato_parte_id}, "
f"ato_principal_id={self.ato_principal_id}, "
f"nome='{self.nome}')>"
)

View file

@ -11,26 +11,36 @@ from sqlalchemy import (
ForeignKey,
CheckConstraint,
)
from sqlalchemy.dialects.postgresql import INET
# No MySQL, usaremos String(45) para armazenar endereços IPv4/IPv6
from sqlalchemy.sql import func
from database.postgres import Base
# Atualize para importar Base do MySQL
from database.mysql import Base
class AtoPrincipal(Base):
"""
Representa o modelo da tabela 'ato_principal' no banco de dados PostgreSQL.
Representa o modelo da tabela 'ato_principal' no banco de dados MySQL.
"""
__tablename__ = "ato_principal"
ato_principal_id = Column(BigInteger, primary_key=True, autoincrement=True, index=True)
# ID principal do ato
ato_principal_id = Column(
BigInteger, primary_key=True, autoincrement=True, index=True
)
# Relacionamento com o próprio ato (auto-relacionamento)
origem_ato_principal_id = Column(
BigInteger,
ForeignKey("ato_principal.ato_principal_id", ondelete="SET NULL", onupdate="CASCADE"),
ForeignKey(
"ato_principal.ato_principal_id", ondelete="SET NULL", onupdate="CASCADE"
),
nullable=True,
)
# Campos principais
identificacao_pedido_cgj = Column(BigInteger, nullable=False)
tipo_ato = Column(Integer, nullable=False)
codigo_selo = Column(String(50), nullable=False, unique=True)
@ -38,19 +48,36 @@ class AtoPrincipal(Base):
nome_civil_ato = Column(String(255), nullable=False)
nome_serventuario_praticou_ato = Column(String(255), nullable=False)
data_solicitacao = Column(DateTime(timezone=True), nullable=False)
ip_maquina = Column(INET, nullable=True)
# Substituído INET por String(45)
# O tamanho 45 cobre IPv6, IPv4 e localhost
ip_maquina = Column(String(45), nullable=True)
# Conteúdo principal do ato
inteiro_teor = Column(Text, nullable=False)
# Valores financeiros
valor_entrada = Column(Numeric(12, 2), nullable=True, default=0)
emolumento = Column(Numeric(12, 2), nullable=False)
taxa_judiciaria = Column(Numeric(12, 2), nullable=False)
fundos_estaduais = Column(Numeric(12, 2), nullable=False)
# Protocolos opcionais
protocolo_protesto = Column(String(50), nullable=True)
protocolo_imovel = Column(String(50), nullable=True)
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False)
# Controle de auditoria
created_at = Column(
DateTime(timezone=True), server_default=func.now(), nullable=False
)
updated_at = Column(
DateTime(timezone=True),
server_default=func.now(),
onupdate=func.now(),
nullable=False,
)
# Constraints e índices adicionais
# Mantém constraint de valores positivos
__table_args__ = (
CheckConstraint(
"(COALESCE(valor_entrada, 0) >= 0) AND (emolumento >= 0) AND (taxa_judiciaria >= 0) AND (fundos_estaduais >= 0)",
@ -59,6 +86,7 @@ class AtoPrincipal(Base):
)
def __repr__(self):
"""Representação legível do objeto."""
return (
f"<AtoPrincipal(id={self.ato_principal_id}, "
f"codigo_ato='{self.codigo_ato}', selo='{self.codigo_selo}', tipo={self.tipo_ato})>"

View file

@ -0,0 +1,59 @@
# packages/v1/administrativo/models/usuario_model.py
# Gerado a partir da DDL corrigida da tabela 'usuario'
from sqlalchemy import (
Column,
BigInteger, # Alterado para BigInteger para usuario_id e IDs de auditoria
String,
DateTime,
# Não há Text, Numeric ou CheckConstraint nesta DDL
)
from sqlalchemy.sql import func
# Importa Base do MySQL (assumindo que o caminho 'database.mysql' é o correto)
from database.mysql import Base
class Usuario(Base):
"""
Representa o modelo da tabela 'usuario' no banco de dados MySQL.
Mapeia a DDL corrigida.
"""
__tablename__ = "usuario"
# usuario_id bigint NOT NULL AUTO_INCREMENT, PRIMARY KEY
usuario_id = Column(BigInteger, primary_key=True, autoincrement=True)
# Campos principais (varchar)
nome = Column(String(255), nullable=True)
email = Column(String(255), nullable=True)
username = Column(String(120), nullable=True)
password = Column(String(255), nullable=True)
# status varchar(1) NOT NULL DEFAULT 'A'
status = Column(String(1), nullable=False, default="A")
# date_register datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
date_register = Column(DateTime(), server_default=func.now(), nullable=False)
# date_update datetime DEFAULT NULL
date_update = Column(DateTime(), onupdate=func.now(), nullable=True)
# usuario_id_create bigint DEFAULT NULL
user_id_create = Column(BigInteger, nullable=True)
# user_id_update bigint DEFAULT NULL
user_id_update = Column(BigInteger, nullable=True)
# Não há __table_args__ (como CheckConstraint) na DDL original
def __repr__(self):
"""Representação legível do objeto."""
return (
f"<Usuario(usuario_id={self.usuario_id}, "
f"username='{self.username}', "
f"email='{self.email}', "
f"status='{self.status}')>"
)

View file

@ -0,0 +1,63 @@
from fastapi import HTTPException, status
from database.mysql import SessionLocal
from packages.v1.administrativo.models.ato_documento_model import AtoDocumento
from packages.v1.administrativo.schemas.ato_documento_schema import AtoDocumentoSchema
class DeleteRepository:
"""
Classe responsável por excluir documentos no banco de dados.
Segue a mesma metodologia do SaveRepository/UpdateRepository:
- abre sessão
- busca registro
- trata not found
- deleta/commita
- rollback em erro
- fecha sessão
"""
def execute(self, ato_documento_schema: AtoDocumentoSchema):
db = SessionLocal()
try:
# 1. Buscar documento
documento_db = (
db.query(AtoDocumento)
.filter(
AtoDocumento.ato_documento_id
== ato_documento_schema.ato_documento_id
)
.first()
)
if not documento_db:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Documento não encontrado.",
)
# 2. Excluir (delete físico)
db.delete(documento_db)
db.commit()
return {
"success": True,
"message": "Documento excluído com sucesso!",
"data": {
"ato_documento_id": ato_documento_schema.ato_documento_id,
},
}
except HTTPException:
db.rollback()
raise
except Exception as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Erro ao excluir documento: {e}",
)
finally:
db.close()

View file

@ -0,0 +1,41 @@
from database.mysql import SessionLocal, get_database_settings
from sqlalchemy import func
from packages.v1.administrativo.models.ato_documento_model import AtoDocumento
from packages.v1.administrativo.schemas.ato_documento_schema import (
AtoDocumentoSchema,
)
# === Recupera as configurações do banco ===
DB_SETTINGS = get_database_settings()
# Mantemos a referência a AES_KEY por consistência do ambiente, mas não a utilizamos.
AES_KEY = getattr(DB_SETTINGS, "aeskey", None)
class IndexRepository:
def execute(self):
# Cria a sessão dentro do repositório
db = SessionLocal()
try:
# Executa a query, selecionando todos os campos de ato_documento
result = db.query(
AtoDocumento.ato_documento_id,
AtoDocumento.ato_principal_id,
func.AES_DECRYPT(AtoDocumento.url, AES_KEY).label("url"),
func.AES_DECRYPT(AtoDocumento.nome_documento, AES_KEY).label(
"nome_documento"
),
func.AES_DECRYPT(AtoDocumento.tipo_documento, AES_KEY).label(
"tipo_documento"
),
AtoDocumento.created_at,
AtoDocumento.updated_at,
).all()
# Converte os models SQLAlchemy em schemas Pydantic
data = [AtoDocumentoSchema.model_validate(obj) for obj in result]
return data
finally:
# Fecha a sessão após o uso (evita vazamento de conexão)
db.close()

View file

@ -0,0 +1,67 @@
import os
from fastapi import HTTPException, status
from sqlalchemy import func
from database.mysql import SessionLocal, get_database_settings
from database.mysql import SessionLocal
from packages.v1.administrativo.models.ato_documento_model import AtoDocumento
from packages.v1.administrativo.schemas.ato_documento_schema import (
AtoDocumentoSaveSchema,
)
# pega do mesmo lugar que o engine pega
DB_SETTINGS = get_database_settings()
# A chave AES não é utilizada neste modelo, mas é mantida por consistência do ambiente
AES_KEY = getattr(DB_SETTINGS, "aeskey", None)
class SaveRepository:
"""
Classe responsável por salvar (inserir) novos documentos no banco de dados.
"""
def execute(self, ato_documento_schema: AtoDocumentoSaveSchema):
db = SessionLocal()
try:
# Não há verificações de unicidade específicas para documentos neste contexto.
# 1) monta o objeto com os dados fornecidos pelo schema
new_documento = AtoDocumento(
ato_principal_id=ato_documento_schema.ato_principal_id,
)
# Criptografa os dados sensiveis
new_documento.url = func.AES_ENCRYPT(ato_documento_schema.url, AES_KEY)
new_documento.nome_documento = func.AES_ENCRYPT(
ato_documento_schema.nome_documento, AES_KEY
)
new_documento.tipo_documento = func.AES_ENCRYPT(
ato_documento_schema.tipo_documento, AES_KEY
)
# 2) persiste
db.add(new_documento)
db.commit()
db.refresh(new_documento)
return {
"success": True,
"message": "Documento criado com sucesso!",
"data": {
"ato_documento_id": new_documento.ato_documento_id,
"ato_principal_id": new_documento.ato_principal_id,
},
}
except HTTPException:
db.rollback()
raise
except Exception as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Erro ao salvar documento: {e}",
)
finally:
db.close()

View file

@ -0,0 +1,53 @@
from typing import Optional
from database.mysql import SessionLocal, get_database_settings
from sqlalchemy import func
from packages.v1.administrativo.models.ato_documento_model import AtoDocumento
from packages.v1.administrativo.schemas.ato_documento_schema import AtoDocumentoSchema
# === Recupera as configurações do banco ===
DB_SETTINGS = get_database_settings()
# AES_KEY não é utilizada neste modelo, mas mantemos o padrão de importação
AES_KEY = getattr(DB_SETTINGS, "aeskey", None)
class ShowRepository:
"""
Repositório responsável por buscar um documento pelo ID.
Retorna `AtoDocumentoSchema` se encontrar, senão `None`.
"""
def execute(self, ato_documento_schema: AtoDocumentoSchema):
db = SessionLocal()
try:
# 1. pega o ID que veio do schema
ato_documento_id_to_find = ato_documento_schema.ato_documento_id
# 2. busca no banco, selecionando todos os campos da DDL
result = (
db.query(
AtoDocumento.ato_documento_id,
AtoDocumento.ato_principal_id,
func.AES_DECRYPT(AtoDocumento.url, AES_KEY).label("url"),
func.AES_DECRYPT(AtoDocumento.nome_documento, AES_KEY).label(
"nome_documento"
),
func.AES_DECRYPT(AtoDocumento.tipo_documento, AES_KEY).label(
"tipo_documento"
),
AtoDocumento.created_at,
AtoDocumento.updated_at,
)
.filter(AtoDocumento.ato_documento_id == ato_documento_id_to_find)
.first()
)
# 3. se não achou, devolve None (comportamento de SHOW)
if result is None:
return None
# 4. se achou, converte para pydantic
return AtoDocumentoSchema.model_validate(result)
finally:
db.close()

View file

@ -0,0 +1,91 @@
from datetime import datetime
from fastapi import HTTPException, status
from database.mysql import SessionLocal, get_database_settings
from sqlalchemy import func
from packages.v1.administrativo.models.ato_documento_model import AtoDocumento
from packages.v1.administrativo.schemas.ato_documento_schema import (
AtoDocumentoUpdateSchema,
)
# === Recupera as configurações do banco ===
DB_SETTINGS = get_database_settings()
# Mantemos a referência a AES_KEY por consistência do ambiente, mas não a utilizamos.
AES_KEY = getattr(DB_SETTINGS, "aeskey", None)
class UpdateRepository:
"""
Classe responsável por atualizar documentos no banco de dados.
"""
def execute(
self, ato_documento_id: int, ato_documento_schema: AtoDocumentoUpdateSchema
):
db = SessionLocal()
try:
# 1. Busca o documento existente
documento_db = (
db.query(AtoDocumento)
.filter(AtoDocumento.ato_documento_id == ato_documento_id)
.first()
)
if not documento_db:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Documento não encontrado.",
)
# 2. Atualiza os campos se o valor foi fornecido no schema
# ato_principal_id (bigint NOT NULL)
if ato_documento_schema.ato_principal_id is not None:
documento_db.ato_principal_id = ato_documento_schema.ato_principal_id
# url (text NOT NULL)
if ato_documento_schema.url is not None:
documento_db.url = func.AES_ENCRYPT(ato_documento_schema.url, AES_KEY)
# nome_documento (varchar(255) NOT NULL)
if ato_documento_schema.nome_documento is not None:
documento_db.nome_documento = func.AES_ENCRYPT(
ato_documento_schema.nome_documento, AES_KEY
)
# tipo_documento (varchar(50) NOT NULL)
if ato_documento_schema.tipo_documento is not None:
documento_db.tipo_documento = func.AES_ENCRYPT(
ato_documento_schema.tipo_documento, AES_KEY
)
# Nota: updated_at é atualizado automaticamente pelo ORM (onupdate=func.now())
# 3. Persiste as alterações
db.add(documento_db)
db.commit()
db.refresh(documento_db)
# 4. Retorna o resultado
return {
"success": True,
"message": "Documento atualizado com sucesso!",
"data": {
"ato_documento_id": documento_db.ato_documento_id,
"ato_principal_id": documento_db.ato_principal_id,
},
}
except HTTPException:
db.rollback()
raise
except Exception as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Erro ao atualizar documento: {e}",
)
finally:
db.close()

View file

@ -0,0 +1,60 @@
from fastapi import HTTPException, status
from database.mysql import SessionLocal, get_database_settings
from sqlalchemy import func
from packages.v1.administrativo.models.ato_documento_model import AtoDocumento
from packages.v1.administrativo.schemas.ato_documento_schema import AtoDocumentoSchema
# === Recupera as configurações do banco ===
# A chave AES não é necessária para este model, mas mantemos as configurações por consistência
DB_SETTINGS = get_database_settings()
AES_KEY = getattr(DB_SETTINGS, "aeskey", None) # compatível com SimpleNamespace
class ShowRepository:
# O esquema de entrada deve conter o ato_documento_id para a busca
def execute(self, ato_documento_schema: AtoDocumentoSchema):
# Cria a sessão dentro do repositório
db = SessionLocal()
try:
# 1. Obtém o ID do documento
ato_principal_id_to_find = ato_documento_schema.ato_principal_id
# 2. Executa a query com filtro, selecionando todos os campos do DDL
result = (
db.query(
AtoDocumento.ato_documento_id,
AtoDocumento.ato_principal_id,
func.AES_DECRYPT(AtoDocumento.url, AES_KEY).label("url"),
func.AES_DECRYPT(AtoDocumento.nome_documento, AES_KEY).label(
"nome_documento"
),
func.AES_DECRYPT(AtoDocumento.tipo_documento, AES_KEY).label(
"tipo_documento"
),
AtoDocumento.created_at,
AtoDocumento.updated_at,
)
.filter(AtoDocumento.ato_principal_id == ato_principal_id_to_find)
.all()
)
# 3. Verifica se o documento foi encontrado
if not result:
# Lança uma exceção HTTP 404 (Not Found) se não houver resultado
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Documento '{ato_principal_id_to_find}' não encontrado.",
)
# 4. Converte os models SQLAlchemy em schemas Pydantic
data = [AtoDocumentoSchema.model_validate(obj) for obj in result]
return data
finally:
# Fecha a sessão após o uso (evita vazamento de conexão)
db.close()

View file

@ -0,0 +1,60 @@
from fastapi import HTTPException, status
from database.mysql import SessionLocal
from packages.v1.administrativo.models.ato_parte_model import AtoParte
from packages.v1.administrativo.schemas.ato_parte_schema import AtoParteSchema
class DeleteRepository:
"""
Classe responsável por excluir partes (pessoas físicas ou jurídicas) no banco de dados.
Segue a mesma metodologia do SaveRepository/UpdateRepository:
- abre sessão
- busca registro
- trata not found
- deleta/commita
- rollback em erro
- fecha sessão
"""
def execute(self, ato_parte_schema: AtoParteSchema):
db = SessionLocal()
try:
# 1. Buscar a parte pelo ID
parte_db = (
db.query(AtoParte)
.filter(AtoParte.ato_parte_id == ato_parte_schema.ato_parte_id)
.first()
)
if not parte_db:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Parte não encontrada.",
)
# 2. Excluir (delete físico)
db.delete(parte_db)
db.commit()
return {
"success": True,
"message": "Parte excluída com sucesso!",
"data": {
"ato_parte_id": ato_parte_schema.ato_parte_id,
},
}
except HTTPException:
db.rollback()
raise
except Exception as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Erro ao excluir parte: {e}",
)
finally:
db.close()

View file

@ -0,0 +1,41 @@
from database.mysql import SessionLocal, get_database_settings
from sqlalchemy import func
from packages.v1.administrativo.models.ato_parte_model import AtoParte
from packages.v1.administrativo.schemas.ato_parte_schema import (
AtoParteSchema,
)
# === Recupera as configurações do banco ===
DB_SETTINGS = get_database_settings()
# Mantemos a referência a AES_KEY por consistência do ambiente, mas não a utilizamos.
AES_KEY = getattr(DB_SETTINGS, "aeskey", None)
class IndexRepository:
"""
Classe responsável por listar todas as partes cadastradas.
"""
def execute(self):
# Cria a sessão dentro do repositório
db = SessionLocal()
try:
# Executa a query, selecionando todos os campos de ato_parte
result = db.query(
AtoParte.ato_parte_id,
AtoParte.ato_principal_id,
func.AES_DECRYPT(AtoParte.nome, AES_KEY).label("nome"),
func.AES_DECRYPT(AtoParte.telefone, AES_KEY).label("telefone"),
func.AES_DECRYPT(AtoParte.cpf_cnpj, AES_KEY).label("cpf_cnpj"),
AtoParte.created_at,
AtoParte.updated_at,
).all()
# Converte os models SQLAlchemy em schemas Pydantic
data = [AtoParteSchema.model_validate(obj) for obj in result]
return data
finally:
# Fecha a sessão após o uso (evita vazamento de conexão)
db.close()

View file

@ -0,0 +1,60 @@
import os
from fastapi import HTTPException, status
from sqlalchemy import func
from database.mysql import SessionLocal, get_database_settings
from packages.v1.administrativo.models.ato_parte_model import AtoParte
from packages.v1.administrativo.schemas.ato_parte_schema import (
AtoParteSaveSchema,
)
# pega do mesmo lugar que o engine pega
DB_SETTINGS = get_database_settings()
# A chave AES não é utilizada neste modelo, mas é mantida por consistência do ambiente
AES_KEY = getattr(DB_SETTINGS, "aeskey", None)
class SaveRepository:
"""
Classe responsável por salvar (inserir) novas partes (pessoas físicas ou jurídicas) no banco de dados.
"""
def execute(self, ato_parte_schema: AtoParteSaveSchema):
db = SessionLocal()
try:
# 1) monta o objeto com os dados fornecidos pelo schema
new_parte = AtoParte(
ato_principal_id=ato_parte_schema.ato_principal_id,
)
# Para os campos sensíveis (nomes, telefones, cpf/cnpj), atribui expressão SQL para criptografia
new_parte.nome = func.AES_ENCRYPT(ato_parte_schema.nome, AES_KEY)
new_parte.telefone = func.AES_ENCRYPT(ato_parte_schema.telefone, AES_KEY)
new_parte.cpf_cnpj = func.AES_ENCRYPT(ato_parte_schema.cpf_cnpj, AES_KEY)
# 2) persiste
db.add(new_parte)
db.commit()
db.refresh(new_parte)
return {
"success": True,
"message": "Parte criada com sucesso!",
"data": {
"ato_parte_id": new_parte.ato_parte_id,
"ato_principal_id": new_parte.ato_principal_id,
},
}
except HTTPException:
db.rollback()
raise
except Exception as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Erro ao salvar parte: {e}",
)
finally:
db.close()

View file

@ -0,0 +1,49 @@
from typing import Optional
from database.mysql import SessionLocal, get_database_settings
from sqlalchemy import func
from packages.v1.administrativo.models.ato_parte_model import AtoParte
from packages.v1.administrativo.schemas.ato_parte_schema import AtoParteSchema
# === Recupera as configurações do banco ===
DB_SETTINGS = get_database_settings()
# AES_KEY não é utilizada neste modelo, mas mantemos o padrão de importação
AES_KEY = getattr(DB_SETTINGS, "aeskey", None)
class ShowRepository:
"""
Repositório responsável por buscar uma parte (pessoa física ou jurídica) pelo ID.
Retorna `AtoParteSchema` se encontrar, senão `None`.
"""
def execute(self, ato_parte_schema: AtoParteSchema):
db = SessionLocal()
try:
# 1. Pega o ID que veio do schema
ato_parte_id_to_find = ato_parte_schema.ato_parte_id
# 2. Busca no banco, selecionando todos os campos da DDL
result = (
db.query(
AtoParte.ato_parte_id,
AtoParte.ato_principal_id,
func.AES_DECRYPT(AtoParte.nome, AES_KEY).label("nome"),
func.AES_DECRYPT(AtoParte.telefone, AES_KEY).label("telefone"),
func.AES_DECRYPT(AtoParte.cpf_cnpj, AES_KEY).label("cpf_cnpj"),
AtoParte.created_at,
AtoParte.updated_at,
)
.filter(AtoParte.ato_parte_id == ato_parte_id_to_find)
.first()
)
# 3. Se não achou, devolve None (comportamento de SHOW)
if result is None:
return None
# 4. Se achou, converte para pydantic
return AtoParteSchema.model_validate(result)
finally:
db.close()

View file

@ -0,0 +1,84 @@
from datetime import datetime
from fastapi import HTTPException, status
from sqlalchemy import func
from database.mysql import SessionLocal, get_database_settings
from packages.v1.administrativo.models.ato_parte_model import AtoParte
from packages.v1.administrativo.schemas.ato_parte_schema import (
AtoParteUpdateSchema,
)
# pega do mesmo lugar que o engine pega
DB_SETTINGS = get_database_settings()
# A chave AES não é utilizada neste modelo, mas é mantida por consistência do ambiente
AES_KEY = getattr(DB_SETTINGS, "aeskey", None)
class UpdateRepository:
"""
Classe responsável por atualizar partes no banco de dados.
"""
def execute(self, ato_parte_id: int, ato_parte_schema: AtoParteUpdateSchema):
db = SessionLocal()
try:
# 1. Busca a parte existente
parte_db = (
db.query(AtoParte).filter(AtoParte.ato_parte_id == ato_parte_id).first()
)
if not parte_db:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Parte não encontrada.",
)
# 2. Atualiza os campos se o valor foi fornecido no schema
# ato_principal_id (bigint NOT NULL)
if ato_parte_schema.ato_principal_id is not None:
parte_db.ato_principal_id = ato_parte_schema.ato_principal_id
# nome (varchar(255) NOT NULL)
if ato_parte_schema.nome is not None:
parte_db.nome = func.AES_ENCRYPT(ato_parte_schema.nome, AES_KEY)
# telefone (varchar(20) DEFAULT NULL)
if ato_parte_schema.telefone is not None:
parte_db.telefone = func.AES_ENCRYPT(ato_parte_schema.telefone, AES_KEY)
# cpf_cnpj (varchar(20) NOT NULL)
if ato_parte_schema.cpf_cnpj is not None:
parte_db.cpf_cnpj = func.AES_ENCRYPT(ato_parte_schema.cpf_cnpj, AES_KEY)
# Nota: updated_at é atualizado automaticamente pelo ORM (onupdate=func.now() ou similar)
# 3. Persiste as alterações
db.add(parte_db)
db.commit()
db.refresh(parte_db)
# 4. Retorna o resultado
return {
"success": True,
"message": "Parte atualizada com sucesso!",
"data": {
"ato_parte_id": parte_db.ato_parte_id,
"ato_principal_id": parte_db.ato_principal_id,
},
}
except HTTPException:
db.rollback()
raise
except Exception as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Erro ao atualizar parte: {e}",
)
finally:
db.close()

View file

@ -0,0 +1,60 @@
from fastapi import HTTPException, status
from database.mysql import SessionLocal, get_database_settings
from sqlalchemy import func
from packages.v1.administrativo.models.ato_parte_model import AtoParte
from packages.v1.administrativo.schemas.ato_parte_schema import AtoParteSchema
# === Recupera as configurações do banco ===
# A chave AES não é necessária para este model, mas mantemos as configurações por consistência
DB_SETTINGS = get_database_settings()
AES_KEY = getattr(DB_SETTINGS, "aeskey", None) # compatível com SimpleNamespace
class ShowRepository:
"""
Classe responsável por buscar partes de um ato principal no banco de dados.
"""
# O esquema de entrada deve conter o ato_principal_id para a busca
def execute(self, ato_parte_schema: AtoParteSchema):
# Cria a sessão dentro do repositório
db = SessionLocal()
try:
# 1. Obtém o ID do ato principal
ato_principal_id_to_find = ato_parte_schema.ato_principal_id
# 2. Executa a query com filtro, selecionando todos os campos do DDL
result = (
db.query(
AtoParte.ato_parte_id,
AtoParte.ato_principal_id,
func.AES_DECRYPT(AtoParte.nome, AES_KEY).label("nome"),
func.AES_DECRYPT(AtoParte.telefone, AES_KEY).label("telefone"),
func.AES_DECRYPT(AtoParte.cpf_cnpj, AES_KEY).label("cpf_cnpj"),
AtoParte.created_at,
AtoParte.updated_at,
)
.filter(AtoParte.ato_principal_id == ato_principal_id_to_find)
.all()
)
# 3. Verifica se o resultado foi encontrado
if not result:
# Lança uma exceção HTTP 404 (Not Found) se não houver resultado
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Nenhuma parte encontrada para o ato principal ID '{ato_principal_id_to_find}'.",
)
# 4. Converte os models SQLAlchemy em schemas Pydantic
# Nota: É importante que o schema 'AtoParteSchema' esteja pronto para receber todos os campos.
data = [AtoParteSchema.model_validate(obj) for obj in result]
return data
finally:
# Fecha a sessão após o uso (evita vazamento de conexão)
db.close()

View file

@ -0,0 +1,63 @@
from fastapi import HTTPException, status
from database.mysql import SessionLocal
from packages.v1.administrativo.models.ato_principal_model import AtoPrincipal
from packages.v1.administrativo.schemas.ato_principal_schema import AtoPrincipalIdSchema
class DeleteRepository:
"""
Classe responsável por excluir atos principais no banco de dados.
Segue a mesma metodologia do SaveRepository/UpdateRepository:
- abre sessão
- busca registro
- trata not found
- deleta/commita
- rollback em erro
- fecha sessão
"""
def execute(self, ato_principal_schema: AtoPrincipalIdSchema):
db = SessionLocal()
try:
# 1. Buscar ato principal
ato_principal_db = (
db.query(AtoPrincipal)
.filter(
AtoPrincipal.ato_principal_id
== ato_principal_schema.ato_principal_id
)
.first()
)
if not ato_principal_db:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Ato principal não encontrado.",
)
# 2. Excluir (delete físico)
db.delete(ato_principal_db)
db.commit()
return {
"success": True,
"message": "Ato principal excluído com sucesso!",
"data": {
"ato_principal_id": ato_principal_schema.ato_principal_id,
},
}
except HTTPException:
db.rollback()
raise
except Exception as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Erro ao excluir ato principal: {e}",
)
finally:
db.close()

View file

@ -1,21 +1,52 @@
from database.postgres import SessionLocal
from database.mysql import SessionLocal, get_database_settings
from sqlalchemy import func
from packages.v1.administrativo.models.ato_principal_model import AtoPrincipal
from packages.v1.administrativo.schemas.ato_principal_schema import AtoPrincipalResponseSchema
from packages.v1.administrativo.schemas.ato_principal_schema import (
AtoPrincipalSchema,
)
# === Recupera as configurações do banco ===
DB_SETTINGS = get_database_settings()
AES_KEY = getattr(DB_SETTINGS, "aeskey", None) # compatível com SimpleNamespace
class AtoPrincipalIndexRepository:
class IndexRepository:
def execute(self):
# Cria a sessão dentro do repositório
db = SessionLocal()
try:
# Executa a query
result = db.query(AtoPrincipal).all()
result = db.query(
AtoPrincipal.ato_principal_id,
AtoPrincipal.origem_ato_principal_id,
AtoPrincipal.identificacao_pedido_cgj,
AtoPrincipal.tipo_ato,
AtoPrincipal.codigo_selo,
AtoPrincipal.codigo_ato,
func.AES_DECRYPT(AtoPrincipal.nome_civil_ato, AES_KEY).label(
"nome_civil_ato"
),
func.AES_DECRYPT(
AtoPrincipal.nome_serventuario_praticou_ato, AES_KEY
).label("nome_serventuario_praticou_ato"),
AtoPrincipal.data_solicitacao,
AtoPrincipal.ip_maquina,
AtoPrincipal.inteiro_teor,
AtoPrincipal.valor_entrada,
AtoPrincipal.emolumento,
AtoPrincipal.taxa_judiciaria,
AtoPrincipal.fundos_estaduais,
AtoPrincipal.protocolo_protesto,
AtoPrincipal.protocolo_imovel,
AtoPrincipal.created_at,
AtoPrincipal.updated_at,
).all()
# Converte os models SQLAlchemy em schemas Pydantic
data = [AtoPrincipalResponseSchema.model_validate(obj) for obj in result]
data = [AtoPrincipalSchema.model_validate(obj) for obj in result]
return data
finally:
# Fecha a sessão após o uso (evita vazamento de conexão)
db.close()
db.close()

View file

@ -0,0 +1,200 @@
from typing import List, Optional
from fastapi import HTTPException, status
import traceback
from sqlalchemy import func
from sqlalchemy.orm import Session # Importação para tipagem da session
from database.mysql import SessionLocal, get_database_settings
from packages.v1.administrativo.models.ato_principal_model import AtoPrincipal
from packages.v1.administrativo.models.ato_parte_model import AtoParte
from packages.v1.administrativo.models.ato_documento_model import AtoDocumento
from packages.v1.administrativo.schemas.ato_principal_schema import (
AtoPrincipalSaveSchema,
)
# Configuração da Chave AES
DB_SETTINGS = get_database_settings()
AES_KEY = getattr(DB_SETTINGS, "aeskey", None)
class SaveMultipleRepository:
"""
Repositório para salvar múltiplos atos principais com suas partes e documentos,
usando criptografia nativa do MySQL via AES_ENCRYPT.
Implementa lógica recursiva para atos vinculados.
"""
def _save_single_recursive_transaction(
self,
db: Session,
ato_schema: AtoPrincipalSaveSchema,
parent_ato_principal_id: Optional[int] = None,
) -> AtoPrincipal:
"""
Salva um único Ato Principal, seus filhos (partes/documentos) e
chama recursivamente o salvamento dos atos vinculados.
"""
codigo_selo = ato_schema.codigo_selo
# 1. Pré-processamento e Criptografia
ato_data = ato_schema.model_dump(
exclude_unset=True,
exclude={"ato_partes", "ato_documentos", "atos_vinculados"},
)
# Define o ID de origem se for um ato vinculado
if parent_ato_principal_id is not None:
ato_data["origem_ato_principal_id"] = parent_ato_principal_id
elif ato_data.get("origem_ato_principal_id") is None:
ato_data["origem_ato_principal_id"] = None
# Verifica duplicidade usando descriptografia
existing_ato = (
db.query(AtoPrincipal)
.filter(func.aes_decrypt(AtoPrincipal.codigo_selo, AES_KEY) == codigo_selo)
.first()
)
if existing_ato:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail=f"O Código do Selo '{codigo_selo}' já está cadastrado.",
)
campos_criptografar = [
"nome_civil_ato",
"nome_serventuario_praticou_ato",
]
# Criptografa os campos de texto necessários
for campo in campos_criptografar:
valor = ato_data.get(campo)
if isinstance(valor, str) and valor.strip():
ato_data[campo] = func.aes_encrypt(valor, AES_KEY)
elif campo not in ato_data or valor is None:
ato_data[campo] = None
# 2. Criação e Persistência do Ato Principal
new_ato = AtoPrincipal(**ato_data)
db.add(new_ato)
db.flush()
new_ato_id = new_ato.ato_principal_id
# 3. Salva os Filhos Diretos: Ato Partes
for parte in ato_schema.ato_partes:
parte_data = parte.model_dump(exclude_unset=True)
parte_campos_criptografar = ["nome", "telefone", "cpf_cnpj"]
for campo in parte_campos_criptografar:
valor = parte_data.get(campo)
# A validação/conversão para string já foi feita pelo Pydantic,
# mas mantemos a checagem de tipo e strip para segurança antes da criptografia
if isinstance(valor, str) and valor.strip():
parte_data[campo] = func.aes_encrypt(valor, AES_KEY)
else:
parte_data[campo] = None
new_parte = AtoParte(**parte_data, ato_principal_id=new_ato_id)
db.add(new_parte)
# 4. Salva os Filhos Diretos: Ato Documentos
for doc in ato_schema.ato_documentos:
doc_data = doc.model_dump(exclude_unset=True)
doc_campos_criptografar = ["url", "nome_documento", "tipo_documento"]
for campo in doc_campos_criptografar:
valor = doc_data.get(campo)
if isinstance(valor, str) and valor.strip():
doc_data[campo] = func.aes_encrypt(valor, AES_KEY)
else:
doc_data[campo] = None
new_documento = AtoDocumento(**doc_data, ato_principal_id=new_ato_id)
db.add(new_documento)
# 5. Lógica Recursiva para Atos Vinculados (Estrutura de loop idêntica)
if ato_schema.atos_vinculados:
print(f"--- Iniciando salvamento recursivo para {new_ato_id} ---")
# A iteração é semelhante, mas o que é executado é a chamada recursiva
for linked_ato_schema in ato_schema.atos_vinculados:
# O ato vinculado é persistido chamando a rotina de salvamento completa
self._save_single_recursive_transaction(
db,
linked_ato_schema,
parent_ato_principal_id=new_ato_id,
)
print(f"--- Fim do salvamento recursivo para {new_ato_id} ---")
return new_ato
# Método principal (chamado pela Action)
def execute(self, atos_principais: List[AtoPrincipalSaveSchema]):
db = SessionLocal()
results = []
# 1. Checa a chave AES
if not AES_KEY:
db.close()
raise Exception("A chave AES (aeskey) não está configurada.")
# 2. Loop principal: Cada iteração é uma transação completa (incluindo recursão)
for ato_schema in atos_principais:
codigo_selo_log = getattr(ato_schema, "codigo_selo", "SELO_INDISPONÍVEL")
try:
# A rotina completa de salvamento (incluindo filhos e recursão) é encapsulada
saved_ato = self._save_single_recursive_transaction(
db,
ato_schema,
parent_ato_principal_id=None, # Ato de nível superior
)
# ---------- COMMIT final para a transação completa (Ato Principal + Filhos + Atos Vinculados) ----------
db.commit()
# Retorno de sucesso
ato_result = {
"success": True,
"message": "Ato Principal e vinculados salvos com sucesso",
"data": {
"ato_principal_id": saved_ato.ato_principal_id,
"codigo_selo": codigo_selo_log,
"tipo_ato": saved_ato.tipo_ato,
},
}
results.append(ato_result)
# Tratamento de erro específico para duplicidade ou HTTP
except HTTPException as he:
db.rollback()
results.append(
{
"success": False,
"error": he.detail,
"data": {"codigo_selo": codigo_selo_log},
}
)
# Tratamento de erro genérico
except Exception as e:
db.rollback()
# Log completo do erro
print("====")
print(f"ERRO DE PERSISTÊNCIA NO SELO: {codigo_selo_log}")
traceback.print_exc()
print("----")
results.append(
{
"success": False,
"error": "Erro ao salvar o registro. Verifique o formato dos dados ou os logs do servidor.",
"data": {"codigo_selo": codigo_selo_log},
}
)
db.close()
return results

View file

@ -0,0 +1,105 @@
import os
from fastapi import HTTPException, status
from sqlalchemy import func
from database.mysql import SessionLocal, get_database_settings
from database.mysql import SessionLocal
from packages.v1.administrativo.models.ato_principal_model import AtoPrincipal
from packages.v1.administrativo.schemas.ato_principal_schema import (
AtoPrincipalSaveSchema,
)
# pega do mesmo lugar que o engine pega
DB_SETTINGS = get_database_settings()
# pega aeskey de forma compatível com SimpleNamespace
AES_KEY = getattr(DB_SETTINGS, "aeskey", None)
class SaveRepository:
"""
Classe responsável por salvar (inserir) novos atos principais no banco de dados.
Campos de nome criptografados via AES_ENCRYPT (nativo MySQL) usando ORM.
"""
def execute(self, ato_principal_schema: AtoPrincipalSaveSchema):
db = SessionLocal()
try:
# 1) verifica codigo_selo (texto puro)
existing_by_selo = (
db.query(AtoPrincipal)
.filter(AtoPrincipal.codigo_selo == ato_principal_schema.codigo_selo)
.first()
)
if existing_by_selo:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Já existe um ato principal com esse código de selo.",
)
# 2) verifica codigo_ato (texto puro)
existing_by_codigo = (
db.query(AtoPrincipal)
.filter(AtoPrincipal.codigo_ato == ato_principal_schema.codigo_ato)
.first()
)
if existing_by_codigo:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Já existe um ato principal com esse código de ato.",
)
# 3) monta o objeto com todos os campos da DDL
new_ato = AtoPrincipal(
origem_ato_principal_id=ato_principal_schema.origem_ato_principal_id,
identificacao_pedido_cgj=ato_principal_schema.identificacao_pedido_cgj,
tipo_ato=ato_principal_schema.tipo_ato,
codigo_selo=ato_principal_schema.codigo_selo,
codigo_ato=ato_principal_schema.codigo_ato,
data_solicitacao=ato_principal_schema.data_solicitacao,
ip_maquina=ato_principal_schema.ip_maquina,
inteiro_teor=ato_principal_schema.inteiro_teor,
valor_entrada=ato_principal_schema.valor_entrada,
emolumento=ato_principal_schema.emolumento,
taxa_judiciaria=ato_principal_schema.taxa_judiciaria,
fundos_estaduais=ato_principal_schema.fundos_estaduais,
protocolo_protesto=ato_principal_schema.protocolo_protesto,
protocolo_imovel=ato_principal_schema.protocolo_imovel,
)
# 4) para os campos sensíveis (nomes), atribui expressão SQL para criptografia
new_ato.nome_civil_ato = func.AES_ENCRYPT(
ato_principal_schema.nome_civil_ato, AES_KEY
)
new_ato.nome_serventuario_praticou_ato = func.AES_ENCRYPT(
ato_principal_schema.nome_serventuario_praticou_ato, AES_KEY
)
# 5) persiste
db.add(new_ato)
db.commit()
db.refresh(new_ato)
return {
"success": True,
"message": "Ato principal criado com sucesso!",
"data": {
"ato_principal_id": new_ato.ato_principal_id,
"codigo_selo": ato_principal_schema.codigo_selo,
"codigo_ato": ato_principal_schema.codigo_ato,
"nome_civil_ato": ato_principal_schema.nome_civil_ato,
"data_solicitacao": ato_principal_schema.data_solicitacao.isoformat(),
},
}
except HTTPException:
db.rollback()
raise
except Exception as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Erro ao salvar ato principal: {e}",
)
finally:
db.close()

View file

@ -0,0 +1,78 @@
from typing import Optional
from database.mysql import SessionLocal, get_database_settings
from sqlalchemy import func
from packages.v1.administrativo.models.ato_principal_model import AtoPrincipal
from packages.v1.administrativo.schemas.ato_principal_schema import AtoPrincipalSchema
from packages.v1.administrativo.schemas.ato_principal_schema import (
AtoPrincipalIdSchema,
) # Assumindo um schema para receber o ID
# === Recupera as configurações do banco ===
# A chave AES (AES_KEY) não será utilizada neste repositório,
# pois nenhum campo da tabela 'ato_principal' parece requerer
# criptografia/decriptografia no banco de dados, ao contrário
# do 'usuario' original.
# === Recupera as configurações do banco ===
DB_SETTINGS = get_database_settings()
AES_KEY = getattr(DB_SETTINGS, "aeskey", None) # compatível com SimpleNamespace
class ShowRepository:
"""
Repositório responsável por buscar um ato principal pelo ato_principal_id.
Retorna `AtoPrincipalSchema` se encontrar, senão `None`.
"""
# O esquema de entrada deve ser aquele que contém o ID necessário.
def execute(
self, ato_principal_id_schema: AtoPrincipalIdSchema
) -> Optional[AtoPrincipalSchema]:
db = SessionLocal()
try:
# 1. pega o ID que veio do schema de entrada
# Ajuste: Assumindo que o schema de entrada (AtoPrincipalIdSchema) tem o campo 'ato_principal_id'
ato_principal_id_to_find = ato_principal_id_schema.ato_principal_id
# 2. busca no banco, selecionando explicitamente todos os campos da DDL
result = (
db.query(
AtoPrincipal.ato_principal_id,
AtoPrincipal.origem_ato_principal_id,
AtoPrincipal.identificacao_pedido_cgj,
AtoPrincipal.tipo_ato,
AtoPrincipal.codigo_selo,
AtoPrincipal.codigo_ato,
func.AES_DECRYPT(AtoPrincipal.nome_civil_ato, AES_KEY).label(
"nome_civil_ato"
),
func.AES_DECRYPT(
AtoPrincipal.nome_serventuario_praticou_ato, AES_KEY
).label("nome_serventuario_praticou_ato"),
AtoPrincipal.data_solicitacao,
AtoPrincipal.ip_maquina,
AtoPrincipal.inteiro_teor,
AtoPrincipal.valor_entrada,
AtoPrincipal.emolumento,
AtoPrincipal.taxa_judiciaria,
AtoPrincipal.fundos_estaduais,
AtoPrincipal.protocolo_protesto,
AtoPrincipal.protocolo_imovel,
AtoPrincipal.created_at,
AtoPrincipal.updated_at,
)
.filter(AtoPrincipal.ato_principal_id == ato_principal_id_to_find)
.first()
)
# 3. se não achou, devolve None
if result is None:
return None
# 4. se achou, converte para pydantic
# (Assumindo que AtoPrincipalSchema tem Config.from_attributes = True)
return AtoPrincipalSchema.model_validate(result)
finally:
db.close()

View file

@ -0,0 +1,169 @@
from datetime import datetime
from typing import Dict, Any
from fastapi import HTTPException, status
from sqlalchemy import func
from database.mysql import SessionLocal, get_database_settings
from packages.v1.administrativo.models.ato_principal_model import AtoPrincipal
from packages.v1.administrativo.schemas.ato_principal_schema import (
AtoPrincipalUpdateSchema,
)
# pega as configurações do banco
DB_SETTINGS = get_database_settings()
# pega a chave AES do banco (mesma usada no save)
AES_KEY = getattr(DB_SETTINGS, "aeskey", None)
class UpdateRepository:
"""
Classe responsável por atualizar atos principais no banco de dados.
Campos de nome são criptografados com AES_ENCRYPT (nativo MySQL).
"""
def execute(
self, ato_principal_id: int, ato_principal_schema: AtoPrincipalUpdateSchema
) -> Dict[str, Any]:
db = SessionLocal()
try:
# 1. Busca o ato principal existente
ato_principal_db = (
db.query(AtoPrincipal)
.filter(AtoPrincipal.ato_principal_id == ato_principal_id)
.first()
)
if not ato_principal_db:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Ato principal não encontrado.",
)
# 2. Verifica duplicidade de codigo_selo e codigo_ato
if ato_principal_schema.codigo_selo or ato_principal_schema.codigo_ato:
duplicidade_selo = None
if ato_principal_schema.codigo_selo:
duplicidade_selo = (
db.query(AtoPrincipal)
.filter(AtoPrincipal.ato_principal_id != ato_principal_id)
.filter(
AtoPrincipal.codigo_selo == ato_principal_schema.codigo_selo
)
.first()
)
duplicidade_ato = None
if ato_principal_schema.codigo_ato and not duplicidade_selo:
duplicidade_ato = (
db.query(AtoPrincipal)
.filter(AtoPrincipal.ato_principal_id != ato_principal_id)
.filter(
AtoPrincipal.codigo_ato == ato_principal_schema.codigo_ato
)
.first()
)
if duplicidade_selo or duplicidade_ato:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Já existe outro ato principal com este código de selo ou código de ato.",
)
# 3. Atualiza campos comuns (não criptografados)
if ato_principal_schema.origem_ato_principal_id is not None:
ato_principal_db.origem_ato_principal_id = (
ato_principal_schema.origem_ato_principal_id
)
if ato_principal_schema.identificacao_pedido_cgj is not None:
ato_principal_db.identificacao_pedido_cgj = (
ato_principal_schema.identificacao_pedido_cgj
)
if ato_principal_schema.tipo_ato is not None:
ato_principal_db.tipo_ato = ato_principal_schema.tipo_ato
if ato_principal_schema.codigo_selo is not None:
ato_principal_db.codigo_selo = ato_principal_schema.codigo_selo
if ato_principal_schema.codigo_ato is not None:
ato_principal_db.codigo_ato = ato_principal_schema.codigo_ato
if ato_principal_schema.data_solicitacao is not None:
ato_principal_db.data_solicitacao = (
ato_principal_schema.data_solicitacao
)
if ato_principal_schema.ip_maquina is not None:
ato_principal_db.ip_maquina = ato_principal_schema.ip_maquina
if ato_principal_schema.inteiro_teor is not None:
ato_principal_db.inteiro_teor = ato_principal_schema.inteiro_teor
if ato_principal_schema.valor_entrada is not None:
ato_principal_db.valor_entrada = ato_principal_schema.valor_entrada
if ato_principal_schema.emolumento is not None:
ato_principal_db.emolumento = ato_principal_schema.emolumento
if ato_principal_schema.taxa_judiciaria is not None:
ato_principal_db.taxa_judiciaria = ato_principal_schema.taxa_judiciaria
if ato_principal_schema.fundos_estaduais is not None:
ato_principal_db.fundos_estaduais = (
ato_principal_schema.fundos_estaduais
)
if ato_principal_schema.protocolo_protesto is not None:
ato_principal_db.protocolo_protesto = (
ato_principal_schema.protocolo_protesto
)
if ato_principal_schema.protocolo_imovel is not None:
ato_principal_db.protocolo_imovel = (
ato_principal_schema.protocolo_imovel
)
# 4. Atualiza campos criptografados se enviados
if ato_principal_schema.nome_civil_ato is not None:
ato_principal_db.nome_civil_ato = func.AES_ENCRYPT(
ato_principal_schema.nome_civil_ato, AES_KEY
)
if ato_principal_schema.nome_serventuario_praticou_ato is not None:
ato_principal_db.nome_serventuario_praticou_ato = func.AES_ENCRYPT(
ato_principal_schema.nome_serventuario_praticou_ato, AES_KEY
)
# 5. Atualiza o timestamp de modificação
ato_principal_db.updated_at = datetime.now()
# 6. Persiste as alterações
db.add(ato_principal_db)
db.commit()
db.refresh(ato_principal_db)
return {
"success": True,
"message": "Ato principal atualizado com sucesso!",
"data": {
"ato_principal_id": ato_principal_db.ato_principal_id,
"codigo_selo": ato_principal_db.codigo_selo,
"codigo_ato": ato_principal_db.codigo_ato,
"identificacao_pedido_cgj": ato_principal_db.identificacao_pedido_cgj,
},
}
except HTTPException:
db.rollback()
raise
except Exception as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Erro ao atualizar ato principal: {e}",
)
finally:
db.close()

View file

@ -0,0 +1,60 @@
from fastapi import HTTPException, status
from database.mysql import SessionLocal
from packages.v1.administrativo.models.usuario_model import Usuario
from packages.v1.administrativo.schemas.usuario_schema import UsuarioIdSchema
class DeleteRepository:
"""
Classe responsável por excluir usuários no banco de dados.
Segue a mesma metodologia do SaveRepository/UpdateRepository:
- abre sessão
- busca registro
- trata not found
- deleta/commita
- rollback em erro
- fecha sessão
"""
def execute(self, usuario_schema: UsuarioIdSchema):
db = SessionLocal()
try:
# 1. Buscar usuário
usuario_db = (
db.query(Usuario)
.filter(Usuario.usuario_id == usuario_schema.usuario_id)
.first()
)
if not usuario_db:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Usuário não encontrado.",
)
# 2. Excluir (delete físico)
db.delete(usuario_db)
db.commit()
return {
"success": True,
"message": "Usuário excluído com sucesso!",
"data": {
"usuario_id": usuario_schema.usuario_id,
},
}
except HTTPException:
db.rollback()
raise
except Exception as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Erro ao excluir usuário: {e}",
)
finally:
db.close()

View file

@ -0,0 +1,63 @@
from fastapi import HTTPException, status
from database.mysql import SessionLocal, get_database_settings
from sqlalchemy import func
from packages.v1.administrativo.models.usuario_model import Usuario
from packages.v1.administrativo.schemas.usuario_schema import (
UsuarioAuthenticateSchema,
)
# === Recupera as configurações do banco ===
DB_SETTINGS = get_database_settings()
AES_KEY = getattr(DB_SETTINGS, "aeskey", None) # compatível com SimpleNamespace
class GetByAuthenticateRepository:
def execute(self, user_authenticate_schema: UsuarioAuthenticateSchema):
"""
Busca um usuário pelo email fornecido no schema e retorna seus dados.
Retorna HTTP 404 se não encontrado.
"""
# Cria a sessão dentro do repositório
db = SessionLocal()
try:
# 1. Obtém o email do schema de autenticação
username_to_find = user_authenticate_schema.username
# 2. Executa a query com filtro
# Substituímos .all() por .filter() e .first()
# Usamos Usuario.username para acessar a coluna do Model
result = (
db.query(
Usuario.usuario_id,
func.AES_DECRYPT(Usuario.nome, AES_KEY).label("nome"),
func.AES_DECRYPT(Usuario.email, AES_KEY).label("email"),
Usuario.username,
Usuario.password,
Usuario.status,
Usuario.date_update,
Usuario.user_id_create,
Usuario.user_id_update,
)
.filter(Usuario.username == username_to_find)
.first()
)
# 3. Verifica se o usuário foi encontrado
if not result:
# Lança uma exceção HTTP 404 (Not Found) se não houver resultado
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Usuário '{username_to_find}' não encontrado.",
)
# 4. Converte o model SQLAlchemy (um único objeto) para o schema Pydantic
# Note que não é mais uma lista, e sim um único objeto
data = UsuarioAuthenticateSchema.model_validate(result)
return data
finally:
# Fecha a sessão após o uso (evita vazamento de conexão)
db.close()

View file

@ -0,0 +1,62 @@
from typing import Optional
from sqlalchemy import func
from database.mysql import SessionLocal, get_database_settings
from packages.v1.administrativo.models.usuario_model import Usuario
from packages.v1.administrativo.schemas.usuario_schema import (
UsuarioEmailSchema,
UsuarioSchema,
)
# === Recupera as configurações do banco ===
DB_SETTINGS = get_database_settings()
AES_KEY = getattr(DB_SETTINGS, "aeskey", None) # compatível com SimpleNamespace
class GetByUsuarioEmailRepository:
"""
Repositório responsável por buscar um usuário pelo e-mail.
Retorna `UsuarioSchema` se encontrar, senão `None`.
"""
def execute(self, usuario_schema: UsuarioEmailSchema) -> Optional[UsuarioSchema]:
db = SessionLocal()
try:
# 1. Pega o e-mail que veio do schema (texto puro)
usuario_email_to_find = usuario_schema.email
# 2. Faz a busca com descriptografia AES_DECRYPT diretamente no SQL
result = (
db.query(
Usuario.usuario_id,
func.AES_DECRYPT(Usuario.nome, AES_KEY).label("nome"),
func.AES_DECRYPT(Usuario.email, AES_KEY).label("email"),
Usuario.username,
Usuario.password,
Usuario.status,
Usuario.date_update,
Usuario.user_id_create,
Usuario.user_id_update,
)
.filter(
func.AES_DECRYPT(Usuario.email, AES_KEY) == usuario_email_to_find
)
.first()
)
# 3. Se não achou, devolve None
if not result:
return None
# 4. Converte os campos criptografados de bytes → string
def to_str(value):
return (
value.decode("utf-8")
if isinstance(value, (bytes, bytearray))
else value
)
# 5. Valida e retorna via schema Pydantic
return UsuarioSchema.model_validate(result)
finally:
db.close()

View file

@ -0,0 +1,59 @@
from fastapi import HTTPException, status
from database.mysql import SessionLocal, get_database_settings
from sqlalchemy import func
from packages.v1.administrativo.models.usuario_model import Usuario
from packages.v1.administrativo.schemas.usuario_schema import UsuarioSchema
# === Recupera as configurações do banco ===
DB_SETTINGS = get_database_settings()
AES_KEY = getattr(DB_SETTINGS, "aeskey", None) # compatível com SimpleNamespace
class GetByUsuarioIdRepository:
def execute(self, usuario_schema=UsuarioSchema):
# Cria a sessão dentro do repositório
db = SessionLocal()
try:
# 1. Obtém o email do schema de autenticação
usuario_id_to_find = usuario_schema.usuario_id
# 2. Executa a query com filtro
# Substituímos .all() por .filter() e .first()
# Usamos Usuario.username para acessar a coluna do Model
result = (
db.query(
Usuario.usuario_id,
func.AES_DECRYPT(Usuario.nome, AES_KEY).label("nome"),
func.AES_DECRYPT(Usuario.email, AES_KEY).label("email"),
Usuario.username,
Usuario.password,
Usuario.status,
Usuario.date_update,
Usuario.user_id_create,
Usuario.user_id_update,
)
.filter(Usuario.usuario_id == usuario_id_to_find)
.first()
)
# 3. Verifica se o usuário foi encontrado
if not result:
# Lança uma exceção HTTP 404 (Not Found) se não houver resultado
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Usuário '{usuario_id_to_find}' não encontrado.",
)
# 4. Converte o model SQLAlchemy (um único objeto) para o schema Pydantic
# Note que não é mais uma lista, e sim um único objeto
data = UsuarioSchema.model_validate(result)
return data
finally:
# Fecha a sessão após o uso (evita vazamento de conexão)
db.close()

View file

@ -0,0 +1,38 @@
from database.mysql import SessionLocal, get_database_settings
from sqlalchemy import func
from packages.v1.administrativo.models.usuario_model import Usuario
from packages.v1.administrativo.schemas.usuario_schema import (
UsuarioSchema,
)
# === Recupera as configurações do banco ===
DB_SETTINGS = get_database_settings()
AES_KEY = getattr(DB_SETTINGS, "aeskey", None) # compatível com SimpleNamespace
class IndexRepository:
def execute(self):
# Cria a sessão dentro do repositório
db = SessionLocal()
try:
# Executa a query
result = db.query(
Usuario.usuario_id,
func.AES_DECRYPT(Usuario.nome, AES_KEY).label("nome"),
func.AES_DECRYPT(Usuario.email, AES_KEY).label("email"),
Usuario.username,
Usuario.password,
Usuario.status,
Usuario.date_update,
Usuario.user_id_create,
Usuario.user_id_update,
).all()
# Converte os models SQLAlchemy em schemas Pydantic
data = [UsuarioSchema.model_validate(obj) for obj in result]
return data
finally:
# Fecha a sessão após o uso (evita vazamento de conexão)
db.close()

View file

@ -0,0 +1,91 @@
import os
from fastapi import HTTPException, status
from sqlalchemy import func
from database.mysql import SessionLocal, get_database_settings
from database.mysql import SessionLocal
from packages.v1.administrativo.models.usuario_model import Usuario
from packages.v1.administrativo.schemas.usuario_schema import UsuarioSaveSchema
# pega do mesmo lugar que o engine pega
DB_SETTINGS = get_database_settings()
# pega aeskey de forma compatível com SimpleNamespace
AES_KEY = getattr(DB_SETTINGS, "aeskey", None)
class SaveRepository:
"""
Classe responsável por salvar (inserir) novos usuários no banco de dados.
Nome e e-mail criptografados via AES_ENCRYPT (nativo MySQL) usando ORM.
"""
def execute(self, usuario_schema: UsuarioSaveSchema):
db = SessionLocal()
try:
# 1) verifica username (texto puro)
existing_by_username = (
db.query(Usuario)
.filter(Usuario.username == usuario_schema.username)
.first()
)
if existing_by_username:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Já existe um usuário com esse nome de usuário.",
)
# 2) verifica e-mail (está criptografado no banco)
existing_by_email = (
db.query(Usuario)
.filter(
func.AES_DECRYPT(Usuario.email, AES_KEY) == usuario_schema.email
)
.first()
)
if existing_by_email:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Já existe um usuário com esse e-mail.",
)
# 3) monta o objeto normalmente
new_user = Usuario(
username=usuario_schema.username,
password=usuario_schema.password, # ideal: já vir hash
status=usuario_schema.status or "A",
user_id_create=usuario_schema.user_id_create,
)
# 4) para os campos sensíveis, atribui expressão SQL
new_user.nome = func.AES_ENCRYPT(usuario_schema.nome, AES_KEY)
new_user.email = func.AES_ENCRYPT(usuario_schema.email, AES_KEY)
# 5) persiste
db.add(new_user)
db.commit()
db.refresh(new_user)
return {
"success": True,
"message": "Usuário criado com sucesso!",
"data": {
"usuario_id": new_user.usuario_id,
"nome": usuario_schema.nome,
"email": usuario_schema.email,
"username": usuario_schema.username,
"status": new_user.status,
},
}
except HTTPException:
db.rollback()
raise
except Exception as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Erro ao salvar usuário: {e}",
)
finally:
db.close()

View file

@ -0,0 +1,51 @@
from typing import Optional
from database.mysql import SessionLocal, get_database_settings
from sqlalchemy import func
from packages.v1.administrativo.models.usuario_model import Usuario
from packages.v1.administrativo.schemas.usuario_schema import UsuarioSchema
# === Recupera as configurações do banco ===
DB_SETTINGS = get_database_settings()
AES_KEY = getattr(DB_SETTINGS, "aeskey", None) # compatível com SimpleNamespace
class ShowRepository:
"""
Repositório responsável por buscar um usuário pelo e-mail.
Retorna `UsuarioSchema` se encontrar, senão `None`.
"""
def execute(self, usuario_schema: UsuarioSchema):
db = SessionLocal()
try:
# 1. pega o e-mail que veio do schema
usuario_id_to_find = usuario_schema.usuario_id
# 2. busca no banco
result = (
db.query(
Usuario.usuario_id,
func.AES_DECRYPT(Usuario.nome, AES_KEY).label("nome"),
func.AES_DECRYPT(Usuario.email, AES_KEY).label("email"),
Usuario.username,
Usuario.password,
Usuario.status,
Usuario.date_update,
Usuario.user_id_create,
Usuario.user_id_update,
)
.filter(Usuario.usuario_id == usuario_id_to_find)
.first()
)
# 3. se não achou, devolve None
if result is None:
return None
# 4. se achou, converte para pydantic
# (assumindo que UsuarioSchema tem Config.from_attributes = True)
return UsuarioSchema.model_validate(result)
finally:
db.close()

View file

@ -0,0 +1,113 @@
from datetime import datetime
from fastapi import HTTPException, status
from sqlalchemy import func
from database.mysql import SessionLocal, get_database_settings
from packages.v1.administrativo.models.usuario_model import Usuario
from packages.v1.administrativo.schemas.usuario_schema import UsuarioUpdateSchema
# pega as configurações do banco
DB_SETTINGS = get_database_settings()
AES_KEY = getattr(DB_SETTINGS, "aeskey", None)
class UpdateRepository:
"""
Classe responsável por atualizar usuários no banco de dados.
Segue a mesma metodologia do SaveRepository (sessão, try/except, rollback, retorno padronizado).
"""
def execute(self, usuario_id: int, usuario_schema: UsuarioUpdateSchema):
db = SessionLocal()
try:
# Busca o usuário existente
usuario_db = (
db.query(Usuario).filter(Usuario.usuario_id == usuario_id).first()
)
if not usuario_db:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Usuário não encontrado.",
)
# Verifica duplicidade de username e e-mail
if usuario_schema.email or usuario_schema.username:
consulta_duplicidade = db.query(Usuario).filter(
Usuario.usuario_id != usuario_id
)
# verifica duplicidade de username (texto puro)
if usuario_schema.username:
consulta_duplicidade = consulta_duplicidade.filter(
Usuario.username == usuario_schema.username
)
# verifica duplicidade de e-mail criptografado
if usuario_schema.email:
consulta_duplicidade = consulta_duplicidade.filter(
func.AES_DECRYPT(Usuario.email, AES_KEY) == usuario_schema.email
)
usuario_conflitante = consulta_duplicidade.first()
if usuario_conflitante:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Já existe outro usuário com este e-mail ou username.",
)
# Atualiza apenas campos enviados
if usuario_schema.nome is not None:
# Criptografa novamente o nome
usuario_db.nome = func.AES_ENCRYPT(usuario_schema.nome, AES_KEY)
if usuario_schema.email is not None:
# Criptografa novamente o e-mail
usuario_db.email = func.AES_ENCRYPT(usuario_schema.email, AES_KEY)
if usuario_schema.username is not None:
usuario_db.username = usuario_schema.username
if usuario_schema.password is not None:
usuario_db.password = usuario_schema.password # deve vir com hash
if usuario_schema.status is not None:
usuario_db.status = usuario_schema.status
if usuario_schema.user_id_update is not None:
usuario_db.user_id_update = usuario_schema.user_id_update
# Atualiza data da modificação
if hasattr(usuario_db, "date_update"):
usuario_db.date_update = datetime.now()
# Persiste as alterações
db.add(usuario_db)
db.commit()
db.refresh(usuario_db)
return {
"success": True,
"message": "Usuário atualizado com sucesso!",
"data": {
"usuario_id": usuario_db.usuario_id,
"nome": usuario_schema.nome,
"email": usuario_schema.email,
"username": usuario_db.username,
"status": usuario_db.status,
},
}
except HTTPException:
db.rollback()
raise
except Exception as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Erro ao atualizar usuário: {e}",
)
finally:
db.close()

View file

@ -0,0 +1,152 @@
from pydantic import BaseModel, constr, field_validator, validator
from fastapi import HTTPException, status
from typing import Optional
from datetime import datetime
# Funções para sanitização de entradas (evitar XSS, SQLi etc.)
# É importante que esta função seja mantida/implementada no seu ambiente
# from actions.validations.text import Text # Descomentar se for usar
# Funções para validar URL (ajustar importação conforme seu projeto)
# from actions.validations.url import URL # Descomentar se for usar
# ----------------------------------------------------
# Schema Base: Usado para leitura (GET, SHOW, INDEX)
# Inclui todos os campos, incluindo os gerados pelo banco
# ----------------------------------------------------
class AtoDocumentoSchema(BaseModel):
ato_documento_id: Optional[int] = None
ato_principal_id: Optional[int] = None # bigint NOT NULL
url: Optional[str] = None # text NOT NULL
nome_documento: Optional[str] = None # varchar(255) NOT NULL
tipo_documento: Optional[str] = None # varchar(50) NOT NULL
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
class Config:
from_attributes = True
# ----------------------------------------------------
# Schema para Requisição de ID: Usado em SHOW e DELETE
# ----------------------------------------------------
class AtoDocumentoIdSchema(BaseModel):
ato_documento_id: int
@field_validator("ato_documento_id")
def validate_ato_documento_id(cls, v: int):
if v <= 0:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=[
{
"input": "ato_documento_id",
"message": "ID do documento deve ser um valor positivo.",
}
],
)
return v
# ----------------------------------------------------
# Schema para Criação (SAVE): Campos obrigatórios e sem ID
# ----------------------------------------------------
class AtoDocumentoSaveSchema(BaseModel):
# Campos obrigatórios
ato_principal_id: Optional[int] = None # <<< tornar opcional
url: str
nome_documento: constr(max_length=255)
tipo_documento: constr(max_length=50)
# Nota: created_at e updated_at são tratados pelo Model/Banco
# Validação e Sanitização de URL (chk_url_https)
@field_validator("url")
def validate_url(cls, v: str):
v = v.strip()
if not v.startswith("https://"):
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=[
{"input": "url", "message": "A URL do documento deve ser HTTPS."}
],
)
# Adicionar aqui a validação de URL (ex: URL.is_valid_url(v)) se disponível
return v
# Validação e Sanitização de Tipo Documento (chk_tipo_documento_not_empty)
@field_validator("tipo_documento")
def validate_tipo_documento(cls, v: str):
v = v.strip()
if not v:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=[
{
"input": "tipo_documento",
"message": "O tipo de documento não pode ser vazio.",
}
],
)
# Adicionar aqui a sanitização de texto (ex: Text.sanitize(v)) se disponível
return v
# Validação e Sanitização de Nome Documento
@field_validator("nome_documento")
def validate_nome_documento(cls, v: str):
v = v.strip()
if not v:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=[
{
"input": "nome_documento",
"message": "O nome do documento não pode ser vazio.",
}
],
)
return v
# ----------------------------------------------------
# Schema para Atualização (UPDATE): Todos opcionais (parciais)
# ----------------------------------------------------
class AtoDocumentoUpdateSchema(BaseModel):
# Todos os campos são opcionais no UPDATE
ato_principal_id: Optional[int] = None
url: Optional[str] = None
nome_documento: Optional[constr(max_length=255)] = None
tipo_documento: Optional[constr(max_length=50)] = None
# Validação de URL
@field_validator("url")
def validate_url(cls, v: Optional[str]):
if v is None:
return v
v = v.strip()
if v and not v.startswith("https://"):
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=[
{"input": "url", "message": "A URL do documento deve ser HTTPS."}
],
)
return v
# Validação de Tipo Documento
@field_validator("tipo_documento")
def validate_tipo_documento(cls, v: Optional[str]):
if v is None:
return v
v = v.strip()
if not v:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=[
{
"input": "tipo_documento",
"message": "O tipo de documento não pode ser vazio.",
}
],
)
return v

View file

@ -0,0 +1,138 @@
from pydantic import BaseModel, constr, field_validator
from fastapi import HTTPException, status
from typing import Optional
from datetime import datetime
import re
# Funções para sanitização de entradas (evitar XSS, SQLi etc.)
# É importante que esta função seja mantida/implementada no seu ambiente
# from actions.validations.text import Text # Descomentar se for usar
# ----------------------------------------------------
# Schema Base: Usado para leitura (GET, SHOW, INDEX)
# Inclui todos os campos, incluindo os gerados pelo banco
# ----------------------------------------------------
class AtoParteSchema(BaseModel):
ato_parte_id: Optional[int] = None
ato_principal_id: Optional[int] = None # bigint NOT NULL
nome: Optional[str] = None # varchar(255) NOT NULL
telefone: Optional[str] = None # varchar(20) DEFAULT NULL
cpf_cnpj: Optional[str] = None # varchar(20) NOT NULL
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
class Config:
from_attributes = True
# ----------------------------------------------------
# Schema para Requisição de ID: Usado em SHOW e DELETE
# ----------------------------------------------------
class AtoParteIdSchema(BaseModel):
ato_parte_id: int
@field_validator("ato_parte_id")
def validate_ato_parte_id(cls, v: int):
if v <= 0:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=[
{
"input": "ato_parte_id",
"message": "ID da parte deve ser um valor positivo.",
}
],
)
return v
# ----------------------------------------------------
# Funções de Validação Comuns
# ----------------------------------------------------
def validate_cpf_cnpj(cls, v: str):
v = re.sub(r"[^\d]", "", v).strip() # Remove caracteres não numéricos
# chk_cpf_cnpj_formato CHECK (regexp_like(`cpf_cnpj`,_utf8mb4'^[0-9]{11,14}$'))
if not re.match(r"^\d{11,14}$", v):
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=[
{
"input": "cpf_cnpj",
"message": "CPF/CNPJ deve conter apenas 11 (CPF) ou 14 (CNPJ) dígitos numéricos.",
}
],
)
return v
def validate_nome_not_empty(cls, v: str):
v = v.strip()
if not v:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=[
{
"input": "nome",
"message": "O nome não pode ser vazio.",
}
],
)
# Adicionar aqui a sanitização de texto (ex: Text.sanitize(v)) se disponível
return v
# ----------------------------------------------------
# Schema para Criação (SAVE): Campos obrigatórios e sem ID
# ----------------------------------------------------
class AtoParteSaveSchema(BaseModel):
# Campos obrigatórios
ato_principal_id: Optional[int] = None # <<< tornar opcional
nome: constr(max_length=255)
cpf_cnpj: constr(
max_length=20
) # Permitindo até 20 para acomodar a entrada antes de limpar
# Campo opcional (nullable na DDL)
telefone: Optional[constr(max_length=20)] = None
# Validação de Nome
@field_validator("nome")
def validate_nome(cls, v: str):
return validate_nome_not_empty(cls, v)
# Validação de CPF/CNPJ
@field_validator("cpf_cnpj")
def validate_cpf_cnpj_field(cls, v: str):
return validate_cpf_cnpj(cls, v)
# ----------------------------------------------------
# Schema para Atualização (UPDATE): Todos opcionais (parciais)
# ----------------------------------------------------
class AtoParteUpdateSchema(BaseModel):
# Todos os campos são opcionais no UPDATE
ato_principal_id: Optional[int] = None
nome: Optional[constr(max_length=255)] = None
telefone: Optional[constr(max_length=20)] = None
cpf_cnpj: Optional[constr(max_length=20)] = None
# Validação de Nome
@field_validator("nome")
def validate_nome_update(cls, v: Optional[str]):
if v is None:
return v
return validate_nome_not_empty(cls, v)
# Validação de CPF/CNPJ
@field_validator("cpf_cnpj")
def validate_cpf_cnpj_update(cls, v: Optional[str]):
if v is None:
return v
return validate_cpf_cnpj(cls, v)
# Nota: Telefone não precisa de validação complexa além do constr(max_length)
# se o objetivo for apenas armazenar a string fornecida.

View file

@ -1,56 +1,277 @@
from pydantic import BaseModel
from pydantic import BaseModel, constr, field_validator
from fastapi import HTTPException, status
from typing import List
from typing import Optional
from datetime import datetime
from ipaddress import IPv4Address, IPv6Address
from decimal import Decimal
from decimal import Decimal # Importar Decimal para campos monetários
# Funções para sanitização de entradas (evitar XSS, SQLi etc.)
# É importante que esta função seja mantida/implementada no seu ambiente
# from actions.validations.text import Text # Descomentar se for usar
# Schemas dos itens
from packages.v1.administrativo.schemas.ato_parte_schema import AtoParteSaveSchema
from packages.v1.administrativo.schemas.ato_documento_schema import (
AtoDocumentoSaveSchema,
)
# ----------------------------------------------------
# Schema base - campos principais e comuns
# Schema Base: Usado para leitura (GET, SHOW, INDEX)
# Inclui todos os campos, incluindo os gerados pelo banco
# ----------------------------------------------------
class AtoPrincipalBaseSchema(BaseModel):
class AtoPrincipalSchema(BaseModel):
# DDL: bigint NOT NULL AUTO_INCREMENT
ato_principal_id: Optional[int] = None
# DDL: bigint DEFAULT NULL
origem_ato_principal_id: Optional[int] = None
# DDL: bigint NOT NULL
identificacao_pedido_cgj: int
# DDL: int NOT NULL
tipo_ato: int
codigo_selo: str
codigo_ato: str
nome_civil_ato: str
nome_serventuario_praticou_ato: str
# DDL: varchar(50) NOT NULL (UNIQUE)
codigo_selo: constr(max_length=50)
# DDL: varchar(50) NOT NULL (UNIQUE)
codigo_ato: constr(max_length=50)
# DDL: varchar(255) NOT NULL (mas o banco pode conter valores NULL em registros antigos)
nome_civil_ato: Optional[constr(max_length=255)] = None
# DDL: varchar(255) NOT NULL (mas o banco pode conter valores NULL em registros antigos)
nome_serventuario_praticou_ato: Optional[constr(max_length=255)] = None
# DDL: datetime NOT NULL
data_solicitacao: datetime
ip_maquina: Optional[IPv4Address | IPv6Address] = None
# DDL: varchar(45) DEFAULT NULL
ip_maquina: Optional[constr(max_length=45)] = None
# DDL: text NOT NULL
inteiro_teor: str
# DDL: decimal(12,2) DEFAULT NULL
valor_entrada: Optional[Decimal] = None
# DDL: decimal(12,2) NOT NULL
emolumento: Decimal
# DDL: decimal(12,2) NOT NULL
taxa_judiciaria: Decimal
# DDL: decimal(12,2) NOT NULL
fundos_estaduais: Decimal
protocolo_protesto: Optional[str] = None
protocolo_imovel: Optional[str] = None
# DDL: varchar(50) DEFAULT NULL
protocolo_protesto: Optional[constr(max_length=50)] = None
# DDL: varchar(50) DEFAULT NULL
protocolo_imovel: Optional[constr(max_length=50)] = None
# DDL: datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
created_at: Optional[datetime] = None
# DDL: datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
updated_at: Optional[datetime] = None
class Config:
from_attributes = True
# Permite a conversão de float/int para Decimal
json_encoders = {Decimal: lambda v: float(v)}
# Define a precisão para Decimal
decimal_places = 2
# ----------------------------------------------------
# Schema de resposta (GET) — inclui metadata
# Schema para Requisição de ID: Usado em SHOW e DELETE
# ----------------------------------------------------
class AtoPrincipalResponseSchema(AtoPrincipalBaseSchema):
class AtoPrincipalIdSchema(BaseModel):
ato_principal_id: int
created_at: datetime
updated_at: datetime
@field_validator("ato_principal_id")
def validate_ato_principal_id(cls, v: int):
if v <= 0:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=[
{
"input": "ato_principal_id",
"message": "ID do Ato Principal deve ser um valor positivo.",
}
],
)
return v
# ----------------------------------------------------
# Schema para Criação (SAVE): Campos obrigatórios e sem ID
# ----------------------------------------------------
class AtoPrincipalSaveSchema(BaseModel):
ato_principal_id: Optional[int] = None # <<< tornar opcional
# Campos obrigatórios baseados na DDL (NOT NULL)
origem_ato_principal_id: Optional[int] = None # bigint DEFAULT NULL
identificacao_pedido_cgj: int # bigint NOT NULL
tipo_ato: int # int NOT NULL
codigo_selo: constr(max_length=50) # varchar(50) NOT NULL
codigo_ato: constr(max_length=50) # varchar(50) NOT NULL
nome_civil_ato: constr(max_length=255) # varchar(255) NOT NULL
nome_serventuario_praticou_ato: constr(max_length=255) # varchar(255) NOT NULL
data_solicitacao: datetime # datetime NOT NULL
ip_maquina: Optional[constr(max_length=45)] = None # varchar(45) DEFAULT NULL
inteiro_teor: str # text NOT NULL
valor_entrada: Optional[Decimal] = None # decimal(12,2) DEFAULT NULL
emolumento: Decimal # decimal(12,2) NOT NULL
taxa_judiciaria: Decimal # decimal(12,2) NOT NULL
fundos_estaduais: Decimal # decimal(12,2) NOT NULL
protocolo_protesto: Optional[constr(max_length=50)] = (
None # varchar(50) DEFAULT NULL
)
protocolo_imovel: Optional[constr(max_length=50)] = None # varchar(50) DEFAULT NULL
ato_partes: List[AtoParteSaveSchema] = [] # precisa existir
ato_documentos: List[AtoDocumentoSaveSchema] = [] # precisa existir
# NOVO CAMPO: Referência recursiva a outros atos principais
# Usando string para forward reference
atos_vinculados: Optional[List["AtoPrincipalSaveSchema"]] = None
# Validação e Sanitização de campos de texto obrigatórios (adaptado do código fonte)
@field_validator(
"codigo_selo",
"codigo_ato",
"nome_civil_ato",
"nome_serventuario_praticou_ato",
"inteiro_teor",
)
def validate_required_strings(cls, v: str):
v = v.strip()
if not v:
# Identifica o campo que falhou a validação
input_name = (
"Erro de campo" # Será substituído se a função for mais complexa
)
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=[
{
"input": input_name,
"message": "Este campo obrigatório não pode ser vazio.",
}
],
)
# Adicionar aqui a sanitização de texto (ex: Text.sanitize(v)) se disponível
return v
# Validação dos campos monetários (baseado na CHECK CONSTRAINT da DDL)
@field_validator(
"valor_entrada", "emolumento", "taxa_judiciaria", "fundos_estaduais"
)
def validate_positive_values(cls, v: Optional[Decimal]):
if v is None and (
"valor_entrada" in cls.__annotations__
and cls.__annotations__["valor_entrada"] == Optional[Decimal]
):
# valor_entrada é o único campo DEFAULT NULL, aceita None
return v
if v is not None and v < 0:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=[
{
"input": "campo_monetario",
"message": "Os valores monetários não podem ser negativos.",
}
],
)
return v
class Config:
from_attributes = True
json_encoders = {Decimal: lambda v: float(v)}
decimal_places = 2
# ----------------------------------------------------
# Schema de criação (POST)
# Schema para Atualização (UPDATE): Todos opcionais (parciais)
# ----------------------------------------------------
class AtoPrincipalCreateSchema(AtoPrincipalBaseSchema):
pass
class AtoPrincipalUpdateSchema(BaseModel):
# Todos os campos que podem ser atualizados são opcionais
origem_ato_principal_id: Optional[int] = None
identificacao_pedido_cgj: Optional[int] = None
tipo_ato: Optional[int] = None
codigo_selo: Optional[constr(max_length=50)] = None
codigo_ato: Optional[constr(max_length=50)] = None
nome_civil_ato: Optional[constr(max_length=255)] = None
nome_serventuario_praticou_ato: Optional[constr(max_length=255)] = None
data_solicitacao: Optional[datetime] = None
ip_maquina: Optional[constr(max_length=45)] = None
inteiro_teor: Optional[str] = None
valor_entrada: Optional[Decimal] = None
emolumento: Optional[Decimal] = None
taxa_judiciaria: Optional[Decimal] = None
fundos_estaduais: Optional[Decimal] = None
protocolo_protesto: Optional[constr(max_length=50)] = None
protocolo_imovel: Optional[constr(max_length=50)] = None
# Reutiliza a validação de strings obrigatórias (só valida se o campo for fornecido)
@field_validator(
"codigo_selo",
"codigo_ato",
"nome_civil_ato",
"nome_serventuario_praticou_ato",
"inteiro_teor",
)
def validate_non_empty_strings_on_update(cls, v: Optional[str]):
if v is not None:
v_stripped = v.strip()
if not v_stripped:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=[
{
"input": "campo_de_texto",
"message": "Este campo não pode ser atualizado para um valor vazio.",
}
],
)
return v
# Reutiliza a validação de valores positivos
@field_validator(
"valor_entrada", "emolumento", "taxa_judiciaria", "fundos_estaduais"
)
def validate_positive_values_on_update(cls, v: Optional[Decimal]):
if v is not None and v < 0:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=[
{
"input": "campo_monetario",
"message": "Os valores monetários não podem ser negativos.",
}
],
)
return v
class Config:
from_attributes = True
json_encoders = {Decimal: lambda v: float(v)}
decimal_places = 2
# ----------------------------------------------------
# Schema de atualização (PUT)
# Schema auxiliar para buscar por campos únicos (opcional, mas útil para o Save Service)
# ----------------------------------------------------
class AtoPrincipalUpdateSchema(AtoPrincipalBaseSchema):
ato_principal_id: int
class AtoPrincipalCodigoSchema(BaseModel):
codigo: constr(max_length=50) # Pode ser codigo_selo ou codigo_ato
@field_validator("codigo")
def validate_codigo(cls, v: str):
v = v.strip()
if not v:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=[
{
"input": "codigo",
"message": "O código de busca não pode ser vazio.",
}
],
)
return v
# --- ADIÇÃO CRÍTICA PARA RECURSIVIDADE (Resolução da forward reference) ---
# Necessário para resolver a referência da classe dentro de si mesma.
# Tenta Pydantic v2 (model_rebuild) e, se falhar, Pydantic v1 (update_forward_refs).
try:
AtoPrincipalSaveSchema.model_rebuild()
except AttributeError:
# Se estiver usando Pydantic v1
AtoPrincipalSaveSchema.update_forward_refs()

View file

@ -0,0 +1,223 @@
from pydantic import BaseModel, EmailStr, constr, field_validator, model_validator
from pydantic_core import PydanticCustomError
from fastapi import HTTPException, status
from typing import Optional
from datetime import datetime
# Funções utilitárias para segurança (hash e verificação de senha)
# Supondo que você ajustará as importações conforme seu projeto
from actions.security.security import Security
# Funções para sanitização de entradas (evitar XSS, SQLi etc.)
from actions.validations.text import Text
# Funções para validar E-mail
from actions.validations.email import Email
# Funções para validar cpf (Não está na DDL, mas mantido como opcional no Save e Update)
# from actions.validations.cpf import CPF # Não utilizado neste novo escopo
# ----------------------------------------------------
# Schema base
# ----------------------------------------------------
class UsuarioSchema(BaseModel):
usuario_id: Optional[int] = None
nome: Optional[str] = None
email: Optional[EmailStr] = None
username: Optional[str] = None
password: Optional[str] = None
status: Optional[str] = None # Corresponde ao 'status' na DDL
date_register: Optional[datetime] = None
date_update: Optional[datetime] = None
user_id_create: Optional[int] = None
user_id_update: Optional[int] = None
class Config:
from_attributes = True
# ----------------------------------------------------
# Schema para acesso ao sistema (Autenticação)
# ----------------------------------------------------
class UsuarioAuthenticateSchema(BaseModel):
# Campos utilizados
username: str # Usando username para login, é mais comum em DDLs simples
password: str # Corresponde ao 'password' na DDL
status: Optional[str] = None
usuario_id: Optional[int] = None
nome: Optional[str] = None
email: Optional[str] = None
# Validação e sanitização do email
@field_validator("username")
def validar_e_sanitizar_password(cls, v):
if not v:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, detail="Informe o username"
)
# Sanitiza e-mail
return Text.sanitize_input(v)
# Validação e sanitização da senha
@field_validator("password")
def validar_e_sanitizar_senha(cls, v):
if not v:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, detail="Informe a password"
)
# Sanitiza a senha
return Text.sanitize_input(v)
class Config:
from_attributes = True
# ----------------------------------------------------
# Schema para localizar um usuário especifico pelo ID (GET)
# ----------------------------------------------------
class UsuarioIdSchema(BaseModel):
usuario_id: int
# ----------------------------------------------------
# Schema para criação de novo usuário (POST)
# ----------------------------------------------------
class UsuarioSaveSchema(BaseModel):
usuario_id: Optional[int] = None
nome: constr(min_length=1)
email: EmailStr
username: constr(min_length=1)
password: constr(min_length=1)
status: Optional[str] = "A"
user_id_create: Optional[int] = None
# Sanitiza os inputs enviados
@field_validator("nome", "email", "username", "password", "status")
def validate_and_sanitize_fields(cls, v):
if v is not None:
return Text.sanitize_input(v)
return v
# Verifica se os campos obrigatórios foram enviados e hash da senha
@model_validator(mode="after")
def validate_all_fields_and_hash_password(self):
errors = []
# Validação do nome
if not self.nome or len(self.nome.strip()) == 0:
errors.append({"input": "nome", "message": "O nome é obrigatório."})
# Validação do email (EmailStr do Pydantic já faz a validação de formato)
if not self.email or len(self.email.strip()) == 0:
errors.append({"input": "email", "message": "O e-mail é obrigatório."})
# Validação da username
if not self.username or len(self.username.strip()) == 0:
errors.append({"input": "username", "message": "O username é obrigatório."})
# Validação da senha
if not self.password or len(self.password.strip()) == 0:
errors.append({"input": "password", "message": "A senha é obrigatória."})
# Criptografa a senha
if (
self.password and not errors
): # Hash somente se a senha estiver presente e sem erros anteriores
self.password = Security.hash_password(self.password)
# Se houver errors, lança uma única exceção
if errors:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=errors
)
return self
# ----------------------------------------------------
# Schema para atualizar usuário (PUT)
# ----------------------------------------------------
class UsuarioUpdateSchema(BaseModel):
nome: Optional[str] = None
email: Optional[EmailStr] = None
username: Optional[str] = None
password: Optional[str] = None
status: Optional[str] = None
user_id_update: Optional[int] = None
# Sanitiza os inputs enviados
@field_validator(
"nome",
"email",
"username",
"password",
"status",
)
def validate_and_sanitize_fields(cls, v):
if v is not None:
return Text.sanitize_input(v)
return v
# Hash da senha se ela for fornecida
@model_validator(mode="after")
def hash_password_if_provided(self):
# Hash da nova senha, se fornecida.
if self.password:
self.password = Security.hash_password(self.password)
# Não estamos forçando a obrigatoriedade de todos os campos aqui (PUT é parcial),
# mas garantimos a sanitização e o hash da senha se ela existir.
return self
# ----------------------------------------------------
# Schema para localizar usuário pelo e-mail
# ----------------------------------------------------
class UsuarioEmailSchema(BaseModel):
email: Optional[EmailStr] = None
# Sanitiza o e-mail informado
@field_validator("email", mode="before")
def sanitize_email(cls, v: Optional[str]):
if v is None:
return v
v = v.strip()
return v
# Verifica se o e-mail foi informado e se é válido
@field_validator("email")
def validate_email(cls, v: str):
errors = []
# vazio
if not v:
errors.append({"input": "e-mail", "message": "Informe um e-mail."})
# inválido pelo seu helper
if not Email.is_valid_email(v):
errors.append({"input": "e-mail", "message": "E-mail inválido."})
# Se houver errors, lança uma única exceção
if errors:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=errors
)
return v
class Config:
from_attributes = True

View file

@ -0,0 +1,18 @@
from packages.v1.administrativo.schemas.ato_documento_schema import AtoDocumentoSchema
from packages.v1.administrativo.actions.ato_documento.ato_documento_delete_action import (
DeleteAction,
)
class DeleteService:
def execute(self, ato_documento_schema: AtoDocumentoSchema):
# Instânciamento de ação
delete_action = DeleteAction()
# Executa a ação em questão, passando o schema com o ID
data = delete_action.execute(ato_documento_schema)
# Retorno da informação (inclui a mensagem de sucesso do Repository/Action)
return data

View file

@ -0,0 +1,29 @@
from fastapi import HTTPException, status
from packages.v1.administrativo.actions.ato_documento.ato_documento_index_action import (
IndexAction,
)
# Nota: O Schema de índice (AtoDocumentoSchema) não é importado aqui
# porque a Action já faz a conversão do Model para Schema.
class IndexService:
def execute(self):
# Instânciamento da Action
index_action = IndexAction()
# Executa a busca de todos os documentos
data = index_action.execute()
# Verifica se foram localizados registros
if not data:
# Retorna uma exceção 404
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Não foi possível localizar os documentos",
)
# Retorna as informações localizadas
return data

View file

@ -0,0 +1,26 @@
from packages.v1.administrativo.schemas.ato_documento_schema import (
AtoDocumentoSaveSchema,
)
from packages.v1.administrativo.actions.ato_documento.ato_documento_save_action import (
SaveAction,
)
class SaveService:
"""
Service responsável por validar o schema e executar a Action de salvamento
do documento. Não requer lógica de verificação de duplicidade de campos
que não são únicos (url, tipo_documento, nome_documento) ou
que são chaves estrangeiras (ato_principal_id).
"""
def execute(self, ato_documento_schema: AtoDocumentoSaveSchema):
# A verificação de duplicidade (como e-mail em usuário) não é necessária
# para ato_documento neste ponto. A Action/Repository é chamada diretamente.
# Instânciamento da Action
save_action = SaveAction()
# Executa a ação de salvamento
return save_action.execute(ato_documento_schema)

View file

@ -0,0 +1,26 @@
from fastapi import HTTPException, status
from packages.v1.administrativo.schemas.ato_documento_schema import AtoDocumentoSchema
from packages.v1.administrativo.actions.ato_documento.ato_documento_show_action import (
ShowAction,
)
class ShowService:
def execute(self, ato_documento_schema: AtoDocumentoSchema):
# Instânciamento da Action
show_action = ShowAction()
# Executa a ação em questão (busca pelo ID)
data = show_action.execute(ato_documento_schema)
if not data:
# Retorna uma exceção 404
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Não foi possível localizar o documento.",
)
# Retorno da informação
return data

View file

@ -0,0 +1,26 @@
from packages.v1.administrativo.schemas.ato_documento_schema import (
AtoDocumentoUpdateSchema,
)
from packages.v1.administrativo.actions.ato_documento.ato_documento_update_action import (
UpdateAction,
)
class UpdateService:
"""
Service responsável por validar o schema e executar a Action de atualização
do documento.
"""
def execute(
self, ato_documento_id: int, ato_documento_schema: AtoDocumentoUpdateSchema
):
# A lógica de verificação de duplicidade de campos sensíveis (como e-mail)
# não é necessária para ato_documento neste ponto.
# Instânciamento de ações
updateAction = UpdateAction()
# Executa a atualização, passando o ID e o schema com os dados
return updateAction.execute(ato_documento_id, ato_documento_schema)

View file

@ -0,0 +1,26 @@
from fastapi import HTTPException, status
from packages.v1.administrativo.schemas.ato_documento_schema import AtoDocumentoSchema
from packages.v1.administrativo.actions.ato_documento.ato_principal_show_action import (
ShowAction,
)
class ShowAtoPrincipalService:
def execute(self, ato_documento_schema: AtoDocumentoSchema):
# Instânciamento da Action
show_action = ShowAction()
# Executa a ação em questão (busca pelo ID)
data = show_action.execute(ato_documento_schema)
if not data:
# Retorna uma exceção 404
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Não foi possível localizar o documento.",
)
# Retorno da informação
return data

View file

@ -0,0 +1,23 @@
from packages.v1.administrativo.schemas.ato_parte_schema import AtoParteIdSchema
from packages.v1.administrativo.actions.ato_parte.ato_parte_delete_action import (
DeleteAction,
)
class DeleteService:
"""
Service responsável por orquestrar a ação de exclusão de uma parte do ato.
"""
def execute(self, ato_parte_schema: AtoParteIdSchema):
# Instânciamento de ação (assumindo que o nome da Action é 'DeleteAction'
# e está no caminho 'packages.v1.administrativo.actions.ato_parte')
delete_action = DeleteAction()
# Executa a ação em questão, passando o schema com o ID
# A Action deve receber um schema contendo apenas o ID, como AtoParteIdSchema
data = delete_action.execute(ato_parte_schema)
# Retorno da informação (inclui a mensagem de sucesso do Repository/Action)
return data

View file

@ -0,0 +1,32 @@
from fastapi import HTTPException, status
from packages.v1.administrativo.actions.ato_parte.ato_parte_index_action import (
IndexAction,
)
# Nota: O Schema de índice (AtoParteSchema) não é importado aqui
# porque a Action já faz a conversão do Model para Schema.
class IndexService:
"""
Service responsável por orquestrar a busca de todas as partes de ato.
"""
def execute(self):
# Instânciamento da Action
index_action = IndexAction()
# Executa a busca de todas as partes
data = index_action.execute()
# Verifica se foram localizados registros
if not data:
# Retorna uma exceção 404
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Não foi possível localizar as partes de ato",
)
# Retorna as informações localizadas
return data

View file

@ -0,0 +1,25 @@
from packages.v1.administrativo.schemas.ato_parte_schema import (
AtoParteSaveSchema,
)
from packages.v1.administrativo.actions.ato_parte.ato_parte_save_action import (
SaveAction,
)
class SaveService:
"""
Service responsável por validar o schema e executar a Action de salvamento
da parte de ato.
Nota: Para CPF/CNPJ, é **crucial** que haja uma verificação de duplicidade,
pois é um campo que pode ser considerado único ou que precisa de validação
de existência no contexto da sua regra de negócio.
Para este template, mantive a estrutura simples.
"""
def execute(self, ato_parte_schema: AtoParteSaveSchema):
# Instânciamento da Action
save_action = SaveAction()
# Executa a ação de salvamento
return save_action.execute(ato_parte_schema)

View file

@ -0,0 +1,32 @@
from fastapi import HTTPException, status
from packages.v1.administrativo.schemas.ato_parte_schema import AtoParteSchema
from packages.v1.administrativo.actions.ato_parte.ato_parte_show_action import (
ShowAction,
)
class ShowService:
"""
Service responsável por orquestrar a busca de uma ou mais partes
vinculadas a um ato principal.
(O Schema AtoParteSchema é presumido para a entrada que contém
o ato_principal_id para busca)
"""
def execute(self, ato_parte_schema: AtoParteSchema):
# Instânciamento da Action
show_action = ShowAction()
# Executa a ação em questão (busca pelo ato_principal_id)
data = show_action.execute(ato_parte_schema)
if not data:
# Retorna uma exceção 404
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Não foi possível localizar partes para o ato principal informado.",
)
# Retorno da informação
return data

View file

@ -0,0 +1,25 @@
from packages.v1.administrativo.schemas.ato_parte_schema import (
AtoParteUpdateSchema,
)
from packages.v1.administrativo.actions.ato_parte.ato_parte_update_action import (
UpdateAction,
)
class UpdateService:
"""
Service responsável por validar o schema e executar a Action de atualização
da parte de ato.
"""
def execute(self, ato_parte_id: int, ato_parte_schema: AtoParteUpdateSchema):
# A lógica de verificação de duplicidade de campos sensíveis (como CPF/CNPJ)
# geralmente é tratada em um ponto anterior, como no Service de SAVE ou na Action.
# Aqui, o foco é na orquestração da atualização.
# Instânciamento de ações
updateAction = UpdateAction()
# Executa a atualização, passando o ID e o schema com os dados
return updateAction.execute(ato_parte_id, ato_parte_schema)

View file

@ -0,0 +1,30 @@
from fastapi import HTTPException, status
from packages.v1.administrativo.schemas.ato_parte_schema import AtoParteSchema
from packages.v1.administrativo.actions.ato_parte.ato_principal_show_action import (
AtoPartePrincipalShowAction,
)
class AtoPrincipalShowService:
"""
Service responsável por orquestrar a busca de partes vinculadas
a um ato principal específico (busca por ato_principal_id).
"""
def execute(self, ato_parte_schema: AtoParteSchema):
# Instânciamento da Action adaptada
show_action = AtoPartePrincipalShowAction()
# Executa a ação em questão (busca pelas partes do ato principal)
data = show_action.execute(ato_parte_schema)
if not data:
# Retorna uma exceção 404
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Não foi possível localizar partes para o ato principal.",
)
# Retorno da informação
return data

View file

@ -0,0 +1,25 @@
from packages.v1.administrativo.schemas.ato_principal_schema import AtoPrincipalSchema
from packages.v1.administrativo.actions.ato_principal.ato_principal_delete_action import (
DeleteAction,
)
class DeleteService:
"""
Camada de Serviço responsável por orquestrar a exclusão
de um AtoPrincipal, geralmente fazendo validações de negócio
antes de chamar a Action de exclusão.
"""
def execute(self, ato_principal_schema: AtoPrincipalSchema):
# Instânciamento de ação (DeleteAction, mantendo o padrão)
delete_action = DeleteAction()
# Executa a ação em questão, passando o schema com o ID
# Assumindo que o schema AtoPrincipalSchema ou um específico
# como AtoPrincipalIdSchema possui o ID necessário para a exclusão.
data = delete_action.execute(ato_principal_schema)
# Retorno da informação (inclui a mensagem de sucesso do Repository/Action)
return data

View file

@ -0,0 +1,51 @@
# packages/v1/administrativo/services/ato_principal/ato_principal_get_codigo_service.py
from fastapi import HTTPException, status
from database.mysql import SessionLocal
from packages.v1.administrativo.models.ato_principal_model import AtoPrincipal
class GetCodigoService:
"""
Serviço responsável por buscar um registro de ato_principal
com base no código do selo ou código do ato.
"""
def execute(self, codigo_selo: str = None, codigo_ato: str = None):
# Garante que pelo menos um parâmetro foi informado
if not codigo_selo and not codigo_ato:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=[
{
"input": "codigo",
"message": "Informe pelo menos 'codigo_selo' ou 'codigo_ato'.",
}
],
)
db = SessionLocal()
try:
query = db.query(AtoPrincipal)
if codigo_selo:
query = query.filter(AtoPrincipal.codigo_selo == codigo_selo)
elif codigo_ato:
query = query.filter(AtoPrincipal.codigo_ato == codigo_ato)
result = query.first()
if not result:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=[
{
"input": "codigo",
"message": "Nenhum ato principal encontrado para o código informado.",
}
],
)
return result
finally:
db.close()

View file

@ -0,0 +1,33 @@
from fastapi import HTTPException, status
from packages.v1.administrativo.actions.ato_principal.ato_principal_index_action import (
IndexAction,
)
# Nota: O Schema de índice (AtoPrincipalSchema) não é importado aqui
# porque a Action já deve fazer a conversão da lista de Models para lista de Schemas.
class IndexService:
"""
Camada de Serviço responsável por orquestrar a listagem (Index)
de todos os Atos Principais.
"""
def execute(self):
# Instânciamento da Action
index_action = IndexAction()
# Executa a busca de todos os atos principais
data = index_action.execute()
# Verifica se foram localizados registros
if not data:
# Retorna uma exceção 404
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Não foi possível localizar os atos principais.",
)
# Retorna as informações localizadas
return data

View file

@ -0,0 +1,22 @@
from fastapi import status, HTTPException
# Assumindo a existência dos Schemas com o prefixo 'ato_principal'
from packages.v1.administrativo.schemas.ato_principal_schema import (
AtoPrincipalSaveSchema,
)
# Assumindo a existência da Action de salvamento com o novo prefixo
from packages.v1.administrativo.actions.ato_principal.ato_principal_save_multiple_action import (
SaveMultipleAction,
)
class SaveMultipleService:
def execute(self, atos_principais: list[AtoPrincipalSaveSchema]):
save_action = SaveMultipleAction()
# A lista completa é passada diretamente para a Action.
results = save_action.execute(atos_principais)
return results

View file

@ -0,0 +1,23 @@
from fastapi import status, HTTPException
# Assumindo a existência dos Schemas com o prefixo 'ato_principal'
from packages.v1.administrativo.schemas.ato_principal_schema import (
AtoPrincipalSaveSchema,
)
# Assumindo a existência da Action de salvamento com o novo prefixo
from packages.v1.administrativo.actions.ato_principal.ato_principal_save_action import (
SaveAction,
)
# Mantendo o padrão de nome de classe
class SaveService:
def execute(self, ato_principal_schema: AtoPrincipalSaveSchema):
# Instânciamento da Action de salvamento
saveAction = SaveAction()
# Retorna o resultado da ação de salvamento
return saveAction.execute(ato_principal_schema)

View file

@ -0,0 +1,37 @@
from fastapi import HTTPException, status
# Assumindo que o novo schema se chama AtoPrincipalSchema
from packages.v1.administrativo.schemas.ato_principal_schema import AtoPrincipalSchema
# Assumindo que a nova action se chama ShowAction
from packages.v1.administrativo.actions.ato_principal.ato_principal_show_action import (
ShowAction,
)
# Mantendo o padrão de nome de classe
class ShowService:
"""
Serviço responsável por buscar os dados de um Ato Principal
utilizando o seu ID como parâmetro de busca.
"""
# O parâmetro do execute deve usar o novo Schema
def execute(self, ato_principal_schema: AtoPrincipalSchema):
# Instânciamento da Action com o novo nome
show_action = ShowAction()
# Executa a ação em questão (busca pelo ID)
# O action.execute receberá o schema com o ID a ser buscado
data = show_action.execute(ato_principal_schema)
if not data:
# Retorna uma exceção 404 se não encontrar
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Não foi possível localizar o Ato Principal.",
)
# Retorno da informação (dados do Ato Principal)
return data

View file

@ -0,0 +1,28 @@
# Assumindo a existência do Schema de Update com o prefixo 'ato_principal'
from packages.v1.administrativo.schemas.ato_principal_schema import (
AtoPrincipalUpdateSchema,
)
# Assumindo a existência da Action de Update com o prefixo 'ato_principal'
from packages.v1.administrativo.actions.ato_principal.ato_principal_update_action import (
UpdateAction,
)
# Mantendo o padrão de nome de classe
class UpdateService:
"""
Serviço responsável por coordenar a atualização de um Ato Principal
com base no seu ID e nos dados fornecidos.
"""
# Ajusta os nomes dos parâmetros para o novo prefixo
def execute(
self, ato_principal_id: int, ato_principal_schema: AtoPrincipalUpdateSchema
):
# Instânciamento da Action
updateAction = UpdateAction()
# Executa a ação de atualização, passando o ID e o Schema de atualização
return updateAction.execute(ato_principal_id, ato_principal_schema)

View file

@ -1,45 +0,0 @@
from fastapi import HTTPException, status
from packages.v1.administrativo.actions.ato_principal.ato_principal_index_action import AtoPrincipalIndexAction
class AtoPrincipalIndexService:
"""
Serviço responsável por encapsular a lógica de negócio para a operação
de listagem de registros na tabela G_GRAMATICA.
"""
def execute(self):
"""
Executa a operação de busca de todos os registros no banco de dados.
Args:
g_cartorio_index_schema (GCartorioIndexSchema):
Esquema que pode conter filtros ou parâmetros de busca.
Returns:
A lista de registros encontrados.
"""
# ----------------------------------------------------
# Instanciamento da ação
# ----------------------------------------------------
ato_principal_index_action = AtoPrincipalIndexAction()
# ----------------------------------------------------
# Execução da ação
# ----------------------------------------------------
data = ato_principal_index_action.execute()
# ----------------------------------------------------
# Verificação de retorno
# ----------------------------------------------------
if not data:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Não foi possível localizar registros de G_GRAMATICA."
)
# ----------------------------------------------------
# Retorno da informação
# ----------------------------------------------------
return data

View file

@ -0,0 +1,58 @@
from fastapi import HTTPException, status
from actions.jwt.create_token import CreateToken
from packages.v1.administrativo.schemas.usuario_schema import UsuarioAuthenticateSchema
from packages.v1.administrativo.actions.usuario.usuario_get_by_authenticate_action import (
GetByAuthenticateAction,
)
import json
from actions.security.security import Security
class AuthenticateService:
def execute(self, user_authenticate_schema: UsuarioAuthenticateSchema):
# Instância da action de autenticação
get_by_authenticate_action = GetByAuthenticateAction()
get_by_authenticate_result = get_by_authenticate_action.execute(
user_authenticate_schema
)
# Verifica se o usuário existe
if not get_by_authenticate_result:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Usuário não encontrado ou credenciais inválidas",
)
# Verifica se a senha armazenada é um hash válido
if not Security.is_hash(get_by_authenticate_result.password):
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail="A senha armazenada é inválida",
)
# Verifica se a senha informada confere
if not Security.verify_password(
user_authenticate_schema.password, get_by_authenticate_result.password
):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="Senha incorreta"
)
# Verifica se o usuário está ativo
if get_by_authenticate_result.status != "A":
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="O usuário encontra-se desativado",
)
# Gera o token de acesso
create_token = CreateToken()
jwtUser = {
"usuario_id": int(get_by_authenticate_result.usuario_id),
"nome": str(get_by_authenticate_result.nome),
"email": str(get_by_authenticate_result.email),
}
return create_token.execute("access-token", json.dumps(jwtUser))

View file

@ -0,0 +1,18 @@
from packages.v1.administrativo.schemas.usuario_schema import UsuarioIdSchema
from packages.v1.administrativo.actions.usuario.usuario_delete_action import (
DeleteAction,
)
class DeleteService:
def execute(self, usuario_schema: UsuarioIdSchema):
# Instânciamento de ação
delete_action = DeleteAction()
# Executa a ação em questão
data = delete_action.execute(usuario_schema)
# Retorno da informação
return data

View file

@ -0,0 +1,26 @@
from fastapi import HTTPException, status
from packages.v1.administrativo.schemas.user_schema import UserCpfSchema
from packages.v1.administrativo.actions.user.user_get_by_cpf_action import GetByUsuarioCpfAction
class GetCpfService:
def execute(self, usuario_schema: UserCpfSchema, messageValidate: bool):
# Instânciamento de ação
cpf_action = GetByUsuarioCpfAction()
# Executa a ação em questão
data = cpf_action.execute(usuario_schema)
if messageValidate:
if not data:
# Retorna uma exceção
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail='Não foi possível localizar o CPF do usuário'
)
# Retorno da informação
return data

View file

@ -0,0 +1,28 @@
from fastapi import HTTPException, status
from packages.v1.administrativo.schemas.usuario_schema import UsuarioEmailSchema
from packages.v1.administrativo.actions.usuario.usuario_get_by_email_action import (
GetByUsuarioEmailAction,
)
class GetEmailService:
def execute(self, usuario_schema: UsuarioEmailSchema, messageValidate: bool):
# Instânciamento de ação
email_action = GetByUsuarioEmailAction()
# Executa a ação em questão
data = email_action.execute(usuario_schema)
if messageValidate:
if not data:
# Retorna uma exceção
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Não foi possível localizar o e-mail do usuário",
)
# Retorno da informação
return data

Some files were not shown because too many files have changed in this diff Show more