From e8fbb28cf4e75d559721145c9db8daca007cff08 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Tue, 13 Oct 2026 13:53:00 +0800 Subject: [PATCH] =?UTF-8?q?feat(claim):=20=E4=BA=BA=E5=B7=A5=E5=B1=A5?= =?UTF-8?q?=E7=BA=A6=E5=8D=95=E9=A2=86=E5=8F=96=E9=A1=B5=E5=8F=AA=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E3=80=8C=E4=BA=BA=E5=B7=A5=E5=A4=84=E7=90=86=E4=B8=AD?= =?UTF-8?q?=EF=BC=8C=E8=AF=B7=E7=AD=89=E5=BE=85=E5=AE=A2=E6=9C=8D=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 未命中 AFFILIATE_DASH_SKU_MAPPING 的抖音单显式回落 manual_dispatch(人工),这类单没有 自动发货通道(无 claim token、无绑定二维码),但卡券 cert_link 仍指向 /h5/<订单号> 领取页; 原页面会让买家「输 UID」,提交后没有任何自动发货动作,属于误导买家。 - 后端 kuaishou-cloud-claim-context.ts:新增 MANUAL_FULFILLMENT_MESSAGE 与 resolveManualFulfillmentMessage(executorKey)(manual_dispatch → 等待话术,其它执行器空串); 三个 payload builder 统一输出 manualFulfillment / manualFulfillmentMessage - 前端 claim-snapshot.ts:输出 isManualFulfillment / manualFulfillmentMessage, progressText 同步显示等待话术 - 前端 ClaimPage.tsx:在「渠道已下线」之后、「输 UID」之前插入人工单卡片(只读 + 刷新状态), 人工单不会自动轮询(无 expectedUid → 不安排下一次轮询) - types/claim.ts:补齐两个可选字段;话术只在后端维护一份,前端只做兜底 验证:后端 npm run check(701 单测)与前端 npm run check(格式/lint/类型/143 单测/构建)全绿 --- .../kuaishou-cloud-claim-context.test.ts | 15 ++++++++++++ .../claim/kuaishou-cloud-claim-context.ts | 24 +++++++++++++++++++ apps/frontend/src/pages/claim/ClaimPage.tsx | 20 ++++++++++++++++ .../src/pages/claim/claim-snapshot.ts | 13 ++++++++++ apps/frontend/src/types/claim.ts | 7 ++++++ 5 files changed, 79 insertions(+) diff --git a/apps/backend/src/services/claim/kuaishou-cloud-claim-context.test.ts b/apps/backend/src/services/claim/kuaishou-cloud-claim-context.test.ts index 6beba4ec..d721dbb3 100644 --- a/apps/backend/src/services/claim/kuaishou-cloud-claim-context.test.ts +++ b/apps/backend/src/services/claim/kuaishou-cloud-claim-context.test.ts @@ -3,10 +3,25 @@ import test from 'node:test' import { CLAIM_TERMINAL_STATUSES, + MANUAL_FULFILLMENT_MESSAGE, resolveClaimProductImageUrl, + resolveManualFulfillmentMessage, } from './kuaishou-cloud-claim-context.js' import { TASK_STATUS } from '../../domain/task-status.js' +test('人工履约提示:manual_dispatch 给等待客服话术,自动履约与已下线渠道给空串', () => { + // 人工单(未命中自动发货映射 → 回落 manual_dispatch):领取页不许再让买家输 UID + assert.equal(resolveManualFulfillmentMessage('manual_dispatch'), MANUAL_FULFILLMENT_MESSAGE) + assert.equal(resolveManualFulfillmentMessage(' manual_dispatch '), MANUAL_FULFILLMENT_MESSAGE) + assert.equal(MANUAL_FULFILLMENT_MESSAGE, '人工处理中,请等待客服') + + // 自动发货 / 已下线渠道 / 空值都不算人工履约 + assert.equal(resolveManualFulfillmentMessage('affiliate_dash'), '') + assert.equal(resolveManualFulfillmentMessage('kuaishou_feifei'), '') + assert.equal(resolveManualFulfillmentMessage(''), '') + assert.equal(resolveManualFulfillmentMessage(undefined), '') +}) + test('CLAIM_TERMINAL_STATUSES 包含 closed 与 expired 状态', () => { assert.equal(CLAIM_TERMINAL_STATUSES.has(TASK_STATUS.CLOSED), true) assert.equal(CLAIM_TERMINAL_STATUSES.has(TASK_STATUS.EXPIRED), true) diff --git a/apps/backend/src/services/claim/kuaishou-cloud-claim-context.ts b/apps/backend/src/services/claim/kuaishou-cloud-claim-context.ts index d3dba07f..0b6b7450 100644 --- a/apps/backend/src/services/claim/kuaishou-cloud-claim-context.ts +++ b/apps/backend/src/services/claim/kuaishou-cloud-claim-context.ts @@ -15,6 +15,7 @@ import { buildClaimUrl } from './claim-service.js' import { resolveProductImageUrl } from '../platforms/affiliate-dash/product-image.js' import { buildClaimIdentityPayload } from './claim-identity.js' import { normalizeAffiliateDashFlow } from '../fulfillment/affiliate-dash/index.js' +import { isManualDispatchExecutor } from '../fulfillment/executors/types.js' import type { ClaimTokenRow, OrderItemRow, OrderRow, TaskRow } from '../../types/repository/rows.js' import type { JsonObject } from '../../types/json.js' @@ -25,6 +26,20 @@ const RETIRED_CHANNEL_MESSAGE: Record = { kuaishou_ct_assisted: '乐玩履约渠道已下线,本链接仅可查看历史信息,无法继续领取', } +/** 领取页给人工履约单买家看的话术(人工派单 / 未命中自动发货映射)。 */ +export const MANUAL_FULFILLMENT_MESSAGE = '人工处理中,请等待客服' + +/** + * 人工履约提示话术:`manual_dispatch` 返回文案,其余执行器返回空串。 + * + * 为什么要在领取详情里显式区分:人工单没有自动发货通道(没有 claim token、也不会有 + * affiliate_dash 绑定二维码),买家在领取页输 UID 既无用又误导。前端据此隐藏「输 UID」步骤、 + * 只显示等待客服的说明;话术只在此处维护一份,前端只做兜底。 + */ +export function resolveManualFulfillmentMessage(executorKey: unknown): string { + return isManualDispatchExecutor(executorKey) ? MANUAL_FULFILLMENT_MESSAGE : '' +} + export type ClaimContext = { claimToken: ClaimTokenRow task: TaskRow @@ -290,6 +305,8 @@ function buildAffiliateDashClaimDetailPayload({ flowType: 'affiliate_dash' as const, channelOffline: false, channelOfflineMessage: '', + manualFulfillment: false, + manualFulfillmentMessage: '', claimIdentity, task: buildClaimTaskPayload(task, claimToken), order: buildClaimOrderPayload(order), @@ -372,6 +389,8 @@ function buildRetiredClaimDetailPayload({ claimToken, task, order, orderItem }: : ('kuaishou_cloud' as const), channelOffline: true, channelOfflineMessage: offlineMessage, + manualFulfillment: false, + manualFulfillmentMessage: '', claimIdentity, task: buildClaimTaskPayload(task, claimToken), order: buildClaimOrderPayload(order), @@ -413,6 +432,7 @@ function buildGenericClaimDetailPayload({ claimToken, task, order, orderItem }: const context = parseTaskContext(task) const claimIdentity = buildClaimIdentityPayload(context.claimIdentity) const productTitle = String(orderItem.sku_name || orderItem.sku_code || '').trim() + const manualFulfillmentMessage = resolveManualFulfillmentMessage(task.executor_key) return { tokenStatus: claimToken.status, @@ -420,6 +440,10 @@ function buildGenericClaimDetailPayload({ claimToken, task, order, orderItem }: flowType: String(task.executor_key || 'manual_dispatch').trim() || 'manual_dispatch', channelOffline: false, channelOfflineMessage: '', + // 人工履约单:买家在本页**无事可做**(没有自动发货通道可用),页面只展示等待话术, + // 不许再让买家输 UID ——否则会误导买家以为提交后就会自动发货。 + manualFulfillment: Boolean(manualFulfillmentMessage), + manualFulfillmentMessage, claimIdentity, task: buildClaimTaskPayload(task, claimToken), order: buildClaimOrderPayload(order), diff --git a/apps/frontend/src/pages/claim/ClaimPage.tsx b/apps/frontend/src/pages/claim/ClaimPage.tsx index 0b391c9a..8456526f 100644 --- a/apps/frontend/src/pages/claim/ClaimPage.tsx +++ b/apps/frontend/src/pages/claim/ClaimPage.tsx @@ -1,4 +1,5 @@ import { + ClockCircleOutlined, ExclamationCircleOutlined, LoadingOutlined, ReloadOutlined, @@ -301,6 +302,25 @@ export default function ClaimPage() {

{snapshot.channelOfflineMessage || '该履约渠道已下线,无法继续领取'}

+ ) : snapshot.isManualFulfillment ? ( + // 人工履约单(未命中自动发货映射 → manual_dispatch):没有自动发货通道, + // 买家输 UID 也没人自动发货,这里只如实告知「等客服」,不给任何操作入口。 + + +

+ {snapshot.manualFulfillmentMessage || '人工处理中,请等待客服'} +

+

+ 该订单由人工客服处理,本页无需任何操作;处理完成后刷新本页即可查看结果。 +

+ +
) : snapshot.currentStep === 1 ? (