fix: resolver problemas identificados no code review
Correções de segurança: - SQL Injection: usar prepared statements em ranking_repository.py - Validação de entrada para parâmetros page/size Correções de bugs: - Bônus de continuidade: 15→20 pts para 8+ anos (conforme especificação) - Memory leak: limpar _consultores após processamento do ranking Melhorias de robustez: - Substituir bare except por exceções específicas - Threading.Lock para padrão singleton thread-safe - Pool Oracle com configuração otimizada (timeout/getmode) - ES client com timeouts diferenciados e verificação is_closed - Logging para tipos de coordenação desconhecidos Correções frontend: - Polling com timeout máximo de 5 minutos - useEffect cleanup para setTimeout - React.memo e useMemo para otimização de performance
This commit is contained in:
@@ -30,7 +30,12 @@ class ElasticsearchClient:
|
||||
"Accept": "application/json"
|
||||
},
|
||||
verify=self.verify_ssl,
|
||||
timeout=120.0
|
||||
timeout=httpx.Timeout(
|
||||
connect=10.0,
|
||||
read=60.0,
|
||||
write=30.0,
|
||||
pool=10.0,
|
||||
)
|
||||
)
|
||||
|
||||
async def close(self) -> None:
|
||||
@@ -39,8 +44,8 @@ class ElasticsearchClient:
|
||||
|
||||
@property
|
||||
def client(self) -> httpx.AsyncClient:
|
||||
if not self._client:
|
||||
raise RuntimeError("Cliente Elasticsearch não conectado. Execute connect() primeiro.")
|
||||
if not self._client or self._client.is_closed:
|
||||
raise RuntimeError("Cliente Elasticsearch não conectado ou fechado. Execute connect() primeiro.")
|
||||
return self._client
|
||||
|
||||
async def buscar_por_id(self, id_pessoa: int) -> Optional[dict]:
|
||||
|
||||
@@ -21,13 +21,20 @@ class OracleClient:
|
||||
user=self.user,
|
||||
password=self.password,
|
||||
dsn=self.dsn,
|
||||
min=2,
|
||||
max=10,
|
||||
increment=1,
|
||||
min=1,
|
||||
max=20,
|
||||
increment=5,
|
||||
timeout=30,
|
||||
wait_timeout=10,
|
||||
getmode=oracledb.POOL_GETMODE_TIMEDWAIT,
|
||||
)
|
||||
self._connected = True
|
||||
logger.info("Pool Oracle conectado com sucesso")
|
||||
except oracledb.Error as e:
|
||||
logger.error(f"Oracle database error: {e}", exc_info=True)
|
||||
self._connected = False
|
||||
except Exception as e:
|
||||
logger.warning(f"Oracle: {e}")
|
||||
logger.error(f"Oracle connection error: {e}", exc_info=True)
|
||||
self._connected = False
|
||||
|
||||
def close(self) -> None:
|
||||
@@ -45,11 +52,13 @@ class OracleClient:
|
||||
def get_connection(self):
|
||||
if not self._pool:
|
||||
raise RuntimeError("Pool Oracle não conectado. Execute connect() primeiro.")
|
||||
conn = self._pool.acquire()
|
||||
conn = None
|
||||
try:
|
||||
conn = self._pool.acquire()
|
||||
yield conn
|
||||
finally:
|
||||
self._pool.release(conn)
|
||||
if conn:
|
||||
self._pool.release(conn)
|
||||
|
||||
def executar_query(self, query: str, params: Optional[dict] = None) -> List[Dict[str, Any]]:
|
||||
if not self.is_connected:
|
||||
|
||||
@@ -99,11 +99,19 @@ class RankingOracleRepository:
|
||||
"""
|
||||
Busca ranking paginado ordenado por posição.
|
||||
"""
|
||||
if page < 1:
|
||||
page = 1
|
||||
if size < 1 or size > 10000:
|
||||
size = 50
|
||||
|
||||
offset = (page - 1) * size
|
||||
limit_end = offset + size
|
||||
|
||||
where_clause = ""
|
||||
params = {}
|
||||
params = {
|
||||
"offset": offset,
|
||||
"limit_end": limit_end,
|
||||
}
|
||||
|
||||
if filtro_ativo is not None:
|
||||
where_clause = "AND ATIVO = :ativo"
|
||||
@@ -128,7 +136,7 @@ class RankingOracleRepository:
|
||||
FROM TB_RANKING_CONSULTOR
|
||||
WHERE 1=1 {where_clause}
|
||||
)
|
||||
WHERE RN > {offset} AND RN <= {limit_end}
|
||||
WHERE RN > :offset AND RN <= :limit_end
|
||||
"""
|
||||
|
||||
results = self.client.executar_query(query, params)
|
||||
|
||||
@@ -63,6 +63,10 @@ class ConsultorRepositoryImpl(ConsultorRepository):
|
||||
return "CAJ_MP"
|
||||
elif "adjunt" in texto:
|
||||
return "CAJ"
|
||||
elif "coordenador" in texto or "área" in texto or "area" in texto:
|
||||
return "CA"
|
||||
|
||||
logger.warning(f"Tipo de coordenação desconhecido, inferindo CA: tipo='{tipo_coord}', texto='{texto[:100]}'")
|
||||
return "CA"
|
||||
|
||||
def _extrair_coordenacoes_capes(self, atuacoes: List[Dict[str, Any]]) -> List[CoordenacaoCapes]:
|
||||
|
||||
Reference in New Issue
Block a user