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.

62 lines
1.8 KiB

import { compareUiTierSortOrder } from "@/lib/constants/quote";
import type { QuoteItem } from "@/modules/providers/quote-provider";
import { RpaError } from "@/modules/rpa/errors";
function tierKey(item: QuoteItem): string {
const carrier = (item.carrier ?? "").trim().toLowerCase();
return `${item.serviceLevel}/${item.rateOption}/${carrier}`;
}
/** 保留 MotherShip 实际返回的全部 UI 档位;同档不同承运商不去重挤掉 */
export function normalizeQuoteItems(items: QuoteItem[]): QuoteItem[] {
const map = new Map<string, QuoteItem>();
for (const item of items) {
map.set(tierKey(item), item);
}
const normalized = [...map.values()].sort((a, b) => {
const byTier = compareUiTierSortOrder(
a.serviceLevel,
a.rateOption,
b.serviceLevel,
b.rateOption,
);
if (byTier !== 0) return byTier;
return (a.carrier ?? "").localeCompare(b.carrier ?? "");
});
if (normalized.length === 0) {
throw new RpaError(
"RPA_DATA_INVALID",
"未解析到有效报价档位",
{ retryable: false },
);
}
return normalized;
}
/** 网络层报价严格校验(唯一验收入口) */
export function validateQuoteSchema(items: QuoteItem[]): QuoteItem[] {
const normalized = normalizeQuoteItems(items);
for (const tier of normalized) {
if (tier.rawFreight <= 0) {
throw new RpaError(
"RPA_DATA_INVALID",
`档位 ${tier.serviceLevel}/${tier.rateOption} 运费无效`,
{ retryable: false },
);
}
if (!tier.transitDays?.trim() || tier.transitDays === "—") {
throw new RpaError(
"RPA_DATA_INVALID",
`档位 ${tier.serviceLevel}/${tier.rateOption} 时效为空`,
{ retryable: false },
);
}
}
return normalized;
}