76f266645d
含 Phase 1.1 IM seq 排序、断线重连、多端已读同步与微信式语音转文字 UI。 排除 node_modules、构建产物、安装包与 .env 密钥。 Co-authored-by: Cursor <cursoragent@cursor.com>
132 lines
4.3 KiB
TypeScript
132 lines
4.3 KiB
TypeScript
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());
|