/** * MotherShip fill-only 结账:选承运商 + 保障 → 入队 RPA */ import { prisma } from "@/lib/prisma"; import { validateMsCheckoutSelection, type MsCoverageKind, } from "@/lib/mothership/ms-checkout-selection"; import { saveMsCheckoutStatus, readMsCheckoutStatus } from "@/lib/mothership/ms-checkout-store"; import { enqueueMsCheckoutFillJob } from "@/modules/mothership/checkout-queue"; import { validateQuoteInput } from "@/modules/quote/validation"; import type { QuoteRequestBody } from "@/lib/frontend/types"; export async function submitMsCheckoutFill(input: { customerId: string; quoteId: string; preferredCarrier: string; coverage: MsCoverageKind; cargoValueUsd?: number; mothershipDetails?: QuoteRequestBody["mothership_details"]; pickupAccessorials?: string[]; deliveryAccessorials?: string[]; }): Promise<{ quote_id: string; status: "processing" }> { const err = validateMsCheckoutSelection({ preferredCarrier: input.preferredCarrier, coverage: input.coverage, cargoValueUsd: input.cargoValueUsd, }); if (err) throw new Error(err); const record = await prisma.quoteRecord.findUnique({ where: { quoteId: input.quoteId }, }); if (!record || record.customerId !== input.customerId) { throw new Error("报价单不存在"); } if (record.status !== "done") { throw new Error("请等待初步报价完成后再结账"); } const existing = await readMsCheckoutStatus(input.quoteId); if (existing?.status === "processing") { throw new Error("结账同步进行中,请稍候"); } const pickup = record.pickupJson as unknown as QuoteRequestBody["pickup_address"]; const delivery = record.deliveryJson as unknown as QuoteRequestBody["delivery_address"]; const body = { request_id: record.requestId, customer_id: input.customerId, pickup_address: pickup, delivery_address: delivery, weight: { value: Number(record.weightLb), unit: "lb" as const }, dimensions: { length: Number(record.dimLIn), width: Number(record.dimWIn), height: Number(record.dimHIn), unit: "in" as const, }, pallet_count: record.palletCount, cargo_type: record.cargoType as QuoteRequestBody["cargo_type"], mothership_details: input.mothershipDetails, pickup_accessorials: input.pickupAccessorials, delivery_accessorials: input.deliveryAccessorials, preferred_carrier: input.preferredCarrier, coverage: input.coverage, cargo_value_usd: input.cargoValueUsd, }; const cargo = validateQuoteInput(body); cargo.preferredCarrier = input.preferredCarrier; cargo.coverage = input.coverage; cargo.cargoValueUsd = input.cargoValueUsd; await saveMsCheckoutStatus({ quote_id: input.quoteId, status: "processing", coverage: input.coverage, cargo_value_usd: input.cargoValueUsd ?? null, message: "正在官网选择承运商与保障并填齐详情…", updated_at_ms: Date.now(), }); try { await enqueueMsCheckoutFillJob({ quoteId: input.quoteId, requestId: record.requestId, customerId: input.customerId, cargo, preferredCarrier: input.preferredCarrier, coverage: input.coverage, cargoValueUsd: input.cargoValueUsd, }); } catch (err) { const msg = err instanceof Error ? err.message : String(err); if (/already exists|JobId/i.test(msg)) { throw new Error("结账同步进行中,请稍候"); } await saveMsCheckoutStatus({ quote_id: input.quoteId, status: "failed", coverage: input.coverage, cargo_value_usd: input.cargoValueUsd ?? null, message: "官网同步失败,请稍后重试", updated_at_ms: Date.now(), }); throw err; } return { quote_id: input.quoteId, status: "processing" }; }