下沉后台履约规则校验编排逻辑
This commit is contained in:
@@ -74,7 +74,6 @@ import {
|
|||||||
saveKuaishouCloudFulfillmentConfig,
|
saveKuaishouCloudFulfillmentConfig,
|
||||||
} from '../../order/kuaishou-cloud-fulfillment-config-service.js'
|
} from '../../order/kuaishou-cloud-fulfillment-config-service.js'
|
||||||
import { getFulfillmentProfileByKey } from '../../../repositories/fulfillment-profile-repo.js'
|
import { getFulfillmentProfileByKey } from '../../../repositories/fulfillment-profile-repo.js'
|
||||||
import { createHttpError } from '../../../utils/http.js'
|
|
||||||
import { syncConfiguredFulfillmentBindings } from '../../bootstrap/fulfillment-bootstrap-service.js'
|
import { syncConfiguredFulfillmentBindings } from '../../bootstrap/fulfillment-bootstrap-service.js'
|
||||||
import { resolveDisplayShopName } from '../admin-read-shared-helpers.js'
|
import { resolveDisplayShopName } from '../admin-read-shared-helpers.js'
|
||||||
import {
|
import {
|
||||||
@@ -94,9 +93,7 @@ import {
|
|||||||
} from './cloudtentacles.js'
|
} from './cloudtentacles.js'
|
||||||
import { listAdminObservedProducts } from './observed-products.js'
|
import { listAdminObservedProducts } from './observed-products.js'
|
||||||
import {
|
import {
|
||||||
assertAdminFulfillmentBindingsInput,
|
validateAdminFulfillmentBindingConfigs,
|
||||||
buildAdminFulfillmentBindingUniqueKey,
|
|
||||||
normalizeAdminFulfillmentBindingItem,
|
|
||||||
} from './validation.js'
|
} from './validation.js'
|
||||||
import {
|
import {
|
||||||
applyOptionalStringField,
|
applyOptionalStringField,
|
||||||
@@ -740,7 +737,11 @@ export async function updateAdminFulfillmentBindingConfigs(
|
|||||||
payload = /** @type {AdminFulfillmentBindingConfigSaveInput} */ ({}),
|
payload = /** @type {AdminFulfillmentBindingConfigSaveInput} */ ({}),
|
||||||
) {
|
) {
|
||||||
const bindingsInput = Array.isArray(payload.bindings) ? payload.bindings : []
|
const bindingsInput = Array.isArray(payload.bindings) ? payload.bindings : []
|
||||||
const normalizedBindings = await validateAdminFulfillmentBindingConfigs(bindingsInput)
|
const normalizedBindings = await validateAdminFulfillmentBindingConfigs(bindingsInput, {
|
||||||
|
kuaishouEticketSource: getKuaishouEticketSourceConfig(),
|
||||||
|
resolveKuaishouEticketShopConfig,
|
||||||
|
getFulfillmentProfileByKey,
|
||||||
|
})
|
||||||
const saved = saveOrderFulfillmentBindingConfigs(normalizedBindings)
|
const saved = saveOrderFulfillmentBindingConfigs(normalizedBindings)
|
||||||
await syncConfiguredFulfillmentBindings()
|
await syncConfiguredFulfillmentBindings()
|
||||||
|
|
||||||
@@ -749,41 +750,3 @@ export async function updateAdminFulfillmentBindingConfigs(
|
|||||||
bindings: saved.map(mapAdminFulfillmentBindingConfigItem),
|
bindings: saved.map(mapAdminFulfillmentBindingConfigItem),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function validateAdminFulfillmentBindingConfigs(bindings = []) {
|
|
||||||
assertAdminFulfillmentBindingsInput(bindings)
|
|
||||||
|
|
||||||
const seenKeys = new Set()
|
|
||||||
const kuaishouEticketSource = getKuaishouEticketSourceConfig()
|
|
||||||
const normalizedBindings = []
|
|
||||||
|
|
||||||
for (const [index, rawBinding] of bindings.entries()) {
|
|
||||||
const normalized = normalizeAdminFulfillmentBindingItem(rawBinding, {
|
|
||||||
index,
|
|
||||||
kuaishouEticketSource,
|
|
||||||
resolveKuaishouEticketShopConfig,
|
|
||||||
})
|
|
||||||
|
|
||||||
const profile = await getFulfillmentProfileByKey(normalized.profileKey)
|
|
||||||
if (!profile) {
|
|
||||||
throw createHttpError(`第 ${index + 1} 条规则使用了不存在的履约方式: ${normalized.profileKey}`, {
|
|
||||||
statusCode: 400,
|
|
||||||
errorCode: 'admin_fulfillment_bindings_invalid_profile_key',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const uniqueKey = buildAdminFulfillmentBindingUniqueKey(normalized)
|
|
||||||
|
|
||||||
if (seenKeys.has(uniqueKey)) {
|
|
||||||
throw createHttpError(`第 ${index + 1} 条规则与其它规则重复,请调整匹配条件或内部履约 SKU`, {
|
|
||||||
statusCode: 409,
|
|
||||||
errorCode: 'admin_fulfillment_bindings_duplicate_rule',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
seenKeys.add(uniqueKey)
|
|
||||||
normalizedBindings.push(normalized)
|
|
||||||
}
|
|
||||||
|
|
||||||
return normalizedBindings
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -135,3 +135,49 @@ export function buildAdminFulfillmentBindingUniqueKey(binding) {
|
|||||||
String(binding?.skuCode || '').trim(),
|
String(binding?.skuCode || '').trim(),
|
||||||
].join('::')
|
].join('::')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function validateAdminFulfillmentBindingConfigs(
|
||||||
|
bindings,
|
||||||
|
options = {},
|
||||||
|
) {
|
||||||
|
assertAdminFulfillmentBindingsInput(bindings)
|
||||||
|
|
||||||
|
const kuaishouEticketSource = /** @type {Record<string, unknown>} */ (options.kuaishouEticketSource || {})
|
||||||
|
const resolveKuaishouEticketShopConfig = /** @type {(shop: Record<string, unknown>, source: Record<string, unknown>) => Record<string, unknown> | null} */ (
|
||||||
|
options.resolveKuaishouEticketShopConfig || (() => null)
|
||||||
|
)
|
||||||
|
const getFulfillmentProfileByKey = /** @type {(profileKey: string) => Promise<unknown>} */ (
|
||||||
|
options.getFulfillmentProfileByKey || (async () => null)
|
||||||
|
)
|
||||||
|
const seenKeys = new Set()
|
||||||
|
const normalizedBindings = []
|
||||||
|
|
||||||
|
for (const [index, rawBinding] of bindings.entries()) {
|
||||||
|
const normalized = normalizeAdminFulfillmentBindingItem(rawBinding, {
|
||||||
|
index,
|
||||||
|
kuaishouEticketSource,
|
||||||
|
resolveKuaishouEticketShopConfig,
|
||||||
|
})
|
||||||
|
|
||||||
|
const profile = await getFulfillmentProfileByKey(normalized.profileKey)
|
||||||
|
if (!profile) {
|
||||||
|
throw createHttpError(`第 ${index + 1} 条规则使用了不存在的履约方式: ${normalized.profileKey}`, {
|
||||||
|
statusCode: 400,
|
||||||
|
errorCode: 'admin_fulfillment_bindings_invalid_profile_key',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const uniqueKey = buildAdminFulfillmentBindingUniqueKey(normalized)
|
||||||
|
if (seenKeys.has(uniqueKey)) {
|
||||||
|
throw createHttpError(`第 ${index + 1} 条规则与其它规则重复,请调整匹配条件或内部履约 SKU`, {
|
||||||
|
statusCode: 409,
|
||||||
|
errorCode: 'admin_fulfillment_bindings_duplicate_rule',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
seenKeys.add(uniqueKey)
|
||||||
|
normalizedBindings.push(normalized)
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizedBindings
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
assertAdminFulfillmentBindingsInput,
|
assertAdminFulfillmentBindingsInput,
|
||||||
buildAdminFulfillmentBindingUniqueKey,
|
buildAdminFulfillmentBindingUniqueKey,
|
||||||
normalizeAdminFulfillmentBindingItem,
|
normalizeAdminFulfillmentBindingItem,
|
||||||
|
validateAdminFulfillmentBindingConfigs,
|
||||||
} from './validation.js'
|
} from './validation.js'
|
||||||
|
|
||||||
test('assertAdminFulfillmentBindingsInput rejects non-array payloads', () => {
|
test('assertAdminFulfillmentBindingsInput rejects non-array payloads', () => {
|
||||||
@@ -117,3 +118,73 @@ test('buildAdminFulfillmentBindingUniqueKey uses normalized external sku name an
|
|||||||
'khhao::kuaishou::khhao-a::::::礼包 a::sku-1',
|
'khhao::kuaishou::khhao-a::::::礼包 a::sku-1',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('validateAdminFulfillmentBindingConfigs rejects missing fulfillment profile', async () => {
|
||||||
|
await assert.rejects(
|
||||||
|
() =>
|
||||||
|
validateAdminFulfillmentBindingConfigs(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
provider: 'agiso',
|
||||||
|
platform: 'xianyu',
|
||||||
|
shopId: 'shop-1',
|
||||||
|
skuCode: 'sku-1',
|
||||||
|
profileKey: 'missing-profile',
|
||||||
|
match: {
|
||||||
|
externalSkuCode: 'ext-1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
{
|
||||||
|
kuaishouEticketSource: {},
|
||||||
|
resolveKuaishouEticketShopConfig() {
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
async getFulfillmentProfileByKey() {
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
/第 1 条规则使用了不存在的履约方式: missing-profile/,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('validateAdminFulfillmentBindingConfigs rejects duplicate rules after normalization', async () => {
|
||||||
|
await assert.rejects(
|
||||||
|
() =>
|
||||||
|
validateAdminFulfillmentBindingConfigs(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
provider: 'agiso',
|
||||||
|
platform: 'xianyu',
|
||||||
|
shopId: 'shop-1',
|
||||||
|
skuCode: 'sku-1',
|
||||||
|
profileKey: 'manual_review',
|
||||||
|
match: {
|
||||||
|
externalSkuCode: ' ext-1 ',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provider: 'agiso',
|
||||||
|
platform: 'xianyu',
|
||||||
|
shopId: ' shop-1 ',
|
||||||
|
skuCode: 'sku-1',
|
||||||
|
profileKey: 'manual_review',
|
||||||
|
match: {
|
||||||
|
externalSkuCode: 'ext-1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
{
|
||||||
|
kuaishouEticketSource: {},
|
||||||
|
resolveKuaishouEticketShopConfig() {
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
async getFulfillmentProfileByKey() {
|
||||||
|
return { key: 'manual_review' }
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
/第 2 条规则与其它规则重复/,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user