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
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,
|
|
};
|
|
}
|