Files
LWLT-AIBOT/chrome-extension/ltjt-order-assistant/README.md
T
2026-08-30 00:13:22 +08:00

125 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 联泰下单助手
Chrome Manifest V3 扩展,在用户已登录的 LTJT ERP 页面内执行经过平台确认、Schema 校验和 ERP 只读唯一解析的确定性业务操作。
## 职责边界
- 平台与 Agent 负责业务意图、用户事实、任务/会话、确认和通讯。
- 插件负责前门禁、ERP 对象/资源解析、页面联动、严格写前校验、原生提交与 fresh requery。
- 插件不解释新的自然语言含义,不绕过候选唯一/首条选择规则,不维护主数据,不凭 HTTP 200 宣称成功。
- `operation.source` 只允许显式历史测试上下文;普通平台元数据不得混入业务 operation。
## 当前版本
当前源码版本为 `0.5.163`。版本化 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.163 当前重点:
- 保留 `0.5.162` 的稳定态处理:独立团单个下单只有在表单结构、客户/跟单人/销售人三个原生 SelectBox lookup 数据以及页面 jQuery Ajax 全部就绪后才进入预检;`GetProduct` 外层完成后还会等待页面自身 `ajaxStop`,再应用最终业务字段。该门禁使用原生完成事件和失败超时,不使用固定 sleep,也不在 live gate 静默回填。
- 按生产操作方明确要求,独立团单个下单的受保护字段范围恢复为 `0.5.156` 的实际效果:日期、客户、产品、人数、房型、跟单人、销售人等核心字段继续执行写前漂移阻断;带编号的 `ys_danwei*`、`ys_danweiid*`、`ys_bizhong*`、`ys_shuliang*`、`ys_jine*` 不再进入该哈希门禁,允许 ERP 原生表单在预检后调整这些动态应收行。漂移诊断仍只报告字段名,不保存字段值。
- 修复 ERP 链路恢复在 Chrome MV3 服务工作线程中断后残留 `running`、导致操作台永久显示“已连接,有告警”并阻断新任务的问题。真实内存恢复仍在执行时继续保持 `running`;Promise 已不存在时收敛为 `interrupted`,后续普通只读保活成功后转为 `idle`。该修复不重放业务任务,也不放宽 ERP 写入门禁。
- 正式控制面 `https://lwlt.nianxx.cn` 现在进入精确业务系统白名单;Popup、后台标签页发现、Manifest host permission 和 content script 注入保持同一 origin。相似域名、子域名及其他 HTTPS 站点仍不允许注入。
- 团队文件导出新增散拼母团“整团游客信息”窄分支:解析与唯一定位必须明确 `shared_plan`,只能单独导出 `visitor-list`,且源请求只携带母团 `tid`;任何子单 `did/ddid`、其他文件类型或混合范围都在请求前失败关闭。独立团和散拼具体子单“游客名单”的既有 `did+tid` 路径保持不变。
- 名单所需游客位超过 ERP 原生页面实际可物化数量时,继续在父表单保存前失败关闭,但用户主提示改为“本次名单共 N 人,超过该独立团/散拼子单在 ERP 中最多可录入的 M 人”。`passenger_native_*` 连锁 blocker 只保留在技术详情中;该例 25 人名单、16 个原生游客位会明确显示 25/16,而不再把技术码当成用户错误说明。
- 名单收到 `full_replace + confirmed=true` 后,用户的覆盖确认就是写入授权:附件中所有指定序号都进入原生覆写集合,即使 ERP 当前游客字段与附件相同也不再降级为 `passenger_import_no_changes`。未确认时,只要目标行已占用(包括同值),仍先要求覆盖确认。
- 名单继续严格执行附件/Program-only、精确对象定位、覆盖确认、游客行和领队联系人完整写前投影;父表单 ERP 明确返回成功后立即判定录入成功,不再执行写后逐行回查或自动名单对账。未取得明确成功响应仍停止且不自动重提;其他生命周期业务继续要求原有写后回查。
- 名单 canonical TSV 中恰好一行备注严格等于`领队`时,适配器校验结构化 `leader_contact` 与该行序号、姓名、电话全等;`DaoRuDones` 后先恢复四个接送联系人/电话字段,再只把姓名、电话写入 `jj_lianxiren/jj_dianhua`。父表单保存前全等检查;无领队标记时保留原字段,多行标记或缺姓名/电话时不写。
- 名单确认覆盖已占用行时,原生 `DaoRuDones` 可能遗留旧英文名。适配器现在只对合并计划确认变更的序号回写按原生规则去空白后的 `NAME → pinyinxm`,再执行完整写前投影;未变更行仍从快照恢复,任一字段不一致仍不保存父表单。
- 名单解析阶段不把 ERP 初始 16/31 个游客位当作统一静态上限;适配器仍按当前页面行数与附件最高序号尝试动态补足空白行,并保留 5000 行技术上限。若精确目标页的原生 `DaoRuDones` 实际只能物化较少游客位(本次独立团为 16),则以该页面实际数量作为 ERP 容量上限并返回上述业务提示,不进入父表单保存。
- 独立团信息修改新增 `pax.child_no_bed → ertrenshu`,与成人、占床儿童、领队共用非负校验、整表哈希、原生 `DoInfoJH` 提交和 fresh 表单回查;四类人数仍待授权 ERP 实写复测。
- 独立团单个下单把预检批准拆成两条严格基线:`SubmitInfoForm()` 返回后的浏览器表单哈希,以及预检拦截到的原生请求哈希。正式保存分别校验两者,允许 ERP 原生函数对页面表单做确定性规范化,但任何后续业务字段漂移仍在网络前失败关闭。
- 页内门禁现在明确区分“调用原生提交函数”和“已向 ERP 发出网络请求”。若哈希、表单就绪或提交函数在网络前阻断,扩展回执固定为 `blocked / prewrite_blocked / write_attempted=false / no_erp_write=true`;只有确实放行原生 Ajax 后才进入写入不确定边界。
- 独立团单个下单的产品联动直接等待原生 `GetProduct` 完成回调,客户恢复在同步写入五个受保护字段后立即做全字段精确复核,正式保存直接等待 `DoInfoJH` 完成回调;不再使用会被 Chrome 后台标签页放大到数十秒的 25/50/250ms 页面定时轮询。
- 预检提交仍由持久 Ajax guard 阻止网络,并保留唯一 payload/protected-fields hash;正式提交只临时放行一个匹配请求,重复分支在网络前阻断,完成或返回列表前 guard 持续阻止迟到的重复提交。
- 下单/批量下单通过 `webNavigation` 先定位准确 ERP iframe,再只对候选 frame 执行轻量 readiness 探针,不再向标签页全部 frame 广播脚本;正式动作仍只注入实际依赖并复用同版本页内运行时。
- 平台只在任务当前状态确为失败时展示 failure/error timeline;成功解析的 `no_plugin_dispatch/no_erp_write` 边界事实不再被误判为失败,已完成任务也不再追加伪造的“获取回执失败”。
- 扩展本地任务历史在领取新任务时有界清理,同时永久保留运行中、已跨写边界、结果不确定和待回查任务;存储排队、读写、清理、页签查找及预检子阶段均进入脱敏耗时明细,用于定位原先未归因的编排时间。
- 任务结果通过 `chrome.storage.onChanged` 即时推送到平台;平台按任务合并为“当前正在持久化的一条 + 最新待处理的一条”,原 2.5 秒轮询继续作为断线/丢事件兜底。
- 生命周期 route preparation 用本地生成的一次性 128-bit token 只标记本次精确选中的 list/edit Document;后台 frame discovery 同时要求 token 与既有路径、marker、tid/ddid 条件,避免同一标签页的陈旧同路径 frame 造成假歧义。token 原值不进入 operation、日志或 ERP 请求,正式 preflight 仍再次校验唯一业务行/表单、状态与 ownership proof。
- 外部 Agent 返回 NEED_INPUT、有效 question prompts 但遗漏顶层 reply 时,平台仅把这些已校验 prompt 合成为展示回复;没有可展示 question 时仍失败关闭,不生成 operation 或派发插件。
- ERP 唯一解析后的取消按对象类型选择已验证路径:独立团/散拼母团走列表取消,散拼子单继续走编辑页;列表行必须唯一读出一个当前状态,状态缺失、冲突或已取消都在写前停止。
- 编辑页首道身份检查与正式 preflight 统一为“表单稳定引用优先、URL 参数回退”,兼容 ERP 的 tid/ddid 参数大小写与表单键差异;表单和 URL 冲突时拒绝,并在超时报告中只保留页面/表单/引用命中计数。
- 散拼母团没有子单时,酒店安排页会按 ERP 业务规则隐藏提交入口;插件只在“已解析为散拼母团 + ERP 页面明确显示无子团数据 + 提交入口缺失”三项同时成立时,返回可操作的“请先新增散拼子单”提示,其他缺少提交控件的异常仍保持原安全阻断。
- 全部业务通过共用结果状态机记录阶段耗时,并在每次 ERP 页面动作中区分脚本注入、浏览器往返、页面执行与无 URL 的网络等待聚合;计时只保留代码、状态、毫秒数和计数,控制面再次净化后加密保存完整回执并保留安全摘要。
- 产品与客户分别执行 ERP 唯一解析;来源地关键字仅保留客户侧候选辅助,不再强制要求产品与客户来源地相互匹配。
- 散拼母团创建收到 ERP 成功响应后,精确团号回查会清空无关筛选并进行有限延迟只读重查;任一团号仍未命中时转逐日期唯一回查,整个恢复过程不重新提交创建请求。
- 无编号独立团检索以客户 + 出发日期为主条件;客户支持中文分词匹配和日期回退。
- 产品与领队只作候选补充校验,不作为原生列表硬筛。
- SGL/TWN 房型映射到 `frenshu0/frenshu1`,成人/占床儿童/不占床儿童/领队四类人数映射到 `darenshu/xiaorenshu/ertrenshu/quanrenshu`,均有非负校验和 fresh 表单回查;这些新增映射仍待授权 ERP 实写复测。
- ERP 明确返回业务规则拒绝时保留原生提示,任务标记为业务阻断,不进入“结果不确定/待回查”,也不自动重试。
- 跨订单列表和计划列表合并候选后,再用已提供的产品/领队可见文本做二次筛选;仍不唯一时继续阻断,不猜选。
- 恢复/取消进入精确编辑页后读取当前状态;目标状态相同则返回“无需迁移”,只有状态不同才继续写入前门禁。
- 恢复订单按对象类型主动切换 ERP 原生【已取消】或母团【收客中】状态标签;状态标签由原生菜单点击触发,避免遗留筛选把已取消记录排除在候选集之外。
- 插件实际领取任务后 10 分钟无最终结果自动失败;启动/重载时清理过期运行态、无执行孤儿缓存和超过 30 分钟的残留当前任务指针,可能跨过 ERP 写入边界的记录保留只读回查,不自动重试。
- ERP 页面脚本注入前检查 host permission 和批准 origin;重定向到非白名单页面、站点访问被关闭或 Chrome 拒绝注入时 fail-closed,并回传可操作的诊断码,不再把原始权限异常当作普通导出失败。
- ERP 待机保活默认开启:Chrome alarm 约每 5 分钟在已有 ERP 标签页内发送一次带 Cookie 的只读 `GET /System/Mainlt.asp`;执行任务期间跳过,不新开标签、不刷新页面、不修改业务数据,可在插件面板单独关闭。
- ERP 只读探测发现会话失效、页面注入异常或链路失败时,在无插件执行任务且已有同源 ERP 标签页的前提下,按 2 分钟冷却最多自动绕缓存刷新一次现有标签页,并等待完成后再次只读复测;失败只保留告警,不自动重交业务任务。
- 其他未验证独立团字段保留在 operation 并转人工复核,不静默丢弃。
完整开放/阻断边界见 [生命周期发布门槛](../../agent设计规范/test-fixtures/lwlt-lifecycle/release-gate.md)。
## 本地加载
1. 打开 `chrome://extensions`。
2. 启用开发者模式。
3. 选择“加载已解压的扩展程序”。
4. 选择本目录 `chrome-extension/ltjt-order-assistant/`。
5. 每次源码或版本更新后在扩展管理页重载,并通过平台握手确认运行版本。
ZIP 仅用于交付,不从 ZIP 目录直接维护源码。
## 执行链
```text
平台领取任务
→ validateDispatchOperation
→ 找到已登录 ERP 页面
→ resolveLifecycleOperation / 资源解析
→ validateOperation 严格门禁
→ 原生页面操作
→ 明确服务端响应
→ 名单:明确成功即完成;其他业务:fresh requery
→ 结构化结果回传平台
```
零候选、多候选、页面身份不一致、登录失效、联动失败、状态不支持或提交不确定都必须在写前阻断或进入安全停止;名单明确成功即完成,其他业务回查不一致进入只读 reconciliation。不得自动重提同一写操作。
## 主要源码
| 文件 | 职责 |
|---|---|
| `background.js` | 平台桥接、任务领取、版本握手和调度 |
| `business-bridge.js` | 页面与扩展消息桥 |
| `operation-plans.js` | 前门禁、严格门禁和执行规划 |
| `operation-timing.js` | 全业务阶段计时、跨 Worker 恢复、边界与隐私净化 |
| `inpage.js` | ERP 页面发现、列表检索和业务路由 |
| `lifecycle-adapters.js` | 生命周期表单操作、写入和回查 |
| `team-batch*.js` | 独立团/散拼创建与批量执行 |
| `product-lookup.js`、`source-region.js` | 产品候选和客户候选解析;来源地仅作客户侧辅助 |
| `lifecycle-result.js`、`split-plan-reconciliation.js` | 结果统一与只读收敛 |
## 验证
```bash
node --check chrome-extension/ltjt-order-assistant/background.js
node --check chrome-extension/ltjt-order-assistant/inpage.js
npm run test:legacy
npm run check:repo
```
修改插件源码时必须递增 `manifest.json` 版本,同步平台最低版本、`mappings/lifecycle.mapping.json`、回归断言、版本化 ZIP 和 `dist/release-manifest.json`。真实 ERP 写入仍需用户针对本轮对象明确授权。