Files
Public/apps/api/prisma/wash-bid-buckets.ts
T
daiyongkang 76f266645d Initial commit: 风影 OA 全栈源码(API/Web/Flutter/IM)
含 Phase 1.1 IM seq 排序、断线重连、多端已读同步与微信式语音转文字 UI。
排除 node_modules、构建产物、安装包与 .env 密钥。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-02 10:04:03 +00:00

132 lines
4.3 KiB
TypeScript
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.
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<string, unknown>;
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<string, Row[]>;
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<string>();
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());