修改领取顺序,先核销

This commit is contained in:
yml2213
2026-05-31 11:58:12 +08:00
parent 11cf0e93a4
commit 904ecc9303
6 changed files with 105 additions and 39 deletions
+2 -2
View File
@@ -38,8 +38,8 @@ router.post(
'/:token/kuaishou-cloud/verify-ticket', '/:token/kuaishou-cloud/verify-ticket',
claimWriteRateLimit, claimWriteRateLimit,
createRouteHandler((req) => verifyKuaishouCloudClaimTicket(req.params.token, req.body), { createRouteHandler((req) => verifyKuaishouCloudClaimTicket(req.params.token, req.body), {
successMessage: '核销码校验成功', successMessage: '核销码提交并核销成功',
errorMessage: '校验核销码失败', errorMessage: '提交核销码失败',
scope: '[claims/:token/kuaishou-cloud/verify-ticket]', scope: '[claims/:token/kuaishou-cloud/verify-ticket]',
}), }),
) )
@@ -549,8 +549,12 @@ export async function returnNumberAdminTaskKuaishouCloudFulfillment(
let nextTaskStatus = 'completed' let nextTaskStatus = 'completed'
let nextResultCode = 'kuaishou_cloud_completed' let nextResultCode = 'kuaishou_cloud_completed'
let nextResultMessage = 'cloudtentacles 发货、退号并完成快手核销' let nextResultMessage = 'cloudtentacles 发货、退号并完成快手核销'
const consumeAlreadyCompleted = flow.consume.status === 'success'
if (!order) { if (consumeAlreadyCompleted) {
consumeStatus = 'success'
consumedAt = flow.consume.consumedAt || now
} else if (!order) {
consumeStatus = 'failed' consumeStatus = 'failed'
consumeErrorMessage = '任务关联订单不存在,无法执行快手核销' consumeErrorMessage = '任务关联订单不存在,无法执行快手核销'
} else if (!ticketCode) { } else if (!ticketCode) {
@@ -652,7 +656,11 @@ export async function returnNumberAdminTaskKuaishouCloudFulfillment(
await createTaskEvent( await createTaskEvent(
task.id, task.id,
consumeStatus === 'success' ? 'kuaishou_cloud_consumed' : 'kuaishou_cloud_consume_failed', consumeAlreadyCompleted
? 'kuaishou_cloud_consume_already_completed'
: consumeStatus === 'success'
? 'kuaishou_cloud_consumed'
: 'kuaishou_cloud_consume_failed',
{ {
ticketCodeMasked: maskCode(ticketCode), ticketCodeMasked: maskCode(ticketCode),
shopId, shopId,
@@ -19,7 +19,10 @@ import {
resolveKuaishouEticketShopConfig, resolveKuaishouEticketShopConfig,
type KuaishouEticketShopConfig, type KuaishouEticketShopConfig,
} from '../platforms/kuaishou-eticket/source-config-service.js' } from '../platforms/kuaishou-eticket/source-config-service.js'
import { queryKuaishouEticketConsumeDetail } from '../platforms/kuaishou-eticket/consume-service.js' import {
consumeKuaishouEticket,
queryKuaishouEticketConsumeDetail,
} from '../platforms/kuaishou-eticket/consume-service.js'
import { isKuaishouEticketMockTicketCode } from '../platforms/kuaishou-eticket/mock-ticket-service.js' import { isKuaishouEticketMockTicketCode } from '../platforms/kuaishou-eticket/mock-ticket-service.js'
import { import {
dispatchKuaishouCloudFulfillmentTask, dispatchKuaishouCloudFulfillmentTask,
@@ -59,12 +62,26 @@ export async function verifyKuaishouCloudClaimTicket(token: unknown, payload: Js
}) })
} }
const persistedTicketCode = String(flow.ticket.code || '').trim()
if (flow.consume.status === 'success') {
if (!persistedTicketCode || persistedTicketCode === ticketCode) {
return getKuaishouCloudClaimDetail(token)
}
throw createHttpError('当前领取链接已完成核销,不能更换核销码', {
statusCode: 409,
errorCode: 'claim_kuaishou_cloud_ticket_already_consumed',
})
}
const mockTicketCode = isKuaishouEticketMockTicketCode(ticketCode) const mockTicketCode = isKuaishouEticketMockTicketCode(ticketCode)
let shopId = String( let shopId = String(
flow.consume.shopId || context.order.shop_id || (mockTicketCode ? 'mock' : ''), flow.consume.shopId || context.order.shop_id || (mockTicketCode ? 'mock' : ''),
).trim() ).trim()
let shopName = String(flow.consume.shopName || context.order.shop_name || '').trim() let shopName = String(flow.consume.shopName || context.order.shop_name || '').trim()
let detailResult let detailResult
let shopCookie = ''
let consumeBaseUrl = ''
if (mockTicketCode) { if (mockTicketCode) {
detailResult = await queryKuaishouEticketConsumeDetail({ detailResult = await queryKuaishouEticketConsumeDetail({
@@ -82,6 +99,8 @@ export async function verifyKuaishouCloudClaimTicket(token: unknown, payload: Js
detailResult = matched.detailResult detailResult = matched.detailResult
shopId = matched.shopConfig.shopId || shopId shopId = matched.shopConfig.shopId || shopId
shopName = matched.shopConfig.kshopName || shopName shopName = matched.shopConfig.kshopName || shopName
shopCookie = matched.shopConfig.cookie
consumeBaseUrl = getKuaishouEticketSourceConfig().baseUrl
} }
if (!detailResult.ok || detailResult.alreadyConsumed || !detailResult.detail) { if (!detailResult.ok || detailResult.alreadyConsumed || !detailResult.detail) {
@@ -93,6 +112,22 @@ export async function verifyKuaishouCloudClaimTicket(token: unknown, payload: Js
}) })
} }
const consumePayload = {
baseUrl: consumeBaseUrl,
cookie: shopCookie,
eTicketId: detailResult.eTicketId || ticketCode,
oid: String(detailResult.detail.oid || '').trim(),
formToken: String(detailResult.detail.formToken || '').trim(),
num: Number(detailResult.detail.leftCount || 0) || 1,
}
const consumeResult = await consumeKuaishouEticket(consumePayload)
if (!consumeResult.consumed) {
throw createHttpError(consumeResult.errorMessage || '核销失败,请确认核销码状态后重试', {
statusCode: 409,
errorCode: 'claim_kuaishou_cloud_consume_failed',
})
}
const taskContext = parseTaskContext(context.task) const taskContext = parseTaskContext(context.task)
const nextContext = { const nextContext = {
...taskContext, ...taskContext,
@@ -114,7 +149,11 @@ export async function verifyKuaishouCloudClaimTicket(token: unknown, payload: Js
}, },
consume: { consume: {
...flow.consume, ...flow.consume,
status: 'success',
shopId, shopId,
shopName,
consumedAt: now,
errorMessage: '',
}, },
}, },
} }
@@ -136,31 +175,29 @@ export async function verifyKuaishouCloudClaimTicket(token: unknown, payload: Js
}) })
} else { } else {
const preparedFlow = normalizeKuaishouCloudFlow(nextContext.kuaishouCloudFulfillment) const preparedFlow = normalizeKuaishouCloudFlow(nextContext.kuaishouCloudFulfillment)
if (preparedFlow.binding.prepareStatus !== 'ready' || !preparedFlow.binding.bindUrl) { const taskWithConsumedTicket = (await updateTask(context.task.id, {
await prepareKuaishouCloudFulfillmentTask( claim_token: context.claimToken.token,
{ claim_expires_at: context.claimToken.expired_at,
...context.task, context_json: JSON.stringify(nextContext),
context_json: JSON.stringify(nextContext), claimed_at: context.task.claimed_at || now,
}, task_status: TASK_STATUS.WAITING_BINDING,
{ user_action_status: 'pending_claim',
source: 'claim_page_ticket_verified', last_error: '',
actor: { source: 'claim_page' }, updated_at: now,
}, })) || {
) ...context.task,
} else { context_json: JSON.stringify(nextContext),
await updateTask(context.task.id, { claimed_at: context.task.claimed_at || now,
claim_token: context.claimToken.token, task_status: TASK_STATUS.WAITING_BINDING,
claim_expires_at: context.claimToken.expired_at, user_action_status: 'pending_claim',
context_json: JSON.stringify(nextContext), last_error: '',
last_error: '', updated_at: now,
updated_at: now,
})
} }
if (!context.task.claimed_at) { if (preparedFlow.binding.prepareStatus !== 'ready' || !preparedFlow.binding.bindUrl) {
await updateTask(context.task.id, { await prepareKuaishouCloudFulfillmentTask(taskWithConsumedTicket, {
claimed_at: now, source: 'claim_page_ticket_verified',
updated_at: now, actor: { source: 'claim_page' },
}) })
} }
} }
@@ -174,6 +211,23 @@ export async function verifyKuaishouCloudClaimTicket(token: unknown, payload: Js
shopName, shopName,
goodsTitle: String(detailResult.goods?.itemTitle || '').trim(), goodsTitle: String(detailResult.goods?.itemTitle || '').trim(),
mock: mockTicketCode, mock: mockTicketCode,
consumedAt: now,
consumeAlreadyCompleted: consumeResult.alreadyConsumed === true,
},
now,
)
await createTaskEvent(
context.task.id,
'kuaishou_cloud_consumed',
{
source: 'claim_page_ticket_submit',
ticketCodeMasked: maskCode(detailResult.eTicketId || ticketCode),
shopId,
shopName,
consumeStatus: 'success',
consumedAt: now,
mock: mockTicketCode,
}, },
now, now,
) )
@@ -523,8 +523,12 @@ export async function returnKuaishouCloudFulfillmentTask(
let nextTaskStatus: TaskStatus = TASK_STATUS.COMPLETED; let nextTaskStatus: TaskStatus = TASK_STATUS.COMPLETED;
let nextResultCode = "kuaishou_cloud_completed"; let nextResultCode = "kuaishou_cloud_completed";
let nextResultMessage = "cloudtentacles 发货、退号并完成快手核销"; let nextResultMessage = "cloudtentacles 发货、退号并完成快手核销";
const consumeAlreadyCompleted = flow.consume.status === "success";
if (!order) { if (consumeAlreadyCompleted) {
consumeStatus = "success";
consumedAt = flow.consume.consumedAt || now;
} else if (!order) {
consumeStatus = "failed"; consumeStatus = "failed";
consumeErrorMessage = "任务关联订单不存在,无法执行快手核销"; consumeErrorMessage = "任务关联订单不存在,无法执行快手核销";
} else if (!ticketCode) { } else if (!ticketCode) {
@@ -626,9 +630,11 @@ export async function returnKuaishouCloudFulfillmentTask(
await createTaskEvent( await createTaskEvent(
task.id, task.id,
consumeStatus === "success" consumeAlreadyCompleted
? "kuaishou_cloud_consumed" ? "kuaishou_cloud_consume_already_completed"
: "kuaishou_cloud_consume_failed", : consumeStatus === "success"
? "kuaishou_cloud_consumed"
: "kuaishou_cloud_consume_failed",
{ {
source: String(options.source || "system").trim() || "system", source: String(options.source || "system").trim() || "system",
ticketCodeMasked: maskCode(ticketCode), ticketCodeMasked: maskCode(ticketCode),
@@ -24,7 +24,7 @@ const activeGuideNames = ref(['guide'])
<section class="content-card"> <section class="content-card">
<h2> 1 提交核销码</h2> <h2> 1 提交核销码</h2>
<p class="muted"> <p class="muted">
请您先从快手小店复制核销码验证通过后系统会自动准备绑定资源. 请您先从快手小店复制核销码提交后会立即核销核销成功后系统会自动准备绑定资源
</p> </p>
<el-input <el-input
@@ -43,15 +43,13 @@ const activeGuideNames = ref(['guide'])
:disabled="!canSubmitTicket" :disabled="!canSubmitTicket"
@click="$emit('submitTicket')" @click="$emit('submitTicket')"
> >
验证核销码并继续 提交核销码并继续
</el-button> </el-button>
<div class="status-banner info"> <div class="status-banner info">
<el-icon><Clock /></el-icon> <el-icon><Clock /></el-icon>
<span>{{ <span>{{
isBindingPreparing isBindingPreparing ? '核销完成后会自动准备绑定资源。' : '核销完成后会自动进入扫码绑定步骤。'
? '绑定资源会在核销通过后自动准备。'
: '核销完成后会自动进入扫码绑定步骤。'
}}</span> }}</span>
</div> </div>
@@ -116,7 +116,7 @@ export function useKuaishouCloudClaim(token: () => string) {
if (isTicketVerified.value) { if (isTicketVerified.value) {
return isBindingPrepared.value ? '请完成扫码绑定' : '绑定链接刷新中,请稍候' return isBindingPrepared.value ? '请完成扫码绑定' : '绑定链接刷新中,请稍候'
} }
return '等待提交核销' return '等待提交核销'
}) })
const resultTitle = computed(() => { const resultTitle = computed(() => {
@@ -198,7 +198,7 @@ export function useKuaishouCloudClaim(token: () => string) {
ticketCode: normalizedTicketCode, ticketCode: normalizedTicketCode,
}) })
await applyDetail(response.data) await applyDetail(response.data)
showSuccess('核销码验证通过,系统已开始准备绑定资源') showSuccess('核销已完成,系统已开始准备绑定资源')
syncPolling() syncPolling()
} catch (error) { } catch (error) {
showError(error instanceof Error ? error.message : '核销码验证失败') showError(error instanceof Error ? error.message : '核销码验证失败')