// 예비창업자 자격 + 현재 열린 공고 전체를 CSV 로 내보낸다(신청 추적용). // 실행: LD_LIBRARY_PATH=$ORACLE_IC_LIB_DIR node scripts/export_eligible_csv.js import { writeFile, mkdir } from 'node:fs/promises'; import { withConnection, closePool, oracledb } from '../src/db.js'; const SQL = ` SELECT * FROM ( SELECT title, category, agency, source_code, detail_url, apply_start, apply_end, CASE WHEN apply_end IS NULL THEN 9999 ELSE (apply_end - TRUNC(SYSDATE)) END AS dleft, ROW_NUMBER() OVER (PARTITION BY title ORDER BY CASE source_code WHEN 'kstartup' THEN 1 WHEN 'bizinfo' THEN 2 ELSE 3 END) rn FROM gov_opportunity WHERE (apply_end IS NULL OR apply_end >= TRUNC(SYSDATE)) AND ( target LIKE '%' || :k1 || '%' OR target LIKE '%' || :k2 || '%' OR DBMS_LOB.INSTR(body_text, :k1) > 0 OR DBMS_LOB.INSTR(body_text, :k2) > 0 ) ) WHERE rn = 1 ORDER BY dleft ASC, source_code`; function csvCell(v) { if (v == null) return ''; const s = String(v).replace(/"/g, '""').replace(/\r?\n/g, ' '); return `"${s}"`; } function ymd(d) { return d ? new Date(d).toISOString().slice(0, 10) : ''; } function region(title) { const m = /^\[([^\]]{1,8})\]/.exec(title); return m ? m[1] : ''; } const rows = await withConnection(async (conn) => { const r = await conn.execute( SQL, { k1: '예비창업', k2: '예비 창업' }, { outFormat: oracledb.OUT_FORMAT_OBJECT } ); return r.rows; }); await closePool(); const header = ['신청완료', 'D-day', '마감일', '지역', '분야', '주관기관', '소스', '제목', '링크']; const lines = [header.map(csvCell).join(',')]; for (const x of rows) { lines.push( [ '', x.DLEFT === 9999 ? '상시' : `D-${x.DLEFT}`, x.DLEFT === 9999 ? '상시/예산소진' : ymd(x.APPLY_END), region(x.TITLE), x.CATEGORY || '', x.AGENCY || '', x.SOURCE_CODE, x.TITLE, x.DETAIL_URL || '', ] .map(csvCell) .join(',') ); } await mkdir(new URL('../exports/', import.meta.url), { recursive: true }); const out = new URL('../exports/eligible_opportunities.csv', import.meta.url); // 엑셀 한글 깨짐 방지 BOM await writeFile(out, '' + lines.join('\n'), 'utf8'); console.log(`내보냄: ${rows.length}건 → government/exports/eligible_opportunities.csv`);