fix:修复问题接单平台使用游戏ID查询不到数据

This commit is contained in:
2026-10-22 15:32:26 +08:00
parent 35259bd643
commit 72a758fd7c
3 changed files with 16 additions and 6 deletions
@@ -139,17 +139,20 @@ test('空白搜索词不生成全表 LIKE 条件', () => {
assert.deepEqual(query.params, []) assert.deepEqual(query.params, [])
}) })
test('平台单号检索只生成可走索引的等值/前缀条件,不得出现前置通配符', () => { test('纯数字检索同时覆盖平台单号与游戏字段,且不出现非索引通配符', () => {
const query = buildWorkOrderWhere({ keyword: '2627401680611492' }) const query = buildWorkOrderWhere({ keyword: '2627401680611492' })
// 纯数字既可能是平台单号,也可能是游戏 ID:等值/前缀单号分支必须保留。
assert.match( assert.match(
query.whereClause, query.whereClause,
/\(wo\.platform_order_id = \$1 OR wo\.platform_order_id LIKE \$1 \|\| '%'\)/, /\(wo\.platform_order_id = \$1 OR wo\.platform_order_id LIKE \$1 \|\| '%'\)/,
) )
// 前置通配符非 sargable:一旦混进来,整条查询会退化成 work_orders 全表扫, // 游戏 ID(快手等 10 位纯数字)也走同一关键词:必须并上统一搜索表达式。
// 生产实测每次读 211MB / 398ms,列表与统计并行触发一次打两遍。 assert.match(query.whereClause, /material_json->'collect'->'fields'->>'gameId'/)
assert.match(query.whereClause, /LIKE LOWER\(\$2\)/)
// 不允许 `platform_order_id ILIKE '%...%'` 这种非 sargable 写法;游戏字段走 073 的 trgm 表达式索引。
assert.doesNotMatch(query.whereClause, /ILIKE\s*'%'/) assert.doesNotMatch(query.whereClause, /ILIKE\s*'%'/)
assert.deepEqual(query.params, ['2627401680611492']) assert.deepEqual(query.params, ['2627401680611492', '%2627401680611492%'])
}) })
test('工单号检索大小写不敏感且只走索引,不得出现前置通配符(含 W0/WK 前缀容错)', () => { test('工单号检索大小写不敏感且只走索引,不得出现前置通配符(含 W0/WK 前缀容错)', () => {
@@ -1271,8 +1271,15 @@ export function buildWorkOrderWhere({
if (isPlatformOrderIdLookupKeyword(normalizedKeyword)) { if (isPlatformOrderIdLookupKeyword(normalizedKeyword)) {
params.push(normalizedKeyword) params.push(normalizedKeyword)
const paramIndex = `$${params.length}` const paramIndex = `$${params.length}`
// 纯数字关键词不能只当平台单号查:游戏 ID(快手等普遍 10 位左右纯数字)同样是纯数字,
// 只匹配 platform_order_id 会让「按游戏 ID 搜索」永远空结果(生产已复现)。
// 这里再并上统一搜索表达式(含各版本 material 里的 gameId / 游戏昵称 / 账号),
// 该表达式的 trigram GIN 索引(073)让这一支走 Bitmap Index Scan,
// 15.6 万行实测约 8ms,不会退化成全表扫。
params.push(`%${normalizedKeyword}%`)
const keywordParam = `$${params.length}`
filters.push( filters.push(
`(wo.platform_order_id = ${paramIndex} OR wo.platform_order_id LIKE ${paramIndex} || '%')`, `((wo.platform_order_id = ${paramIndex} OR wo.platform_order_id LIKE ${paramIndex} || '%') OR ${workOrderSearchExpression()} LIKE LOWER(${keywordParam}))`,
) )
} else if (isWorkOrderNoLookupKeyword(normalizedKeyword)) { } else if (isWorkOrderNoLookupKeyword(normalizedKeyword)) {
// 工单号同时匹配 `WK…`(现役)/`WO…`(历史)/`W0…`(把 O 看成 0)三种写法: // 工单号同时匹配 `WK…`(现役)/`WO…`(历史)/`W0…`(把 O 看成 0)三种写法:
@@ -1150,7 +1150,7 @@ export default function WorkOrdersPanel() {
cleared.push('定位工单') cleared.push('定位工单')
} }
if (cleared.length > 0) { if (cleared.length > 0) {
message.info(`已按单号精准检索,自动忽略:${cleared.join(' / ')}`) message.info(`已按单号 / 游戏 ID 精准检索,自动忽略:${cleared.join(' / ')}`)
} }
} }