优化发码回调,失败停止整个流程
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
ALTER TABLE kuaishou_industry_vouchers
|
||||
ADD COLUMN IF NOT EXISTS send_callback_status TEXT NOT NULL DEFAULT 'success',
|
||||
ADD COLUMN IF NOT EXISTS send_callback_attempt_count INTEGER NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS send_callback_last_error TEXT NOT NULL DEFAULT '',
|
||||
ADD COLUMN IF NOT EXISTS send_callback_response_json JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
ADD COLUMN IF NOT EXISTS send_callback_sent_at TIMESTAMPTZ;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_kuaishou_industry_vouchers_send_callback_status
|
||||
ON kuaishou_industry_vouchers(send_callback_status);
|
||||
@@ -63,6 +63,11 @@ async function insertKuaishouIndustryVoucher(
|
||||
consume_details_json,
|
||||
consumed_at,
|
||||
destroyed_at,
|
||||
send_callback_status,
|
||||
send_callback_attempt_count,
|
||||
send_callback_last_error,
|
||||
send_callback_response_json,
|
||||
send_callback_sent_at,
|
||||
raw_payload_json,
|
||||
created_at,
|
||||
updated_at
|
||||
@@ -82,9 +87,14 @@ async function insertKuaishouIndustryVoucher(
|
||||
$12::jsonb,
|
||||
$13,
|
||||
$14,
|
||||
$15::jsonb,
|
||||
$15,
|
||||
$16,
|
||||
$17
|
||||
$17,
|
||||
$18::jsonb,
|
||||
$19,
|
||||
$20::jsonb,
|
||||
$21,
|
||||
$22
|
||||
)
|
||||
ON CONFLICT (oid, unit_index) DO UPDATE
|
||||
SET
|
||||
@@ -100,6 +110,10 @@ async function insertKuaishouIndustryVoucher(
|
||||
task_id = COALESCE(kuaishou_industry_vouchers.task_id, EXCLUDED.task_id),
|
||||
valid_start_time = EXCLUDED.valid_start_time,
|
||||
valid_end_time = EXCLUDED.valid_end_time,
|
||||
send_callback_status = EXCLUDED.send_callback_status,
|
||||
send_callback_last_error = EXCLUDED.send_callback_last_error,
|
||||
send_callback_response_json = EXCLUDED.send_callback_response_json,
|
||||
send_callback_sent_at = EXCLUDED.send_callback_sent_at,
|
||||
raw_payload_json = EXCLUDED.raw_payload_json,
|
||||
updated_at = EXCLUDED.updated_at
|
||||
RETURNING *
|
||||
@@ -119,6 +133,11 @@ async function insertKuaishouIndustryVoucher(
|
||||
stringifyJson(input.consumeDetailsJson ?? []),
|
||||
input.consumedAt || null,
|
||||
input.destroyedAt || null,
|
||||
input.sendCallbackStatus || 'success',
|
||||
normalizeNonNegativeInteger(input.sendCallbackAttemptCount, 0),
|
||||
input.sendCallbackLastError || '',
|
||||
stringifyJson(input.sendCallbackResponseJson ?? {}),
|
||||
input.sendCallbackSentAt || null,
|
||||
stringifyJson(input.rawPayloadJson ?? {}),
|
||||
input.createdAt,
|
||||
input.updatedAt,
|
||||
@@ -277,6 +296,33 @@ function normalizeVoucherPatchColumns(
|
||||
columns.push({ column: 'destroyed_at', value: patch.destroyedAt || null })
|
||||
}
|
||||
|
||||
if (patch.sendCallbackStatus !== undefined) {
|
||||
columns.push({ column: 'send_callback_status', value: patch.sendCallbackStatus || 'success' })
|
||||
}
|
||||
|
||||
if (patch.sendCallbackAttemptCount !== undefined) {
|
||||
columns.push({
|
||||
column: 'send_callback_attempt_count',
|
||||
value: normalizeNonNegativeInteger(patch.sendCallbackAttemptCount, 0),
|
||||
})
|
||||
}
|
||||
|
||||
if (patch.sendCallbackLastError !== undefined) {
|
||||
columns.push({ column: 'send_callback_last_error', value: patch.sendCallbackLastError || '' })
|
||||
}
|
||||
|
||||
if (patch.sendCallbackResponseJson !== undefined) {
|
||||
columns.push({
|
||||
column: 'send_callback_response_json',
|
||||
value: stringifyJson(patch.sendCallbackResponseJson ?? {}),
|
||||
cast: '::jsonb',
|
||||
})
|
||||
}
|
||||
|
||||
if (patch.sendCallbackSentAt !== undefined) {
|
||||
columns.push({ column: 'send_callback_sent_at', value: patch.sendCallbackSentAt || null })
|
||||
}
|
||||
|
||||
if (patch.rawPayloadJson !== undefined) {
|
||||
columns.push({
|
||||
column: 'raw_payload_json',
|
||||
@@ -337,6 +383,11 @@ function normalizeNullableId(value: unknown): number | null {
|
||||
return Number.isFinite(parsed) && parsed > 0 ? parsed : null
|
||||
}
|
||||
|
||||
function normalizeNonNegativeInteger(value: unknown, fallback: number) {
|
||||
const parsed = Number(value)
|
||||
return Number.isInteger(parsed) && parsed >= 0 ? parsed : fallback
|
||||
}
|
||||
|
||||
function stringifyJson(value: unknown): string {
|
||||
if (typeof value === 'string') {
|
||||
return value
|
||||
|
||||
@@ -638,7 +638,8 @@ function hasKuaishouIndustryVoucherContext(value: JsonObject): boolean {
|
||||
: {};
|
||||
const voucherCode = String(voucher.voucherCode || voucher.eticketId || "").trim();
|
||||
const status = String(voucher.status || "UNUSED").trim().toUpperCase();
|
||||
return Boolean(voucherCode && status !== "DESTROYED");
|
||||
const sendCallbackStatus = String(voucher.sendCallbackStatus || "success").trim().toLowerCase();
|
||||
return Boolean(voucherCode && status !== "DESTROYED" && sendCallbackStatus === "success");
|
||||
}
|
||||
|
||||
export function buildDispatchStockItems(
|
||||
|
||||
@@ -92,7 +92,7 @@ export async function createOpen91Order(payload = {}, { requestId = '' } = {}) {
|
||||
outTradeNo: '',
|
||||
orderStatus: 30,
|
||||
failCode: OPEN_91_DEFAULT_FAIL_CODE,
|
||||
failReason: '商品未配置或订单无法履约',
|
||||
failReason: resolveOpen91CreateFailReason(result.ignoreReason),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -104,3 +104,11 @@ export async function createOpen91Order(payload = {}, { requestId = '' } = {}) {
|
||||
cards: '',
|
||||
})
|
||||
}
|
||||
|
||||
function resolveOpen91CreateFailReason(ignoreReason: unknown) {
|
||||
if (String(ignoreReason || '').trim() === 'kuaishou_industry_send_callback_unconfirmed') {
|
||||
return '电子凭证发码回调未成功,订单暂不能履约'
|
||||
}
|
||||
|
||||
return '商品未配置或订单无法履约'
|
||||
}
|
||||
|
||||
@@ -4,9 +4,14 @@ import {
|
||||
updateOrder,
|
||||
} from '../../repositories/order-repo.js'
|
||||
import { replaceOrderItems } from '../../repositories/order-item-repo.js'
|
||||
import { listKuaishouIndustryVouchersByOid } from '../../repositories/kuaishou-industry-voucher-repo.js'
|
||||
import { syncDeliveryTasksForOrder } from './delivery-task-service.js'
|
||||
import { resolveOrderItemForFulfillment } from './product-match-service.js'
|
||||
import { bindKuaishouIndustryVouchersToOrderTasks } from '../platforms/kuaishou-industry/voucher-binding-service.js'
|
||||
import {
|
||||
isKuaishouIndustryVoucherSendCallbackSuccess,
|
||||
resolveKuaishouIndustryVoucherSendCallbackMessage,
|
||||
} from '../platforms/kuaishou-industry/voucher-service.js'
|
||||
import { nowIso } from '../../utils/time.js'
|
||||
import { logIntegration } from '../../utils/logger.js'
|
||||
import { createHttpError } from '../../utils/http.js'
|
||||
@@ -184,6 +189,25 @@ export async function upsertOrderFromSource(
|
||||
})),
|
||||
)
|
||||
|
||||
const industryVoucherGate = await resolveKuaishouIndustryVoucherGate(order.platform_order_id)
|
||||
if (!industryVoucherGate.allowed) {
|
||||
logIntegration('[order-service]', `${sourceLabel} 订单暂停履约:电子凭证发码回调未确认`, {
|
||||
orderId: order.id,
|
||||
provider: order.provider,
|
||||
platform: order.platform,
|
||||
platformOrderId: order.platform_order_id,
|
||||
voucherCount: industryVoucherGate.voucherCount,
|
||||
reason: industryVoucherGate.reason,
|
||||
}, { level: 'warn' })
|
||||
|
||||
return {
|
||||
ignoreReason: 'kuaishou_industry_send_callback_unconfirmed',
|
||||
order,
|
||||
orderItems,
|
||||
tasks: [],
|
||||
}
|
||||
}
|
||||
|
||||
const tasks = await syncDeliveryTasksForOrder(order, orderItems)
|
||||
await bindKuaishouIndustryVouchersToOrderTasks(order, tasks, {
|
||||
source: `${sourceLabel}_order_upsert`,
|
||||
@@ -209,6 +233,36 @@ export async function upsertOrderFromSource(
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveKuaishouIndustryVoucherGate(platformOrderId: unknown) {
|
||||
const oid = String(platformOrderId || '').trim()
|
||||
if (!oid) {
|
||||
return {
|
||||
allowed: true,
|
||||
voucherCount: 0,
|
||||
reason: '',
|
||||
}
|
||||
}
|
||||
|
||||
const vouchers = await listKuaishouIndustryVouchersByOid(oid)
|
||||
if (vouchers.length === 0 || vouchers.every(isKuaishouIndustryVoucherSendCallbackSuccess)) {
|
||||
return {
|
||||
allowed: true,
|
||||
voucherCount: vouchers.length,
|
||||
reason: '',
|
||||
}
|
||||
}
|
||||
|
||||
const blockedVoucher = vouchers.find((voucher) => !isKuaishouIndustryVoucherSendCallbackSuccess(voucher))
|
||||
|
||||
return {
|
||||
allowed: false,
|
||||
voucherCount: vouchers.length,
|
||||
reason: blockedVoucher
|
||||
? resolveKuaishouIndustryVoucherSendCallbackMessage(blockedVoucher)
|
||||
: '电子凭证发码回调未确认',
|
||||
}
|
||||
}
|
||||
|
||||
const ORDER_STATUS_PRIORITY = {
|
||||
created: 0,
|
||||
paid: 1,
|
||||
|
||||
@@ -13,7 +13,10 @@ import {
|
||||
buildIndustryErrorResponse,
|
||||
buildIndustryQueryCodeData,
|
||||
} from './response.js'
|
||||
import { buildKuaishouIndustryEticketFromVoucher } from './voucher-service.js'
|
||||
import {
|
||||
buildKuaishouIndustryEticketFromVoucher,
|
||||
isKuaishouIndustryVoucherSendCallbackSuccess,
|
||||
} from './voucher-service.js'
|
||||
|
||||
type JsonObject = Record<string, any>
|
||||
|
||||
@@ -33,6 +36,9 @@ export async function handleQueryCode(rawBody: JsonObject = {}) {
|
||||
if (!matched) {
|
||||
return buildIndustryErrorResponse(4012005, `卡券不存在: ${params.eticketId}`)
|
||||
}
|
||||
if (!isKuaishouIndustryVoucherSendCallbackSuccess(matched)) {
|
||||
return buildIndustryErrorResponse(4012005, `卡券尚未完成发码: ${params.eticketId}`)
|
||||
}
|
||||
|
||||
const eticket = buildKuaishouIndustryEticketFromVoucher(matched, params.eticketType)
|
||||
return buildIndustrySuccessResponse(
|
||||
@@ -45,7 +51,8 @@ export async function handleQueryCode(rawBody: JsonObject = {}) {
|
||||
)
|
||||
}
|
||||
|
||||
const vouchers = await listKuaishouIndustryVouchersByOid(normalizedOid)
|
||||
const vouchers = (await listKuaishouIndustryVouchersByOid(normalizedOid))
|
||||
.filter(isKuaishouIndustryVoucherSendCallbackSuccess)
|
||||
if (vouchers.length === 0) {
|
||||
return buildIndustryErrorResponse(4012002, `订单不存在: ${normalizedOid}`)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { findLatestOrderByPlatformOrderId } from '../../../repositories/order-repo.js'
|
||||
import { listTasksByOrderId } from '../../../repositories/task-repo.js'
|
||||
import { upsertKuaishouIndustryVoucher } from '../../../repositories/kuaishou-industry-voucher-repo.js'
|
||||
import {
|
||||
updateKuaishouIndustryVoucherByCode,
|
||||
upsertKuaishouIndustryVoucher,
|
||||
} from '../../../repositories/kuaishou-industry-voucher-repo.js'
|
||||
import { OPEN_91_PLATFORM, OPEN_91_PROVIDER } from '../../open-91/config.js'
|
||||
import { normalizeTimestampIso } from '../../../utils/time.js'
|
||||
import { logIntegration } from '../../../utils/logger.js'
|
||||
@@ -11,6 +14,7 @@ import {
|
||||
import { normalizeSendCodePayload, assertSendCodePayload } from './payload.js'
|
||||
import { assertKuaishouIndustrySignature } from './crypto.js'
|
||||
import {
|
||||
buildIndustryErrorResponse,
|
||||
buildIndustrySuccessResponse,
|
||||
buildIndustryEticketItem,
|
||||
buildIndustrySendCodeData,
|
||||
@@ -18,11 +22,12 @@ import {
|
||||
import { sendCallback } from './send-callback-service.js'
|
||||
import {
|
||||
buildKuaishouIndustryEticketFromVoucher,
|
||||
KUAISHOU_INDUSTRY_SEND_CALLBACK_STATUS,
|
||||
resolveKuaishouIndustryVoucherValidity,
|
||||
} from './voucher-service.js'
|
||||
import { bindKuaishouIndustryVouchersToOrderTasks } from './voucher-binding-service.js'
|
||||
import { parseAmountToFen } from '../../../utils/money.js'
|
||||
import type { OrderRow } from '../../../types/repository/rows.js'
|
||||
import type { KuaishouIndustryVoucherRow, OrderRow } from '../../../types/repository/rows.js'
|
||||
|
||||
type JsonObject = Record<string, any>
|
||||
|
||||
@@ -47,7 +52,7 @@ export async function handleSendCode(rawBody: JsonObject = {}) {
|
||||
const targetTaskCount = params.num > 0 ? params.num : 1
|
||||
const tasks = order ? await listTasksByOrderId(order.id) : []
|
||||
const validity = resolveKuaishouIndustryVoucherValidity(params, nowMs)
|
||||
const vouchers = []
|
||||
const vouchers: KuaishouIndustryVoucherRow[] = []
|
||||
|
||||
for (let index = 0; index < targetTaskCount; index += 1) {
|
||||
const unitIndex = index + 1
|
||||
@@ -60,6 +65,10 @@ export async function handleSendCode(rawBody: JsonObject = {}) {
|
||||
orderId: order?.id || null,
|
||||
taskId: task?.id || null,
|
||||
status: 'UNUSED',
|
||||
sendCallbackStatus: KUAISHOU_INDUSTRY_SEND_CALLBACK_STATUS.PENDING,
|
||||
sendCallbackLastError: '',
|
||||
sendCallbackResponseJson: {},
|
||||
sendCallbackSentAt: null,
|
||||
validStartTime: validity.validStartTime,
|
||||
validEndTime: validity.validEndTime,
|
||||
rawPayloadJson: {
|
||||
@@ -76,30 +85,15 @@ export async function handleSendCode(rawBody: JsonObject = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
if (order) {
|
||||
await bindKuaishouIndustryVouchersToOrderTasks(order, tasks, {
|
||||
source: 'kuaishou_industry_send_code',
|
||||
now,
|
||||
})
|
||||
}
|
||||
|
||||
const etickets = vouchers.map((voucher) =>
|
||||
buildKuaishouIndustryEticketFromVoucher(voucher, params.eticketType),
|
||||
)
|
||||
|
||||
const response = buildIndustrySuccessResponse(
|
||||
buildIndustrySendCodeData({
|
||||
oid: normalizedOid,
|
||||
sendType: params.sendType,
|
||||
sendNum: etickets.length,
|
||||
etickets,
|
||||
}),
|
||||
)
|
||||
|
||||
fireSendCallback({
|
||||
const callbackResult = await sendAndRecordCallback({
|
||||
oid: normalizedOid,
|
||||
sendType: params.sendType,
|
||||
etickets,
|
||||
vouchers,
|
||||
sellerId: params.sellerId,
|
||||
token: params.token,
|
||||
eticketType: params.eticketType,
|
||||
@@ -107,13 +101,35 @@ export async function handleSendCode(rawBody: JsonObject = {}) {
|
||||
preferredTotalGoodsValue: resolveSendCallbackPreferredTotalGoodsValue(order),
|
||||
})
|
||||
|
||||
return response
|
||||
if (!callbackResult.success) {
|
||||
return buildIndustryErrorResponse(
|
||||
4010003,
|
||||
resolveSendCallbackFailureMessage(callbackResult),
|
||||
)
|
||||
}
|
||||
|
||||
if (order) {
|
||||
await bindKuaishouIndustryVouchersToOrderTasks(order, tasks, {
|
||||
source: 'kuaishou_industry_send_code',
|
||||
now,
|
||||
})
|
||||
}
|
||||
|
||||
return buildIndustrySuccessResponse(
|
||||
buildIndustrySendCodeData({
|
||||
oid: normalizedOid,
|
||||
sendType: params.sendType,
|
||||
sendNum: etickets.length,
|
||||
etickets,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
function fireSendCallback(input: {
|
||||
async function sendAndRecordCallback(input: {
|
||||
oid: string
|
||||
sendType: string
|
||||
etickets: ReturnType<typeof buildIndustryEticketItem>[]
|
||||
vouchers: KuaishouIndustryVoucherRow[]
|
||||
sellerId: string
|
||||
token: string
|
||||
eticketType?: string
|
||||
@@ -139,7 +155,7 @@ function fireSendCallback(input: {
|
||||
const sendNum = eticketItems.reduce((sum, e) => sum + e.num, 0)
|
||||
const totalGoodsValue = goodsValuePlan.totalGoodsValue
|
||||
|
||||
logIntegration('[kuaishou-industry/send-code]', '电子凭证发货回调已调度', {
|
||||
logIntegration('[kuaishou-industry/send-code]', '准备发起电子凭证发货回调', {
|
||||
oid: input.oid,
|
||||
sellerId: input.sellerId,
|
||||
sendType: input.sendType,
|
||||
@@ -151,7 +167,9 @@ function fireSendCallback(input: {
|
||||
eticketType: input.eticketType || '',
|
||||
})
|
||||
|
||||
sendCallback({
|
||||
let result: Awaited<ReturnType<typeof sendCallback>>
|
||||
try {
|
||||
result = await sendCallback({
|
||||
oid: input.oid,
|
||||
sendType: input.sendType,
|
||||
etickets: eticketItemsWithGoodsValue,
|
||||
@@ -160,26 +178,63 @@ function fireSendCallback(input: {
|
||||
totalGoodsValue,
|
||||
token: input.token,
|
||||
...(input.eticketType ? { eticketType: input.eticketType } : {}),
|
||||
}).then((result) => {
|
||||
})
|
||||
} catch (error) {
|
||||
result = {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
}
|
||||
}
|
||||
|
||||
const sentAt = normalizeTimestampIso(new Date().toISOString())
|
||||
await Promise.all(input.vouchers.map((voucher) =>
|
||||
updateKuaishouIndustryVoucherByCode(voucher.voucher_code, {
|
||||
sendCallbackStatus: result.success
|
||||
? KUAISHOU_INDUSTRY_SEND_CALLBACK_STATUS.SUCCESS
|
||||
: KUAISHOU_INDUSTRY_SEND_CALLBACK_STATUS.FAILED,
|
||||
sendCallbackAttemptCount: normalizePositiveInteger(voucher.send_callback_attempt_count) + 1,
|
||||
sendCallbackLastError: result.success ? '' : resolveSendCallbackFailureMessage(result),
|
||||
sendCallbackResponseJson: result.response || {},
|
||||
sendCallbackSentAt: sentAt,
|
||||
updatedAt: sentAt,
|
||||
}),
|
||||
))
|
||||
|
||||
if (!result.success) {
|
||||
logIntegration('[kuaishou-industry/send-code]', '电子凭证发货回调异步执行失败', {
|
||||
logIntegration('[kuaishou-industry/send-code]', '电子凭证发货回调执行失败,发码请求已拒绝继续', {
|
||||
oid: input.oid,
|
||||
error: result.error || '',
|
||||
response: result.response || null,
|
||||
}, { level: 'warn' })
|
||||
return
|
||||
return result
|
||||
}
|
||||
|
||||
logIntegration('[kuaishou-industry/send-code]', '电子凭证发货回调异步执行完成', {
|
||||
logIntegration('[kuaishou-industry/send-code]', '电子凭证发货回调执行完成', {
|
||||
oid: input.oid,
|
||||
response: result.response || null,
|
||||
})
|
||||
}).catch((err) => {
|
||||
logIntegration('[kuaishou-industry/send-code]', '电子凭证发货回调异步执行异常', {
|
||||
oid: input.oid,
|
||||
error: err instanceof Error ? err.message : String(err),
|
||||
}, { level: 'warn' })
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
function resolveSendCallbackFailureMessage(result: Awaited<ReturnType<typeof sendCallback>>) {
|
||||
const error = String(result.error || '').trim()
|
||||
if (error) {
|
||||
return error
|
||||
}
|
||||
|
||||
const response = parseJsonObject(result.response)
|
||||
const errorMsg = String(response.error_msg || response.message || response.msg || '').trim()
|
||||
if (errorMsg) {
|
||||
return errorMsg
|
||||
}
|
||||
|
||||
const resultCode = String(response.result || response.code || '').trim()
|
||||
if (resultCode) {
|
||||
return `电子凭证发货回调失败,平台返回码:${resultCode}`
|
||||
}
|
||||
|
||||
return '电子凭证发货回调失败'
|
||||
}
|
||||
|
||||
export function resolveSendCallbackGoodsValuePlan(
|
||||
|
||||
@@ -6,6 +6,11 @@ import {
|
||||
import { normalizeTimestampIso } from '../../../utils/time.js'
|
||||
import { parseTaskContext } from '../../../utils/task-json.js'
|
||||
import { normalizeKuaishouCloudFlow } from '../../fulfillment/kuaishou-cloud/domain.js'
|
||||
import {
|
||||
isKuaishouIndustryVoucherSendCallbackSuccess,
|
||||
normalizeKuaishouIndustrySendCallbackStatus,
|
||||
resolveKuaishouIndustryVoucherSendCallbackMessage,
|
||||
} from './voucher-service.js'
|
||||
import type {
|
||||
KuaishouIndustryVoucherRow,
|
||||
OrderRow,
|
||||
@@ -41,7 +46,7 @@ export async function bindKuaishouIndustryVouchersToOrderTasks(
|
||||
const currentVoucher = nextVoucher || voucher
|
||||
bound.push(currentVoucher)
|
||||
|
||||
if (task) {
|
||||
if (task && isKuaishouIndustryVoucherSendCallbackSuccess(currentVoucher)) {
|
||||
await attachKuaishouIndustryVoucherToTask(task, currentVoucher, {
|
||||
source: options.source || 'voucher_bind',
|
||||
now,
|
||||
@@ -67,12 +72,16 @@ export async function attachKuaishouIndustryVoucherToTask(
|
||||
context.kuaishouIndustryVoucher,
|
||||
now,
|
||||
)
|
||||
const sendCallbackConfirmed = isKuaishouIndustryVoucherSendCallbackSuccess(voucher)
|
||||
const nextContext: JsonObject = {
|
||||
...context,
|
||||
kuaishouIndustryVoucher: nextVoucherContext,
|
||||
}
|
||||
|
||||
if (String(task.executor_key || '').trim() === 'kuaishou_ct_assisted' || context.kuaishouCloudFulfillment) {
|
||||
if (
|
||||
sendCallbackConfirmed &&
|
||||
(String(task.executor_key || '').trim() === 'kuaishou_ct_assisted' || context.kuaishouCloudFulfillment)
|
||||
) {
|
||||
const flow = normalizeKuaishouCloudFlow(context.kuaishouCloudFulfillment)
|
||||
const consumedAt = voucher.consumed_at || nextVoucherContext.consumedAt || null
|
||||
const status = String(voucher.status || 'UNUSED').trim().toUpperCase()
|
||||
@@ -143,6 +152,10 @@ export function buildKuaishouIndustryVoucherContext(
|
||||
voucherCode: String(voucher.voucher_code || '').trim(),
|
||||
unitIndex: Number(voucher.unit_index || 0) || 0,
|
||||
status,
|
||||
sendCallbackStatus: normalizeKuaishouIndustrySendCallbackStatus(voucher.send_callback_status),
|
||||
sendCallbackLastError: resolveKuaishouIndustryVoucherSendCallbackMessage(voucher),
|
||||
sendCallbackAttemptCount: Number(voucher.send_callback_attempt_count || 0) || 0,
|
||||
sendCallbackSentAt: voucher.send_callback_sent_at || existing.sendCallbackSentAt || null,
|
||||
validStartTime: Number(voucher.valid_start_time || 0) || 0,
|
||||
validEndTime: Number(voucher.valid_end_time || 0) || 0,
|
||||
verifiedAt: existing.verifiedAt || now,
|
||||
|
||||
@@ -12,6 +12,11 @@ import type { KuaishouIndustryVoucherRow, TaskRow } from '../../../types/reposit
|
||||
type JsonObject = Record<string, any>
|
||||
|
||||
export const KUAISHOU_INDUSTRY_DEFAULT_VALID_DAYS = 3
|
||||
export const KUAISHOU_INDUSTRY_SEND_CALLBACK_STATUS = {
|
||||
PENDING: 'pending',
|
||||
SUCCESS: 'success',
|
||||
FAILED: 'failed',
|
||||
} as const
|
||||
|
||||
export function resolveKuaishouIndustryVoucherValidity(
|
||||
input: {
|
||||
@@ -61,6 +66,43 @@ export function buildKuaishouIndustryEticketFromVoucher(
|
||||
})
|
||||
}
|
||||
|
||||
export function normalizeKuaishouIndustrySendCallbackStatus(value: unknown) {
|
||||
const normalized = String(value || '').trim().toLowerCase()
|
||||
if (
|
||||
normalized === KUAISHOU_INDUSTRY_SEND_CALLBACK_STATUS.PENDING ||
|
||||
normalized === KUAISHOU_INDUSTRY_SEND_CALLBACK_STATUS.FAILED
|
||||
) {
|
||||
return normalized
|
||||
}
|
||||
|
||||
return KUAISHOU_INDUSTRY_SEND_CALLBACK_STATUS.SUCCESS
|
||||
}
|
||||
|
||||
export function isKuaishouIndustryVoucherSendCallbackSuccess(
|
||||
voucher: Pick<KuaishouIndustryVoucherRow, 'send_callback_status'>,
|
||||
) {
|
||||
return normalizeKuaishouIndustrySendCallbackStatus(voucher.send_callback_status) ===
|
||||
KUAISHOU_INDUSTRY_SEND_CALLBACK_STATUS.SUCCESS
|
||||
}
|
||||
|
||||
export function resolveKuaishouIndustryVoucherSendCallbackMessage(
|
||||
voucher: Pick<KuaishouIndustryVoucherRow, 'send_callback_status' | 'send_callback_last_error'>,
|
||||
) {
|
||||
const status = normalizeKuaishouIndustrySendCallbackStatus(voucher.send_callback_status)
|
||||
if (status === KUAISHOU_INDUSTRY_SEND_CALLBACK_STATUS.SUCCESS) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const errorMessage = String(voucher.send_callback_last_error || '').trim()
|
||||
if (errorMessage) {
|
||||
return errorMessage
|
||||
}
|
||||
|
||||
return status === KUAISHOU_INDUSTRY_SEND_CALLBACK_STATUS.PENDING
|
||||
? '电子凭证发码回调尚未成功,暂不能继续履约'
|
||||
: '电子凭证发码回调失败,暂不能继续履约'
|
||||
}
|
||||
|
||||
export async function consumeKuaishouIndustryVouchersForTask(
|
||||
task: TaskRow,
|
||||
input: {
|
||||
@@ -149,6 +191,15 @@ export async function consumeKuaishouIndustryVoucher(
|
||||
}
|
||||
}
|
||||
|
||||
if (!isKuaishouIndustryVoucherSendCallbackSuccess(voucher)) {
|
||||
return {
|
||||
ok: false,
|
||||
voucher,
|
||||
callbackSuccess: false,
|
||||
errorMessage: resolveKuaishouIndustryVoucherSendCallbackMessage(voucher),
|
||||
}
|
||||
}
|
||||
|
||||
const now = new Date().toISOString()
|
||||
const consumeTime = Number(input.consumeTime || Date.now()) || Date.now()
|
||||
const consumeType = String(input.consumeType || 'delivery').trim() || 'delivery'
|
||||
|
||||
@@ -142,6 +142,11 @@ export type KuaishouIndustryVoucherUpsertInput = {
|
||||
consumeDetailsJson?: string | Record<string, unknown> | unknown[]
|
||||
consumedAt?: string | null
|
||||
destroyedAt?: string | null
|
||||
sendCallbackStatus?: string
|
||||
sendCallbackAttemptCount?: number
|
||||
sendCallbackLastError?: string
|
||||
sendCallbackResponseJson?: string | Record<string, unknown>
|
||||
sendCallbackSentAt?: string | null
|
||||
rawPayloadJson?: string | Record<string, unknown>
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
|
||||
@@ -120,6 +120,11 @@ export type KuaishouIndustryVoucherRow = {
|
||||
consume_details_json: string | Record<string, unknown> | unknown[]
|
||||
consumed_at: string | null
|
||||
destroyed_at: string | null
|
||||
send_callback_status: string
|
||||
send_callback_attempt_count: number
|
||||
send_callback_last_error: string
|
||||
send_callback_response_json: string | Record<string, unknown>
|
||||
send_callback_sent_at: string | null
|
||||
raw_payload_json: string | Record<string, unknown>
|
||||
created_at: string
|
||||
updated_at: string
|
||||
|
||||
Reference in New Issue
Block a user