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.

104 lines
2.5 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 type { QuoteContractRates } from "@/workers/rpa/quote-capture/quote-contract-guard";
/** 归一化后的报价 rates契约校验唯一输入 */
export type CanonicalQuote = {
rates: QuoteContractRates;
/** 解析来源路径debug */
sourcePath: string;
};
function isRatesObject(value: unknown): value is QuoteContractRates {
return value != null && typeof value === "object" && !Array.isArray(value);
}
function hasRatesBranchShape(rates: QuoteContractRates): boolean {
return Object.keys(rates).some((key) => {
if (key === "fastest") {
return true;
}
const val = rates[key];
return val != null && typeof val === "object" && !Array.isArray(val);
});
}
function readPath(obj: unknown, ...keys: string[]): unknown {
let cur = obj;
for (const key of keys) {
if (cur == null || typeof cur !== "object" || Array.isArray(cur)) {
return undefined;
}
cur = (cur as Record<string, unknown>)[key];
}
return cur;
}
type RatesCandidate = { rates: QuoteContractRates; sourcePath: string };
function tryRatesAt(
rates: unknown,
sourcePath: string,
): RatesCandidate | null {
if (!isRatesObject(rates) || !hasRatesBranchShape(rates)) {
return null;
}
return { rates, sourcePath };
}
/**
* 从 API body 解析 rates支持常见嵌套变体
* body.rates | body.data.rates | body.result.rates | array[0].rates
*/
export function resolveRatesFromBody(body: unknown): RatesCandidate | null {
if (body == null) {
return null;
}
if (Array.isArray(body)) {
if (body.length > 0) {
const fromFirst = tryRatesAt(
readPath(body[0], "rates"),
"[0].rates",
);
if (fromFirst) {
return fromFirst;
}
}
return null;
}
if (typeof body !== "object") {
return null;
}
const candidates: RatesCandidate[] = [];
const direct = tryRatesAt(readPath(body, "rates"), "rates");
if (direct) {
candidates.push(direct);
}
const fromData = tryRatesAt(readPath(body, "data", "rates"), "data.rates");
if (fromData) {
candidates.push(fromData);
}
const fromResult = tryRatesAt(
readPath(body, "result", "rates"),
"result.rates",
);
if (fromResult) {
candidates.push(fromResult);
}
return candidates[0] ?? null;
}
/** body → CanonicalQuote无法解析 rates 时返回 null */
export function normalizeQuote(body: unknown): CanonicalQuote | null {
const resolved = resolveRatesFromBody(body);
if (!resolved) {
return null;
}
return {
rates: resolved.rates,
sourcePath: resolved.sourcePath,
};
}