修正行业凭证发货回调金额参数
This commit is contained in:
@@ -23,7 +23,6 @@ type SendCallbackInput = {
|
||||
totalGoodsValue?: number
|
||||
token: string
|
||||
eticketType?: string
|
||||
ext?: string
|
||||
expressCode?: string
|
||||
expressNo?: string
|
||||
}
|
||||
@@ -77,7 +76,6 @@ export async function sendCallback(input: SendCallbackInput): Promise<{ success:
|
||||
|
||||
if (input.totalGoodsValue != null) bizParams.totalGoodsValue = input.totalGoodsValue
|
||||
if (input.eticketType) bizParams.eticketType = input.eticketType
|
||||
if (input.ext) bizParams.ext = input.ext
|
||||
if (input.expressCode) bizParams.expressCode = input.expressCode
|
||||
if (input.expressNo) bizParams.expressNo = input.expressNo
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
|
||||
import {
|
||||
resolveSendCallbackGoodsValuePlan,
|
||||
resolveSendCallbackPreferredTotalGoodsValue,
|
||||
} from './send-code-service.js'
|
||||
|
||||
test('resolveSendCallbackPreferredTotalGoodsValue converts 91 maxAmount yuan to fen', () => {
|
||||
const total = resolveSendCallbackPreferredTotalGoodsValue({
|
||||
raw_payload_json: JSON.stringify({
|
||||
body: {
|
||||
maxAmount: '0.01',
|
||||
},
|
||||
}),
|
||||
total_amount: 100,
|
||||
} as any)
|
||||
|
||||
assert.equal(total, 1)
|
||||
})
|
||||
|
||||
test('resolveSendCallbackPreferredTotalGoodsValue falls back to order total amount', () => {
|
||||
const total = resolveSendCallbackPreferredTotalGoodsValue({
|
||||
raw_payload_json: '{}',
|
||||
total_amount: 66,
|
||||
} as any)
|
||||
|
||||
assert.equal(total, 66)
|
||||
})
|
||||
|
||||
test('resolveSendCallbackGoodsValuePlan prefers 91 amount over ext payment', () => {
|
||||
const plan = resolveSendCallbackGoodsValuePlan(
|
||||
[{}],
|
||||
'{"payment":100,"platformBearAmount":0}',
|
||||
1,
|
||||
)
|
||||
|
||||
assert.deepEqual(plan.goodsValues, [1])
|
||||
assert.equal(plan.totalGoodsValue, 1)
|
||||
assert.equal(plan.source, 'preferred')
|
||||
})
|
||||
|
||||
test('resolveSendCallbackGoodsValuePlan splits preferred amount and keeps sum equal to total', () => {
|
||||
const plan = resolveSendCallbackGoodsValuePlan(
|
||||
[{}, {}, {}],
|
||||
'{"payment":999}',
|
||||
100,
|
||||
)
|
||||
|
||||
assert.deepEqual(plan.goodsValues, [33, 33, 34])
|
||||
assert.equal(plan.totalGoodsValue, 100)
|
||||
assert.equal(plan.source, 'preferred')
|
||||
})
|
||||
|
||||
test('resolveSendCallbackGoodsValuePlan uses ext payment for a single voucher', () => {
|
||||
const plan = resolveSendCallbackGoodsValuePlan(
|
||||
[{}],
|
||||
'{"payment":1,"platformBearAmount":0}',
|
||||
)
|
||||
|
||||
assert.deepEqual(plan.goodsValues, [1])
|
||||
assert.equal(plan.totalGoodsValue, 1)
|
||||
})
|
||||
|
||||
test('resolveSendCallbackGoodsValuePlan splits ext payment and keeps sum equal to total', () => {
|
||||
const plan = resolveSendCallbackGoodsValuePlan(
|
||||
[{}, {}, {}],
|
||||
'{"payment":100}',
|
||||
)
|
||||
|
||||
assert.deepEqual(plan.goodsValues, [33, 33, 34])
|
||||
assert.equal(plan.totalGoodsValue, 100)
|
||||
})
|
||||
|
||||
test('resolveSendCallbackGoodsValuePlan falls back to one weight per voucher', () => {
|
||||
const plan = resolveSendCallbackGoodsValuePlan([{}, {}], '')
|
||||
|
||||
assert.deepEqual(plan.goodsValues, [1, 1])
|
||||
assert.equal(plan.totalGoodsValue, 2)
|
||||
})
|
||||
@@ -21,6 +21,8 @@ import {
|
||||
resolveKuaishouIndustryVoucherValidity,
|
||||
} from './voucher-service.js'
|
||||
import { bindKuaishouIndustryVouchersToOrderTasks } from './voucher-binding-service.js'
|
||||
import { parseAmountToFen } from '../../../utils/money.js'
|
||||
import type { OrderRow } from '../../../types/repository/rows.js'
|
||||
|
||||
type JsonObject = Record<string, any>
|
||||
|
||||
@@ -100,6 +102,7 @@ export async function handleSendCode(rawBody: JsonObject = {}) {
|
||||
token: params.token,
|
||||
eticketType: params.eticketType,
|
||||
ext: params.ext,
|
||||
preferredTotalGoodsValue: resolveSendCallbackPreferredTotalGoodsValue(order),
|
||||
})
|
||||
|
||||
return response
|
||||
@@ -112,6 +115,7 @@ function fireSendCallback(input: {
|
||||
token: string
|
||||
eticketType?: string
|
||||
ext?: string
|
||||
preferredTotalGoodsValue?: number
|
||||
}) {
|
||||
const eticketItems = input.etickets.map((e) => ({
|
||||
id: String(e.id || ''),
|
||||
@@ -119,29 +123,38 @@ function fireSendCallback(input: {
|
||||
num: Number(e.num) || 1,
|
||||
validStartTime: Number(e.validStartTime) || 0,
|
||||
validEndTime: Number(e.validEndTime) || 0,
|
||||
goodsValue: Number(e.goodsValue) || 0,
|
||||
}))
|
||||
const goodsValuePlan = resolveSendCallbackGoodsValuePlan(
|
||||
eticketItems,
|
||||
input.ext,
|
||||
input.preferredTotalGoodsValue,
|
||||
)
|
||||
const eticketItemsWithGoodsValue = eticketItems.map((item, index) => ({
|
||||
...item,
|
||||
goodsValue: goodsValuePlan.goodsValues[index] || 1,
|
||||
}))
|
||||
const sendNum = eticketItems.reduce((sum, e) => sum + e.num, 0)
|
||||
const totalGoodsValue = eticketItems.reduce((sum, e) => sum + e.goodsValue, 0)
|
||||
const totalGoodsValue = goodsValuePlan.totalGoodsValue
|
||||
|
||||
logIntegration('[kuaishou-industry/send-code]', '电子凭证发货回调已调度', {
|
||||
oid: input.oid,
|
||||
sendType: input.sendType,
|
||||
sendNum,
|
||||
totalGoodsValue,
|
||||
etickets: eticketItems,
|
||||
goodsValueSource: goodsValuePlan.source,
|
||||
preferredTotalGoodsValue: normalizePositiveInteger(input.preferredTotalGoodsValue),
|
||||
etickets: eticketItemsWithGoodsValue,
|
||||
eticketType: input.eticketType || '',
|
||||
})
|
||||
|
||||
sendCallback({
|
||||
oid: input.oid,
|
||||
sendType: input.sendType,
|
||||
etickets: eticketItems,
|
||||
etickets: eticketItemsWithGoodsValue,
|
||||
sendNum,
|
||||
totalGoodsValue,
|
||||
token: input.token,
|
||||
...(input.eticketType ? { eticketType: input.eticketType } : {}),
|
||||
...(input.ext ? { ext: input.ext } : {}),
|
||||
}).then((result) => {
|
||||
if (!result.success) {
|
||||
logIntegration('[kuaishou-industry/send-code]', '电子凭证发货回调异步执行失败', {
|
||||
@@ -163,3 +176,102 @@ function fireSendCallback(input: {
|
||||
}, { level: 'warn' })
|
||||
})
|
||||
}
|
||||
|
||||
export function resolveSendCallbackGoodsValuePlan(
|
||||
etickets: Array<{ goodsValue?: unknown, [key: string]: unknown }>,
|
||||
ext = '',
|
||||
preferredTotalGoodsValue: unknown = null,
|
||||
) {
|
||||
const count = Math.max(etickets.length, 1)
|
||||
const preferredTotal = normalizePositiveInteger(preferredTotalGoodsValue)
|
||||
if (preferredTotal >= count) {
|
||||
return buildSplitGoodsValuePlan(preferredTotal, count, 'preferred')
|
||||
}
|
||||
|
||||
const extTotal = resolveGoodsValueTotalFromExt(ext)
|
||||
if (extTotal >= count) {
|
||||
return buildSplitGoodsValuePlan(extTotal, count, 'ext')
|
||||
}
|
||||
|
||||
const explicitGoodsValues = etickets
|
||||
.map((item) => normalizePositiveInteger(item.goodsValue))
|
||||
.filter((value) => value > 0)
|
||||
if (explicitGoodsValues.length === etickets.length && explicitGoodsValues.length > 0) {
|
||||
return {
|
||||
totalGoodsValue: explicitGoodsValues.reduce((sum, value) => sum + value, 0),
|
||||
goodsValues: explicitGoodsValues,
|
||||
source: 'eticket',
|
||||
}
|
||||
}
|
||||
|
||||
const goodsValues = Array.from({ length: count }, () => 1)
|
||||
return {
|
||||
totalGoodsValue: count,
|
||||
goodsValues,
|
||||
source: 'fallback',
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveSendCallbackPreferredTotalGoodsValue(order: OrderRow | null): number {
|
||||
if (!order) {
|
||||
return 0
|
||||
}
|
||||
|
||||
const rawPayload = parseJsonObject(order.raw_payload_json)
|
||||
const body = parseJsonObject(rawPayload.body)
|
||||
const maxAmountFen = normalizePositiveInteger(parseAmountToFen(body.maxAmount ?? rawPayload.maxAmount))
|
||||
if (maxAmountFen > 0) {
|
||||
return maxAmountFen
|
||||
}
|
||||
|
||||
return normalizePositiveInteger(order.total_amount)
|
||||
}
|
||||
|
||||
function buildSplitGoodsValuePlan(totalGoodsValue: number, count: number, source: string) {
|
||||
const goodsValues = splitGoodsValue(totalGoodsValue, count)
|
||||
return {
|
||||
totalGoodsValue: goodsValues.reduce((sum, value) => sum + value, 0),
|
||||
goodsValues,
|
||||
source,
|
||||
}
|
||||
}
|
||||
|
||||
function splitGoodsValue(totalGoodsValue: number, count: number): number[] {
|
||||
const base = Math.floor(totalGoodsValue / count)
|
||||
if (base <= 0) {
|
||||
return Array.from({ length: count }, () => 1)
|
||||
}
|
||||
|
||||
return Array.from({ length: count }, (_item, index) =>
|
||||
index === count - 1 ? totalGoodsValue - base * (count - 1) : base,
|
||||
)
|
||||
}
|
||||
|
||||
function resolveGoodsValueTotalFromExt(ext: unknown): number {
|
||||
const parsed = parseJsonObject(ext)
|
||||
return normalizePositiveInteger(
|
||||
parsed.totalGoodsValue
|
||||
?? parsed.goodsValue
|
||||
?? parsed.payment
|
||||
?? parsed.payAmount
|
||||
?? parsed.amount,
|
||||
)
|
||||
}
|
||||
|
||||
function normalizePositiveInteger(value: unknown): number {
|
||||
const parsed = Number(value)
|
||||
return Number.isFinite(parsed) && parsed > 0 ? Math.trunc(parsed) : 0
|
||||
}
|
||||
|
||||
function parseJsonObject(value: unknown): JsonObject {
|
||||
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
||||
return value as JsonObject
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(String(value || '{}'))
|
||||
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {}
|
||||
} catch {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user