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.

63 lines
1.9 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 { 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();
// 同承运商不同价(官网多张 rate-card)必须保留,不能挤成一档
return `${item.serviceLevel}/${item.rateOption}/${carrier}/${item.rawTotal}`;
}
/** 保留 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;
}