修正快手行业电子凭证核销参数

This commit is contained in:
yml2213
2026-07-09 19:46:40 +08:00
parent 179f34408b
commit 01171c1761
9 changed files with 77 additions and 35 deletions
@@ -104,11 +104,12 @@ export async function reverseAdminKuaishouIndustryVoucher(input: JsonObject = {}
export async function consumeAdminKuaishouIndustryVoucherByCode(input: JsonObject = {}) {
const voucher = await resolveRequiredVoucher(input)
const task = voucher.task_id ? await getTaskById(voucher.task_id) : null
const consumeType = String(input.consumeType || '').trim()
const consumeInput: Parameters<typeof consumeKuaishouIndustryVoucher>[1] = {
source: 'admin_tool_manual_consume',
token: String(input.token || voucher.token || '').trim(),
consumeType: String(input.consumeType || 'delivery').trim() || 'delivery',
consumeTime: Date.now(),
...(consumeType ? { consumeType } : {}),
...(input.serialNum ? { serialNum: String(input.serialNum).trim() } : {}),
...(input.eticketType ? { eticketType: String(input.eticketType).trim() } : {}),
...(input.storeName ? { storeName: String(input.storeName).trim() } : {}),
@@ -857,12 +857,13 @@ export async function consumeAdminTaskKuaishouIndustryVoucher(
}
const voucher = await getRequiredIndustryVoucherForTask(task)
const consumeType = String(payload.consumeType || '').trim()
const result = await consumeKuaishouIndustryVoucher(voucher, {
source: 'admin_manual_consume',
task,
token: voucher.token,
consumeType: String(payload.consumeType || 'delivery').trim() || 'delivery',
consumeTime: Date.now(),
...(consumeType ? { consumeType } : {}),
...(payload.storeName ? { storeName: payload.storeName } : {}),
...(payload.storeAddress ? { storeAddress: payload.storeAddress } : {}),
...(payload.expressCode ? { expressCode: payload.expressCode } : {}),
@@ -817,7 +817,6 @@ export async function returnKuaishouCloudFulfillmentTask(
? await consumeKuaishouIndustryVouchersForTask(task, {
source: String(options.source || "system_auto_finalize").trim() || "system_auto_finalize",
token: String(taskContext.kuaishouIndustryVoucher?.token || "").trim(),
consumeType: "delivery",
consumeTime: Date.now(),
})
: { ok: true, consumed: [], failed: [] };
@@ -146,7 +146,6 @@ export async function syncKuaishouFeifeiTaskStatus(task: TaskRow) {
if (order.rechargeStatus === 30) {
const consumeResult = await consumeKuaishouIndustryVouchersForTask(task, {
source: 'kuaishou_feifei_completed',
consumeType: 'delivery',
consumeTime: Date.now(),
})
const consumedVoucher = consumeResult.consumed[0] || null
@@ -0,0 +1,26 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import { buildConsumeCallbackBizParams } from './consume-callback-service.js'
test('buildConsumeCallbackBizParams omits eticket status', () => {
const bizParams = buildConsumeCallbackBizParams({
oid: '2619000086068982',
etickets: [{
id: 'KSVASXT6Q955WT4SS36',
num: 1,
status: 'CONSUMED',
} as any],
status: 'CONSUMED',
consumeType: 'consume',
consumeTime: 1783596649024,
token: 'ticket-token',
seriallNum: 'CONSUME-KSVASXT6Q955WT4SS36',
})
assert.deepEqual(bizParams.etickets, [{
id: 'KSVASXT6Q955WT4SS36',
num: 1,
}])
assert.equal(bizParams.consumeType, 'consume')
})
@@ -1,7 +1,7 @@
import { logInfo, logWarn } from '../../../utils/logger.js'
import { requestKuaishouIndustryOpenApi, type JsonObject } from './openapi-client.js'
type ConsumeCallbackInput = {
export type ConsumeCallbackInput = {
oid: string
sellerId?: string
etickets: Array<{
@@ -9,7 +9,6 @@ type ConsumeCallbackInput = {
code?: string
num: number
goodsValue?: number
status?: string
}>
status: string
consumeType: string
@@ -29,30 +28,7 @@ type ConsumeCallbackInput = {
export async function consumeCallback(
input: ConsumeCallbackInput,
): Promise<{ success: boolean; response?: JsonObject; error?: string }> {
const bizParams: JsonObject = {
oid: input.oid,
etickets: input.etickets.map((e) => {
const item: JsonObject = { id: e.id, num: e.num }
if (e.code) item.code = e.code
if (e.goodsValue != null) item.goodsValue = e.goodsValue
if (e.status) item.status = e.status
return item
}),
status: input.status,
consumeType: input.consumeType,
token: input.token,
}
if (input.consumeTime != null) bizParams.consumeTime = input.consumeTime
if (input.storeName) bizParams.storeName = input.storeName
if (input.storeAddress) bizParams.storeAddress = input.storeAddress
if (input.expressNo) bizParams.expressNo = input.expressNo
if (input.expressCode) bizParams.expressCode = input.expressCode
if (input.appointmentTime) bizParams.appointmentTime = input.appointmentTime
if (input.eticketType) bizParams.eticketType = input.eticketType
if (input.ext) bizParams.ext = input.ext
if (input.seriallNum) bizParams.seriallNum = input.seriallNum
if (input.consumePoiId != null) bizParams.consumePoiId = input.consumePoiId
const bizParams = buildConsumeCallbackBizParams(input)
const result = await requestKuaishouIndustryOpenApi({
apiMethod: 'integration.callback.virtual.eticket.consume',
@@ -115,3 +91,31 @@ export async function consumeCallback(
...(result.error ? { error: result.error } : {}),
}
}
export function buildConsumeCallbackBizParams(input: ConsumeCallbackInput): JsonObject {
const bizParams: JsonObject = {
oid: input.oid,
etickets: input.etickets.map((e) => {
const item: JsonObject = { id: e.id, num: e.num }
if (e.code) item.code = e.code
if (e.goodsValue != null) item.goodsValue = e.goodsValue
return item
}),
status: input.status,
consumeType: input.consumeType,
token: input.token,
}
if (input.consumeTime != null) bizParams.consumeTime = input.consumeTime
if (input.storeName) bizParams.storeName = input.storeName
if (input.storeAddress) bizParams.storeAddress = input.storeAddress
if (input.expressNo) bizParams.expressNo = input.expressNo
if (input.expressCode) bizParams.expressCode = input.expressCode
if (input.appointmentTime) bizParams.appointmentTime = input.appointmentTime
if (input.eticketType) bizParams.eticketType = input.eticketType
if (input.ext) bizParams.ext = input.ext
if (input.seriallNum) bizParams.seriallNum = input.seriallNum
if (input.consumePoiId != null) bizParams.consumePoiId = input.consumePoiId
return bizParams
}
@@ -5,6 +5,7 @@ import { generateKuaishouIndustryVoucherCode } from '../../../repositories/kuais
import { buildKuaishouIndustryVoucherContext } from './voucher-binding-service.js'
import {
buildKuaishouIndustryEticketFromVoucher,
normalizeKuaishouIndustryConsumeType,
resolveKuaishouIndustryVoucherValidity,
} from './voucher-service.js'
@@ -62,6 +63,12 @@ test('resolveKuaishouIndustryVoucherValidity prefers actual certificate time ran
assert.equal(validity.validEndTime, 5000)
})
test('normalizeKuaishouIndustryConsumeType defaults to consume', () => {
assert.equal(normalizeKuaishouIndustryConsumeType(undefined), 'consume')
assert.equal(normalizeKuaishouIndustryConsumeType(''), 'consume')
assert.equal(normalizeKuaishouIndustryConsumeType(' pickup '), 'pickup')
})
test('buildKuaishouIndustryEticketFromVoucher mirrors voucher code into id and code', () => {
const eticket = buildKuaishouIndustryEticketFromVoucher({
voucher_code: 'KSV15K821FDHPMTX1ST',
@@ -17,6 +17,7 @@ export const KUAISHOU_INDUSTRY_SEND_CALLBACK_STATUS = {
SUCCESS: 'success',
FAILED: 'failed',
} as const
export const KUAISHOU_INDUSTRY_DEFAULT_CONSUME_TYPE = 'consume'
export function resolveKuaishouIndustryVoucherValidity(
input: {
@@ -124,7 +125,7 @@ export async function consumeKuaishouIndustryVouchersForTask(
for (const voucher of vouchers) {
const consumeInput: Parameters<typeof consumeKuaishouIndustryVoucher>[1] = {
source: input.source || 'fulfillment_completed',
consumeType: input.consumeType || 'delivery',
consumeType: normalizeKuaishouIndustryConsumeType(input.consumeType),
consumeTime: input.consumeTime || Date.now(),
task,
}
@@ -202,7 +203,7 @@ export async function consumeKuaishouIndustryVoucher(
const now = new Date().toISOString()
const consumeTime = Number(input.consumeTime || Date.now()) || Date.now()
const consumeType = String(input.consumeType || 'delivery').trim() || 'delivery'
const consumeType = normalizeKuaishouIndustryConsumeType(input.consumeType)
const serialNum = String(input.serialNum || voucher.consume_serial_num || `CONSUME-${voucher.voucher_code}`).trim()
const token = String(input.token || voucher.token || '').trim()
const consumeDetail = {
@@ -224,7 +225,6 @@ export async function consumeKuaishouIndustryVoucher(
etickets: [{
id: voucher.voucher_code,
num: 1,
status: 'CONSUMED',
}],
status: 'CONSUMED',
consumeType,
@@ -317,7 +317,7 @@ function resolveVoucherConsumeDetails(voucher: KuaishouIndustryVoucherRow): Json
return [{
serialNum: voucher.consume_serial_num,
consumeType: 'delivery',
consumeType: KUAISHOU_INDUSTRY_DEFAULT_CONSUME_TYPE,
consumeTime: voucher.consumed_at ? Date.parse(voucher.consumed_at) : 0,
}]
}
@@ -340,6 +340,11 @@ function normalizeVoucherStatus(value: unknown): string {
return 'UNUSED'
}
export function normalizeKuaishouIndustryConsumeType(value: unknown): string {
return String(value || KUAISHOU_INDUSTRY_DEFAULT_CONSUME_TYPE).trim() ||
KUAISHOU_INDUSTRY_DEFAULT_CONSUME_TYPE
}
function normalizePositiveTimestamp(value: unknown): number {
const parsed = Number(value)
return Number.isFinite(parsed) && parsed > 0 ? Math.trunc(parsed) : 0
@@ -91,7 +91,7 @@ export default function AdminKuaishouIndustryPage() {
})
const [toolForm, setToolForm] = useState<AdminKuaishouIndustryVoucherToolPayload>({
eticketType: DEFAULT_ETICKET_TYPE,
consumeType: 'delivery',
consumeType: 'consume',
})
const [refundDateRange, setRefundDateRange] = useState<DateRangeValue>(() => [
dayjs().subtract(1, 'day'),