/** * DT worker'ının doldurduğu SQLite'ı okuyup ihaleist tender modeline çevirir. * Veritabanı yoksa (worker hiç koşmadıysa) sessizce boş liste döner. */ import fs from 'node:fs'; import { sektorBul } from './siniflandir.mjs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { titleCaseTr } from './tr-text.mjs'; // node:sqlite bazı Node sürümlerinde deneysel/flag'li. Yoksa DT devre dışı kalır, // ilan.gov.tr canlı verisi yine de çalışır. let DatabaseSync = null; try { ({ DatabaseSync } = await import('node:sqlite')); } catch { console.warn('[dt-store] node:sqlite yok — doğrudan temin devre dışı (açık ihaleler etkilenmez)'); } const __dirname = path.dirname(fileURLToPath(import.meta.url)); const DB_PATH = path.join(__dirname, 'data', 'dt.sqlite'); function parseDetail(detailJson) { if (!detailJson) return {}; try { const d = JSON.parse(detailJson).dogrudanTeminDetayResult || {}; const dt = d.DogrudanTeminBilgileri || {}; const idare = d.IdareBilgileri || {}; const soz = d.SozlesmeBilgileri?.SozlesmeBilgisiList?.[0] || null; return { yasaMaddesi: dt.YasaKapsamiTeminMaddesi || null, durum: dt.DtDurumu || null, bransKodlari: dt.BransKodList || [], kismiTeklif: dt.KismiTeklif || null, idareTam: idare.Idare || null, enUstIdare: idare.EnUstIdare || null, kazananFirma: soz?.IstekliAdi || null, sozlesmeBedeli: soz?.SozlesmeBedeli || null, sozlesmeTarihi: soz?.SozlesmeTarihi || null, }; } catch { return {}; } } const KURUM_RULES = [ [/hastane|sağlık|başhekim|tıp merkezi|adsm|ağız ve diş/i, 'Sağlık Kurumları'], [/ceza infaz|cezaevi|tutukevi|işyurt/i, 'Ceza İnfaz Kurumları'], [/üniversite|rektörlü/i, 'Üniversiteler'], [/büyükşehir belediye/i, 'Büyükşehir Belediyeleri'], [/belediye/i, 'Belediyeler'], [/bakanlığı|valiliği|müdürlüğü|komutanlığı/i, 'Bakanlık / Kamu Kurumları'], ]; function classifyKurum(text) { for (const [re, label] of KURUM_RULES) if (re.test(text || '')) return label; return 'Diğer Kamu Kurumları'; } function mapDtRow(row) { const det = parseDetail(row.detail_json); const kurum = det.idareTam || row.idare_adi || ''; return { id: `dt-${row.dogrudan_temin_id}`, kaynak: 'EKAP · Doğrudan Temin', kaynakUrl: 'https://ekap.kik.gov.tr/EKAP/YeniIhaleArama.aspx', ikn: row.dtn || '', baslik: row.isin_adi || '', kurum, sehir: row.ili && !row.ili.startsWith('Plaka') ? titleCaseTr(row.ili) : '', bolge: '', tarih: row.dt_tarihi || '', saat: '', alimTuru: 'Doğrudan Temin', usul: det.yasaMaddesi ? `Doğrudan Temin (4734 ${det.yasaMaddesi})` : 'Doğrudan Temin (4734 22/d)', // Sektör ihale TÜRÜ değildir; başlık + kalem adlarından çıkarılır. sektor: sektorBul(row.isin_adi || '', (det.bransKodlari || []).join(' '), row.turu || ''), brans: (det.bransKodlari || []).join(', ') || row.turu || 'Genel', kurumTipi: classifyKurum(kurum), eklenmeZamani: row.yayim_tarihi ? `Yayım: ${row.yayim_tarihi}` : '', isYeni: false, isLiveFeed: true, aciliyetBadge: null, items: [], aciklama: null, yaklasikMaliyet: null, teslimatSuresi: null, teslimYeri: null, yeterlikKriterleri: [], aiMatchScore: null, aiRiskScore: null, aiBidRange: null, kanbanStatus: 'incelenecek', atananPersonel: null, durum: det.durum || 'Aktif', incelendi: false, takipte: false, // Sonuçlanmış DT'lerde kazanan/fiyat bilgisi sozlesmeBedeli: det.sozlesmeBedeli || null, kazananFirma: det.kazananFirma || null, }; } export function readDtTenders(limit = 300) { if (!DatabaseSync || !fs.existsSync(DB_PATH)) return { tenders: [], totalCount: 0, available: false }; const db = new DatabaseSync(DB_PATH, { readOnly: true }); try { const total = db.prepare('SELECT COUNT(*) c FROM dt_tenders').get().c; const rows = db .prepare( `SELECT dogrudan_temin_id, dtn, isin_adi, idare_adi, turu, ili, dt_tarihi, yayim_tarihi, detail_json FROM dt_tenders ORDER BY rowid DESC LIMIT ?`, ) .all(limit); return { tenders: rows.map(mapDtRow), totalCount: total, available: true }; } finally { db.close(); } }