修复 lewan 虚拟号配额被占满:过期绑定自动回收、退号失败不再追加取号、取号占满增加冷却
This commit is contained in:
@@ -10,6 +10,10 @@ import {
|
||||
} from "../../platforms/cloudtentacles/virtual-number-service.js";
|
||||
import { KUAISHOU_CLOUD_FIXED_VN_KEY, type JsonObject } from "./domain.js";
|
||||
|
||||
/** 账号号码配额占满后冷却:避免领取页轮询 / open-91 交付每轮都狂打上游 */
|
||||
const APPOINT_QUOTA_COOLDOWN_MS = 60_000;
|
||||
const appointQuotaCooldowns = new Map<string, number>();
|
||||
|
||||
export function resolveKuaishouCloudBindingResources(
|
||||
flow: JsonObject,
|
||||
{ skuItems = [], knapsackItems = [] }: { skuItems?: unknown[], knapsackItems?: unknown[] } = {}
|
||||
@@ -77,11 +81,24 @@ export function resolveKuaishouCloudVnKeyCandidates(_input: JsonObject = {}) {
|
||||
export async function prepareKuaishouCloudBindResourceWithFallback(input: JsonObject = {}) {
|
||||
const { cloudContext = {}, vnKeyCandidates = [] } = input;
|
||||
const candidates = Array.isArray(vnKeyCandidates) ? vnKeyCandidates : [];
|
||||
const sourceKey = String(cloudContext.resolvedSourceKey || "").trim();
|
||||
let lastError = null;
|
||||
|
||||
for (const vnKey of candidates) {
|
||||
let vnId = 0;
|
||||
let vnPhone = "";
|
||||
const cooldownKey = `${sourceKey}|${vnKey}`;
|
||||
|
||||
const cooldownUntil = appointQuotaCooldowns.get(cooldownKey) || 0;
|
||||
if (cooldownUntil > Date.now()) {
|
||||
throw createHttpError(
|
||||
"账号虚拟号配额已满,请先退回已占用号码或稍后重试",
|
||||
{
|
||||
statusCode: 409,
|
||||
errorCode: "cloudtentacles_vn_quota_cooldown",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const appointed = await appointCloudtentaclesVirtualNumber({
|
||||
@@ -132,6 +149,10 @@ export async function prepareKuaishouCloudBindResourceWithFallback(input: JsonOb
|
||||
} catch (error) {
|
||||
lastError = error;
|
||||
|
||||
if (isAppointQuotaExhaustedError(error)) {
|
||||
appointQuotaCooldowns.set(cooldownKey, Date.now() + APPOINT_QUOTA_COOLDOWN_MS);
|
||||
}
|
||||
|
||||
if (vnId > 0) {
|
||||
try {
|
||||
await backCloudtentaclesVirtualNumber({
|
||||
@@ -229,6 +250,15 @@ function isCloudSkuLikeItem(item: unknown): item is JsonObject {
|
||||
return Number(current.id || 0) > 0;
|
||||
}
|
||||
|
||||
function isAppointQuotaExhaustedError(error: unknown) {
|
||||
const current = error && typeof error === "object" ? error as JsonObject : {};
|
||||
return (
|
||||
String(current.errorCode || current.code || "").trim() ===
|
||||
"cloudtentacles_vn_appoint_failed" &&
|
||||
String(current.message || "").trim().includes("最多同时占用")
|
||||
);
|
||||
}
|
||||
|
||||
function isRecoverableKuaishouCloudVnKeyError(error: unknown) {
|
||||
const current = error && typeof error === "object" ? error as JsonObject : {};
|
||||
const errorCode = String(current.errorCode || current.code || "").trim();
|
||||
|
||||
@@ -381,7 +381,8 @@ export async function refreshKuaishouCloudTaskBindUrl(task: TaskRow, options: Js
|
||||
const taskContext = parseTaskContext(task)
|
||||
const flow = normalizeKuaishouCloudFlow(taskContext.kuaishouCloudFulfillment)
|
||||
const source = String(options.source || 'system').trim() || 'system'
|
||||
const allowSkipBack = options.allowSkipBack !== false
|
||||
// 默认退号失败即中止:避免旧号没退掉又占新号,导致账号号码配额被净泄漏
|
||||
const allowSkipBack = options.allowSkipBack === true
|
||||
const preferReuseVn = options.preferReuseVn !== false
|
||||
|
||||
if (isKuaishouCloudBindingMutationFrozen(task, flow)) {
|
||||
|
||||
Reference in New Issue
Block a user