diff --git a/apps/backend/src/services/admin/platform-config/fulfillment-bindings-service.js b/apps/backend/src/services/admin/platform-config/fulfillment-bindings-service.js index 0b983d7b..edb1064f 100644 --- a/apps/backend/src/services/admin/platform-config/fulfillment-bindings-service.js +++ b/apps/backend/src/services/admin/platform-config/fulfillment-bindings-service.js @@ -2,14 +2,16 @@ import { getAgisoShopConfig } from '../../platforms/agiso/shop-config-service.js' import { - getOrderFulfillmentBindingConfigs, + filterLegacyOrderFulfillmentBindings, getOrderFulfillmentBindingsFilePath, + getLegacyOrderFulfillmentBindingConfigs, saveOrderFulfillmentBindingConfigs, } from '../../order/fulfillment-binding-config-service.js' import { enrichAgisoXianyuTradeOrder } from '../../platforms/agiso/xianyu/order-detail-service.js' import { getKuaishouEticketSourceConfig, resolveKuaishouEticketShopConfig } from '../../platforms/kuaishou-eticket/source-config-service.js' import { syncConfiguredFulfillmentBindings } from '../../bootstrap/fulfillment-bootstrap-service.js' import { getFulfillmentProfileByKey } from '../../../repositories/fulfillment-profile-repo.js' +import { createHttpError } from '../../../utils/http.js' import { buildFulfillmentLookupResult, normalizeFulfillmentLookupPayload, @@ -23,7 +25,7 @@ import { mapAdminFulfillmentBindingConfigItem } from './domain.js' /** @typedef {import('../../../types/admin-write-inputs.js').AdminFulfillmentBindingLookupInput} AdminFulfillmentBindingLookupInput */ export async function getAdminFulfillmentBindingConfigs() { - const bindings = getOrderFulfillmentBindingConfigs() + const bindings = getLegacyOrderFulfillmentBindingConfigs() return { filePath: getOrderFulfillmentBindingsFilePath(), @@ -57,7 +59,7 @@ export async function lookupAdminFulfillmentBindingOrder(payload = /** @type {Ad }) const { detail } = resolveFulfillmentLookupDetail(detailResult, platformOrderId) - const bindings = getOrderFulfillmentBindingConfigs() + const bindings = getLegacyOrderFulfillmentBindingConfigs() return buildFulfillmentLookupResult({ provider, platform, @@ -80,6 +82,7 @@ export async function updateAdminFulfillmentBindingConfigs( resolveKuaishouEticketShopConfig, getFulfillmentProfileByKey, }) + assertLegacyBindingConfigsOnly(normalizedBindings) const saved = saveOrderFulfillmentBindingConfigs(normalizedBindings) await syncConfiguredFulfillmentBindings() @@ -88,3 +91,14 @@ export async function updateAdminFulfillmentBindingConfigs( bindings: saved.map(mapAdminFulfillmentBindingConfigItem), } } + +function assertLegacyBindingConfigsOnly(bindings = []) { + if (filterLegacyOrderFulfillmentBindings(bindings).length === bindings.length) { + return + } + + throw createHttpError('快手履约规则请改到“快手 Cloud 新履约”页维护,旧履约规则仅保留给 Agiso 等历史场景', { + statusCode: 400, + errorCode: 'admin_fulfillment_bindings_kuaishou_cloud_moved', + }) +} diff --git a/apps/backend/src/services/bootstrap/fulfillment-bootstrap-service.js b/apps/backend/src/services/bootstrap/fulfillment-bootstrap-service.js index ddb1b172..393e656c 100644 --- a/apps/backend/src/services/bootstrap/fulfillment-bootstrap-service.js +++ b/apps/backend/src/services/bootstrap/fulfillment-bootstrap-service.js @@ -8,7 +8,7 @@ import { import { upsertProductMatchRule } from '../../repositories/product-match-rule-repo.js' import { normalizeProductName } from '../order/product-match-service.js' import { query } from '../../db/client.js' -import { getOrderFulfillmentBindingConfigs } from '../order/fulfillment-binding-config-service.js' +import { getLegacyOrderFulfillmentBindingConfigs } from '../order/fulfillment-binding-config-service.js' import { getKuaishouCloudFulfillmentConfig, mapKuaishouCloudFulfillmentItemsToBindings, @@ -107,7 +107,7 @@ async function ensureCoreProfiles() { export async function syncConfiguredFulfillmentBindings(profileMap = {}) { const timestamp = nowIso() const bindingsToApply = [ - ...getOrderFulfillmentBindingConfigs(), + ...getLegacyOrderFulfillmentBindingConfigs(), ...mapKuaishouCloudFulfillmentItemsToBindings(getKuaishouCloudFulfillmentConfig()), ] diff --git a/apps/backend/src/services/order/fulfillment-binding-config-service.js b/apps/backend/src/services/order/fulfillment-binding-config-service.js index aa3f2550..d12f08cd 100644 --- a/apps/backend/src/services/order/fulfillment-binding-config-service.js +++ b/apps/backend/src/services/order/fulfillment-binding-config-service.js @@ -13,6 +13,10 @@ export function getOrderFulfillmentBindingConfigs() { return loadOrderFulfillmentBindingConfigsFromFile() } +export function getLegacyOrderFulfillmentBindingConfigs() { + return filterLegacyOrderFulfillmentBindings(getOrderFulfillmentBindingConfigs()) +} + export function saveOrderFulfillmentBindingConfigs(rawValue) { const normalized = normalizeOrderFulfillmentBindingConfigs(rawValue) fs.mkdirSync(path.dirname(ORDER_FULFILLMENT_BINDINGS_FILE_PATH), { recursive: true }) @@ -48,6 +52,15 @@ function normalizeOrderFulfillmentBindingConfigs(rawValue) { .filter(Boolean) } +export function filterLegacyOrderFulfillmentBindings(bindings = []) { + return (Array.isArray(bindings) ? bindings : []).filter((item) => !isKuaishouCloudFulfillmentBinding(item)) +} + +export function isKuaishouCloudFulfillmentBinding(binding = {}) { + return String(binding?.provider || '').trim() === 'khhao' + && String(binding?.platform || '').trim() === 'kuaishou' +} + function normalizeOrderFulfillmentBinding(rawValue) { if (!isPlainObject(rawValue)) { return null diff --git a/apps/backend/src/services/order/fulfillment-binding-config-service.test.js b/apps/backend/src/services/order/fulfillment-binding-config-service.test.js new file mode 100644 index 00000000..60df4603 --- /dev/null +++ b/apps/backend/src/services/order/fulfillment-binding-config-service.test.js @@ -0,0 +1,53 @@ +import test from 'node:test' +import assert from 'node:assert/strict' + +import { + filterLegacyOrderFulfillmentBindings, + isKuaishouCloudFulfillmentBinding, +} from './fulfillment-binding-config-service.js' + +test('isKuaishouCloudFulfillmentBinding only marks khhao kuaishou rules as cloud rules', () => { + assert.equal( + isKuaishouCloudFulfillmentBinding({ + provider: 'khhao', + platform: 'kuaishou', + }), + true, + ) + + assert.equal( + isKuaishouCloudFulfillmentBinding({ + provider: 'agiso', + platform: 'xianyu', + }), + false, + ) +}) + +test('filterLegacyOrderFulfillmentBindings keeps agiso rules and removes khhao kuaishou rules', () => { + assert.deepEqual( + filterLegacyOrderFulfillmentBindings([ + { + provider: 'agiso', + platform: 'xianyu', + shopId: 'shop-1', + skuCode: 'sku-a', + }, + { + provider: 'khhao', + platform: 'kuaishou', + shopId: '4269276762', + khhaoShopId: '10', + skuCode: 'sku-b', + }, + ]), + [ + { + provider: 'agiso', + platform: 'xianyu', + shopId: 'shop-1', + skuCode: 'sku-a', + }, + ], + ) +})