Webhook 增加订单号查询
This commit is contained in:
@@ -75,6 +75,7 @@ export async function listWebhookEvents({
|
||||
pageSize = 20,
|
||||
provider = '',
|
||||
platform = '',
|
||||
platformOrderId = '',
|
||||
processed = '',
|
||||
relatedOrderId = '',
|
||||
dateFrom = '',
|
||||
@@ -94,6 +95,11 @@ export async function listWebhookEvents({
|
||||
filters.push(`platform = $${params.length}`)
|
||||
}
|
||||
|
||||
if (platformOrderId) {
|
||||
params.push(`%${platformOrderId}%`)
|
||||
filters.push(`(event_key ILIKE $${params.length} OR body_json::text ILIKE $${params.length})`)
|
||||
}
|
||||
|
||||
if (processed === '0' || processed === '1') {
|
||||
params.push(processed === '1')
|
||||
filters.push(`processed = $${params.length}`)
|
||||
|
||||
@@ -41,6 +41,7 @@ import { nowIso } from '../../utils/time.js'
|
||||
import { reserveInventoryForTask } from '../order/inventory-service.js'
|
||||
import { replayAgisoTradeWebhookEvent } from '../order/webhook-service.js'
|
||||
import { syncConfiguredFulfillmentBindings } from '../bootstrap/fulfillment-bootstrap-service.js'
|
||||
import { extractAgisoTradePayload, resolveAgisoTradePlatformOrderId } from '../order/agiso-trade-parsing.js'
|
||||
import { normalizeProductName } from '../order/product-match-service.js'
|
||||
import { normalizeDateQuery, normalizePage, normalizePageSize, safeParseJson } from './admin-query-utils.js'
|
||||
import { normalizeAdminRole } from './admin-auth-service.js'
|
||||
@@ -493,6 +494,7 @@ export async function getAdminWebhookEvents(query = {}) {
|
||||
pageSize,
|
||||
provider: String(query.provider || '').trim(),
|
||||
platform: String(query.platform || '').trim(),
|
||||
platformOrderId: String(query.platformOrderId || '').trim(),
|
||||
processed: String(query.processed || '').trim(),
|
||||
relatedOrderId: String(query.relatedOrderId || '').trim(),
|
||||
dateFrom: normalizeDateQuery(query.dateFrom),
|
||||
@@ -1404,7 +1406,6 @@ async function mapAdminWebhookEvent(item, { includeRaw = false } = {}) {
|
||||
const body = normalizeRecord(safeParseJson(item.body_json))
|
||||
const payload = extractWebhookPayload(body)
|
||||
const itemSources = extractWebhookItemSources(payload)
|
||||
const firstItem = itemSources[0] || {}
|
||||
const rawAmount = pickFirstNonEmpty([
|
||||
payload.total_fee,
|
||||
payload.totalFee,
|
||||
@@ -1473,18 +1474,7 @@ async function mapAdminWebhookEvent(item, { includeRaw = false } = {}) {
|
||||
processError: item.process_error,
|
||||
relatedOrderId: item.related_order_id,
|
||||
createdAt: item.created_at,
|
||||
platformOrderId: pickFirstNonEmpty([
|
||||
payload.biz_order_id,
|
||||
payload.bizOrderId,
|
||||
payload.Tid,
|
||||
payload.tid,
|
||||
payload.Oid,
|
||||
payload.oid,
|
||||
payload.order_id,
|
||||
payload.orderId,
|
||||
firstItem.Oid,
|
||||
firstItem.oid,
|
||||
]),
|
||||
platformOrderId: resolveAgisoTradePlatformOrderId(payload),
|
||||
buyerId: pickFirstNonEmpty([
|
||||
payload.buyer_id,
|
||||
payload.buyerId,
|
||||
@@ -2238,14 +2228,7 @@ function isPlainObject(value) {
|
||||
}
|
||||
|
||||
function extractWebhookPayload(body) {
|
||||
const normalizedBody = normalizeRecord(body)
|
||||
const rawJson = String(normalizedBody.json || normalizedBody.JSON || '').trim()
|
||||
|
||||
if (rawJson) {
|
||||
return normalizeRecord(safeParseJson(rawJson))
|
||||
}
|
||||
|
||||
return normalizedBody
|
||||
return extractAgisoTradePayload(body)
|
||||
}
|
||||
|
||||
function extractWebhookItemSources(payload) {
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import { parseJsonObject } from '../../utils/json.js'
|
||||
|
||||
export function extractAgisoTradePayload(body) {
|
||||
const normalizedBody = normalizeRecord(body)
|
||||
const rawJson = String(normalizedBody.json || normalizedBody.JSON || '').trim()
|
||||
|
||||
if (rawJson) {
|
||||
return normalizeRecord(parseJsonObject(rawJson, { preserveLargeIntegers: true }))
|
||||
}
|
||||
|
||||
return normalizedBody
|
||||
}
|
||||
|
||||
export function resolveAgisoTradePlatformOrderId(payload) {
|
||||
const normalizedPayload = normalizeRecord(payload)
|
||||
const firstItem = extractAgisoTradeOrderItemSources(normalizedPayload)[0] || {}
|
||||
|
||||
return pickFirstNonEmpty([
|
||||
normalizedPayload.biz_order_id,
|
||||
normalizedPayload.Tid,
|
||||
normalizedPayload.tid,
|
||||
normalizedPayload.Oid,
|
||||
normalizedPayload.oid,
|
||||
normalizedPayload.order_id,
|
||||
normalizedPayload.orderId,
|
||||
firstItem.Oid,
|
||||
firstItem.oid,
|
||||
])
|
||||
}
|
||||
|
||||
export function extractAgisoTradeOrderItemSources(payload) {
|
||||
const normalizedPayload = normalizeRecord(payload)
|
||||
const candidates = [
|
||||
normalizedPayload.items,
|
||||
normalizedPayload.Items,
|
||||
normalizedPayload.orders,
|
||||
normalizedPayload.Orders,
|
||||
normalizedPayload.order_list,
|
||||
normalizedPayload.OrderList,
|
||||
]
|
||||
|
||||
for (const current of candidates) {
|
||||
if (Array.isArray(current) && current.length > 0) {
|
||||
return current.map((item) => normalizeRecord(item)).filter((item) => Object.keys(item).length > 0)
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(normalizedPayload).length > 0) {
|
||||
return [normalizedPayload]
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
function normalizeRecord(value) {
|
||||
return value && typeof value === 'object' && !Array.isArray(value) ? value : {}
|
||||
}
|
||||
|
||||
function pickFirstNonEmpty(values) {
|
||||
for (const value of values) {
|
||||
if (typeof value === 'string' && value.trim()) {
|
||||
return value.trim()
|
||||
}
|
||||
|
||||
if (typeof value === 'number' && Number.isFinite(value)) {
|
||||
return String(value)
|
||||
}
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
|
||||
import { extractAgisoTradePayload, resolveAgisoTradePlatformOrderId } from './agiso-trade-parsing.js'
|
||||
|
||||
test('extractAgisoTradePayload preserves large integer order ids from raw json', () => {
|
||||
const payload = extractAgisoTradePayload({
|
||||
json: '{"biz_order_id":4502259793206016214,"item_id":978102431355,"order_status":4}',
|
||||
})
|
||||
|
||||
assert.equal(payload.biz_order_id, '4502259793206016214')
|
||||
assert.equal(typeof payload.biz_order_id, 'string')
|
||||
})
|
||||
|
||||
test('resolveAgisoTradePlatformOrderId keeps exact webhook order id', () => {
|
||||
const payload = extractAgisoTradePayload({
|
||||
json: '{"biz_order_id":4502259793206016214,"item_id":978102431355,"order_status":4}',
|
||||
})
|
||||
|
||||
assert.equal(resolveAgisoTradePlatformOrderId(payload), '4502259793206016214')
|
||||
})
|
||||
|
||||
test('resolveAgisoTradePlatformOrderId falls back to tid when biz_order_id is absent', () => {
|
||||
assert.equal(
|
||||
resolveAgisoTradePlatformOrderId({
|
||||
tid: '4502251008019011224',
|
||||
orders: [{ oid: '4502251008019011888' }],
|
||||
}),
|
||||
'4502251008019011224',
|
||||
)
|
||||
})
|
||||
@@ -3,6 +3,10 @@ import crypto from 'node:crypto'
|
||||
import { runtimeConfig } from '../../config/runtime.js'
|
||||
import { createWebhookEvent, updateWebhookEvent } from '../../repositories/webhook-event-repo.js'
|
||||
import { enrichAgisoXianyuTradeOrder } from '../platforms/agiso/xianyu/order-detail-service.js'
|
||||
import {
|
||||
extractAgisoTradePayload,
|
||||
resolveAgisoTradePlatformOrderId,
|
||||
} from './agiso-trade-parsing.js'
|
||||
import { upsertOrderFromWebhook } from './order-service.js'
|
||||
import { hasConfiguredOrderItems } from './product-match-service.js'
|
||||
import { createHttpError } from '../../utils/http.js'
|
||||
@@ -305,7 +309,7 @@ function parseAgisoTradeRequest(requestLike) {
|
||||
const query = normalizeRecord(requestLike.query)
|
||||
const body = normalizeRecord(requestLike.body)
|
||||
const rawJson = String(body.json || '').trim()
|
||||
const payload = rawJson ? parsePayloadJson(rawJson) : body
|
||||
const payload = rawJson ? parsePayloadJson(rawJson) : extractAgisoTradePayload(body)
|
||||
const timestamp = String(query.timestamp || '').trim()
|
||||
const sign = String(query.sign || '').trim().toLowerCase()
|
||||
const provider = 'agiso'
|
||||
@@ -323,19 +327,7 @@ function parseAgisoTradeRequest(requestLike) {
|
||||
const shop = resolveShop(payload)
|
||||
const eventType = resolveEventType(query.aopic, payload)
|
||||
const signatureValid = verifyAgisoSignature({ rawJson, timestamp, sign })
|
||||
const itemSources = extractOrderItemSources(payload)
|
||||
const firstItem = itemSources[0] || {}
|
||||
const platformOrderId = pickFirstNonEmpty([
|
||||
payload.biz_order_id,
|
||||
payload.Tid,
|
||||
payload.tid,
|
||||
payload.Oid,
|
||||
payload.oid,
|
||||
payload.order_id,
|
||||
payload.orderId,
|
||||
firstItem.Oid,
|
||||
firstItem.oid,
|
||||
])
|
||||
const platformOrderId = resolveAgisoTradePlatformOrderId(payload)
|
||||
|
||||
return {
|
||||
provider,
|
||||
@@ -663,25 +655,6 @@ function normalizeOrderItems(payload) {
|
||||
})
|
||||
}
|
||||
|
||||
function extractOrderItemSources(payload) {
|
||||
const candidates = [
|
||||
payload.items,
|
||||
payload.Items,
|
||||
payload.orders,
|
||||
payload.Orders,
|
||||
payload.order_list,
|
||||
payload.OrderList,
|
||||
]
|
||||
|
||||
for (const current of candidates) {
|
||||
if (Array.isArray(current) && current.length > 0) {
|
||||
return current
|
||||
}
|
||||
}
|
||||
|
||||
return [payload]
|
||||
}
|
||||
|
||||
function resolveBusinessPlatform(rawPlatform) {
|
||||
const normalized = normalizePlatformKey(rawPlatform)
|
||||
|
||||
@@ -910,6 +883,10 @@ function isPlainObject(value) {
|
||||
}
|
||||
|
||||
function safeParseJson(rawValue) {
|
||||
if (rawValue && typeof rawValue === 'object' && !Array.isArray(rawValue)) {
|
||||
return rawValue
|
||||
}
|
||||
|
||||
const normalized = String(rawValue || '').trim()
|
||||
|
||||
if (!normalized) {
|
||||
|
||||
@@ -17,6 +17,7 @@ const errorMessage = ref('')
|
||||
const items = ref<AdminWebhookEventListItem[]>([])
|
||||
const provider = ref('')
|
||||
const platform = ref('')
|
||||
const platformOrderId = ref('')
|
||||
const processed = ref('')
|
||||
const relatedOrderId = ref('')
|
||||
const dateFrom = ref('')
|
||||
@@ -37,6 +38,7 @@ async function loadEvents(page = pagination.value.page) {
|
||||
pageSize: pagination.value.pageSize,
|
||||
provider: provider.value.trim(),
|
||||
platform: platform.value.trim(),
|
||||
platformOrderId: platformOrderId.value.trim(),
|
||||
processed: processed.value.trim(),
|
||||
relatedOrderId: relatedOrderId.value.trim(),
|
||||
dateFrom: dateFrom.value.trim(),
|
||||
@@ -114,10 +116,11 @@ onMounted(loadEvents)
|
||||
{{ option.label }}
|
||||
</option>
|
||||
</select>
|
||||
<input v-model="relatedOrderId" class="text-input" placeholder="关联订单 ID" />
|
||||
<input v-model="platformOrderId" class="text-input" placeholder="平台订单号" />
|
||||
</section>
|
||||
|
||||
<section class="filter-bar">
|
||||
<input v-model="relatedOrderId" class="text-input" placeholder="关联订单 ID" />
|
||||
<input v-model="dateFrom" class="text-input" type="date" placeholder="开始日期" />
|
||||
<input v-model="dateTo" class="text-input" type="date" placeholder="结束日期" />
|
||||
<el-button round type="primary" @click="() => loadEvents()">查询</el-button>
|
||||
|
||||
Reference in New Issue
Block a user