Files
order_site/apps/backend/scripts/fix-consignment-price-cap.ts
T

124 lines
4.6 KiB
JavaScript
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.
#!/usr/bin/env node
/**
* 修复寄卖「改价上限」被寄卖数量放大的历史数据。
*
* 背景:模板驱动规格曾是 unit 计价模板「模板单价 × 寄卖数量」写进 price_cap_fen,
* 但售价/库存/匹配引擎都是单价口径,导致改价弹窗提示的上限是单价的 N 倍。
* 取价逻辑已改为按单价返回;本脚本把历史脏数据修正为单价。
*
* 只处理命中 bug 特征的行:price_cap_fen === 单价 × quantity 且 quantity > 1。
* 默认 dry-run(只打印),加 --apply 才写库。
*
* 用法(apps/backend 目录下):
* npx tsx scripts/fix-consignment-price-cap.ts
* npx tsx scripts/fix-consignment-price-cap.ts --apply
*/
import process from 'node:process'
import { closeDb, query } from '../src/db/client.js'
import { parseConsignmentSpecValues } from '../src/services/worker-platform/consignment-spec-service.js'
import { getWorkerConsignmentPriceSuggestion } from '../src/services/worker-platform/consignment-worker-service.js'
type CandidateRow = {
id: number
profile_id: number
quantity: number
price_fen: number
price_cap_fen: number
spec_values_json: unknown
}
type Fix = {
table: 'submissions' | 'inventories'
id: number
quantity: number
beforeFen: number
afterFen: number
}
const apply = process.argv.includes('--apply')
async function loadCandidates() {
const submissions = await query<CandidateRow>(
`SELECT id, profile_id, quantity, asking_price_fen AS price_fen, price_cap_fen, spec_values_json
FROM consignment_submissions
WHERE profile_id IS NOT NULL AND quantity > 1 AND price_cap_fen > 0
AND status IN ('pending_review', 'approved')
ORDER BY id`,
)
const inventories = await query<CandidateRow>(
`SELECT id, profile_id, quantity, sale_price_fen AS price_fen, price_cap_fen, spec_values_json
FROM consignment_inventories
WHERE profile_id IS NOT NULL AND quantity > 1 AND price_cap_fen > 0
ORDER BY id`,
)
return { submissions: submissions.rows, inventories: inventories.rows }
}
async function resolveFixes(table: Fix['table'], rows: CandidateRow[]): Promise<Fix[]> {
const fixes: Fix[] = []
for (const row of rows) {
const suggestion = await getWorkerConsignmentPriceSuggestion({
profileId: Number(row.profile_id),
values: parseConsignmentSpecValues(row.spec_values_json),
})
const unitCapFen = Math.max(0, Math.round(Number(suggestion.capFen || 0)))
// pg 的 BIGINT 默认返回字符串,这里统一转数字再比较
const quantity = Math.max(1, Math.round(Number(row.quantity || 1)))
const beforeFen = Math.max(0, Math.round(Number(row.price_cap_fen || 0)))
const priceFen = Math.max(0, Math.round(Number(row.price_fen || 0)))
// bug 特征:上限 = 单价 × 寄卖数量。用「售价」和「当前建议价」两种口径各判一次,
// 避免运营改过折扣后当前建议价与历史值不一致导致漏修。
const inflatedByPrice = priceFen > 0 && beforeFen === Math.round(priceFen * quantity)
const inflatedBySuggestion = unitCapFen > 0 && beforeFen === Math.round(unitCapFen * quantity)
if (!inflatedByPrice && !inflatedBySuggestion) continue
const afterFen = Math.max(0, Math.round(beforeFen / quantity))
if (afterFen <= 0 || afterFen === beforeFen) continue
fixes.push({
table,
id: Number(row.id),
quantity,
beforeFen,
afterFen,
})
}
return fixes
}
const yuan = (fen: number) => `¥${(fen / 100).toFixed(2)}`
try {
const { submissions, inventories } = await loadCandidates()
const fixes = [
...(await resolveFixes('submissions', submissions)),
...(await resolveFixes('inventories', inventories)),
]
console.log(`扫描:提报 ${submissions.length} 条 / 库存 ${inventories.length} 条(数量 > 1)`)
if (fixes.length === 0) {
console.log('没有命中需要修复的数据。')
} else {
console.log('需要修复:')
for (const fix of fixes) {
console.log(
` [${fix.table}] id=${fix.id} 数量=${fix.quantity} 上限 ${yuan(fix.beforeFen)} → ${yuan(fix.afterFen)}`,
)
}
}
if (!apply) {
console.log('\n当前为预览模式。确认后追加 `--apply` 执行写库。')
} else if (fixes.length > 0) {
for (const fix of fixes) {
const table =
fix.table === 'submissions' ? 'consignment_submissions' : 'consignment_inventories'
await query(`UPDATE ${table} SET price_cap_fen = $1 WHERE id = $2`, [fix.afterFen, fix.id])
}
console.log(`\n已修正 ${fixes.length} 条记录。`)
}
} finally {
await closeDb()
}