优化履约账号优先级配置
This commit is contained in:
@@ -14,8 +14,7 @@
|
|||||||
"externalItemId": "",
|
"externalItemId": "",
|
||||||
"externalSkuName": "示例外部商品",
|
"externalSkuName": "示例外部商品",
|
||||||
"resolvedSkuName": "示例快手云商品",
|
"resolvedSkuName": "示例快手云商品",
|
||||||
"cloudSourceKey": "default",
|
"cloudSourceKeys": ["account_example"],
|
||||||
"cloudSourceKeyFallbacks": [],
|
|
||||||
"cloudSkuId": 1,
|
"cloudSkuId": 1,
|
||||||
"cloudSkuName": "示例云商品",
|
"cloudSkuName": "示例云商品",
|
||||||
"autoBuyEnabled": true,
|
"autoBuyEnabled": true,
|
||||||
|
|||||||
@@ -84,9 +84,8 @@ export function mapKuaishouCloudFulfillmentContext(value: unknown): JsonRecord |
|
|||||||
},
|
},
|
||||||
binding: {
|
binding: {
|
||||||
prepareStatus: String(binding.prepareStatus || 'pending').trim() || 'pending',
|
prepareStatus: String(binding.prepareStatus || 'pending').trim() || 'pending',
|
||||||
cloudSourceKey: String(binding.cloudSourceKey || '').trim(),
|
cloudSourceKeys: Array.isArray(binding.cloudSourceKeys)
|
||||||
cloudSourceKeyFallbacks: Array.isArray(binding.cloudSourceKeyFallbacks)
|
? binding.cloudSourceKeys.map((value: unknown) => String(value || '').trim()).filter(Boolean)
|
||||||
? binding.cloudSourceKeyFallbacks.map((value: unknown) => String(value || '').trim()).filter(Boolean)
|
|
||||||
: [],
|
: [],
|
||||||
resolvedSourceKey: String(binding.resolvedSourceKey || '').trim(),
|
resolvedSourceKey: String(binding.resolvedSourceKey || '').trim(),
|
||||||
skuId: Number(binding.skuId || 0) || 0,
|
skuId: Number(binding.skuId || 0) || 0,
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ test('mapAdminKuaishouCloudFulfillmentSource normalizes items and defaults', ()
|
|||||||
id: ' item-1 ',
|
id: ' item-1 ',
|
||||||
provider: '',
|
provider: '',
|
||||||
platform: '',
|
platform: '',
|
||||||
cloudSourceKey: '',
|
cloudSourceKeys: [' account_a ', 'account_b'],
|
||||||
cloudSkuId: '0',
|
cloudSkuId: '0',
|
||||||
autoConsumeAfterDispatch: true,
|
autoConsumeAfterDispatch: true,
|
||||||
kuaishouConsumeShopId: ' ks1 ',
|
kuaishouConsumeShopId: ' ks1 ',
|
||||||
@@ -103,8 +103,7 @@ test('mapAdminKuaishouCloudFulfillmentSource normalizes items and defaults', ()
|
|||||||
externalItemId: '',
|
externalItemId: '',
|
||||||
externalSkuName: '',
|
externalSkuName: '',
|
||||||
resolvedSkuName: '',
|
resolvedSkuName: '',
|
||||||
cloudSourceKey: 'default',
|
cloudSourceKeys: ['account_a', 'account_b'],
|
||||||
cloudSourceKeyFallbacks: [],
|
|
||||||
cloudSkuId: 0,
|
cloudSkuId: 0,
|
||||||
cloudSkuName: '',
|
cloudSkuName: '',
|
||||||
vnKey: '',
|
vnKey: '',
|
||||||
|
|||||||
@@ -75,10 +75,8 @@ export function mapAdminKuaishouCloudFulfillmentItem(item: JsonObject = {}) {
|
|||||||
externalItemId: String(item.externalItemId || "").trim(),
|
externalItemId: String(item.externalItemId || "").trim(),
|
||||||
externalSkuName: String(item.externalSkuName || "").trim(),
|
externalSkuName: String(item.externalSkuName || "").trim(),
|
||||||
resolvedSkuName: String(item.resolvedSkuName || "").trim(),
|
resolvedSkuName: String(item.resolvedSkuName || "").trim(),
|
||||||
cloudSourceKey:
|
cloudSourceKeys: Array.isArray(item.cloudSourceKeys)
|
||||||
String(item.cloudSourceKey || "default").trim() || "default",
|
? item.cloudSourceKeys
|
||||||
cloudSourceKeyFallbacks: Array.isArray(item.cloudSourceKeyFallbacks)
|
|
||||||
? item.cloudSourceKeyFallbacks
|
|
||||||
.map((value) => String(value || "").trim())
|
.map((value) => String(value || "").trim())
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
: [],
|
: [],
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export async function prepareAdminTaskKuaishouCloudFulfillment(
|
|||||||
|
|
||||||
const taskContext = parseTaskContext(task)
|
const taskContext = parseTaskContext(task)
|
||||||
const flow = normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment)
|
const flow = normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment)
|
||||||
const cloudContext = resolvePersistedCloudtentaclesContext()
|
const cloudContext = resolvePersistedCloudtentaclesContext(flow.binding.cloudSourceKeys)
|
||||||
const [knapsack, skuList] = await Promise.all([
|
const [knapsack, skuList] = await Promise.all([
|
||||||
getCloudtentaclesKnapsack(cloudContext),
|
getCloudtentaclesKnapsack(cloudContext),
|
||||||
listCloudtentaclesSku(cloudContext),
|
listCloudtentaclesSku(cloudContext),
|
||||||
@@ -164,6 +164,7 @@ export async function prepareAdminTaskKuaishouCloudFulfillment(
|
|||||||
...flowWithResolvedBinding,
|
...flowWithResolvedBinding,
|
||||||
binding: {
|
binding: {
|
||||||
...flowWithResolvedBinding.binding,
|
...flowWithResolvedBinding.binding,
|
||||||
|
resolvedSourceKey: cloudContext.resolvedSourceKey,
|
||||||
vnKey: preparedBinding.vnKey,
|
vnKey: preparedBinding.vnKey,
|
||||||
prepareStatus: 'ready',
|
prepareStatus: 'ready',
|
||||||
vnId,
|
vnId,
|
||||||
@@ -250,7 +251,10 @@ export async function dispatchAdminTaskKuaishouCloudFulfillment(
|
|||||||
|
|
||||||
const taskContext = parseTaskContext(task)
|
const taskContext = parseTaskContext(task)
|
||||||
const flow = normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment)
|
const flow = normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment)
|
||||||
const cloudContext = resolvePersistedCloudtentaclesContext()
|
const cloudContext = resolvePersistedCloudtentaclesContext([
|
||||||
|
flow.binding.resolvedSourceKey,
|
||||||
|
...flow.binding.cloudSourceKeys,
|
||||||
|
])
|
||||||
|
|
||||||
if (!flow.binding.skuId || !flow.binding.vnId || !flow.binding.vnPhone) {
|
if (!flow.binding.skuId || !flow.binding.vnId || !flow.binding.vnPhone) {
|
||||||
throw createHttpError('当前任务还没有准备好绑定资源,请先准备绑定资源', {
|
throw createHttpError('当前任务还没有准备好绑定资源,请先准备绑定资源', {
|
||||||
@@ -364,7 +368,10 @@ export async function refreshAdminTaskKuaishouCloudRoleInfo(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const cloudContext = resolvePersistedCloudtentaclesContext()
|
const cloudContext = resolvePersistedCloudtentaclesContext([
|
||||||
|
flow.binding.resolvedSourceKey,
|
||||||
|
...flow.binding.cloudSourceKeys,
|
||||||
|
])
|
||||||
const bindInfoResult = await getCloudtentaclesBindInfo({
|
const bindInfoResult = await getCloudtentaclesBindInfo({
|
||||||
...cloudContext,
|
...cloudContext,
|
||||||
key: flow.binding.vnKey,
|
key: flow.binding.vnKey,
|
||||||
@@ -441,7 +448,10 @@ export async function returnNumberAdminTaskKuaishouCloudFulfillment(
|
|||||||
|
|
||||||
const taskContext = parseTaskContext(task)
|
const taskContext = parseTaskContext(task)
|
||||||
const flow = normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment)
|
const flow = normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment)
|
||||||
const cloudContext = resolvePersistedCloudtentaclesContext()
|
const cloudContext = resolvePersistedCloudtentaclesContext([
|
||||||
|
flow.binding.resolvedSourceKey,
|
||||||
|
...flow.binding.cloudSourceKeys,
|
||||||
|
])
|
||||||
|
|
||||||
if (!flow.binding.vnId || !flow.binding.vnKey) {
|
if (!flow.binding.vnId || !flow.binding.vnKey) {
|
||||||
throw createHttpError('当前任务缺少可退还的虚拟号信息', {
|
throw createHttpError('当前任务缺少可退还的虚拟号信息', {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createHttpError } from '../../../utils/http.js'
|
import { createHttpError } from '../../../utils/http.js'
|
||||||
import type { HttpErrorLike } from '../../../utils/http.js'
|
import type { HttpErrorLike } from '../../../utils/http.js'
|
||||||
import {
|
import {
|
||||||
resolvePersistedCloudtentaclesContext as resolveFulfillmentPersistedCloudtentaclesContext,
|
resolvePersistedCloudtentaclesContextBySourceKeys,
|
||||||
} from '../../fulfillment/kuaishou-cloud/cloudtentacles-context.js'
|
} from '../../fulfillment/kuaishou-cloud/cloudtentacles-context.js'
|
||||||
|
|
||||||
export {
|
export {
|
||||||
@@ -23,6 +23,7 @@ export type CloudtentaclesContext = {
|
|||||||
token: string
|
token: string
|
||||||
deviceId: string
|
deviceId: string
|
||||||
deviceType: number
|
deviceType: number
|
||||||
|
resolvedSourceKey: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CloudSkuLikeItem = {
|
export type CloudSkuLikeItem = {
|
||||||
@@ -62,9 +63,9 @@ export type PreparedKuaishouCloudBindResource = {
|
|||||||
bindUrl: string
|
bindUrl: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolvePersistedCloudtentaclesContext(): CloudtentaclesContext {
|
export function resolvePersistedCloudtentaclesContext(sourceKeys: unknown[]): CloudtentaclesContext {
|
||||||
try {
|
try {
|
||||||
return resolveFulfillmentPersistedCloudtentaclesContext()
|
return resolvePersistedCloudtentaclesContextBySourceKeys(sourceKeys)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isMissingCloudtentaclesTokenError(error)) {
|
if (isMissingCloudtentaclesTokenError(error)) {
|
||||||
throw createHttpError('当前 cloudtentacles 没有可用 token,请先到平台配置完成登录校验', {
|
throw createHttpError('当前 cloudtentacles 没有可用 token,请先到平台配置完成登录校验', {
|
||||||
|
|||||||
@@ -172,7 +172,10 @@ export function mapClaimKuaishouCloudFulfillment(task: TaskRow, order: OrderRow)
|
|||||||
},
|
},
|
||||||
binding: {
|
binding: {
|
||||||
prepareStatus: String(binding.prepareStatus || 'pending').trim() || 'pending',
|
prepareStatus: String(binding.prepareStatus || 'pending').trim() || 'pending',
|
||||||
cloudSourceKey: String(binding.cloudSourceKey || '').trim(),
|
cloudSourceKeys: Array.isArray(binding.cloudSourceKeys)
|
||||||
|
? binding.cloudSourceKeys.map((value: unknown) => String(value || '').trim()).filter(Boolean)
|
||||||
|
: [],
|
||||||
|
resolvedSourceKey: String(binding.resolvedSourceKey || '').trim(),
|
||||||
skuId: Number(binding.skuId || 0) || 0,
|
skuId: Number(binding.skuId || 0) || 0,
|
||||||
skuName: String(binding.skuName || '').trim(),
|
skuName: String(binding.skuName || '').trim(),
|
||||||
vnKey: String(binding.vnKey || '').trim(),
|
vnKey: String(binding.vnKey || '').trim(),
|
||||||
|
|||||||
@@ -1,56 +1,18 @@
|
|||||||
import { createHttpError } from "../../../utils/http.js";
|
import { createHttpError } from "../../../utils/http.js";
|
||||||
import {
|
import { getCloudtentaclesSourceByKey } from "../../platforms/cloudtentacles/source-config-service.js";
|
||||||
getCloudtentaclesSourceConfig,
|
import { getCloudtentaclesSessionStateByKey } from "../../platforms/cloudtentacles/session-state-service.js";
|
||||||
getCloudtentaclesSourceByKey,
|
|
||||||
} from "../../platforms/cloudtentacles/source-config-service.js";
|
|
||||||
import {
|
|
||||||
getCloudtentaclesSessionState,
|
|
||||||
getCloudtentaclesSessionStateByKey,
|
|
||||||
} from "../../platforms/cloudtentacles/session-state-service.js";
|
|
||||||
import { normalizeStringArray } from "./domain.js";
|
import { normalizeStringArray } from "./domain.js";
|
||||||
|
|
||||||
export function resolvePersistedCloudtentaclesContext(sourceKey = "default") {
|
|
||||||
const source =
|
|
||||||
getCloudtentaclesSourceByKey(sourceKey) || getCloudtentaclesSourceConfig();
|
|
||||||
const session =
|
|
||||||
getCloudtentaclesSessionStateByKey(sourceKey) ||
|
|
||||||
getCloudtentaclesSessionState();
|
|
||||||
const token = String(session.token || "").trim();
|
|
||||||
|
|
||||||
if (!token) {
|
|
||||||
throw createHttpError(
|
|
||||||
"当前 cloudtentacles 没有可用 token,请先到平台配置完成登录校验",
|
|
||||||
{
|
|
||||||
statusCode: 409,
|
|
||||||
errorCode: "kuaishou_cloud_missing_cloud_token",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
baseUrl:
|
|
||||||
String(session.baseUrl || source.baseUrl || "").trim() ||
|
|
||||||
"https://123.207.217.176",
|
|
||||||
token,
|
|
||||||
deviceId: String(session.deviceId || source.deviceId || "-").trim() || "-",
|
|
||||||
deviceType: Number(session.deviceType ?? source.deviceType ?? 0),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按优先级依次尝试 cloudSourceKey 和 fallbacks 列表中的账号,
|
* 按优先级依次尝试 cloudSourceKeys 列表中的账号,
|
||||||
* 找到第一个有可用 token 的账号返回其 context。
|
* 找到第一个有可用 token 的账号返回其 context。
|
||||||
* 将实际使用的 resolvedSourceKey 也返回,确保后续操作(退号、发货等)
|
* 将实际使用的 resolvedSourceKey 也返回,确保后续操作(退号、发货等)
|
||||||
* 使用同一个 sourceKey,防止跨账号操作导致数据不一致。
|
* 使用同一个 sourceKey,防止跨账号操作导致数据不一致。
|
||||||
*/
|
*/
|
||||||
export function resolvePersistedCloudtentaclesContextWithFallback(
|
export function resolvePersistedCloudtentaclesContextBySourceKeys(
|
||||||
primarySourceKey = "default",
|
sourceKeys: unknown[] = []
|
||||||
fallbacks: unknown[] = []
|
|
||||||
) {
|
) {
|
||||||
const candidates = [
|
const candidates = normalizeStringArray(sourceKeys);
|
||||||
String(primarySourceKey || "default").trim() || "default",
|
|
||||||
...normalizeStringArray(fallbacks),
|
|
||||||
];
|
|
||||||
|
|
||||||
const uniqueCandidates = [...new Set(candidates)];
|
const uniqueCandidates = [...new Set(candidates)];
|
||||||
|
|
||||||
@@ -101,7 +63,7 @@ export function resolvePersistedCloudtentaclesContextWithFallback(
|
|||||||
throw (
|
throw (
|
||||||
lastError ||
|
lastError ||
|
||||||
createHttpError(
|
createHttpError(
|
||||||
"所有 cloudtentacles 备选账号均不可用,请先到平台配置完成登录校验",
|
"所有 cloudtentacles 账号均不可用,请先到平台配置完成登录校验",
|
||||||
{ statusCode: 409, errorCode: "kuaishou_cloud_all_source_keys_exhausted" }
|
{ statusCode: 409, errorCode: "kuaishou_cloud_all_source_keys_exhausted" }
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -62,11 +62,7 @@ export function normalizeKuaishouCloudFlow(value: unknown) {
|
|||||||
binding: {
|
binding: {
|
||||||
prepareStatus:
|
prepareStatus:
|
||||||
String(binding.prepareStatus || "pending").trim() || "pending",
|
String(binding.prepareStatus || "pending").trim() || "pending",
|
||||||
cloudSourceKey:
|
cloudSourceKeys: normalizeStringArray(binding.cloudSourceKeys),
|
||||||
String(binding.cloudSourceKey || "default").trim() || "default",
|
|
||||||
cloudSourceKeyFallbacks: normalizeStringArray(
|
|
||||||
binding.cloudSourceKeyFallbacks
|
|
||||||
),
|
|
||||||
resolvedSourceKey: String(binding.resolvedSourceKey || "").trim(),
|
resolvedSourceKey: String(binding.resolvedSourceKey || "").trim(),
|
||||||
skuId: Number(binding.skuId || 0) || 0,
|
skuId: Number(binding.skuId || 0) || 0,
|
||||||
skuName: String(binding.skuName || "").trim(),
|
skuName: String(binding.skuName || "").trim(),
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import {
|
|||||||
resolveKuaishouCloudVnKeyCandidates,
|
resolveKuaishouCloudVnKeyCandidates,
|
||||||
} from "./binding-resources.js";
|
} from "./binding-resources.js";
|
||||||
import {
|
import {
|
||||||
resolvePersistedCloudtentaclesContextWithFallback,
|
resolvePersistedCloudtentaclesContextBySourceKeys,
|
||||||
} from "./cloudtentacles-context.js";
|
} from "./cloudtentacles-context.js";
|
||||||
import {
|
import {
|
||||||
getTaskClaimExpiresAt,
|
getTaskClaimExpiresAt,
|
||||||
@@ -55,8 +55,7 @@ export {
|
|||||||
resolveKuaishouCloudBindUrlExpiresAt,
|
resolveKuaishouCloudBindUrlExpiresAt,
|
||||||
} from "./domain.js";
|
} from "./domain.js";
|
||||||
export {
|
export {
|
||||||
resolvePersistedCloudtentaclesContext,
|
resolvePersistedCloudtentaclesContextBySourceKeys,
|
||||||
resolvePersistedCloudtentaclesContextWithFallback,
|
|
||||||
} from "./cloudtentacles-context.js";
|
} from "./cloudtentacles-context.js";
|
||||||
|
|
||||||
export async function ensureTaskClaimLink(task: TaskRow) {
|
export async function ensureTaskClaimLink(task: TaskRow) {
|
||||||
@@ -135,9 +134,8 @@ export async function prepareKuaishouCloudFulfillmentTask(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const cloudContext = resolvePersistedCloudtentaclesContextWithFallback(
|
const cloudContext = resolvePersistedCloudtentaclesContextBySourceKeys(
|
||||||
flow.binding.cloudSourceKey || "default",
|
flow.binding.cloudSourceKeys
|
||||||
flow.binding.cloudSourceKeyFallbacks || []
|
|
||||||
);
|
);
|
||||||
const [knapsack, skuList] = await Promise.all([
|
const [knapsack, skuList] = await Promise.all([
|
||||||
getCloudtentaclesKnapsack(cloudContext),
|
getCloudtentaclesKnapsack(cloudContext),
|
||||||
@@ -335,12 +333,10 @@ export async function refreshKuaishouCloudTaskBindUrl(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const effectiveSourceKey =
|
const cloudContext = resolvePersistedCloudtentaclesContextBySourceKeys([
|
||||||
flow.binding.resolvedSourceKey || flow.binding.cloudSourceKey || "default";
|
flow.binding.resolvedSourceKey,
|
||||||
const cloudContext = resolvePersistedCloudtentaclesContextWithFallback(
|
...flow.binding.cloudSourceKeys,
|
||||||
effectiveSourceKey,
|
]);
|
||||||
flow.binding.cloudSourceKeyFallbacks || []
|
|
||||||
);
|
|
||||||
const oldVnKey = flow.binding.vnKey;
|
const oldVnKey = flow.binding.vnKey;
|
||||||
const oldVnId = flow.binding.vnId;
|
const oldVnId = flow.binding.vnId;
|
||||||
const oldVnPhone = flow.binding.vnPhone;
|
const oldVnPhone = flow.binding.vnPhone;
|
||||||
@@ -707,12 +703,10 @@ export async function refreshKuaishouCloudTaskRoleInfo(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const effectiveSourceKey =
|
const cloudContext = resolvePersistedCloudtentaclesContextBySourceKeys([
|
||||||
flow.binding.resolvedSourceKey || flow.binding.cloudSourceKey || "default";
|
flow.binding.resolvedSourceKey,
|
||||||
const cloudContext = resolvePersistedCloudtentaclesContextWithFallback(
|
...flow.binding.cloudSourceKeys,
|
||||||
effectiveSourceKey,
|
]);
|
||||||
flow.binding.cloudSourceKeyFallbacks || []
|
|
||||||
);
|
|
||||||
const bindInfoResult = await getCloudtentaclesBindInfo({
|
const bindInfoResult = await getCloudtentaclesBindInfo({
|
||||||
...cloudContext,
|
...cloudContext,
|
||||||
key: flow.binding.vnKey,
|
key: flow.binding.vnKey,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
normalizeKuaishouCloudFlow,
|
normalizeKuaishouCloudFlow,
|
||||||
type JsonObject,
|
type JsonObject,
|
||||||
} from "./domain.js";
|
} from "./domain.js";
|
||||||
import { resolvePersistedCloudtentaclesContextWithFallback } from "./cloudtentacles-context.js";
|
import { resolvePersistedCloudtentaclesContextBySourceKeys } from "./cloudtentacles-context.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";
|
||||||
|
|
||||||
@@ -37,12 +37,10 @@ export async function dispatchKuaishouCloudFulfillmentTask(
|
|||||||
const now = nowIso();
|
const now = nowIso();
|
||||||
const taskContext = parseTaskContext(task);
|
const taskContext = parseTaskContext(task);
|
||||||
const flow = normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment);
|
const flow = normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment);
|
||||||
const effectiveSourceKey =
|
const cloudContext = resolvePersistedCloudtentaclesContextBySourceKeys([
|
||||||
flow.binding.resolvedSourceKey || flow.binding.cloudSourceKey || "default";
|
flow.binding.resolvedSourceKey,
|
||||||
const cloudContext = resolvePersistedCloudtentaclesContextWithFallback(
|
...flow.binding.cloudSourceKeys,
|
||||||
effectiveSourceKey,
|
]);
|
||||||
flow.binding.cloudSourceKeyFallbacks || []
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!flow.binding.skuId || !flow.binding.vnId || !flow.binding.vnPhone) {
|
if (!flow.binding.skuId || !flow.binding.vnId || !flow.binding.vnPhone) {
|
||||||
throw createHttpError(
|
throw createHttpError(
|
||||||
@@ -178,12 +176,10 @@ export async function returnKuaishouCloudFulfillmentTask(
|
|||||||
const now = nowIso();
|
const now = nowIso();
|
||||||
const taskContext = parseTaskContext(task);
|
const taskContext = parseTaskContext(task);
|
||||||
const flow = normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment);
|
const flow = normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment);
|
||||||
const effectiveSourceKey =
|
const cloudContext = resolvePersistedCloudtentaclesContextBySourceKeys([
|
||||||
flow.binding.resolvedSourceKey || flow.binding.cloudSourceKey || "default";
|
flow.binding.resolvedSourceKey,
|
||||||
const cloudContext = resolvePersistedCloudtentaclesContextWithFallback(
|
...flow.binding.cloudSourceKeys,
|
||||||
effectiveSourceKey,
|
]);
|
||||||
flow.binding.cloudSourceKeyFallbacks || []
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!flow.binding.vnId || !flow.binding.vnKey) {
|
if (!flow.binding.vnId || !flow.binding.vnKey) {
|
||||||
throw createHttpError("当前任务缺少可退还的虚拟号信息", {
|
throw createHttpError("当前任务缺少可退还的虚拟号信息", {
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ export async function syncDeliveryTasksForOrderWithDeps(
|
|||||||
},
|
},
|
||||||
binding: {
|
binding: {
|
||||||
prepareStatus: 'pending',
|
prepareStatus: 'pending',
|
||||||
cloudSourceKey: String(cloudtentaclesConfig.cloudSourceKey || 'default').trim() || 'default',
|
cloudSourceKeys: normalizeStringArray(cloudtentaclesConfig.cloudSourceKeys),
|
||||||
skuId: Number(cloudtentaclesConfig.skuId || 0) || 0,
|
skuId: Number(cloudtentaclesConfig.skuId || 0) || 0,
|
||||||
skuName: String(cloudtentaclesConfig.skuName || '').trim(),
|
skuName: String(cloudtentaclesConfig.skuName || '').trim(),
|
||||||
vnKey: '1',
|
vnKey: '1',
|
||||||
@@ -384,6 +384,16 @@ function parseJsonObject(value: unknown): JsonObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeStringArray(value: unknown): string[] {
|
||||||
|
if (!Array.isArray(value)) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(
|
||||||
|
new Set(value.map((item) => String(item || '').trim()).filter(Boolean)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ test('mapKuaishouCloudFulfillmentItemsToBindings maps 91kaquan rules', () => {
|
|||||||
externalSkuCode: '952',
|
externalSkuCode: '952',
|
||||||
externalItemId: '952',
|
externalItemId: '952',
|
||||||
externalSkuName: '测试2',
|
externalSkuName: '测试2',
|
||||||
|
cloudSourceKeys: ['account_a', 'account_b'],
|
||||||
cloudSkuId: 28,
|
cloudSkuId: 28,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -35,8 +36,7 @@ test('mapKuaishouCloudFulfillmentItemsToBindings maps 91kaquan rules', () => {
|
|||||||
flowType: 'kuaishou_cloud_fulfillment',
|
flowType: 'kuaishou_cloud_fulfillment',
|
||||||
configId: '',
|
configId: '',
|
||||||
cloudtentacles: {
|
cloudtentacles: {
|
||||||
cloudSourceKey: 'default',
|
cloudSourceKeys: ['account_a', 'account_b'],
|
||||||
cloudSourceKeyFallbacks: [],
|
|
||||||
skuId: 28,
|
skuId: 28,
|
||||||
skuName: '',
|
skuName: '',
|
||||||
vnKey: '1',
|
vnKey: '1',
|
||||||
|
|||||||
@@ -46,11 +46,7 @@ export function mapKuaishouCloudFulfillmentItemsToBindings(config: JsonObject =
|
|||||||
flowType: "kuaishou_cloud_fulfillment",
|
flowType: "kuaishou_cloud_fulfillment",
|
||||||
configId: String(item.id || "").trim(),
|
configId: String(item.id || "").trim(),
|
||||||
cloudtentacles: {
|
cloudtentacles: {
|
||||||
cloudSourceKey:
|
cloudSourceKeys: normalizeStringArray(item.cloudSourceKeys),
|
||||||
String(item.cloudSourceKey || "default").trim() || "default",
|
|
||||||
cloudSourceKeyFallbacks: Array.isArray(item.cloudSourceKeyFallbacks)
|
|
||||||
? item.cloudSourceKeyFallbacks
|
|
||||||
: [],
|
|
||||||
skuId: normalizePositiveInteger(item.cloudSkuId),
|
skuId: normalizePositiveInteger(item.cloudSkuId),
|
||||||
skuName: String(item.cloudSkuName || "").trim(),
|
skuName: String(item.cloudSkuName || "").trim(),
|
||||||
vnKey: "1",
|
vnKey: "1",
|
||||||
@@ -113,6 +109,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 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();
|
||||||
const externalSkuName = String(rawValue.externalSkuName || "").trim();
|
const externalSkuName = String(rawValue.externalSkuName || "").trim();
|
||||||
@@ -135,6 +132,10 @@ function normalizeKuaishouCloudFulfillmentItem(rawValue: unknown) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cloudSourceKeys.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (!externalSkuCode && !externalItemId && !externalSkuName) {
|
if (!externalSkuCode && !externalItemId && !externalSkuName) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -155,11 +156,7 @@ function normalizeKuaishouCloudFulfillmentItem(rawValue: unknown) {
|
|||||||
resolvedSkuName: String(
|
resolvedSkuName: String(
|
||||||
rawValue.resolvedSkuName || rawValue.internalSkuName || ""
|
rawValue.resolvedSkuName || rawValue.internalSkuName || ""
|
||||||
).trim(),
|
).trim(),
|
||||||
cloudSourceKey:
|
cloudSourceKeys,
|
||||||
String(rawValue.cloudSourceKey || "default").trim() || "default",
|
|
||||||
cloudSourceKeyFallbacks: normalizeStringArray(
|
|
||||||
rawValue.cloudSourceKeyFallbacks
|
|
||||||
),
|
|
||||||
cloudSkuId,
|
cloudSkuId,
|
||||||
cloudSkuName: String(rawValue.cloudSkuName || "").trim(),
|
cloudSkuName: String(rawValue.cloudSkuName || "").trim(),
|
||||||
vnKey: "1",
|
vnKey: "1",
|
||||||
@@ -204,12 +201,5 @@ function normalizeStringArray(value: unknown) {
|
|||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
return value.map((v: unknown) => String(v || "").trim()).filter(Boolean);
|
return value.map((v: unknown) => String(v || "").trim()).filter(Boolean);
|
||||||
}
|
}
|
||||||
// 兼容旧格式:字符串用逗号分隔
|
|
||||||
if (typeof value === "string") {
|
|
||||||
return value
|
|
||||||
.split(",")
|
|
||||||
.map((v: string) => v.trim())
|
|
||||||
.filter(Boolean);
|
|
||||||
}
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,7 @@ export type AdminKuaishouCloudFulfillmentItemInput = {
|
|||||||
externalItemId?: string
|
externalItemId?: string
|
||||||
externalSkuName?: string
|
externalSkuName?: string
|
||||||
resolvedSkuName?: string
|
resolvedSkuName?: string
|
||||||
cloudSourceKey?: string
|
cloudSourceKeys?: string[]
|
||||||
cloudSourceKeyFallbacks?: string[]
|
|
||||||
cloudSkuId?: number | string
|
cloudSkuId?: number | string
|
||||||
cloudSkuName?: string
|
cloudSkuName?: string
|
||||||
vnKey?: string
|
vnKey?: string
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export interface AdminKuaishouCloudFulfillmentItem {
|
|||||||
externalItemId: string
|
externalItemId: string
|
||||||
externalSkuName: string
|
externalSkuName: string
|
||||||
resolvedSkuName: string
|
resolvedSkuName: string
|
||||||
cloudSourceKey: string
|
cloudSourceKeys: string[]
|
||||||
cloudSkuId: number
|
cloudSkuId: number
|
||||||
cloudSkuName: string
|
cloudSkuName: string
|
||||||
vnKey: string
|
vnKey: string
|
||||||
@@ -22,7 +22,6 @@ export interface AdminKuaishouCloudFulfillmentItem {
|
|||||||
kuaishouConsumeShopId: string
|
kuaishouConsumeShopId: string
|
||||||
kuaishouConsumeShopName: string
|
kuaishouConsumeShopName: string
|
||||||
notes: string
|
notes: string
|
||||||
cloudSourceKeyFallbacks?: string[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AdminKuaishouCloudFulfillmentConfig {
|
export interface AdminKuaishouCloudFulfillmentConfig {
|
||||||
|
|||||||
@@ -126,8 +126,7 @@ export interface AdminTaskDetail {
|
|||||||
}
|
}
|
||||||
binding: {
|
binding: {
|
||||||
prepareStatus: string
|
prepareStatus: string
|
||||||
cloudSourceKey: string
|
cloudSourceKeys: string[]
|
||||||
cloudSourceKeyFallbacks: string[]
|
|
||||||
resolvedSourceKey: string
|
resolvedSourceKey: string
|
||||||
skuId: number
|
skuId: number
|
||||||
skuName: string
|
skuName: string
|
||||||
|
|||||||
@@ -74,7 +74,8 @@ export interface ClaimKuaishouCloudFlowInfo {
|
|||||||
}
|
}
|
||||||
binding: {
|
binding: {
|
||||||
prepareStatus: string
|
prepareStatus: string
|
||||||
cloudSourceKey: string
|
cloudSourceKeys: string[]
|
||||||
|
resolvedSourceKey: string
|
||||||
skuId: number
|
skuId: number
|
||||||
skuName: string
|
skuName: string
|
||||||
vnKey: string
|
vnKey: string
|
||||||
|
|||||||
+2
@@ -58,6 +58,7 @@ onMounted(config.loadConfigs)
|
|||||||
:get-card-summary="config.getCardSummary"
|
:get-card-summary="config.getCardSummary"
|
||||||
:get-rule-state="config.getRuleState"
|
:get-rule-state="config.getRuleState"
|
||||||
:get-external-match-summary="config.getExternalMatchSummary"
|
:get-external-match-summary="config.getExternalMatchSummary"
|
||||||
|
: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-cloud-sku-options="cloudSku.getCloudSkuOptions"
|
:get-cloud-sku-options="cloudSku.getCloudSkuOptions"
|
||||||
@@ -78,6 +79,7 @@ onMounted(config.loadConfigs)
|
|||||||
(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)"
|
||||||
@remove-item="config.removeItem($event)"
|
@remove-item="config.removeItem($event)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
+13
-20
@@ -16,6 +16,7 @@ defineProps<{
|
|||||||
getCardSummary: (item: EditableItem) => string
|
getCardSummary: (item: EditableItem) => string
|
||||||
getRuleState: (item: EditableItem) => { label: string; tone: string }
|
getRuleState: (item: EditableItem) => { label: string; tone: string }
|
||||||
getExternalMatchSummary: (item: EditableItem) => string
|
getExternalMatchSummary: (item: EditableItem) => string
|
||||||
|
getCloudSourceSummary: (item: EditableItem) => string
|
||||||
getCloudSkuSummary: (item: EditableItem) => string
|
getCloudSkuSummary: (item: EditableItem) => string
|
||||||
getConsumeShopSummary: (item: EditableItem) => string
|
getConsumeShopSummary: (item: EditableItem) => string
|
||||||
getCloudSkuOptions: (item: EditableItem) => AdminCloudtentaclesSkuItem[]
|
getCloudSkuOptions: (item: EditableItem) => AdminCloudtentaclesSkuItem[]
|
||||||
@@ -29,6 +30,7 @@ const emit = defineEmits<{
|
|||||||
handleCloudSkuSelected: [value: number | string | undefined]
|
handleCloudSkuSelected: [value: number | string | undefined]
|
||||||
handleCloudSkuDropdownVisible: [visible: boolean]
|
handleCloudSkuDropdownVisible: [visible: boolean]
|
||||||
handleKuaishouConsumeShopSelected: []
|
handleKuaishouConsumeShopSelected: []
|
||||||
|
handleCloudSourceKeysChanged: []
|
||||||
remove: []
|
remove: []
|
||||||
}>()
|
}>()
|
||||||
</script>
|
</script>
|
||||||
@@ -66,6 +68,9 @@ const emit = defineEmits<{
|
|||||||
<el-descriptions-item label="内部 SKU">{{
|
<el-descriptions-item label="内部 SKU">{{
|
||||||
item.internalSkuCode || '-'
|
item.internalSkuCode || '-'
|
||||||
}}</el-descriptions-item>
|
}}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="cloud 账号">{{
|
||||||
|
getCloudSourceSummary(item)
|
||||||
|
}}</el-descriptions-item>
|
||||||
<el-descriptions-item label="cloud 资源">{{ getCloudSkuSummary(item) }}</el-descriptions-item>
|
<el-descriptions-item label="cloud 资源">{{ getCloudSkuSummary(item) }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="核销店铺">{{
|
<el-descriptions-item label="核销店铺">{{
|
||||||
getConsumeShopSummary(item)
|
getConsumeShopSummary(item)
|
||||||
@@ -178,11 +183,15 @@ const emit = defineEmits<{
|
|||||||
placeholder="展示用,可选但建议填"
|
placeholder="展示用,可选但建议填"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="cloud 主账号">
|
<el-form-item label="cloud 账号优先级" class="field-wide">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="item.cloudSourceKey"
|
v-model="item.cloudSourceKeys"
|
||||||
class="text-input"
|
class="text-input"
|
||||||
placeholder="选择 cloud 账号"
|
multiple
|
||||||
|
collapse-tags
|
||||||
|
collapse-tags-tooltip
|
||||||
|
placeholder="按顺序选择可用于履约的 cloud 账号"
|
||||||
|
@change="emit('handleCloudSourceKeysChanged')"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="opt in cloudtentaclesSourceOptions"
|
v-for="opt in cloudtentaclesSourceOptions"
|
||||||
@@ -191,23 +200,7 @@ const emit = defineEmits<{
|
|||||||
:value="opt.key"
|
:value="opt.key"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
<small class="field-help">执行时会从左到右尝试,SKU 列表使用第一个账号加载。</small>
|
||||||
<el-form-item label="备选 cloud 账号" class="field-wide">
|
|
||||||
<el-select
|
|
||||||
v-model="item.cloudSourceKeyFallbacks"
|
|
||||||
class="text-input"
|
|
||||||
multiple
|
|
||||||
placeholder="主账号不可用时自动尝试备选"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="opt in cloudtentaclesSourceOptions.filter(
|
|
||||||
(o) => o.key !== item.cloudSourceKey,
|
|
||||||
)"
|
|
||||||
:key="opt.key"
|
|
||||||
:label="opt.label || opt.key"
|
|
||||||
:value="opt.key"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="cloud SKU ID">
|
<el-form-item label="cloud SKU ID">
|
||||||
<el-select
|
<el-select
|
||||||
|
|||||||
+4
@@ -21,6 +21,7 @@ defineProps<{
|
|||||||
getCardSummary: (item: EditableItem) => string
|
getCardSummary: (item: EditableItem) => string
|
||||||
getRuleState: (item: EditableItem) => { label: string; tone: string }
|
getRuleState: (item: EditableItem) => { label: string; tone: string }
|
||||||
getExternalMatchSummary: (item: EditableItem) => string
|
getExternalMatchSummary: (item: EditableItem) => string
|
||||||
|
getCloudSourceSummary: (item: EditableItem) => string
|
||||||
getCloudSkuSummary: (item: EditableItem) => string
|
getCloudSkuSummary: (item: EditableItem) => string
|
||||||
getConsumeShopSummary: (item: EditableItem) => string
|
getConsumeShopSummary: (item: EditableItem) => string
|
||||||
getCloudSkuOptions: (item: EditableItem) => AdminCloudtentaclesSkuItem[]
|
getCloudSkuOptions: (item: EditableItem) => AdminCloudtentaclesSkuItem[]
|
||||||
@@ -42,6 +43,7 @@ const emit = defineEmits<{
|
|||||||
handleCloudSkuSelected: [item: EditableItem, value: number | string | undefined]
|
handleCloudSkuSelected: [item: EditableItem, value: number | string | undefined]
|
||||||
handleCloudSkuDropdownVisible: [item: EditableItem, visible: boolean]
|
handleCloudSkuDropdownVisible: [item: EditableItem, visible: boolean]
|
||||||
handleKuaishouConsumeShopSelected: [item: EditableItem]
|
handleKuaishouConsumeShopSelected: [item: EditableItem]
|
||||||
|
handleCloudSourceKeysChanged: [item: EditableItem]
|
||||||
removeItem: [localId: string]
|
removeItem: [localId: string]
|
||||||
}>()
|
}>()
|
||||||
</script>
|
</script>
|
||||||
@@ -125,6 +127,7 @@ const emit = defineEmits<{
|
|||||||
:get-card-summary="getCardSummary"
|
:get-card-summary="getCardSummary"
|
||||||
:get-rule-state="getRuleState"
|
:get-rule-state="getRuleState"
|
||||||
:get-external-match-summary="getExternalMatchSummary"
|
:get-external-match-summary="getExternalMatchSummary"
|
||||||
|
: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-cloud-sku-options="getCloudSkuOptions"
|
:get-cloud-sku-options="getCloudSkuOptions"
|
||||||
@@ -135,6 +138,7 @@ const emit = defineEmits<{
|
|||||||
@handle-cloud-sku-selected="emit('handleCloudSkuSelected', item, $event)"
|
@handle-cloud-sku-selected="emit('handleCloudSkuSelected', item, $event)"
|
||||||
@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)"
|
||||||
@remove="emit('removeItem', item.localId)"
|
@remove="emit('removeItem', item.localId)"
|
||||||
/>
|
/>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import type { AdminKuaishouCloudFulfillmentItem } from '@/types/admin'
|
|||||||
|
|
||||||
export type EditableItem = AdminKuaishouCloudFulfillmentItem & {
|
export type EditableItem = AdminKuaishouCloudFulfillmentItem & {
|
||||||
localId: string
|
localId: string
|
||||||
|
cloudSourceKeys: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ValidationState = {
|
export type ValidationState = {
|
||||||
|
|||||||
+46
-11
@@ -61,6 +61,7 @@ export function useKuaishouCloudConfig() {
|
|||||||
|
|
||||||
function createEmptyItem(): EditableItem {
|
function createEmptyItem(): EditableItem {
|
||||||
const defaultShop = getDefaultKuaishouConsumeShop()
|
const defaultShop = getDefaultKuaishouConsumeShop()
|
||||||
|
const sourceKeys = getDefaultCloudSourceKeys()
|
||||||
return {
|
return {
|
||||||
localId: crypto.randomUUID(),
|
localId: crypto.randomUUID(),
|
||||||
id: '',
|
id: '',
|
||||||
@@ -75,7 +76,7 @@ export function useKuaishouCloudConfig() {
|
|||||||
externalItemId: '',
|
externalItemId: '',
|
||||||
externalSkuName: '',
|
externalSkuName: '',
|
||||||
resolvedSkuName: '',
|
resolvedSkuName: '',
|
||||||
cloudSourceKey: 'default',
|
cloudSourceKeys: sourceKeys,
|
||||||
cloudSkuId: 0,
|
cloudSkuId: 0,
|
||||||
cloudSkuName: '',
|
cloudSkuName: '',
|
||||||
vnKey: '1',
|
vnKey: '1',
|
||||||
@@ -86,7 +87,6 @@ export function useKuaishouCloudConfig() {
|
|||||||
kuaishouConsumeShopId: defaultShop?.shopId || '',
|
kuaishouConsumeShopId: defaultShop?.shopId || '',
|
||||||
kuaishouConsumeShopName: defaultShop?.kshopName || '',
|
kuaishouConsumeShopName: defaultShop?.kshopName || '',
|
||||||
notes: '',
|
notes: '',
|
||||||
cloudSourceKeyFallbacks: [],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,6 +95,7 @@ export function useKuaishouCloudConfig() {
|
|||||||
item.kuaishouConsumeShopId,
|
item.kuaishouConsumeShopId,
|
||||||
item.kuaishouConsumeShopName,
|
item.kuaishouConsumeShopName,
|
||||||
)
|
)
|
||||||
|
const sourceKeys = normalizeCloudSourceKeys(item.cloudSourceKeys)
|
||||||
return {
|
return {
|
||||||
localId: crypto.randomUUID(),
|
localId: crypto.randomUUID(),
|
||||||
id: item.id || crypto.randomUUID(),
|
id: item.id || crypto.randomUUID(),
|
||||||
@@ -109,7 +110,7 @@ export function useKuaishouCloudConfig() {
|
|||||||
externalItemId: item.externalItemId || '',
|
externalItemId: item.externalItemId || '',
|
||||||
externalSkuName: item.externalSkuName || '',
|
externalSkuName: item.externalSkuName || '',
|
||||||
resolvedSkuName: item.resolvedSkuName || '',
|
resolvedSkuName: item.resolvedSkuName || '',
|
||||||
cloudSourceKey: item.cloudSourceKey || 'default',
|
cloudSourceKeys: sourceKeys,
|
||||||
cloudSkuId: Number(item.cloudSkuId || 0) || 0,
|
cloudSkuId: Number(item.cloudSkuId || 0) || 0,
|
||||||
cloudSkuName: item.cloudSkuName || '',
|
cloudSkuName: item.cloudSkuName || '',
|
||||||
vnKey: item.vnKey || '',
|
vnKey: item.vnKey || '',
|
||||||
@@ -120,9 +121,6 @@ export function useKuaishouCloudConfig() {
|
|||||||
kuaishouConsumeShopId: matchedShop?.shopId || item.kuaishouConsumeShopId || '',
|
kuaishouConsumeShopId: matchedShop?.shopId || item.kuaishouConsumeShopId || '',
|
||||||
kuaishouConsumeShopName: matchedShop?.kshopName || item.kuaishouConsumeShopName || '',
|
kuaishouConsumeShopName: matchedShop?.kshopName || item.kuaishouConsumeShopName || '',
|
||||||
notes: item.notes || '',
|
notes: item.notes || '',
|
||||||
cloudSourceKeyFallbacks: Array.isArray(item.cloudSourceKeyFallbacks)
|
|
||||||
? item.cloudSourceKeyFallbacks
|
|
||||||
: [],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,6 +129,7 @@ export function useKuaishouCloudConfig() {
|
|||||||
item.kuaishouConsumeShopId,
|
item.kuaishouConsumeShopId,
|
||||||
item.kuaishouConsumeShopName,
|
item.kuaishouConsumeShopName,
|
||||||
)
|
)
|
||||||
|
const sourceKeys = normalizeCloudSourceKeys(item.cloudSourceKeys)
|
||||||
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,7 +143,7 @@ export function useKuaishouCloudConfig() {
|
|||||||
externalItemId: item.externalItemId.trim(),
|
externalItemId: item.externalItemId.trim(),
|
||||||
externalSkuName: item.externalSkuName.trim(),
|
externalSkuName: item.externalSkuName.trim(),
|
||||||
resolvedSkuName: item.resolvedSkuName.trim(),
|
resolvedSkuName: item.resolvedSkuName.trim(),
|
||||||
cloudSourceKey: item.cloudSourceKey.trim() || 'default',
|
cloudSourceKeys: sourceKeys,
|
||||||
cloudSkuId: Number(item.cloudSkuId || 0) || 0,
|
cloudSkuId: Number(item.cloudSkuId || 0) || 0,
|
||||||
cloudSkuName: item.cloudSkuName.trim(),
|
cloudSkuName: item.cloudSkuName.trim(),
|
||||||
vnKey: item.vnKey.trim(),
|
vnKey: item.vnKey.trim(),
|
||||||
@@ -157,12 +156,20 @@ export function useKuaishouCloudConfig() {
|
|||||||
matchedShop?.kshopName || item.kuaishouConsumeShopName,
|
matchedShop?.kshopName || item.kuaishouConsumeShopName,
|
||||||
).trim(),
|
).trim(),
|
||||||
notes: item.notes.trim(),
|
notes: item.notes.trim(),
|
||||||
cloudSourceKeyFallbacks: Array.isArray(item.cloudSourceKeyFallbacks)
|
|
||||||
? item.cloudSourceKeyFallbacks.filter(Boolean)
|
|
||||||
: [],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeCloudSourceKeys(values: unknown[] = []) {
|
||||||
|
const keys = values
|
||||||
|
.map((value) => String(value || '').trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
return Array.from(new Set(keys))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDefaultCloudSourceKeys() {
|
||||||
|
return normalizeCloudSourceKeys([cloudtentaclesSourceOptions.value[0]?.key])
|
||||||
|
}
|
||||||
|
|
||||||
// ── validation helpers ────────────────────────────────
|
// ── validation helpers ────────────────────────────────
|
||||||
|
|
||||||
function hasExternalMatch(
|
function hasExternalMatch(
|
||||||
@@ -181,7 +188,7 @@ export function useKuaishouCloudConfig() {
|
|||||||
item.externalItemId.trim() ||
|
item.externalItemId.trim() ||
|
||||||
item.externalSkuName.trim() ||
|
item.externalSkuName.trim() ||
|
||||||
item.resolvedSkuName.trim() ||
|
item.resolvedSkuName.trim() ||
|
||||||
item.cloudSourceKey.trim() !== 'default' ||
|
item.cloudSourceKeys.length > 0 ||
|
||||||
Number(item.cloudSkuId || 0) > 0 ||
|
Number(item.cloudSkuId || 0) > 0 ||
|
||||||
item.cloudSkuName.trim() ||
|
item.cloudSkuName.trim() ||
|
||||||
item.vnKey.trim() ||
|
item.vnKey.trim() ||
|
||||||
@@ -203,6 +210,7 @@ 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 &&
|
||||||
Number(item.cloudSkuId || 0) > 0 &&
|
Number(item.cloudSkuId || 0) > 0 &&
|
||||||
(!item.autoConsumeAfterDispatch || Boolean(item.kuaishouConsumeShopId.trim())) &&
|
(!item.autoConsumeAfterDispatch || Boolean(item.kuaishouConsumeShopId.trim())) &&
|
||||||
hasExternalMatch(item),
|
hasExternalMatch(item),
|
||||||
@@ -220,6 +228,10 @@ export function useKuaishouCloudConfig() {
|
|||||||
return { localId: item.localId, message: `${title} 缺少内部 SKU 编码` }
|
return { localId: item.localId, message: `${title} 缺少内部 SKU 编码` }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item.cloudSourceKeys.length === 0) {
|
||||||
|
return { localId: item.localId, message: `${title} 需要选择至少一个 cloud 账号` }
|
||||||
|
}
|
||||||
|
|
||||||
if (Number(item.cloudSkuId || 0) <= 0) {
|
if (Number(item.cloudSkuId || 0) <= 0) {
|
||||||
return { localId: item.localId, message: `${title} 需要填写 cloud SKU ID` }
|
return { localId: item.localId, message: `${title} 需要填写 cloud SKU ID` }
|
||||||
}
|
}
|
||||||
@@ -340,6 +352,20 @@ export function useKuaishouCloudConfig() {
|
|||||||
return name || id || '未绑定核销店铺'
|
return name || id || '未绑定核销店铺'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getCloudSourceSummary(item: EditableItem) {
|
||||||
|
const keys = normalizeCloudSourceKeys(item.cloudSourceKeys)
|
||||||
|
if (keys.length === 0) {
|
||||||
|
return '未选择 cloud 账号'
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys
|
||||||
|
.map((key, index) => {
|
||||||
|
const option = cloudtentaclesSourceOptions.value.find((source) => source.key === key)
|
||||||
|
return `${index + 1}. ${option?.label || key}`
|
||||||
|
})
|
||||||
|
.join(' / ')
|
||||||
|
}
|
||||||
|
|
||||||
function getCloudSkuSummary(item: EditableItem) {
|
function getCloudSkuSummary(item: EditableItem) {
|
||||||
if (item.cloudSkuId > 0 && item.cloudSkuName.trim()) {
|
if (item.cloudSkuId > 0 && item.cloudSkuName.trim()) {
|
||||||
return `${item.cloudSkuName.trim()} · #${item.cloudSkuId}`
|
return `${item.cloudSkuName.trim()} · #${item.cloudSkuId}`
|
||||||
@@ -441,6 +467,13 @@ export function useKuaishouCloudConfig() {
|
|||||||
setCollapsed(localId, false)
|
setCollapsed(localId, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleCloudSourceKeysChanged(item: EditableItem) {
|
||||||
|
const sourceKeys = normalizeCloudSourceKeys(item.cloudSourceKeys)
|
||||||
|
item.cloudSourceKeys = sourceKeys
|
||||||
|
item.cloudSkuId = 0
|
||||||
|
item.cloudSkuName = ''
|
||||||
|
}
|
||||||
|
|
||||||
async function focusValidationTarget(localId: string) {
|
async function focusValidationTarget(localId: string) {
|
||||||
if (!localId) {
|
if (!localId) {
|
||||||
return
|
return
|
||||||
@@ -524,6 +557,7 @@ export function useKuaishouCloudConfig() {
|
|||||||
getRuleState,
|
getRuleState,
|
||||||
getExternalMatchSummary,
|
getExternalMatchSummary,
|
||||||
getCloudSkuSummary,
|
getCloudSkuSummary,
|
||||||
|
getCloudSourceSummary,
|
||||||
getConsumeShopSummary,
|
getConsumeShopSummary,
|
||||||
// consume shops
|
// consume shops
|
||||||
getKuaishouConsumeShopOptions,
|
getKuaishouConsumeShopOptions,
|
||||||
@@ -531,6 +565,7 @@ export function useKuaishouCloudConfig() {
|
|||||||
getDefaultKuaishouConsumeShop,
|
getDefaultKuaishouConsumeShop,
|
||||||
formatKuaishouConsumeShopOption,
|
formatKuaishouConsumeShopOption,
|
||||||
handleKuaishouConsumeShopSelected,
|
handleKuaishouConsumeShopSelected,
|
||||||
|
handleCloudSourceKeysChanged,
|
||||||
// collapse
|
// collapse
|
||||||
isCollapsed,
|
isCollapsed,
|
||||||
setCollapsed,
|
setCollapsed,
|
||||||
|
|||||||
+4
-1
@@ -42,6 +42,9 @@ export function useKuaishouCloudNinetyone(
|
|||||||
const defaultShop = config.getDefaultKuaishouConsumeShop()
|
const defaultShop = config.getDefaultKuaishouConsumeShop()
|
||||||
const shopOpts = config.getKuaishouConsumeShopOptions()
|
const shopOpts = config.getKuaishouConsumeShopOptions()
|
||||||
const shop = defaultShop || (shopOpts.length > 0 ? shopOpts[0] : null)
|
const shop = defaultShop || (shopOpts.length > 0 ? shopOpts[0] : null)
|
||||||
|
const sourceKeys = config.cloudtentaclesSourceOptions.value[0]?.key
|
||||||
|
? [config.cloudtentaclesSourceOptions.value[0].key]
|
||||||
|
: []
|
||||||
return {
|
return {
|
||||||
localId: crypto.randomUUID(),
|
localId: crypto.randomUUID(),
|
||||||
id: '',
|
id: '',
|
||||||
@@ -56,7 +59,7 @@ export function useKuaishouCloudNinetyone(
|
|||||||
externalItemId: productNo,
|
externalItemId: productNo,
|
||||||
externalSkuName: productName,
|
externalSkuName: productName,
|
||||||
resolvedSkuName: productName,
|
resolvedSkuName: productName,
|
||||||
cloudSourceKey: 'default',
|
cloudSourceKeys: sourceKeys,
|
||||||
cloudSkuId: 0,
|
cloudSkuId: 0,
|
||||||
cloudSkuName: '',
|
cloudSkuName: '',
|
||||||
vnKey: '1',
|
vnKey: '1',
|
||||||
|
|||||||
+3
-11
@@ -12,7 +12,7 @@ export function useKuaishouCloudSku(cloudtentaclesSourceOptions?: { value: { key
|
|||||||
const cloudSkuCatalogBySourceKey = ref<Record<string, AdminCloudtentaclesSkuItem[]>>({})
|
const cloudSkuCatalogBySourceKey = ref<Record<string, AdminCloudtentaclesSkuItem[]>>({})
|
||||||
|
|
||||||
function getSourceKey(item: EditableItem) {
|
function getSourceKey(item: EditableItem) {
|
||||||
return String(item.cloudSourceKey || 'default').trim()
|
return String(item.cloudSourceKeys[0] || '').trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ensureCloudSkuCatalogLoaded(item: EditableItem, force = false) {
|
async function ensureCloudSkuCatalogLoaded(item: EditableItem, force = false) {
|
||||||
@@ -149,19 +149,13 @@ export function useKuaishouCloudSku(cloudtentaclesSourceOptions?: { value: { key
|
|||||||
const count = cloudSkuCatalogBySourceKey.value[sourceKey]?.length ?? 0
|
const count = cloudSkuCatalogBySourceKey.value[sourceKey]?.length ?? 0
|
||||||
showSuccess(`cloud SKU 列表(${sourceKey})已刷新,共 ${count} 条`)
|
showSuccess(`cloud SKU 列表(${sourceKey})已刷新,共 ${count} 条`)
|
||||||
} else {
|
} else {
|
||||||
// No item provided — refresh all configured sourceKeys, or at least 'default'
|
|
||||||
const cachedKeys = Object.keys(cloudSkuCatalogBySourceKey.value)
|
const cachedKeys = Object.keys(cloudSkuCatalogBySourceKey.value)
|
||||||
const configuredKeys = cloudtentaclesSourceOptions?.value?.map((s) => s.key) || []
|
const configuredKeys = cloudtentaclesSourceOptions?.value?.map((s) => s.key) || []
|
||||||
const keysToRefresh =
|
const keysToRefresh = cachedKeys.length > 0 ? cachedKeys : configuredKeys
|
||||||
cachedKeys.length > 0
|
|
||||||
? cachedKeys
|
|
||||||
: configuredKeys.length > 0
|
|
||||||
? configuredKeys
|
|
||||||
: ['default']
|
|
||||||
|
|
||||||
for (const key of keysToRefresh) {
|
for (const key of keysToRefresh) {
|
||||||
await ensureCloudSkuCatalogLoaded(
|
await ensureCloudSkuCatalogLoaded(
|
||||||
{ cloudSourceKey: key, localId: '' } as EditableItem,
|
{ cloudSourceKeys: [key], localId: '' } as EditableItem,
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -177,8 +171,6 @@ export function useKuaishouCloudSku(cloudtentaclesSourceOptions?: { value: { key
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cloudSkuCatalogLoading = ref(false)
|
const cloudSkuCatalogLoading = ref(false)
|
||||||
// Maintain backward-compat: derive a combined loading state
|
|
||||||
// The template uses cloudSkuCatalogLoading for the refresh button spinner
|
|
||||||
function syncCloudSkuCatalogLoading() {
|
function syncCloudSkuCatalogLoading() {
|
||||||
cloudSkuCatalogLoading.value = Object.values(cloudSkuCatalogLoadingBySourceKey.value).some(
|
cloudSkuCatalogLoading.value = Object.values(cloudSkuCatalogLoadingBySourceKey.value).some(
|
||||||
Boolean,
|
Boolean,
|
||||||
|
|||||||
@@ -221,19 +221,14 @@ const emit = defineEmits<{
|
|||||||
<el-collapse-item name="cloud" title="Cloud 资源与发货">
|
<el-collapse-item name="cloud" title="Cloud 资源与发货">
|
||||||
<div class="flow-meta-list flow-meta-list--two-column">
|
<div class="flow-meta-list flow-meta-list--two-column">
|
||||||
<div>
|
<div>
|
||||||
<span>规则主账号</span><strong>{{ flow.binding.cloudSourceKey || '-' }}</strong>
|
<span>账号优先级</span>
|
||||||
|
<strong>{{
|
||||||
|
flow.binding.cloudSourceKeys?.length ? flow.binding.cloudSourceKeys.join(' / ') : '-'
|
||||||
|
}}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span>实际使用账号</span>
|
<span>实际使用账号</span>
|
||||||
<strong>{{ flow.binding.resolvedSourceKey || flow.binding.cloudSourceKey || '-' }}</strong>
|
<strong>{{ flow.binding.resolvedSourceKey || '-' }}</strong>
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span>备选账号</span>
|
|
||||||
<strong>{{
|
|
||||||
flow.binding.cloudSourceKeyFallbacks?.length
|
|
||||||
? flow.binding.cloudSourceKeyFallbacks.join(' / ')
|
|
||||||
: '-'
|
|
||||||
}}</strong>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span>cloud SKU</span>
|
<span>cloud SKU</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user