fix: require exact ERP account identity

This commit is contained in:
inman committed 2026-09-07 20:21:09 +08:00
1 parent 6bdaa2a6e6
commit 8f70cbae4d
17 files changed
+201 -31

No files matched your search

@@ -11,9 +11,14 @@ Chrome Manifest V3 扩展,在用户已登录的 LTJT ERP 页面内执行经过
## 当前版本
当前源码版本为 `0.5.167`。版本化 ZIP、文件哈希和 Skill/DOCX 基线见 [`../../dist/release-manifest.json`](../../dist/release-manifest.json)。旧版本实现流水已冻结在 [`../../archive/project-history/2026-08-16/chrome-extension-README.pre-governance.md`](../../archive/project-history/2026-08-16/chrome-extension-README.pre-governance.md)。
当前源码版本为 `0.5.168`。版本化 ZIP、文件哈希和 Skill/DOCX 基线见 [`../../dist/release-manifest.json`](../../dist/release-manifest.json)。旧版本实现流水已冻结在 [`../../archive/project-history/2026-08-16/chrome-extension-README.pre-governance.md`](../../archive/project-history/2026-08-16/chrome-extension-README.pre-governance.md)。
0.5.167 当前重点:
0.5.168 当前重点:
- ERP 登录身份只从当前主页面唯一的 `#Lable_UserName` 节点读取,并与平台绑定身份做 Unicode、空白和大小写规范化后的精确全等比较。缺失、重复、空白或仅为子串时都不再执行;不再用整页业务文字猜测账号。
- 扩展仍只向平台返回账号是否存在、是否唯一和是否匹配,不回传 ERP 页面显示的账号文本。
继续保留 0.5.167 的空闲证明和受控重载协议:
- 插件 PING 会返回当前运行任务、持久化写入边界和待回查结果汇总后的 `extension_update.safe`。只有内存与持久化状态都完全空闲,服务端才允许同一 ECS 实例进入文件部署。
- 中央服务完成共享目录替换后,平台通过受控消息请求后台 `chrome.runtime.reload()`,并刷新平台页以重新注入 content script;执行中、已跨写边界或待回查状态会拒绝重载。
@@ -1,6 +1,6 @@
'use strict';
importScripts('source-region.js', 'operation-plans.js', 'execution-guard.js', 'lifecycle-result.js', 'operation-timing.js');
importScripts('source-region.js', 'operation-plans.js', 'execution-guard.js', 'lifecycle-result.js', 'operation-timing.js', 'erp-account-identity.js');
const runningTasks = new Set();
const executionWorkerKeepalives = new Map();
@@ -9,6 +9,7 @@ const operationPlans = self.LTJTOperationPlans;
const executionGuard = self.LTJTExecutionGuard;
const lifecycleResult = self.LTJTLifecycleResult;
const operationTiming = self.LTJTOperationTiming;
const erpAccountIdentity = self.LTJTErpAccountIdentity;
const EXTENSION_VERSION = chrome.runtime.getManifest().version;
const operationTimingDetailBuffers = new Map();
const erpTabCache = new Map();
@@ -1406,7 +1407,7 @@ async function findOrOpenErpTab(taskId = '') {
}
async function readErpSessionStatus(expectedErpAccount = '') {
const normalizedExpectedAccount = String(expectedErpAccount || '').trim();
const normalizedExpectedAccount = erpAccountIdentity.normalizeErpAccountIdentity(expectedErpAccount);
const permission = await erpHostPermissionStatus();
const keepalive = await readErpKeepaliveState();
const tabs = await chrome.tabs.query({ url: `${ERP_ORIGIN}/*` });
@@ -1446,25 +1447,44 @@ async function readErpSessionStatus(expectedErpAccount = '') {
try {
const [inspection] = await executeErpScript(tab.id, {
target: { tabId: tab.id },
func: (expectedAccount) => {
func: () => {
const bodyText = document.body?.innerText || '';
const accountNodes = Array.from(document.querySelectorAll('#Lable_UserName'));
const accountNode = accountNodes.length === 1 ? accountNodes[0] : null;
return {
url: location.href,
title: document.title,
account_configured: Boolean(expectedAccount),
account_matched: Boolean(expectedAccount) && bodyText.includes(expectedAccount),
observed_account_identity: accountNode?.textContent || '',
account_identity_node_count: accountNodes.length,
login_or_permission_error: Boolean(document.querySelector('input[type="password"]'))
|| /login|无权限|权限不足|permission denied/i.test(`${location.href}\n${document.title}\n${bodyText.slice(0, 400)}`)
};
},
args: [normalizedExpectedAccount]
args: []
});
const inspectionResult = inspection?.result || {
const rawInspectionResult = inspection?.result || {
url: tab.url || '',
title: '',
account_matched: false,
observed_account_identity: '',
account_identity_node_count: 0,
login_or_permission_error: true
};
const {
observed_account_identity: observedAccountIdentity,
...safeInspectionResult
} = rawInspectionResult;
const inspectionResult = {
...safeInspectionResult,
account_configured: Boolean(normalizedExpectedAccount),
account_identity_present: Boolean(
erpAccountIdentity.normalizeErpAccountIdentity(observedAccountIdentity)
),
account_matched: Number(safeInspectionResult.account_identity_node_count) === 1
&& erpAccountIdentity.exactErpAccountIdentityMatch(
normalizedExpectedAccount,
observedAccountIdentity
)
};
const recoveryRequest = (shouldRecoverFromSessionStatus(inspectionResult)
|| keepalive.last_status === 'session_expired')
? requestErpLinkRecovery(
@@ -0,0 +1,26 @@
(function installLTJTErpAccountIdentity(root, factory) {
const api = factory();
if (typeof module !== 'undefined' && module.exports) module.exports = api;
if (root) root.LTJTErpAccountIdentity = api;
}(typeof self !== 'undefined' ? self : globalThis, () => {
'use strict';
function normalizeErpAccountIdentity(value) {
return String(value || '')
.normalize('NFKC')
.replace(/\s+/gu, ' ')
.trim()
.toLocaleLowerCase();
}
function exactErpAccountIdentityMatch(expectedAccount, observedAccount) {
const expected = normalizeErpAccountIdentity(expectedAccount);
const observed = normalizeErpAccountIdentity(observedAccount);
return Boolean(expected && observed && expected === observed);
}
return {
normalizeErpAccountIdentity,
exactErpAccountIdentityMatch
};
}));
@@ -6118,7 +6118,7 @@
}
window.LTJTOrderAssistant = {
version: '0.5.167',
version: '0.5.168',
resolveNativeListSearchValues,
lookupKeywordMatchesText,
inspectLifecycleSearchCriteria: lifecycleSearchCriteria,
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "联泰下单助手",
"version": "0.5.167",
"version": "0.5.168",
"description": "在已登录 LTJT ERP 页面内规划并受控执行联泰 ERP 业务操作。",
"permissions": [
"activeTab",
@@ -944,7 +944,7 @@
window.LTJTOrderAssistant = {
...(window.LTJTOrderAssistant || {}),
version: '0.5.167',
version: '0.5.168',
openTeamBatchForm,
pingTeamBatchFrame,
preflightTeamBatchNative,