You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

79 lines
3.1 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import { QUOTE_TIMEOUT_MS } from "@/lib/constants/quote";
import type { RpaErrorCode } from "@/modules/rpa/errors";
import { RpaError } from "@/modules/rpa/errors";
const QUOTE_TIMEOUT_SEC = Math.round(QUOTE_TIMEOUT_MS / 1_000);
/** 对外展示的询价失败 error_codedomain-rules §10 + RPA 细分) */
export type QuoteFailureCode =
| "QUOTE_UNAVAILABLE"
| "QUOTE_TIMEOUT"
| "QUOTE_ENTRY_UNAVAILABLE"
| "ADDRESS_SUGGESTION_NOT_FOUND"
| "CARRIER_NO_CAPACITY"
| "ADDRESS_NOT_SUPPORTED"
| "RPA_CAPTCHA"
| "SESSION_EXPIRED"
| "RPA_DATA_INVALID"
| "PAGE_LOAD_TIMEOUT";
const DEFAULT_MESSAGES: Record<QuoteFailureCode, string> = {
QUOTE_UNAVAILABLE: "暂时无法获取报价,请稍后重试",
QUOTE_TIMEOUT: `查询超时(超过 ${QUOTE_TIMEOUT_SEC} 秒),请重试`,
QUOTE_ENTRY_UNAVAILABLE:
"MotherShip 报价入口打不开(网络超时或被拦截)。请检查 VPN、将 RPA_HEADLESS=false 后重录 .rpa/mothership-storage.json或增大 RPA_PAGE_GOTO_TIMEOUT_MS",
ADDRESS_SUGGESTION_NOT_FOUND:
"无法在 MotherShip 页面匹配您已确认的地址,请重新选择提货/派送地址",
CARRIER_NO_CAPACITY: "该线路暂无可用运力,请调整货物或地址后重试",
ADDRESS_NOT_SUPPORTED: "该地址不在 MotherShip 服务范围内",
RPA_CAPTCHA: "MotherShip 触发验证码RPA 已暂停,请稍后重试或联系管理员",
SESSION_EXPIRED: "MotherShip 登录会话已过期,请配置凭据后重试",
RPA_DATA_INVALID: "未能从 MotherShip 页面解析完整四档报价,请稍后重试",
PAGE_LOAD_TIMEOUT: "MotherShip 页面加载超时,请稍后重试",
};
const RPA_CODE_MAP: Partial<Record<RpaErrorCode, QuoteFailureCode>> = {
QUOTE_ENTRY_UNAVAILABLE: "QUOTE_ENTRY_UNAVAILABLE",
ADDRESS_SUGGESTION_NOT_FOUND: "ADDRESS_SUGGESTION_NOT_FOUND",
CARRIER_NO_CAPACITY: "CARRIER_NO_CAPACITY",
ADDRESS_NOT_SUPPORTED: "ADDRESS_NOT_SUPPORTED",
RPA_CAPTCHA: "RPA_CAPTCHA",
SESSION_EXPIRED: "SESSION_EXPIRED",
RPA_DATA_INVALID: "RPA_DATA_INVALID",
PAGE_LOAD_TIMEOUT: "PAGE_LOAD_TIMEOUT",
STRUCT_CHANGE: "QUOTE_ENTRY_UNAVAILABLE",
};
export function resolveQuoteFailureCode(error: unknown): QuoteFailureCode {
if (error instanceof RpaError) {
return RPA_CODE_MAP[error.code] ?? "QUOTE_UNAVAILABLE";
}
return "QUOTE_UNAVAILABLE";
}
export function resolveQuoteFailureMessage(
error: unknown,
errorCode: QuoteFailureCode,
): string {
if (error instanceof RpaError && error.message.trim()) {
return error.message.trim();
}
if (error instanceof Error && error.message.trim()) {
return error.message.trim();
}
return DEFAULT_MESSAGES[errorCode];
}
/** GET /quotes/{id} 展示文案:优先库内明细,否则按 error_code 映射 */
export function formatQuoteErrorMessage(
errorCode: string | null | undefined,
storedMessage?: string | null,
): string {
const trimmed = storedMessage?.trim();
if (trimmed) {
return trimmed;
}
const code = (errorCode ?? "QUOTE_UNAVAILABLE") as QuoteFailureCode;
return DEFAULT_MESSAGES[code] ?? DEFAULT_MESSAGES.QUOTE_UNAVAILABLE;
}