/** * 西雅图 → 迈阿密 截图对齐验收(4 托 723lb 40×48×45) * 用法:RPA_ADDRESS_MODE=real npm run demo:seattle-miami */ import { randomUUID } from "node:crypto"; import { loadDevEnv } from "@/lib/dev-env"; import { getRpaAddressMode } from "@/lib/rpa/address-mode"; import { ensureNativeInfraIfNeeded } from "@/lib/infra/ensure-native-infra"; import { hostCreateQuote, hostFetchMothershipCandidates, hostGetQuote, hostPreheatQuoteSession, } from "@/lib/frontend/api-client"; import { applyMothershipCandidate } from "@/lib/frontend/mothership-address"; import type { QuoteRequestBody } from "@/lib/frontend/types"; import { pollQuoteUntilDone } from "@/hooks/use-quote-polling"; import { isAnyRpaWorkerHealthy } from "@/lib/rpa/worker-health"; import { prisma } from "@/lib/prisma"; import { ensureRedisReady, resetRedisClient } from "@/lib/redis"; loadDevEnv(); const BASE = process.env.PROVE_BASE_URL ?? "http://localhost:3000"; const TOKEN = "demo-host-token"; const CUSTOMER_ID = "CUST_001"; const PICKUP = { street: "3131 Western Ave", city: "Seattle", state: "WA" } as const; const DELIVERY = { street: "7000 NW 52nd St", city: "Miami", state: "FL" } as const; const CARGO = { pallet_count: 4, weight_lb: 723, width_in: 40, length_in: 48, height_in: 45, } as const; /** 参考截图 standard 档最低价与最优性价比(允许 ±$0.02) */ const EXPECTED_STANDARD: Record = { "standard/lowest": 1338.66, "standard/bestValue": 1365.86, }; function draftAddress(street: string, city: string, state: string) { return { street, city, state, zip: "", place_id: `draft_${street}_${city}_${state}`.replace(/\s+/g, "_").slice(0, 64), formatted_address: `${street}, ${city}, ${state}`, selected_from_suggestions: true, }; } function pickCandidate< T extends { street: string; display_label: string }, >(candidates: T[], draft: { street: string }): T { const num = draft.street.match(/^\d+/)?.[0]; if (num) { const matched = candidates.find((c) => { const blob = `${c.street} ${c.display_label}`.replace(/\s+/g, " "); return blob.includes(num); }); if (matched) return matched; } const first = candidates[0]; if (!first) throw new Error("联想无候选"); return first; } async function main() { if (getRpaAddressMode() !== "real") { throw new Error("须 RPA_ADDRESS_MODE=real"); } ensureNativeInfraIfNeeded(); resetRedisClient(); await ensureRedisReady(12, 1_000); await prisma.$queryRaw`SELECT 1`; if (!(await isAnyRpaWorkerHealthy())) { throw new Error("RPA Worker 无心跳"); } const candRes = await hostFetchMothershipCandidates( BASE, TOKEN, CUSTOMER_ID, { ...PICKUP, zip: "" }, { ...DELIVERY, zip: "" }, ); if (candRes.code !== 0 || !candRes.data) { throw new Error(candRes.message ?? "MotherShip 联想失败"); } const pickup = pickCandidate(candRes.data.pickup_candidates, PICKUP); const delivery = pickCandidate(candRes.data.delivery_candidates, DELIVERY); if (candRes.data.quote_session_id) { await hostPreheatQuoteSession(BASE, TOKEN, CUSTOMER_ID, candRes.data.quote_session_id); } const body: QuoteRequestBody = { request_id: randomUUID(), customer_id: CUSTOMER_ID, pickup_address: applyMothershipCandidate(draftAddress(PICKUP.street, PICKUP.city, PICKUP.state), pickup), delivery_address: applyMothershipCandidate( draftAddress(DELIVERY.street, DELIVERY.city, DELIVERY.state), delivery, ), weight: { value: CARGO.weight_lb, unit: "lb" }, dimensions: { length: CARGO.length_in, width: CARGO.width_in, height: CARGO.height_in, unit: "in", }, pallet_count: CARGO.pallet_count, cargo_type: "general_freight", }; const created = await hostCreateQuote(BASE, TOKEN, body); if (created.code !== 0 || !created.data?.quote_id) { throw new Error(created.message ?? "创建询价失败"); } const quoteId = created.data.quote_id; let detail; if (created.data.status === "done") { detail = (await hostGetQuote(BASE, TOKEN, CUSTOMER_ID, quoteId)).data; } else { const poll = await pollQuoteUntilDone( async () => { const got = await hostGetQuote(BASE, TOKEN, CUSTOMER_ID, quoteId); if (got.code !== 0) return { ok: false, errorMessage: got.message }; return { ok: true, data: got.data }; }, { timeoutMs: 480_000 }, ); if (poll.type !== "done") { throw new Error(poll.type === "timeout" ? "轮询超时" : poll.message); } detail = poll.quote; } const tiers = detail?.quotes ?? []; console.log("\n=== 西雅图 → 迈阿密 报价 ==="); for (const q of tiers) { console.log( ` ${q.service_level}/${q.rate_option}: $${q.final_total?.toFixed(2)} (${q.transit_days ?? "—"})`, ); } const mismatches: string[] = []; for (const [key, expected] of Object.entries(EXPECTED_STANDARD)) { const [service, rate] = key.split("/"); const got = tiers.find((t) => t.service_level === service && t.rate_option === rate); if (!got) { mismatches.push(`${key}: 缺失`); continue; } if (Math.abs((got.final_total ?? 0) - expected) > 0.02) { mismatches.push(`${key}: 期望 $${expected} 实际 $${got.final_total}`); } } if (mismatches.length > 0) { throw new Error(`与参考截图未对齐:\n${mismatches.join("\n")}`); } console.log("\n[PASS] standard/lowest 与 standard/bestValue 与参考截图对齐"); } main() .catch((e) => { console.error("[FAIL]", e); process.exit(1); }) .finally(() => prisma.$disconnect());