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_code(domain-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 = { 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> = { 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; }