import { PrismaClient } from '@prisma/client'; import * as fs from 'fs'; const prisma = new PrismaClient(); const DUMP = '/opt/import/prod/oa_core.json'; type Row = Record; function str(v: unknown) { if (v == null) return ''; return String(v).trim(); } function officialNo(raw: unknown) { const n = str(raw).replace(/[))]+$/, '').trim(); if (!n || n === '无' || n === '-' || n === '—') return ''; return n; } function mapQual(raw: unknown) { const s = str(raw); if (!s) return ''; try { const arr = JSON.parse(s) as { item?: string }[]; const item = str(arr?.[0]?.item); if (!item || item === '无') return '无需资质'; if (item.includes('军')) return '军二'; if (item.includes('涉')) return '涉乙'; return item.slice(0, 20); } catch { if (s.includes('军')) return '军二'; if (s.includes('涉')) return '涉乙'; return ''; } } function mapStatus(row: Row) { const st = str(row.project_status); const win = str(row.winning_status); if (st === '中标' || (st === '' && win.includes('中标'))) return 'WON'; if (st === '未中' || (st === '' && win.includes('未中'))) return 'LOST'; if (st === '流标') return 'FAILED'; if (st === '弃标' || win.includes('弃标')) return 'TERMINATED'; return ''; } function isRealTender(row: Row) { return /^TB-\d/.test(str(row.system_number)) && Boolean(str(row.project_name)); } async function main() { const raw = JSON.parse(fs.readFileSync(DUMP, 'utf8')) as Record; const tenders = (raw.oa_tender || []).filter(isRealTender); const bids = await prisma.bidCase.findMany(); const users = await prisma.user.findMany({ select: { id: true, displayName: true } }); const userByName = new Map(users.map((u) => [u.displayName, u.id])); userByName.set('张靖妍', userByName.get('张婧妍') || ''); const testNos = ['SP-TEST-1788068192', 'SP-FIX-1788068273']; const tests = bids.filter((b) => testNos.includes(b.bidNo) || testNos.includes(b.externalNo || '')); const testIds = tests.map((b) => b.id); if (testIds.length) { await prisma.todoTask.deleteMany({ where: { bizId: { in: testIds } } }); await prisma.approvalTask.deleteMany({ where: { bizId: { in: testIds } } }); await prisma.bidCase.deleteMany({ where: { id: { in: testIds } } }); } const used = new Set(); const remaining = await prisma.bidCase.findMany(); let patched = 0; const moves: { name: string; from: string; to: string; no: string }[] = []; for (const t of tenders) { const name = str(t.project_name); const sys = str(t.system_number); const official = officialNo(t.project_number); const row = remaining.find((b) => b.name === name) || remaining.find((b) => b.bidNo === sys || b.externalNo === official || b.externalNo === str(t.project_number)); if (!row) continue; const status = mapStatus(t) || row.status; const creatorName = str(t.bid_prepare_person) === '张靖妍' ? '张婧妍' : str(t.bid_prepare_person); let bidNo = official || sys; if (used.has(bidNo)) bidNo = `${official || sys}-${sys.slice(-3)}`; used.add(bidNo); if (row.status !== status) { moves.push({ name: name.slice(0, 24), from: row.status, to: status, no: official || sys }); } const qual = mapQual(t.bid_doc_requirement); await prisma.bidCase.update({ where: { id: row.id }, data: { bidNo: `TMP-${row.id.replace(/-/g, '').slice(0, 18)}`, }, }); await prisma.bidCase.update({ where: { id: row.id }, data: { bidNo, externalNo: official || null, status, sourceType: 'FLOW', qualificationNeed: qual || row.qualificationNeed, tenderMethod: str(t.tender_method) || row.tenderMethod, applyMethod: row.applyMethod || (str(t.announcement_file) ? '网上申领' : row.applyMethod), delivererName: str(t.bid_delivery_person) || row.delivererName, createdById: userByName.get(creatorName) || row.createdById, }, }); patched += 1; } const after = await prisma.bidCase.groupBy({ by: ['status'], _count: true }); console.log( JSON.stringify( { deletedTests: testIds.length, patched, moves, counts: after }, null, 2, ), ); } main() .catch((e) => { console.error(e); process.exit(1); }) .finally(() => prisma.$disconnect());