修复核销提前导致的链接不初始化错误

This commit is contained in:
yml2213
2026-07-12 09:48:05 +08:00
parent 38c022e20c
commit 1b8cdf16d3
3 changed files with 142 additions and 10 deletions
@@ -51,6 +51,34 @@ async function requireExecutorAction<T>(
return value
}
function isKuaishouCloudBindingReady(flow: ReturnType<typeof normalizeKuaishouCloudFlow>) {
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<TaskRow> {
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<ReturnType<typeof getClaimContext>>,
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<ClaimDetailPayload> {
@@ -178,9 +223,14 @@ export async function getKuaishouCloudClaimDetail(token: unknown): Promise<Claim
if (executorKey === 'kuaishou-industry') {
const flow = normalizeKuaishouCloudFlow(parseTaskContext(task).kuaishouCloudFulfillment)
if (flow.consume.status !== 'success') {
// 已核销也要走 verify:内部会补 prepare 绑定资源
if (flow.consume.status !== 'success' || !isKuaishouCloudBindingReady(flow)) {
const now = nowIso()
await verifyIndustryVoucherTicket(context, now).catch(() => 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<Claim
hasUsableIndustryVoucher(taskContext) &&
(
flow.ticket.status !== 'verified' ||
(flow.binding.prepareStatus !== 'ready' && !flow.binding.bindUrl)
!isKuaishouCloudBindingReady(flow)
)
if (needsIndustryVoucherPrepare) {
@@ -202,6 +252,20 @@ export async function getKuaishouCloudClaimDetail(token: unknown): Promise<Claim
return prepared
}
}
// 兜底:不依赖 UID;凭证已确认但绑定未就绪时自动补 prepare(覆盖提前核销场景)
if (!isKuaishouCloudMockTask(task)) {
const latestFlow = normalizeKuaishouCloudFlow(parseTaskContext(task).kuaishouCloudFulfillment)
if (
(latestFlow.ticket.status === 'verified' || hasUsableIndustryVoucher(parseTaskContext(task))) &&
!isKuaishouCloudBindingReady(latestFlow)
) {
task = await ensureKuaishouCloudBindingPrepared(task, {
source: 'claim_page_auto_binding_prepare',
actorSource: 'system',
})
}
}
}
// 已提交 UID 的 lewan 任务:详情查询时顺带刷新角色,便于前端轮询匹配状态
@@ -10,7 +10,12 @@ export async function syncKuaishouCloudRoleInfo(task: TaskRow) {
const flow = normalizeKuaishouCloudFlow(parseTaskContext(task).kuaishouCloudFulfillment)
if (!flow.binding.vnId || !flow.binding.vnKey || !flow.binding.vnPhone) {
if (flow.ticket.status === 'verified' && flow.binding.prepareStatus !== 'ready') {
// ticket verified 或绑定尚未 ready:补 prepare(覆盖提前核销后未取号场景)
if (
flow.ticket.status === 'verified' ||
flow.binding.prepareStatus !== 'ready' ||
!flow.binding.bindUrl
) {
try {
const prepared = await prepareFulfillmentBinding(task, {
source: 'claim_page_retry_binding_prepare',