fix(consignment): 寄卖改价上限按单价计算并补充单价/合计提示
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
#!/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()
|
||||
}
|
||||
Reference in New Issue
Block a user