接入多发货平台与电子凭证
This commit is contained in:
@@ -87,6 +87,10 @@ export async function getClaimContext(token: unknown) {
|
||||
}
|
||||
|
||||
export function buildClaimDetailPayload({ claimToken, task, order, orderItem }: ClaimContext) {
|
||||
if (String(task.executor_key || '').trim() === 'kuaishou_feifei') {
|
||||
return buildKuaishouFeifeiClaimDetailPayload({ claimToken, task, order, orderItem })
|
||||
}
|
||||
|
||||
const kuaishouCloudSource = resolveClaimKuaishouCloudSource(task)
|
||||
const kuaishouCloudFulfillment = mapClaimKuaishouCloudFulfillment(task, order)
|
||||
const displaySkuName = resolveClaimOrderItemDisplaySkuName(
|
||||
@@ -133,6 +137,7 @@ export function buildClaimDetailPayload({ claimToken, task, order, orderItem }:
|
||||
product,
|
||||
session: null as null,
|
||||
kuaishouCloudFulfillment,
|
||||
kuaishouFeifei: null as null,
|
||||
result: task.redeemed_at
|
||||
? {
|
||||
resultCode: String(task.result_code || ''),
|
||||
@@ -144,6 +149,93 @@ export function buildClaimDetailPayload({ claimToken, task, order, orderItem }:
|
||||
}
|
||||
}
|
||||
|
||||
function buildKuaishouFeifeiClaimDetailPayload({ claimToken, task, order, orderItem }: ClaimContext) {
|
||||
const context = parseTaskContext(task)
|
||||
const flow = mapClaimKuaishouFeifeiFulfillment(context.kuaishouFeifei)
|
||||
const product = {
|
||||
title: String(flow.productName || orderItem.sku_name || orderItem.sku_code || '').trim(),
|
||||
skuCode: String(orderItem.sku_code || '').trim(),
|
||||
quantity: Math.max(1, Number(orderItem.quantity || 1) || 1),
|
||||
isBundle: false,
|
||||
items: [{
|
||||
cloudSkuId: 0,
|
||||
name: String(flow.productName || orderItem.sku_name || orderItem.sku_code || '').trim(),
|
||||
quantity: Math.max(1, Number(orderItem.quantity || 1) || 1),
|
||||
}],
|
||||
}
|
||||
|
||||
return {
|
||||
tokenStatus: claimToken.status,
|
||||
claimUrl: buildClaimUrl(claimToken.token),
|
||||
flowType: 'kuaishou_feifei',
|
||||
task: {
|
||||
taskId: task.id,
|
||||
taskNo: task.task_no,
|
||||
status: task.task_status,
|
||||
executorKey: task.executor_key || '',
|
||||
requiresSupportReview: false,
|
||||
expiresAt: claimToken.expired_at,
|
||||
claimedAt: task.claimed_at,
|
||||
roleConfirmedAt: task.role_confirmed_at,
|
||||
redeemedAt: task.redeemed_at,
|
||||
loginType: task.login_type,
|
||||
lastError: task.last_error,
|
||||
runtimeSessionId: task.runtime_session_id,
|
||||
},
|
||||
order: {
|
||||
orderId: order.id,
|
||||
platform: order.platform,
|
||||
platformOrderId: order.platform_order_id,
|
||||
payStatus: order.pay_status,
|
||||
orderStatus: order.order_status,
|
||||
totalAmount: formatFenToAmount(order.total_amount),
|
||||
totalAmountFen: normalizeFen(order.total_amount),
|
||||
currency: order.currency,
|
||||
},
|
||||
orderItem: {
|
||||
orderItemId: orderItem.id,
|
||||
skuCode: orderItem.sku_code,
|
||||
skuName: product.title,
|
||||
quantity: orderItem.quantity,
|
||||
},
|
||||
product,
|
||||
session: null as null,
|
||||
kuaishouCloudFulfillment: null as null,
|
||||
kuaishouFeifei: flow,
|
||||
result: task.redeemed_at
|
||||
? {
|
||||
resultCode: String(task.result_code || ''),
|
||||
resultMessage: String(task.result_message || ''),
|
||||
screenshotReady: false,
|
||||
screenshotUrl: '',
|
||||
}
|
||||
: null,
|
||||
}
|
||||
}
|
||||
|
||||
function mapClaimKuaishouFeifeiFulfillment(value: unknown) {
|
||||
const source = isPlainObject(value) ? value : {}
|
||||
const h5 = isPlainObject(source.h5) ? source.h5 : {}
|
||||
|
||||
return {
|
||||
flowType: 'kuaishou_feifei',
|
||||
productCode: String(source.productCode || '').trim(),
|
||||
productName: String(source.productName || '').trim(),
|
||||
platformOrderNo: String(source.platformOrderNo || '').trim(),
|
||||
orderNo: String(source.orderNo || '').trim(),
|
||||
rechargeStatus: Number(source.rechargeStatus || 0) || 0,
|
||||
rechargeStatusLabel: String(source.rechargeStatusLabel || '').trim(),
|
||||
rechargeResultMessage: String(source.rechargeResultMessage || '').trim(),
|
||||
claimUrl: String(source.claimUrl || '').trim(),
|
||||
consumeStatus: String(source.consumeStatus || 'pending').trim(),
|
||||
h5: {
|
||||
entryUrl: String(h5.entryUrl || '').trim(),
|
||||
rechargeUrl: String(h5.rechargeUrl || '').trim(),
|
||||
},
|
||||
lastSyncedAt: source.lastSyncedAt || null,
|
||||
}
|
||||
}
|
||||
|
||||
function resolveClaimOrderItemDisplaySkuName(
|
||||
orderItem: OrderItemRow,
|
||||
kuaishouCloudFulfillment: JsonObject | null,
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
rebindKuaishouCloudTaskRole,
|
||||
refreshKuaishouCloudTaskRoleInfo,
|
||||
} from '../fulfillment/kuaishou-cloud/index.js'
|
||||
import { syncKuaishouFeifeiTaskStatus } from '../fulfillment/kuaishou-feifei/index.js'
|
||||
import { buildClaimDetailPayload, getClaimContext } from './kuaishou-cloud-claim-context.js'
|
||||
import { syncKuaishouCloudRoleInfo } from './kuaishou-cloud-sync-service.js'
|
||||
import type { TaskRow } from '../../types/repository/rows.js'
|
||||
@@ -42,6 +43,7 @@ const KUAISHOU_CLOUD_GUIDE_DIR = path.resolve(PROJECT_ROOT, '../../tems/imgs')
|
||||
const ALLOWED_GUIDE_FILES = new Set(['1.png', '2.png', '3.png'])
|
||||
|
||||
type JsonObject = Record<string, any>
|
||||
type ClaimDetailPayload = ReturnType<typeof buildClaimDetailPayload>
|
||||
|
||||
export async function verifyKuaishouCloudClaimTicket(token: unknown, payload: JsonObject = {}) {
|
||||
const context = await getClaimContext(token)
|
||||
@@ -50,7 +52,7 @@ export async function verifyKuaishouCloudClaimTicket(token: unknown, payload: Js
|
||||
const executorKey = String(context.task.executor_key || '').trim()
|
||||
|
||||
if (executorKey === 'kuaishou-industry') {
|
||||
return verifyIndustryeVoucherTicket(context, now)
|
||||
return verifyIndustryVoucherTicket(context, now)
|
||||
}
|
||||
|
||||
if (executorKey !== 'kuaishou_ct_assisted') {
|
||||
@@ -61,6 +63,9 @@ export async function verifyKuaishouCloudClaimTicket(token: unknown, payload: Js
|
||||
}
|
||||
|
||||
const flow = normalizeKuaishouCloudFlow(parseTaskContext(context.task).kuaishouCloudFulfillment)
|
||||
if (hasUsableIndustryVoucher(parseTaskContext(context.task))) {
|
||||
return verifyIndustryVoucherTicket(context, now)
|
||||
}
|
||||
|
||||
const ticketCode = String(payload.ticketCode || payload.eTicketId || '').trim()
|
||||
if (!ticketCode) {
|
||||
@@ -243,10 +248,10 @@ export async function verifyKuaishouCloudClaimTicket(token: unknown, payload: Js
|
||||
return getKuaishouCloudClaimDetail(token)
|
||||
}
|
||||
|
||||
async function verifyIndustryeVoucherTicket(
|
||||
async function verifyIndustryVoucherTicket(
|
||||
context: Awaited<ReturnType<typeof getClaimContext>>,
|
||||
now: string,
|
||||
) {
|
||||
): Promise<ClaimDetailPayload> {
|
||||
const taskContext = parseTaskContext(context.task)
|
||||
const flow = normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment)
|
||||
|
||||
@@ -255,33 +260,58 @@ async function verifyIndustryeVoucherTicket(
|
||||
}
|
||||
|
||||
const industryContext = typeof taskContext === 'object' ? taskContext : {}
|
||||
const token = String(industryContext.token || '').trim()
|
||||
const voucherContext = normalizeIndustryVoucherContext(industryContext.kuaishouIndustryVoucher)
|
||||
if (voucherContext.status === 'DESTROYED') {
|
||||
throw createHttpError('当前电子凭证已销毁,无法继续领取', {
|
||||
statusCode: 409,
|
||||
errorCode: 'claim_kuaishou_industry_voucher_destroyed',
|
||||
})
|
||||
}
|
||||
|
||||
const token = String(voucherContext.token || industryContext.token || '').trim()
|
||||
const voucherCode = String(voucherContext.voucherCode || voucherContext.eticketId || '').trim()
|
||||
const certExpireType = Number(industryContext.certExpireType || 0)
|
||||
const certActualStartTime = Number(industryContext.certActualStartTime || 0)
|
||||
const certActualEndTime = Number(industryContext.certActualEndTime || 0)
|
||||
const certActualStartTime = Number(
|
||||
voucherContext.validStartTime || industryContext.certActualStartTime || 0,
|
||||
)
|
||||
const certActualEndTime = Number(
|
||||
voucherContext.validEndTime || industryContext.certActualEndTime || 0,
|
||||
)
|
||||
|
||||
const nextContext = {
|
||||
...taskContext,
|
||||
kuaishouIndustryVoucher: {
|
||||
...voucherContext,
|
||||
oid: voucherContext.oid || context.order.platform_order_id,
|
||||
token,
|
||||
eticketId: voucherCode,
|
||||
voucherCode,
|
||||
status: voucherContext.status || 'UNUSED',
|
||||
verifiedAt: voucherContext.verifiedAt || now,
|
||||
},
|
||||
kuaishouCloudFulfillment: {
|
||||
...flow,
|
||||
ticket: {
|
||||
...flow.ticket,
|
||||
code: '',
|
||||
code: voucherCode,
|
||||
status: 'verified',
|
||||
capturedAt: flow.ticket.capturedAt || now,
|
||||
capturedBy: flow.ticket.capturedBy || { source: 'send_code_callback' },
|
||||
verifiedAt: now,
|
||||
oid: context.order.platform_order_id,
|
||||
oid: voucherContext.oid || context.order.platform_order_id,
|
||||
formToken: token,
|
||||
leftCount: 0,
|
||||
leftCount: voucherContext.status === 'CONSUMED' ? 0 : 1,
|
||||
goodsTitle: context.orderItem.sku_name || context.orderItem.sku_code || '',
|
||||
},
|
||||
consume: {
|
||||
...flow.consume,
|
||||
status: 'pending',
|
||||
status: voucherContext.status === 'CONSUMED' ? 'success' : 'pending',
|
||||
shopId: context.order.shop_id,
|
||||
shopName: context.order.shop_name,
|
||||
autoConsumeEnabled: true,
|
||||
consumedAt: voucherContext.status === 'CONSUMED'
|
||||
? voucherContext.consumedAt || flow.consume.consumedAt || now
|
||||
: flow.consume.consumedAt,
|
||||
},
|
||||
certInfo: {
|
||||
certExpireType,
|
||||
@@ -353,17 +383,41 @@ export async function getKuaishouCloudClaimGuideAssetPath(filename: unknown) {
|
||||
return filePath
|
||||
}
|
||||
|
||||
export async function getKuaishouCloudClaimDetail(token: unknown) {
|
||||
export async function getKuaishouCloudClaimDetail(token: unknown): Promise<ClaimDetailPayload> {
|
||||
const context = await getClaimContext(token)
|
||||
let task = context.task
|
||||
|
||||
const executorKey = String(task.executor_key || '').trim()
|
||||
|
||||
if (executorKey === 'kuaishou_feifei') {
|
||||
task = (await syncKuaishouFeifeiTaskStatus(task)) || task
|
||||
}
|
||||
|
||||
if (executorKey === 'kuaishou-industry') {
|
||||
const flow = normalizeKuaishouCloudFlow(parseTaskContext(task).kuaishouCloudFulfillment)
|
||||
if (flow.consume.status !== 'success') {
|
||||
const now = nowIso()
|
||||
await verifyIndustryeVoucherTicket(context, now).catch(() => null)
|
||||
await verifyIndustryVoucherTicket(context, now).catch(() => null)
|
||||
}
|
||||
}
|
||||
|
||||
if (executorKey === 'kuaishou_ct_assisted') {
|
||||
const taskContext = parseTaskContext(task)
|
||||
const flow = normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment)
|
||||
const needsIndustryVoucherPrepare =
|
||||
hasUsableIndustryVoucher(taskContext) &&
|
||||
(
|
||||
flow.ticket.status !== 'verified' ||
|
||||
(flow.binding.prepareStatus !== 'ready' && !flow.binding.bindUrl)
|
||||
)
|
||||
|
||||
if (needsIndustryVoucherPrepare) {
|
||||
const now = nowIso()
|
||||
const prepared: ClaimDetailPayload | null = await verifyIndustryVoucherTicket(context, now)
|
||||
.catch(() => null)
|
||||
if (prepared) {
|
||||
return prepared
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,6 +471,35 @@ function parseTaskContext(task: Partial<TaskRow> | null | undefined): JsonObject
|
||||
}
|
||||
}
|
||||
|
||||
function hasUsableIndustryVoucher(context: JsonObject = {}) {
|
||||
const voucher = normalizeIndustryVoucherContext(context.kuaishouIndustryVoucher)
|
||||
if (!voucher.voucherCode && !voucher.eticketId) {
|
||||
return false
|
||||
}
|
||||
|
||||
return voucher.status !== 'DESTROYED'
|
||||
}
|
||||
|
||||
function normalizeIndustryVoucherContext(value: unknown): JsonObject {
|
||||
const source = value && typeof value === 'object' && !Array.isArray(value)
|
||||
? value as JsonObject
|
||||
: {}
|
||||
const status = String(source.status || 'UNUSED').trim().toUpperCase()
|
||||
|
||||
return {
|
||||
...source,
|
||||
oid: String(source.oid || '').trim(),
|
||||
token: String(source.token || '').trim(),
|
||||
eticketId: String(source.eticketId || source.voucherCode || '').trim(),
|
||||
voucherCode: String(source.voucherCode || source.eticketId || '').trim(),
|
||||
status: status === 'CONSUMED' || status === 'DESTROYED' ? status : 'UNUSED',
|
||||
validStartTime: Number(source.validStartTime || 0) || 0,
|
||||
validEndTime: Number(source.validEndTime || 0) || 0,
|
||||
verifiedAt: source.verifiedAt || null,
|
||||
consumedAt: source.consumedAt || null,
|
||||
}
|
||||
}
|
||||
|
||||
async function queryKuaishouEticketConsumeDetailWithShopFallback({
|
||||
ticketCode,
|
||||
shopId,
|
||||
|
||||
Reference in New Issue
Block a user