Files
order_site/apps/backend/src/services/bootstrap/fulfillment-bootstrap-service.js
T
2026-05-03 18:44:44 +08:00

162 lines
4.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 { getOrderFulfillmentBindingConfigs } 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 = [
...getOrderFulfillmentBindingConfigs(),
...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
}
await upsertSkuFulfillmentBinding({
skuCode: binding.skuCode,
provider: binding.provider,
platform: binding.platform,
shopId: binding.shopId,
profileId: profile.id,
enabled: binding.enabled,
priority: binding.priority,
configJson: JSON.stringify(binding.config || {}),
createdAt: timestamp,
updatedAt: timestamp,
})
const match = binding.match || {}
const externalSkuName = String(match.externalSkuName || '').trim()
const externalItemId = String(match.externalItemId || '').trim()
const externalSkuCode = String(match.externalSkuCode || '').trim()
if (!externalSkuName && !externalItemId && !externalSkuCode) {
continue
}
await upsertProductMatchRule({
provider: binding.provider,
platform: binding.platform,
shopId: binding.shopId,
externalItemId,
externalSkuCode,
externalSkuName,
externalSkuNameNormalized: normalizeProductName(externalSkuName),
resolvedSkuCode: binding.skuCode,
enabled: binding.enabled,
priority: binding.priority,
configJson: JSON.stringify(match.config || {}),
createdAt: timestamp,
updatedAt: timestamp,
})
}
}