From 01171c176158e9202b28fce4d13cbf118e60fe0e Mon Sep 17 00:00:00 2001 From: yml2213 Date: Thu, 9 Jul 2026 19:46:40 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=BF=AB=E6=89=8B=E8=A1=8C?= =?UTF-8?q?=E4=B8=9A=E7=94=B5=E5=AD=90=E5=87=AD=E8=AF=81=E6=A0=B8=E9=94=80?= =?UTF-8?q?=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/kuaishou-industry-admin-service.ts | 3 +- .../admin/write/kuaishou-cloud-actions.ts | 3 +- .../kuaishou-cloud/task-finalization.ts | 1 - .../fulfillment/kuaishou-feifei/index.ts | 1 - .../consume-callback-service.test.ts | 26 +++++++++ .../consume-callback-service.ts | 56 ++++++++++--------- .../kuaishou-industry/voucher-service.test.ts | 7 +++ .../kuaishou-industry/voucher-service.ts | 13 +++-- .../pages/admin/AdminKuaishouIndustryPage.tsx | 2 +- 9 files changed, 77 insertions(+), 35 deletions(-) create mode 100644 apps/backend/src/services/platforms/kuaishou-industry/consume-callback-service.test.ts diff --git a/apps/backend/src/services/admin/kuaishou-industry-admin-service.ts b/apps/backend/src/services/admin/kuaishou-industry-admin-service.ts index cc5af94e..b07442aa 100644 --- a/apps/backend/src/services/admin/kuaishou-industry-admin-service.ts +++ b/apps/backend/src/services/admin/kuaishou-industry-admin-service.ts @@ -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[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() } : {}), diff --git a/apps/backend/src/services/admin/write/kuaishou-cloud-actions.ts b/apps/backend/src/services/admin/write/kuaishou-cloud-actions.ts index 5c9ec9d8..c7a58ad6 100644 --- a/apps/backend/src/services/admin/write/kuaishou-cloud-actions.ts +++ b/apps/backend/src/services/admin/write/kuaishou-cloud-actions.ts @@ -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 } : {}), diff --git a/apps/backend/src/services/fulfillment/kuaishou-cloud/task-finalization.ts b/apps/backend/src/services/fulfillment/kuaishou-cloud/task-finalization.ts index cdf7485b..da7f975a 100644 --- a/apps/backend/src/services/fulfillment/kuaishou-cloud/task-finalization.ts +++ b/apps/backend/src/services/fulfillment/kuaishou-cloud/task-finalization.ts @@ -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: [] }; diff --git a/apps/backend/src/services/fulfillment/kuaishou-feifei/index.ts b/apps/backend/src/services/fulfillment/kuaishou-feifei/index.ts index 2180cb3b..607081e8 100644 --- a/apps/backend/src/services/fulfillment/kuaishou-feifei/index.ts +++ b/apps/backend/src/services/fulfillment/kuaishou-feifei/index.ts @@ -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 diff --git a/apps/backend/src/services/platforms/kuaishou-industry/consume-callback-service.test.ts b/apps/backend/src/services/platforms/kuaishou-industry/consume-callback-service.test.ts new file mode 100644 index 00000000..d0df3acb --- /dev/null +++ b/apps/backend/src/services/platforms/kuaishou-industry/consume-callback-service.test.ts @@ -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') +}) diff --git a/apps/backend/src/services/platforms/kuaishou-industry/consume-callback-service.ts b/apps/backend/src/services/platforms/kuaishou-industry/consume-callback-service.ts index 7e96bc8b..9b955e24 100644 --- a/apps/backend/src/services/platforms/kuaishou-industry/consume-callback-service.ts +++ b/apps/backend/src/services/platforms/kuaishou-industry/consume-callback-service.ts @@ -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 +} diff --git a/apps/backend/src/services/platforms/kuaishou-industry/voucher-service.test.ts b/apps/backend/src/services/platforms/kuaishou-industry/voucher-service.test.ts index dc7a9ca6..47e5f56d 100644 --- a/apps/backend/src/services/platforms/kuaishou-industry/voucher-service.test.ts +++ b/apps/backend/src/services/platforms/kuaishou-industry/voucher-service.test.ts @@ -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', diff --git a/apps/backend/src/services/platforms/kuaishou-industry/voucher-service.ts b/apps/backend/src/services/platforms/kuaishou-industry/voucher-service.ts index e5ebd42f..c7c07607 100644 --- a/apps/backend/src/services/platforms/kuaishou-industry/voucher-service.ts +++ b/apps/backend/src/services/platforms/kuaishou-industry/voucher-service.ts @@ -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[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 diff --git a/apps/frontend/src/pages/admin/AdminKuaishouIndustryPage.tsx b/apps/frontend/src/pages/admin/AdminKuaishouIndustryPage.tsx index aa74da4b..235c8c37 100644 --- a/apps/frontend/src/pages/admin/AdminKuaishouIndustryPage.tsx +++ b/apps/frontend/src/pages/admin/AdminKuaishouIndustryPage.tsx @@ -91,7 +91,7 @@ export default function AdminKuaishouIndustryPage() { }) const [toolForm, setToolForm] = useState({ eticketType: DEFAULT_ETICKET_TYPE, - consumeType: 'delivery', + consumeType: 'consume', }) const [refundDateRange, setRefundDateRange] = useState(() => [ dayjs().subtract(1, 'day'),