feat: Implementa job de ranking para 300k consultores

Backend:
- Adiciona Scroll API no cliente Elasticsearch para processar todos os 300k+ consultores
- Cria tabela TB_RANKING_CONSULTOR no Oracle para ranking pré-calculado
- Implementa job de processamento com APScheduler (diário às 3h)
- Adiciona endpoints: /ranking/paginado, /ranking/status, /ranking/processar, /ranking/estatisticas
- Repository Oracle com paginação eficiente via ROW_NUMBER
- Status do job com progresso em tempo real (polling)
- Leitura automática de LOBs no OracleClient

Frontend:
- Componente RankingPaginado com paginação completa
- Barra de progresso do job em tempo real
- Botão para reprocessar ranking
- Alternância entre Top N (rápido) e Ranking Completo (300k)

Infraestrutura:
- Docker compose com depends_on para garantir Oracle disponível
- Schema SQL com procedure SP_ATUALIZAR_POSICOES
- Índices otimizados para paginação
This commit is contained in:
Frederico Castro
2025-12-10 01:33:00 -03:00
parent 0213a55791
commit 3ea6a4409e
19 changed files with 1596 additions and 20 deletions

View File

@@ -0,0 +1,61 @@
from pydantic import BaseModel, Field
from typing import Optional, List
from datetime import datetime
class ConsultorRankingResumoSchema(BaseModel):
id_pessoa: int
nome: str
posicao: Optional[int]
pontuacao_total: float
componente_a: float
componente_b: float
componente_c: float
componente_d: float
ativo: bool
anos_atuacao: float
class RankingPaginadoResponseSchema(BaseModel):
total: int
page: int
size: int
total_pages: int
consultores: List[ConsultorRankingResumoSchema]
class EstatisticasRankingSchema(BaseModel):
total_consultores: int
total_ativos: int
total_inativos: int
ultima_atualizacao: Optional[str]
pontuacao_media: float
pontuacao_maxima: float
pontuacao_minima: float
media_componentes: dict
distribuicao: List[dict]
class JobStatusSchema(BaseModel):
running: bool
progress: int
processados: int
total: int
mensagem: str
batch_atual: int
total_batches: int
tempo_decorrido: Optional[str]
tempo_estimado: Optional[str]
inicio: Optional[str]
fim: Optional[str]
erro: Optional[str]
class ProcessarRankingRequestSchema(BaseModel):
limpar_antes: bool = Field(default=True, description="Se deve limpar a tabela antes de processar")
class ProcessarRankingResponseSchema(BaseModel):
sucesso: bool
mensagem: str
job_id: Optional[str] = None