From 90fe910d1cb9bb63915f2d99f0869b23d6dcb825 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Thu, 9 Jul 2026 21:12:23 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E8=87=AA=E5=8A=A8=E5=8F=91?= =?UTF-8?q?=E8=B4=A7=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 后台准备和发货复用统一履约服务,更新提示文案。 --- .../admin/write/kuaishou-cloud-actions.ts | 291 +----------------- .../src/pages/admin/AdminTaskDetailPage.tsx | 2 +- 2 files changed, 16 insertions(+), 277 deletions(-) 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 9e98bb27..0d0f4d90 100644 --- a/apps/backend/src/services/admin/write/kuaishou-cloud-actions.ts +++ b/apps/backend/src/services/admin/write/kuaishou-cloud-actions.ts @@ -6,13 +6,6 @@ import { listKuaishouIndustryVouchersByOid, listKuaishouIndustryVouchersByTaskId, } from '../../../repositories/kuaishou-industry-voucher-repo.js' -import { - buyCloudtentaclesSku, - getCloudtentaclesAsset, - listCloudtentaclesSku, - useCloudtentaclesSku, -} from '../../platforms/cloudtentacles/catalog-service.js' -import { getCloudtentaclesKnapsack } from '../../platforms/cloudtentacles/knapsack-service.js' import { backCloudtentaclesVirtualNumber, getCloudtentaclesBindInfo, @@ -28,24 +21,18 @@ import { nowIso } from '../../../utils/time.js' import { TASK_STATUS } from '../../../domain/task-status.js' import { createAdminViewerContext, parseTaskContext } from '../admin-read-shared-helpers.js' import { getRequiredTask, mapTaskActionPayload } from '../admin-task-read-helpers.js' +import { maskCode, maskPhone } from '../write-helpers.js' import { - ensureTaskClaimLink, - getTaskClaimExpiresAt, - maskCode, - maskPhone, -} from '../write-helpers.js' -import { rebindKuaishouCloudTaskRole } from '../../fulfillment/kuaishou-cloud/index.js' + dispatchKuaishouCloudFulfillmentTask, + prepareKuaishouCloudFulfillmentTask, + rebindKuaishouCloudTaskRole, +} from '../../fulfillment/kuaishou-cloud/index.js' import { isKuaishouCloudTask, normalizeKuaishouCloudFlow, normalizeKuaishouCloudRoleInfo, - prepareKuaishouCloudBindResourceWithFallback, - resolveKuaishouCloudBindUrlExpiresAt, - resolveKuaishouCloudBindingResources, - resolveKuaishouCloudVnKeyCandidates, resolvePersistedCloudtentaclesContext, } from './kuaishou-cloud-helpers.js' -import { syncKuaishouCloudRoleInfoBeforeDispatch } from '../../fulfillment/kuaishou-cloud/dispatch-role-sync.js' import type { AdminEntityIdInput, @@ -62,7 +49,6 @@ export async function prepareAdminTaskKuaishouCloudFulfillment( session: AdminViewerSessionInput | null = null, ): Promise { const task = await getRequiredTask(taskId) - const now = nowIso() const viewerContext = createAdminViewerContext(session) if (!viewerContext.canManageTaskLifecycle) { @@ -79,161 +65,15 @@ export async function prepareAdminTaskKuaishouCloudFulfillment( }) } - const taskContext = parseTaskContext(task) - const flow = normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment) - const cloudContext = resolvePersistedCloudtentaclesContext(flow.binding.cloudSourceKeys) - const [knapsack, skuList] = await Promise.all([ - getCloudtentaclesKnapsack(cloudContext), - listCloudtentaclesSku(cloudContext), - ]) - const resolvedBinding = resolveKuaishouCloudBindingResources(flow, { - skuItems: Array.isArray(skuList.items) ? skuList.items : [], - knapsackItems: Array.isArray(knapsack.items) ? knapsack.items : [], + const result = await prepareKuaishouCloudFulfillmentTask(task, { + source: 'admin_task_prepare', + actor: buildAdminActionActor(session), }) - const vnKeyCandidates = resolveKuaishouCloudVnKeyCandidates({ - flow, - binding: resolvedBinding, - }) - - if (!resolvedBinding.skuId || vnKeyCandidates.length === 0) { - throw createHttpError('当前任务缺少可用 cloud 资源,且无法根据商品名自动解析 SKU / VN Key', { - statusCode: 409, - errorCode: 'admin_task_kuaishou_cloud_missing_binding_config', - }) - } - - const flowWithResolvedBinding = { - ...flow, - binding: { - ...flow.binding, - skuId: resolvedBinding.skuId, - skuName: resolvedBinding.skuName, - vnKey: resolvedBinding.vnKey, - }, - } - const knapsackItem = resolvedBinding.knapsackItem - let usedKnapsack = Number(knapsackItem?.count || 0) > 0 - let purchaseTriggered = false - let assetBefore = 0 - let assetAfter = 0 - const claimLinkState = await ensureTaskClaimLink(task) - - if (!usedKnapsack) { - if (!flowWithResolvedBinding.purchase.autoBuyEnabled) { - throw createHttpError('背包中没有现成库存,且当前配置未开启自动购买', { - statusCode: 409, - errorCode: 'admin_task_kuaishou_cloud_auto_buy_disabled', - }) - } - - const asset = await getCloudtentaclesAsset(cloudContext) - const targetSku = resolvedBinding.skuItem - - if (!targetSku) { - throw createHttpError(`cloudtentacles 未找到 SKU ${flowWithResolvedBinding.binding.skuId}`, { - statusCode: 404, - errorCode: 'admin_task_kuaishou_cloud_sku_not_found', - }) - } - - assetBefore = Number(asset.asset || 0) || 0 - const targetPrice = Number(targetSku.price || 0) || 0 - const requiredAsset = targetPrice + flowWithResolvedBinding.purchase.minAssetReserve - if (assetBefore < requiredAsset) { - throw createHttpError(`余额不足,当前 ${assetBefore},至少需要 ${requiredAsset}`, { - statusCode: 409, - errorCode: 'admin_task_kuaishou_cloud_asset_not_enough', - }) - } - - await buyCloudtentaclesSku({ - ...cloudContext, - id: flowWithResolvedBinding.binding.skuId, - count: 1, - }) - purchaseTriggered = true - - const assetResult = await getCloudtentaclesAsset(cloudContext) - assetAfter = Number(assetResult.asset || 0) || 0 - } - - const preparedBinding = await prepareKuaishouCloudBindResourceWithFallback({ - cloudContext, - vnKeyCandidates, - }) - const vnId = preparedBinding.vnId - const vnPhone = preparedBinding.vnPhone - - const nextContext = { - ...taskContext, - kuaishouCloudFulfillment: { - ...flowWithResolvedBinding, - binding: { - ...flowWithResolvedBinding.binding, - resolvedSourceKey: cloudContext.resolvedSourceKey, - vnKey: preparedBinding.vnKey, - prepareStatus: 'ready', - vnId, - vnPhone, - bindUrl: preparedBinding.bindUrl, - bindPreparedAt: now, - bindExpiresAt: resolveKuaishouCloudBindUrlExpiresAt(now), - bindProbeAt: null as null, - bindProbeStatus: 'pending', - bindProbeMessage: '', - roleName: '', - roleId: '', - }, - role: { - status: 'pending', - name: '', - rid: '', - refreshedAt: null as null, - errorMessage: '', - rawInfo: null as null, - }, - purchase: { - ...flowWithResolvedBinding.purchase, - usedKnapsack, - purchaseTriggered, - assetBefore, - assetAfter, - purchaseAt: purchaseTriggered ? now : flowWithResolvedBinding.purchase.purchaseAt, - }, - }, - } - - const updatedTask = await updateTask(task.id, { - task_status: TASK_STATUS.WAITING_BINDING, - user_action_status: 'pending', - claim_token: claimLinkState.token || task.claim_token || '', - claim_expires_at: claimLinkState.expiredAt || getTaskClaimExpiresAt(task), - last_error: '', - context_json: JSON.stringify(nextContext), - updated_at: now, - }) - - await createTaskEvent( - task.id, - 'kuaishou_cloud_binding_prepared', - { - skuId: flowWithResolvedBinding.binding.skuId, - skuName: flowWithResolvedBinding.binding.skuName, - vnKey: preparedBinding.vnKey, - vnId, - vnPhoneMasked: maskPhone(vnPhone), - bindUrl: preparedBinding.bindUrl, - purchaseTriggered, - usedKnapsack, - resolvedByName: resolvedBinding.resolvedByName, - }, - now, - ) return { - task: mapTaskActionPayload(updatedTask), - claimUrl: claimLinkState.claimUrl, - token: claimLinkState.token, + task: mapTaskActionPayload(result.task), + claimUrl: result.claimUrl, + token: result.token, } } @@ -242,7 +82,6 @@ export async function dispatchAdminTaskKuaishouCloudFulfillment( session: AdminViewerSessionInput | null = null, ): Promise { const task = await getRequiredTask(taskId) - const now = nowIso() const viewerContext = createAdminViewerContext(session) if (!viewerContext.canManageTaskLifecycle) { @@ -259,114 +98,14 @@ export async function dispatchAdminTaskKuaishouCloudFulfillment( }) } - const taskContext = parseTaskContext(task) - const flow = normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment) - const cloudContext = resolvePersistedCloudtentaclesContext([ - flow.binding.resolvedSourceKey, - ...flow.binding.cloudSourceKeys, - ]) - - if (!flow.binding.skuId || !flow.binding.vnId || !flow.binding.vnPhone) { - throw createHttpError('当前任务还没有准备好绑定资源,请先准备绑定资源', { - statusCode: 409, - errorCode: 'admin_task_kuaishou_cloud_not_prepared', - }) - } - - const persistedTicketCode = String(flow.ticket.code || '').trim() - const voucherContext = normalizeAdminTaskIndustryVoucherContext(taskContext.kuaishouIndustryVoucher) - const industryVoucherCode = String( - voucherContext.voucherCode || voucherContext.eticketId || '', - ).trim() - const resolvedTicketCode = persistedTicketCode || industryVoucherCode - - if (!resolvedTicketCode) { - throw createHttpError('旧快手小店核销流程已停用,请改用行业电子凭证处理', { - statusCode: 409, - errorCode: 'admin_task_kuaishou_cloud_missing_ticket_code', - }) - } - - const synced = await syncKuaishouCloudRoleInfoBeforeDispatch(task, { - now, - actor: session - ? { - userId: Number(session.userId || 0) || 0, - username: String(session.username || '').trim(), - role: String(session.role || '').trim(), - } - : null, + const result = await dispatchKuaishouCloudFulfillmentTask(task, { source: 'admin_task_dispatch', - errorCodePrefix: 'admin_task_kuaishou_cloud', - cloudContext, - taskContext, - flow, + actor: buildAdminActionActor(session), + autoFinalize: false, }) - const syncedFlow = synced.flow - const syncedTaskContext = synced.taskContext - - const dispatchResult = await useCloudtentaclesSku({ - ...cloudContext, - id: syncedFlow.binding.skuId, - virtualNumberId: syncedFlow.binding.vnId, - phone: syncedFlow.binding.vnPhone, - }) - - const nextContext = { - ...syncedTaskContext, - kuaishouCloudFulfillment: { - ...syncedFlow, - ticket: { - ...syncedFlow.ticket, - code: resolvedTicketCode, - capturedAt: resolvedTicketCode ? (syncedFlow.ticket.capturedAt || now) : syncedFlow.ticket.capturedAt, - capturedBy: syncedFlow.ticket.capturedBy, - }, - dispatch: { - ...syncedFlow.dispatch, - status: 'success', - dispatchAt: now, - dispatchBy: session - ? { - userId: Number(session.userId || 0) || 0, - username: String(session.username || '').trim(), - role: String(session.role || '').trim(), - } - : null, - sendType: Number(dispatchResult.sendType || 0) || 0, - note: String(dispatchResult.note || dispatchResult.responseMessage || '').trim(), - }, - }, - } - - const updatedTask = await updateTask(task.id, { - task_status: TASK_STATUS.DISPATCHED_PENDING_RETURN, - delivery_status: 'delivered', - result_code: 'kuaishou_cloud_dispatched', - result_message: String( - dispatchResult.responseMessage || dispatchResult.note || 'cloudtentacles 发货成功', - ).trim(), - last_error: '', - context_json: JSON.stringify(nextContext), - updated_at: now, - }) - - await createTaskEvent( - task.id, - 'kuaishou_cloud_dispatched', - { - ticketCodeMasked: maskCode(resolvedTicketCode), - skuId: syncedFlow.binding.skuId, - vnId: syncedFlow.binding.vnId, - vnPhoneMasked: maskPhone(syncedFlow.binding.vnPhone), - sendType: dispatchResult.sendType, - note: dispatchResult.note, - }, - now, - ) return { - task: mapTaskActionPayload(updatedTask), + task: mapTaskActionPayload(result.task), } } diff --git a/apps/frontend/src/pages/admin/AdminTaskDetailPage.tsx b/apps/frontend/src/pages/admin/AdminTaskDetailPage.tsx index 5e3e049f..5ed4c9cc 100644 --- a/apps/frontend/src/pages/admin/AdminTaskDetailPage.tsx +++ b/apps/frontend/src/pages/admin/AdminTaskDetailPage.tsx @@ -289,7 +289,7 @@ export default function AdminTaskDetailPage() { 'prepare-kuaishou-cloud', () => prepareAdminTaskKuaishouCloudFulfillment(resolvedDetail.task.taskId), '绑定资源已准备完成', - `确认开始为任务 ${resolvedDetail.task.taskNo} 准备绑定资源吗?系统会自动检查背包、余额并申请虚拟号。`, + `确认开始为任务 ${resolvedDetail.task.taskNo} 准备绑定资源吗?系统会自动检查库存配置并申请虚拟号。`, ) } onDispatchKuaishouCloud={() =>