增加履约高级配置
This commit is contained in:
@@ -106,6 +106,7 @@ test('mapAdminKuaishouCloudFulfillmentSource normalizes items and defaults', ()
|
|||||||
cloudSourceKeys: ['account_a', 'account_b'],
|
cloudSourceKeys: ['account_a', 'account_b'],
|
||||||
cloudSkuId: 0,
|
cloudSkuId: 0,
|
||||||
cloudSkuName: '',
|
cloudSkuName: '',
|
||||||
|
deliveryItems: [],
|
||||||
vnKey: '',
|
vnKey: '',
|
||||||
autoBuyEnabled: true,
|
autoBuyEnabled: true,
|
||||||
minAssetReserve: 0,
|
minAssetReserve: 0,
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ export function mapAdminCloudtentaclesSession(session: JsonObject = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function mapAdminKuaishouCloudFulfillmentItem(item: JsonObject = {}) {
|
export function mapAdminKuaishouCloudFulfillmentItem(item: JsonObject = {}) {
|
||||||
|
const deliveryItems = normalizeDeliveryItems(item);
|
||||||
return {
|
return {
|
||||||
id: String(item.id || "").trim(),
|
id: String(item.id || "").trim(),
|
||||||
enabled: item.enabled !== false,
|
enabled: item.enabled !== false,
|
||||||
@@ -82,6 +83,7 @@ export function mapAdminKuaishouCloudFulfillmentItem(item: JsonObject = {}) {
|
|||||||
: [],
|
: [],
|
||||||
cloudSkuId: Number(item.cloudSkuId || 0) || 0,
|
cloudSkuId: Number(item.cloudSkuId || 0) || 0,
|
||||||
cloudSkuName: String(item.cloudSkuName || "").trim(),
|
cloudSkuName: String(item.cloudSkuName || "").trim(),
|
||||||
|
deliveryItems,
|
||||||
vnKey: String(item.vnKey || "").trim(),
|
vnKey: String(item.vnKey || "").trim(),
|
||||||
autoBuyEnabled: item.autoBuyEnabled !== false,
|
autoBuyEnabled: item.autoBuyEnabled !== false,
|
||||||
minAssetReserve: Number(item.minAssetReserve || 0) || 0,
|
minAssetReserve: Number(item.minAssetReserve || 0) || 0,
|
||||||
@@ -101,3 +103,42 @@ export function mapAdminKuaishouCloudFulfillmentSource(config: JsonObject = {})
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeDeliveryItems(item: JsonObject = {}) {
|
||||||
|
const rawItems = Array.isArray(item.deliveryItems) ? item.deliveryItems : [];
|
||||||
|
const normalizedItems = rawItems
|
||||||
|
.map((value) => normalizeDeliveryItem(value))
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
if (normalizedItems.length > 0) {
|
||||||
|
return normalizedItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cloudSkuId = Number(item.cloudSkuId || 0) || 0;
|
||||||
|
if (cloudSkuId <= 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
cloudSkuId,
|
||||||
|
cloudSkuName: String(item.cloudSkuName || "").trim(),
|
||||||
|
quantity: 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeDeliveryItem(value: unknown) {
|
||||||
|
const source = value && typeof value === "object" ? value as JsonObject : {};
|
||||||
|
const cloudSkuId = Number(source.cloudSkuId || source.skuId || 0) || 0;
|
||||||
|
if (cloudSkuId <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const quantity = Number(source.quantity || 1) || 1;
|
||||||
|
return {
|
||||||
|
cloudSkuId,
|
||||||
|
cloudSkuName: String(source.cloudSkuName || source.skuName || "").trim(),
|
||||||
|
quantity: Math.max(1, Math.round(quantity)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export function normalizeKuaishouCloudFlow(value: unknown) {
|
|||||||
source.consume && typeof source.consume === "object" ? source.consume : {};
|
source.consume && typeof source.consume === "object" ? source.consume : {};
|
||||||
const ticket =
|
const ticket =
|
||||||
source.ticket && typeof source.ticket === "object" ? source.ticket : {};
|
source.ticket && typeof source.ticket === "object" ? source.ticket : {};
|
||||||
|
const deliveryItems = normalizeDeliveryItems(source, binding);
|
||||||
|
|
||||||
const roleName = String(role.name || binding.roleName || "").trim();
|
const roleName = String(role.name || binding.roleName || "").trim();
|
||||||
const roleId = String(role.rid || binding.roleId || "").trim();
|
const roleId = String(role.rid || binding.roleId || "").trim();
|
||||||
@@ -48,6 +49,7 @@ export function normalizeKuaishouCloudFlow(value: unknown) {
|
|||||||
configId: String(source.configId || "").trim(),
|
configId: String(source.configId || "").trim(),
|
||||||
internalSkuCode: String(source.internalSkuCode || "").trim(),
|
internalSkuCode: String(source.internalSkuCode || "").trim(),
|
||||||
internalSkuName: String(source.internalSkuName || "").trim(),
|
internalSkuName: String(source.internalSkuName || "").trim(),
|
||||||
|
deliveryItems,
|
||||||
ticket: {
|
ticket: {
|
||||||
code: String(ticket.code || "").trim(),
|
code: String(ticket.code || "").trim(),
|
||||||
status: String(ticket.status || "pending").trim() || "pending",
|
status: String(ticket.status || "pending").trim() || "pending",
|
||||||
@@ -100,6 +102,7 @@ export function normalizeKuaishouCloudFlow(value: unknown) {
|
|||||||
assetBefore: Number(purchase.assetBefore || 0) || 0,
|
assetBefore: Number(purchase.assetBefore || 0) || 0,
|
||||||
assetAfter: Number(purchase.assetAfter || 0) || 0,
|
assetAfter: Number(purchase.assetAfter || 0) || 0,
|
||||||
purchaseAt: purchase.purchaseAt || null,
|
purchaseAt: purchase.purchaseAt || null,
|
||||||
|
items: Array.isArray(purchase.items) ? purchase.items : [],
|
||||||
},
|
},
|
||||||
dispatch: {
|
dispatch: {
|
||||||
status: String(dispatch.status || "pending").trim() || "pending",
|
status: String(dispatch.status || "pending").trim() || "pending",
|
||||||
@@ -107,6 +110,7 @@ export function normalizeKuaishouCloudFlow(value: unknown) {
|
|||||||
dispatchBy: dispatch.dispatchBy || null,
|
dispatchBy: dispatch.dispatchBy || null,
|
||||||
sendType: Number(dispatch.sendType || 0) || 0,
|
sendType: Number(dispatch.sendType || 0) || 0,
|
||||||
note: String(dispatch.note || "").trim(),
|
note: String(dispatch.note || "").trim(),
|
||||||
|
items: Array.isArray(dispatch.items) ? dispatch.items : [],
|
||||||
},
|
},
|
||||||
returnNumber: {
|
returnNumber: {
|
||||||
status: String(returnNumber.status || "pending").trim() || "pending",
|
status: String(returnNumber.status || "pending").trim() || "pending",
|
||||||
@@ -126,6 +130,13 @@ export function normalizeKuaishouCloudFlow(value: unknown) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function normalizeKuaishouCloudDeliveryItems(value: unknown) {
|
||||||
|
const source: JsonObject = value && typeof value === "object" ? value as JsonObject : {};
|
||||||
|
const binding =
|
||||||
|
source.binding && typeof source.binding === "object" ? source.binding : {};
|
||||||
|
return normalizeDeliveryItems(source, binding);
|
||||||
|
}
|
||||||
|
|
||||||
export function normalizeKuaishouCloudRoleInfo(value: unknown) {
|
export function normalizeKuaishouCloudRoleInfo(value: unknown) {
|
||||||
const rawInfo: JsonObject | null = value && typeof value === "object" ? value as JsonObject : null;
|
const rawInfo: JsonObject | null = value && typeof value === "object" ? value as JsonObject : null;
|
||||||
const nestedBindInfo =
|
const nestedBindInfo =
|
||||||
@@ -204,3 +215,61 @@ export function normalizeStringArray(value: unknown) {
|
|||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeDeliveryItems(source: JsonObject, binding: JsonObject) {
|
||||||
|
const rawItems = Array.isArray(source.deliveryItems) ? source.deliveryItems : [];
|
||||||
|
const items = rawItems
|
||||||
|
.map((item) => normalizeDeliveryItem(item))
|
||||||
|
.filter(Boolean) as Array<{ cloudSkuId: number; cloudSkuName: string; quantity: number }>;
|
||||||
|
|
||||||
|
if (items.length > 0) {
|
||||||
|
return mergeDeliveryItems(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
const skuId = Number(binding.skuId || 0) || 0;
|
||||||
|
if (!skuId) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
cloudSkuId: skuId,
|
||||||
|
cloudSkuName: String(binding.skuName || "").trim(),
|
||||||
|
quantity: 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeDeliveryItem(value: unknown) {
|
||||||
|
const source = value && typeof value === "object" ? value as JsonObject : {};
|
||||||
|
const cloudSkuId = Number(source.cloudSkuId || source.skuId || 0) || 0;
|
||||||
|
if (!Number.isInteger(cloudSkuId) || cloudSkuId <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const quantity = Number(source.quantity || 1) || 1;
|
||||||
|
return {
|
||||||
|
cloudSkuId,
|
||||||
|
cloudSkuName: String(source.cloudSkuName || source.skuName || "").trim(),
|
||||||
|
quantity: Number.isInteger(quantity) && quantity > 0 ? quantity : 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeDeliveryItems(
|
||||||
|
items: Array<{ cloudSkuId: number; cloudSkuName: string; quantity: number }>
|
||||||
|
) {
|
||||||
|
const merged = new Map<number, { cloudSkuId: number; cloudSkuName: string; quantity: number }>();
|
||||||
|
|
||||||
|
for (const item of items) {
|
||||||
|
const existing = merged.get(item.cloudSkuId);
|
||||||
|
if (existing) {
|
||||||
|
existing.quantity += item.quantity;
|
||||||
|
existing.cloudSkuName = existing.cloudSkuName || item.cloudSkuName;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
merged.set(item.cloudSkuId, { ...item });
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(merged.values());
|
||||||
|
}
|
||||||
|
|||||||
@@ -45,6 +45,16 @@ import {
|
|||||||
} from "./task-context.js";
|
} from "./task-context.js";
|
||||||
import type { TaskRow } from "../../../types/repository/rows.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 {
|
export {
|
||||||
isKuaishouCloudBindUrlFresh,
|
isKuaishouCloudBindUrlFresh,
|
||||||
isKuaishouCloudTask,
|
isKuaishouCloudTask,
|
||||||
@@ -167,8 +177,18 @@ export async function prepareKuaishouCloudFulfillmentTask(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const knapsackItem = resolvedBinding.knapsackItem;
|
const deliveryPlan = resolveKuaishouCloudDeliveryPlan(flowWithResolvedBinding, {
|
||||||
let usedKnapsack = Number(knapsackItem?.count || 0) > 0;
|
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 purchaseTriggered = false;
|
||||||
let assetBefore = 0;
|
let assetBefore = 0;
|
||||||
let assetAfter = 0;
|
let assetAfter = 0;
|
||||||
@@ -182,11 +202,12 @@ export async function prepareKuaishouCloudFulfillmentTask(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const asset = await getCloudtentaclesAsset(cloudContext);
|
const asset = await getCloudtentaclesAsset(cloudContext);
|
||||||
const targetSku = resolvedBinding.skuItem;
|
const missingSku = deliveryPlan.items.find(
|
||||||
|
(item) => item.missingCount > 0 && !item.skuItem
|
||||||
if (!targetSku) {
|
);
|
||||||
|
if (missingSku) {
|
||||||
throw createHttpError(
|
throw createHttpError(
|
||||||
`cloudtentacles 未找到 SKU ${flowWithResolvedBinding.binding.skuId}`,
|
`cloudtentacles 未找到 SKU ${missingSku.cloudSkuId}`,
|
||||||
{
|
{
|
||||||
statusCode: 404,
|
statusCode: 404,
|
||||||
errorCode: "kuaishou_cloud_sku_not_found",
|
errorCode: "kuaishou_cloud_sku_not_found",
|
||||||
@@ -195,7 +216,11 @@ export async function prepareKuaishouCloudFulfillmentTask(
|
|||||||
}
|
}
|
||||||
|
|
||||||
assetBefore = Number(asset.asset || 0) || 0;
|
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 =
|
const requiredAsset =
|
||||||
targetPrice + flowWithResolvedBinding.purchase.minAssetReserve;
|
targetPrice + flowWithResolvedBinding.purchase.minAssetReserve;
|
||||||
if (assetBefore < requiredAsset) {
|
if (assetBefore < requiredAsset) {
|
||||||
@@ -204,7 +229,10 @@ export async function prepareKuaishouCloudFulfillmentTask(
|
|||||||
flow: flowWithResolvedBinding,
|
flow: flowWithResolvedBinding,
|
||||||
assetBefore,
|
assetBefore,
|
||||||
requiredAsset,
|
requiredAsset,
|
||||||
skuName: targetSku.name,
|
skuName: deliveryPlan.items
|
||||||
|
.filter((item) => item.missingCount > 0)
|
||||||
|
.map((item) => item.cloudSkuName || `SKU ${item.cloudSkuId}`)
|
||||||
|
.join("、"),
|
||||||
});
|
});
|
||||||
throw createHttpError(
|
throw createHttpError(
|
||||||
`余额不足,当前 ${assetBefore},至少需要 ${requiredAsset}`,
|
`余额不足,当前 ${assetBefore},至少需要 ${requiredAsset}`,
|
||||||
@@ -215,11 +243,17 @@ export async function prepareKuaishouCloudFulfillmentTask(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await buyCloudtentaclesSku({
|
for (const item of deliveryPlan.items) {
|
||||||
...cloudContext,
|
if (item.missingCount <= 0) {
|
||||||
id: flowWithResolvedBinding.binding.skuId,
|
continue;
|
||||||
count: 1,
|
}
|
||||||
});
|
|
||||||
|
await buyCloudtentaclesSku({
|
||||||
|
...cloudContext,
|
||||||
|
id: item.cloudSkuId,
|
||||||
|
count: item.missingCount,
|
||||||
|
});
|
||||||
|
}
|
||||||
purchaseTriggered = true;
|
purchaseTriggered = true;
|
||||||
|
|
||||||
const assetResult = await getCloudtentaclesAsset(cloudContext);
|
const assetResult = await getCloudtentaclesAsset(cloudContext);
|
||||||
@@ -265,6 +299,13 @@ export async function prepareKuaishouCloudFulfillmentTask(
|
|||||||
purchaseTriggered,
|
purchaseTriggered,
|
||||||
assetBefore,
|
assetBefore,
|
||||||
assetAfter,
|
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
|
purchaseAt: purchaseTriggered
|
||||||
? now
|
? now
|
||||||
: flowWithResolvedBinding.purchase.purchaseAt,
|
: flowWithResolvedBinding.purchase.purchaseAt,
|
||||||
@@ -296,6 +337,13 @@ export async function prepareKuaishouCloudFulfillmentTask(
|
|||||||
vnPhoneMasked: maskPhone(preparedBinding.vnPhone),
|
vnPhoneMasked: maskPhone(preparedBinding.vnPhone),
|
||||||
purchaseTriggered,
|
purchaseTriggered,
|
||||||
usedKnapsack,
|
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,
|
resolvedByName: resolvedBinding.resolvedByName,
|
||||||
actor,
|
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 {
|
export {
|
||||||
dispatchKuaishouCloudFulfillmentTask,
|
dispatchKuaishouCloudFulfillmentTask,
|
||||||
returnKuaishouCloudFulfillmentTask,
|
returnKuaishouCloudFulfillmentTask,
|
||||||
|
|||||||
@@ -23,6 +23,19 @@ import { syncKuaishouCloudRoleInfoBeforeDispatch } from "./dispatch-role-sync.js
|
|||||||
import { normalizeActor, parseTaskContext } from "./task-context.js";
|
import { normalizeActor, parseTaskContext } from "./task-context.js";
|
||||||
import type { TaskRow } from "../../../types/repository/rows.js";
|
import type { TaskRow } from "../../../types/repository/rows.js";
|
||||||
|
|
||||||
|
type DispatchDeliveryItem = {
|
||||||
|
cloudSkuId: number;
|
||||||
|
cloudSkuName: string;
|
||||||
|
quantity: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type DispatchResultItem = DispatchDeliveryItem & {
|
||||||
|
unitIndex: number;
|
||||||
|
sendType: number;
|
||||||
|
note: string;
|
||||||
|
responseMessage: string;
|
||||||
|
};
|
||||||
|
|
||||||
export async function dispatchKuaishouCloudFulfillmentTask(
|
export async function dispatchKuaishouCloudFulfillmentTask(
|
||||||
task: TaskRow,
|
task: TaskRow,
|
||||||
options: JsonObject = {}
|
options: JsonObject = {}
|
||||||
@@ -80,12 +93,51 @@ export async function dispatchKuaishouCloudFulfillmentTask(
|
|||||||
const syncedFlow = synced.flow;
|
const syncedFlow = synced.flow;
|
||||||
const syncedTaskContext = synced.taskContext;
|
const syncedTaskContext = synced.taskContext;
|
||||||
|
|
||||||
const dispatchResult = await useCloudtentaclesSku({
|
const deliveryItems = resolveDispatchDeliveryItems(syncedFlow);
|
||||||
...cloudContext,
|
if (deliveryItems.length === 0) {
|
||||||
id: syncedFlow.binding.skuId,
|
throw createHttpError("当前任务缺少 cloud 发货物品配置", {
|
||||||
virtualNumberId: syncedFlow.binding.vnId,
|
statusCode: 409,
|
||||||
phone: syncedFlow.binding.vnPhone,
|
errorCode: "kuaishou_cloud_missing_delivery_items",
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const dispatchResults: DispatchResultItem[] = [];
|
||||||
|
for (const item of deliveryItems) {
|
||||||
|
for (let index = 0; index < item.quantity; index += 1) {
|
||||||
|
const dispatchResult = await useCloudtentaclesSku({
|
||||||
|
...cloudContext,
|
||||||
|
id: item.cloudSkuId,
|
||||||
|
virtualNumberId: syncedFlow.binding.vnId,
|
||||||
|
phone: syncedFlow.binding.vnPhone,
|
||||||
|
});
|
||||||
|
dispatchResults.push({
|
||||||
|
cloudSkuId: item.cloudSkuId,
|
||||||
|
cloudSkuName: item.cloudSkuName,
|
||||||
|
unitIndex: index + 1,
|
||||||
|
quantity: item.quantity,
|
||||||
|
sendType: Number(dispatchResult.sendType || 0) || 0,
|
||||||
|
note: String(dispatchResult.note || "").trim(),
|
||||||
|
responseMessage: String(dispatchResult.responseMessage || "").trim(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const firstDispatchResult: DispatchResultItem = dispatchResults[0] || {
|
||||||
|
cloudSkuId: syncedFlow.binding.skuId,
|
||||||
|
cloudSkuName: syncedFlow.binding.skuName,
|
||||||
|
quantity: 1,
|
||||||
|
unitIndex: 1,
|
||||||
|
sendType: 0,
|
||||||
|
note: "",
|
||||||
|
responseMessage: "",
|
||||||
|
};
|
||||||
|
const dispatchSummary =
|
||||||
|
dispatchResults.length > 1
|
||||||
|
? `cloudtentacles 发货成功,共 ${dispatchResults.length} 次`
|
||||||
|
: String(
|
||||||
|
firstDispatchResult.responseMessage ||
|
||||||
|
firstDispatchResult.note ||
|
||||||
|
"cloudtentacles 发货成功"
|
||||||
|
).trim();
|
||||||
|
|
||||||
const nextContext = {
|
const nextContext = {
|
||||||
...syncedTaskContext,
|
...syncedTaskContext,
|
||||||
@@ -102,12 +154,9 @@ export async function dispatchKuaishouCloudFulfillmentTask(
|
|||||||
status: "success",
|
status: "success",
|
||||||
dispatchAt: now,
|
dispatchAt: now,
|
||||||
dispatchBy: actor,
|
dispatchBy: actor,
|
||||||
sendType: Number(dispatchResult.sendType || 0) || 0,
|
sendType: Number(firstDispatchResult.sendType || 0) || 0,
|
||||||
note: String(
|
note: dispatchSummary,
|
||||||
dispatchResult.note ||
|
items: dispatchResults,
|
||||||
dispatchResult.responseMessage ||
|
|
||||||
"cloudtentacles 发货成功"
|
|
||||||
).trim(),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -116,11 +165,7 @@ export async function dispatchKuaishouCloudFulfillmentTask(
|
|||||||
task_status: "dispatched_pending_return",
|
task_status: "dispatched_pending_return",
|
||||||
delivery_status: "delivered",
|
delivery_status: "delivered",
|
||||||
result_code: "kuaishou_cloud_dispatched",
|
result_code: "kuaishou_cloud_dispatched",
|
||||||
result_message: String(
|
result_message: dispatchSummary,
|
||||||
dispatchResult.responseMessage ||
|
|
||||||
dispatchResult.note ||
|
|
||||||
"cloudtentacles 发货成功"
|
|
||||||
).trim(),
|
|
||||||
user_action_status: "not_required",
|
user_action_status: "not_required",
|
||||||
last_error: "",
|
last_error: "",
|
||||||
context_json: JSON.stringify(nextContext),
|
context_json: JSON.stringify(nextContext),
|
||||||
@@ -142,8 +187,10 @@ export async function dispatchKuaishouCloudFulfillmentTask(
|
|||||||
skuId: syncedFlow.binding.skuId,
|
skuId: syncedFlow.binding.skuId,
|
||||||
vnId: syncedFlow.binding.vnId,
|
vnId: syncedFlow.binding.vnId,
|
||||||
vnPhoneMasked: maskPhone(syncedFlow.binding.vnPhone),
|
vnPhoneMasked: maskPhone(syncedFlow.binding.vnPhone),
|
||||||
sendType: dispatchResult.sendType,
|
sendType: firstDispatchResult.sendType,
|
||||||
note: dispatchResult.note,
|
note: dispatchSummary,
|
||||||
|
deliveryItems,
|
||||||
|
dispatchResults,
|
||||||
actor,
|
actor,
|
||||||
},
|
},
|
||||||
now
|
now
|
||||||
@@ -173,6 +220,43 @@ export async function dispatchKuaishouCloudFulfillmentTask(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolveDispatchDeliveryItems(flow: JsonObject): DispatchDeliveryItem[] {
|
||||||
|
const rawItems = Array.isArray(flow.deliveryItems) ? flow.deliveryItems : [];
|
||||||
|
const items = rawItems
|
||||||
|
.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 (!Number.isInteger(cloudSkuId) || cloudSkuId <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
cloudSkuId,
|
||||||
|
cloudSkuName: String(source.cloudSkuName || source.skuName || "").trim(),
|
||||||
|
quantity: Number.isInteger(quantity) && quantity > 0 ? quantity : 1,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter((item): item is DispatchDeliveryItem => Boolean(item));
|
||||||
|
|
||||||
|
if (items.length > 0) {
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fallbackSkuId = Number(flow?.binding?.skuId || 0) || 0;
|
||||||
|
if (!fallbackSkuId) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
cloudSkuId: fallbackSkuId,
|
||||||
|
cloudSkuName: String(flow?.binding?.skuName || "").trim(),
|
||||||
|
quantity: 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
export async function returnKuaishouCloudFulfillmentTask(
|
export async function returnKuaishouCloudFulfillmentTask(
|
||||||
task: TaskRow,
|
task: TaskRow,
|
||||||
options: JsonObject = {}
|
options: JsonObject = {}
|
||||||
|
|||||||
@@ -131,6 +131,12 @@ export async function syncDeliveryTasksForOrderWithDeps(
|
|||||||
const cloudtentaclesConfig = parseJsonObject(fulfillmentConfig.cloudtentacles)
|
const cloudtentaclesConfig = parseJsonObject(fulfillmentConfig.cloudtentacles)
|
||||||
const kuaishouConsumeConfig = parseJsonObject(fulfillmentConfig.kuaishouConsume)
|
const kuaishouConsumeConfig = parseJsonObject(fulfillmentConfig.kuaishouConsume)
|
||||||
const kuaishouShopConfig = parseJsonObject(fulfillmentConfig.kuaishouShop)
|
const kuaishouShopConfig = parseJsonObject(fulfillmentConfig.kuaishouShop)
|
||||||
|
const deliveryItems = normalizeCloudDeliveryItems(cloudtentaclesConfig)
|
||||||
|
const primaryDeliveryItem = deliveryItems[0] || {
|
||||||
|
cloudSkuId: Number(cloudtentaclesConfig.skuId || 0) || 0,
|
||||||
|
cloudSkuName: String(cloudtentaclesConfig.skuName || '').trim(),
|
||||||
|
quantity: 1,
|
||||||
|
}
|
||||||
|
|
||||||
for (let index = 0; index < quantity; index += 1) {
|
for (let index = 0; index < quantity; index += 1) {
|
||||||
const createdAt = getNowIso()
|
const createdAt = getNowIso()
|
||||||
@@ -172,6 +178,7 @@ export async function syncDeliveryTasksForOrderWithDeps(
|
|||||||
configId: String(fulfillmentConfig.configId || '').trim(),
|
configId: String(fulfillmentConfig.configId || '').trim(),
|
||||||
internalSkuCode: item.sku_code,
|
internalSkuCode: item.sku_code,
|
||||||
internalSkuName: item.sku_name,
|
internalSkuName: item.sku_name,
|
||||||
|
deliveryItems,
|
||||||
ticket: {
|
ticket: {
|
||||||
code: '',
|
code: '',
|
||||||
status: 'pending',
|
status: 'pending',
|
||||||
@@ -186,8 +193,8 @@ export async function syncDeliveryTasksForOrderWithDeps(
|
|||||||
binding: {
|
binding: {
|
||||||
prepareStatus: 'pending',
|
prepareStatus: 'pending',
|
||||||
cloudSourceKeys: normalizeStringArray(cloudtentaclesConfig.cloudSourceKeys),
|
cloudSourceKeys: normalizeStringArray(cloudtentaclesConfig.cloudSourceKeys),
|
||||||
skuId: Number(cloudtentaclesConfig.skuId || 0) || 0,
|
skuId: primaryDeliveryItem.cloudSkuId,
|
||||||
skuName: String(cloudtentaclesConfig.skuName || '').trim(),
|
skuName: primaryDeliveryItem.cloudSkuName,
|
||||||
vnKey: '1',
|
vnKey: '1',
|
||||||
vnId: 0,
|
vnId: 0,
|
||||||
vnPhone: '',
|
vnPhone: '',
|
||||||
@@ -394,6 +401,68 @@ function normalizeStringArray(value: unknown): string[] {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeCloudDeliveryItems(value: JsonObject): Array<{
|
||||||
|
cloudSkuId: number
|
||||||
|
cloudSkuName: string
|
||||||
|
quantity: number
|
||||||
|
}> {
|
||||||
|
const rawItems = Array.isArray(value.deliveryItems) ? value.deliveryItems : []
|
||||||
|
const items = rawItems
|
||||||
|
.map((item) => normalizeCloudDeliveryItem(item))
|
||||||
|
.filter((item): item is { cloudSkuId: number; cloudSkuName: string; quantity: number } => Boolean(item))
|
||||||
|
|
||||||
|
if (items.length > 0) {
|
||||||
|
return mergeCloudDeliveryItems(items)
|
||||||
|
}
|
||||||
|
|
||||||
|
const cloudSkuId = Number(value.skuId || 0) || 0
|
||||||
|
if (!cloudSkuId) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
return [{
|
||||||
|
cloudSkuId,
|
||||||
|
cloudSkuName: String(value.skuName || '').trim(),
|
||||||
|
quantity: 1,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeCloudDeliveryItem(value: unknown) {
|
||||||
|
const source = value && typeof value === 'object' && !Array.isArray(value)
|
||||||
|
? value as JsonObject
|
||||||
|
: {}
|
||||||
|
const cloudSkuId = Number(source.cloudSkuId || source.skuId || 0) || 0
|
||||||
|
if (!Number.isInteger(cloudSkuId) || cloudSkuId <= 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const quantity = Number(source.quantity || 1) || 1
|
||||||
|
return {
|
||||||
|
cloudSkuId,
|
||||||
|
cloudSkuName: String(source.cloudSkuName || source.skuName || '').trim(),
|
||||||
|
quantity: Number.isInteger(quantity) && quantity > 0 ? quantity : 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeCloudDeliveryItems(
|
||||||
|
items: Array<{ cloudSkuId: number; cloudSkuName: string; quantity: number }>,
|
||||||
|
) {
|
||||||
|
const merged = new Map<number, { cloudSkuId: number; cloudSkuName: string; quantity: number }>()
|
||||||
|
|
||||||
|
for (const item of items) {
|
||||||
|
const existing = merged.get(item.cloudSkuId)
|
||||||
|
if (existing) {
|
||||||
|
existing.quantity += item.quantity
|
||||||
|
existing.cloudSkuName = existing.cloudSkuName || item.cloudSkuName
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
merged.set(item.cloudSkuId, { ...item })
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(merged.values())
|
||||||
|
}
|
||||||
|
|
||||||
function isTaskRow(task: TaskRow | DeliveryTaskRow | null | undefined): task is TaskRow {
|
function isTaskRow(task: TaskRow | DeliveryTaskRow | null | undefined): task is TaskRow {
|
||||||
return Boolean(task && Number(task.id || 0) > 0)
|
return Boolean(task && Number(task.id || 0) > 0)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,13 @@ test('mapKuaishouCloudFulfillmentItemsToBindings maps 91kaquan rules', () => {
|
|||||||
profileKey: 'kuaishou_ct_assisted',
|
profileKey: 'kuaishou_ct_assisted',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
priority: 100,
|
priority: 100,
|
||||||
|
deliveryItems: [
|
||||||
|
{
|
||||||
|
cloudSkuId: 28,
|
||||||
|
cloudSkuName: '',
|
||||||
|
quantity: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
config: {
|
config: {
|
||||||
flowType: 'kuaishou_cloud_fulfillment',
|
flowType: 'kuaishou_cloud_fulfillment',
|
||||||
configId: '',
|
configId: '',
|
||||||
@@ -39,6 +46,13 @@ test('mapKuaishouCloudFulfillmentItemsToBindings maps 91kaquan rules', () => {
|
|||||||
cloudSourceKeys: ['account_a', 'account_b'],
|
cloudSourceKeys: ['account_a', 'account_b'],
|
||||||
skuId: 28,
|
skuId: 28,
|
||||||
skuName: '',
|
skuName: '',
|
||||||
|
deliveryItems: [
|
||||||
|
{
|
||||||
|
cloudSkuId: 28,
|
||||||
|
cloudSkuName: '',
|
||||||
|
quantity: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
vnKey: '1',
|
vnKey: '1',
|
||||||
autoBuyEnabled: true,
|
autoBuyEnabled: true,
|
||||||
minAssetReserve: 0,
|
minAssetReserve: 0,
|
||||||
@@ -63,3 +77,32 @@ test('mapKuaishouCloudFulfillmentItemsToBindings maps 91kaquan rules', () => {
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('mapKuaishouCloudFulfillmentItemsToBindings maps package delivery items', () => {
|
||||||
|
const [binding] = mapKuaishouCloudFulfillmentItemsToBindings({
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
enabled: true,
|
||||||
|
internalSkuCode: 'package-1',
|
||||||
|
internalSkuName: '套餐 1',
|
||||||
|
externalSkuCode: 'P001',
|
||||||
|
cloudSourceKeys: ['account_a'],
|
||||||
|
deliveryItems: [
|
||||||
|
{ cloudSkuId: 73, cloudSkuName: '物品 A', quantity: 1 },
|
||||||
|
{ cloudSkuId: 76, cloudSkuName: '物品 B', quantity: 50 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.deepEqual(binding?.deliveryItems, [
|
||||||
|
{ cloudSkuId: 73, cloudSkuName: '物品 A', quantity: 1 },
|
||||||
|
{ cloudSkuId: 76, cloudSkuName: '物品 B', quantity: 50 },
|
||||||
|
])
|
||||||
|
assert.deepEqual(binding?.config.cloudtentacles.deliveryItems, [
|
||||||
|
{ cloudSkuId: 73, cloudSkuName: '物品 A', quantity: 1 },
|
||||||
|
{ cloudSkuId: 76, cloudSkuName: '物品 B', quantity: 50 },
|
||||||
|
])
|
||||||
|
assert.equal(binding?.config.cloudtentacles.skuId, 73)
|
||||||
|
assert.equal(binding?.config.cloudtentacles.skuName, '物品 A')
|
||||||
|
})
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ const KUAISHOU_CLOUD_FULFILLMENT_FILE_PATH = path.join(
|
|||||||
|
|
||||||
type JsonObject = Record<string, any>;
|
type JsonObject = Record<string, any>;
|
||||||
|
|
||||||
|
type DeliveryItem = {
|
||||||
|
cloudSkuId: number;
|
||||||
|
cloudSkuName: string;
|
||||||
|
quantity: number;
|
||||||
|
};
|
||||||
|
|
||||||
export function getKuaishouCloudFulfillmentFilePath() {
|
export function getKuaishouCloudFulfillmentFilePath() {
|
||||||
return KUAISHOU_CLOUD_FULFILLMENT_FILE_PATH;
|
return KUAISHOU_CLOUD_FULFILLMENT_FILE_PATH;
|
||||||
}
|
}
|
||||||
@@ -42,13 +48,19 @@ export function mapKuaishouCloudFulfillmentItemsToBindings(config: JsonObject =
|
|||||||
profileKey: "kuaishou_ct_assisted",
|
profileKey: "kuaishou_ct_assisted",
|
||||||
enabled: item.enabled !== false,
|
enabled: item.enabled !== false,
|
||||||
priority: normalizePriority(item.priority),
|
priority: normalizePriority(item.priority),
|
||||||
|
deliveryItems: normalizeDeliveryItems(item),
|
||||||
config: {
|
config: {
|
||||||
flowType: "kuaishou_cloud_fulfillment",
|
flowType: "kuaishou_cloud_fulfillment",
|
||||||
configId: String(item.id || "").trim(),
|
configId: String(item.id || "").trim(),
|
||||||
cloudtentacles: {
|
cloudtentacles: {
|
||||||
cloudSourceKeys: normalizeStringArray(item.cloudSourceKeys),
|
cloudSourceKeys: normalizeStringArray(item.cloudSourceKeys),
|
||||||
skuId: normalizePositiveInteger(item.cloudSkuId),
|
skuId:
|
||||||
skuName: String(item.cloudSkuName || "").trim(),
|
normalizeDeliveryItems(item)[0]?.cloudSkuId ||
|
||||||
|
normalizePositiveInteger(item.cloudSkuId),
|
||||||
|
skuName:
|
||||||
|
normalizeDeliveryItems(item)[0]?.cloudSkuName ||
|
||||||
|
String(item.cloudSkuName || "").trim(),
|
||||||
|
deliveryItems: normalizeDeliveryItems(item),
|
||||||
vnKey: "1",
|
vnKey: "1",
|
||||||
autoBuyEnabled: item.autoBuyEnabled !== false,
|
autoBuyEnabled: item.autoBuyEnabled !== false,
|
||||||
minAssetReserve: normalizeNonNegativeInteger(item.minAssetReserve, 0),
|
minAssetReserve: normalizeNonNegativeInteger(item.minAssetReserve, 0),
|
||||||
@@ -109,6 +121,7 @@ function normalizeKuaishouCloudFulfillmentItem(rawValue: unknown) {
|
|||||||
|
|
||||||
const internalSkuCode = String(rawValue.internalSkuCode || "").trim();
|
const internalSkuCode = String(rawValue.internalSkuCode || "").trim();
|
||||||
const cloudSkuId = normalizePositiveInteger(rawValue.cloudSkuId);
|
const cloudSkuId = normalizePositiveInteger(rawValue.cloudSkuId);
|
||||||
|
const deliveryItems = normalizeDeliveryItems(rawValue);
|
||||||
const cloudSourceKeys = normalizeStringArray(rawValue.cloudSourceKeys);
|
const cloudSourceKeys = normalizeStringArray(rawValue.cloudSourceKeys);
|
||||||
const externalSkuCode = String(rawValue.externalSkuCode || "").trim();
|
const externalSkuCode = String(rawValue.externalSkuCode || "").trim();
|
||||||
const externalItemId = String(rawValue.externalItemId || "").trim();
|
const externalItemId = String(rawValue.externalItemId || "").trim();
|
||||||
@@ -128,7 +141,7 @@ function normalizeKuaishouCloudFulfillmentItem(rawValue: unknown) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cloudSkuId) {
|
if (deliveryItems.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,8 +170,11 @@ function normalizeKuaishouCloudFulfillmentItem(rawValue: unknown) {
|
|||||||
rawValue.resolvedSkuName || rawValue.internalSkuName || ""
|
rawValue.resolvedSkuName || rawValue.internalSkuName || ""
|
||||||
).trim(),
|
).trim(),
|
||||||
cloudSourceKeys,
|
cloudSourceKeys,
|
||||||
cloudSkuId,
|
cloudSkuId: deliveryItems[0]?.cloudSkuId || cloudSkuId,
|
||||||
cloudSkuName: String(rawValue.cloudSkuName || "").trim(),
|
cloudSkuName:
|
||||||
|
deliveryItems[0]?.cloudSkuName ||
|
||||||
|
String(rawValue.cloudSkuName || "").trim(),
|
||||||
|
deliveryItems,
|
||||||
vnKey: "1",
|
vnKey: "1",
|
||||||
autoBuyEnabled: rawValue.autoBuyEnabled !== false,
|
autoBuyEnabled: rawValue.autoBuyEnabled !== false,
|
||||||
minAssetReserve: normalizeNonNegativeInteger(rawValue.minAssetReserve, 0),
|
minAssetReserve: normalizeNonNegativeInteger(rawValue.minAssetReserve, 0),
|
||||||
@@ -185,6 +201,64 @@ function normalizeNonNegativeInteger(value: unknown, fallback: number) {
|
|||||||
return Number.isInteger(parsed) && parsed >= 0 ? parsed : fallback;
|
return Number.isInteger(parsed) && parsed >= 0 ? parsed : fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeDeliveryItems(value: JsonObject): DeliveryItem[] {
|
||||||
|
const rawItems = Array.isArray(value.deliveryItems) ? value.deliveryItems : [];
|
||||||
|
const items = rawItems
|
||||||
|
.map((item: unknown) => normalizeDeliveryItem(item))
|
||||||
|
.filter(Boolean) as DeliveryItem[];
|
||||||
|
|
||||||
|
if (items.length > 0) {
|
||||||
|
return mergeDeliveryItems(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cloudSkuId = normalizePositiveInteger(value.cloudSkuId);
|
||||||
|
if (!cloudSkuId) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
cloudSkuId,
|
||||||
|
cloudSkuName: String(value.cloudSkuName || "").trim(),
|
||||||
|
quantity: 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeDeliveryItem(value: unknown): DeliveryItem | null {
|
||||||
|
if (!isPlainObject(value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cloudSkuId = normalizePositiveInteger(value.cloudSkuId || value.skuId);
|
||||||
|
if (!cloudSkuId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
cloudSkuId,
|
||||||
|
cloudSkuName: String(value.cloudSkuName || value.skuName || "").trim(),
|
||||||
|
quantity: normalizePositiveInteger(value.quantity) || 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeDeliveryItems(items: DeliveryItem[]): DeliveryItem[] {
|
||||||
|
const merged = new Map<number, DeliveryItem>();
|
||||||
|
|
||||||
|
for (const item of items) {
|
||||||
|
const existing = merged.get(item.cloudSkuId);
|
||||||
|
if (existing) {
|
||||||
|
existing.quantity += item.quantity;
|
||||||
|
existing.cloudSkuName = existing.cloudSkuName || item.cloudSkuName;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
merged.set(item.cloudSkuId, { ...item });
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(merged.values());
|
||||||
|
}
|
||||||
|
|
||||||
function normalizePriority(value: unknown) {
|
function normalizePriority(value: unknown) {
|
||||||
const parsed = Number(value);
|
const parsed = Number(value);
|
||||||
if (!Number.isFinite(parsed)) {
|
if (!Number.isFinite(parsed)) {
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ export type {
|
|||||||
AdminCloudtentaclesSkuListResult,
|
AdminCloudtentaclesSkuListResult,
|
||||||
AdminCloudtentaclesDeliveryRecordItem,
|
AdminCloudtentaclesDeliveryRecordItem,
|
||||||
AdminCloudtentaclesDeliveryRecordListResult,
|
AdminCloudtentaclesDeliveryRecordListResult,
|
||||||
|
AdminKuaishouCloudDeliveryItem,
|
||||||
AdminKuaishouCloudFulfillmentItem,
|
AdminKuaishouCloudFulfillmentItem,
|
||||||
AdminKuaishouCloudFulfillmentConfig,
|
AdminKuaishouCloudFulfillmentConfig,
|
||||||
} from './platform-config'
|
} from './platform-config'
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ export type {
|
|||||||
} from './cloudtentacles'
|
} from './cloudtentacles'
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
|
AdminKuaishouCloudDeliveryItem,
|
||||||
AdminKuaishouCloudFulfillmentItem,
|
AdminKuaishouCloudFulfillmentItem,
|
||||||
AdminKuaishouCloudFulfillmentConfig,
|
AdminKuaishouCloudFulfillmentConfig,
|
||||||
} from './kuaishou-cloud-fulfillment'
|
} from './kuaishou-cloud-fulfillment'
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
export interface AdminKuaishouCloudDeliveryItem {
|
||||||
|
cloudSkuId: number
|
||||||
|
cloudSkuName: string
|
||||||
|
quantity: number
|
||||||
|
}
|
||||||
|
|
||||||
export interface AdminKuaishouCloudFulfillmentItem {
|
export interface AdminKuaishouCloudFulfillmentItem {
|
||||||
id: string
|
id: string
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
@@ -14,6 +20,7 @@ export interface AdminKuaishouCloudFulfillmentItem {
|
|||||||
cloudSourceKeys: string[]
|
cloudSourceKeys: string[]
|
||||||
cloudSkuId: number
|
cloudSkuId: number
|
||||||
cloudSkuName: string
|
cloudSkuName: string
|
||||||
|
deliveryItems: AdminKuaishouCloudDeliveryItem[]
|
||||||
vnKey: string
|
vnKey: string
|
||||||
autoBuyEnabled: boolean
|
autoBuyEnabled: boolean
|
||||||
minAssetReserve: number
|
minAssetReserve: number
|
||||||
|
|||||||
+29
@@ -429,6 +429,34 @@
|
|||||||
padding: var(--space-4);
|
padding: var(--space-4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.delivery-items-editor {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--space-3);
|
||||||
|
padding: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.delivery-items-head {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: var(--space-3);
|
||||||
|
align-items: center;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delivery-item-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(220px, 1fr) minmax(160px, 0.7fr) 132px 48px;
|
||||||
|
gap: var(--space-3);
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delivery-item-sku,
|
||||||
|
.delivery-item-name,
|
||||||
|
.delivery-item-count {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.field-wide {
|
.field-wide {
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
}
|
}
|
||||||
@@ -501,6 +529,7 @@
|
|||||||
.rule-editor-main,
|
.rule-editor-main,
|
||||||
.strategy-grid,
|
.strategy-grid,
|
||||||
.mapping-editor,
|
.mapping-editor,
|
||||||
|
.delivery-item-row,
|
||||||
.mapping-grid,
|
.mapping-grid,
|
||||||
.mapping-extra {
|
.mapping-extra {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
|||||||
+24
-1
@@ -13,6 +13,25 @@ const config = useKuaishouCloudConfig()
|
|||||||
const ninetyone = useKuaishouCloudNinetyone(config)
|
const ninetyone = useKuaishouCloudNinetyone(config)
|
||||||
const cloudSku = useKuaishouCloudSku(config.cloudtentaclesSourceOptions)
|
const cloudSku = useKuaishouCloudSku(config.cloudtentaclesSourceOptions)
|
||||||
|
|
||||||
|
function handlePrimaryCloudSkuSelected(item: (typeof config.items.value)[number], value: number | string | undefined) {
|
||||||
|
cloudSku.handleCloudSkuSelected(item, value)
|
||||||
|
config.syncDeliveryItemsFromPrimaryCloudSku(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDeliveryItemCloudSkuSelected(
|
||||||
|
item: (typeof config.items.value)[number],
|
||||||
|
index: number,
|
||||||
|
value: number | string | undefined,
|
||||||
|
) {
|
||||||
|
const deliveryItem = item.deliveryItems[index]
|
||||||
|
if (!deliveryItem) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cloudSku.handleDeliveryItemCloudSkuSelected(item, deliveryItem, value)
|
||||||
|
config.syncPrimaryCloudSkuFromDeliveryItems(item)
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(config.loadConfigs)
|
onMounted(config.loadConfigs)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -61,6 +80,7 @@ onMounted(config.loadConfigs)
|
|||||||
:get-cloud-source-summary="config.getCloudSourceSummary"
|
:get-cloud-source-summary="config.getCloudSourceSummary"
|
||||||
:get-cloud-sku-summary="config.getCloudSkuSummary"
|
:get-cloud-sku-summary="config.getCloudSkuSummary"
|
||||||
:get-consume-shop-summary="config.getConsumeShopSummary"
|
:get-consume-shop-summary="config.getConsumeShopSummary"
|
||||||
|
:get-delivery-plan-summary="config.getDeliveryPlanSummary"
|
||||||
:get-cloud-sku-options="cloudSku.getCloudSkuOptions"
|
:get-cloud-sku-options="cloudSku.getCloudSkuOptions"
|
||||||
:format-cloud-sku-option-label="cloudSku.formatCloudSkuOptionLabel"
|
:format-cloud-sku-option-label="cloudSku.formatCloudSkuOptionLabel"
|
||||||
:get-kuaishou-consume-shop-options="config.getKuaishouConsumeShopOptions"
|
:get-kuaishou-consume-shop-options="config.getKuaishouConsumeShopOptions"
|
||||||
@@ -74,12 +94,15 @@ onMounted(config.loadConfigs)
|
|||||||
@add-item="config.addItem()"
|
@add-item="config.addItem()"
|
||||||
@save-configs="config.saveConfigs()"
|
@save-configs="config.saveConfigs()"
|
||||||
@toggle-collapsed="config.toggleCollapsed($event)"
|
@toggle-collapsed="config.toggleCollapsed($event)"
|
||||||
@handle-cloud-sku-selected="(item, value) => cloudSku.handleCloudSkuSelected(item, value)"
|
@handle-cloud-sku-selected="handlePrimaryCloudSkuSelected"
|
||||||
|
@handle-delivery-item-cloud-sku-selected="handleDeliveryItemCloudSkuSelected"
|
||||||
@handle-cloud-sku-dropdown-visible="
|
@handle-cloud-sku-dropdown-visible="
|
||||||
(item, visible) => cloudSku.handleCloudSkuDropdownVisible(item, visible)
|
(item, visible) => cloudSku.handleCloudSkuDropdownVisible(item, visible)
|
||||||
"
|
"
|
||||||
@handle-kuaishou-consume-shop-selected="config.handleKuaishouConsumeShopSelected($event)"
|
@handle-kuaishou-consume-shop-selected="config.handleKuaishouConsumeShopSelected($event)"
|
||||||
@handle-cloud-source-keys-changed="config.handleCloudSourceKeysChanged($event)"
|
@handle-cloud-source-keys-changed="config.handleCloudSourceKeysChanged($event)"
|
||||||
|
@add-delivery-item="config.addDeliveryItem($event)"
|
||||||
|
@remove-delivery-item="(item, index) => config.removeDeliveryItem(item, index)"
|
||||||
@remove-item="config.removeItem($event)"
|
@remove-item="config.removeItem($event)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
+68
@@ -19,6 +19,7 @@ defineProps<{
|
|||||||
getCloudSourceSummary: (item: EditableItem) => string
|
getCloudSourceSummary: (item: EditableItem) => string
|
||||||
getCloudSkuSummary: (item: EditableItem) => string
|
getCloudSkuSummary: (item: EditableItem) => string
|
||||||
getConsumeShopSummary: (item: EditableItem) => string
|
getConsumeShopSummary: (item: EditableItem) => string
|
||||||
|
getDeliveryPlanSummary: (item: EditableItem) => string
|
||||||
getCloudSkuOptions: (item: EditableItem) => AdminCloudtentaclesSkuItem[]
|
getCloudSkuOptions: (item: EditableItem) => AdminCloudtentaclesSkuItem[]
|
||||||
formatCloudSkuOptionLabel: (sku: AdminCloudtentaclesSkuItem) => string
|
formatCloudSkuOptionLabel: (sku: AdminCloudtentaclesSkuItem) => string
|
||||||
getKuaishouConsumeShopOptions: () => AdminKuaishouEticketShopConfigItem[]
|
getKuaishouConsumeShopOptions: () => AdminKuaishouEticketShopConfigItem[]
|
||||||
@@ -28,11 +29,21 @@ defineProps<{
|
|||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
toggleCollapsed: []
|
toggleCollapsed: []
|
||||||
handleCloudSkuSelected: [value: number | string | undefined]
|
handleCloudSkuSelected: [value: number | string | undefined]
|
||||||
|
handleDeliveryItemCloudSkuSelected: [index: number, value: number | string | undefined]
|
||||||
handleCloudSkuDropdownVisible: [visible: boolean]
|
handleCloudSkuDropdownVisible: [visible: boolean]
|
||||||
handleKuaishouConsumeShopSelected: []
|
handleKuaishouConsumeShopSelected: []
|
||||||
handleCloudSourceKeysChanged: []
|
handleCloudSourceKeysChanged: []
|
||||||
|
addDeliveryItem: []
|
||||||
|
removeDeliveryItem: [index: number]
|
||||||
remove: []
|
remove: []
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
function isAdvancedDeliveryConfigured(item: EditableItem) {
|
||||||
|
return (
|
||||||
|
item.deliveryItems.length > 1 ||
|
||||||
|
item.deliveryItems.some((deliveryItem) => Number(deliveryItem.quantity || 1) > 1)
|
||||||
|
)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -151,6 +162,7 @@ const emit = defineEmits<{
|
|||||||
v-model="item.cloudSkuId"
|
v-model="item.cloudSkuId"
|
||||||
class="text-input"
|
class="text-input"
|
||||||
placeholder="请选择 cloud SKU"
|
placeholder="请选择 cloud SKU"
|
||||||
|
:disabled="isAdvancedDeliveryConfigured(item)"
|
||||||
@change="emit('handleCloudSkuSelected', item.cloudSkuId)"
|
@change="emit('handleCloudSkuSelected', item.cloudSkuId)"
|
||||||
@visible-change="emit('handleCloudSkuDropdownVisible', $event)"
|
@visible-change="emit('handleCloudSkuDropdownVisible', $event)"
|
||||||
>
|
>
|
||||||
@@ -168,6 +180,7 @@ const emit = defineEmits<{
|
|||||||
:model-value="item.cloudSkuName"
|
:model-value="item.cloudSkuName"
|
||||||
class="text-input"
|
class="text-input"
|
||||||
placeholder="选择后自动带出"
|
placeholder="选择后自动带出"
|
||||||
|
disabled
|
||||||
readonly
|
readonly
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -229,6 +242,61 @@ const emit = defineEmits<{
|
|||||||
</el-form>
|
</el-form>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<details class="advanced-fields">
|
||||||
|
<summary>
|
||||||
|
<span>高级履约配置</span>
|
||||||
|
<small>{{ getDeliveryPlanSummary(item) }}</small>
|
||||||
|
</summary>
|
||||||
|
<div class="delivery-items-editor">
|
||||||
|
<div class="delivery-items-head">
|
||||||
|
<span>发货物品明细</span>
|
||||||
|
<el-button size="small" @click="emit('addDeliveryItem')">添加物品</el-button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="(deliveryItem, deliveryIndex) in item.deliveryItems"
|
||||||
|
:key="`${item.localId}-${deliveryIndex}`"
|
||||||
|
class="delivery-item-row"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-model="deliveryItem.cloudSkuId"
|
||||||
|
class="delivery-item-sku"
|
||||||
|
placeholder="请选择 cloud SKU"
|
||||||
|
@change="emit('handleDeliveryItemCloudSkuSelected', deliveryIndex, deliveryItem.cloudSkuId)"
|
||||||
|
@visible-change="emit('handleCloudSkuDropdownVisible', $event)"
|
||||||
|
>
|
||||||
|
<el-option label="请选择 cloud SKU" :value="0" />
|
||||||
|
<el-option
|
||||||
|
v-for="sku in getCloudSkuOptions(item)"
|
||||||
|
:key="sku.id"
|
||||||
|
:label="formatCloudSkuOptionLabel(sku)"
|
||||||
|
:value="sku.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
<el-input
|
||||||
|
:model-value="deliveryItem.cloudSkuName || '-'"
|
||||||
|
class="delivery-item-name"
|
||||||
|
readonly
|
||||||
|
/>
|
||||||
|
<el-input-number
|
||||||
|
v-model="deliveryItem.quantity"
|
||||||
|
class="delivery-item-count"
|
||||||
|
:min="1"
|
||||||
|
:max="999"
|
||||||
|
controls-position="right"
|
||||||
|
/>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
:disabled="item.deliveryItems.length <= 1"
|
||||||
|
@click="emit('removeDeliveryItem', deliveryIndex)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<small class="field-help">同一个用户只绑定一次角色,系统会按这里的物品和数量依次发货。</small>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
<details class="advanced-fields">
|
<details class="advanced-fields">
|
||||||
<summary>
|
<summary>
|
||||||
<span>高级匹配与备注</span>
|
<span>高级匹配与备注</span>
|
||||||
|
|||||||
+10
@@ -24,6 +24,7 @@ defineProps<{
|
|||||||
getCloudSourceSummary: (item: EditableItem) => string
|
getCloudSourceSummary: (item: EditableItem) => string
|
||||||
getCloudSkuSummary: (item: EditableItem) => string
|
getCloudSkuSummary: (item: EditableItem) => string
|
||||||
getConsumeShopSummary: (item: EditableItem) => string
|
getConsumeShopSummary: (item: EditableItem) => string
|
||||||
|
getDeliveryPlanSummary: (item: EditableItem) => string
|
||||||
getCloudSkuOptions: (item: EditableItem) => AdminCloudtentaclesSkuItem[]
|
getCloudSkuOptions: (item: EditableItem) => AdminCloudtentaclesSkuItem[]
|
||||||
formatCloudSkuOptionLabel: (sku: AdminCloudtentaclesSkuItem) => string
|
formatCloudSkuOptionLabel: (sku: AdminCloudtentaclesSkuItem) => string
|
||||||
getKuaishouConsumeShopOptions: () => AdminKuaishouEticketShopConfigItem[]
|
getKuaishouConsumeShopOptions: () => AdminKuaishouEticketShopConfigItem[]
|
||||||
@@ -41,9 +42,12 @@ const emit = defineEmits<{
|
|||||||
saveConfigs: []
|
saveConfigs: []
|
||||||
toggleCollapsed: [localId: string]
|
toggleCollapsed: [localId: string]
|
||||||
handleCloudSkuSelected: [item: EditableItem, value: number | string | undefined]
|
handleCloudSkuSelected: [item: EditableItem, value: number | string | undefined]
|
||||||
|
handleDeliveryItemCloudSkuSelected: [item: EditableItem, index: number, value: number | string | undefined]
|
||||||
handleCloudSkuDropdownVisible: [item: EditableItem, visible: boolean]
|
handleCloudSkuDropdownVisible: [item: EditableItem, visible: boolean]
|
||||||
handleKuaishouConsumeShopSelected: [item: EditableItem]
|
handleKuaishouConsumeShopSelected: [item: EditableItem]
|
||||||
handleCloudSourceKeysChanged: [item: EditableItem]
|
handleCloudSourceKeysChanged: [item: EditableItem]
|
||||||
|
addDeliveryItem: [item: EditableItem]
|
||||||
|
removeDeliveryItem: [item: EditableItem, index: number]
|
||||||
removeItem: [localId: string]
|
removeItem: [localId: string]
|
||||||
}>()
|
}>()
|
||||||
</script>
|
</script>
|
||||||
@@ -130,15 +134,21 @@ const emit = defineEmits<{
|
|||||||
:get-cloud-source-summary="getCloudSourceSummary"
|
:get-cloud-source-summary="getCloudSourceSummary"
|
||||||
:get-cloud-sku-summary="getCloudSkuSummary"
|
:get-cloud-sku-summary="getCloudSkuSummary"
|
||||||
:get-consume-shop-summary="getConsumeShopSummary"
|
:get-consume-shop-summary="getConsumeShopSummary"
|
||||||
|
:get-delivery-plan-summary="getDeliveryPlanSummary"
|
||||||
:get-cloud-sku-options="getCloudSkuOptions"
|
:get-cloud-sku-options="getCloudSkuOptions"
|
||||||
:format-cloud-sku-option-label="formatCloudSkuOptionLabel"
|
:format-cloud-sku-option-label="formatCloudSkuOptionLabel"
|
||||||
:get-kuaishou-consume-shop-options="getKuaishouConsumeShopOptions"
|
:get-kuaishou-consume-shop-options="getKuaishouConsumeShopOptions"
|
||||||
:format-kuaishou-consume-shop-option="formatKuaishouConsumeShopOption"
|
:format-kuaishou-consume-shop-option="formatKuaishouConsumeShopOption"
|
||||||
@toggle-collapsed="emit('toggleCollapsed', item.localId)"
|
@toggle-collapsed="emit('toggleCollapsed', item.localId)"
|
||||||
@handle-cloud-sku-selected="emit('handleCloudSkuSelected', item, $event)"
|
@handle-cloud-sku-selected="emit('handleCloudSkuSelected', item, $event)"
|
||||||
|
@handle-delivery-item-cloud-sku-selected="
|
||||||
|
(index, value) => emit('handleDeliveryItemCloudSkuSelected', item, index, value)
|
||||||
|
"
|
||||||
@handle-cloud-sku-dropdown-visible="emit('handleCloudSkuDropdownVisible', item, $event)"
|
@handle-cloud-sku-dropdown-visible="emit('handleCloudSkuDropdownVisible', item, $event)"
|
||||||
@handle-kuaishou-consume-shop-selected="emit('handleKuaishouConsumeShopSelected', item)"
|
@handle-kuaishou-consume-shop-selected="emit('handleKuaishouConsumeShopSelected', item)"
|
||||||
@handle-cloud-source-keys-changed="emit('handleCloudSourceKeysChanged', item)"
|
@handle-cloud-source-keys-changed="emit('handleCloudSourceKeysChanged', item)"
|
||||||
|
@add-delivery-item="emit('addDeliveryItem', item)"
|
||||||
|
@remove-delivery-item="emit('removeDeliveryItem', item, $event)"
|
||||||
@remove="emit('removeItem', item.localId)"
|
@remove="emit('removeItem', item.localId)"
|
||||||
/>
|
/>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|||||||
+164
-13
@@ -8,6 +8,7 @@ import {
|
|||||||
saveAdminKuaishouCloudFulfillmentConfig,
|
saveAdminKuaishouCloudFulfillmentConfig,
|
||||||
} from '@/services/admin'
|
} from '@/services/admin'
|
||||||
import type {
|
import type {
|
||||||
|
AdminKuaishouCloudDeliveryItem,
|
||||||
AdminKuaishouCloudFulfillmentConfig,
|
AdminKuaishouCloudFulfillmentConfig,
|
||||||
AdminKuaishouCloudFulfillmentItem,
|
AdminKuaishouCloudFulfillmentItem,
|
||||||
AdminKuaishouEticketShopConfigItem,
|
AdminKuaishouEticketShopConfigItem,
|
||||||
@@ -79,6 +80,7 @@ export function useKuaishouCloudConfig() {
|
|||||||
cloudSourceKeys: sourceKeys,
|
cloudSourceKeys: sourceKeys,
|
||||||
cloudSkuId: 0,
|
cloudSkuId: 0,
|
||||||
cloudSkuName: '',
|
cloudSkuName: '',
|
||||||
|
deliveryItems: [],
|
||||||
vnKey: '1',
|
vnKey: '1',
|
||||||
autoBuyEnabled: true,
|
autoBuyEnabled: true,
|
||||||
minAssetReserve: 0,
|
minAssetReserve: 0,
|
||||||
@@ -96,6 +98,8 @@ export function useKuaishouCloudConfig() {
|
|||||||
item.kuaishouConsumeShopName,
|
item.kuaishouConsumeShopName,
|
||||||
)
|
)
|
||||||
const sourceKeys = normalizeCloudSourceKeys(item.cloudSourceKeys)
|
const sourceKeys = normalizeCloudSourceKeys(item.cloudSourceKeys)
|
||||||
|
const deliveryItems = normalizeDeliveryItems(item)
|
||||||
|
const primaryDeliveryItem = deliveryItems[0]
|
||||||
return {
|
return {
|
||||||
localId: crypto.randomUUID(),
|
localId: crypto.randomUUID(),
|
||||||
id: item.id || crypto.randomUUID(),
|
id: item.id || crypto.randomUUID(),
|
||||||
@@ -111,8 +115,9 @@ export function useKuaishouCloudConfig() {
|
|||||||
externalSkuName: item.externalSkuName || '',
|
externalSkuName: item.externalSkuName || '',
|
||||||
resolvedSkuName: item.resolvedSkuName || '',
|
resolvedSkuName: item.resolvedSkuName || '',
|
||||||
cloudSourceKeys: sourceKeys,
|
cloudSourceKeys: sourceKeys,
|
||||||
cloudSkuId: Number(item.cloudSkuId || 0) || 0,
|
cloudSkuId: primaryDeliveryItem?.cloudSkuId || Number(item.cloudSkuId || 0) || 0,
|
||||||
cloudSkuName: item.cloudSkuName || '',
|
cloudSkuName: primaryDeliveryItem?.cloudSkuName || item.cloudSkuName || '',
|
||||||
|
deliveryItems,
|
||||||
vnKey: item.vnKey || '',
|
vnKey: item.vnKey || '',
|
||||||
autoBuyEnabled: item.autoBuyEnabled !== false,
|
autoBuyEnabled: item.autoBuyEnabled !== false,
|
||||||
minAssetReserve: Number(item.minAssetReserve || 0) || 0,
|
minAssetReserve: Number(item.minAssetReserve || 0) || 0,
|
||||||
@@ -130,6 +135,8 @@ export function useKuaishouCloudConfig() {
|
|||||||
item.kuaishouConsumeShopName,
|
item.kuaishouConsumeShopName,
|
||||||
)
|
)
|
||||||
const sourceKeys = normalizeCloudSourceKeys(item.cloudSourceKeys)
|
const sourceKeys = normalizeCloudSourceKeys(item.cloudSourceKeys)
|
||||||
|
const deliveryItems = normalizeDeliveryItems(item)
|
||||||
|
const primaryDeliveryItem = deliveryItems[0]
|
||||||
return {
|
return {
|
||||||
id: item.id.trim() || item.internalSkuCode.trim() || item.localId,
|
id: item.id.trim() || item.internalSkuCode.trim() || item.localId,
|
||||||
enabled: item.enabled,
|
enabled: item.enabled,
|
||||||
@@ -144,8 +151,9 @@ export function useKuaishouCloudConfig() {
|
|||||||
externalSkuName: item.externalSkuName.trim(),
|
externalSkuName: item.externalSkuName.trim(),
|
||||||
resolvedSkuName: item.resolvedSkuName.trim(),
|
resolvedSkuName: item.resolvedSkuName.trim(),
|
||||||
cloudSourceKeys: sourceKeys,
|
cloudSourceKeys: sourceKeys,
|
||||||
cloudSkuId: Number(item.cloudSkuId || 0) || 0,
|
cloudSkuId: primaryDeliveryItem?.cloudSkuId || Number(item.cloudSkuId || 0) || 0,
|
||||||
cloudSkuName: item.cloudSkuName.trim(),
|
cloudSkuName: primaryDeliveryItem?.cloudSkuName || item.cloudSkuName.trim(),
|
||||||
|
deliveryItems,
|
||||||
vnKey: item.vnKey.trim(),
|
vnKey: item.vnKey.trim(),
|
||||||
autoBuyEnabled: item.autoBuyEnabled,
|
autoBuyEnabled: item.autoBuyEnabled,
|
||||||
minAssetReserve: Math.max(0, Number(item.minAssetReserve || 0) || 0),
|
minAssetReserve: Math.max(0, Number(item.minAssetReserve || 0) || 0),
|
||||||
@@ -170,6 +178,64 @@ export function useKuaishouCloudConfig() {
|
|||||||
return normalizeCloudSourceKeys([cloudtentaclesSourceOptions.value[0]?.key])
|
return normalizeCloudSourceKeys([cloudtentaclesSourceOptions.value[0]?.key])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeDeliveryItems(
|
||||||
|
item: Pick<EditableItem, 'deliveryItems' | 'cloudSkuId' | 'cloudSkuName'> | AdminKuaishouCloudFulfillmentItem,
|
||||||
|
): AdminKuaishouCloudDeliveryItem[] {
|
||||||
|
const rawItems = Array.isArray(item.deliveryItems) ? item.deliveryItems : []
|
||||||
|
const normalizedItems = rawItems
|
||||||
|
.map((value) => normalizeDeliveryItem(value))
|
||||||
|
.filter((value): value is AdminKuaishouCloudDeliveryItem => Boolean(value))
|
||||||
|
|
||||||
|
if (normalizedItems.length > 0) {
|
||||||
|
return mergeDeliveryItems(normalizedItems)
|
||||||
|
}
|
||||||
|
|
||||||
|
const cloudSkuId = Number(item.cloudSkuId || 0) || 0
|
||||||
|
if (cloudSkuId <= 0) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
cloudSkuId,
|
||||||
|
cloudSkuName: String(item.cloudSkuName || '').trim(),
|
||||||
|
quantity: 1,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeDeliveryItem(value: unknown): AdminKuaishouCloudDeliveryItem | null {
|
||||||
|
const source = value && typeof value === 'object' ? (value as Partial<AdminKuaishouCloudDeliveryItem>) : {}
|
||||||
|
const cloudSkuId = Number(source.cloudSkuId || 0) || 0
|
||||||
|
if (!Number.isInteger(cloudSkuId) || cloudSkuId <= 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const quantity = Number(source.quantity || 1) || 1
|
||||||
|
return {
|
||||||
|
cloudSkuId,
|
||||||
|
cloudSkuName: String(source.cloudSkuName || '').trim(),
|
||||||
|
quantity: Number.isInteger(quantity) && quantity > 0 ? quantity : 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeDeliveryItems(items: AdminKuaishouCloudDeliveryItem[]) {
|
||||||
|
const merged = new Map<number, AdminKuaishouCloudDeliveryItem>()
|
||||||
|
|
||||||
|
for (const item of items) {
|
||||||
|
const existing = merged.get(item.cloudSkuId)
|
||||||
|
if (existing) {
|
||||||
|
existing.quantity += item.quantity
|
||||||
|
existing.cloudSkuName = existing.cloudSkuName || item.cloudSkuName
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
merged.set(item.cloudSkuId, { ...item })
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(merged.values())
|
||||||
|
}
|
||||||
|
|
||||||
// ── validation helpers ────────────────────────────────
|
// ── validation helpers ────────────────────────────────
|
||||||
|
|
||||||
function hasExternalMatch(
|
function hasExternalMatch(
|
||||||
@@ -191,6 +257,7 @@ export function useKuaishouCloudConfig() {
|
|||||||
item.cloudSourceKeys.length > 0 ||
|
item.cloudSourceKeys.length > 0 ||
|
||||||
Number(item.cloudSkuId || 0) > 0 ||
|
Number(item.cloudSkuId || 0) > 0 ||
|
||||||
item.cloudSkuName.trim() ||
|
item.cloudSkuName.trim() ||
|
||||||
|
normalizeDeliveryItems(item).length > 0 ||
|
||||||
item.vnKey.trim() ||
|
item.vnKey.trim() ||
|
||||||
item.shopId.trim() ||
|
item.shopId.trim() ||
|
||||||
item.notes.trim() ||
|
item.notes.trim() ||
|
||||||
@@ -209,9 +276,9 @@ export function useKuaishouCloudConfig() {
|
|||||||
|
|
||||||
function isItemComplete(item: EditableItem) {
|
function isItemComplete(item: EditableItem) {
|
||||||
return Boolean(
|
return Boolean(
|
||||||
item.internalSkuCode.trim() &&
|
item.internalSkuCode.trim() &&
|
||||||
item.cloudSourceKeys.length > 0 &&
|
item.cloudSourceKeys.length > 0 &&
|
||||||
Number(item.cloudSkuId || 0) > 0 &&
|
normalizeDeliveryItems(item).length > 0 &&
|
||||||
(!item.autoConsumeAfterDispatch || Boolean(item.kuaishouConsumeShopId.trim())) &&
|
(!item.autoConsumeAfterDispatch || Boolean(item.kuaishouConsumeShopId.trim())) &&
|
||||||
hasExternalMatch(item),
|
hasExternalMatch(item),
|
||||||
)
|
)
|
||||||
@@ -232,8 +299,16 @@ export function useKuaishouCloudConfig() {
|
|||||||
return { localId: item.localId, message: `${title} 需要选择至少一个 cloud 账号` }
|
return { localId: item.localId, message: `${title} 需要选择至少一个 cloud 账号` }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Number(item.cloudSkuId || 0) <= 0) {
|
const deliveryItems = normalizeDeliveryItems(item)
|
||||||
return { localId: item.localId, message: `${title} 需要填写 cloud SKU ID` }
|
if (deliveryItems.length === 0) {
|
||||||
|
return { localId: item.localId, message: `${title} 需要至少配置 1 个 cloud 发货物品` }
|
||||||
|
}
|
||||||
|
|
||||||
|
const invalidDeliveryItem = deliveryItems.find(
|
||||||
|
(deliveryItem) => Number(deliveryItem.quantity || 0) <= 0,
|
||||||
|
)
|
||||||
|
if (invalidDeliveryItem) {
|
||||||
|
return { localId: item.localId, message: `${title} 的发货数量必须大于 0` }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasExternalMatch(item)) {
|
if (!hasExternalMatch(item)) {
|
||||||
@@ -263,7 +338,7 @@ export function useKuaishouCloudConfig() {
|
|||||||
item.provider.trim() || '91kaquan',
|
item.provider.trim() || '91kaquan',
|
||||||
item.platform.trim() || 'kuaishou',
|
item.platform.trim() || 'kuaishou',
|
||||||
item.shopId.trim() || '跨店铺',
|
item.shopId.trim() || '跨店铺',
|
||||||
item.cloudSkuId > 0 ? `cloud#${item.cloudSkuId}` : '待填 cloud SKU',
|
getDeliveryPlanSummary(item),
|
||||||
]
|
]
|
||||||
return parts.join(' / ')
|
return parts.join(' / ')
|
||||||
}
|
}
|
||||||
@@ -367,17 +442,45 @@ export function useKuaishouCloudConfig() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getCloudSkuSummary(item: EditableItem) {
|
function getCloudSkuSummary(item: EditableItem) {
|
||||||
if (item.cloudSkuId > 0 && item.cloudSkuName.trim()) {
|
const deliveryItems = normalizeDeliveryItems(item)
|
||||||
return `${item.cloudSkuName.trim()} · #${item.cloudSkuId}`
|
if (deliveryItems.length > 1) {
|
||||||
|
return `${deliveryItems.length} 个物品 / 共 ${getDeliveryTotalQuantity(item)} 次`
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.cloudSkuId > 0) {
|
const primaryItem = deliveryItems[0]
|
||||||
return `cloud SKU #${item.cloudSkuId}`
|
if (primaryItem?.cloudSkuId && primaryItem.cloudSkuName.trim()) {
|
||||||
|
const suffix = primaryItem.quantity > 1 ? ` × ${primaryItem.quantity}` : ''
|
||||||
|
return `${primaryItem.cloudSkuName.trim()} · #${primaryItem.cloudSkuId}${suffix}`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (primaryItem?.cloudSkuId) {
|
||||||
|
const suffix = primaryItem.quantity > 1 ? ` × ${primaryItem.quantity}` : ''
|
||||||
|
return `cloud SKU #${primaryItem.cloudSkuId}${suffix}`
|
||||||
}
|
}
|
||||||
|
|
||||||
return '待选择 cloud SKU'
|
return '待选择 cloud SKU'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getDeliveryPlanSummary(item: EditableItem) {
|
||||||
|
const deliveryItems = normalizeDeliveryItems(item)
|
||||||
|
if (deliveryItems.length === 0) {
|
||||||
|
return '待填发货物品'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deliveryItems.length === 1) {
|
||||||
|
const firstItem = deliveryItems[0]
|
||||||
|
return firstItem.quantity > 1
|
||||||
|
? `cloud#${firstItem.cloudSkuId} × ${firstItem.quantity}`
|
||||||
|
: `cloud#${firstItem.cloudSkuId}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${deliveryItems.length} 个物品 / ${getDeliveryTotalQuantity(item)} 次`
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDeliveryTotalQuantity(item: EditableItem) {
|
||||||
|
return normalizeDeliveryItems(item).reduce((sum, deliveryItem) => sum + deliveryItem.quantity, 0)
|
||||||
|
}
|
||||||
|
|
||||||
// ── collapse state ────────────────────────────────────
|
// ── collapse state ────────────────────────────────────
|
||||||
|
|
||||||
function isCollapsed(localId: string) {
|
function isCollapsed(localId: string) {
|
||||||
@@ -472,6 +575,49 @@ export function useKuaishouCloudConfig() {
|
|||||||
item.cloudSourceKeys = sourceKeys
|
item.cloudSourceKeys = sourceKeys
|
||||||
item.cloudSkuId = 0
|
item.cloudSkuId = 0
|
||||||
item.cloudSkuName = ''
|
item.cloudSkuName = ''
|
||||||
|
item.deliveryItems = []
|
||||||
|
}
|
||||||
|
|
||||||
|
function addDeliveryItem(item: EditableItem) {
|
||||||
|
item.deliveryItems.push({
|
||||||
|
cloudSkuId: 0,
|
||||||
|
cloudSkuName: '',
|
||||||
|
quantity: 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeDeliveryItem(item: EditableItem, index: number) {
|
||||||
|
item.deliveryItems.splice(index, 1)
|
||||||
|
syncPrimaryCloudSkuFromDeliveryItems(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncPrimaryCloudSkuFromDeliveryItems(item: EditableItem) {
|
||||||
|
const primaryItem = normalizeDeliveryItems(item)[0]
|
||||||
|
item.cloudSkuId = primaryItem?.cloudSkuId || 0
|
||||||
|
item.cloudSkuName = primaryItem?.cloudSkuName || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncDeliveryItemsFromPrimaryCloudSku(item: EditableItem) {
|
||||||
|
if (Number(item.cloudSkuId || 0) <= 0) {
|
||||||
|
item.deliveryItems = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.deliveryItems.length === 0) {
|
||||||
|
item.deliveryItems.push({
|
||||||
|
cloudSkuId: item.cloudSkuId,
|
||||||
|
cloudSkuName: item.cloudSkuName,
|
||||||
|
quantity: 1,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
item.deliveryItems[0] = {
|
||||||
|
...item.deliveryItems[0],
|
||||||
|
cloudSkuId: item.cloudSkuId,
|
||||||
|
cloudSkuName: item.cloudSkuName,
|
||||||
|
quantity: Math.max(1, Number(item.deliveryItems[0].quantity || 1) || 1),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function focusValidationTarget(localId: string) {
|
async function focusValidationTarget(localId: string) {
|
||||||
@@ -559,6 +705,7 @@ export function useKuaishouCloudConfig() {
|
|||||||
getCloudSkuSummary,
|
getCloudSkuSummary,
|
||||||
getCloudSourceSummary,
|
getCloudSourceSummary,
|
||||||
getConsumeShopSummary,
|
getConsumeShopSummary,
|
||||||
|
getDeliveryPlanSummary,
|
||||||
// consume shops
|
// consume shops
|
||||||
getKuaishouConsumeShopOptions,
|
getKuaishouConsumeShopOptions,
|
||||||
findKuaishouConsumeShop,
|
findKuaishouConsumeShop,
|
||||||
@@ -566,6 +713,10 @@ export function useKuaishouCloudConfig() {
|
|||||||
formatKuaishouConsumeShopOption,
|
formatKuaishouConsumeShopOption,
|
||||||
handleKuaishouConsumeShopSelected,
|
handleKuaishouConsumeShopSelected,
|
||||||
handleCloudSourceKeysChanged,
|
handleCloudSourceKeysChanged,
|
||||||
|
addDeliveryItem,
|
||||||
|
removeDeliveryItem,
|
||||||
|
syncPrimaryCloudSkuFromDeliveryItems,
|
||||||
|
syncDeliveryItemsFromPrimaryCloudSku,
|
||||||
// collapse
|
// collapse
|
||||||
isCollapsed,
|
isCollapsed,
|
||||||
setCollapsed,
|
setCollapsed,
|
||||||
|
|||||||
+1
@@ -62,6 +62,7 @@ export function useKuaishouCloudNinetyone(
|
|||||||
cloudSourceKeys: sourceKeys,
|
cloudSourceKeys: sourceKeys,
|
||||||
cloudSkuId: 0,
|
cloudSkuId: 0,
|
||||||
cloudSkuName: '',
|
cloudSkuName: '',
|
||||||
|
deliveryItems: [],
|
||||||
vnKey: '1',
|
vnKey: '1',
|
||||||
autoBuyEnabled: true,
|
autoBuyEnabled: true,
|
||||||
minAssetReserve: 0,
|
minAssetReserve: 0,
|
||||||
|
|||||||
+22
-9
@@ -2,7 +2,7 @@ import { ref } from 'vue'
|
|||||||
|
|
||||||
import { showError, showSuccess } from '@/lib/feedback'
|
import { showError, showSuccess } from '@/lib/feedback'
|
||||||
import { fetchAdminCloudtentaclesSkuList } from '@/services/admin'
|
import { fetchAdminCloudtentaclesSkuList } from '@/services/admin'
|
||||||
import type { AdminCloudtentaclesSkuItem } from '@/types/admin'
|
import type { AdminCloudtentaclesSkuItem, AdminKuaishouCloudDeliveryItem } from '@/types/admin'
|
||||||
|
|
||||||
import type { EditableItem } from './types'
|
import type { EditableItem } from './types'
|
||||||
|
|
||||||
@@ -86,9 +86,8 @@ export function useKuaishouCloudSku(cloudtentaclesSourceOptions?: { value: { key
|
|||||||
const sourceKey = getSourceKey(item)
|
const sourceKey = getSourceKey(item)
|
||||||
const catalog = cloudSkuCatalogBySourceKey.value[sourceKey] || []
|
const catalog = cloudSkuCatalogBySourceKey.value[sourceKey] || []
|
||||||
const keyword = getCloudSkuKeyword(item)
|
const keyword = getCloudSkuKeyword(item)
|
||||||
const scored = catalog
|
return catalog
|
||||||
.map((sku) => ({ sku, score: scoreCloudSkuMatch(sku, keyword) }))
|
.map((sku) => ({ sku, score: scoreCloudSkuMatch(sku, keyword) }))
|
||||||
.filter((entry) => entry.score > 0 || entry.sku.id === Number(item.cloudSkuId || 0))
|
|
||||||
.sort((left, right) => {
|
.sort((left, right) => {
|
||||||
if (right.score !== left.score) {
|
if (right.score !== left.score) {
|
||||||
return right.score - left.score
|
return right.score - left.score
|
||||||
@@ -97,12 +96,6 @@ export function useKuaishouCloudSku(cloudtentaclesSourceOptions?: { value: { key
|
|||||||
return left.sku.name.localeCompare(right.sku.name, 'zh-CN')
|
return left.sku.name.localeCompare(right.sku.name, 'zh-CN')
|
||||||
})
|
})
|
||||||
.map((entry) => entry.sku)
|
.map((entry) => entry.sku)
|
||||||
|
|
||||||
if (scored.length > 0) {
|
|
||||||
return scored.slice(0, 80)
|
|
||||||
}
|
|
||||||
|
|
||||||
return catalog.slice(0, 80)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatCloudSkuOptionLabel(item: AdminCloudtentaclesSkuItem) {
|
function formatCloudSkuOptionLabel(item: AdminCloudtentaclesSkuItem) {
|
||||||
@@ -126,6 +119,25 @@ export function useKuaishouCloudSku(cloudtentaclesSourceOptions?: { value: { key
|
|||||||
item.cloudSkuName = matched?.name || item.cloudSkuName || ''
|
item.cloudSkuName = matched?.name || item.cloudSkuName || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleDeliveryItemCloudSkuSelected(
|
||||||
|
item: EditableItem,
|
||||||
|
deliveryItem: AdminKuaishouCloudDeliveryItem,
|
||||||
|
value: number | string | undefined,
|
||||||
|
) {
|
||||||
|
const skuId = Number(value || 0)
|
||||||
|
deliveryItem.cloudSkuId = Number.isInteger(skuId) && skuId > 0 ? skuId : 0
|
||||||
|
|
||||||
|
if (!deliveryItem.cloudSkuId) {
|
||||||
|
deliveryItem.cloudSkuName = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceKey = getSourceKey(item)
|
||||||
|
const catalog = cloudSkuCatalogBySourceKey.value[sourceKey] || []
|
||||||
|
const matched = catalog.find((sku) => sku.id === deliveryItem.cloudSkuId)
|
||||||
|
deliveryItem.cloudSkuName = matched?.name || deliveryItem.cloudSkuName || ''
|
||||||
|
}
|
||||||
|
|
||||||
async function handleCloudSkuDropdownVisible(item: EditableItem, visible: boolean) {
|
async function handleCloudSkuDropdownVisible(item: EditableItem, visible: boolean) {
|
||||||
if (!visible) {
|
if (!visible) {
|
||||||
return
|
return
|
||||||
@@ -185,6 +197,7 @@ export function useKuaishouCloudSku(cloudtentaclesSourceOptions?: { value: { key
|
|||||||
getCloudSkuOptions,
|
getCloudSkuOptions,
|
||||||
formatCloudSkuOptionLabel,
|
formatCloudSkuOptionLabel,
|
||||||
handleCloudSkuSelected,
|
handleCloudSkuSelected,
|
||||||
|
handleDeliveryItemCloudSkuSelected,
|
||||||
handleCloudSkuDropdownVisible,
|
handleCloudSkuDropdownVisible,
|
||||||
refreshCloudSkuCatalog,
|
refreshCloudSkuCatalog,
|
||||||
syncCloudSkuCatalogLoading,
|
syncCloudSkuCatalogLoading,
|
||||||
|
|||||||
Reference in New Issue
Block a user