diff --git a/apps/backend/src/services/claim/kuaishou-cloud-claim-service.ts b/apps/backend/src/services/claim/kuaishou-cloud-claim-service.ts index c222550a..a5fb267b 100644 --- a/apps/backend/src/services/claim/kuaishou-cloud-claim-service.ts +++ b/apps/backend/src/services/claim/kuaishou-cloud-claim-service.ts @@ -51,6 +51,34 @@ async function requireExecutorAction( return value } +function isKuaishouCloudBindingReady(flow: ReturnType) { + return flow.binding.prepareStatus === 'ready' && Boolean(String(flow.binding.bindUrl || '').trim()) +} + +/** + * 确保 Cloud 绑定资源已准备(取号 + 绑链)。 + * 提前核销后仍可能未 prepare,不能因 consume.success 跳过。 + */ +async function ensureKuaishouCloudBindingPrepared( + task: TaskRow, + options: { source: string; actorSource?: string } = { source: 'system_auto_prepare_binding' }, +): Promise { + const flow = normalizeKuaishouCloudFlow(parseTaskContext(task).kuaishouCloudFulfillment) + if (isKuaishouCloudBindingReady(flow)) { + return task + } + + try { + const prepared = await prepareFulfillmentBinding(task, { + source: options.source, + actor: { source: options.actorSource || options.source }, + }) + return prepared?.task || task + } catch { + return task + } +} + async function verifyIndustryVoucherTicket( context: Awaited>, now: string, @@ -58,8 +86,19 @@ async function verifyIndustryVoucherTicket( const taskContext = parseTaskContext(context.task) const flow = normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment) + // 提前核销后 consume 已是 success,但仍可能未准备绑定资源;禁止整段短路。 + // 注意:不要再调 getKuaishouCloudClaimDetail,避免 prepare 失败时递归。 if (flow.consume.status === 'success') { - return getKuaishouCloudClaimDetail(context.claimToken.token) + const task = await ensureKuaishouCloudBindingPrepared(context.task, { + source: 'claim_after_early_consume_prepare', + actorSource: 'system', + }) + return buildClaimDetailPayload({ + claimToken: context.claimToken, + task, + order: context.order, + orderItem: context.orderItem, + }) } const industryContext = typeof taskContext === 'object' ? taskContext : {} @@ -144,10 +183,11 @@ async function verifyIndustryVoucherTicket( } const preparedFlow = normalizeKuaishouCloudFlow(nextContext.kuaishouCloudFulfillment) - if (preparedFlow.binding.prepareStatus !== 'ready' || !preparedFlow.binding.bindUrl) { - await prepareFulfillmentBinding(taskWithTicket, { + let taskAfterPrepare = taskWithTicket + if (!isKuaishouCloudBindingReady(preparedFlow)) { + taskAfterPrepare = await ensureKuaishouCloudBindingPrepared(taskWithTicket, { source: 'send_code_callback', - actor: { source: 'send_code_callback' }, + actorSource: 'send_code_callback', }) } @@ -163,7 +203,12 @@ async function verifyIndustryVoucherTicket( now, ) - return getKuaishouCloudClaimDetail(context.claimToken.token) + return buildClaimDetailPayload({ + claimToken: context.claimToken, + task: taskAfterPrepare, + order: context.order, + orderItem: context.orderItem, + }) } export async function getKuaishouCloudClaimDetail(token: unknown): Promise { @@ -178,9 +223,14 @@ export async function getKuaishouCloudClaimDetail(token: unknown): Promise null) + const prepared: ClaimDetailPayload | null = await verifyIndustryVoucherTicket(context, now) + .catch(() => null) + if (prepared) { + return prepared + } } } @@ -191,7 +241,7 @@ export async function getKuaishouCloudClaimDetail(token: unknown): Promise