Criação dos endpoint's da tabela client
This commit is contained in:
parent
017a06613a
commit
6406f10958
19 changed files with 1086 additions and 0 deletions
|
|
@ -0,0 +1,25 @@
|
|||
from packages.v1.administrativo.schemas.client_schema import ClientIdSchema
|
||||
from packages.v1.administrativo.repositories.client.client_delete_repository import ClientDeleteRepository
|
||||
|
||||
|
||||
class ClientDeleteAction:
|
||||
"""
|
||||
Action para a exclusão de um registro na tabela 'client'.
|
||||
Utiliza o schema com o ID do cliente e delega a operação ao repositório.
|
||||
"""
|
||||
|
||||
def execute(self, client_schema: ClientIdSchema):
|
||||
"""
|
||||
Executa a lógica de exclusão do cliente.
|
||||
|
||||
A exclusão requer apenas a chave primária ('client_id'), que deve ser
|
||||
encapsulada no schema 'ClientIdSchema'.
|
||||
|
||||
:param client_schema: Schema contendo o ID do cliente a ser excluído.
|
||||
:return: Resultado da operação de exclusão do repositório.
|
||||
"""
|
||||
# Instancia o repositório específico para a exclusão de clientes
|
||||
delete_repository = ClientDeleteRepository()
|
||||
|
||||
# Chama o método execute do repositório, passando o schema do cliente
|
||||
return delete_repository.execute(client_schema)
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
from abstracts.action import BaseAction
|
||||
# O repositório deve ser adaptado para a listagem (indexação) da tabela 'client'
|
||||
from packages.v1.administrativo.repositories.client.client_index_repository import ClientIndexRepository
|
||||
from typing import Tuple, List, Dict, Any
|
||||
|
||||
|
||||
class ClientIndexAction(BaseAction):
|
||||
"""
|
||||
Action responsável por orquestrar a listagem (indexação) de todos
|
||||
os registros da tabela 'client' com suporte a paginação.
|
||||
"""
|
||||
|
||||
# O método execute recebe 'first' e 'skip' para paginação
|
||||
def execute(self, first: int, skip: int) -> Tuple[List[Dict[str, Any]], int]:
|
||||
"""
|
||||
Executa a lógica de listagem de clientes com paginação.
|
||||
|
||||
:param first: Número máximo de registros a retornar (LIMIT).
|
||||
:param skip: Número de registros a pular (OFFSET).
|
||||
:return: Tupla com a lista de clientes e o total de registros.
|
||||
"""
|
||||
# Instânciamento do repositório de indexação (listagem) de clientes
|
||||
# Supondo que ClientIndexRepository é onde a lógica de acesso ao BD está (SELECT * FROM client LIMIT first OFFSET skip)
|
||||
client_index_repository = ClientIndexRepository()
|
||||
|
||||
# Execução do repositório para buscar os clientes com paginação
|
||||
# A resposta (response) conteria os campos da DDL: client_id, cns, name, date_register, state, city, responsible, consultant, type_contract
|
||||
response, total_records = client_index_repository.execute(first, skip)
|
||||
|
||||
# Retorno da informação
|
||||
return response, total_records
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
from packages.v1.administrativo.schemas.client_schema import ClientSaveSchema
|
||||
from packages.v1.administrativo.repositories.client.client_save_repository import ClientSaveRepository
|
||||
|
||||
|
||||
class ClientSaveAction:
|
||||
"""
|
||||
Action responsável por orquestrar a operação de salvar (inserir ou atualizar)
|
||||
um registro na tabela 'client'.
|
||||
"""
|
||||
|
||||
def execute(self, client_schema: ClientSaveSchema):
|
||||
"""
|
||||
Executa a lógica de salvamento do cliente.
|
||||
|
||||
O schema 'ClientSaveSchema' deve conter todos os campos necessários
|
||||
para a operação de persistência, baseados na DDL:
|
||||
cns, name, date_register (opcional na entrada, pois tem DEFAULT),
|
||||
state, city, responsible, consultant, e type_contract.
|
||||
|
||||
:param client_schema: Schema contendo os dados do cliente a serem salvos.
|
||||
:return: Resultado da operação de salvamento do repositório.
|
||||
"""
|
||||
# Instancia o repositório específico para a operação de salvar clientes
|
||||
save_repository = ClientSaveRepository()
|
||||
|
||||
# Chama o método execute do repositório, passando o objeto schema
|
||||
return save_repository.execute(client_schema)
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
from abstracts.action import BaseAction
|
||||
from packages.v1.administrativo.schemas.client_schema import ClientSchema
|
||||
from packages.v1.administrativo.repositories.client.client_show_repository import ClientShowRepository
|
||||
|
||||
|
||||
class ClientShowAction(BaseAction):
|
||||
"""
|
||||
Action responsável por orquestrar a visualização (show) de um registro
|
||||
único na tabela 'client', geralmente utilizando o 'client_id'.
|
||||
"""
|
||||
|
||||
def execute(self, client_schema: ClientSchema):
|
||||
"""
|
||||
Executa a lógica de busca e exibição do cliente.
|
||||
|
||||
O schema 'ClientSchema' é usado para transportar o 'client_id', que
|
||||
será o critério principal para buscar os dados completos do cliente:
|
||||
cns, name, date_register, state, city, responsible, consultant,
|
||||
e type_contract.
|
||||
|
||||
:param client_schema: Schema contendo o ID do cliente a ser exibido.
|
||||
:return: O registro de cliente encontrado ou None/erro.
|
||||
"""
|
||||
# Instânciamento do repositório de visualização (show)
|
||||
show_repository = ClientShowRepository()
|
||||
|
||||
# Execução do repositório
|
||||
response = show_repository.execute(client_schema)
|
||||
|
||||
# Retorno da informação
|
||||
return response
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
from packages.v1.administrativo.schemas.client_schema import ClientUpdateSchema
|
||||
from packages.v1.administrativo.repositories.client.client_update_repository import ClientUpdateRepository
|
||||
|
||||
|
||||
class ClientUpdateAction:
|
||||
"""
|
||||
Action responsável por orquestrar a operação de atualização (UPDATE)
|
||||
de um registro na tabela 'client', identificado pelo seu ID.
|
||||
"""
|
||||
|
||||
def execute(self, client_id: int, client_schema: ClientUpdateSchema):
|
||||
"""
|
||||
Executa a lógica de atualização do cliente.
|
||||
|
||||
O 'client_id' identifica qual registro será modificado, e o
|
||||
'client_schema' contém os novos valores para os campos
|
||||
(cns, name, state, city, responsible, consultant, type_contract).
|
||||
O campo 'date_register' geralmente é omitido ou atualizado automaticamente.
|
||||
|
||||
:param client_id: ID do cliente a ser atualizado.
|
||||
:param client_schema: Schema contendo os novos dados do cliente.
|
||||
:return: Resultado da operação de atualização do repositório.
|
||||
"""
|
||||
# Instancia o repositório específico para a operação de atualização de clientes
|
||||
update_repository = ClientUpdateRepository()
|
||||
|
||||
# Chama o método execute do repositório, passando o ID e o objeto schema
|
||||
return update_repository.execute(client_id, client_schema)
|
||||
156
packages/v1/administrativo/controllers/client_controller.py
Normal file
156
packages/v1/administrativo/controllers/client_controller.py
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
from actions.dynamic_import.dynamic_import import DynamicImport
|
||||
# Adaptando os Schemas para a entidade 'Client'
|
||||
from packages.v1.administrativo.schemas.client_schema import (
|
||||
ClientSchema,
|
||||
ClientAuthenticateSchema,
|
||||
ClientSaveSchema,
|
||||
ClientUpdateSchema,
|
||||
ClientIdSchema,
|
||||
ClientFileSchema,
|
||||
ClientCNSchema # Adaptando 'LogClientIdSchema' para um campo relevante de Cliente, como o 'cns'
|
||||
)
|
||||
|
||||
import json # Necessário para carregar o arquivo app.json
|
||||
import math
|
||||
|
||||
# Carrega as configurações de paginação do app.json
|
||||
with open('config/app.json', 'r') as f:
|
||||
app_config = json.load(f)
|
||||
PAGINATION_FIRST = app_config.get('pagination', {}).get('first', 20)
|
||||
PAGINATION_SKIP = app_config.get('pagination', {}).get('skip', 0)
|
||||
|
||||
|
||||
class ClientController:
|
||||
"""
|
||||
Controller responsável por orquestrar as operações (CRUD e outras buscas)
|
||||
para a tabela 'client'.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
# Action responsável por carregar as services de acordo 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("client")
|
||||
pass
|
||||
|
||||
|
||||
# Lista todos os clientes com paginação
|
||||
def index(self, first: int = PAGINATION_FIRST, skip: int = PAGINATION_SKIP):
|
||||
|
||||
# Importação da classe desejada
|
||||
indexService = self.dynamic_import.service("client_index_service", "IndexService")
|
||||
|
||||
# Instânciamento da classe service
|
||||
self.indexService = indexService()
|
||||
|
||||
# Lista todos os clientes, recebendo a lista de dados e o total de registros
|
||||
data, total_records = self.indexService.execute(first, skip)
|
||||
|
||||
# Cálculo dos metadados de paginação
|
||||
total_pages = math.ceil(total_records / first)
|
||||
current_page = (skip // first) + 1
|
||||
|
||||
next_page = None
|
||||
# Verifica se existe uma próxima página
|
||||
if current_page < total_pages:
|
||||
next_page = current_page + 1
|
||||
|
||||
# Retorna a lista de clientes e os metadados de paginação
|
||||
return {
|
||||
'message': 'Clientes localizados com sucesso',
|
||||
'data': data,
|
||||
'pagination': {
|
||||
'total_records': total_records,
|
||||
'total_pages': total_pages,
|
||||
'current_page': current_page,
|
||||
'next_page': next_page,
|
||||
'first': first, # Total de registros por página
|
||||
'skip': skip # Registros pulados
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Busca um cliente específico pelo cns (Adaptado de logClient)
|
||||
def getByCns(self, client_schema: ClientCNSchema):
|
||||
|
||||
#Importação da classe desejada
|
||||
client_cns_service = self.dynamic_import.service('client_cns_service', 'ClientCNSService')
|
||||
|
||||
# Instânciamento da classe desejada
|
||||
self.client_cns_service = client_cns_service()
|
||||
|
||||
# Busca e retorna o cliente desejado
|
||||
return {
|
||||
'message': 'Cliente(s) localizados com sucesso pelo CNS',
|
||||
'data': self.client_cns_service.execute(client_schema)
|
||||
}
|
||||
|
||||
# Busca um cliente específico pelo ID (client_id)
|
||||
def show(self, client_schema: ClientSchema):
|
||||
|
||||
#Importação da classe desejada
|
||||
show_service = self.dynamic_import.service('client_show_service', 'ShowService')
|
||||
|
||||
# Instânciamento da classe desejada
|
||||
self.show_service = show_service()
|
||||
|
||||
# Busca e retorna o cliente desejado
|
||||
return {
|
||||
'message': 'Cliente localizado com sucesso',
|
||||
'data': self.show_service.execute(client_schema)
|
||||
}
|
||||
|
||||
# Cadastra um novo cliente
|
||||
def save(self, client_schema: ClientSaveSchema):
|
||||
|
||||
#Importação da classe desejada
|
||||
save_service = self.dynamic_import.service('client_save_service', 'ClientSaveService')
|
||||
|
||||
# Instânciamento da classe desejada
|
||||
self.save_service = save_service()
|
||||
|
||||
# Busca e retorna o cliente desejado
|
||||
return {
|
||||
'message': 'Cliente salvo com sucesso',
|
||||
'data': self.save_service.execute(client_schema)
|
||||
}
|
||||
|
||||
# Atualiza os dados de um cliente
|
||||
def update(self, client_id: int, client_schema: ClientUpdateSchema):
|
||||
|
||||
#Importação da classe desejada
|
||||
update_service = self.dynamic_import.service('client_update_service', 'ClientUpdateService')
|
||||
|
||||
# Instânciamento da classe desejada
|
||||
self.update_service = update_service()
|
||||
|
||||
# Busca e retorna o cliente desejado
|
||||
return {
|
||||
'message': 'Cliente atualizado com sucesso',
|
||||
'data': self.update_service.execute(client_id, client_schema)
|
||||
}
|
||||
|
||||
# Exclui um cliente
|
||||
def delete(self, client_schema: ClientIdSchema):
|
||||
|
||||
#Importação da classe desejada
|
||||
delete_service = self.dynamic_import.service('client_delete_service', 'DeleteService')
|
||||
|
||||
# Instânciamento da classe desejada
|
||||
self.delete_service = delete_service()
|
||||
|
||||
# Busca e retorna o cliente desejado
|
||||
return {
|
||||
'message': 'Cliente removido com sucesso',
|
||||
'data': self.delete_service.execute(client_schema)
|
||||
}
|
||||
|
||||
|
||||
# Métodos específicos do Log que não se aplicam diretamente a Client foram removidos ou adaptados:
|
||||
# getGed, getServer, getDatabase, getBackup, getDisk, getWarning (Estes parecem ser específicos de logs/monitoramento).
|
||||
# Mantendo apenas as operações CRUD e buscas por campos relevantes (CNS, State, ID).
|
||||
pass
|
||||
133
packages/v1/administrativo/endpoints/client_endpoint.py
Normal file
133
packages/v1/administrativo/endpoints/client_endpoint.py
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
# 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.user_controller import UserController
|
||||
from packages.v1.administrativo.schemas.user_schema import (
|
||||
UserSchema,
|
||||
UserAuthenticateSchema,
|
||||
UserSaveSchema,
|
||||
UserUpdateSchema,
|
||||
UserEmailSchema,
|
||||
UserIdSchema
|
||||
)
|
||||
|
||||
# Inicializa o roteador para as rotas de usuário
|
||||
router = APIRouter()
|
||||
|
||||
# Instânciamento do controller desejado
|
||||
user_controller = UserController()
|
||||
|
||||
# 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 : UserAuthenticateSchema):
|
||||
|
||||
# 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',
|
||||
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 = UserEmailSchema(email=email)
|
||||
|
||||
# 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('/{user_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(user_id : int, current_user: dict = Depends(get_current_user)):
|
||||
|
||||
# Cria o schema com os dados recebidos
|
||||
usuario_schema = UserIdSchema(user_id=user_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 : UserSaveSchema, 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('/{user_id}',
|
||||
status_code=status.HTTP_200_OK,
|
||||
summary='Atualiza um usuário',
|
||||
response_description='Atualiza um usuário')
|
||||
async def update(user_id : int, usuario_schema : UserUpdateSchema, current_user: dict = Depends(get_current_user)):
|
||||
|
||||
# Efetua a atualização dos dados de usuário
|
||||
response = user_controller.update(user_id, usuario_schema)
|
||||
|
||||
# Retorna os dados localizados
|
||||
return response
|
||||
|
||||
# Exclui um determinado usuário
|
||||
@router.delete('/{user_id}',
|
||||
status_code=status.HTTP_200_OK,
|
||||
summary='Remove um usuário',
|
||||
response_description='Remove um usuário')
|
||||
async def delete(user_id : int, current_user: dict = Depends(get_current_user)):
|
||||
|
||||
# Cria o schema com os dados recebidos
|
||||
usuario_schema = UserIdSchema(user_id=user_id)
|
||||
|
||||
# Efetua a exclusão de um determinado usuário
|
||||
response = user_controller.delete(usuario_schema)
|
||||
|
||||
# Retorna os dados localizados
|
||||
return response
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
from packages.v1.administrativo.schemas.client_schema import \
|
||||
ClientIdSchema
|
||||
from abstracts.repository import BaseRepository
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
|
||||
class ClientDeleteRepository(BaseRepository):
|
||||
"""
|
||||
Repositório responsável pela operação de exclusão (DELETE) de um
|
||||
registro na tabela 'client', usando o client_id como critério.
|
||||
"""
|
||||
|
||||
def execute(self, client_schema: ClientIdSchema):
|
||||
|
||||
try:
|
||||
|
||||
# Montagem do sql para exclusão
|
||||
sql = """ DELETE FROM client c WHERE c.client_id = :clientId """
|
||||
|
||||
# Preenchimento de parâmetros
|
||||
params = {
|
||||
"clientId" : client_schema.client_id
|
||||
}
|
||||
|
||||
# Execução do sql
|
||||
response = self.run(sql, params)
|
||||
|
||||
# Retorna o resultado (número de linhas afetadas)
|
||||
return response
|
||||
|
||||
|
||||
except Exception as e:
|
||||
|
||||
# Informa que houve uma falha na exclusão do cliente
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
detail=f"Erro ao excluir cliente: {e}"
|
||||
)
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
from abstracts.repository import BaseRepository
|
||||
from typing import Tuple, List, Dict, Any
|
||||
|
||||
|
||||
class ClientIndexRepository(BaseRepository):
|
||||
"""
|
||||
Repositório responsável por buscar e retornar todos os registros
|
||||
da tabela 'client' (indexação), com suporte a paginação.
|
||||
"""
|
||||
|
||||
# O método execute recebe 'first' (limite) e 'skip' (offset)
|
||||
def execute(self, first: int, skip: int) -> Tuple[List[Dict[str, Any]], int]:
|
||||
"""
|
||||
Executa a busca de clientes com paginação e retorna o total de registros.
|
||||
|
||||
O SELECT retorna todos os campos da DDL da tabela client:
|
||||
client_id, cns, name, date_register, state, city, responsible, consultant, type_contract.
|
||||
|
||||
:param first: Número máximo de registros a retornar (LIMIT).
|
||||
:param skip: Número de registros a pular (OFFSET).
|
||||
:return: Uma tupla contendo a lista de clientes e o total de registros.
|
||||
"""
|
||||
|
||||
# 1. SQL para contar o total de registros (ignorando LIMIT/OFFSET)
|
||||
sql_count = """ SELECT COUNT(*)
|
||||
FROM client c """
|
||||
|
||||
# Assumindo que self.fetch_one() retorna o resultado do banco
|
||||
total_records = self.fetch_one(sql_count)['COUNT(*)']
|
||||
|
||||
|
||||
# 2. SQL para listar os clientes com LIMIT e OFFSET (Paginação)
|
||||
# Selecionando todos os campos da DDL
|
||||
sql = f""" SELECT c.client_id,
|
||||
c.cns,
|
||||
c.name,
|
||||
c.date_register,
|
||||
c.state,
|
||||
c.city,
|
||||
c.responsible,
|
||||
c.consultant,
|
||||
c.type_contract
|
||||
FROM client c
|
||||
LIMIT {first} OFFSET {skip} """
|
||||
|
||||
# Execução do sql para buscar múltiplos registros
|
||||
# Assumindo que self.fetch_all() retorna a lista de dicionários
|
||||
response = self.fetch_all(sql)
|
||||
|
||||
# Retorna os dados localizados e o total de registros
|
||||
return response, total_records
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
from fastapi import HTTPException, status
|
||||
from abstracts.repository import BaseRepository
|
||||
from packages.v1.administrativo.schemas.client_schema import ClientSaveSchema # Importação do schema ClientSaveSchema
|
||||
|
||||
class ClientSaveRepository(BaseRepository):
|
||||
"""
|
||||
Repositório responsável pela operação de salvar/atualizar (Upsert)
|
||||
um registro na tabela 'client', utilizando a lógica INSERT...ON DUPLICATE KEY UPDATE.
|
||||
"""
|
||||
|
||||
def execute(self, client_schema: ClientSaveSchema):
|
||||
|
||||
try:
|
||||
# SQL adaptado para MySQL: INSERT ... ON DUPLICATE KEY UPDATE para a tabela 'client'.
|
||||
|
||||
# Colunas para o INSERT (todos os campos da DDL):
|
||||
insert_cols = """
|
||||
client_id, cns, name, date_register, state, city, responsible, consultant, type_contract
|
||||
"""
|
||||
|
||||
# Valores para o INSERT (usando os placeholders):
|
||||
insert_vals = """
|
||||
:client_id, :cns, :name, :date_register, :state, :city, :responsible, :consultant, :type_contract
|
||||
"""
|
||||
|
||||
# Ações no ON DUPLICATE KEY UPDATE (atualiza os campos, e o date_register como data de atualização)
|
||||
update_actions = """
|
||||
cns = VALUES(cns),
|
||||
name = VALUES(name),
|
||||
date_register = NOW(), # Atualiza o timestamp para o momento da modificação
|
||||
state = VALUES(state),
|
||||
city = VALUES(city),
|
||||
responsible = VALUES(responsible),
|
||||
consultant = VALUES(consultant),
|
||||
type_contract = VALUES(type_contract)
|
||||
"""
|
||||
|
||||
sql = f"""
|
||||
INSERT INTO client ({insert_cols})
|
||||
VALUES ({insert_vals})
|
||||
ON DUPLICATE KEY UPDATE
|
||||
{update_actions};
|
||||
"""
|
||||
|
||||
# Preenchimento de parâmetros. client_id será None/0 para INSERT ou o ID para UPDATE.
|
||||
params = {
|
||||
'client_id': client_schema.client_id,
|
||||
'cns': client_schema.cns,
|
||||
'name': client_schema.name,
|
||||
'date_register': client_schema.date_register,
|
||||
'state': client_schema.state,
|
||||
'city': client_schema.city,
|
||||
'responsible': client_schema.responsible,
|
||||
'consultant': client_schema.consultant,
|
||||
'type_contract': client_schema.type_contract,
|
||||
}
|
||||
|
||||
# Execução do SQL.
|
||||
result = self.run_and_return(sql, params)
|
||||
|
||||
# Se for um INSERT e a execução retornar um ID, retorna o novo ID do cliente.
|
||||
if not client_schema.client_id and result:
|
||||
return result
|
||||
|
||||
# Se for um UPDATE ou um resultado de sucesso da operação.
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
|
||||
# Informa que houve uma falha ao salvar/atualizar o cliente
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
detail=f"Erro ao salvar cliente: {e}"
|
||||
)
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
from abstracts.repository import BaseRepository
|
||||
from packages.v1.administrativo.schemas.client_schema import ClientSchema
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
|
||||
class ClientShowRepository(BaseRepository):
|
||||
"""
|
||||
Repositório responsável por buscar um registro único na tabela 'client'
|
||||
utilizando a chave primária 'client_id'.
|
||||
"""
|
||||
|
||||
def execute(self, client_schema: ClientSchema):
|
||||
"""
|
||||
Executa a busca de um cliente pelo seu ID.
|
||||
|
||||
:param client_schema: Schema contendo o client_id.
|
||||
:return: O registro de cliente encontrado ou None.
|
||||
"""
|
||||
|
||||
try:
|
||||
# Montagem do sql. O SELECT retorna todos os campos da DDL:
|
||||
# client_id, cns, name, date_register, state, city, responsible, consultant, type_contract.
|
||||
sql = """ SELECT c.client_id,
|
||||
c.cns,
|
||||
c.name,
|
||||
c.date_register,
|
||||
c.state,
|
||||
c.city,
|
||||
c.responsible,
|
||||
c.consultant,
|
||||
c.type_contract
|
||||
FROM client c
|
||||
WHERE c.client_id = :clientId """
|
||||
|
||||
# Preenchimento de parâmetros
|
||||
params = {
|
||||
'clientId' : client_schema.client_id
|
||||
}
|
||||
|
||||
# Execução do sql para buscar um único registro
|
||||
response = self.fetch_one(sql, params)
|
||||
|
||||
# Se não encontrar o cliente, pode-se lançar uma exceção
|
||||
if not response:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Cliente com ID {client_schema.client_id} não encontrado."
|
||||
)
|
||||
|
||||
return response
|
||||
|
||||
except HTTPException as e:
|
||||
raise e
|
||||
|
||||
except Exception as e:
|
||||
# Informa que houve uma falha na busca do cliente
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
detail=f"Erro ao buscar cliente: {e}"
|
||||
)
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
from abstracts.repository import BaseRepository
|
||||
from packages.v1.administrativo.schemas.client_schema import ClientUpdateSchema
|
||||
from fastapi import HTTPException, status
|
||||
# A importação de 'datetime' foi removida no código original e não é necessária aqui.
|
||||
|
||||
|
||||
class ClientUpdateRepository(BaseRepository):
|
||||
"""
|
||||
Repositório responsável pela atualização (UPDATE) dinâmica de um
|
||||
registro na tabela 'client', identificado pelo 'client_id'.
|
||||
"""
|
||||
|
||||
def execute(self, client_id: int, client_schema: ClientUpdateSchema):
|
||||
|
||||
try:
|
||||
updates = []
|
||||
params = {}
|
||||
|
||||
# --- Mapeamento e inclusão dos campos da DDL para atualização dinâmica ---
|
||||
|
||||
# cns
|
||||
if client_schema.cns is not None:
|
||||
updates.append("cns = :cns")
|
||||
params["cns"] = client_schema.cns
|
||||
|
||||
# name
|
||||
if client_schema.name is not None:
|
||||
updates.append("name = :name")
|
||||
params["name"] = client_schema.name
|
||||
|
||||
# state
|
||||
if client_schema.state is not None:
|
||||
updates.append("state = :state")
|
||||
params["state"] = client_schema.state
|
||||
|
||||
# city
|
||||
if client_schema.city is not None:
|
||||
updates.append("city = :city")
|
||||
params["city"] = client_schema.city
|
||||
|
||||
# responsible
|
||||
if client_schema.responsible is not None:
|
||||
updates.append("responsible = :responsible")
|
||||
params["responsible"] = client_schema.responsible
|
||||
|
||||
# consultant
|
||||
if client_schema.consultant is not None:
|
||||
updates.append("consultant = :consultant")
|
||||
params["consultant"] = client_schema.consultant
|
||||
|
||||
# type_contract
|
||||
if client_schema.type_contract is not None:
|
||||
updates.append("type_contract = :type_contract")
|
||||
params["type_contract"] = client_schema.type_contract
|
||||
|
||||
# Os campos 'client_id' (chave) e 'date_register' (timestamp de criação) não são atualizados dinamicamente.
|
||||
|
||||
if not updates:
|
||||
# Se não houver campos para atualizar, retorna False
|
||||
return False
|
||||
|
||||
params["client_id"] = client_id
|
||||
|
||||
# SQL para UPDATE
|
||||
sql = f"UPDATE client SET {', '.join(updates)} WHERE client_id = :client_id;"
|
||||
|
||||
# Executa a query
|
||||
result = self.run(sql, params)
|
||||
|
||||
if result is None or result == 0:
|
||||
# Se 0 linhas afetadas, pode ser que o cliente não exista ou não houve alteração.
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
detail='Nenhum cliente localizado para esta solicitação ou nenhuma alteração realizada.'
|
||||
)
|
||||
|
||||
# Retorna True, indicando o sucesso da operação
|
||||
return True
|
||||
|
||||
|
||||
except Exception as e:
|
||||
|
||||
# Informa que houve uma falha na atualização do cliente
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
detail=f"Erro ao atualizar cliente: {e}"
|
||||
)
|
||||
113
packages/v1/administrativo/schemas/client_schema.py
Normal file
113
packages/v1/administrativo/schemas/client_schema.py
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
from pydantic import BaseModel, constr, field_validator, model_validator
|
||||
from fastapi import HTTPException, status
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
|
||||
# Funções utilitárias para sanitização de entradas (evitar XSS, SQLi etc.)
|
||||
# É necessário importar a função de sanitização se for utilizada nos validadores
|
||||
from actions.validations.text import Text
|
||||
|
||||
# ----------------------------------------------------
|
||||
# Schema base - Representa a estrutura completa do Cliente (usado em Show e Index)
|
||||
# ----------------------------------------------------
|
||||
class ClientSchema(BaseModel):
|
||||
# Campos da DDL, todos opcionais para o Schema base (principalmente para leitura)
|
||||
client_id: Optional[int] = None
|
||||
cns: Optional[str] = None
|
||||
name: Optional[str] = None
|
||||
date_register: Optional[datetime] = None
|
||||
state: Optional[str] = None
|
||||
city: Optional[str] = None
|
||||
responsible: Optional[str] = None
|
||||
consultant: Optional[str] = None
|
||||
type_contract: Optional[str] = None
|
||||
|
||||
class Config:
|
||||
# Permite que o Pydantic mapeie campos vindos do banco (ex: via ORM)
|
||||
from_attributes = True
|
||||
|
||||
# ----------------------------------------------------
|
||||
# Schema para operações que requerem apenas o ID
|
||||
# ----------------------------------------------------
|
||||
class ClientIdSchema(BaseModel):
|
||||
client_id: Optional[int] = None
|
||||
|
||||
# Valida se o ID não está vazio
|
||||
@model_validator(mode='after')
|
||||
def validate_client_id(self):
|
||||
if not self.client_id or self.client_id <= 0:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
detail='O ID do cliente é obrigatório para esta operação.'
|
||||
)
|
||||
return self
|
||||
|
||||
# ----------------------------------------------------
|
||||
# Schema para localizar cliente pelo CNS
|
||||
# ----------------------------------------------------
|
||||
class ClientCNSSchema(BaseModel):
|
||||
cns: Optional[str] = None
|
||||
|
||||
# Sanitiza o input
|
||||
@field_validator('cns')
|
||||
def sanitize_cns(cls, v):
|
||||
if v:
|
||||
return Text.sanitize_input(v) # Mantendo o padrão de sanitização
|
||||
return v
|
||||
|
||||
# Valida se o campo não está vazio
|
||||
@model_validator(mode='after')
|
||||
def validate_cns(self):
|
||||
if not self.cns or len(self.cns.strip()) == 0:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
detail='Informe um CNS para a busca.'
|
||||
)
|
||||
return self
|
||||
|
||||
# ----------------------------------------------------
|
||||
# Schema para cadastrar (SAVE) um novo cliente (name é NOT NULL)
|
||||
# ----------------------------------------------------
|
||||
class ClientSaveSchema(BaseModel):
|
||||
# Opcional, pois é AUTO_INCREMENT, mas pode ser usado para Upsert
|
||||
client_id: Optional[int] = None
|
||||
|
||||
# name é NOT NULL na DDL, então é obrigatório no save
|
||||
name: constr(min_length=1, max_length=550)
|
||||
|
||||
# Os demais são NULL DEFAULT ou com DEFAULT, então podem ser Optional na entrada
|
||||
cns: Optional[str] = None
|
||||
state: Optional[str] = None
|
||||
city: Optional[str] = None
|
||||
responsible: Optional[str] = None
|
||||
consultant: Optional[str] = None
|
||||
type_contract: Optional[str] = None
|
||||
|
||||
# Sanitiza os inputs de string
|
||||
@field_validator('cns', 'name', 'state', 'city', 'responsible', 'consultant', 'type_contract')
|
||||
def validate_and_sanitize_fields(cls, v):
|
||||
if v is not None:
|
||||
# Assumindo que Text.sanitize_input existe e faz a sanitização
|
||||
return Text.sanitize_input(v)
|
||||
return v
|
||||
|
||||
# ----------------------------------------------------
|
||||
# Schema para atualizar (UPDATE) um cliente (tudo opcional)
|
||||
# ----------------------------------------------------
|
||||
class ClientUpdateSchema(BaseModel):
|
||||
# Todos os campos que podem ser atualizados são opcionais
|
||||
cns: Optional[str] = None
|
||||
name: Optional[constr(min_length=1, max_length=550)] = None
|
||||
state: Optional[str] = None
|
||||
city: Optional[str] = None
|
||||
responsible: Optional[str] = None
|
||||
consultant: Optional[str] = None
|
||||
type_contract: Optional[str] = None
|
||||
|
||||
# Sanitiza os inputs de string
|
||||
@field_validator('cns', 'name', 'state', 'city', 'responsible', 'consultant', 'type_contract')
|
||||
def validate_and_sanitize_fields(cls, v):
|
||||
if v is not None:
|
||||
# Assumindo que Text.sanitize_input existe e faz a sanitização
|
||||
return Text.sanitize_input(v)
|
||||
return v
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
from packages.v1.administrativo.schemas.client_schema import ClientIdSchema
|
||||
from packages.v1.administrativo.actions.client.client_delete_action import ClientDeleteAction
|
||||
|
||||
|
||||
class ClientDeleteService:
|
||||
"""
|
||||
Service responsável por orquestrar a exclusão de um cliente,
|
||||
delegando a execução para a Action correspondente.
|
||||
"""
|
||||
|
||||
def execute(self, client_schema: ClientIdSchema):
|
||||
"""
|
||||
Executa o serviço de exclusão de um cliente.
|
||||
|
||||
:param client_schema: Schema contendo o client_id do registro a ser excluído.
|
||||
:return: Resultado da operação de exclusão (geralmente o número de linhas afetadas).
|
||||
"""
|
||||
|
||||
# Instânciamento de ação
|
||||
delete_action = ClientDeleteAction()
|
||||
|
||||
# Executa a ação em questão
|
||||
data = delete_action.execute(client_schema)
|
||||
|
||||
# Retorno da informação
|
||||
return data
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
from fastapi import HTTPException, status
|
||||
from packages.v1.administrativo.schemas.client_schema import ClientSchema
|
||||
from packages.v1.administrativo.actions.client.client_index_action import ClientIndexAction
|
||||
|
||||
|
||||
class ClientIndexService:
|
||||
"""
|
||||
Service responsável por orquestrar a listagem (indexação) de todos
|
||||
os clientes, delegando a busca para a Action correspondente.
|
||||
"""
|
||||
|
||||
# O método execute pode ser adaptado para receber 'first' e 'skip' se a Action/Repository suportar paginação.
|
||||
# No entanto, mantendo o padrão da assinatura do arquivo de referência, ele não recebe parâmetros aqui.
|
||||
def execute(self):
|
||||
"""
|
||||
Executa o serviço de listagem de clientes.
|
||||
|
||||
:return: Lista de registros de clientes.
|
||||
"""
|
||||
|
||||
# Instânciamento de ação
|
||||
index_action = ClientIndexAction()
|
||||
|
||||
# Executa a busca de todos os clientes (a Action/Repository fará a busca, potencialmente com paginação)
|
||||
data = index_action.execute()
|
||||
|
||||
# Verifica se foram localizados registros
|
||||
if not data:
|
||||
# Retorna uma exceção
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail='Não foi possível localizar os clientes'
|
||||
)
|
||||
|
||||
# Retorna as informações localizadas
|
||||
return data
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
from actions.dynamic_import.dynamic_import import DynamicImport
|
||||
from packages.v1.administrativo.schemas.client_schema import ClientSaveSchema, ClientCNSSchema
|
||||
from packages.v1.administrativo.actions.client.client_save_action import ClientSaveAction
|
||||
from fastapi import status, HTTPException
|
||||
|
||||
|
||||
class ClientSaveService:
|
||||
"""
|
||||
Service responsável por orquestrar o salvamento (INSERT/UPDATE) de um cliente.
|
||||
Inclui validações de regra de negócio, como a unicidade do CNS.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
# Action responsável por carregar as services de acordo 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("client")
|
||||
pass
|
||||
|
||||
def execute(self, client_schema: ClientSaveSchema):
|
||||
"""
|
||||
Executa a lógica de serviço para salvar um cliente.
|
||||
|
||||
:param client_schema: Schema contendo os dados do cliente a serem salvos.
|
||||
:return: Resultado da operação de salvamento.
|
||||
"""
|
||||
|
||||
# Armazena possíveis erros de validação
|
||||
errors = []
|
||||
|
||||
# --- Validação de Unicidade do CNS (Cadastro Nacional de Saúde) ---
|
||||
if client_schema.cns:
|
||||
# Importação de service de busca por CNS (Adaptado de user_get_email_service)
|
||||
cns_service = self.dynamic_import.service("client_get_cns_service", "GetCNSService")
|
||||
|
||||
# Instânciamento da service
|
||||
self.cns_service = cns_service()
|
||||
|
||||
# Tenta localizar o cliente pelo CNS, sem levantar erro HTTP se não encontrar (o False no segundo param)
|
||||
self.response = self.cns_service.execute(ClientCNSSchema(cns=client_schema.cns), False)
|
||||
|
||||
# Se houver retorno, significa que o CNS já está sendo utilizado
|
||||
if self.response:
|
||||
# Se for um UPDATE (client_id existe) e o CNS encontrado pertencer ao próprio cliente que está atualizando,
|
||||
# não deve ser considerado erro. A validação precisa garantir que o CNS pertence a OUTRO cliente.
|
||||
if not client_schema.client_id or self.response.get('client_id') != client_schema.client_id:
|
||||
errors.append({'input': 'cns', 'message': 'O CNS informado já está sendo utilizado por outro cliente.'})
|
||||
|
||||
# Se houver erros de validação, informa
|
||||
if errors:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail=errors
|
||||
)
|
||||
|
||||
# Instânciamento de ações
|
||||
save_action = ClientSaveAction()
|
||||
|
||||
# Executa a ação de persistência
|
||||
return save_action.execute(client_schema)
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
from fastapi import HTTPException, status
|
||||
from packages.v1.administrativo.schemas.client_schema import ClientSchema
|
||||
from packages.v1.administrativo.actions.client.client_show_action import ClientShowAction
|
||||
|
||||
|
||||
class ClientShowService:
|
||||
"""
|
||||
Service responsável por orquestrar a visualização de um cliente específico
|
||||
(geralmente pelo client_id), delegando a execução para a Action.
|
||||
"""
|
||||
|
||||
def execute(self, client_schema: ClientSchema):
|
||||
"""
|
||||
Executa o serviço de visualização de um cliente.
|
||||
|
||||
:param client_schema: Schema contendo o client_id.
|
||||
:return: O registro do cliente encontrado.
|
||||
"""
|
||||
|
||||
# Instânciamento de ação
|
||||
show_action = ClientShowAction()
|
||||
|
||||
# Executa a ação em questão
|
||||
data = show_action.execute(client_schema)
|
||||
|
||||
if not data:
|
||||
# Retorna uma exceção se o registro não for encontrado
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail='Não foi possível localizar o registro do cliente'
|
||||
)
|
||||
|
||||
# Retorno da informação
|
||||
return data
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
from packages.v1.administrativo.schemas.client_schema import ClientUpdateSchema, ClientCNSSchema
|
||||
from packages.v1.administrativo.actions.client.client_update_action import ClientUpdateAction
|
||||
from actions.dynamic_import.dynamic_import import DynamicImport
|
||||
from fastapi import status, HTTPException
|
||||
|
||||
|
||||
class ClientUpdateService:
|
||||
"""
|
||||
Service responsável por orquestrar a atualização de um cliente,
|
||||
incluindo validações de regra de negócio antes de delegar a ação.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
# Action responsável por carregar as services de acordo 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("client")
|
||||
pass
|
||||
|
||||
def execute(self, client_id: int, client_schema: ClientUpdateSchema):
|
||||
"""
|
||||
Executa o serviço de atualização de um cliente.
|
||||
|
||||
:param client_id: ID do cliente a ser atualizado.
|
||||
:param client_schema: Schema contendo os dados do cliente para atualização.
|
||||
:return: Resultado da operação de atualização.
|
||||
"""
|
||||
|
||||
# Armazena possíveis erros de validação
|
||||
errors = []
|
||||
|
||||
# --- Validação de Unicidade do CNS (Cadastro Nacional de Saúde) ---
|
||||
if client_schema.cns:
|
||||
# Importação de service de busca por CNS
|
||||
cns_service = self.dynamic_import.service("client_get_cns_service", "GetCNSService")
|
||||
|
||||
# Instânciamento da service
|
||||
self.cns_service = cns_service()
|
||||
|
||||
# Tenta localizar o cliente pelo CNS, sem levantar erro HTTP se não encontrar (o False no segundo param)
|
||||
# É necessário usar um schema de busca específico para o CNS
|
||||
self.response = self.cns_service.execute(ClientCNSSchema(cns=client_schema.cns), False)
|
||||
|
||||
# Se houver retorno, significa que o CNS já está sendo utilizado
|
||||
if self.response:
|
||||
# O CNS é um erro APENAS se pertencer a outro cliente, e não ao que está sendo atualizado.
|
||||
if self.response.get('client_id') != client_id:
|
||||
errors.append({'input': 'cns', 'message': 'O CNS informado já está sendo utilizado por outro cliente.'})
|
||||
|
||||
# Se houver erros de validação, informa
|
||||
if errors:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail=errors
|
||||
)
|
||||
|
||||
# Instânciamento da Action de atualização
|
||||
update_action = ClientUpdateAction()
|
||||
|
||||
# Executa a ação de atualização
|
||||
return update_action.execute(client_id, client_schema)
|
||||
|
|
@ -4,6 +4,7 @@ from fastapi import APIRouter
|
|||
# Importa os módulos de rotas específicos
|
||||
from packages.v1.administrativo.endpoints import user_endpoint
|
||||
from packages.v1.administrativo.endpoints import log_endpoint
|
||||
from packages.v1.administrativo.endpoints import client_endpoint
|
||||
|
||||
# Cria uma instância do APIRouter que vai agregar todas as rotas da API
|
||||
api_router = APIRouter()
|
||||
|
|
@ -18,3 +19,9 @@ api_router.include_router(
|
|||
log_endpoint.router, prefix="/administrativo/log", tags=["Gerenciamento de log's"]
|
||||
)
|
||||
|
||||
# Inclui as rotas de client
|
||||
api_router.include_router(
|
||||
client_endpoint.router, prefix="/administrativo/client", tags=["Gerenciamento de client's"]
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue