fix(fulfillment): 修复自动发货任务上下文并发覆盖
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
|
||||
import type { JsonObject } from '../../../types/json.js'
|
||||
import type { KuaishouIndustryVoucherRow, TaskRow } from '../../../types/repository/rows.js'
|
||||
import { attachKuaishouIndustryVoucherToTask } from './voucher-binding-service.js'
|
||||
|
||||
test('电子凭证绑定只原子合并自己的 context key,不覆盖 affiliateDash', async () => {
|
||||
const task = {
|
||||
id: 120509,
|
||||
context_json: {
|
||||
affiliateDash: {
|
||||
orderNo: 'FO2026110313080188725b91e99',
|
||||
clientOrderNo: '2630702009068141',
|
||||
},
|
||||
},
|
||||
} as unknown as TaskRow
|
||||
const voucher = {
|
||||
oid: '2630702009068141',
|
||||
voucher_code: 'KSV4YQWP6SBEQFRBCBD',
|
||||
token: 'token',
|
||||
status: 'CONSUMED',
|
||||
unit_index: 1,
|
||||
send_callback_status: 'success',
|
||||
send_callback_attempt_count: 1,
|
||||
} as unknown as KuaishouIndustryVoucherRow
|
||||
let capturedTaskId = 0
|
||||
let capturedPatch: JsonObject | null = null
|
||||
let capturedUpdatedAt = ''
|
||||
|
||||
const merged = await attachKuaishouIndustryVoucherToTask(
|
||||
task,
|
||||
voucher,
|
||||
{ now: '2026-11-03T13:08:01.383Z' },
|
||||
{
|
||||
mergeTaskContext: async (taskId, patch, updatedAt) => {
|
||||
capturedTaskId = Number(taskId)
|
||||
capturedPatch = patch
|
||||
capturedUpdatedAt = String(updatedAt || '')
|
||||
return task
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
assert.equal(merged, task)
|
||||
assert.equal(capturedTaskId, 120509)
|
||||
assert.equal(capturedUpdatedAt, '2026-11-03T13:08:01.383Z')
|
||||
assert.deepEqual(Object.keys(capturedPatch || {}), ['kuaishouIndustryVoucher'])
|
||||
assert.equal(
|
||||
(capturedPatch?.kuaishouIndustryVoucher as Record<string, unknown> | undefined)?.oid,
|
||||
'2630702009068141',
|
||||
)
|
||||
assert.equal('affiliateDash' in (capturedPatch || {}), false)
|
||||
})
|
||||
@@ -1,11 +1,10 @@
|
||||
import { updateTask } from '../../../repositories/task-repo.js'
|
||||
import { mergeTaskContext } from '../../../repositories/task-repo.js'
|
||||
import type { JsonObject } from '../../../types/json.js'
|
||||
import {
|
||||
listKuaishouIndustryVouchersByOid,
|
||||
updateKuaishouIndustryVoucherByCode,
|
||||
} from '../../../repositories/kuaishou-industry-voucher-repo.js'
|
||||
import { normalizeTimestampIso } from '../../../utils/time.js'
|
||||
import { parseTaskContext } from '../../../utils/task-json.js'
|
||||
import {
|
||||
isKuaishouIndustryVoucherSendCallbackSuccess,
|
||||
normalizeKuaishouIndustrySendCallbackStatus,
|
||||
@@ -17,6 +16,10 @@ import type {
|
||||
TaskRow,
|
||||
} from '../../../types/repository/rows.js'
|
||||
|
||||
type VoucherBindingDeps = {
|
||||
mergeTaskContext?: typeof mergeTaskContext
|
||||
}
|
||||
|
||||
export async function bindKuaishouIndustryVouchersToOrderTasks(
|
||||
order: OrderRow | null | undefined,
|
||||
tasks: TaskRow[] = [],
|
||||
@@ -62,27 +65,17 @@ export async function attachKuaishouIndustryVoucherToTask(
|
||||
source?: string
|
||||
now?: string
|
||||
} = {},
|
||||
deps: VoucherBindingDeps = {},
|
||||
) {
|
||||
const now = normalizeTimestampIso(options.now || new Date().toISOString())
|
||||
const context = parseTaskContext(task)
|
||||
const nextVoucherContext = buildKuaishouIndustryVoucherContext(
|
||||
voucher,
|
||||
context.kuaishouIndustryVoucher,
|
||||
const nextVoucherContext = buildKuaishouIndustryVoucherContext(voucher, null, now)
|
||||
const mergeContext = deps.mergeTaskContext || mergeTaskContext
|
||||
// 只原子替换本模块负责的顶层 key,禁止用旧 TaskRow 整段覆盖 affiliateDash。
|
||||
return mergeContext(
|
||||
task.id,
|
||||
{ kuaishouIndustryVoucher: nextVoucherContext } satisfies JsonObject,
|
||||
now,
|
||||
)
|
||||
const nextContext: JsonObject = {
|
||||
...context,
|
||||
kuaishouIndustryVoucher: nextVoucherContext,
|
||||
}
|
||||
|
||||
if (JSON.stringify(context) === JSON.stringify(nextContext)) {
|
||||
return task
|
||||
}
|
||||
|
||||
return updateTask(task.id, {
|
||||
context_json: JSON.stringify(nextContext),
|
||||
updated_at: now,
|
||||
})
|
||||
}
|
||||
|
||||
function resolveTaskForVoucher(voucher: KuaishouIndustryVoucherRow, tasks: TaskRow[]) {
|
||||
|
||||
Reference in New Issue
Block a user