大交通检索修复

This commit is contained in:
andy committed 2026-09-16 11:50:50 +08:00
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,