|
|
import { hasEffectiveMothershipLogin } from "@/lib/rpa/mothership-login-context";
|
|
|
import type { RpaErrorCode } from "@/modules/rpa/errors";
|
|
|
|
|
|
type PageTextRule = {
|
|
|
patterns: RegExp[];
|
|
|
code: RpaErrorCode;
|
|
|
};
|
|
|
|
|
|
const PAGE_TEXT_RULES: PageTextRule[] = [
|
|
|
{
|
|
|
patterns: [
|
|
|
/no carriers available/i,
|
|
|
/no capacity/i,
|
|
|
/unable to find a carrier/i,
|
|
|
/无可用承运商/,
|
|
|
/暂无运力/,
|
|
|
],
|
|
|
code: "CARRIER_NO_CAPACITY",
|
|
|
},
|
|
|
{
|
|
|
patterns: [
|
|
|
/address not supported/i,
|
|
|
/zip code is not supported/i,
|
|
|
/we don't service this area/i,
|
|
|
/不支持该邮编/,
|
|
|
/地址不在服务范围/,
|
|
|
],
|
|
|
code: "ADDRESS_NOT_SUPPORTED",
|
|
|
},
|
|
|
];
|
|
|
|
|
|
export function hasMothershipCredentials(): boolean {
|
|
|
return hasEffectiveMothershipLogin();
|
|
|
}
|
|
|
|
|
|
/** Mothership 页面文案 → 业务 error_code(task-110 / task-128) */
|
|
|
export function mapPageTextToErrorCode(pageText: string): RpaErrorCode | null {
|
|
|
const normalized = pageText.trim();
|
|
|
if (!normalized) {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
for (const rule of PAGE_TEXT_RULES) {
|
|
|
if (rule.patterns.some((pattern) => pattern.test(normalized))) {
|
|
|
return rule.code;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* URL → 错误分类(v0.6)
|
|
|
* login 无凭据由 QuotePageAdapter.isLoginWithoutCredentials 处理为 STRUCT_CHANGE
|
|
|
*/
|
|
|
export function mapUrlToErrorCode(url: string): RpaErrorCode | null {
|
|
|
const lower = url.toLowerCase();
|
|
|
if (lower.includes("captcha") || lower.includes("challenge")) {
|
|
|
return "RPA_CAPTCHA";
|
|
|
}
|
|
|
if (
|
|
|
lower.includes("/login") ||
|
|
|
lower.includes("/sign-in") ||
|
|
|
lower.includes("/signin")
|
|
|
) {
|
|
|
if (hasMothershipCredentials()) {
|
|
|
// 有账密仍落登录页:优先按账密错误处理,而非笼统会话过期
|
|
|
return "PROVIDER_LOGIN_FAILED";
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
/** 入口类错误:不重试、不消耗 job 重试配额(task-128/129) */
|
|
|
export function isEntryErrorCode(code: RpaErrorCode): boolean {
|
|
|
return (
|
|
|
code === "QUOTE_ENTRY_UNAVAILABLE" ||
|
|
|
code === "ADDRESS_SUGGESTION_NOT_FOUND"
|
|
|
);
|
|
|
}
|