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.

116 lines
3.0 KiB

import type { QuoteRecord } from "@prisma/client";
import { getConfidenceScore } from "@/modules/quote/confidence";
import { formatQuoteErrorMessage } from "@/modules/quote/quote-error-messages";
export type QuoteDetailResponse = {
quote_id: string;
request_id: string;
status: "processing" | "done" | "failed" | "expired";
valid_until?: string;
source_type?: "rpa" | "cache" | "stale";
is_realtime?: boolean;
confidence_score?: number;
currency: string;
error_code?: string;
error_message?: string;
quotes?: unknown[];
};
export type QuoteSummaryItem = {
quote_id: string;
request_id: string;
status: string;
source_type: string | null;
is_realtime: boolean;
created_at: string;
valid_until: string | null;
pallet_count: number;
cargo_type: string;
error_code: string | null;
};
function resolveStatus(record: QuoteRecord): QuoteDetailResponse["status"] {
if (
record.status === "done" &&
record.validUntil &&
record.validUntil.getTime() < Date.now()
) {
return "expired";
}
return record.status as QuoteDetailResponse["status"];
}
/** 将 QuoteRecord 格式化为 GET /quotes/{id} 响应 */
export function serializeQuoteDetail(record: QuoteRecord): QuoteDetailResponse {
const status = resolveStatus(record);
const sourceType = record.sourceType as
| "rpa"
| "cache"
| "stale"
| null
| undefined;
const base: QuoteDetailResponse = {
quote_id: record.quoteId,
request_id: record.requestId,
status,
currency: record.currency,
};
if (status === "processing") {
return base;
}
if (status === "failed") {
const errorCode = record.errorCode ?? "QUOTE_UNAVAILABLE";
return {
...base,
error_code: errorCode,
error_message: formatQuoteErrorMessage(errorCode, record.errorMessage),
is_realtime: record.isRealtime,
};
}
if (status === "expired") {
return {
...base,
valid_until: record.validUntil?.toISOString(),
source_type: sourceType ?? undefined,
is_realtime: record.isRealtime,
};
}
const confidence =
record.confidenceScore != null
? Number(record.confidenceScore)
: sourceType
? getConfidenceScore(sourceType)
: 0.95;
return {
...base,
valid_until: record.validUntil?.toISOString(),
source_type: sourceType ?? undefined,
is_realtime: record.isRealtime,
confidence_score: confidence,
quotes: (record.quotesJson as unknown[]) ?? [],
};
}
/** 历史列表摘要 */
export function serializeQuoteSummary(record: QuoteRecord): QuoteSummaryItem {
const status = resolveStatus(record);
return {
quote_id: record.quoteId,
request_id: record.requestId,
status,
source_type: record.sourceType,
is_realtime: record.isRealtime,
created_at: record.createdAt.toISOString(),
valid_until: record.validUntil?.toISOString() ?? null,
pallet_count: record.palletCount,
cargo_type: record.cargoType,
error_code: record.errorCode,
};
}