feat(fulfillment): 统一中文内部 SKU 履约路由
This commit is contained in:
@@ -132,6 +132,32 @@ async function main() {
|
||||
ORDER BY count(*) DESC, internal_sku`,
|
||||
)
|
||||
|
||||
const internalChannels = await query<{
|
||||
channel: string
|
||||
source: string
|
||||
total: number
|
||||
}>(
|
||||
`SELECT channel, source, count(*)::int AS total
|
||||
FROM internal_sku_channels
|
||||
WHERE enabled = TRUE
|
||||
GROUP BY channel, source
|
||||
ORDER BY channel, source`,
|
||||
)
|
||||
|
||||
const canonicalChannelDuplicates = await query<{
|
||||
internal_sku: string
|
||||
channel: string
|
||||
total: number
|
||||
}>(
|
||||
`SELECT canonicalize_internal_sku(internal_sku) AS internal_sku,
|
||||
channel, count(*)::int AS total
|
||||
FROM internal_sku_channels
|
||||
WHERE enabled = TRUE
|
||||
GROUP BY canonicalize_internal_sku(internal_sku), channel
|
||||
HAVING count(*) > 1
|
||||
ORDER BY count(*) DESC, internal_sku, channel`,
|
||||
)
|
||||
|
||||
const assignedCarrierConflicts = await query<{
|
||||
order_item_id: number
|
||||
channel: string
|
||||
@@ -197,7 +223,9 @@ async function main() {
|
||||
rules: rules.rows,
|
||||
duplicateProducts: duplicateProducts.rows[0],
|
||||
capabilities: capabilities.rows,
|
||||
internalChannels: internalChannels.rows,
|
||||
internalChannelConflicts: internalChannelConflicts.rows,
|
||||
canonicalChannelDuplicates: canonicalChannelDuplicates.rows,
|
||||
assignedCarrierConflicts: assignedCarrierConflicts.rows,
|
||||
byProvider: byProvider.rows,
|
||||
},
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
/**
|
||||
* 从 PDD 纯数字内部 SKU 的订单项回填中文映射(只读扫描 + 幂等写入)。
|
||||
* 从平台 SKU 非中文、但商品名含中文的订单项回填中文内部 SKU 映射。
|
||||
*
|
||||
* 只处理 sku_name 含中文、sku_code 为纯数字的情况;
|
||||
* 找不到中文名的纯数字码会列出来,等运营补映射。
|
||||
* 数字码、英文码一视同仁;找不到中文名的平台码会列出来,等运营补映射。
|
||||
*
|
||||
* 用法:npx tsx scripts/backfill-internal-sku-mappings.ts [--apply]
|
||||
*/
|
||||
@@ -22,7 +21,7 @@ async function main() {
|
||||
SELECT o.provider, o.shop_id, oi.sku_code, MIN(oi.sku_name) AS sku_name, count(*)::int AS total
|
||||
FROM order_items oi
|
||||
JOIN orders o ON o.id = oi.order_id
|
||||
WHERE btrim(COALESCE(oi.manual_sku_key, '')) ~ '^[0-9]+$'
|
||||
WHERE COALESCE(canonicalize_internal_sku(oi.manual_sku_key), '') !~ '[一-龥]'
|
||||
AND btrim(COALESCE(oi.sku_name, '')) ~ '[一-龥]'
|
||||
GROUP BY 1, 2, 3
|
||||
ORDER BY 5 DESC
|
||||
@@ -38,7 +37,7 @@ async function main() {
|
||||
SELECT o.provider, o.shop_id, oi.sku_code, count(*)::int AS total
|
||||
FROM order_items oi
|
||||
JOIN orders o ON o.id = oi.order_id
|
||||
WHERE btrim(COALESCE(oi.manual_sku_key, '')) ~ '^[0-9]+$'
|
||||
WHERE COALESCE(canonicalize_internal_sku(oi.manual_sku_key), '') !~ '[一-龥]'
|
||||
AND btrim(COALESCE(oi.sku_name, '')) !~ '[一-龥]'
|
||||
GROUP BY 1, 2, 3
|
||||
ORDER BY 4 DESC
|
||||
|
||||
@@ -118,6 +118,62 @@ async function main() {
|
||||
)
|
||||
).rows
|
||||
|
||||
checks.canonicalChannelDuplicates = (
|
||||
await query<CountRow>(
|
||||
`SELECT canonicalize_internal_sku(internal_sku) AS label, count(*)::int AS total
|
||||
FROM internal_sku_channels
|
||||
WHERE enabled = TRUE
|
||||
GROUP BY canonicalize_internal_sku(internal_sku), channel
|
||||
HAVING count(*) > 1
|
||||
ORDER BY count(*) DESC, label`,
|
||||
)
|
||||
).rows
|
||||
|
||||
checks.incompleteCarrierConfigs = (
|
||||
await query<CountRow>(
|
||||
`SELECT channel AS label, count(*)::int AS total
|
||||
FROM internal_sku_channels
|
||||
WHERE enabled = TRUE
|
||||
AND (
|
||||
(
|
||||
channel = 'work_order'
|
||||
AND (
|
||||
COALESCE(config_json->>'workRuleKey', '') = ''
|
||||
OR NOT EXISTS (
|
||||
SELECT 1 FROM work_product_rules rule
|
||||
WHERE rule.rule_key = config_json->>'workRuleKey'
|
||||
AND rule.enabled = TRUE
|
||||
)
|
||||
)
|
||||
)
|
||||
OR (
|
||||
channel = 'manual'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM manual_fulfillment_configs cfg
|
||||
WHERE cfg.enabled = TRUE
|
||||
AND canonicalize_internal_sku(cfg.sku_key)
|
||||
= canonicalize_internal_sku(internal_sku_channels.internal_sku)
|
||||
)
|
||||
)
|
||||
)
|
||||
GROUP BY channel ORDER BY count(*) DESC`,
|
||||
)
|
||||
).rows
|
||||
|
||||
checks.pendingWithoutChineseInternalSku = (
|
||||
await query<CountRow>(
|
||||
`SELECT 'pending_without_chinese_internal_sku' AS label, count(*)::int AS total
|
||||
FROM order_items oi
|
||||
JOIN orders o ON o.id = oi.order_id
|
||||
WHERE oi.fulfillment_status = 'pending'
|
||||
AND ($1::boolean OR o.shop_id NOT LIKE 'sim-%')
|
||||
AND COALESCE(resolve_internal_sku(
|
||||
oi.manual_sku_key, oi.sku_name, oi.sku_code
|
||||
), '') = ''`,
|
||||
[includeDemo],
|
||||
)
|
||||
).rows[0]
|
||||
|
||||
checks.enabledLegacyRules = (
|
||||
await query<CountRow>(
|
||||
`SELECT match_key_type || '/' || channel AS label, count(*)::int AS total
|
||||
@@ -202,6 +258,12 @@ async function main() {
|
||||
failed.push('存在同时挂载多个真实履约载体的已分配订单项')
|
||||
if ((checks.internalChannelConflicts as CountRow[]).length > 0)
|
||||
failed.push('存在多个非兜底主渠道')
|
||||
if ((checks.canonicalChannelDuplicates as CountRow[]).length > 0)
|
||||
failed.push('存在规范化后重复的内部 SKU 渠道配置')
|
||||
if ((checks.incompleteCarrierConfigs as CountRow[]).length > 0)
|
||||
failed.push('存在缺少 workRuleKey 或人工履约配置的启用渠道')
|
||||
if ((checks.pendingWithoutChineseInternalSku as CountRow).total > 0)
|
||||
failed.push('存在无法归一到中文内部 SKU 的 pending 订单项')
|
||||
if ((checks.enabledLegacyRules as CountRow[]).length > 0)
|
||||
failed.push('仍有启用的 sku_code 兼容规则,未完全收敛到内部 SKU')
|
||||
if ((checks.pendingWithoutReason as CountRow).total > 0)
|
||||
|
||||
Reference in New Issue
Block a user