276 lines
9.1 KiB
TypeScript
276 lines
9.1 KiB
TypeScript
import { resolveCloudtentaclesConfig } from "../../platforms/cloudtentacles/helpers.js";
|
|
import {
|
|
maskCode as maskCodeValue,
|
|
maskPhone as maskPhoneValue,
|
|
} from "../../../utils/masking.js";
|
|
|
|
export const KUAISHOU_CLOUD_FIXED_VN_KEY = "1";
|
|
|
|
export type JsonObject = Record<string, any>;
|
|
|
|
export function isKuaishouCloudTask(task: unknown) {
|
|
const source = task && typeof task === "object" ? task as JsonObject : {};
|
|
return String(source.executor_key || "").trim() === "kuaishou_ct_assisted";
|
|
}
|
|
|
|
export function normalizeKuaishouCloudFlow(value: unknown) {
|
|
const source: JsonObject = value && typeof value === "object" ? value as JsonObject : {};
|
|
const binding =
|
|
source.binding && typeof source.binding === "object" ? source.binding : {};
|
|
const role =
|
|
source.role && typeof source.role === "object" ? source.role : {};
|
|
const purchase =
|
|
source.purchase && typeof source.purchase === "object"
|
|
? source.purchase
|
|
: {};
|
|
const dispatch =
|
|
source.dispatch && typeof source.dispatch === "object"
|
|
? source.dispatch
|
|
: {};
|
|
const returnNumber =
|
|
source.returnNumber && typeof source.returnNumber === "object"
|
|
? source.returnNumber
|
|
: {};
|
|
const consume =
|
|
source.consume && typeof source.consume === "object" ? source.consume : {};
|
|
const ticket =
|
|
source.ticket && typeof source.ticket === "object" ? source.ticket : {};
|
|
const deliveryItems = normalizeDeliveryItems(source, binding);
|
|
|
|
const roleName = String(role.name || binding.roleName || "").trim();
|
|
const roleId = String(role.rid || binding.roleId || "").trim();
|
|
const bindPreparedAt = binding.bindPreparedAt || null;
|
|
const bindExpiresAt =
|
|
binding.bindExpiresAt ||
|
|
resolveKuaishouCloudBindUrlExpiresAt(bindPreparedAt);
|
|
|
|
return {
|
|
...source,
|
|
configId: String(source.configId || "").trim(),
|
|
internalSkuCode: String(source.internalSkuCode || "").trim(),
|
|
internalSkuName: String(source.internalSkuName || "").trim(),
|
|
deliveryItems,
|
|
ticket: {
|
|
code: String(ticket.code || "").trim(),
|
|
status: String(ticket.status || "pending").trim() || "pending",
|
|
capturedAt: ticket.capturedAt || null,
|
|
capturedBy: ticket.capturedBy || null,
|
|
verifiedAt: ticket.verifiedAt || null,
|
|
oid: String(ticket.oid || "").trim(),
|
|
formToken: String(ticket.formToken || "").trim(),
|
|
leftCount: Number(ticket.leftCount || 0) || 0,
|
|
goodsTitle: String(ticket.goodsTitle || "").trim(),
|
|
},
|
|
binding: {
|
|
prepareStatus:
|
|
String(binding.prepareStatus || "pending").trim() || "pending",
|
|
cloudSourceKeys: normalizeStringArray(binding.cloudSourceKeys),
|
|
resolvedSourceKey: String(binding.resolvedSourceKey || "").trim(),
|
|
skuId: Number(binding.skuId || 0) || 0,
|
|
skuName: String(binding.skuName || "").trim(),
|
|
vnKey:
|
|
String(binding.vnKey || KUAISHOU_CLOUD_FIXED_VN_KEY).trim() ||
|
|
KUAISHOU_CLOUD_FIXED_VN_KEY,
|
|
vnId: Number(binding.vnId || 0) || 0,
|
|
vnPhone: String(binding.vnPhone || "").trim(),
|
|
bindUrl: String(binding.bindUrl || "").trim(),
|
|
bindPreparedAt,
|
|
bindExpiresAt,
|
|
bindProbeAt: binding.bindProbeAt || null,
|
|
bindProbeStatus: String(binding.bindProbeStatus || "").trim(),
|
|
bindProbeMessage: String(binding.bindProbeMessage || "").trim(),
|
|
roleName,
|
|
roleId,
|
|
},
|
|
role: {
|
|
status:
|
|
String(
|
|
role.status || (roleName || roleId ? "ready" : "pending")
|
|
).trim() || "pending",
|
|
name: roleName,
|
|
rid: roleId,
|
|
refreshedAt: role.refreshedAt || null,
|
|
errorMessage: String(role.errorMessage || "").trim(),
|
|
rawInfo:
|
|
role.rawInfo && typeof role.rawInfo === "object" ? role.rawInfo : null,
|
|
},
|
|
purchase: {
|
|
autoBuyEnabled: purchase.autoBuyEnabled !== false,
|
|
minAssetReserve: Number(purchase.minAssetReserve || 0) || 0,
|
|
usedKnapsack: purchase.usedKnapsack === true,
|
|
purchaseTriggered: purchase.purchaseTriggered === true,
|
|
assetBefore: Number(purchase.assetBefore || 0) || 0,
|
|
assetAfter: Number(purchase.assetAfter || 0) || 0,
|
|
purchaseAt: purchase.purchaseAt || null,
|
|
items: Array.isArray(purchase.items) ? purchase.items : [],
|
|
},
|
|
dispatch: {
|
|
status: String(dispatch.status || "pending").trim() || "pending",
|
|
dispatchAt: dispatch.dispatchAt || null,
|
|
dispatchBy: dispatch.dispatchBy || null,
|
|
sendType: Number(dispatch.sendType || 0) || 0,
|
|
note: String(dispatch.note || "").trim(),
|
|
items: Array.isArray(dispatch.items) ? dispatch.items : [],
|
|
},
|
|
returnNumber: {
|
|
status: String(returnNumber.status || "pending").trim() || "pending",
|
|
returnedAt: returnNumber.returnedAt || null,
|
|
returnedBy: returnNumber.returnedBy || null,
|
|
autoReturnEnabled: returnNumber.autoReturnEnabled === true,
|
|
},
|
|
consume: {
|
|
status: String(consume.status || "pending").trim() || "pending",
|
|
shopId: String(consume.shopId || "").trim(),
|
|
shopName: String(consume.shopName || "").trim(),
|
|
autoConsumeEnabled: consume.autoConsumeEnabled === true,
|
|
consumedAt: consume.consumedAt || null,
|
|
errorMessage: String(consume.errorMessage || "").trim(),
|
|
},
|
|
notes: String(source.notes || "").trim(),
|
|
};
|
|
}
|
|
|
|
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) {
|
|
const rawInfo: JsonObject | null = value && typeof value === "object" ? value as JsonObject : null;
|
|
const nestedBindInfo =
|
|
rawInfo?.sBindInfo && typeof rawInfo.sBindInfo === "object"
|
|
? rawInfo.sBindInfo
|
|
: null;
|
|
const source = nestedBindInfo || rawInfo;
|
|
|
|
return {
|
|
name: String(
|
|
source?.name ||
|
|
source?.roleName ||
|
|
source?.nickname ||
|
|
source?.sRoleName ||
|
|
""
|
|
).trim(),
|
|
rid: String(
|
|
source?.rid ||
|
|
source?.roleId ||
|
|
source?.uid ||
|
|
source?.sRoleId ||
|
|
source?.sUserId ||
|
|
""
|
|
).trim(),
|
|
rawInfo,
|
|
};
|
|
}
|
|
|
|
export function maskPhone(value: unknown) {
|
|
return maskPhoneValue(value);
|
|
}
|
|
|
|
export function maskCode(value: unknown) {
|
|
return maskCodeValue(value);
|
|
}
|
|
|
|
export function resolveKuaishouCloudBindUrlExpiresAt(preparedAt: unknown) {
|
|
const preparedTime = Date.parse(String(preparedAt || ""));
|
|
if (!Number.isFinite(preparedTime)) {
|
|
return null;
|
|
}
|
|
|
|
const config = resolveCloudtentaclesConfig();
|
|
const ttlSeconds = Number(config.bindUrlTtlSeconds || 600);
|
|
return new Date(preparedTime + Math.max(1, ttlSeconds) * 1000).toISOString();
|
|
}
|
|
|
|
export function isKuaishouCloudBindUrlFresh(flow: unknown, now = new Date()) {
|
|
const normalizedFlow = normalizeKuaishouCloudFlow(flow);
|
|
if (!normalizedFlow.binding.bindUrl) {
|
|
return false;
|
|
}
|
|
|
|
const expiresAt = normalizedFlow.binding.bindExpiresAt;
|
|
if (!expiresAt) {
|
|
return false;
|
|
}
|
|
|
|
const expiresTime = Date.parse(String(expiresAt || ""));
|
|
if (!Number.isFinite(expiresTime)) {
|
|
return false;
|
|
}
|
|
|
|
return expiresTime > now.getTime();
|
|
}
|
|
|
|
export function normalizeStringArray(value: unknown) {
|
|
if (Array.isArray(value)) {
|
|
return value.map((v) => String(v || "").trim()).filter(Boolean);
|
|
}
|
|
if (typeof value === "string") {
|
|
return value
|
|
.split(",")
|
|
.map((v) => v.trim())
|
|
.filter(Boolean);
|
|
}
|
|
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());
|
|
}
|