/** * axel/quote 响应契约解析(阶段0 录证专用,与 RPA normalize 解耦) * * 真实结构(2026-06 录证): * body.data.rates.standard.{ lowest, fastest, bestValue } * body.data.rates.guaranteed.{ lowest, fastest, bestValue? } * body.data.availableRates.{ rate_: RateObject } */ export type AxelRateTier = { id?: string; price?: number; finalPrice?: number; baseQuotePrice?: number; days?: number; serviceLevel?: string; serviceType?: string; quoteType?: string; serviceCommitment?: string; serviceCommitmentDescription?: string; }; export type AxelQuoteContract = { sourcePath: string; dataRates: { standard: Record; guaranteed: Record; } | null; availableRateCount: number; /** 四档映射:standard/guaranteed × lowest/fastest */ fourTierCoverage: { standardLowest: boolean; standardFastest: boolean; guaranteedLowest: boolean; guaranteedFastest: boolean; }; pricedLeafCount: number; }; function readData(body: unknown): Record | null { if (body == null || typeof body !== "object" || Array.isArray(body)) { return null; } const data = (body as Record).data; if (data == null || typeof data !== "object" || Array.isArray(data)) { return null; } return data as Record; } function asTierMap(value: unknown): Record | null { if (value == null || typeof value !== "object" || Array.isArray(value)) { return null; } return value as Record; } function hasPositivePrice(tier: AxelRateTier): boolean { for (const key of ["price", "finalPrice", "baseQuotePrice"] as const) { const val = tier[key]; if (typeof val === "number" && val > 0) { return true; } } return false; } function countPricedLeavesInAvailableRates( availableRates: unknown, ): number { if ( availableRates == null || typeof availableRates !== "object" || Array.isArray(availableRates) ) { return 0; } return Object.values(availableRates as Record).filter( (r) => hasPositivePrice(r), ).length; } function pickTier( branch: Record | null | undefined, name: string, ): AxelRateTier | null { if (!branch) { return null; } const tier = branch[name]; return tier && hasPositivePrice(tier) ? tier : null; } /** 从 axel/quote body 提取契约摘要 */ export function extractAxelQuoteContract(body: unknown): AxelQuoteContract { const data = readData(body); const dataRatesRaw = data?.rates; let dataRates: AxelQuoteContract["dataRates"] = null; if (dataRatesRaw && typeof dataRatesRaw === "object" && !Array.isArray(dataRatesRaw)) { const dr = dataRatesRaw as Record; dataRates = { standard: asTierMap(dr.standard) ?? {}, guaranteed: asTierMap(dr.guaranteed) ?? {}, }; } const availableRateCount = countPricedLeavesInAvailableRates( data?.availableRates, ); const std = dataRates?.standard; const guar = dataRates?.guaranteed; const fourTierCoverage = { standardLowest: pickTier(std, "lowest") != null, standardFastest: pickTier(std, "fastest") != null, guaranteedLowest: pickTier(guar, "lowest") != null || pickTier(guar, "bestValue") != null, guaranteedFastest: pickTier(guar, "fastest") != null || pickTier(guar, "bestValue") != null, }; let pricedLeafCount = 0; if (std) { pricedLeafCount += Object.values(std).filter(hasPositivePrice).length; } if (guar) { pricedLeafCount += Object.values(guar).filter(hasPositivePrice).length; } return { sourcePath: "data.rates", dataRates, availableRateCount, fourTierCoverage, pricedLeafCount, }; } /** 阶段0 A3 验收:四档齐全或 data.rates 定价叶子 ≥ 4 */ export function countAxelQuoteTiers(body: unknown): { pricedLeafCount: number; fourTierOk: boolean; availableRateCount: number; } { const c = extractAxelQuoteContract(body); const fourTierOk = c.fourTierCoverage.standardLowest && c.fourTierCoverage.standardFastest && c.fourTierCoverage.guaranteedLowest && c.fourTierCoverage.guaranteedFastest; return { pricedLeafCount: Math.max(c.pricedLeafCount, c.availableRateCount), fourTierOk, availableRateCount: c.availableRateCount, }; }