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 9dd3d4ce..c87e21ca 100644 --- a/apps/backend/src/services/claim/kuaishou-cloud-claim-context.ts +++ b/apps/backend/src/services/claim/kuaishou-cloud-claim-context.ts @@ -86,8 +86,14 @@ export async function getClaimContext(token: unknown) { } export function buildClaimDetailPayload({ claimToken, task, order, orderItem }: ClaimContext) { + const kuaishouCloudSource = resolveClaimKuaishouCloudSource(task) const kuaishouCloudFulfillment = mapClaimKuaishouCloudFulfillment(task, order) - const displaySkuName = resolveClaimOrderItemDisplaySkuName(orderItem, kuaishouCloudFulfillment) + const displaySkuName = resolveClaimOrderItemDisplaySkuName( + orderItem, + kuaishouCloudFulfillment, + kuaishouCloudSource, + ) + const product = buildClaimProductPayload(displaySkuName, orderItem, kuaishouCloudSource) return { tokenStatus: claimToken.status, @@ -123,6 +129,7 @@ export function buildClaimDetailPayload({ claimToken, task, order, orderItem }: skuName: displaySkuName, quantity: orderItem.quantity, }, + product, session: null as null, kuaishouCloudFulfillment, result: task.redeemed_at @@ -139,6 +146,7 @@ export function buildClaimDetailPayload({ claimToken, task, order, orderItem }: function resolveClaimOrderItemDisplaySkuName( orderItem: OrderItemRow, kuaishouCloudFulfillment: JsonObject | null, + kuaishouCloudSource: JsonObject = {}, ) { const fulfillment = isPlainObject(kuaishouCloudFulfillment) ? kuaishouCloudFulfillment : {} const binding = isPlainObject(fulfillment.binding) ? fulfillment.binding : {} @@ -146,10 +154,10 @@ function resolveClaimOrderItemDisplaySkuName( const skuCode = String(orderItem.sku_code || '').trim() const rawSkuName = String(orderItem.sku_name || '').trim() const candidates = [ - fulfillment.internalSkuName, - binding.skuName, - ticket.goodsTitle, + kuaishouCloudSource.internalSkuName, rawSkuName, + ticket.goodsTitle, + binding.skuName, skuCode, ] @@ -163,6 +171,65 @@ function resolveClaimOrderItemDisplaySkuName( return rawSkuName || skuCode } +function buildClaimProductPayload( + displaySkuName: string, + orderItem: OrderItemRow, + kuaishouCloudSource: JsonObject = {}, +) { + const binding = isPlainObject(kuaishouCloudSource.binding) ? kuaishouCloudSource.binding : {} + const deliveryItems = normalizeClaimDeliveryItems(kuaishouCloudSource.deliveryItems, binding) + .map((item) => ({ + cloudSkuId: item.cloudSkuId, + name: item.cloudSkuName, + quantity: item.quantity, + })) + .filter((item) => item.name) + const normalizedDeliveryItems = deliveryItems.length > 0 + ? mergeClaimProductItems(deliveryItems) + : [{ + cloudSkuId: 0, + name: displaySkuName, + quantity: Math.max(1, Number(orderItem.quantity || 1) || 1), + }] + + return { + title: displaySkuName, + skuCode: String(orderItem.sku_code || '').trim(), + quantity: Math.max(1, Number(orderItem.quantity || 1) || 1), + isBundle: normalizedDeliveryItems.length > 1, + items: normalizedDeliveryItems, + } +} + +function mergeClaimProductItems( + items: Array<{ cloudSkuId: number; name: string; quantity: number }>, +) { + const merged = new Map() + + for (const item of items) { + const key = item.cloudSkuId > 0 ? `id:${item.cloudSkuId}` : `name:${item.name}` + const existing = merged.get(key) + if (existing) { + existing.quantity += item.quantity + existing.name = existing.name || item.name + continue + } + + merged.set(key, { ...item }) + } + + return Array.from(merged.values()) +} + +function resolveClaimKuaishouCloudSource(task: TaskRow): JsonObject { + if (String(task?.executor_key || '').trim() !== 'kuaishou_ct_assisted') { + return {} + } + + const source = parseTaskContext(task).kuaishouCloudFulfillment + return isPlainObject(source) ? source : {} +} + export function mapClaimKuaishouCloudFulfillment(task: TaskRow, order: OrderRow) { if (String(task?.executor_key || '').trim() !== 'kuaishou_ct_assisted') { return null @@ -279,6 +346,63 @@ function isPlainObject(value: unknown): value is JsonObject { return Object.prototype.toString.call(value) === '[object Object]' } +function normalizeClaimDeliveryItems(value: unknown, binding: JsonObject) { + const rawItems = Array.isArray(value) ? value : [] + const items = rawItems + .map((item) => normalizeClaimDeliveryItem(item)) + .filter((item): item is { cloudSkuId: number; cloudSkuName: string; quantity: number } => Boolean(item)) + + if (items.length > 0) { + return mergeClaimDeliveryItems(items) + } + + const cloudSkuId = Number(binding.skuId || 0) || 0 + if (!cloudSkuId) { + return [] + } + + return [{ + cloudSkuId, + cloudSkuName: String(binding.skuName || '').trim(), + quantity: 1, + }] +} + +function normalizeClaimDeliveryItem(value: unknown) { + const source = isPlainObject(value) ? value : {} + const cloudSkuId = Number(source.cloudSkuId || source.skuId || 0) || 0 + + if (!Number.isInteger(cloudSkuId) || cloudSkuId <= 0) { + return null + } + + const quantity = Number(source.quantity || 1) || 1 + return { + cloudSkuId, + cloudSkuName: String(source.cloudSkuName || source.skuName || '').trim(), + quantity: Number.isInteger(quantity) && quantity > 0 ? quantity : 1, + } +} + +function mergeClaimDeliveryItems( + items: Array<{ cloudSkuId: number; cloudSkuName: string; quantity: number }>, +) { + const merged = new Map() + + for (const item of items) { + const existing = merged.get(item.cloudSkuId) + if (existing) { + existing.quantity += item.quantity + existing.cloudSkuName = existing.cloudSkuName || item.cloudSkuName + continue + } + + merged.set(item.cloudSkuId, { ...item }) + } + + return Array.from(merged.values()) +} + function isClaimPlaceholderSkuName(value: string) { return ['测试链接专用', '商品领取', '测试商品'].includes(String(value || '').trim()) } diff --git a/apps/frontend/src/types/claim.ts b/apps/frontend/src/types/claim.ts index 7ef213c7..c8212012 100644 --- a/apps/frontend/src/types/claim.ts +++ b/apps/frontend/src/types/claim.ts @@ -52,6 +52,18 @@ export interface ClaimOrderItemInfo { quantity: number } +export interface ClaimProductInfo { + title: string + skuCode: string + quantity: number + isBundle: boolean + items: Array<{ + cloudSkuId: number + name: string + quantity: number + }> +} + export interface ClaimResultInfo { resultCode: string resultMessage: string @@ -117,6 +129,8 @@ export interface ClaimKuaishouCloudFlowInfo { } consume: { status: string + shopId: string + shopName: string consumedAt: string | null errorMessage: string } @@ -129,6 +143,7 @@ export interface ClaimDetailData { task: ClaimTaskInfo order: ClaimOrderInfo orderItem: ClaimOrderItemInfo + product: ClaimProductInfo session: unknown | null kuaishouCloudFulfillment: ClaimKuaishouCloudFlowInfo | null result: ClaimResultInfo | null diff --git a/apps/frontend/src/views/claim/kuaishou-cloud/KuaishouCloudClaimView.vue b/apps/frontend/src/views/claim/kuaishou-cloud/KuaishouCloudClaimView.vue index ac50043d..4c59f16c 100644 --- a/apps/frontend/src/views/claim/kuaishou-cloud/KuaishouCloudClaimView.vue +++ b/apps/frontend/src/views/claim/kuaishou-cloud/KuaishouCloudClaimView.vue @@ -20,7 +20,7 @@ const claim = useKuaishouCloudClaim(() => props.token)
@@ -72,7 +72,7 @@ const claim = useKuaishouCloudClaim(() => props.token) v-else-if="claim.currentStep.value === 3" :role-name="claim.roleName.value" :role-id="claim.roleId.value" - :order-item="claim.orderItem.value" + :product="claim.product.value" :redeeming="claim.redeeming.value" @confirm-redeem="claim.confirmRedeem()" /> @@ -83,7 +83,7 @@ const claim = useKuaishouCloudClaim(() => props.token) :result-description="claim.resultDescription.value" :flow="claim.flow.value" :order="claim.order.value" - :order-item="claim.orderItem.value" + :product="claim.product.value" :role-name="claim.roleName.value" :role-id="claim.roleId.value" :format-date-time="claim.formatAdminDateTime" diff --git a/apps/frontend/src/views/claim/kuaishou-cloud/components/ClaimConfirmStep.vue b/apps/frontend/src/views/claim/kuaishou-cloud/components/ClaimConfirmStep.vue index 2b4d066d..0ca2e73f 100644 --- a/apps/frontend/src/views/claim/kuaishou-cloud/components/ClaimConfirmStep.vue +++ b/apps/frontend/src/views/claim/kuaishou-cloud/components/ClaimConfirmStep.vue @@ -1,12 +1,14 @@ @@ -12,8 +14,9 @@ defineProps<{
快手 Cloud 客户领取 -

{{ orderItem?.skuName || '商品领取' }}

+

{{ product?.title || '商品领取' }}

订单号:{{ order?.platformOrderId || '-' }}

+
+import { computed } from 'vue' + +import type { ClaimProductInfo } from '@/types/claim' + +const props = withDefaults(defineProps<{ + product: ClaimProductInfo | null + compact?: boolean +}>(), { + compact: false, +}) + +const productItems = computed(() => { + const items = Array.isArray(props.product?.items) ? props.product.items : [] + return items + .map((item) => ({ + key: item.cloudSkuId || item.name, + name: String(item.name || '').trim(), + quantity: Math.max(1, Number(item.quantity || 1) || 1), + })) + .filter((item) => item.name) +}) + +const hasMultipleItems = computed(() => props.product?.isBundle || productItems.value.length > 1) + + + + + diff --git a/apps/frontend/src/views/claim/kuaishou-cloud/components/ClaimResultStep.vue b/apps/frontend/src/views/claim/kuaishou-cloud/components/ClaimResultStep.vue index 7fd89f1e..846d445c 100644 --- a/apps/frontend/src/views/claim/kuaishou-cloud/components/ClaimResultStep.vue +++ b/apps/frontend/src/views/claim/kuaishou-cloud/components/ClaimResultStep.vue @@ -1,14 +1,16 @@