完成 admin read helper 领域拆分
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
// @ts-check
|
||||
|
||||
import { listTasksByOrderId } from '../../repositories/task-repo.js'
|
||||
import { formatFenToAmount, parseAmountToFen } from '../../utils/money.js'
|
||||
import { extractAgisoTradePayload, resolveAgisoTradePlatformOrderId } from '../order/agiso-trade-parsing.js'
|
||||
import { safeParseJson } from './admin-query-utils.js'
|
||||
import { resolveDisplayShopName } from './admin-read-shared-helpers.js'
|
||||
|
||||
/** @typedef {import('../../types/admin-read-models.js').AdminWebhookEventListItem} AdminWebhookEventListItem */
|
||||
/** @typedef {import('../../types/repository-rows.js').WebhookEventRow} WebhookEventRow */
|
||||
|
||||
/**
|
||||
* @param {WebhookEventRow} item
|
||||
* @returns {Promise<AdminWebhookEventListItem>}
|
||||
*/
|
||||
export async function mapAdminWebhookEvent(item, { includeRaw = false } = {}) {
|
||||
const headers = normalizeRecord(safeParseJson(item.headers_json))
|
||||
const query = normalizeRecord(safeParseJson(item.query_json))
|
||||
const body = normalizeRecord(safeParseJson(item.body_json))
|
||||
const payload = extractWebhookPayload(body)
|
||||
const itemSources = extractWebhookItemSources(payload)
|
||||
const rawAmount = pickFirstNonEmpty([
|
||||
payload.total_fee,
|
||||
payload.totalFee,
|
||||
payload.TotalFee,
|
||||
payload.pay_fee,
|
||||
payload.payFee,
|
||||
payload.Payment,
|
||||
payload.payment,
|
||||
payload.buy_amount,
|
||||
payload.buyAmount,
|
||||
])
|
||||
const totalAmountFen = parseAmountToFen(rawAmount)
|
||||
const taskCount = item.related_order_id ? (await listTasksByOrderId(item.related_order_id)).length : 0
|
||||
const shopId = item.shop_id || pickFirstNonEmpty([
|
||||
payload.PlatformUserId,
|
||||
payload.platformUserId,
|
||||
payload.platform_user_id,
|
||||
payload.seller_id,
|
||||
payload.sellerId,
|
||||
payload.SellerId,
|
||||
payload.seller_nick,
|
||||
payload.sellerNick,
|
||||
payload.SellerNick,
|
||||
])
|
||||
|
||||
const mapped = {
|
||||
eventId: item.id,
|
||||
provider: item.provider || 'agiso',
|
||||
platform: item.platform,
|
||||
platformRaw: pickFirstNonEmpty([
|
||||
query.fromPlatform,
|
||||
query.from_platform,
|
||||
body.fromPlatform,
|
||||
body.from_platform,
|
||||
payload.fromPlatform,
|
||||
payload.FromPlatform,
|
||||
payload.platform,
|
||||
payload.Platform,
|
||||
]),
|
||||
shopId,
|
||||
shopName: resolveDisplayShopName(
|
||||
item.provider,
|
||||
shopId,
|
||||
item.shop_name || pickFirstNonEmpty([
|
||||
payload.shopName,
|
||||
payload.shop_name,
|
||||
payload.ShopName,
|
||||
payload.seller_nick,
|
||||
payload.sellerNick,
|
||||
payload.SellerNick,
|
||||
]),
|
||||
),
|
||||
eventType: item.event_type,
|
||||
eventKey: item.event_key,
|
||||
signatureValid: Boolean(item.signature_valid),
|
||||
processed: Boolean(item.processed),
|
||||
processError: item.process_error,
|
||||
relatedOrderId: item.related_order_id,
|
||||
createdAt: item.created_at,
|
||||
platformOrderId: resolveAgisoTradePlatformOrderId(payload),
|
||||
buyerId: pickFirstNonEmpty([
|
||||
payload.buyer_id,
|
||||
payload.buyerId,
|
||||
payload.BuyerId,
|
||||
payload.openid,
|
||||
payload.BuyerOpenUid,
|
||||
payload.buyer_open_uid,
|
||||
payload.buyerOpenUid,
|
||||
]),
|
||||
buyerName: pickFirstNonEmpty([
|
||||
payload.buyer_name,
|
||||
payload.buyerName,
|
||||
payload.BuyerName,
|
||||
payload.nick,
|
||||
payload.BuyerNick,
|
||||
payload.buyer_nick,
|
||||
payload.buyerNick,
|
||||
]),
|
||||
totalAmount: rawAmount ? formatFenToAmount(totalAmountFen) : '',
|
||||
totalAmountFen,
|
||||
currency: pickFirstNonEmpty([payload.currency, payload.Currency, 'CNY']) || 'CNY',
|
||||
itemCount: itemSources.length,
|
||||
taskCount,
|
||||
messageText: pickFirstNonEmpty([
|
||||
payload.MsgTypeDes,
|
||||
payload.msgTypeDes,
|
||||
payload.msg_type_desc,
|
||||
payload.message,
|
||||
payload.msg,
|
||||
]),
|
||||
requestTimestamp: pickFirstNonEmpty([query.timestamp]),
|
||||
requestSign: pickFirstNonEmpty([query.sign]),
|
||||
aopic: pickFirstNonEmpty([query.aopic]),
|
||||
sourceHost: pickFirstNonEmpty([headers['x-forwarded-host'], headers.host]),
|
||||
sourceIp: pickFirstNonEmpty([headers['x-forwarded-for'], headers['x-real-ip']]),
|
||||
userAgent: pickFirstNonEmpty([headers['user-agent'], headers['User-Agent']]),
|
||||
}
|
||||
|
||||
if (!includeRaw) {
|
||||
return mapped
|
||||
}
|
||||
|
||||
return {
|
||||
...mapped,
|
||||
headers,
|
||||
query,
|
||||
body,
|
||||
payload,
|
||||
}
|
||||
}
|
||||
|
||||
function extractWebhookPayload(body) {
|
||||
return extractAgisoTradePayload(body)
|
||||
}
|
||||
|
||||
function extractWebhookItemSources(payload) {
|
||||
const normalizedPayload = normalizeRecord(payload)
|
||||
const candidates = [
|
||||
normalizedPayload.orders,
|
||||
normalizedPayload.Orders,
|
||||
normalizedPayload.items,
|
||||
normalizedPayload.Items,
|
||||
normalizedPayload.order_items,
|
||||
]
|
||||
|
||||
for (const candidate of candidates) {
|
||||
if (Array.isArray(candidate) && candidate.length > 0) {
|
||||
return candidate.map((item) => normalizeRecord(item)).filter((item) => Object.keys(item).length > 0)
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
pickFirstNonEmpty([
|
||||
normalizedPayload.item_id,
|
||||
normalizedPayload.itemId,
|
||||
normalizedPayload.ItemId,
|
||||
normalizedPayload.num_iid,
|
||||
normalizedPayload.numIid,
|
||||
normalizedPayload.NumIid,
|
||||
])
|
||||
) {
|
||||
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 ''
|
||||
}
|
||||
Reference in New Issue
Block a user