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:
+44
-6
@@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import { sektorBul } from './siniflandir.mjs';
|
import { sektorKarar } from './siniflandir.mjs';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { titleCaseTr } from './tr-text.mjs';
|
import { titleCaseTr } from './tr-text.mjs';
|
||||||
@@ -28,13 +28,37 @@ function parseDetail(detailJson) {
|
|||||||
const dt = d.DogrudanTeminBilgileri || {};
|
const dt = d.DogrudanTeminBilgileri || {};
|
||||||
const idare = d.IdareBilgileri || {};
|
const idare = d.IdareBilgileri || {};
|
||||||
const soz = d.SozlesmeBilgileri?.SozlesmeBilgisiList?.[0] || null;
|
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 {
|
return {
|
||||||
yasaMaddesi: dt.YasaKapsamiTeminMaddesi || null,
|
yasaMaddesi: dt.YasaKapsamiTeminMaddesi || null,
|
||||||
|
istisnaDayanagi: dt.IstisnaAliminDayanagi || null,
|
||||||
durum: dt.DtDurumu || null,
|
durum: dt.DtDurumu || null,
|
||||||
|
// BransKodList aslında OKAS (CPV) kodudur — idarenin kendi sınıflandırması.
|
||||||
bransKodlari: dt.BransKodList || [],
|
bransKodlari: dt.BransKodList || [],
|
||||||
kismiTeklif: dt.KismiTeklif || null,
|
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,
|
idareTam: idare.Idare || null,
|
||||||
|
ustIdare: idare.UstIdare || null,
|
||||||
enUstIdare: idare.EnUstIdare || 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,
|
kazananFirma: soz?.IstekliAdi || null,
|
||||||
sozlesmeBedeli: soz?.SozlesmeBedeli || null,
|
sozlesmeBedeli: soz?.SozlesmeBedeli || null,
|
||||||
sozlesmeTarihi: soz?.SozlesmeTarihi || null,
|
sozlesmeTarihi: soz?.SozlesmeTarihi || null,
|
||||||
@@ -71,18 +95,32 @@ function mapDtRow(row) {
|
|||||||
sehir: row.ili && !row.ili.startsWith('Plaka') ? titleCaseTr(row.ili) : '',
|
sehir: row.ili && !row.ili.startsWith('Plaka') ? titleCaseTr(row.ili) : '',
|
||||||
bolge: '',
|
bolge: '',
|
||||||
tarih: row.dt_tarihi || '',
|
tarih: row.dt_tarihi || '',
|
||||||
saat: '',
|
saat: (det.tarihSaat || '').split(' ')[1] || '',
|
||||||
alimTuru: 'Doğrudan Temin',
|
alimTuru: 'Doğrudan Temin',
|
||||||
usul: det.yasaMaddesi ? `Doğrudan Temin (4734 ${det.yasaMaddesi})` : 'Doğrudan Temin (4734 22/d)',
|
usul: det.yasaMaddesi
|
||||||
// Sektör ihale TÜRÜ değildir; başlık + kalem adlarından çıkarılır.
|
? `Doğrudan Temin (4734 ${det.yasaMaddesi})`
|
||||||
sektor: sektorBul(row.isin_adi || '', (det.bransKodlari || []).join(' '), row.turu || ''),
|
: 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',
|
brans: (det.bransKodlari || []).join(', ') || row.turu || 'Genel',
|
||||||
kurumTipi: classifyKurum(kurum),
|
kurumTipi: classifyKurum(kurum),
|
||||||
eklenmeZamani: row.yayim_tarihi ? `Yayım: ${row.yayim_tarihi}` : '',
|
eklenmeZamani: row.yayim_tarihi ? `Yayım: ${row.yayim_tarihi}` : '',
|
||||||
isYeni: false,
|
isYeni: false,
|
||||||
isLiveFeed: true,
|
isLiveFeed: true,
|
||||||
aciliyetBadge: null,
|
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,
|
aciklama: null,
|
||||||
yaklasikMaliyet: null,
|
yaklasikMaliyet: null,
|
||||||
teslimatSuresi: null,
|
teslimatSuresi: null,
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ const argVal = (name, def) => {
|
|||||||
return i >= 0 && args[i + 1] ? Number(args[i + 1]) : def;
|
return i >= 0 && args[i + 1] ? Number(args[i + 1]) : def;
|
||||||
};
|
};
|
||||||
const MAX_PAGES = argVal('--pages', 10);
|
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_BASE_MS = 2000;
|
||||||
const DELAY_JITTER_MS = 1500;
|
const DELAY_JITTER_MS = 1500;
|
||||||
|
|
||||||
@@ -265,8 +265,12 @@ async function run() {
|
|||||||
// 2) Eksik detaylar
|
// 2) Eksik detaylar
|
||||||
const pending = db
|
const pending = db
|
||||||
.prepare(
|
.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
|
`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);
|
.all(MAX_DETAILS);
|
||||||
console.log(`[dt-worker] ${pending.length} detay çekilecek`);
|
console.log(`[dt-worker] ${pending.length} detay çekilecek`);
|
||||||
|
|||||||
@@ -101,7 +101,9 @@ function dtTara() {
|
|||||||
dtCalisiyor = true;
|
dtCalisiyor = true;
|
||||||
dtSonTur = { baslangic: new Date().toISOString() };
|
dtSonTur = { baslangic: new Date().toISOString() };
|
||||||
const pages = process.env.DT_PAGES || '5';
|
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)`);
|
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], {
|
const p = spawn(process.execPath, [path.join(__dirname, 'dt-worker.mjs'), '--pages', pages, '--details', details], {
|
||||||
|
|||||||
@@ -118,8 +118,26 @@ export function ilanKonusu(metin) {
|
|||||||
const OKAS_GRUP = {
|
const OKAS_GRUP = {
|
||||||
// 39 — mobilya / ev aletleri / temizlik ürünleri aynı bölümde
|
// 39 — mobilya / ev aletleri / temizlik ürünleri aynı bölümde
|
||||||
'3971': 'Isıtma & İklimlendirme', // elektrikli ev aletleri (buzdolabı, klima)
|
'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
|
'3983': 'Temizlik & Hijyen', // temizlik ürünleri
|
||||||
'3984': 'Temizlik & Hijyen',
|
'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
|
// 79 — iş hizmetleri: güvenlik, matbaa, reklam, danışmanlık, personel
|
||||||
'7971': 'Güvenlik Sistemleri',
|
'7971': 'Güvenlik Sistemleri',
|
||||||
'7972': 'Güvenlik Sistemleri',
|
'7972': 'Güvenlik Sistemleri',
|
||||||
|
|||||||
Reference in New Issue
Block a user