188 lines
5.7 KiB
JavaScript
188 lines
5.7 KiB
JavaScript
import {
|
|
getFulfillmentProfileByKey,
|
|
listFulfillmentProfileRequirements,
|
|
replaceFulfillmentProfileRequirements,
|
|
upsertFulfillmentProfile,
|
|
upsertSkuFulfillmentBinding,
|
|
} from '../../repositories/fulfillment-profile-repo.js'
|
|
import { upsertProductMatchRule } from '../../repositories/product-match-rule-repo.js'
|
|
import { normalizeProductName } from '../order/product-match-service.js'
|
|
import { query } from '../../db/client.js'
|
|
import { getLegacyOrderFulfillmentBindingConfigs } from '../order/fulfillment-binding-config-service.js'
|
|
import {
|
|
getKuaishouCloudFulfillmentConfig,
|
|
mapKuaishouCloudFulfillmentItemsToBindings,
|
|
} from '../order/kuaishou-cloud-fulfillment-config-service.js'
|
|
import { nowIso } from '../../utils/time.js'
|
|
|
|
const CORE_PROFILES = [
|
|
{
|
|
profileKey: 'manual_review',
|
|
name: '人工发货',
|
|
executorKey: 'manual_dispatch',
|
|
requiresClaim: false,
|
|
autoDispatch: false,
|
|
inventoryStrategy: 'static_pool',
|
|
requirements: [],
|
|
},
|
|
{
|
|
profileKey: 'tencent_claim_redeem',
|
|
name: '腾讯领取兑换',
|
|
executorKey: 'tencent_claim_redeem',
|
|
requiresClaim: true,
|
|
autoDispatch: false,
|
|
inventoryStrategy: 'static_pool',
|
|
requirements: [
|
|
{
|
|
roleKey: 'primary_code',
|
|
credentialType: 'tencent_code',
|
|
quantityPerUnit: 1,
|
|
isRequired: true,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
profileKey: 'tencent_claim_assisted',
|
|
name: '腾讯领取兑换(半自动+人工)',
|
|
executorKey: 'tencent_claim_assisted',
|
|
requiresClaim: true,
|
|
autoDispatch: false,
|
|
inventoryStrategy: 'static_pool',
|
|
requirements: [
|
|
{
|
|
roleKey: 'primary_code',
|
|
credentialType: 'tencent_code',
|
|
quantityPerUnit: 1,
|
|
isRequired: true,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
profileKey: 'kuaishou_ct_assisted',
|
|
name: '快手 cloud 履约',
|
|
executorKey: 'kuaishou_ct_assisted',
|
|
requiresClaim: false,
|
|
autoDispatch: false,
|
|
inventoryStrategy: 'external_platform',
|
|
requirements: [],
|
|
},
|
|
]
|
|
|
|
export async function ensureFulfillmentCatalogBootstrapped() {
|
|
const profileMap = await ensureCoreProfiles()
|
|
await syncConfiguredFulfillmentBindings(profileMap)
|
|
}
|
|
|
|
async function ensureCoreProfiles() {
|
|
const timestamp = nowIso()
|
|
const profileMap = {}
|
|
|
|
for (const profile of CORE_PROFILES) {
|
|
const saved = await upsertFulfillmentProfile({
|
|
profileKey: profile.profileKey,
|
|
name: profile.name,
|
|
executorKey: profile.executorKey,
|
|
requiresClaim: profile.requiresClaim,
|
|
autoDispatch: profile.autoDispatch,
|
|
inventoryStrategy: profile.inventoryStrategy,
|
|
configJson: '{}',
|
|
createdAt: timestamp,
|
|
updatedAt: timestamp,
|
|
})
|
|
|
|
if (!saved) {
|
|
continue
|
|
}
|
|
|
|
profileMap[profile.profileKey] = saved
|
|
const currentRequirements = await listFulfillmentProfileRequirements(saved.id)
|
|
if (currentRequirements.length !== profile.requirements.length) {
|
|
await replaceFulfillmentProfileRequirements(saved.id, profile.requirements, timestamp)
|
|
}
|
|
}
|
|
|
|
return profileMap
|
|
}
|
|
|
|
export async function syncConfiguredFulfillmentBindings(profileMap = {}) {
|
|
const timestamp = nowIso()
|
|
const bindingsToApply = [
|
|
...getLegacyOrderFulfillmentBindingConfigs(),
|
|
...mapKuaishouCloudFulfillmentItemsToBindings(getKuaishouCloudFulfillmentConfig()),
|
|
]
|
|
|
|
await query('DELETE FROM product_match_rules')
|
|
await query('DELETE FROM sku_fulfillment_bindings')
|
|
|
|
for (const binding of bindingsToApply) {
|
|
const profile = profileMap[binding.profileKey] || await getFulfillmentProfileByKey(binding.profileKey)
|
|
if (!profile) {
|
|
continue
|
|
}
|
|
|
|
const bindingConfig = resolveBindingRuntimeConfig(binding)
|
|
const matchShopIds = resolveBindingMatchShopIds(binding)
|
|
const match = binding.match || {}
|
|
const externalSkuName = String(match.externalSkuName || '').trim()
|
|
const externalItemId = String(match.externalItemId || '').trim()
|
|
const externalSkuCode = String(match.externalSkuCode || '').trim()
|
|
|
|
for (const matchShopId of matchShopIds) {
|
|
await upsertSkuFulfillmentBinding({
|
|
skuCode: binding.skuCode,
|
|
provider: binding.provider,
|
|
platform: binding.platform,
|
|
shopId: matchShopId,
|
|
profileId: profile.id,
|
|
enabled: binding.enabled,
|
|
priority: binding.priority,
|
|
configJson: JSON.stringify(bindingConfig),
|
|
createdAt: timestamp,
|
|
updatedAt: timestamp,
|
|
})
|
|
|
|
if (!externalSkuName && !externalItemId && !externalSkuCode) {
|
|
continue
|
|
}
|
|
|
|
await upsertProductMatchRule({
|
|
provider: binding.provider,
|
|
platform: binding.platform,
|
|
shopId: matchShopId,
|
|
externalItemId,
|
|
externalSkuCode,
|
|
externalSkuName,
|
|
externalSkuNameNormalized: normalizeProductName(externalSkuName),
|
|
resolvedSkuCode: binding.skuCode,
|
|
enabled: binding.enabled,
|
|
priority: binding.priority,
|
|
configJson: JSON.stringify(match.config || {}),
|
|
createdAt: timestamp,
|
|
updatedAt: timestamp,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
export function resolveBindingMatchShopIds(binding = {}) {
|
|
return [String(binding.shopId || '').trim()]
|
|
}
|
|
|
|
function resolveBindingRuntimeConfig(binding = {}) {
|
|
const baseConfig = isPlainObject(binding.config) ? { ...binding.config } : {}
|
|
|
|
if (String(binding.provider || '').trim() === '91kaquan' && String(binding.platform || '').trim() === 'kuaishou') {
|
|
baseConfig.kuaishouShop = {
|
|
...(isPlainObject(baseConfig.kuaishouShop) ? baseConfig.kuaishouShop : {}),
|
|
shopId: String(binding.shopId || '').trim(),
|
|
shopName: String(binding.shopName || '').trim(),
|
|
}
|
|
}
|
|
|
|
return baseConfig
|
|
}
|
|
|
|
function isPlainObject(value) {
|
|
return Object.prototype.toString.call(value) === '[object Object]'
|
|
}
|