修复取号账号与退号账号混用:操作已有号码严格锁定取号账号,禁止静默 fallback 到其他账号
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
import { listKuaishouCloudSourceLoadStats } from '../../../repositories/kuaishou-cloud-task-state-repo.js'
|
||||
import { normalizeKuaishouCloudFlow, normalizeStringArray, type JsonObject } from './domain.js'
|
||||
import { resolvePersistedCloudtentaclesContextBySourceKeys } from './cloudtentacles-context.js'
|
||||
import { resolvePersistedCloudtentaclesContextWithFallback } from './cloudtentacles-context.js'
|
||||
|
||||
type CloudtentaclesContext = ReturnType<typeof resolvePersistedCloudtentaclesContextBySourceKeys>
|
||||
type CloudtentaclesContext = ReturnType<typeof resolvePersistedCloudtentaclesContextWithFallback>
|
||||
|
||||
type CloudtentaclesSourceSelectionDeps = {
|
||||
resolveContextBySourceKeys?: typeof resolvePersistedCloudtentaclesContextBySourceKeys
|
||||
resolveContextBySourceKeys?: typeof resolvePersistedCloudtentaclesContextWithFallback
|
||||
listSourceLoadStats?: typeof listKuaishouCloudSourceLoadStats
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ export async function selectCloudtentaclesSourceForFulfillment(
|
||||
): Promise<CloudtentaclesSourceSelection> {
|
||||
const flow = normalizeKuaishouCloudFlow(flowLike)
|
||||
const resolveContext =
|
||||
deps.resolveContextBySourceKeys || resolvePersistedCloudtentaclesContextBySourceKeys
|
||||
deps.resolveContextBySourceKeys || resolvePersistedCloudtentaclesContextWithFallback
|
||||
const listSourceLoadStats = deps.listSourceLoadStats || listKuaishouCloudSourceLoadStats
|
||||
const fixedSourceKey = String(flow.binding.resolvedSourceKey || '').trim()
|
||||
const candidates = normalizeCloudtentaclesSourceCandidates(
|
||||
|
||||
@@ -8,48 +8,89 @@ import { getCloudtentaclesSessionStateByKey } from "../../platforms/cloudtentacl
|
||||
import { normalizeStringArray } from "./domain.js";
|
||||
|
||||
/**
|
||||
* 按优先级依次尝试 cloudSourceKeys 列表中的账号,
|
||||
* 找到第一个有可用 token 的账号返回其 context。
|
||||
* 将实际使用的 resolvedSourceKey 也返回,确保后续操作(退号、发货等)
|
||||
* 使用同一个 sourceKey,防止跨账号操作导致数据不一致。
|
||||
* 严格模式:只解析列表中第一个账号(调用方均把任务实际取号账号 resolvedSourceKey 放首位),
|
||||
* 该账号不可用时直接报错,绝不静默 fallback 到其他账号——
|
||||
* 否则会用「非取号账号」去退号/取链,上游会因号码不归属当前会话而报权限不足。
|
||||
* 退号、取链、发货等「操作任务已有号码」的场景必须使用此函数。
|
||||
* 将实际使用的 resolvedSourceKey 也返回,确保后续操作使用同一个 sourceKey。
|
||||
*/
|
||||
export function resolvePersistedCloudtentaclesContextBySourceKeys(
|
||||
sourceKeys: unknown[] = []
|
||||
) {
|
||||
const candidates = normalizeStringArray(sourceKeys);
|
||||
const candidates = [...new Set(normalizeStringArray(sourceKeys))];
|
||||
|
||||
const uniqueCandidates = [...new Set(candidates)];
|
||||
if (candidates.length === 0) {
|
||||
throw createHttpError(
|
||||
"cloudtentacles 没有可用账号,请先到平台配置完成账号配置",
|
||||
{ statusCode: 409, errorCode: "kuaishou_cloud_no_source_keys" }
|
||||
);
|
||||
}
|
||||
|
||||
let lastError = null;
|
||||
const sourceKey = String(candidates[0] || "").trim();
|
||||
|
||||
for (const sourceKey of uniqueCandidates) {
|
||||
return resolveSingleCloudtentaclesAccountContext(sourceKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 宽松模式:按顺序尝试候选账号,找到第一个有可用 token 的账号。
|
||||
* 仅用于「取新号」场景(新号码归属被选中的账号,不会产生跨账号孤儿),
|
||||
* 以及 account-selector 单候选解析。严禁用于退号/取链/发货等操作已有号码的场景。
|
||||
*/
|
||||
export function resolvePersistedCloudtentaclesContextWithFallback(
|
||||
sourceKeys: unknown[] = []
|
||||
) {
|
||||
const candidates = [...new Set(normalizeStringArray(sourceKeys))];
|
||||
|
||||
let lastError: unknown = null;
|
||||
|
||||
for (const sourceKey of candidates) {
|
||||
try {
|
||||
return resolveSingleCloudtentaclesAccountContext(sourceKey);
|
||||
} catch (error) {
|
||||
lastError = error;
|
||||
}
|
||||
}
|
||||
|
||||
throw (
|
||||
lastError ||
|
||||
createHttpError(
|
||||
"所有 cloudtentacles 账号均不可用,请先到平台配置完成登录校验",
|
||||
{ statusCode: 409, errorCode: "kuaishou_cloud_all_source_keys_exhausted" }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function resolveSingleCloudtentaclesAccountContext(sourceKey: string) {
|
||||
const source = getCloudtentaclesSourceByKey(sourceKey);
|
||||
const session = getCloudtentaclesSessionStateByKey(sourceKey);
|
||||
|
||||
if (!source) continue;
|
||||
if (!source) {
|
||||
throw createHttpError(
|
||||
`cloudtentacles 账号 ${sourceKey} 不存在(取号账号已变更或被删除,无法继续操作其虚拟号)`,
|
||||
{ statusCode: 409, errorCode: "kuaishou_cloud_source_missing" }
|
||||
);
|
||||
}
|
||||
|
||||
if (source.enabled === false) {
|
||||
lastError = createHttpError(
|
||||
throw createHttpError(
|
||||
`cloudtentacles 账号 ${sourceKey} 已停用`,
|
||||
{ statusCode: 409, errorCode: "kuaishou_cloud_source_disabled" }
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!session) {
|
||||
lastError = createHttpError(
|
||||
throw createHttpError(
|
||||
`cloudtentacles 账号 ${sourceKey} 没有可用 token`,
|
||||
{ statusCode: 409, errorCode: "kuaishou_cloud_missing_cloud_token" }
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const token = String(session.token || "").trim();
|
||||
if (!token) {
|
||||
lastError = createHttpError(
|
||||
throw createHttpError(
|
||||
`cloudtentacles 账号 ${sourceKey} 没有可用 token`,
|
||||
{ statusCode: 409, errorCode: "kuaishou_cloud_missing_cloud_token" }
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -68,13 +109,4 @@ export function resolvePersistedCloudtentaclesContextBySourceKeys(
|
||||
resolvedSourceKey: sourceKey,
|
||||
accountLabel: String(source.label || sourceKey).trim() || sourceKey,
|
||||
};
|
||||
}
|
||||
|
||||
throw (
|
||||
lastError ||
|
||||
createHttpError(
|
||||
"所有 cloudtentacles 账号均不可用,请先到平台配置完成登录校验",
|
||||
{ statusCode: 409, errorCode: "kuaishou_cloud_all_source_keys_exhausted" }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user