Files
order_site/apps/backend/scripts/backfill-fulfillment-mode.ts
T
yml2213 fba0a13d44 fix(fulfillment): 履约回填改用 keyset 分页并补充待回填部分索引
- 迁移 108:order_items (id) WHERE fulfillment_mode IS NULL 部分索引
  生产已 CONCURRENTLY 预建;取 5000 条待回填 id 4.8s → 4.9ms
- reconcilePendingFulfillmentModes 增加 afterId / lastScannedId,批内固定 id > 游标
- 新增 resolveReconcileCursor:游标单调前进,未前进或不足一批即结束
- backfill-fulfillment-mode 支持 --afterId 断点续跑
2026-10-29 15:10:28 +08:00

89 lines
2.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 process from 'node:process'
import { closeDb, query } from '../src/db/client.js'
import {
getFulfillmentModeCounts,
reconcilePendingFulfillmentModes,
} from '../src/services/fulfillment/fulfillment-mode-service.js'
const options = parseOptions(process.argv.slice(2))
const batchSize = normalizeInteger(options.batchSize, 5000, 1, 50_000)
const maxBatches = normalizeInteger(options.maxBatches, 1000, 1, 10_000)
const afterId = normalizeInteger(options.afterId, 0, 0, Number.MAX_SAFE_INTEGER)
async function main() {
const column = await query<{ exists: boolean }>(
`
SELECT EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'order_items'
AND column_name = 'fulfillment_mode'
) AS exists
`,
)
if (!column.rows[0]?.exists) {
throw new Error('order_items.fulfillment_mode 不存在,请先执行 npm run db:migrate(迁移 105)')
}
const startedAt = Date.now()
const result = await reconcilePendingFulfillmentModes({
batchSize,
maxBatches,
reason: 'backfill_2026_10_23',
afterId,
})
const counts = await getFulfillmentModeCounts()
console.log(
JSON.stringify(
{
...result,
batchSize,
maxBatches,
counts,
durationMs: Date.now() - startedAt,
},
null,
2,
),
)
if (result.remainingCount > 0) {
console.error(`仍有 ${result.remainingCount} 个订单项未回填;请增大 --max-batches 或检查数据。`)
process.exitCode = 2
}
}
main()
.catch((error) => {
console.error(error instanceof Error ? error.message : error)
process.exitCode = 1
})
.finally(async () => {
await closeDb()
})
function parseOptions(args: string[]) {
const result: Record<string, string> = {}
for (const arg of args) {
if (!arg.startsWith('--')) continue
const body = arg.slice(2)
const index = body.indexOf('=')
if (index === -1) {
result[body] = 'true'
} else {
result[body.slice(0, index)] = body.slice(index + 1)
}
}
return result
}
function normalizeInteger(value: unknown, fallback: number, min: number, max: number) {
const parsed = Number(value)
if (!Number.isInteger(parsed)) return fallback
return Math.min(max, Math.max(min, parsed))
}