You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

121 lines
4.1 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import type { Page } from "playwright";
import { isRpaAddressMockMode } from "@/lib/rpa/address-mode";
import { isRpaHumanSubmitStrict, isRpaVisualRushMode } from "@/lib/rpa/env";
import {
clickFirstVisibleFromSpecs,
getSelectorSpecs,
} from "@/lib/rpa/selector-specs";
import { RpaError } from "@/modules/rpa/errors";
import {
assertCargoCommittedState,
assertBothAddressesLocked,
} from "@/workers/rpa/cargo-lock";
import {
debugBusinessStateSnapshot,
waitForBusinessReadyState,
} from "@/workers/rpa/quote-capture/quote-business-ready";
import { waitForPostSubmitDestination } from "@/workers/rpa/quote-capture/post-submit-navigation";
import type { QuoteSubmitOptions } from "@/workers/rpa/quote-capture/types";
const WIDGET_SELECTOR = "#react-quoter-landing-plugin";
/**
* DOM 仅用于提交动作;填表由 kernel fillAddress / fillCargo 完成。
* submit 前必须通过 cargo committed + business-ready 闸门。
*/
export async function submitQuoteForm(
page: Page,
options?: QuoteSubmitOptions,
): Promise<void> {
const rush = isRpaVisualRushMode();
const humanStrict = isRpaHumanSubmitStrict();
if (rush && !humanStrict) {
console.log("[rpa] visual-rush: 跳过 submit 前全部闸门,直接点报价");
await page.keyboard.press("Escape").catch(() => undefined);
await page.waitForTimeout(200);
await page.keyboard.press("Escape").catch(() => undefined);
await page.waitForTimeout(200);
const clicked = await clickFirstVisibleFromSpecs(
page,
getSelectorSpecs("RPA_SELECTOR_SUBMIT"),
);
if (!clicked) {
throw new RpaError("STRUCT_CHANGE", "提交按钮不可见", {
retryable: false,
});
}
const resultUrl = await waitForPostSubmitDestination(page);
console.log(`[rpa] post-submit destination ok url=${resultUrl}`);
return;
}
if (rush && humanStrict) {
console.log("[rpa] human-submit-strict: rush 填表 + 完整 submit 闸门");
if (!isRpaAddressMockMode() && !options?.skipAddressLock) {
await assertBothAddressesLocked(page, WIDGET_SELECTOR);
}
await assertCargoCommittedState(page, WIDGET_SELECTOR);
const snapshot = await waitForBusinessReadyState(page, options);
if (!snapshot.ready) {
await debugBusinessStateSnapshot(page);
throw new RpaError(
"QUOTE_ENTRY_UNAVAILABLE",
`禁止提交business-ready=false${snapshot.reasons.join("")}`,
{ retryable: true },
);
}
const clicked = await clickFirstVisibleFromSpecs(
page,
getSelectorSpecs("RPA_SELECTOR_SUBMIT"),
);
if (!clicked) {
throw new RpaError("STRUCT_CHANGE", "提交按钮不可见", { retryable: false });
}
const resultUrl = await waitForPostSubmitDestination(page);
console.log(`[rpa] post-submit destination ok url=${resultUrl}`);
return;
}
if (!isRpaAddressMockMode() && !options?.skipAddressLock) {
await assertBothAddressesLocked(page, WIDGET_SELECTOR);
} else if (options?.skipAddressLock) {
console.log("[rpa] axel-place-prefetch: 跳过 submit 前双地址锁定闸门");
} else {
console.log("[rpa] address-mock: 跳过 submit 前双地址锁定闸门");
}
await assertCargoCommittedState(page, WIDGET_SELECTOR);
const snapshot = await waitForBusinessReadyState(page, options);
if (!snapshot.ready) {
await debugBusinessStateSnapshot(page);
throw new RpaError(
"QUOTE_ENTRY_UNAVAILABLE",
`禁止提交business-ready=false${snapshot.reasons.join("")}`,
{ retryable: true },
);
}
const clicked = await clickFirstVisibleFromSpecs(
page,
getSelectorSpecs("RPA_SELECTOR_SUBMIT"),
);
if (!clicked) {
throw new RpaError("STRUCT_CHANGE", "提交按钮不可见", {
retryable: false,
});
}
if (isRpaAddressMockMode()) {
console.log(
`[rpa] address-mock: 跳过 post-submit URL 等待url=${page.url()}`,
);
await page.waitForTimeout(1_500);
return;
}
const resultUrl = await waitForPostSubmitDestination(page);
console.log(`[rpa] post-submit destination ok url=${resultUrl}`);
}
export { debugBusinessStateSnapshot, waitForBusinessReadyState };