import type { MothershipAddressCandidate } from "@/modules/address/types"; import type { QuoteRequest } from "@/modules/providers/quote-provider"; import { RpaError } from "@/modules/rpa/errors"; import { RPAContext } from "@/workers/rpa/kernel/context"; import { fillAddressStep } from "@/workers/rpa/kernel/steps/address-step"; import { parkQuoteSession, prepareParkedQuotePage, takeParkedQuoteSession, } from "@/workers/rpa/parked-quote-session"; import { captureRpaDebugScreenshot } from "@/workers/rpa/page-prep"; import { persistContext } from "@/workers/rpa/session-manager"; function candidateToAddressFields( candidate: MothershipAddressCandidate, ): QuoteRequest["pickup"] { return { street: candidate.street, city: candidate.city, state: candidate.state, zip: candidate.zip, placeId: candidate.option_id, formattedAddress: candidate.formatted_address, selectedFromSuggestions: true, mothershipOptionId: candidate.option_id, mothershipDisplayLabel: candidate.display_label, selectedFromMothership: true, }; } function minimalQuoteRequest( sessionId: string, pickup: MothershipAddressCandidate, delivery: MothershipAddressCandidate, ): QuoteRequest { return { cargoHash: "session-confirm", quoteSessionId: sessionId, pickup: candidateToAddressFields(pickup), delivery: candidateToAddressFields(delivery), weightLb: 1, dimsIn: { l: 48, w: 40, h: 48 }, palletCount: 1, cargoType: "general_freight", }; } /** * 用户确认候选后:在同驻留页点选双地址并重新 park, * 后续询价 job 可跳过 fillAddress + 跳过 openQuotePage。 */ export async function confirmAddressesOnParkedSession(input: { sessionId: string; pickup: MothershipAddressCandidate; delivery: MothershipAddressCandidate; }): Promise { const parked = await takeParkedQuoteSession(input.sessionId, { waitMs: 3_000, }); if (!parked) { throw new RpaError( "PAGE_LOAD_TIMEOUT", "驻留浏览器会话不可用(请重新获取地址候选,并确认 RPA Worker 已启动且未走 spawn 子进程)", { retryable: true }, ); } const started = Date.now(); try { await prepareParkedQuotePage(parked.page); const ctx = RPAContext.fromSession(parked.page, parked.context); await fillAddressStep( ctx, minimalQuoteRequest(input.sessionId, input.pickup, input.delivery), ); await persistContext(parked.context); await parkQuoteSession(input.sessionId, parked.page, parked.context); console.log( `[parked-session] 地址已确认并重新驻留 session=${input.sessionId.slice(0, 8)}… ${Date.now() - started}ms`, ); } catch (error) { await captureRpaDebugScreenshot(parked.page, "session-confirm-failure"); await parked.page.close().catch(() => undefined); await parked.context.close().catch(() => undefined); throw error; } }