Files
ihaleist/server/dt-store.mjs
T
hikmettan c701083642 fix: il adları kaynaklar arasında bölünüyordu (Ankara / ANKARA)
Sorun: ilan.gov.tr illeri 'Ankara' biçiminde, EKAP arşivi/DT 'ANKARA' biçiminde
veriyordu. İl filtresi birebir string karşılaştırdığı için kullanıcı 'Ankara'
seçtiğinde 161 'ANKARA' kaydı görünmüyordu. 81 ilin hepsi çift kayıtlıydı
(162 farklı il değeri).

- server/tr-text.mjs: Türkçe duyarlı titleCaseTr / normTr yardımcıları
- history-store + dt-store: il adı okunurken tek biçime indiriliyor
  (mevcut kayıtlar için de geçerli, yeniden tarama gerekmiyor)
- TenderList: il filtresi normalize karşılaştırma yapıyor (savunma katmanı)
2026-08-29 02:40:22 +03:00

119 lines
4.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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 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)',
sektor: row.turu || 'Genel',
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();
}
}