修复不返回链接错误

This commit is contained in:
yml
2026-05-27 19:54:00 +08:00
parent db777d0491
commit 5e5413238f
7 changed files with 38 additions and 302 deletions
@@ -1,287 +0,0 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import { syncDeliveryTasksForOrderWithDeps } from './delivery-task-service.js'
const paidOrder = {
id: 10,
provider: '91kaquan',
platform: 'kuaishou',
shop_id: 'shop-1',
shop_name: '测试店铺',
platform_order_id: 'P10001',
pay_status: 'paid',
}
const orderItems = [
{
id: 20,
sku_code: 'dnf-cdk-a',
sku_name: 'DNF CDK A',
quantity: 1,
},
]
function createTaskFixture(patch = {}) {
return {
id: 30,
order_item_id: 20,
task_status: 'paid',
executor_key: 'claim_link_dispatch',
requires_claim: true,
claim_token: '',
primary_claim_token_id: null,
last_error: '',
context_json: '{}',
...patch,
}
}
test('syncDeliveryTasksForOrderWithDeps sends manual dispatch tasks to manual review', async () => {
const notifications = []
const updates = []
const result = await syncDeliveryTasksForOrderWithDeps(paidOrder, orderItems, {
listTasksByOrderId: async () => [createTaskFixture({
executor_key: 'manual_dispatch',
requires_claim: false,
context_json: '{}',
})],
updateTask: async (taskId, patch) => {
updates.push({ taskId, patch })
return { id: taskId, ...patch }
},
notifyTaskAutoManualReview: async (payload) => {
notifications.push(payload)
return { ok: true }
},
nowIso: () => '2026-04-14T12:01:00.000Z',
})
assert.equal(result[0]?.task_status, 'manual_review')
assert.equal(updates[0]?.patch.last_error, '当前任务需要人工履约处理')
assert.deepEqual(
notifications.map((item) => ({
reason: item.reason,
source: item.source,
taskStatus: item.task.task_status,
})),
[
{
reason: '当前任务需要人工履约处理',
source: 'manual_dispatch_profile',
taskStatus: 'manual_review',
},
],
)
})
test('syncDeliveryTasksForOrderWithDeps prepares kuaishou cloud task with claim token', async () => {
const claimToken = {
token: 'claim-token',
expired_at: '2026-04-15T12:00:00.000Z',
}
const updates = []
const result = await syncDeliveryTasksForOrderWithDeps(paidOrder, orderItems, {
listTasksByOrderId: async () => [createTaskFixture({
executor_key: 'kuaishou_ct_assisted',
task_status: 'paid',
requires_claim: true,
context_json: '{}',
})],
createTaskClaimToken: async (taskId) => ({ ...claimToken, taskId }),
updateTask: async (taskId, patch) => {
updates.push({ taskId, patch })
return { id: taskId, ...patch }
},
nowIso: () => '2026-04-14T12:02:00.000Z',
})
assert.equal(result[0]?.task_status, 'pending_binding_prepare')
assert.deepEqual(updates, [
{
taskId: 30,
patch: {
task_status: 'pending_binding_prepare',
claim_token: 'claim-token',
claim_expires_at: '2026-04-15T12:00:00.000Z',
user_action_status: 'pending_claim',
last_error: '领取链接已生成,等待客户提交核销码',
updated_at: '2026-04-14T12:02:00.000Z',
},
},
])
})
test('syncDeliveryTasksForOrderWithDeps creates kuaishou cloud task from cloudtentacles name match snapshot', async () => {
const createdTasks = []
const updates = []
const dynamicOrderItems = [
{
id: 21,
order_id: 10,
sku_code: '套装-浪漫天命',
sku_name: '套装-浪漫天命',
quantity: 1,
spec_json: '{}',
item_snapshot_json: JSON.stringify({
matchMode: 'cloudtentacles_name',
cloudtentacles: {
cloudSkuId: 28,
cloudSkuName: '套装-浪漫天命',
cloudSourceKeys: ['account-a', 'account-b'],
},
}),
},
]
const result = await syncDeliveryTasksForOrderWithDeps(paidOrder, dynamicOrderItems, {
listTasksByOrderId: async () => [],
getFulfillmentProfileByKey: async () => ({
id: 2,
profile_key: 'kuaishou_ct_assisted',
name: '快手 cloud 履约',
executor_key: 'kuaishou_ct_assisted',
requires_claim: false,
auto_dispatch: false,
config_json: '{}',
}),
createTask: async (input) => {
createdTasks.push(input)
return {
id: 31,
order_id: input.orderId,
order_item_id: input.orderItemId,
task_status: input.taskStatus,
executor_key: input.executorKey,
requires_claim: input.requiresClaim,
claim_token: '',
primary_claim_token_id: null,
last_error: '',
context_json: input.contextJson,
}
},
createTaskClaimToken: async () => ({
token: 'claim-token',
expired_at: '2026-04-15T12:00:00.000Z',
}),
updateTask: async (taskId, patch) => {
updates.push({ taskId, patch })
return { id: taskId, ...patch }
},
nowIso: () => '2026-04-14T12:03:00.000Z',
randomId: () => 'DT-DYNAMIC',
})
assert.equal(createdTasks.length, 1)
assert.equal(createdTasks[0].executorKey, 'kuaishou_ct_assisted')
assert.equal(createdTasks[0].profileId, 2)
assert.equal(createdTasks[0].taskStatus, 'pending_binding_prepare')
const context = JSON.parse(createdTasks[0].contextJson)
assert.equal(context.kuaishouCloudFulfillment.binding.skuId, 28)
assert.equal(context.kuaishouCloudFulfillment.binding.skuName, '套装-浪漫天命')
assert.deepEqual(context.kuaishouCloudFulfillment.binding.cloudSourceKeys, ['account-a', 'account-b'])
assert.equal(context.kuaishouCloudFulfillment.consume.shopId, 'shop-1')
assert.equal(context.kuaishouCloudFulfillment.consume.shopName, '测试店铺')
assert.deepEqual(context.kuaishouCloudFulfillment.deliveryItems, [
{
cloudSkuId: 28,
cloudSkuName: '套装-浪漫天命',
quantity: 1,
},
])
assert.equal(result[0]?.claim_token, 'claim-token')
assert.equal(updates[0]?.patch.claim_token, 'claim-token')
})
test('syncDeliveryTasksForOrderWithDeps keeps override delivery items and quantities', async () => {
const createdTasks = []
const dynamicOrderItems = [
{
id: 22,
order_id: 10,
sku_code: '套装1',
sku_name: '套装1',
quantity: 1,
spec_json: '{}',
item_snapshot_json: JSON.stringify({
matchMode: 'cloudtentacles_override',
cloudtentacles: {
matchMode: 'cloudtentacles_override',
normalizedProductName: '套装1',
cloudSkuId: 28,
cloudSkuName: '商品A',
cloudSourceKeys: ['account-a'],
resolvedSourceKey: 'account-a',
deliveryItems: [
{
cloudSkuId: 28,
cloudSkuName: '商品A',
quantity: 1,
},
{
cloudSkuId: 29,
cloudSkuName: '商品B',
quantity: 20,
},
],
},
}),
},
]
await syncDeliveryTasksForOrderWithDeps(paidOrder, dynamicOrderItems, {
listTasksByOrderId: async () => [],
getFulfillmentProfileByKey: async () => ({
id: 2,
profile_key: 'kuaishou_ct_assisted',
name: '快手 cloud 履约',
executor_key: 'kuaishou_ct_assisted',
requires_claim: false,
auto_dispatch: false,
config_json: '{}',
}),
createTask: async (input) => {
createdTasks.push(input)
return {
id: 32,
order_id: input.orderId,
order_item_id: input.orderItemId,
task_status: input.taskStatus,
executor_key: input.executorKey,
requires_claim: input.requiresClaim,
claim_token: '',
primary_claim_token_id: null,
last_error: '',
context_json: input.contextJson,
}
},
createTaskClaimToken: async () => ({
token: 'claim-token',
expired_at: '2026-04-15T12:00:00.000Z',
}),
updateTask: async (taskId, patch) => ({ id: taskId, ...patch }),
nowIso: () => '2026-04-14T12:04:00.000Z',
randomId: () => 'DT-OVERRIDE',
})
const context = JSON.parse(createdTasks[0].contextJson)
assert.equal(context.kuaishouCloudFulfillment.binding.skuId, 28)
assert.equal(context.kuaishouCloudFulfillment.binding.resolvedSourceKey, 'account-a')
assert.deepEqual(context.kuaishouCloudFulfillment.deliveryItems, [
{
cloudSkuId: 28,
cloudSkuName: '商品A',
quantity: 1,
},
{
cloudSkuId: 29,
cloudSkuName: '商品B',
quantity: 20,
},
])
})
@@ -113,6 +113,7 @@ export async function syncDeliveryTasksForOrderWithDeps(
if (!profile) {
continue
}
const itemSnapshot = parseJsonObject(item.item_snapshot_json)
const quantity = Math.max(1, Number(item.quantity || 1))
const fulfillmentConfig = parseJsonObject(profile.config_json)
const cloudtentaclesConfig = parseJsonObject(fulfillmentConfig.cloudtentacles)
@@ -225,8 +226,8 @@ export async function syncDeliveryTasksForOrderWithDeps(
},
consume: {
status: 'pending',
shopId: String(kuaishouConsumeConfig.shopId || kuaishouShopConfig.shopId || order.shop_id || '').trim(),
shopName: String(kuaishouConsumeConfig.shopName || kuaishouShopConfig.kshopName || order.shop_name || '').trim(),
shopId: String(kuaishouConsumeConfig.shopId || kuaishouShopConfig.shopId || itemSnapshot.shopId || order.shop_id || '').trim(),
shopName: String(kuaishouConsumeConfig.shopName || kuaishouShopConfig.kshopName || itemSnapshot.shopName || order.shop_name || '').trim(),
autoConsumeEnabled: kuaishouConsumeConfig.autoConsumeAfterDispatch === true,
consumedAt: null,
errorMessage: '',
@@ -429,8 +430,8 @@ async function resolveDynamicCloudtentaclesProfile(
autoReturnNumberAfterDispatch: true,
},
kuaishouConsume: {
shopId: '',
shopName: '',
shopId: String(snapshot.shopId || '').trim(),
shopName: String(snapshot.shopName || '').trim(),
autoConsumeAfterDispatch: false,
},
notes: cloudtentacles.matchMode === 'cloudtentacles_override'