+server.js 4.74 KB
const API_BASE = 'http://136.112.29.74/api/acteco';

let cachedClasificador = null;
let cachedEstados = null;
let cachedTimestamp = 0;
const CACHE_TTL = 5 * 60 * 1000;

function getParent(acteco) {
  const idx = acteco.lastIndexOf('.');
  if (idx === -1) return null;
  return acteco.substring(0, idx);
}

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

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

  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.acteco}`);
        if (!res.ok) return null;
        const data = await res.json();
        return { acteco: item.acteco, estados: data.estados || [] };
      } catch { return null; }
    }));
    results.filter(Boolean).forEach(r => estados.set(r.acteco, 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') {
      await loadAndCacheAll();

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

      // Agregar nodos padres faltantes (sectores sin estados propios)
      const ids = new Set(results.map(r => r.acteco));
      const missingParents = new Set();
      results.forEach(r => {
        if (r.parent && !ids.has(r.parent)) missingParents.add(r.parent);
      });
      for (const parentCode of missingParents) {
        const item = cachedClasificador.find(c => c.acteco === parentCode);
        results.push({
          gestion,
          nivel: item?.nivel || 'actividad',
          acteco: parentCode,
          desc_acteco: item?.desc_acteco || parentCode,
          parent: getParent(parentCode),
          devengado: 0
        });
      }

      return new Response(JSON.stringify(results), {
        headers: { 'Content-Type': 'application/json' }
      });
    } else {
      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.acteco}/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 || 'actividad',
              acteco: item.acteco,
              desc_acteco: item.desc_acteco,
              parent: getParent(item.acteco),
              devengado: yearData.monto
            };
          } catch { return null; }
        }));
        results.push(...batchResults.filter(Boolean));
      }

      // Agregar nodos padres faltantes
      const ids2 = new Set(results.map(r => r.acteco));
      const missing2 = new Set();
      results.forEach(r => {
        if (r.parent && !ids2.has(r.parent)) missing2.add(r.parent);
      });
      for (const parentCode of missing2) {
        const item = cachedClasificador.find(c => c.acteco === parentCode);
        results.push({
          gestion,
          nivel: item?.nivel || 'actividad',
          acteco: parentCode,
          desc_acteco: item?.desc_acteco || parentCode,
          parent: getParent(parentCode),
          devengado: 0
        });
      }

      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' }
    });
  }
}