+server.js 3.94 KB
import { API_HOST } from '$lib/api.js';
const API_BASE = API_HOST + '/api/objeto';

// Cache en memoria del servidor
let cachedClasificador = null;
let cachedEstados = null; // Map: objeto → estados[]
let cachedTimestamp = 0;
const CACHE_TTL = 5 * 60 * 1000; // 5 minutos

async function loadAndCacheAll() {
  const now = Date.now();
  if (cachedEstados && (now - cachedTimestamp) < CACHE_TTL) return;

  // 1. Cargar clasificador
  const clasRes = await fetch(`${API_BASE}/clasificador`);
  if (!clasRes.ok) throw new Error('Failed to fetch clasificador');
  cachedClasificador = await clasRes.json();

  // 2. Cargar todos los objetos en paralelo (lotes de 80)
  const estados = new Map();
  const batchSize = 80;

  for (let i = 0; i < cachedClasificador.length; i += batchSize) {
    const batch = cachedClasificador.slice(i, i + batchSize);
    const results = await Promise.all(batch.map(async (item) => {
      try {
        const res = await fetch(`${API_BASE}/${item.objeto}`);
        if (!res.ok) return null;
        const data = await res.json();
        return { objeto: item.objeto, estados: data.estados || [] };
      } catch { return null; }
    }));
    results.filter(Boolean).forEach(r => estados.set(r.objeto, r.estados));
  }

  cachedEstados = estados;
  cachedTimestamp = now;
}

export async function GET({ url }) {
  const gestion = parseInt(url.searchParams.get('gestion') || '2025');
  const entidad = url.searchParams.get('entidad') || '0';

  try {
    if (entidad === '0') {
      // Todo el estado — usar cache
      await loadAndCacheAll();

      const results = [];
      for (const item of cachedClasificador) {
        const estados = cachedEstados.get(item.objeto);
        if (!estados) continue;
        const yearData = estados.find(d => d.gestion === gestion);
        if (!yearData || !yearData.total) continue;
        results.push({
          gestion,
          nivel: item.nivel,
          objeto: item.objeto,
          desc_objeto: item.desc_objeto,
          parent: getParent(item.objeto, item.nivel),
          devengado: yearData.total
        });
      }

      return new Response(JSON.stringify(results), {
        headers: { 'Content-Type': 'application/json' }
      });
    } else {
      // Entidad específica — cargar por entidad (no cacheable eficientemente)
      if (!cachedClasificador) {
        const clasRes = await fetch(`${API_BASE}/clasificador`);
        cachedClasificador = await clasRes.json();
      }

      const results = [];
      const batchSize = 80;

      for (let i = 0; i < cachedClasificador.length; i += batchSize) {
        const batch = cachedClasificador.slice(i, i + batchSize);
        const batchResults = await Promise.all(batch.map(async (item) => {
          try {
            const res = await fetch(`${API_BASE}/${item.objeto}/entidades/${entidad}`);
            if (!res.ok) return null;
            const data = await res.json();
            const yearData = data.find(d => d.gestion === gestion);
            if (!yearData || !yearData.monto) return null;
            return {
              gestion,
              nivel: item.nivel,
              objeto: item.objeto,
              desc_objeto: item.desc_objeto,
              parent: getParent(item.objeto, item.nivel),
              devengado: yearData.monto
            };
          } catch { return null; }
        }));
        results.push(...batchResults.filter(Boolean));
      }

      return new Response(JSON.stringify(results), {
        headers: { 'Content-Type': 'application/json' }
      });
    }
  } catch (err) {
    return new Response(JSON.stringify({ error: 'Failed to build treemap' }), {
      status: 500, headers: { 'Content-Type': 'application/json' }
    });
  }
}

function getParent(objeto, nivel) {
  if (nivel === 'grupo') return null;
  if (nivel === 'subgrupo') return objeto.charAt(0) + '0000';
  if (nivel === 'partida') return objeto.substring(0, 2) + '000';
  if (nivel === 'subpartida') return objeto.substring(0, 3) + '00';
  return null;
}