perf: 筛选统计去掉 JSON 兜底,并补上 orders 覆盖索引

去掉 work_orders 堆访问后,JOIN 会把 orders 整表扫进 Hash Right Join。
领取页在快照新鲜时不再打上游;快手出网统一 10 秒超时。倒计时改为自适应间隔。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
yml2213
2026-10-01 15:05:38 +08:00
co-authored by Cursor
parent f4c405d51a
commit 1239393fc2
20 changed files with 404 additions and 65 deletions
@@ -23,6 +23,23 @@ test('保留清理必须覆盖 work_order_events,否则这张只增不减的
)
})
test('保留任务要回填漏写的付款派生列,并在有命中时打日志', async () => {
const src = await readJobSource()
assert.ok(
src.includes('source_order_amount_fen IS NULL'),
'只处理派生列为空的行,避免把合法 0 或已落列的金额改掉',
)
assert.ok(
src.includes("material_json->'source'->>'orderAmountFen'"),
'回填口径必须与 083 / resolveSourceOrderAmountFen 相同',
)
assert.ok(src.includes("~ '^-?[0-9]{1,15}$'"), '超长数字不能 ::bigint,否则整次保留任务失败')
assert.ok(
src.includes('source_order_amount_fen 出现漏写'),
'有命中必须打日志,否则金额静默回退 orders.total_amount',
)
})
test('保留清理默认窗口:敏感报文/事件 180 天,审计 365 天,工单事件 180 天', async () => {
const defaults = await readFile(new URL('../../config/defaults.ts', import.meta.url), 'utf8')
for (const line of [
@@ -1,5 +1,6 @@
import { query } from '../../db/client.js'
import { runtimeConfig } from '../../config/runtime.js'
import { logWarn } from '../../utils/logger.js'
import { getWorkerProductMatchConfig } from '../worker-platform/worker-product-match-config-service.js'
import { pruneWorkProductMatchLogs } from '../../repositories/worker-platform/product-match-repo.js'
@@ -13,6 +14,7 @@ export type DataRetentionResult = {
workOrderEventRows: number
matchLogRows: number
matchLogDeletedRows: number
sourceOrderAmountBackfillRows: number
}
/** 清理敏感原始报文并删除已过保留期的事件/审计记录。 */
@@ -45,6 +47,14 @@ export async function runDataRetentionJob(): Promise<DataRetentionResult> {
const webhookEventRows = await deleteOldRowsInBatches('webhook_events', webhookEventDays)
const auditLogRows = await deleteOldRowsInBatches('admin_audit_logs', auditLogDays)
const workOrderEventRows = await deleteOldRowsInBatches('work_order_events', workOrderEventDays)
// 统计热路径已不再读 material_json。若将来有第三个写入点忘了维护派生列,
// 金额会静默回退 orders.total_amount。这里把漏写补上并打日志,让口径变化可见。
const sourceOrderAmountBackfillRows = await backfillMissingSourceOrderAmount()
if (sourceOrderAmountBackfillRows > 0) {
logWarn('[data-retention]', 'source_order_amount_fen 出现漏写,已按 JSON 回填', {
rows: sourceOrderAmountBackfillRows,
})
}
return {
status: 'ok',
@@ -56,8 +66,23 @@ export async function runDataRetentionJob(): Promise<DataRetentionResult> {
webhookEventRows,
auditLogRows,
workOrderEventRows,
sourceOrderAmountBackfillRows,
}
}
/** 只补「列为 NULL 且 JSON 里仍有 1~15 位整数」的行。正常应为 0。 */
async function backfillMissingSourceOrderAmount() {
const result = await query(
`
UPDATE work_orders
SET source_order_amount_fen = (material_json->'source'->>'orderAmountFen')::bigint
WHERE source_order_amount_fen IS NULL
AND COALESCE(material_json->'source'->>'orderAmountFen', '') ~ '^-?[0-9]{1,15}$'
`,
)
return result.rowCount || 0
}
async function scrubJsonColumnInBatches(
table: 'orders' | 'kuaishou_industry_vouchers' | 'work_product_match_logs',
dateColumn: 'created_at' | 'updated_at',
@@ -164,6 +164,7 @@ async function runJob(job: JsonObject, { manual = false }: { manual?: boolean }
taskEventRows: Number(summary.taskEventRows || 0),
webhookEventRows: Number(summary.webhookEventRows || 0),
auditLogRows: Number(summary.auditLogRows || 0),
sourceOrderAmountBackfillRows: Number(summary.sourceOrderAmountBackfillRows || 0),
}
: {}),
})