feat: DT detaylari tam cekiliyor — BransKodList=OKAS ile siniflandirma, ilan belgeleri, en yeni kayitlar oncelikli detay kuyrugu, butce 30->250

This commit is contained in:
2026-08-30 13:20:28 +03:00
parent d9ca8a5b52
commit f98568da8e
4 changed files with 71 additions and 9 deletions
+44 -6
View File
@@ -4,7 +4,7 @@
*/
import fs from 'node:fs';
import { sektorBul } from './siniflandir.mjs';
import { sektorKarar } from './siniflandir.mjs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { titleCaseTr } from './tr-text.mjs';
@@ -28,13 +28,37 @@ function parseDetail(detailJson) {
const dt = d.DogrudanTeminBilgileri || {};
const idare = d.IdareBilgileri || {};
const soz = d.SozlesmeBilgileri?.SozlesmeBilgisiList?.[0] || null;
const ilan = d.IlanBilgileri || {};
const duyuru = ilan.DogrudanTeminIlanBilgisiList?.[0] || null;
const sonuc = ilan.SonucIlanBilgisiList?.[0] || null;
const iptal = ilan.IptalIlanBilgisiList?.[0] || null;
return {
yasaMaddesi: dt.YasaKapsamiTeminMaddesi || null,
istisnaDayanagi: dt.IstisnaAliminDayanagi || null,
durum: dt.DtDurumu || null,
// BransKodList aslında OKAS (CPV) kodudur — idarenin kendi sınıflandırması.
bransKodlari: dt.BransKodList || [],
kismiTeklif: dt.KismiTeklif || null,
kisimSayisi: dt.KisimSayisi || null,
ilaninSekli: dt.IlaninSekli || null,
tarihSaat: dt.DtTarihSaati || null,
eIhale: Boolean(dt.EIhale),
duyuruYapilacak: Boolean(dt.DogrudanTeminDuyurusuYapilacakMi),
sozlesmeTasarisiVar: Boolean(dt.DogrudanTeminSozlesmeTasarisiVarMi),
iptalNedeni: dt.IptalNedeni || null,
iptalTarihi: dt.IptalTarihi || null,
idareTam: idare.Idare || null,
ustIdare: idare.UstIdare || null,
enUstIdare: idare.EnUstIdare || null,
ili: idare.Ili || null,
// İlan belgeleri — EncIlanId ile EKAP'ta görüntülenir
// EncIlanId → EKAP ilan görüntüleme adresi. Doğrulanmış tek yol
// EKAP arayüzü olduğundan bağlantıyı kullanıcının tarayıcısında açarız.
ilanlar: [
duyuru && { tip: 'Doğrudan Temin Duyurusu', tarih: duyuru.IlanTarihi, encId: duyuru.EncIlanId },
sonuc && { tip: 'Sonuç İlanı', tarih: sonuc.IlanTarihi, encId: sonuc.EncIlanId },
iptal && { tip: 'İptal İlanı', tarih: iptal.IlanTarihi, encId: iptal.EncIlanId },
].filter(Boolean),
kazananFirma: soz?.IstekliAdi || null,
sozlesmeBedeli: soz?.SozlesmeBedeli || null,
sozlesmeTarihi: soz?.SozlesmeTarihi || null,
@@ -71,18 +95,32 @@ function mapDtRow(row) {
sehir: row.ili && !row.ili.startsWith('Plaka') ? titleCaseTr(row.ili) : '',
bolge: '',
tarih: row.dt_tarihi || '',
saat: '',
saat: (det.tarihSaat || '').split(' ')[1] || '',
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 || ''),
usul: det.yasaMaddesi
? `Doğrudan Temin (4734 ${det.yasaMaddesi})`
: det.istisnaDayanagi ? `Doğrudan Temin (istisna ${det.istisnaDayanagi})` : 'Doğrudan Temin',
// BransKodList = OKAS kodu → idarenin kendi sınıflandırması, başlıktan
// güvenilir. Kod yoksa iş adına düşülür.
sektor: sektorKarar(
(det.bransKodlari || []).map((k) => ({ kod: k })),
row.isin_adi || '',
),
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: [],
// OKAS kalemleri — arayüzde kod+ad gösterilebilsin
items: (det.bransKodlari || []).map((k, i) => ({
id: `okas-${i}`, kod: k, ad: `OKAS ${k}`, miktar: null, birim: null, birimFiyat: null,
})),
ilanBelgeleri: det.ilanlar || [],
ilaninSekli: det.ilaninSekli || null,
kismiTeklif: det.kismiTeklif || null,
eIhale: det.eIhale || false,
iptalNedeni: det.iptalNedeni || null,
aciklama: null,
yaklasikMaliyet: null,
teslimatSuresi: null,
+6 -2
View File
@@ -36,7 +36,7 @@ const argVal = (name, def) => {
return i >= 0 && args[i + 1] ? Number(args[i + 1]) : def;
};
const MAX_PAGES = argVal('--pages', 10);
const MAX_DETAILS = argVal('--details', 40);
const MAX_DETAILS = argVal('--details', 250);
const DELAY_BASE_MS = 2000;
const DELAY_JITTER_MS = 1500;
@@ -265,8 +265,12 @@ async function run() {
// 2) Eksik detaylar
const pending = db
.prepare(
// EN YENİ kayıtlar önce: arayüz en yeni 5000'i gösteriyor; varsayılan
// (rowid) sırada en eskiler çekildiği için gösterilen hiçbir kayda
// detay ulaşmıyordu.
`SELECT dogrudan_temin_id, idare_id FROM dt_tenders
WHERE detail_json IS NULL AND idare_id != '' LIMIT ?`,
WHERE detail_json IS NULL AND idare_id != ''
ORDER BY rowid DESC LIMIT ?`,
)
.all(MAX_DETAILS);
console.log(`[dt-worker] ${pending.length} detay çekilecek`);
+3 -1
View File
@@ -101,7 +101,9 @@ function dtTara() {
dtCalisiyor = true;
dtSonTur = { baslangic: new Date().toISOString() };
const pages = process.env.DT_PAGES || '5';
const details = process.env.DT_DETAILS || '30';
// 30 detay/4 saat ile 10 bin kayıt 55 günde dolar; sektör sınıflandırması
// detaya bağlı olduğu için bütçe yükseltildi.
const details = process.env.DT_DETAILS || '250';
console.log(`[scheduler] doğrudan temin taraması başlıyor (${pages} sayfa, ${details} detay)`);
const p = spawn(process.execPath, [path.join(__dirname, 'dt-worker.mjs'), '--pages', pages, '--details', details], {
+18
View File
@@ -118,8 +118,26 @@ export function ilanKonusu(metin) {
const OKAS_GRUP = {
// 39 — mobilya / ev aletleri / temizlik ürünleri aynı bölümde
'3971': 'Isıtma & İklimlendirme', // elektrikli ev aletleri (buzdolabı, klima)
'3922': 'Temizlik & Hijyen', // süpürge, fırça, temizlik gereçleri
'3953': 'Temizlik & Hijyen', // paspas, yer örtüsü
'3983': 'Temizlik & Hijyen', // temizlik ürünleri
'3984': 'Temizlik & Hijyen',
// 33 — tıbbi bölümde hijyenik kâğıt da var
'3376': 'Temizlik & Hijyen', // tuvalet kâğıdı, kâğıt havlu
'3377': 'Temizlik & Hijyen',
// 50 — bakım/onarım hizmetleri: taşıt onarımı ayrılır
'5011': 'Otomotiv & İş Makinesi',
'5012': 'Otomotiv & İş Makinesi',
'5013': 'Otomotiv & İş Makinesi',
'5014': 'Otomotiv & İş Makinesi',
// 03 — tarımsal ürünlerin yenilebilir olanları gıdadır
'0311': 'Gıda & Catering',
'0313': 'Gıda & Catering',
'0314': 'Gıda & Catering',
'0321': 'Gıda & Catering',
'0322': 'Gıda & Catering',
'0331': 'Gıda & Catering',
'0332': 'Gıda & Catering',
// 79 — iş hizmetleri: güvenlik, matbaa, reklam, danışmanlık, personel
'7971': 'Güvenlik Sistemleri',
'7972': 'Güvenlik Sistemleri',