fix(): Retorno da rotina original de envio de multiplos registros
This commit is contained in:
parent
4cc6ad54a5
commit
5bdbb05a47
1 changed files with 128 additions and 342 deletions
|
|
@ -1,11 +1,8 @@
|
||||||
import hashlib
|
from typing import List, Optional
|
||||||
import logging
|
|
||||||
from typing import List, Optional, Dict, Any
|
|
||||||
from fastapi import HTTPException, status
|
from fastapi import HTTPException, status
|
||||||
from sqlalchemy import text
|
import traceback
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy import func
|
||||||
from sqlalchemy.exc import IntegrityError, OperationalError, SQLAlchemyError
|
from sqlalchemy.orm import Session # Importação para tipagem da session
|
||||||
|
|
||||||
from database.mysql import SessionLocal, get_database_settings
|
from database.mysql import SessionLocal, get_database_settings
|
||||||
from packages.v1.administrativo.models.ato_principal_model import AtoPrincipal
|
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_parte_model import AtoParte
|
||||||
|
|
@ -14,11 +11,6 @@ from packages.v1.administrativo.schemas.ato_principal_schema import (
|
||||||
AtoPrincipalSaveSchema,
|
AtoPrincipalSaveSchema,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Configuração de logging
|
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
|
||||||
)
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
# Configuração da Chave AES
|
# Configuração da Chave AES
|
||||||
DB_SETTINGS = get_database_settings()
|
DB_SETTINGS = get_database_settings()
|
||||||
|
|
@ -27,168 +19,11 @@ AES_KEY = getattr(DB_SETTINGS, "aeskey", None)
|
||||||
|
|
||||||
class SaveMultipleRepository:
|
class SaveMultipleRepository:
|
||||||
"""
|
"""
|
||||||
Repositório otimizado para salvar múltiplos atos principais com suas partes
|
Repositório para salvar múltiplos atos principais com suas partes e documentos,
|
||||||
e documentos, usando criptografia nativa do MySQL via AES_ENCRYPT.
|
usando criptografia nativa do MySQL via AES_ENCRYPT.
|
||||||
|
Implementa lógica recursiva para atos vinculados.
|
||||||
Melhorias implementadas:
|
|
||||||
- Criptografia correta via execução SQL
|
|
||||||
- Hash SHA256 para busca rápida de duplicidades
|
|
||||||
- Gerenciamento adequado de sessões (uma por transação)
|
|
||||||
- Logging profissional estruturado
|
|
||||||
- Tratamento de exceções específicas
|
|
||||||
- Validação robusta da chave AES
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""Inicializa o repositório e valida a configuração da chave AES"""
|
|
||||||
if not AES_KEY:
|
|
||||||
raise ValueError(
|
|
||||||
"A chave AES (aeskey) não está configurada. "
|
|
||||||
"Verifique as configurações do banco de dados."
|
|
||||||
)
|
|
||||||
logger.info("SaveMultipleRepository inicializado com sucesso")
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _generate_hash(value: str) -> str:
|
|
||||||
"""
|
|
||||||
Gera hash SHA256 para busca rápida e verificação de duplicidade.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
value: Valor a ser hasheado
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Hash SHA256 em formato hexadecimal
|
|
||||||
"""
|
|
||||||
if not value:
|
|
||||||
return None
|
|
||||||
return hashlib.sha256(value.encode("utf-8")).hexdigest()
|
|
||||||
|
|
||||||
def _encrypt_field(self, db: Session, value: str) -> Optional[bytes]:
|
|
||||||
"""
|
|
||||||
Criptografa um campo usando AES_ENCRYPT do MySQL.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
db: Sessão do banco de dados
|
|
||||||
value: Valor a ser criptografado
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Valor criptografado em bytes ou None se valor vazio
|
|
||||||
"""
|
|
||||||
if not value or (isinstance(value, str) and not value.strip()):
|
|
||||||
return None
|
|
||||||
|
|
||||||
try:
|
|
||||||
result = db.execute(
|
|
||||||
text("SELECT AES_ENCRYPT(:val, :key) as encrypted"),
|
|
||||||
{"val": value.strip(), "key": AES_KEY},
|
|
||||||
).scalar()
|
|
||||||
return result
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Erro ao criptografar campo: {str(e)}")
|
|
||||||
raise
|
|
||||||
|
|
||||||
def _check_duplicate_codigo_selo(self, db: Session, codigo_selo: str) -> bool:
|
|
||||||
"""
|
|
||||||
Verifica se o código do selo já existe usando hash para busca rápida.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
db: Sessão do banco de dados
|
|
||||||
codigo_selo: Código do selo a verificar
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
True se já existe, False caso contrário
|
|
||||||
"""
|
|
||||||
codigo_hash = self._generate_hash(codigo_selo)
|
|
||||||
|
|
||||||
existing = (
|
|
||||||
db.query(AtoPrincipal)
|
|
||||||
.filter(AtoPrincipal.codigo_selo_hash == codigo_hash)
|
|
||||||
.first()
|
|
||||||
)
|
|
||||||
|
|
||||||
return existing is not None
|
|
||||||
|
|
||||||
def _prepare_encrypted_data(
|
|
||||||
self, db: Session, data: Dict[str, Any], fields_to_encrypt: List[str]
|
|
||||||
) -> Dict[str, Any]:
|
|
||||||
"""
|
|
||||||
Prepara os dados criptografando os campos especificados.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
db: Sessão do banco de dados
|
|
||||||
data: Dicionário com os dados
|
|
||||||
fields_to_encrypt: Lista de campos a criptografar
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Dicionário com campos criptografados
|
|
||||||
"""
|
|
||||||
processed_data = data.copy()
|
|
||||||
|
|
||||||
for field in fields_to_encrypt:
|
|
||||||
value = data.get(field)
|
|
||||||
if value is not None and str(value).strip():
|
|
||||||
processed_data[field] = self._encrypt_field(db, str(value))
|
|
||||||
else:
|
|
||||||
processed_data[field] = None
|
|
||||||
|
|
||||||
return processed_data
|
|
||||||
|
|
||||||
def _save_ato_partes(
|
|
||||||
self, db: Session, partes: List, ato_principal_id: int
|
|
||||||
) -> None:
|
|
||||||
"""
|
|
||||||
Salva as partes de um ato principal.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
db: Sessão do banco de dados
|
|
||||||
partes: Lista de schemas de partes
|
|
||||||
ato_principal_id: ID do ato principal
|
|
||||||
"""
|
|
||||||
campos_criptografar = ["nome", "telefone", "cpf_cnpj"]
|
|
||||||
|
|
||||||
for parte in partes:
|
|
||||||
parte_data = parte.model_dump(exclude_unset=True)
|
|
||||||
|
|
||||||
# Criptografa campos sensíveis
|
|
||||||
parte_data = self._prepare_encrypted_data(
|
|
||||||
db, parte_data, campos_criptografar
|
|
||||||
)
|
|
||||||
|
|
||||||
# Adiciona relacionamento
|
|
||||||
parte_data["ato_principal_id"] = ato_principal_id
|
|
||||||
|
|
||||||
new_parte = AtoParte(**parte_data)
|
|
||||||
db.add(new_parte)
|
|
||||||
|
|
||||||
logger.debug(f"Salvas {len(partes)} partes para ato {ato_principal_id}")
|
|
||||||
|
|
||||||
def _save_ato_documentos(
|
|
||||||
self, db: Session, documentos: List, ato_principal_id: int
|
|
||||||
) -> None:
|
|
||||||
"""
|
|
||||||
Salva os documentos de um ato principal.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
db: Sessão do banco de dados
|
|
||||||
documentos: Lista de schemas de documentos
|
|
||||||
ato_principal_id: ID do ato principal
|
|
||||||
"""
|
|
||||||
campos_criptografar = ["url", "nome_documento", "tipo_documento"]
|
|
||||||
|
|
||||||
for doc in documentos:
|
|
||||||
doc_data = doc.model_dump(exclude_unset=True)
|
|
||||||
|
|
||||||
# Criptografa campos sensíveis
|
|
||||||
doc_data = self._prepare_encrypted_data(db, doc_data, campos_criptografar)
|
|
||||||
|
|
||||||
# Adiciona relacionamento
|
|
||||||
doc_data["ato_principal_id"] = ato_principal_id
|
|
||||||
|
|
||||||
new_documento = AtoDocumento(**doc_data)
|
|
||||||
db.add(new_documento)
|
|
||||||
|
|
||||||
logger.debug(f"Salvos {len(documentos)} documentos para ato {ato_principal_id}")
|
|
||||||
|
|
||||||
def _save_single_recursive_transaction(
|
def _save_single_recursive_transaction(
|
||||||
self,
|
self,
|
||||||
db: Session,
|
db: Session,
|
||||||
|
|
@ -196,76 +31,95 @@ class SaveMultipleRepository:
|
||||||
parent_ato_principal_id: Optional[int] = None,
|
parent_ato_principal_id: Optional[int] = None,
|
||||||
) -> AtoPrincipal:
|
) -> AtoPrincipal:
|
||||||
"""
|
"""
|
||||||
Salva recursivamente um ato principal com todas suas dependências.
|
Salva um único Ato Principal, seus filhos (partes/documentos) e
|
||||||
|
chama recursivamente o salvamento dos atos vinculados.
|
||||||
Args:
|
|
||||||
db: Sessão do banco de dados
|
|
||||||
ato_schema: Schema do ato a salvar
|
|
||||||
parent_ato_principal_id: ID do ato pai (para atos vinculados)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Instância do AtoPrincipal salvo
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
HTTPException: Se código do selo já existe
|
|
||||||
"""
|
"""
|
||||||
codigo_selo = ato_schema.codigo_selo
|
codigo_selo = ato_schema.codigo_selo
|
||||||
|
|
||||||
# 1. Verificação de duplicidade usando hash
|
# 1. Pré-processamento e Criptografia
|
||||||
if self._check_duplicate_codigo_selo(db, codigo_selo):
|
|
||||||
logger.warning(f"Tentativa de cadastro duplicado: {codigo_selo}")
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_409_CONFLICT,
|
|
||||||
detail=f"O Código do Selo '{codigo_selo}' já está cadastrado.",
|
|
||||||
)
|
|
||||||
|
|
||||||
# 2. Preparação dos dados do ato principal
|
|
||||||
ato_data = ato_schema.model_dump(
|
ato_data = ato_schema.model_dump(
|
||||||
exclude_unset=True,
|
exclude_unset=True,
|
||||||
exclude={"ato_partes", "ato_documentos", "atos_vinculados"},
|
exclude={"ato_partes", "ato_documentos", "atos_vinculados"},
|
||||||
)
|
)
|
||||||
|
|
||||||
# Define relacionamento com ato pai
|
# Define o ID de origem se for um ato vinculado
|
||||||
if parent_ato_principal_id is not None:
|
if parent_ato_principal_id is not None:
|
||||||
ato_data["origem_ato_principal_id"] = parent_ato_principal_id
|
ato_data["origem_ato_principal_id"] = parent_ato_principal_id
|
||||||
elif "origem_ato_principal_id" not in ato_data:
|
elif ato_data.get("origem_ato_principal_id") is None:
|
||||||
ato_data["origem_ato_principal_id"] = None
|
ato_data["origem_ato_principal_id"] = None
|
||||||
|
|
||||||
# 3. Criptografia dos campos sensíveis
|
# 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 = [
|
campos_criptografar = [
|
||||||
"nome_civil_ato",
|
"nome_civil_ato",
|
||||||
"nome_serventuario_praticou_ato",
|
"nome_serventuario_praticou_ato",
|
||||||
]
|
]
|
||||||
|
|
||||||
ato_data = self._prepare_encrypted_data(db, ato_data, campos_criptografar)
|
# 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
|
||||||
|
|
||||||
# Adiciona hash do código do selo para busca rápida
|
# 2. Criação e Persistência do Ato Principal
|
||||||
ato_data["codigo_selo_hash"] = self._generate_hash(codigo_selo)
|
|
||||||
|
|
||||||
# 4. Criação e persistência do ato principal
|
|
||||||
new_ato = AtoPrincipal(**ato_data)
|
new_ato = AtoPrincipal(**ato_data)
|
||||||
db.add(new_ato)
|
db.add(new_ato)
|
||||||
db.flush() # Obtém o ID sem fazer commit
|
db.flush()
|
||||||
|
|
||||||
new_ato_id = new_ato.ato_principal_id
|
new_ato_id = new_ato.ato_principal_id
|
||||||
logger.info(f"Ato principal criado: ID={new_ato_id}, Selo={codigo_selo}")
|
|
||||||
|
|
||||||
# 5. Salva partes do ato
|
# 3. Salva os Filhos Diretos: Ato Partes
|
||||||
if ato_schema.ato_partes:
|
for parte in ato_schema.ato_partes:
|
||||||
self._save_ato_partes(db, ato_schema.ato_partes, new_ato_id)
|
parte_data = parte.model_dump(exclude_unset=True)
|
||||||
|
|
||||||
# 6. Salva documentos do ato
|
parte_campos_criptografar = ["nome", "telefone", "cpf_cnpj"]
|
||||||
if ato_schema.ato_documentos:
|
|
||||||
self._save_ato_documentos(db, ato_schema.ato_documentos, new_ato_id)
|
|
||||||
|
|
||||||
# 7. Salvamento recursivo de atos vinculados
|
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:
|
if ato_schema.atos_vinculados:
|
||||||
logger.info(
|
|
||||||
f"Processando {len(ato_schema.atos_vinculados)} atos vinculados "
|
|
||||||
f"para selo {codigo_selo}"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
# A iteração é semelhante, mas o que é executado é a chamada recursiva
|
||||||
for linked_ato_schema in ato_schema.atos_vinculados:
|
for linked_ato_schema in ato_schema.atos_vinculados:
|
||||||
|
# O ato vinculado é persistido chamando a rotina de salvamento completa
|
||||||
self._save_single_recursive_transaction(
|
self._save_single_recursive_transaction(
|
||||||
db,
|
db,
|
||||||
linked_ato_schema,
|
linked_ato_schema,
|
||||||
|
|
@ -274,138 +128,70 @@ class SaveMultipleRepository:
|
||||||
|
|
||||||
return new_ato
|
return new_ato
|
||||||
|
|
||||||
def _process_single_ato(self, ato_schema: AtoPrincipalSaveSchema) -> Dict[str, Any]:
|
# Método principal (chamado pela Action)
|
||||||
"""
|
def execute(self, atos_principais: List[AtoPrincipalSaveSchema]):
|
||||||
Processa um único ato em uma transação isolada.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
ato_schema: Schema do ato a processar
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Dicionário com resultado da operação
|
|
||||||
"""
|
|
||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
codigo_selo = getattr(ato_schema, "codigo_selo", "CÓDIGO_INDISPONÍVEL")
|
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:
|
try:
|
||||||
logger.info(f"Iniciando processamento do selo: {codigo_selo}")
|
# A rotina completa de salvamento (incluindo filhos e recursão) é encapsulada
|
||||||
|
|
||||||
# Salva o ato com toda sua hierarquia
|
|
||||||
saved_ato = self._save_single_recursive_transaction(
|
saved_ato = self._save_single_recursive_transaction(
|
||||||
db, ato_schema, parent_ato_principal_id=None
|
db,
|
||||||
|
ato_schema,
|
||||||
|
parent_ato_principal_id=None, # Ato de nível superior
|
||||||
)
|
)
|
||||||
|
|
||||||
# Commit da transação completa
|
# ---------- COMMIT final para a transação completa (Ato Principal + Filhos + Atos Vinculados) ----------
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
logger.info(
|
# Retorno de sucesso
|
||||||
f"Ato salvo com sucesso: ID={saved_ato.ato_principal_id}, "
|
ato_result = {
|
||||||
f"Selo={codigo_selo}"
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
"success": True,
|
"success": True,
|
||||||
"message": "Ato Principal e vinculados salvos com sucesso",
|
"message": "Ato Principal e vinculados salvos com sucesso",
|
||||||
"data": {
|
"data": {
|
||||||
"ato_principal_id": saved_ato.ato_principal_id,
|
"ato_principal_id": saved_ato.ato_principal_id,
|
||||||
"codigo_selo": codigo_selo,
|
"codigo_selo": codigo_selo_log,
|
||||||
"tipo_ato": saved_ato.tipo_ato,
|
"tipo_ato": saved_ato.tipo_ato,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
results.append(ato_result)
|
||||||
|
|
||||||
|
# Tratamento de erro específico para duplicidade ou HTTP
|
||||||
except HTTPException as he:
|
except HTTPException as he:
|
||||||
db.rollback()
|
db.rollback()
|
||||||
logger.warning(f"Erro HTTP ao processar selo {codigo_selo}: {he.detail}")
|
results.append(
|
||||||
return {
|
{
|
||||||
"success": False,
|
"success": False,
|
||||||
"error": he.detail,
|
"error": he.detail,
|
||||||
"data": {"codigo_selo": codigo_selo},
|
"data": {"codigo_selo": codigo_selo_log},
|
||||||
}
|
}
|
||||||
|
|
||||||
except IntegrityError as ie:
|
|
||||||
db.rollback()
|
|
||||||
logger.error(
|
|
||||||
f"Erro de integridade ao processar selo {codigo_selo}: {str(ie)}",
|
|
||||||
exc_info=True,
|
|
||||||
)
|
)
|
||||||
return {
|
|
||||||
"success": False,
|
|
||||||
"error": "Erro de integridade: violação de constraint no banco de dados",
|
|
||||||
"data": {"codigo_selo": codigo_selo},
|
|
||||||
}
|
|
||||||
|
|
||||||
except OperationalError as oe:
|
|
||||||
db.rollback()
|
|
||||||
logger.error(
|
|
||||||
f"Erro operacional ao processar selo {codigo_selo}: {str(oe)}",
|
|
||||||
exc_info=True,
|
|
||||||
)
|
|
||||||
return {
|
|
||||||
"success": False,
|
|
||||||
"error": "Erro de conexão ou operação no banco de dados",
|
|
||||||
"data": {"codigo_selo": codigo_selo},
|
|
||||||
}
|
|
||||||
|
|
||||||
except SQLAlchemyError as se:
|
|
||||||
db.rollback()
|
|
||||||
logger.error(
|
|
||||||
f"Erro SQLAlchemy ao processar selo {codigo_selo}: {str(se)}",
|
|
||||||
exc_info=True,
|
|
||||||
)
|
|
||||||
return {
|
|
||||||
"success": False,
|
|
||||||
"error": "Erro ao interagir com o banco de dados",
|
|
||||||
"data": {"codigo_selo": codigo_selo},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
# Tratamento de erro genérico
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
db.rollback()
|
db.rollback()
|
||||||
logger.error(
|
|
||||||
f"Erro inesperado ao processar selo {codigo_selo}: {str(e)}",
|
# Log completo do erro
|
||||||
exc_info=True,
|
print(f"ERRO DE PERSISTÊNCIA NO SELO: {codigo_selo_log}")
|
||||||
)
|
traceback.print_exc()
|
||||||
return {
|
print("--------------")
|
||||||
|
|
||||||
|
results.append(
|
||||||
|
{
|
||||||
"success": False,
|
"success": False,
|
||||||
"error": f"Erro inesperado: {str(e)}",
|
"error": "Erro ao salvar o registro. Verifique o formato dos dados ou os logs do servidor.",
|
||||||
"data": {"codigo_selo": codigo_selo},
|
"data": {"codigo_selo": codigo_selo_log},
|
||||||
}
|
}
|
||||||
|
|
||||||
finally:
|
|
||||||
db.close()
|
|
||||||
|
|
||||||
def execute(
|
|
||||||
self, atos_principais: List[AtoPrincipalSaveSchema]
|
|
||||||
) -> List[Dict[str, Any]]:
|
|
||||||
"""
|
|
||||||
Método principal para salvar múltiplos atos principais.
|
|
||||||
Cada ato é processado em uma transação independente.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
atos_principais: Lista de schemas de atos a salvar
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Lista de dicionários com resultado de cada operação
|
|
||||||
"""
|
|
||||||
logger.info(f"Iniciando processamento de {len(atos_principais)} atos")
|
|
||||||
|
|
||||||
results = []
|
|
||||||
successful_count = 0
|
|
||||||
failed_count = 0
|
|
||||||
|
|
||||||
for idx, ato_schema in enumerate(atos_principais, 1):
|
|
||||||
logger.info(f"Processando ato {idx}/{len(atos_principais)}")
|
|
||||||
|
|
||||||
result = self._process_single_ato(ato_schema)
|
|
||||||
results.append(result)
|
|
||||||
|
|
||||||
if result["success"]:
|
|
||||||
successful_count += 1
|
|
||||||
else:
|
|
||||||
failed_count += 1
|
|
||||||
|
|
||||||
logger.info(
|
|
||||||
f"Processamento concluído: {successful_count} sucessos, "
|
|
||||||
f"{failed_count} falhas"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
db.close()
|
||||||
return results
|
return results
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue