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:
Frederico Castro
2025-12-15 07:11:28 -03:00
parent d639b82087
commit df3d03d3b2
10 changed files with 91 additions and 37 deletions

View File

@@ -1,4 +1,4 @@
import React, { useState, useRef, useEffect } from 'react';
import React, { useState, useRef, useEffect, useMemo, memo } from 'react';
import './ConsultorCard.css';
const SELOS = {
@@ -123,7 +123,7 @@ const FORMULAS = {
},
bloco_c: {
titulo: 'Consultoria',
descricao: 'CONS_ATIVO=150 | CONS_HIST=100 | CONS_FALECIDO=100\nTempo: 5 pts/ano (max 50)\nContinuidade 8a+=+20 (escalonado)\nRetorno (reativação): +15 (uma vez)',
descricao: 'CONS_ATIVO=150 | CONS_HIST=100 | CONS_FALECIDO=100\nTempo: 5 pts/ano (max 50)\nContinuidade: 3a=+5, 5a=+10, 8a+=+20 (escalonado)\nRetorno (reativação): +15 (uma vez)',
},
bloco_d: {
titulo: 'Premiacoes/Avaliacoes',
@@ -183,15 +183,16 @@ const ScoreItemWithTooltip = ({ value, label, formula, style }) => (
</div>
);
const ConsultorCard = ({ consultor, highlight, selecionado, onToggleSelecionado }) => {
const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecionado }) => {
const [expanded, setExpanded] = useState(false);
const cardRef = useRef(null);
useEffect(() => {
if (highlight && cardRef.current) {
setTimeout(() => {
const timeoutId = setTimeout(() => {
cardRef.current?.scrollIntoView({ behavior: 'smooth', block: 'center' });
}, 100);
return () => clearTimeout(timeoutId);
}
}, [highlight]);
@@ -219,7 +220,7 @@ const ConsultorCard = ({ consultor, highlight, selecionado, onToggleSelecionado
const blocoD = pontuacao?.bloco_d || { total: consultor.bloco_d || 0 };
const pontuacaoTotal = (blocoA.total || 0) + (blocoB.total || 0) + (blocoC.total || 0) + (blocoD.total || 0);
const selos = gerarSelos(consultor);
const selos = useMemo(() => gerarSelos(consultor), [consultor]);
return (
<div ref={cardRef} className={`ranking-card ${expanded ? 'expanded' : ''} ${highlight ? 'highlight' : ''} ${selecionado ? 'selecionado' : ''}`} onClick={() => setExpanded(!expanded)}>
@@ -421,9 +422,11 @@ const ConsultorCard = ({ consultor, highlight, selecionado, onToggleSelecionado
)}
</div>
);
};
});
const BlocoDetalhes = ({ titulo, bloco, cor }) => (
ConsultorCard.displayName = 'ConsultorCard';
const BlocoDetalhes = memo(({ titulo, bloco, cor }) => (
<div className="detail-section">
<h4 style={{ color: cor }}>{titulo}</h4>
<div className="score-breakdown">
@@ -468,6 +471,8 @@ const BlocoDetalhes = ({ titulo, bloco, cor }) => (
</div>
</div>
</div>
);
));
BlocoDetalhes.displayName = 'BlocoDetalhes';
export default ConsultorCard;