From b7d246e5b93d24c60f9d6b6c20ca49ad4e8c4b1e Mon Sep 17 00:00:00 2001 From: yml2213 Date: Tue, 15 Sep 2026 13:03:47 +0800 Subject: [PATCH] =?UTF-8?q?fix(admin):=20=E4=BB=BB=E5=8A=A1=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E5=95=86=E5=93=81=E5=90=8D=E4=B8=8E=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E5=99=A8=E6=A0=87=E7=AD=BE=E6=94=B9=E4=B8=BA=E4=B8=AD=E6=96=87?= =?UTF-8?q?=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 建单时不再把英文 sku 写入商品名,列表按映射反查中文名,并将 Affiliate Dash 等标签本地化。 Co-authored-by: Cursor --- .../src/services/admin/admin-read-service.ts | 11 +++++- .../services/admin/admin-task-read-helpers.ts | 12 +++++- .../product-resolution-service.test.ts | 20 ++++++++++ .../fulfillment/product-resolution-service.ts | 5 ++- .../platforms/affiliate-dash/sku-mapping.ts | 39 +++++++++++++++++++ .../src/components/admin/StatusTag.tsx | 2 + .../src/pages/admin/AdminTaskDetailPage.tsx | 2 +- .../src/pages/admin/AdminTasksPage.tsx | 6 ++- .../platform/AdminPlatformFulfillmentPage.tsx | 2 +- apps/frontend/src/utils/admin-options.ts | 2 +- 10 files changed, 92 insertions(+), 9 deletions(-) diff --git a/apps/backend/src/services/admin/admin-read-service.ts b/apps/backend/src/services/admin/admin-read-service.ts index cd77579d..26bfd5a9 100644 --- a/apps/backend/src/services/admin/admin-read-service.ts +++ b/apps/backend/src/services/admin/admin-read-service.ts @@ -33,6 +33,11 @@ import { canMarkManualReviewTaskStatus, canRetryTaskStatus, } from '../../domain/task-status.js' +import { getAffiliateDashConfig } from '../platforms/affiliate-dash/config.js' +import { + mergeAffiliateDashSkuMapping, + resolveAffiliateDashDisplayProductName, +} from '../platforms/affiliate-dash/sku-mapping.js' import { canViewerCloseTask, canRegenerateClaimLinkForViewer, @@ -361,7 +366,11 @@ export async function getAdminTaskDetail( ? { orderItemId: orderItem.id, skuCode: orderItem.sku_code, - skuName: orderItem.sku_name, + skuName: resolveAffiliateDashDisplayProductName( + String(orderItem.sku_code || ''), + String(orderItem.sku_name || ''), + mergeAffiliateDashSkuMapping(getAffiliateDashConfig().skuMapping), + ), quantity: orderItem.quantity, } : null, diff --git a/apps/backend/src/services/admin/admin-task-read-helpers.ts b/apps/backend/src/services/admin/admin-task-read-helpers.ts index ac221f03..9083014c 100644 --- a/apps/backend/src/services/admin/admin-task-read-helpers.ts +++ b/apps/backend/src/services/admin/admin-task-read-helpers.ts @@ -7,6 +7,11 @@ import { isTaskFulfillmentCompletedStatus, normalizeTaskStatus, } from '../../domain/task-status.js' +import { getAffiliateDashConfig } from '../platforms/affiliate-dash/config.js' +import { + mergeAffiliateDashSkuMapping, + resolveAffiliateDashDisplayProductName, +} from '../platforms/affiliate-dash/sku-mapping.js' import { createAdminViewerContext, getTaskPrimaryClaimTokenId, @@ -81,13 +86,16 @@ export function mapAdminTaskListItem( viewerContext: AdminViewerContext = createAdminViewerContext(), ): AdminTaskListItem { const fulfillment = buildTaskFulfillmentState(task) + const skuCode = String(task.sku_code || '').trim() + const rawSkuName = String(task.sku_name || '').trim() + const skuMapping = mergeAffiliateDashSkuMapping(getAffiliateDashConfig().skuMapping) return { taskId: task.id, taskNo: task.task_no, platformOrderId: task.platform_order_id, - skuCode: task.sku_code || '', - skuName: task.sku_name || '', + skuCode, + skuName: resolveAffiliateDashDisplayProductName(skuCode, rawSkuName, skuMapping), status: task.task_status, executorKey: task.executor_key || '', deliveryStatus: task.delivery_status || '', diff --git a/apps/backend/src/services/fulfillment/product-resolution-service.test.ts b/apps/backend/src/services/fulfillment/product-resolution-service.test.ts index ff7fc788..6df1827f 100644 --- a/apps/backend/src/services/fulfillment/product-resolution-service.test.ts +++ b/apps/backend/src/services/fulfillment/product-resolution-service.test.ts @@ -9,6 +9,8 @@ import { import { AFFILIATE_DASH_SKU_MAPPING, mergeAffiliateDashSkuMapping, + resolveAffiliateDashDisplayProductName, + reverseAffiliateDashSkuToProductNo, } from '../platforms/affiliate-dash/sku-mapping.js' test('权威表覆盖核心中文商品', () => { @@ -76,3 +78,21 @@ test('matchAffiliateDashSkuPassthrough 不在商品列表时未命中', () => { test('matchAffiliateDashSkuPassthrough 空集合未命中', () => { assert.equal(matchAffiliateDashSkuPassthrough('suit_alan_walker', new Set()), false) }) + +test('resolveAffiliateDashDisplayProductName 英文 sku 反查中文', () => { + assert.equal( + resolveAffiliateDashDisplayProductName('suit_romantic_destiny', 'suit_romantic_destiny'), + '套装-浪漫天命', + ) + assert.equal(resolveAffiliateDashDisplayProductName('bag_cute_bear', ''), '萌熊伴侣背包') + assert.equal( + resolveAffiliateDashDisplayProductName('suit_romantic_destiny', '套装-浪漫天命'), + '套装-浪漫天命', + ) + assert.equal(resolveAffiliateDashDisplayProductName('', '幸运币2个'), '幸运币2个') +}) + +test('reverseAffiliateDashSkuToProductNo 未命中返回空串', () => { + assert.equal(reverseAffiliateDashSkuToProductNo('not_a_real_sku'), '') + assert.equal(reverseAffiliateDashSkuToProductNo(''), '') +}) diff --git a/apps/backend/src/services/fulfillment/product-resolution-service.ts b/apps/backend/src/services/fulfillment/product-resolution-service.ts index de22f6af..6401523d 100644 --- a/apps/backend/src/services/fulfillment/product-resolution-service.ts +++ b/apps/backend/src/services/fulfillment/product-resolution-service.ts @@ -84,8 +84,11 @@ export async function resolveOrderItemForFulfillment({ externalSkuCode, externalItemId, ]) + // 运营侧展示用中文商品名;英文 sku 只写在 skuCode。 const resolvedSkuName = pickFirstNonEmpty([ - selectedExecutorKey === FULFILLMENT_EXECUTOR_KEYS.AFFILIATE_DASH ? affiliateDashMatch?.sku : '', + selectedExecutorKey === FULFILLMENT_EXECUTOR_KEYS.AFFILIATE_DASH + ? affiliateDashMatch?.productNo || affiliateDashMatch?.skuName + : '', item.skuName, externalSkuName, resolvedSkuCode, diff --git a/apps/backend/src/services/platforms/affiliate-dash/sku-mapping.ts b/apps/backend/src/services/platforms/affiliate-dash/sku-mapping.ts index d54c433e..1736d4d4 100644 --- a/apps/backend/src/services/platforms/affiliate-dash/sku-mapping.ts +++ b/apps/backend/src/services/platforms/affiliate-dash/sku-mapping.ts @@ -60,3 +60,42 @@ export function mergeAffiliateDashSkuMapping( } return merged } + +/** + * 英文 sku → 91 中文商品名(运营后台列表展示用)。 + * 同一 sku 若被多个中文名映射,取表中先出现的键。 + */ +export function reverseAffiliateDashSkuToProductNo( + sku: string, + mapping: Readonly> = AFFILIATE_DASH_SKU_MAPPING, +): string { + const target = String(sku || '').trim() + if (!target) { + return '' + } + + for (const [productNo, mappedSku] of Object.entries(mapping)) { + if (String(mappedSku || '').trim() === target) { + return String(productNo || '').trim() + } + } + + return '' +} + +/** 优先用中文商品名展示;已是中文或未命中映射时原样返回。 */ +export function resolveAffiliateDashDisplayProductName( + skuCode: string, + skuName = '', + mapping?: Readonly> | null, +): string { + const code = String(skuCode || '').trim() + const name = String(skuName || '').trim() + const table = mapping || AFFILIATE_DASH_SKU_MAPPING + return ( + reverseAffiliateDashSkuToProductNo(code, table) || + reverseAffiliateDashSkuToProductNo(name, table) || + name || + code + ) +} diff --git a/apps/frontend/src/components/admin/StatusTag.tsx b/apps/frontend/src/components/admin/StatusTag.tsx index 4b22280d..13b7518f 100644 --- a/apps/frontend/src/components/admin/StatusTag.tsx +++ b/apps/frontend/src/components/admin/StatusTag.tsx @@ -28,6 +28,7 @@ const STATUS_LABELS: Record = { delivered: '已发放', disabled: '已停用', dispatched_pending_return: '待退还号码', + draft: '草稿', expired: '已过期', external: '外部链接', failed: '失败', @@ -91,6 +92,7 @@ const STATUS_COLORS: Record = { customer_processing: 'blue', delivered: 'green', disabled: 'red', + draft: 'gold', external: 'cyan', failed: 'red', invalid: 'orange', diff --git a/apps/frontend/src/pages/admin/AdminTaskDetailPage.tsx b/apps/frontend/src/pages/admin/AdminTaskDetailPage.tsx index b4c998ae..04d5c4aa 100644 --- a/apps/frontend/src/pages/admin/AdminTaskDetailPage.tsx +++ b/apps/frontend/src/pages/admin/AdminTaskDetailPage.tsx @@ -845,7 +845,7 @@ function resolveDeliveryStepSummary(detail: AdminTaskDetail) { function resolveTaskExecutorDisplay(value: unknown) { const executorKey = String(value || '').trim() if (executorKey === 'affiliate_dash') { - return { label: 'Affiliate Dash', color: 'green' } + return { label: '联盟发货', color: 'green' } } if (executorKey === 'kuaishou_ct_assisted') { return { label: '乐玩云(已下线)', color: 'default' } diff --git a/apps/frontend/src/pages/admin/AdminTasksPage.tsx b/apps/frontend/src/pages/admin/AdminTasksPage.tsx index 4e93bd66..9da30cb6 100644 --- a/apps/frontend/src/pages/admin/AdminTasksPage.tsx +++ b/apps/frontend/src/pages/admin/AdminTasksPage.tsx @@ -137,7 +137,9 @@ export default function AdminTasksPage() { return (
{row.skuName || '-'} - {row.skuCode || '-'} + {row.skuCode && row.skuCode !== row.skuName ? ( + {row.skuCode} + ) : null} {executor.label} @@ -472,7 +474,7 @@ export default function AdminTasksPage() { function resolveTaskExecutorDisplay(value: unknown) { const executorKey = String(value || '').trim() if (executorKey === 'affiliate_dash') { - return { key: executorKey, label: 'Affiliate Dash', color: 'green' } + return { key: executorKey, label: '联盟发货', color: 'green' } } if (executorKey === 'kuaishou_ct_assisted') { return { key: executorKey, label: '乐玩云(已下线)', color: 'default' } diff --git a/apps/frontend/src/pages/admin/platform/AdminPlatformFulfillmentPage.tsx b/apps/frontend/src/pages/admin/platform/AdminPlatformFulfillmentPage.tsx index 538e3b3f..5775158c 100644 --- a/apps/frontend/src/pages/admin/platform/AdminPlatformFulfillmentPage.tsx +++ b/apps/frontend/src/pages/admin/platform/AdminPlatformFulfillmentPage.tsx @@ -45,7 +45,7 @@ type EditableRoutingRule = Omit