/** * Flock Freight 登录态选项组合约束 * 依据:官网表单校验(仅冷藏须温控)、调度重量锁、批量探测易无价组合 */ import { FLOCK_LIMITS } from "@/lib/constants/flock-limits"; export type FlockCompatIssue = { severity: "block" | "warn"; code: string; message: string; }; /** 探测与官网实测:易无报价的地点类型(仍可选,但提示) */ export const FLOCK_LOCATION_QUOTE_RISK: Readonly> = { limited_construction: "「施工工地」地点类型询价常无结果,建议改选「商业地址(无月台)」并勾选尾板/托盘车", limited_farm: "「农场 / 种养殖地」询价常无结果,建议改选「住宅」或「商业地址(无月台)」", limited_military: "「军事基地」等受限通道常需通行证,询价易失败,请确认承运商可进出后再选", limited_airport: "「机场」受限通道询价易失败或需额外手续,建议优先商业地址", limited_port: "「港口码头」受限通道询价易失败或需额外手续,建议优先商业地址", }; /** 易无价包装 */ export const FLOCK_PACKAGING_QUOTE_RISK: Readonly> = { units: "「单件 / 整机」包装询价常无结果,建议改用纸箱、木箱或标准托盘", pails: "「小桶 / 提桶」单独作为主包装时询价不稳定,建议改用纸箱或托盘", }; export function isFlockReeferOnlyWithoutTemp(input: { vehicleTypes: readonly string[]; additionalServices: readonly string[]; }): boolean { const vehicles = input.vehicleTypes.map((v) => v.trim()).filter(Boolean); if (vehicles.length === 0) return false; const onlyReefer = vehicles.every((v) => v === "refrigerated"); if (!onlyReefer) return false; return !input.additionalServices.includes("temperature_control"); } export function isFlockTempWithoutReefer(input: { vehicleTypes: readonly string[]; additionalServices: readonly string[]; }): boolean { if (!input.additionalServices.includes("temperature_control")) return false; return !input.vehicleTypes.includes("refrigerated"); } export function isFlockSprinterOnly( vehicleTypes: readonly string[], ): boolean { const vehicles = vehicleTypes.map((v) => v.trim()).filter(Boolean); return vehicles.length > 0 && vehicles.every((v) => v === "sprinter"); } export function evaluateFlockOptionCompat(input: { pickupLocationType: string; deliveryLocationType: string; packagingTypes: readonly string[]; vehicleTypes: readonly string[]; additionalServices: readonly string[]; shipmentWeightLb?: number; }): FlockCompatIssue[] { const issues: FlockCompatIssue[] = []; if ( isFlockReeferOnlyWithoutTemp({ vehicleTypes: input.vehicleTypes, additionalServices: input.additionalServices, }) ) { issues.push({ severity: "block", code: "flock_reefer_needs_temp", message: "仅允许「冷藏车」时须同时勾选附加服务「温控 / 冷链」,否则官网无法提交询价", }); } if ( isFlockTempWithoutReefer({ vehicleTypes: input.vehicleTypes, additionalServices: input.additionalServices, }) ) { issues.push({ severity: "block", code: "flock_temp_needs_reefer", message: "已勾选「温控 / 冷链」时,允许车型中须包含「冷藏车」", }); } if (isFlockSprinterOnly(input.vehicleTypes)) { issues.push({ severity: "warn", code: "flock_sprinter_only", message: "仅选「小型厢车」时易无报价,建议同时勾选厢式货车或干货挂车", }); } for (const side of ["pickup", "delivery"] as const) { const typeId = side === "pickup" ? input.pickupLocationType : input.deliveryLocationType; const risk = FLOCK_LOCATION_QUOTE_RISK[typeId]; if (risk) { issues.push({ severity: "warn", code: `flock_loc_risk_${side}_${typeId}`, message: `${side === "pickup" ? "提货" : "派送"}:${risk}`, }); } } for (const pack of input.packagingTypes) { const risk = FLOCK_PACKAGING_QUOTE_RISK[pack]; if (risk) { issues.push({ severity: "warn", code: `flock_pack_risk_${pack}`, message: risk, }); } } const wt = input.shipmentWeightLb; if ( typeof wt === "number" && Number.isFinite(wt) && wt > 0 && wt <= FLOCK_LIMITS.schedulingWeightMinLb ) { issues.push({ severity: "warn", code: "flock_scheduling_weight", message: `整票总重不超过 ${FLOCK_LIMITS.schedulingWeightMinLb} lb 时,部分预约/时间窗调度选项不可用(官网灰色)`, }); } return issues; } export function flockOptionHasBlock( issues: readonly FlockCompatIssue[], ): boolean { return issues.some((i) => i.severity === "block"); }