|
|
/** 前端类型定义 — 与 PRD / API 契约一致 */
|
|
|
|
|
|
import type { Priority1DisplayLine } from "@/modules/priority1/quote-storage";
|
|
|
|
|
|
export type QuotePageStatus =
|
|
|
| "idle"
|
|
|
| "resolving_address"
|
|
|
| "validating"
|
|
|
| "processing"
|
|
|
| "success"
|
|
|
| "fallback"
|
|
|
| "manual_followup"
|
|
|
| "error"
|
|
|
| "expired";
|
|
|
|
|
|
export type DataPageStatus = "loading" | "success" | "empty" | "error";
|
|
|
|
|
|
/** MotherShip 已知 Tab;API 可能出现未注册的新 service_level */
|
|
|
export type KnownServiceLevel = "standard" | "guaranteed" | "dedicated";
|
|
|
export type ServiceLevel = KnownServiceLevel | (string & {});
|
|
|
export type RateOption = "lowest" | "fastest" | "bestValue" | (string & {});
|
|
|
export type UnitWeight = "lb" | "kg";
|
|
|
export type UnitDim = "in" | "cm";
|
|
|
export type DisplayUnit = "imperial" | "metric";
|
|
|
|
|
|
export type CargoType =
|
|
|
| "general_freight"
|
|
|
| "machinery"
|
|
|
| "furniture"
|
|
|
| "electronics"
|
|
|
| "building_materials"
|
|
|
| "auto_parts"
|
|
|
| "food_nonperishable"
|
|
|
| "apparel"
|
|
|
| "other";
|
|
|
|
|
|
export interface AddressInput {
|
|
|
street: string;
|
|
|
city: string;
|
|
|
state: string;
|
|
|
zip: string;
|
|
|
place_id: string;
|
|
|
formatted_address: string;
|
|
|
selected_from_suggestions: boolean;
|
|
|
mothership_option_id?: string;
|
|
|
mothership_display_label?: string;
|
|
|
selected_from_mothership?: boolean;
|
|
|
}
|
|
|
|
|
|
export interface MothershipAddressCandidate {
|
|
|
option_id: string;
|
|
|
display_label: string;
|
|
|
formatted_address: string;
|
|
|
street: string;
|
|
|
city: string;
|
|
|
state: string;
|
|
|
zip: string;
|
|
|
selectable?: boolean;
|
|
|
unavailable_reason?: string;
|
|
|
}
|
|
|
|
|
|
export interface MothershipCandidatesResult {
|
|
|
pickup_candidates: MothershipAddressCandidate[];
|
|
|
delivery_candidates: MothershipAddressCandidate[];
|
|
|
requires_selection: boolean;
|
|
|
quote_session_id?: string;
|
|
|
}
|
|
|
|
|
|
export interface QuoteRequestBody {
|
|
|
request_id: string;
|
|
|
customer_id: string;
|
|
|
pickup_address: AddressInput;
|
|
|
delivery_address: AddressInput;
|
|
|
weight: { value: number; unit: UnitWeight };
|
|
|
dimensions: {
|
|
|
length: number;
|
|
|
width: number;
|
|
|
height: number;
|
|
|
unit: UnitDim;
|
|
|
};
|
|
|
pallet_count: number;
|
|
|
cargo_type: CargoType;
|
|
|
service_level?: ServiceLevel;
|
|
|
rate_option?: RateOption;
|
|
|
display_unit?: DisplayUnit;
|
|
|
quote_session_id?: string;
|
|
|
}
|
|
|
|
|
|
export type QuoteStatus = "processing" | "done" | "failed" | "expired";
|
|
|
export type SourceType = "rpa" | "cache" | "stale";
|
|
|
|
|
|
export interface QuoteItem {
|
|
|
service_level: ServiceLevel;
|
|
|
rate_option: RateOption;
|
|
|
carrier: string;
|
|
|
transit_days: string;
|
|
|
transit_description: string;
|
|
|
raw_freight: number;
|
|
|
surcharges: number;
|
|
|
raw_total: number;
|
|
|
markup_percent: number;
|
|
|
markup_amount: number;
|
|
|
final_total: number;
|
|
|
breakdown: Array<{ item: string; amount: number }>;
|
|
|
}
|
|
|
|
|
|
export type Priority1QuoteLineDetail = Priority1DisplayLine;
|
|
|
|
|
|
export interface Priority1QuotePayload {
|
|
|
display_mode: "ltl_cards" | "ftl_calendar";
|
|
|
lines: Priority1DisplayLine[];
|
|
|
shipment_meta?: {
|
|
|
shipment_type?: string;
|
|
|
pickup_date?: string;
|
|
|
commodity?: string;
|
|
|
origin_zip?: string;
|
|
|
destination_zip?: string;
|
|
|
weight_lb?: number;
|
|
|
};
|
|
|
}
|
|
|
|
|
|
export interface QuoteDetail {
|
|
|
quote_id: string;
|
|
|
request_id: string;
|
|
|
status: QuoteStatus;
|
|
|
valid_until?: string | null;
|
|
|
source_type?: SourceType | null;
|
|
|
is_realtime?: boolean;
|
|
|
confidence_score?: number | null;
|
|
|
currency: string;
|
|
|
quotes?: QuoteItem[];
|
|
|
priority1?: Priority1QuotePayload;
|
|
|
error_code?: string | null;
|
|
|
error_message?: string | null;
|
|
|
}
|
|
|
|
|
|
export interface CreateQuoteResult {
|
|
|
quote_id: string;
|
|
|
status: "processing" | "done";
|
|
|
}
|
|
|
|
|
|
/** Priority1 模拟填表提交体 */
|
|
|
export interface Priority1QuoteRequestBody {
|
|
|
request_id: string;
|
|
|
customer_id: string;
|
|
|
priority1_input: Record<string, unknown>;
|
|
|
}
|
|
|
|
|
|
export type HostCustomerStatus = "active" | "disabled";
|
|
|
|
|
|
export type ServiceApiKeySummary = {
|
|
|
key_id: string;
|
|
|
key_prefix: string;
|
|
|
is_active: boolean;
|
|
|
permissions: string[];
|
|
|
created_at: string;
|
|
|
};
|
|
|
|
|
|
export type HostCustomerRecord = {
|
|
|
customer_id: string;
|
|
|
name: string;
|
|
|
status: HostCustomerStatus;
|
|
|
remark: string | null;
|
|
|
embed_password_set: boolean;
|
|
|
created_at: string;
|
|
|
updated_at: string;
|
|
|
api_keys: ServiceApiKeySummary[];
|
|
|
};
|
|
|
|
|
|
export type CreateHostCustomerResponse = HostCustomerRecord & {
|
|
|
api_key: string;
|
|
|
};
|
|
|
|
|
|
export type MarkupType = "percent" | "fixed";
|
|
|
|
|
|
export interface MarkupConfig {
|
|
|
customer_id: string;
|
|
|
markup_type: MarkupType;
|
|
|
markup_percent: number;
|
|
|
markup_fixed_amount: number | null;
|
|
|
operator_id: string;
|
|
|
remark: string | null;
|
|
|
updated_at: string;
|
|
|
}
|
|
|
|
|
|
export type AlertType =
|
|
|
| "PRICE_DEVIATION"
|
|
|
| "STALE_FALLBACK"
|
|
|
| "RPA_FAILED"
|
|
|
| "RPA_CAPTCHA"
|
|
|
| "STRUCT_CHANGE"
|
|
|
| "SESSION_EXPIRED"
|
|
|
| "SECURITY"
|
|
|
| "MANUAL_FALLBACK";
|
|
|
|
|
|
export interface AlertRecord {
|
|
|
id: string;
|
|
|
alert_type: AlertType;
|
|
|
quote_id: string | null;
|
|
|
cargo_hash: string | null;
|
|
|
detail_json: Record<string, unknown> | null;
|
|
|
status: "open" | "resolved";
|
|
|
resolver_id: string | null;
|
|
|
resolved_at: string | null;
|
|
|
created_at: string;
|
|
|
root_cause?: string;
|
|
|
customer_id?: string | null;
|
|
|
pickup_customer?: string;
|
|
|
pickup_selected?: string;
|
|
|
delivery_customer?: string;
|
|
|
delivery_selected?: string;
|
|
|
cargo_summary?: string;
|
|
|
}
|
|
|
|
|
|
export type QuoteQueryOutcome = "processing" | "success" | "failed" | "stale";
|
|
|
|
|
|
export interface QuoteQueryLogRecord {
|
|
|
quote_id: string;
|
|
|
request_id: string;
|
|
|
customer_id: string;
|
|
|
outcome: QuoteQueryOutcome;
|
|
|
failure_reason: string | null;
|
|
|
error_code: string | null;
|
|
|
source_type: string | null;
|
|
|
tier_count: number | null;
|
|
|
pickup_customer: string;
|
|
|
pickup_selected: string;
|
|
|
delivery_customer: string;
|
|
|
delivery_selected: string;
|
|
|
cargo_summary: string;
|
|
|
created_at: string;
|
|
|
completed_at: string | null;
|
|
|
}
|
|
|
|
|
|
export type ErrorCode =
|
|
|
| "VALIDATION_FAILED"
|
|
|
| "UNAUTHORIZED"
|
|
|
| "FORBIDDEN"
|
|
|
| "QUOTE_NOT_FOUND"
|
|
|
| "RATE_LIMITED"
|
|
|
| "QUOTE_UNAVAILABLE"
|
|
|
| "QUOTE_TIMEOUT"
|
|
|
| "INTERNAL_ERROR";
|
|
|
|
|
|
export interface ApiOk<T> {
|
|
|
code: 0;
|
|
|
message: string;
|
|
|
data: T;
|
|
|
}
|
|
|
|
|
|
export interface ApiErr {
|
|
|
code: ErrorCode | string;
|
|
|
message: string;
|
|
|
data: null;
|
|
|
}
|
|
|
|
|
|
export type ApiResponse<T> = ApiOk<T> | ApiErr;
|
|
|
|
|
|
export interface AdminUser {
|
|
|
user_id: string;
|
|
|
role: "admin";
|
|
|
}
|
|
|
|
|
|
export interface LoginResponse {
|
|
|
token: string;
|
|
|
user: AdminUser;
|
|
|
}
|
|
|
|
|
|
export interface PaginatedResult<T> {
|
|
|
list: T[];
|
|
|
total: number;
|
|
|
page: number;
|
|
|
size: number;
|
|
|
}
|
|
|
|
|
|
export interface MetricsDashboard {
|
|
|
api_success_rate: number;
|
|
|
rpa_success_rate: number;
|
|
|
realtime_rate: number;
|
|
|
cache_hit_rate: number;
|
|
|
p95_latency_ms: number;
|
|
|
queue_depth: number;
|
|
|
open_alerts_count: number;
|
|
|
}
|
|
|
|
|
|
export interface RpaStatus {
|
|
|
workers: Array<{
|
|
|
worker_id: string;
|
|
|
last_seen_ms: number | null;
|
|
|
paused_seconds: number;
|
|
|
online: boolean;
|
|
|
}>;
|
|
|
queue_depth: number;
|
|
|
circuit_open: boolean;
|
|
|
success_rate_24h: number;
|
|
|
}
|
|
|
|
|
|
export interface QueueStats {
|
|
|
queue_name: string;
|
|
|
waiting: number;
|
|
|
active: number;
|
|
|
delayed: number;
|
|
|
failed: number;
|
|
|
completed: number;
|
|
|
depth: number;
|
|
|
}
|