feat(iit): harden QC pipeline consistency and release artifacts
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
This commit is contained in:
111
backend/scripts/backfill_equery_context.ts
Normal file
111
backend/scripts/backfill_equery_context.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
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();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user