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,50 @@
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.triggers.cron import CronTrigger
from typing import Optional
from .processar_ranking import ProcessarRankingJob
class RankingScheduler:
def __init__(self, job: ProcessarRankingJob):
self.job = job
self.scheduler: Optional[AsyncIOScheduler] = None
def iniciar(self) -> None:
"""
Inicia o scheduler e agenda o job para rodar diariamente às 3h.
"""
if self.scheduler and self.scheduler.running:
return
self.scheduler = AsyncIOScheduler()
self.scheduler.add_job(
self.job.executar,
trigger=CronTrigger(hour=3, minute=0),
id='ranking_diario',
name='Processamento diário do ranking de consultores',
replace_existing=True,
kwargs={"limpar_antes": True}
)
self.scheduler.start()
def parar(self) -> None:
"""
Para o scheduler.
"""
if self.scheduler and self.scheduler.running:
self.scheduler.shutdown(wait=False)
def executar_agora(self) -> None:
"""
Executa o job imediatamente (fora do agendamento).
"""
if self.scheduler:
self.scheduler.add_job(
self.job.executar,
id='ranking_manual',
replace_existing=True,
kwargs={"limpar_antes": True}
)