Commit Graph
4 Commits
Author SHA1 Message Date
yml2213 90de6b9419 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 项分类器单测。
2026-10-03 14:27:32 +08:00
yml2213 56e718a65d fix(admin): 显式单号检索下,其它筛选不得把正在看的工单从实时补丁里删掉
## 修的是什么

实时补丁的判定链里,「状态 / 接单人员 / 日期范围」这些可证伪条件排在关键词护栏之前,
任一不成立就先返回 mismatch(允许删行),于是第 156 行那条「有关键词就不删行」的护栏
在带筛选时根本执行不到。

d6523635 只在 UI 侧补了一半:搜显式单号时清空 状态/赠送阶段/验收模式,
但**没清接单人员、分类、日期范围**,而 workerId 与 createdRange 同样会先返回 mismatch。
所以「单号 + 接单人员」或「单号 + 日期范围」仍会把行删掉。

实测(真实函数):
  单号 + 状态筛选      -> mismatch(删行)
  单号 + 接单人员筛选  -> mismatch(删行)
  单号 + 日期范围      -> mismatch(删行)

## 修法

只对「显式单号检索」提前短路为 unknown(只原地更新,不插不移):
  if (isExplicitOrderNoSearch(filters.keyword)) return 'unknown'

刻意**不**推翻既有设计:模糊关键词(产品名/昵称等)仍保留「状态可证伪即移除」的语义,
因此原来锁定该语义的用例继续通过。

顺带把 isExplicitOrderNoSearch 合并到 utils:此前面板与 AdminLayout 各有一份规则,
重复两份正是上次 tab 名单漏改那类问题的温床。

## 边界(重要)

**本次修复不覆盖「无任何其它筛选、只按单号搜索」的场景。**
实测该场景下判定本就是 unknown、行不会被删(状态原地更新、分页与统计一致),
因此线上反馈的「状态变化后搜不到、必须整页刷新」若发生在无筛选条件下,另有他因,尚未定位。
2026-10-02 17:51:39 +08:00
yml2213 d262b22a41 fix(admin-worker): 实时补丁同步筛选统计,并锁住"按工单号搜索结果被删行"的回归
线上现象:按工单号搜到的那一单,状态一变(实时 SSE),列表就变成「暂无数据」,而上方
筛选统计仍显示「共 1 单」;整页刷新后才恢复。

成因在前端实时快照补丁:旧逻辑里的 matchesWorkOrderKeyword 只比对「商品名 + 平台单号」,
不比对「工单号」,所以按 WO… 搜出来的行一旦有实时变更就被判成不匹配,直接从缓存里删掉;
而 statistics 是首次响应里带出来的、没被同步,于是出现"统计说有、列表没有"的自相矛盾。
(删行本身已由上一条提交改为:关键词筛选归入「不可证真」→ 只原地更新。)

本条补齐剩下的一致性:
1. 实时补丁增删/原地更新行时,同步维护 data.statistics 的 totalCount 与 statusCounts
   (statusCounts 按运营态口径记账,与后端 getWorkOrderStatistics 一致),列表与统计不再打架;
2. 补两个回归测试:按工单号搜到的行在状态变化后必须原地保留;插入/移除/原地改状态三种
   情况下统计都要跟得上。

验证:前端 80 项单测全绿(新增 2 项),lint / typecheck / build 通过。
2026-10-01 12:35:59 +08:00
yml2213 40ee36d72a fix(consignment): 修复寄卖取消解绑、SUB 单号写空、拼单门禁与后台工单列表实时乱跳
1. 问题单处置「取消」后打手信息不消失:resolveProblemWorkOrder 的取消分支从未解绑寄卖现货,现货永久停在 consumed 占库不还,工单 material 上的寄售标记残留又让列表继续显示「供货打手」。取消时补上解绑;调用必须排在状态 UPDATE 之前——那条 UPDATE 会清空 friend_added_at,清完再解绑会把已确认加好友的货误判成可回池(二次出货);
2. 解绑函数在没有现货可解绑时直接 return,跳过了摘标记,于是「库存行早已不存在」的老帖永远摘不掉寄售标记(列表一直显示供货打手)。现在无论是否解绑到现货都摘标记,痕迹保留在 material.consignmentHistory 的 detachReason 里;
3. 内部现货履约写入的 sourceSubmissionNo 恒为空:取的是占库 `UPDATE ... RETURNING *` 的行,只带回本表列,没有 join 出来的 submission_no,导致工单列表把所有寄卖单都显示成「老单无提报单号」,SUB 单号这个唯一锚点丢失。改取带 join 的预读库存(内部现货资料、指派工单资料、指派事件留痕三处)。存量老单按业务口径不回填;
4. 后台接单工单列表实时乱跳:SSE 快照补丁按数组下标读 queryKey,面板 key 加了字段后下标全错(page 被读成 targetWorkOrderId → 任何页都被当成第 1 页),且完全没判「内部寄售匹配」等筛选,于是任意一条不在当前页的老单只要发生变更就被插到列表顶部。改为结构化 queryKey + 三态判定(match 才允许插顶、mismatch 才允许移除、其余只原地更新),插入还要求 page===1 且工单 id 大于当前首行;状态比对改用 rawStatus(快照的 status 是运营态,拼单母单会被显示成 in_progress),分类筛选因快照不带 parentCategoryId 归入不可证伪;
5. 新增「寄卖只做整单」硬门禁:已有人参与的拼单工单不参与现货匹配与库存提醒,履约入口一律返回 work_order_sharing_not_consignable,前端「手动匹配寄卖」入口按同一口径收敛;开了拼单但无人参与的整单不受影响,避免过度拦截。

验证:后端 520 项单测(517 pass / 3 skip)、前端 78 项全绿;并对真实开发库跑通问题单取消三类场景(老帖无库存行 / 回库 / 已加好友作废)、退回打手与退回大厅的反向用例(不得解绑)、履约写入真实 SUB 单号、拼单门禁四入口 + 未过度拦截反向用例。
2026-10-01 12:17:00 +08:00