增加履约高级配置

This commit is contained in:
yml2213
2026-05-26 22:56:41 +08:00
parent 5377f8893d
commit e8010728a4
18 changed files with 858 additions and 62 deletions
@@ -45,6 +45,16 @@ import {
} from "./task-context.js";
import type { TaskRow } from "../../../types/repository/rows.js";
type CloudDeliveryPlanItem = {
cloudSkuId: number;
cloudSkuName: string;
quantity: number;
skuItem: JsonObject | null;
knapsackItem: JsonObject | null;
knapsackCount: number;
missingCount: number;
};
export {
isKuaishouCloudBindUrlFresh,
isKuaishouCloudTask,
@@ -167,8 +177,18 @@ export async function prepareKuaishouCloudFulfillmentTask(
},
};
const knapsackItem = resolvedBinding.knapsackItem;
let usedKnapsack = Number(knapsackItem?.count || 0) > 0;
const deliveryPlan = resolveKuaishouCloudDeliveryPlan(flowWithResolvedBinding, {
skuItems: Array.isArray(skuList.items) ? skuList.items : [],
knapsackItems: Array.isArray(knapsack.items) ? knapsack.items : [],
});
if (deliveryPlan.items.length === 0) {
throw createHttpError("当前任务缺少 cloud 发货物品配置", {
statusCode: 409,
errorCode: "kuaishou_cloud_missing_delivery_items",
});
}
let usedKnapsack = deliveryPlan.items.every((item) => item.missingCount <= 0);
let purchaseTriggered = false;
let assetBefore = 0;
let assetAfter = 0;
@@ -182,11 +202,12 @@ export async function prepareKuaishouCloudFulfillmentTask(
}
const asset = await getCloudtentaclesAsset(cloudContext);
const targetSku = resolvedBinding.skuItem;
if (!targetSku) {
const missingSku = deliveryPlan.items.find(
(item) => item.missingCount > 0 && !item.skuItem
);
if (missingSku) {
throw createHttpError(
`cloudtentacles 未找到 SKU ${flowWithResolvedBinding.binding.skuId}`,
`cloudtentacles 未找到 SKU ${missingSku.cloudSkuId}`,
{
statusCode: 404,
errorCode: "kuaishou_cloud_sku_not_found",
@@ -195,7 +216,11 @@ export async function prepareKuaishouCloudFulfillmentTask(
}
assetBefore = Number(asset.asset || 0) || 0;
const targetPrice = Number(targetSku.price || 0) || 0;
const targetPrice = deliveryPlan.items.reduce(
(sum, item) =>
sum + Math.max(0, item.missingCount) * Number(item.skuItem?.price || 0),
0
);
const requiredAsset =
targetPrice + flowWithResolvedBinding.purchase.minAssetReserve;
if (assetBefore < requiredAsset) {
@@ -204,7 +229,10 @@ export async function prepareKuaishouCloudFulfillmentTask(
flow: flowWithResolvedBinding,
assetBefore,
requiredAsset,
skuName: targetSku.name,
skuName: deliveryPlan.items
.filter((item) => item.missingCount > 0)
.map((item) => item.cloudSkuName || `SKU ${item.cloudSkuId}`)
.join("、"),
});
throw createHttpError(
`余额不足,当前 ${assetBefore},至少需要 ${requiredAsset}`,
@@ -215,11 +243,17 @@ export async function prepareKuaishouCloudFulfillmentTask(
);
}
await buyCloudtentaclesSku({
...cloudContext,
id: flowWithResolvedBinding.binding.skuId,
count: 1,
});
for (const item of deliveryPlan.items) {
if (item.missingCount <= 0) {
continue;
}
await buyCloudtentaclesSku({
...cloudContext,
id: item.cloudSkuId,
count: item.missingCount,
});
}
purchaseTriggered = true;
const assetResult = await getCloudtentaclesAsset(cloudContext);
@@ -265,6 +299,13 @@ export async function prepareKuaishouCloudFulfillmentTask(
purchaseTriggered,
assetBefore,
assetAfter,
items: deliveryPlan.items.map((item) => ({
cloudSkuId: item.cloudSkuId,
cloudSkuName: item.cloudSkuName,
requiredCount: item.quantity,
knapsackCount: item.knapsackCount,
purchasedCount: Math.max(0, item.missingCount),
})),
purchaseAt: purchaseTriggered
? now
: flowWithResolvedBinding.purchase.purchaseAt,
@@ -296,6 +337,13 @@ export async function prepareKuaishouCloudFulfillmentTask(
vnPhoneMasked: maskPhone(preparedBinding.vnPhone),
purchaseTriggered,
usedKnapsack,
deliveryItems: deliveryPlan.items.map((item) => ({
cloudSkuId: item.cloudSkuId,
cloudSkuName: item.cloudSkuName,
quantity: item.quantity,
knapsackCount: item.knapsackCount,
purchasedCount: Math.max(0, item.missingCount),
})),
resolvedByName: resolvedBinding.resolvedByName,
actor,
},
@@ -766,6 +814,69 @@ export async function refreshKuaishouCloudTaskRoleInfo(
};
}
function resolveKuaishouCloudDeliveryPlan(
flow: JsonObject,
{
skuItems = [],
knapsackItems = [],
}: { skuItems?: unknown[]; knapsackItems?: unknown[] } = {}
): { items: CloudDeliveryPlanItem[] } {
const normalizedSkuItems = Array.isArray(skuItems)
? skuItems.filter(isCloudSkuLikeItem)
: [];
const normalizedKnapsackItems = Array.isArray(knapsackItems)
? knapsackItems.filter(isCloudSkuLikeItem)
: [];
const deliveryItems = Array.isArray(flow.deliveryItems)
? flow.deliveryItems
: [];
return {
items: deliveryItems
.map((item: unknown) => {
const source = item && typeof item === "object" ? item as JsonObject : {};
const cloudSkuId = Number(source.cloudSkuId || source.skuId || 0) || 0;
const quantity = Number(source.quantity || 1) || 1;
if (!cloudSkuId || !Number.isInteger(quantity) || quantity <= 0) {
return null;
}
const skuItem =
normalizedSkuItems.find(
(sku) => Number(sku.id || 0) === cloudSkuId
) || null;
const knapsackItem =
normalizedKnapsackItems.find(
(sku) => Number(sku.id || 0) === cloudSkuId
) || null;
const knapsackCount = Math.max(0, Number(knapsackItem?.count || 0) || 0);
const cloudSkuName = String(
source.cloudSkuName ||
source.skuName ||
skuItem?.name ||
knapsackItem?.name ||
""
).trim();
return {
cloudSkuId,
cloudSkuName,
quantity,
skuItem,
knapsackItem,
knapsackCount,
missingCount: Math.max(0, quantity - knapsackCount),
};
})
.filter((item): item is CloudDeliveryPlanItem => Boolean(item)),
};
}
function isCloudSkuLikeItem(item: unknown): item is JsonObject {
const current = item && typeof item === "object" ? item as JsonObject : {};
return Number(current.id || 0) > 0;
}
export {
dispatchKuaishouCloudFulfillmentTask,
returnKuaishouCloudFulfillmentTask,