feat(admin): 订单列表只展示平台判断 SKU

This commit is contained in:
yml2213
2026-10-22 19:55:13 +08:00
parent 74b62f3494
commit c38cd8ca2d
6 changed files with 135 additions and 62 deletions
@@ -49,6 +49,7 @@ test('mapAdminOrderListItem formats itemTitle, skuNick, and summary correctly fo
assert.equal(mapped.itemTitle, '和平精英地铁逃生装备满钻低价苏尔南怒嚎暗潮手枪璀璨无暇1到7钻')
assert.equal(mapped.skuNick, '安卓血宴2钻')
assert.equal(mapped.matchSku, '安卓血宴2钻')
assert.equal(
mapped.itemSummary,
'和平精英地铁逃生装备满钻低价苏尔南怒嚎暗潮手枪璀璨无暇1到7钻 (安卓血宴2钻)',
@@ -59,6 +60,7 @@ test('mapAdminOrderListItem formats itemTitle, skuNick, and summary correctly fo
{
itemTitle: '和平精英地铁逃生装备满钻低价苏尔南怒嚎暗潮手枪璀璨无暇1到7钻',
skuNick: '安卓血宴2钻',
matchSku: '安卓血宴2钻',
quantity: 1,
},
])
@@ -133,6 +135,7 @@ test('mapAdminOrderListItem falls back to PDD goods_name/out_sku_sn when orderIt
assert.equal(mapped.itemTitle, '和平精英地铁逃生苏尔南暗潮怒嚎血宴')
assert.equal(mapped.skuNick, '安卓怒嚎3钻')
assert.equal(mapped.matchSku, '安卓怒嚎3钻')
assert.equal(mapped.itemCount, 1)
assert.equal(mapped.totalQuantity, 1)
})
@@ -211,4 +214,48 @@ test('抖音码速达:商品列展示内部 SKU,引流标题降级为 skuNic
assert.equal(mapped.skuNick, '【测试专用】和平精英皮肤小店入口出售返场吃鸡皮肤浪漫天命专用')
// 列表摘要同样以内部 SKU 为主
assert.match(summarizeOrderItems(orderItems, order), /^幸运币2个/)
// 列表只展示判断用的内部 SKU,不能退回引流标题
assert.equal(mapped.matchSku, '幸运币2个')
assert.equal(mapped.items?.[0]?.matchSku, '幸运币2个')
})
test('拼多多匹配 SKU 优先 sku_name,不用带地区的长 spec', () => {
const order: OrderListRow = {
id: 100,
provider: 'pdd',
platform: 'pdd',
shop_id: 'pdd-shop',
shop_name: '拼多多店铺',
platform_order_id: '260915-72034516082531',
order_status: 'paid',
pay_status: 'paid',
buyer_id: '',
buyer_name: '',
receiver_contact: '',
total_amount: 100,
currency: 'CNY',
raw_payload_json: {},
paid_at: '2026-10-09T04:55:05.000Z',
created_at: '2026-10-09T04:55:05.000Z',
updated_at: '2026-10-09T04:55:05.000Z',
}
const orderItems: OrderItemRow[] = [
{
id: 1,
order_id: 100,
sku_code: '安卓怒嚎1钻',
sku_name: '安卓怒嚎1钻',
quantity: 1,
spec_json: JSON.stringify({
itemTitle: '和平精英地铁逃生苏尔南暗潮怒嚎血宴',
skuNick: '安卓区(qv互通),苏尔南-怒嚎1钻',
}),
item_snapshot_json: JSON.stringify({}),
},
]
const mapped = mapAdminOrderListItem(order, { orderItems })
assert.equal(mapped.matchSku, '安卓怒嚎1钻')
assert.equal(mapped.items?.[0]?.matchSku, '安卓怒嚎1钻')
})
@@ -1,6 +1,7 @@
import { formatFenToAmount, normalizeFen } from '../../utils/money.js'
import {
resolveDisplayShopName,
resolveOrderItemMatchSku,
resolveOrderItemSkuNick,
resolveOrderItemTitle,
} from './admin-read-shared-helpers.js'
@@ -34,28 +35,34 @@ export function mapAdminOrderListItem(
let resolvedItemTitle = ''
let resolvedSkuNick = ''
let resolvedMatchSku = ''
let itemsList: AdminOrderItemSummary[] = []
if (normalizedItems.length > 0) {
itemsList = normalizedItems.map((orderItem) => {
const itTitle = resolveOrderItemTitle(orderItem, item, vouchers)
const itSkuNick = resolveOrderItemSkuNick(orderItem, item, vouchers)
const itMatchSku = resolveOrderItemMatchSku(orderItem, item, vouchers)
return {
itemTitle: itTitle,
skuNick: itSkuNick,
matchSku: itMatchSku,
quantity: Math.max(1, Number(orderItem.quantity || 1)),
}
})
resolvedItemTitle = itemsList[0]?.itemTitle || ''
resolvedSkuNick = itemsList[0]?.skuNick || ''
resolvedMatchSku = itemsList[0]?.matchSku || ''
} else {
resolvedItemTitle = resolveOrderItemTitle(null, item, vouchers)
resolvedSkuNick = resolveOrderItemSkuNick(null, item, vouchers)
if (resolvedItemTitle || resolvedSkuNick) {
resolvedMatchSku = resolveOrderItemMatchSku(null, item, vouchers)
if (resolvedItemTitle || resolvedSkuNick || resolvedMatchSku) {
itemsList = [
{
itemTitle: resolvedItemTitle,
skuNick: resolvedSkuNick,
matchSku: resolvedMatchSku,
quantity: 1,
},
]
@@ -99,6 +106,7 @@ export function mapAdminOrderListItem(
itemSummary,
itemTitle: resolvedItemTitle,
skuNick: resolvedSkuNick,
matchSku: resolvedMatchSku,
items: itemsList,
taskCount: tasks.length,
workOrderCount,
@@ -637,6 +637,41 @@ export function resolveOrderItemSkuNick(
return ''
}
/**
* 列表展示用的「平台判断 SKU」:只保留能用于匹配的短 SKU,不展示引流长标题。
* 抖音的内部 SKU 存在 itemTitle(resolveOrderItemTitle 已优先返回 code),
* 拼多多用 sku_name/sku_code 更干净,91/快手优先用 skuNick。
*/
export function resolveOrderItemMatchSku(
item: Partial<OrderItemRow> | null | undefined,
fallbackOrder?: Partial<OrderRow> | null | undefined,
vouchers?: KuaishouIndustryVoucherRow[] | null | undefined,
): string {
const provider = String(fallbackOrder?.provider || '')
.trim()
.toLowerCase()
const spec = item ? safeParseJson(item.spec_json) : {}
const snapshot = item ? safeParseJson(item.item_snapshot_json) : {}
const source = pickFirstNonEmpty([spec.source, snapshot.source]).toLowerCase()
const isDouyin = provider.includes('douyin') || source.includes('douyin')
const itemTitle = resolveOrderItemTitle(item, fallbackOrder, vouchers)
const skuNick = resolveOrderItemSkuNick(item, fallbackOrder, vouchers)
const skuName = String(item?.sku_name || '').trim()
const skuCode = String(item?.sku_code || '').trim()
if (isDouyin) {
// 抖音 skuNick 往往是引流标题,内部 SKU 才是判断依据。
return itemTitle || skuName || skuCode
}
if (provider === 'pdd') {
return skuName || skuCode || skuNick || itemTitle
}
return skuNick || skuName || skuCode || itemTitle
}
export function resolveOrderItemDeliveryMode(
tasks: TaskLike[] | null | undefined,
orderItemId: number | string,
@@ -9,6 +9,7 @@ export type AdminOrderFulfillmentProgress = {
export type AdminOrderItemSummary = {
itemTitle: string
skuNick: string
matchSku: string
quantity: number
}
@@ -35,6 +36,7 @@ export type AdminOrderListItem = {
itemSummary: string
itemTitle?: string
skuNick?: string
matchSku?: string
items?: AdminOrderItemSummary[]
taskCount: number
resourceStatus: string