perf(worker): 单号检索收敛为可走索引的等值/前缀,并支持关联单号反查

问题:后台「按单号检索」在平台单号/工单号分支里 OR 了一支
`work_order_no ILIKE '%'||$n||'%'`。前置通配符非 sargable,任何 B-Tree 都用不上,
整条查询退化成 work_orders 的 Seq Scan。

生产实测(10.3 万行 / 572MB,pg_stat_statements 46 小时窗口):
  - 列表查询 + 统计查询用同一份 filters 并行执行,一次检索打两遍全表;
    两者合计占全库 66% 的磁盘读(1.1TB/46h),单次读 26,442 个块(约 211MB)、398ms、返回 1 行。
  - 收敛后同库同参数:等值 0.19ms,前缀 30ms(约 2000 倍)。
  - 真实统计查询端到端 760ms → 61.6ms。

改动:
1. 平台单号/工单号分支收敛为「等值 + 前缀」,与 order-repo / task-repo 既有写法一致;
   工单号含随机小写段(10.2 万行里 7.4 万行非全大写),保留 LOWER() 比较。
2. 迁移 090 补 text_pattern_ops 前缀索引:当前库 collation 为 en_US.utf8,
   前缀 LIKE 在默认 B-Tree 上会退化成全索引扫(实测 438ms)。
3. 新增关联单号反查分支:SUB/INV/AS/DT 不在 work_orders 上,
   经关联表反查工单 id,四条链路均命中唯一索引。
4. 前端 isExplicitOrderNoSearch 同步补齐(否则搜到的单会被状态页签挡掉、
   实时补丁还会把行删掉)。

验证:生产 EXPLAIN 逐条比对 + 14 项仓储单测 + 6 项分类器单测。
This commit is contained in:
yml2213
2026-10-03 14:27:32 +08:00
parent f0d745ea50
commit 90de6b9419
8 changed files with 289 additions and 6 deletions
@@ -100,11 +100,15 @@ function matchesCreatedRange(
}
/**
* 是否为「显式单号检索」——工单号 / 平台单号 / 纯数字单号。
* 是否为「显式单号检索」——工单号 / 平台单号 / 纯数字单号 / 关联单号。
*
* 两处依赖它,必须保持一致,所以放在这里而不是面板内部:
* 1. 面板搜索时据此清空排他的状态/阶段筛选(避免流转后单子脱靶);
* 2. 实时补丁判定据此短路为「只原地更新」,绝不把正在看的单删掉。
*
* 关联单号(寄卖提报号 SUB / 仓库号 INV / 售后案号 AS / 履约任务号 DT)与后端
* work-order-query-repo 的反查分支一一对应:客服界面「只认寄卖单号」,
* 粘进来的就是 SUB,漏掉它会导致搜到的单被状态页签挡掉。
*/
export function isExplicitOrderNoSearch(text: unknown): boolean {
const trimmed = String(text || '').trim()
@@ -112,7 +116,11 @@ export function isExplicitOrderNoSearch(text: unknown): boolean {
return (
/^WO[0-9a-zA-Z]{4,}$/i.test(trimmed) ||
/^\d{6,}(-\d{6,})?$/.test(trimmed) ||
/^OD[0-9a-zA-Z]{4,}$/i.test(trimmed)
/^OD[0-9a-zA-Z]{4,}$/i.test(trimmed) ||
/^SUB\d{8,}$/i.test(trimmed) ||
/^INV\d{8,}$/i.test(trimmed) ||
/^AS[0-9a-f]{8,}$/i.test(trimmed) ||
/^DT[0-9a-f]{8,}$/i.test(trimmed)
)
}