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:
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import Header from './components/Header';
|
||||
import ConsultorCard from './components/ConsultorCard';
|
||||
import RankingPaginado from './components/RankingPaginado';
|
||||
import { rankingService } from './services/api';
|
||||
import './App.css';
|
||||
|
||||
@@ -10,6 +11,7 @@ function App() {
|
||||
const [error, setError] = useState(null);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [limite, setLimite] = useState(10);
|
||||
const [modo, setModo] = useState('completo');
|
||||
|
||||
useEffect(() => {
|
||||
loadRanking();
|
||||
@@ -54,24 +56,45 @@ function App() {
|
||||
<div className="container">
|
||||
<Header total={total} />
|
||||
|
||||
<div className="controls">
|
||||
<label>
|
||||
Limite de consultores:
|
||||
<select value={limite} onChange={(e) => setLimite(Number(e.target.value))}>
|
||||
<option value={10}>Top 10</option>
|
||||
<option value={50}>Top 50</option>
|
||||
<option value={100}>Top 100</option>
|
||||
<option value={200}>Top 200</option>
|
||||
<option value={500}>Top 500</option>
|
||||
</select>
|
||||
</label>
|
||||
<div className="mode-selector">
|
||||
<button
|
||||
className={modo === 'top' ? 'active' : ''}
|
||||
onClick={() => setModo('top')}
|
||||
>
|
||||
Top N (Rápido)
|
||||
</button>
|
||||
<button
|
||||
className={modo === 'completo' ? 'active' : ''}
|
||||
onClick={() => setModo('completo')}
|
||||
>
|
||||
Ranking Completo (300k)
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="ranking-list">
|
||||
{consultores.map((consultor) => (
|
||||
<ConsultorCard key={consultor.id_pessoa} consultor={consultor} />
|
||||
))}
|
||||
</div>
|
||||
{modo === 'top' ? (
|
||||
<>
|
||||
<div className="controls">
|
||||
<label>
|
||||
Limite de consultores:
|
||||
<select value={limite} onChange={(e) => setLimite(Number(e.target.value))}>
|
||||
<option value={10}>Top 10</option>
|
||||
<option value={50}>Top 50</option>
|
||||
<option value={100}>Top 100</option>
|
||||
<option value={200}>Top 200</option>
|
||||
<option value={500}>Top 500</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="ranking-list">
|
||||
{consultores.map((consultor) => (
|
||||
<ConsultorCard key={consultor.id_pessoa} consultor={consultor} />
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<RankingPaginado />
|
||||
)}
|
||||
|
||||
<footer>
|
||||
<p>Dados: ATUACAPES (Elasticsearch) + SUCUPIRA_PAINEL (Oracle)</p>
|
||||
|
||||
Reference in New Issue
Block a user