Implement IIT quality workflow hardening across eQuery deduplication, guard metadata validation, timeline/readability improvements, and chat evidence fallbacks, then synchronize release and development documentation for deployment handoff. Includes migration/scripts for open eQuery dedupe guards, orchestration/status semantics, report/tool readability fixes, and updated module status plus deployment checklist. Made-with: Cursor
112 lines
3.4 KiB
TypeScript
112 lines
3.4 KiB
TypeScript
import { PrismaClient } from '@prisma/client';
|
||
|
||
const prisma = new PrismaClient();
|
||
|
||
async function main() {
|
||
const projectId = process.argv[2];
|
||
if (!projectId) {
|
||
throw new Error('Usage: npx tsx scripts/backfill_equery_context.ts <projectId>');
|
||
}
|
||
|
||
const beforeRows = await prisma.$queryRawUnsafe<Array<{ total: bigint; missing: bigint }>>(`
|
||
SELECT
|
||
COUNT(*)::bigint AS total,
|
||
COUNT(*) FILTER (
|
||
WHERE COALESCE(event_id, '') = '' OR COALESCE(form_name, '') = ''
|
||
)::bigint AS missing
|
||
FROM iit_schema.equery
|
||
WHERE project_id = $1
|
||
`, projectId);
|
||
|
||
// Phase 1: 严格匹配(record + rule + field)
|
||
const strictUpdated = await prisma.$executeRawUnsafe(`
|
||
WITH matched AS (
|
||
SELECT
|
||
e.id,
|
||
fs.event_id,
|
||
fs.form_name,
|
||
ROW_NUMBER() OVER (
|
||
PARTITION BY e.id
|
||
ORDER BY fs.last_qc_at DESC NULLS LAST, fs.updated_at DESC NULLS LAST
|
||
) AS rn
|
||
FROM iit_schema.equery e
|
||
JOIN iit_schema.qc_field_status fs
|
||
ON fs.project_id = e.project_id
|
||
AND fs.record_id = e.record_id
|
||
AND COALESCE(fs.rule_name, '') = COALESCE(e.category, '')
|
||
AND COALESCE(fs.field_name, '') = COALESCE(e.field_name, '')
|
||
WHERE e.project_id = $1
|
||
AND (COALESCE(e.event_id, '') = '' OR COALESCE(e.form_name, '') = '')
|
||
)
|
||
UPDATE iit_schema.equery e
|
||
SET
|
||
event_id = COALESCE(NULLIF(e.event_id, ''), matched.event_id),
|
||
form_name = COALESCE(NULLIF(e.form_name, ''), matched.form_name),
|
||
updated_at = NOW()
|
||
FROM matched
|
||
WHERE e.id = matched.id
|
||
AND matched.rn = 1
|
||
`, projectId);
|
||
|
||
// Phase 2: 容错匹配(record + rule),用于历史“拆字段”eQuery
|
||
const relaxedUpdated = await prisma.$executeRawUnsafe(`
|
||
WITH matched AS (
|
||
SELECT
|
||
e.id,
|
||
fs.event_id,
|
||
fs.form_name,
|
||
ROW_NUMBER() OVER (
|
||
PARTITION BY e.id
|
||
ORDER BY fs.last_qc_at DESC NULLS LAST, fs.updated_at DESC NULLS LAST
|
||
) AS rn
|
||
FROM iit_schema.equery e
|
||
JOIN iit_schema.qc_field_status fs
|
||
ON fs.project_id = e.project_id
|
||
AND fs.record_id = e.record_id
|
||
AND COALESCE(fs.rule_name, '') = COALESCE(e.category, '')
|
||
WHERE e.project_id = $1
|
||
AND (COALESCE(e.event_id, '') = '' OR COALESCE(e.form_name, '') = '')
|
||
)
|
||
UPDATE iit_schema.equery e
|
||
SET
|
||
event_id = COALESCE(NULLIF(e.event_id, ''), matched.event_id),
|
||
form_name = COALESCE(NULLIF(e.form_name, ''), matched.form_name),
|
||
updated_at = NOW()
|
||
FROM matched
|
||
WHERE e.id = matched.id
|
||
AND matched.rn = 1
|
||
`, projectId);
|
||
|
||
const afterRows = await prisma.$queryRawUnsafe<Array<{ total: bigint; missing: bigint }>>(`
|
||
SELECT
|
||
COUNT(*)::bigint AS total,
|
||
COUNT(*) FILTER (
|
||
WHERE COALESCE(event_id, '') = '' OR COALESCE(form_name, '') = ''
|
||
)::bigint AS missing
|
||
FROM iit_schema.equery
|
||
WHERE project_id = $1
|
||
`, projectId);
|
||
|
||
const before = beforeRows[0];
|
||
const after = afterRows[0];
|
||
console.log(JSON.stringify({
|
||
projectId,
|
||
total: Number(before.total),
|
||
missingBefore: Number(before.missing),
|
||
strictUpdatedRows: strictUpdated,
|
||
relaxedUpdatedRows: relaxedUpdated,
|
||
updatedRows: Number(strictUpdated) + Number(relaxedUpdated),
|
||
missingAfter: Number(after.missing),
|
||
}, null, 2));
|
||
}
|
||
|
||
main()
|
||
.catch((e) => {
|
||
console.error(e);
|
||
process.exit(1);
|
||
})
|
||
.finally(async () => {
|
||
await prisma.$disconnect();
|
||
});
|
||
|