|
|
import type { Locator } from "playwright";
|
|
|
import { RPA_STREET_INPUT_CLICK_MS } from "@/lib/constants/rpa";
|
|
|
import { RpaError } from "@/modules/rpa/errors";
|
|
|
import { dismissAddressSuggestionDropdown } from "@/workers/rpa/address-commit";
|
|
|
import { AddressCommitStateMachine } from "@/workers/rpa/address-adapter/commit-state-machine";
|
|
|
import {
|
|
|
createEmptyDiagnostics,
|
|
|
logAddressSelectDiagnostics,
|
|
|
} from "@/workers/rpa/address-adapter/diagnostics";
|
|
|
import type {
|
|
|
AddressAdapter,
|
|
|
AddressCommitResult,
|
|
|
AddressSelectDiagnostics,
|
|
|
SelectAddressParams,
|
|
|
} from "@/workers/rpa/address-adapter/types";
|
|
|
|
|
|
const MOCK_STREET_FILL_MS = Math.min(RPA_STREET_INPUT_CLICK_MS, 5_000);
|
|
|
|
|
|
/** 链路探通:仅 fill 街道文本,不等待 MotherShip 联想/place API */
|
|
|
async function mockTypeStreetInput(
|
|
|
street: Locator,
|
|
|
text: string,
|
|
|
): Promise<void> {
|
|
|
const value = text.replace(/\s+/g, " ").trim().slice(0, 160);
|
|
|
await street.click({ timeout: MOCK_STREET_FILL_MS }).catch(() => undefined);
|
|
|
await street.fill("", { timeout: MOCK_STREET_FILL_MS }).catch(() => undefined);
|
|
|
await street
|
|
|
.fill(value, { timeout: MOCK_STREET_FILL_MS })
|
|
|
.catch(() => undefined);
|
|
|
await street.press("Escape").catch(() => undefined);
|
|
|
await street.press("Tab").catch(() => undefined);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* mock 模式:快速填入地址文本并合成 commit,不触发 MotherShip place 闸门。
|
|
|
* 下游 cargo / submit / quote parser 仍真实执行。
|
|
|
*/
|
|
|
export class MockAddressAdapter implements AddressAdapter {
|
|
|
private readonly stateMachine = new AddressCommitStateMachine();
|
|
|
|
|
|
private result: AddressCommitResult | null = null;
|
|
|
|
|
|
private diagnostics: AddressSelectDiagnostics = createEmptyDiagnostics("");
|
|
|
|
|
|
getCommitState() {
|
|
|
return this.stateMachine.getState();
|
|
|
}
|
|
|
|
|
|
getCommittedAddress(): AddressCommitResult | null {
|
|
|
return this.result;
|
|
|
}
|
|
|
|
|
|
getDiagnostics(): AddressSelectDiagnostics {
|
|
|
return {
|
|
|
...this.diagnostics,
|
|
|
commitState: this.stateMachine.getState(),
|
|
|
};
|
|
|
}
|
|
|
|
|
|
async selectAddress(params: SelectAddressParams): Promise<AddressCommitResult> {
|
|
|
const { ctx, side, address, street } = params;
|
|
|
const page = ctx.page;
|
|
|
const label =
|
|
|
address.mothershipDisplayLabel?.trim() ||
|
|
|
address.formattedAddress?.trim() ||
|
|
|
address.street;
|
|
|
const streetText =
|
|
|
address.formattedAddress?.trim() ||
|
|
|
`${address.street}, ${address.city ?? ""}, ${address.state ?? ""} ${address.zip ?? ""}`.trim();
|
|
|
|
|
|
this.diagnostics = createEmptyDiagnostics(label, "EMPTY");
|
|
|
this.stateMachine.transition("TYPING");
|
|
|
this.diagnostics.method = "mock-fast-fill";
|
|
|
this.diagnostics.events.push("mock-mode-start");
|
|
|
|
|
|
await mockTypeStreetInput(street, streetText);
|
|
|
await dismissAddressSuggestionDropdown(page, street);
|
|
|
this.diagnostics.events.push("street-filled");
|
|
|
this.stateMachine.transition("COMMITTED");
|
|
|
|
|
|
const placeId =
|
|
|
address.placeId?.trim() || `mock-place-${side}-${Date.now()}`;
|
|
|
const formattedAddress = streetText || label;
|
|
|
this.diagnostics.address = label;
|
|
|
this.diagnostics.placeId = placeId;
|
|
|
this.diagnostics.status = "ok";
|
|
|
this.diagnostics.placeRequestObserved = false;
|
|
|
this.diagnostics.events.push("mock-commit-synthesized");
|
|
|
|
|
|
this.result = {
|
|
|
success: true,
|
|
|
placeId,
|
|
|
formattedAddress,
|
|
|
committed: true,
|
|
|
};
|
|
|
|
|
|
logAddressSelectDiagnostics(side, this.getDiagnostics());
|
|
|
console.log(
|
|
|
`[rpa] address-mock:${side} 已合成 commit placeId=${placeId}(跳过联想/place API)`,
|
|
|
);
|
|
|
return this.result;
|
|
|
}
|
|
|
|
|
|
async waitForCommit(): Promise<AddressCommitResult> {
|
|
|
if (!this.result?.committed) {
|
|
|
throw new RpaError(
|
|
|
"ADDRESS_NOT_CONFIRMED",
|
|
|
"mock waitForCommit:selectAddress 未完成",
|
|
|
{ retryable: false },
|
|
|
);
|
|
|
}
|
|
|
return this.result;
|
|
|
}
|
|
|
}
|