这是后台那个"搜工单号搜不到、必须刷新整页"在打手端的同源副本: WorkerLayout 的 operation = event.operation === 'remove' || !matches ? 'remove' : 'upsert' 只要前端判"不匹配"就删行,而前端关键词匹配器**没有比对工单号**—— 后端搜索表达式的第一项恰恰就是 work_order_no(还有 material_json 里更多前端不知道的字段)。 于是按工单号搜索时,该单一旦有实时变更,行就被从缓存删掉、列表变空。 修法与后台侧对齐: 1. 抽出 utils/worker-realtime-work-orders.ts(可单测、无运行时依赖),关键词匹配补上 workOrderNo,并改成三态判定——状态/分类等可证伪维度不符才删行;关键词能找到=match (允许插入),找不到=unknown(不删不插,只就地更新)。 2. patchWorkerWorkOrderListEnvelope 增删行时同步 data.pagination.total, 修掉"列表空了、分页器还显示有 N 条"。 3. 大厅无限下拉只在第 1 页允许插入,其余页只就地更新。 验证:前端 86 项单测全绿(新增 6 项,覆盖"按工单号搜索+实时变更不得删行"、 unknown 只就地更新、删/插同步总数、无限下拉页边界)。
119 lines
4.6 KiB
TypeScript
119 lines
4.6 KiB
TypeScript
import assert from 'node:assert/strict'
|
|
import test from 'node:test'
|
|
|
|
import {
|
|
evaluateWorkerKeywordVerdict,
|
|
matchesWorkerWorkOrderKeyword,
|
|
patchWorkerWorkOrderInfiniteEnvelope,
|
|
patchWorkerWorkOrderListEnvelope,
|
|
} from '../src/utils/worker-realtime-work-orders.ts'
|
|
|
|
function buildRow(overrides: Record<string, unknown> = {}) {
|
|
return {
|
|
workOrderId: 9001,
|
|
workOrderNo: 'WO26093017345168',
|
|
status: 'in_progress',
|
|
productName: '某游戏代练',
|
|
platformOrderId: '2609301734',
|
|
material: { collect: { fields: { gameId: 'g-1', gameNickname: '小明' } } },
|
|
...overrides,
|
|
}
|
|
}
|
|
|
|
function buildEnvelope(items: unknown[], total = items.length) {
|
|
return { ok: true, data: { items, pagination: { page: 1, pageSize: 20, total } } }
|
|
}
|
|
|
|
function itemsOf(envelope: unknown): Array<{ workOrderId: number }> {
|
|
return (envelope as { data: { items: Array<{ workOrderId: number }> } }).data.items
|
|
}
|
|
|
|
function totalOf(envelope: unknown): number {
|
|
return (envelope as { data: { pagination: { total: number } } }).data.pagination.total
|
|
}
|
|
|
|
test('按工单号搜索必须能命中(后端搜索串第一项就是工单号,前端曾完全没比对它)', () => {
|
|
assert.equal(matchesWorkerWorkOrderKeyword(buildRow(), 'WO26093017345168'), true)
|
|
assert.equal(matchesWorkerWorkOrderKeyword(buildRow(), 'wo26093017'), true)
|
|
// 原有字段仍然要能命中
|
|
assert.equal(matchesWorkerWorkOrderKeyword(buildRow(), '小明'), true)
|
|
assert.equal(matchesWorkerWorkOrderKeyword(buildRow(), '2609301734'), true)
|
|
})
|
|
|
|
test('关键词维度:找不到只能是 unknown,绝不能当成"不匹配"(这才是行被删空的根因)', () => {
|
|
const row = buildRow()
|
|
assert.equal(evaluateWorkerKeywordVerdict(row, 'WO26093017345168'), 'match')
|
|
assert.equal(evaluateWorkerKeywordVerdict(row, ''), 'match')
|
|
// 后端还能搜 material_json 里前端不知道的字段,所以找不到 ≠ 不匹配
|
|
assert.equal(evaluateWorkerKeywordVerdict(row, '后端才知道的字段'), 'unknown')
|
|
})
|
|
|
|
test('按工单号搜到的行,实时事件来了也不能被删掉,并且总数要跟着走', () => {
|
|
const row = buildRow({ status: 'pending_material' })
|
|
const envelope = buildEnvelope([row])
|
|
const snapshot = buildRow({ status: 'in_progress' })
|
|
|
|
// 关键词命中 → upsert,原地更新,行数不变
|
|
const updated = patchWorkerWorkOrderListEnvelope(
|
|
envelope,
|
|
{ operation: 'upsert', data: snapshot },
|
|
false,
|
|
)
|
|
assert.equal(itemsOf(updated).length, 1)
|
|
assert.equal(totalOf(updated), 1)
|
|
assert.equal((itemsOf(updated)[0] as { status?: string }).status, 'in_progress')
|
|
})
|
|
|
|
test('不可证真的关键词下,快照只能就地更新:不插入、不删除', () => {
|
|
const envelope = buildEnvelope([buildRow()])
|
|
// 关键词没命中前端已知字段(unknown):既不该把它当"不匹配"删掉
|
|
const patched = patchWorkerWorkOrderListEnvelope(
|
|
envelope,
|
|
{ operation: 'upsert', data: buildRow({ status: 'accepted' }) },
|
|
false,
|
|
)
|
|
assert.equal(itemsOf(patched).length, 1)
|
|
// 也不该把不在列表里的行插进来(留着让后端说了算)
|
|
const notInserted = patchWorkerWorkOrderListEnvelope(
|
|
envelope,
|
|
{ operation: 'upsert', data: buildRow({ workOrderId: 8888 }) },
|
|
false,
|
|
)
|
|
assert.equal(itemsOf(notInserted).length, 1)
|
|
})
|
|
|
|
test('真正移出可见范围时要删行,并同步分页总数(旧实现只删 items,分页器还显示有 N 条)', () => {
|
|
const envelope = buildEnvelope([buildRow(), buildRow({ workOrderId: 9002 })], 2)
|
|
const removed = patchWorkerWorkOrderListEnvelope(
|
|
envelope,
|
|
{ operation: 'remove', data: buildRow() },
|
|
false,
|
|
)
|
|
assert.deepEqual(
|
|
itemsOf(removed).map((item) => item.workOrderId),
|
|
[9002],
|
|
)
|
|
assert.equal(totalOf(removed), 1, '删行后总数必须减一')
|
|
|
|
const inserted = patchWorkerWorkOrderListEnvelope(
|
|
buildEnvelope([buildRow()], 1),
|
|
{ operation: 'upsert', data: buildRow({ workOrderId: 9003 }) },
|
|
true,
|
|
)
|
|
assert.equal(itemsOf(inserted).length, 2)
|
|
assert.equal(totalOf(inserted), 2, '插行后总数必须加一')
|
|
})
|
|
|
|
test('无限下拉:只有第 1 页允许插入,其余页只就地更新', () => {
|
|
const page = (ids: number[]) =>
|
|
buildEnvelope(ids.map((id) => buildRow({ workOrderId: id, status: 'open' })))
|
|
const current = { pages: [page([1, 2]), page([3, 4])], pageParams: [1, 2] }
|
|
const patched = patchWorkerWorkOrderInfiniteEnvelope(
|
|
current,
|
|
{ operation: 'upsert', data: buildRow({ workOrderId: 99, status: 'open' }) },
|
|
true,
|
|
) as { pages: unknown[] }
|
|
assert.equal(itemsOf(patched.pages[0]).length, 3, '第 1 页应插入')
|
|
assert.equal(itemsOf(patched.pages[1]).length, 2, '第 2 页不得插入')
|
|
})
|