Files
order_site/apps/backend/scripts/cleanup-legacy-sku-code-rules.ts
T

135 lines
3.8 KiB
TypeScript

/**
* 把旧的 sku_code 渠道规则转换为 internal_sku 全局规则,并停用旧规则。
*
* 用法:
* npx tsx scripts/cleanup-legacy-sku-code-rules.ts
* npx tsx scripts/cleanup-legacy-sku-code-rules.ts --apply
*/
import { closeDb, query } from '../src/db/client.js'
type LegacyRule = {
id: number
provider: string
shop_id: string
match_key: string
channel: string
priority: number
source: string
}
async function resolveInternalSku(rule: LegacyRule): Promise<string> {
if (rule.channel === 'affiliate_dash') {
const product = await query<{ canonical_name: string }>(
`SELECT canonical_name FROM product_catalog
WHERE canonical_sku = $1 AND status = 'active' AND btrim(canonical_name) <> ''
LIMIT 1`,
[`aff:${rule.match_key}`],
)
if (product.rows[0]?.canonical_name) return String(product.rows[0].canonical_name).trim()
}
const params: unknown[] = [rule.match_key]
let scope = ''
if (rule.provider) {
params.push(rule.provider)
scope += ` AND o.provider = $${params.length}`
}
if (rule.shop_id) {
params.push(rule.shop_id)
scope += ` AND o.shop_id = $${params.length}`
}
const items = await query<{ internal_sku: string }>(
`
SELECT DISTINCT btrim(oi.manual_sku_key) AS internal_sku
FROM order_items oi
JOIN orders o ON o.id = oi.order_id
WHERE oi.sku_code = $1
AND btrim(COALESCE(oi.manual_sku_key, '')) ~ '[㐀-䶿一-鿿豈-﫿𠀀-𯨟𰀀-𲎯]'
${scope}
LIMIT 2
`,
params,
)
const values = items.rows.map((row) => String(row.internal_sku || '').trim()).filter(Boolean)
return values.length === 1 ? values[0]! : ''
}
async function main() {
const apply = process.argv.includes('--apply')
const rules = await query<LegacyRule>(
`
SELECT id, provider, shop_id, match_key, channel, priority, source
FROM sku_channel_rules
WHERE match_key_type = 'sku_code' AND enabled = TRUE
ORDER BY id
`,
)
const converted: Array<{ id: number; matchKey: string; internalSku: string; channel: string }> =
[]
const unresolved: LegacyRule[] = []
for (const rule of rules.rows) {
const internalSku = await resolveInternalSku(rule)
if (!internalSku) {
unresolved.push(rule)
continue
}
converted.push({ id: rule.id, matchKey: rule.match_key, internalSku, channel: rule.channel })
if (!apply) continue
await query(
`UPDATE internal_sku_channels
SET is_fallback = TRUE,
priority = GREATEST(priority, 90),
updated_at = NOW()
WHERE btrim(internal_sku) = $1
AND channel <> $2
AND enabled = TRUE
AND is_fallback = FALSE`,
[internalSku, rule.channel],
)
await query(
`
INSERT INTO internal_sku_channels (
internal_sku, channel, priority, is_fallback, enabled, source
) VALUES ($1, $2, $3, FALSE, TRUE, 'legacy-rule')
ON CONFLICT (internal_sku, channel) DO UPDATE SET
priority = LEAST(internal_sku_channels.priority, EXCLUDED.priority),
is_fallback = FALSE,
enabled = TRUE,
updated_at = NOW()
`,
[internalSku, rule.channel, rule.priority],
)
await query(
`UPDATE sku_channel_rules
SET enabled = FALSE,
note = CONCAT(COALESCE(note, ''), ' [已迁移 internal_sku:', $2::text, ']'),
updated_at = NOW()
WHERE id = $1`,
[rule.id, internalSku],
)
}
console.log(
JSON.stringify(
{
apply,
legacyRules: rules.rows.length,
convertible: converted.length,
converted,
unresolved,
},
null,
2,
),
)
}
main()
.catch((error) => {
console.error(error)
process.exitCode = 1
})
.finally(() => closeDb())