From 4131d500f25fd09fd3fa96a344f7878f7521c630 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Tue, 7 Jul 2026 20:54:48 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=8C=E6=AD=A5=E9=A3=9E=E9=A3=9E=E6=A0=B8?= =?UTF-8?q?=E9=94=80=E5=88=B8=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fulfillment/kuaishou-feifei/index.ts | 28 +++++++++++++++++++ .../voucher-binding-service.ts | 8 ++++-- .../kuaishou-industry/voucher-service.test.ts | 25 +++++++++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/apps/backend/src/services/fulfillment/kuaishou-feifei/index.ts b/apps/backend/src/services/fulfillment/kuaishou-feifei/index.ts index f09fab23..469e1ab7 100644 --- a/apps/backend/src/services/fulfillment/kuaishou-feifei/index.ts +++ b/apps/backend/src/services/fulfillment/kuaishou-feifei/index.ts @@ -1,6 +1,7 @@ import { createTaskEvent } from '../../../repositories/task-event-repo.js' import { updateTask } from '../../../repositories/task-repo.js' import { createHttpError } from '../../../utils/http.js' +import { logIntegration } from '../../../utils/logger.js' import { parseTaskContext } from '../../../utils/task-json.js' import { nowIso } from '../../../utils/time.js' import { TASK_STATUS } from '../../../domain/task-status.js' @@ -9,6 +10,7 @@ import { queryKuaishouFeifeiOrder, } from '../../platforms/kuaishou-feifei/order-service.js' import { consumeKuaishouIndustryVouchersForTask } from '../../platforms/kuaishou-industry/voucher-service.js' +import { buildKuaishouIndustryVoucherContext } from '../../platforms/kuaishou-industry/voucher-binding-service.js' import type { TaskRow } from '../../../types/repository/rows.js' type JsonObject = Record @@ -127,6 +129,7 @@ export async function syncKuaishouFeifeiTaskStatus(task: TaskRow) { syncedAt: now, claimUrl: resolveKuaishouFeifeiOrderClaimUrl(order) || resolveKuaishouFeifeiClaimUrl(flow), }) + let nextIndustryVoucher = taskContext.kuaishouIndustryVoucher if (order.rechargeStatus === 30) { const consumeResult = await consumeKuaishouIndustryVouchersForTask(task, { @@ -134,6 +137,7 @@ export async function syncKuaishouFeifeiTaskStatus(task: TaskRow) { consumeType: 'delivery', consumeTime: Date.now(), }) + const consumedVoucher = consumeResult.consumed[0] || null if (consumeResult.ok || consumeResult.vouchers.length === 0) { nextTaskStatus = TASK_STATUS.COMPLETED @@ -143,12 +147,35 @@ export async function syncKuaishouFeifeiTaskStatus(task: TaskRow) { resultMessage = order.rechargeStatusLabel || 'kuaishou-feifei 履约成功' lastError = '' nextFlow.consumeStatus = consumeResult.vouchers.length > 0 ? 'success' : 'not_required' + if (consumedVoucher) { + nextIndustryVoucher = buildKuaishouIndustryVoucherContext( + consumedVoucher, + taskContext.kuaishouIndustryVoucher, + now, + ) + } + logIntegration('[kuaishou-feifei]', '飞飞履约完成,行业电子凭证核销处理完成', { + taskId: task.id, + platformOrderNo: nextFlow.platformOrderNo, + orderNo: nextFlow.orderNo, + voucherCount: consumeResult.vouchers.length, + consumedCount: consumeResult.consumed.length, + consumeStatus: nextFlow.consumeStatus, + }) } else { nextTaskStatus = TASK_STATUS.MANUAL_REVIEW resultCode = 'kuaishou_feifei_industry_consume_failed' resultMessage = consumeResult.failed[0]?.errorMessage || '电子凭证核销失败,请人工处理' lastError = resultMessage nextFlow.consumeStatus = 'failed' + logIntegration('[kuaishou-feifei]', '飞飞履约完成,但行业电子凭证核销失败', { + taskId: task.id, + platformOrderNo: nextFlow.platformOrderNo, + orderNo: nextFlow.orderNo, + voucherCount: consumeResult.vouchers.length, + failedCount: consumeResult.failed.length, + errorMessage: resultMessage, + }, { level: 'warn' }) } } else if ([40, 50, 60].includes(order.rechargeStatus)) { nextTaskStatus = order.rechargeStatus === 60 ? TASK_STATUS.CLOSED : TASK_STATUS.MANUAL_REVIEW @@ -167,6 +194,7 @@ export async function syncKuaishouFeifeiTaskStatus(task: TaskRow) { context_json: JSON.stringify({ ...taskContext, kuaishouFeifei: nextFlow, + kuaishouIndustryVoucher: nextIndustryVoucher, }), updated_at: now, }) diff --git a/apps/backend/src/services/platforms/kuaishou-industry/voucher-binding-service.ts b/apps/backend/src/services/platforms/kuaishou-industry/voucher-binding-service.ts index e2e2725b..ba6e7913 100644 --- a/apps/backend/src/services/platforms/kuaishou-industry/voucher-binding-service.ts +++ b/apps/backend/src/services/platforms/kuaishou-industry/voucher-binding-service.ts @@ -62,7 +62,11 @@ export async function attachKuaishouIndustryVoucherToTask( ) { const now = normalizeTimestampIso(options.now || new Date().toISOString()) const context = parseTaskContext(task) - const nextVoucherContext = buildVoucherContext(voucher, context.kuaishouIndustryVoucher, now) + const nextVoucherContext = buildKuaishouIndustryVoucherContext( + voucher, + context.kuaishouIndustryVoucher, + now, + ) const nextContext: JsonObject = { ...context, kuaishouIndustryVoucher: nextVoucherContext, @@ -121,7 +125,7 @@ function resolveTaskForVoucher(voucher: KuaishouIndustryVoucherRow, tasks: TaskR return tasks[unitIndex - 1] || null } -function buildVoucherContext( +export function buildKuaishouIndustryVoucherContext( voucher: KuaishouIndustryVoucherRow, existingValue: unknown, now: string, 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 cf61ebda..cdba2896 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 @@ -2,6 +2,7 @@ import test from 'node:test' import assert from 'node:assert/strict' import { generateKuaishouIndustryVoucherCode } from '../../../repositories/kuaishou-industry-voucher-repo.js' +import { buildKuaishouIndustryVoucherContext } from './voucher-binding-service.js' import { resolveKuaishouIndustryVoucherValidity } from './voucher-service.js' test('generateKuaishouIndustryVoucherCode creates non-numeric stable voucher code', () => { @@ -57,3 +58,27 @@ test('resolveKuaishouIndustryVoucherValidity prefers actual certificate time ran assert.equal(validity.validStartTime, 1000) assert.equal(validity.validEndTime, 5000) }) + +test('buildKuaishouIndustryVoucherContext maps consumed voucher state', () => { + const context = buildKuaishouIndustryVoucherContext( + { + oid: '2618800481389561', + voucher_code: 'KSV15K821FDHPMTX1ST', + token: 'ticket-token', + unit_index: 1, + status: 'CONSUMED', + valid_start_time: 1783427213868, + valid_end_time: 1786019213868, + consumed_at: '2026-07-07T12:36:54.626Z', + } as any, + { + verifiedAt: '2026-07-07T12:26:58.421Z', + }, + '2026-07-07T12:36:54.626Z', + ) + + assert.equal(context.status, 'CONSUMED') + assert.equal(context.consumedAt, '2026-07-07T12:36:54.626Z') + assert.equal(context.voucherCode, 'KSV15K821FDHPMTX1ST') + assert.equal(context.verifiedAt, '2026-07-07T12:26:58.421Z') +})