Files
LWLT-AI/archive/handoff/2026-07-12/legacy-erp-handoff/tools/erp_submit_guard.js
2026-07-13 19:57:46 +08:00

175 lines
6.3 KiB
JavaScript

const {
dialogCursor,
dialogMessagesSince,
assertNoNewErpLoginRequired,
} = require('./erp_session_guard');
function isSubmitSuccessText(value) {
return /下单成功|操作成功|修改成功|成功|สำเร็จ|success/i.test(String(value || ''));
}
function createSubmitUnconfirmedError(source, evidence) {
const error = new Error(`ERP submit result was not confirmed (${source || 'erp.submit'})`);
error.code = 'erp_submit_unconfirmed';
error.details = evidence;
return error;
}
async function readFrameBodyText(pageOrFrame) {
return pageOrFrame.evaluate(() => document.body.innerText.replace(/\s+/g, ' ').trim()).catch(() => '');
}
async function installSubmitAjaxProbe(pageOrFrame, minimumTimeout) {
return pageOrFrame.evaluate((timeoutMs) => {
window.__erpSubmitAjaxEvents = [];
window.__erpSubmitAjaxMinimumTimeout = timeoutMs;
const jq = window.jQuery || window.$;
if (!jq || typeof jq.ajax !== 'function') {
return { installed: false, reason: 'jquery_ajax_missing', minimumTimeout: timeoutMs };
}
if (!jq.__erpSubmitAjaxOriginal) {
const originalAjax = jq.ajax;
jq.__erpSubmitAjaxOriginal = originalAjax;
jq.ajax = function erpSubmitAjaxWithProbe(rawOptions, ...rest) {
const eventList = window.__erpSubmitAjaxEvents || (window.__erpSubmitAjaxEvents = []);
const options = typeof rawOptions === 'string'
? { ...(rest[0] || {}), url: rawOptions }
: { ...(rawOptions || {}) };
const minTimeout = Number(window.__erpSubmitAjaxMinimumTimeout || 0);
const currentTimeout = Number(options.timeout || 0);
if (minTimeout && (!currentTimeout || currentTimeout < minTimeout)) {
options.timeout = minTimeout;
}
const base = () => ({
url: options.url || '',
type: options.type || options.method || '',
dataType: options.dataType || '',
timeout: Number(options.timeout || 0),
});
const originalSuccess = options.success;
const originalError = options.error;
const originalComplete = options.complete;
options.success = function successProbe(data, textStatus, jqXHR) {
eventList.push({
event: 'success',
...base(),
status: jqXHR && jqXHR.status || 0,
textStatus: textStatus || '',
responseText: String(jqXHR && jqXHR.responseText || data || '').slice(0, 2000),
});
if (typeof originalSuccess === 'function') return originalSuccess.apply(this, arguments);
return undefined;
};
options.error = function errorProbe(jqXHR, textStatus, errorThrown) {
eventList.push({
event: 'error',
...base(),
status: jqXHR && jqXHR.status || 0,
textStatus: textStatus || '',
error: errorThrown && errorThrown.message ? errorThrown.message : String(errorThrown || ''),
responseText: String(jqXHR && jqXHR.responseText || '').slice(0, 2000),
});
if (typeof originalError === 'function') return originalError.apply(this, arguments);
return undefined;
};
options.complete = function completeProbe(jqXHR, textStatus) {
eventList.push({
event: 'complete',
...base(),
status: jqXHR && jqXHR.status || 0,
textStatus: textStatus || '',
});
if (typeof originalComplete === 'function') return originalComplete.apply(this, arguments);
return undefined;
};
return originalAjax.call(this, options);
};
}
return { installed: true, minimumTimeout: timeoutMs };
}, minimumTimeout).catch((error) => ({
installed: false,
reason: error && error.message ? error.message : String(error),
minimumTimeout,
}));
}
async function readSubmitAjaxEvents(pageOrFrame) {
return pageOrFrame.evaluate(() =>
Array.isArray(window.__erpSubmitAjaxEvents) ? window.__erpSubmitAjaxEvents : []
).catch(() => []);
}
function isPrimarySubmitAjaxEvent(item = {}) {
return /\/System\/DAT\/(?:orders|plan)\.asp/i.test(String(item.url || ''));
}
function isAjaxFailure(item = {}) {
return item.event === 'error' || Number(item.status || 0) >= 400;
}
function ajaxEvidenceText(events = []) {
return events.map((item) => {
const parts = [];
if (item.event === 'error') {
parts.push(item.textStatus || '', item.error || '');
}
parts.push(item.responseText || '');
return parts.filter(Boolean).join(' ');
}).filter(Boolean).join('\n');
}
async function submitInfoFormAndConfirm(pageOrFrame, dialogState = null, options = {}) {
const source = options.source || 'erp.submit';
const waitMs = Number.isFinite(options.waitMs) ? options.waitMs : 30000;
const ajaxTimeoutMs = Number.isFinite(options.ajaxTimeoutMs) ? options.ajaxTimeoutMs : Math.max(waitMs, 30000);
const requireSuccess = options.requireSuccess !== false;
const cursor = dialogCursor(dialogState);
const ajaxProbe = await installSubmitAjaxProbe(pageOrFrame, ajaxTimeoutMs);
await pageOrFrame.evaluate(() => {
if (typeof SubmitInfoForm !== 'function') throw new Error('SubmitInfoForm not found');
SubmitInfoForm();
});
await pageOrFrame.waitForTimeout(waitMs);
assertNoNewErpLoginRequired(dialogState, cursor, source);
const newDialogs = dialogMessagesSince(dialogState, cursor);
const ajaxEvents = await readSubmitAjaxEvents(pageOrFrame);
const primaryAjaxEvents = ajaxEvents.filter(isPrimarySubmitAjaxEvent);
const primaryAjaxFailed = primaryAjaxEvents.some(isAjaxFailure);
const bodyText = await readFrameBodyText(pageOrFrame);
const evidenceText = [
newDialogs.map((item) => item.message || '').join('\n'),
ajaxEvidenceText(ajaxEvents),
bodyText,
].filter(Boolean).join('\n');
const result = {
ok: isSubmitSuccessText(evidenceText) && !primaryAjaxFailed,
source,
dialogs: newDialogs,
ajaxProbe,
ajaxEvents,
primaryAjaxEvents,
primaryAjaxFailed,
bodyPreview: bodyText.slice(0, 1000),
};
if (requireSuccess && !result.ok) {
throw createSubmitUnconfirmedError(source, result);
}
return result;
}
module.exports = {
isSubmitSuccessText,
installSubmitAjaxProbe,
readSubmitAjaxEvents,
isPrimarySubmitAjaxEvent,
ajaxEvidenceText,
submitInfoFormAndConfirm,
createSubmitUnconfirmedError,
};