大交通检索修复
This commit is contained in:
1 parent
364ef4ae30
commit
8a0f02c9be
29 files changed
+1175
-33
No files matched your search
@@ -11,9 +11,13 @@ Chrome Manifest V3 扩展,在用户已登录的 LTJT ERP 页面内执行经过
|
||||
|
||||
## 当前版本
|
||||
|
||||
当前源码版本为 `0.5.185`。版本化 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.186`。版本化 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.185 当前重点:
|
||||
0.5.186 当前重点:
|
||||
|
||||
- 大交通选填车票说明在忽略空格/括号时保留原数字边界,修复 `C91 12:52` 无法用 `C91` 匹配的问题,继续阻止 `C91` 命中 `C911`。供应商与说明仍须匹配同一唯一候选;仅路线也可在唯一时使用,歧义时停止并显示最多 5 条供应商/说明,不暴露内部 ID 或价格。新增/变更以及原生联动前复核使用同一规则。零候选与候选未加载分别提示;未填车票说明的旧行为保持。回归为本地合成验证,真实 ERP 验收待部署方执行;需更新插件及平台最低版本,本修复无数据库迁移。
|
||||
|
||||
继续保留 0.5.185 的查询恢复修复:
|
||||
|
||||
- 目标只读查询遇到 Chrome 主框架移除时,在同一执行仍处于写入前运行状态下,最多等待 8 秒确认同一主文档连续两次加载完成,再重新注入适配器并只读重查一次。持续切换、权限错误、取消或写入边界变化时停止;业务无匹配和多匹配不重试,原生保存不重试。异常按实际查询、路由准备或预检阶段记录;查询失败保留步骤和当时自动恢复时间快照,不记录业务值或恢复正常心跳刷屏。本地合成回归覆盖,真实 ERP 验收待部署方执行。需更新插件及平台最低版本,无数据库迁移。
|
||||
|
||||
|
||||
@@ -2552,6 +2552,33 @@ function erpResolutionFailure(operation, report = {}) {
|
||||
|
||||
function lifecycleRouteFailure(operation, report = {}) {
|
||||
const blockers = Array.isArray(report.blockers) ? report.blockers.map(String) : [];
|
||||
if (operation?.action === 'arrangement_transport') {
|
||||
if (blockers.some((item) => ['arrangement_resource_lookup_missing', 'arrangement_resource_candidate_data_missing'].includes(item))) {
|
||||
return {
|
||||
errorCode: 'arrangement_resource_lookup_not_ready',
|
||||
message: '未能读取 ERP 大交通结算单位候选,请确认页面加载完成后重试;未写入 ERP。'
|
||||
};
|
||||
}
|
||||
const resourceCount = blockers.find((item) => /^arrangement_resource_candidate_count:\d+$/.test(item));
|
||||
if (resourceCount) {
|
||||
const count = Number(resourceCount.split(':').at(-1));
|
||||
const filtered = operation.data?.arrangement?.ticket_description !== undefined;
|
||||
const criteria = filtered ? '同时匹配结算单位和车票说明' : '匹配结算单位搜索条件';
|
||||
const candidates = report.preflight?.resource_resolution?.candidates;
|
||||
const text = (value) => String(value || '').replace(/[\u0000-\u001f\u007f]/g, ' ').slice(0, 120);
|
||||
const candidateText = count > 1 && Array.isArray(candidates)
|
||||
? candidates.slice(0, 5).map((row, index) => `${index + 1}. ${text(row?.resource)} ${text(row?.item)}`.trim()).join('\n')
|
||||
: '';
|
||||
return {
|
||||
errorCode: count === 0 ? 'arrangement_resource_not_found' : 'arrangement_resource_not_unique',
|
||||
message: count === 0
|
||||
? `ERP 中没有找到${criteria}的大交通候选,请核对供应商、线路和车次;未写入 ERP。`
|
||||
: `ERP 中有 ${count} 条大交通候选${criteria},无法唯一选择;未写入 ERP。`
|
||||
+ (candidateText ? `\n候选${count > 5 ? '(仅显示前 5 条)' : ''}:\n${candidateText}` : '')
|
||||
+ '\n请在“车票说明”中补充可区分候选的线路、车次、时刻或票种;说明完全相同的候选请在 ERP 中核对。'
|
||||
};
|
||||
}
|
||||
}
|
||||
if (blockers.includes('arrangement_date_matcher_unavailable')) {
|
||||
return {
|
||||
errorCode: 'arrangement_date_matcher_unavailable',
|
||||
|
||||
@@ -2997,13 +2997,22 @@
|
||||
}
|
||||
|
||||
function candidateDescriptionMatches(item, query) {
|
||||
const text = normalizedLookupSearchValue(String(item || '').normalize('NFKC'));
|
||||
const original = String(item || '').normalize('NFKC').toLowerCase();
|
||||
const offsets = [];
|
||||
let text = '';
|
||||
for (let index = 0; index < original.length; index += 1) {
|
||||
if (/[\s【】[]\[\]()(){}<>《》]/u.test(original[index])) continue;
|
||||
text += original[index];
|
||||
offsets.push(index);
|
||||
}
|
||||
const keyword = normalizedLookupSearchValue(String(query || '').normalize('NFKC'));
|
||||
if (!keyword) return false;
|
||||
// Numeric fragments must not match larger numbers, e.g. 15 vs 115 or D88 vs D888.
|
||||
// Ignore formatting for lookup, but check numeric boundaries in the original
|
||||
// label: C91 followed by " 12:52" is a train plus a time, not C9112.
|
||||
// Contiguous larger numbers (15 vs 115, C91 vs C911) still must not match.
|
||||
for (let at = text.indexOf(keyword); at >= 0; at = text.indexOf(keyword, at + 1)) {
|
||||
if (/\d/.test(keyword[0]) && /\d/.test(text[at - 1] || '')) continue;
|
||||
if (/\d/.test(keyword.at(-1)) && /\d/.test(text[at + keyword.length] || '')) continue;
|
||||
if (/\d/.test(keyword[0]) && /\d/.test(original[offsets[at] - 1] || '')) continue;
|
||||
if (/\d/.test(keyword.at(-1)) && /\d/.test(original[offsets[at + keyword.length - 1] + 1] || '')) continue;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -3074,6 +3083,12 @@
|
||||
candidate: matches.length === 1 || firstMatchDefaulted ? matches[0] : null,
|
||||
match_count: matches.length,
|
||||
candidate_count: candidates.length,
|
||||
...(transport && ticketDescription !== undefined && matches.length > 1 ? {
|
||||
candidates: matches.slice(0, 5).map((candidate) => ({
|
||||
resource: candidate.name.replace(/[\u0000-\u001f\u007f]/g, ' ').slice(0, 120),
|
||||
item: candidate.item.replace(/[\u0000-\u001f\u007f]/g, ' ').slice(0, 120)
|
||||
}))
|
||||
} : {}),
|
||||
strategy: exact.length
|
||||
? 'exact'
|
||||
: matches === tokenized && tokenized.length
|
||||
@@ -3299,7 +3314,10 @@
|
||||
if (!coordinator.value || coordinator.mismatch) blockers.push('arrangement_guide_linked_coordinator_unresolved');
|
||||
}
|
||||
delete arrangement.selection;
|
||||
evidence = { mode: 'update', slot_index: slotIndex, current_row_count: 1, value_redacted: true };
|
||||
evidence = {
|
||||
mode: 'update', slot_index: slotIndex, current_row_count: 1, value_redacted: !selected.candidates,
|
||||
...(transport && selected.candidates ? { candidates: selected.candidates, exact_match_count: selected.match_count } : {})
|
||||
};
|
||||
} else if (hotel && arrangement.mode === 'update') {
|
||||
const selection = arrangement.selection;
|
||||
let slotIndex = 0;
|
||||
@@ -3403,8 +3421,9 @@
|
||||
exact_match_count: selected.match_count,
|
||||
strategy: selected.strategy,
|
||||
selection: selected.selection,
|
||||
...(transport && selected.candidates ? { candidates: selected.candidates } : {}),
|
||||
coordinator_source: guide ? guideCoordinatorResolution(form, operation, form?.ownerDocument).source : '',
|
||||
value_redacted: true
|
||||
value_redacted: !selected.candidates
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6600,7 +6619,7 @@
|
||||
}
|
||||
|
||||
window.LTJTOrderAssistant = {
|
||||
version: '0.5.185',
|
||||
version: '0.5.186',
|
||||
resolveNativeListSearchValues,
|
||||
lookupKeywordMatchesText,
|
||||
inspectLifecycleSearchCriteria: lifecycleSearchCriteria,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "联泰下单助手",
|
||||
"version": "0.5.185",
|
||||
"version": "0.5.186",
|
||||
"description": "在已登录 LTJT ERP 页面内规划并受控执行联泰 ERP 业务操作。",
|
||||
"permissions": [
|
||||
"activeTab",
|
||||
|
||||
@@ -944,7 +944,7 @@
|
||||
|
||||
window.LTJTOrderAssistant = {
|
||||
...(window.LTJTOrderAssistant || {}),
|
||||
version: '0.5.185',
|
||||
version: '0.5.186',
|
||||
openTeamBatchForm,
|
||||
pingTeamBatchFrame,
|
||||
preflightTeamBatchNative,
|
||||
|
||||
Reference in new issue
Block a user