接入多发货平台与电子凭证

This commit is contained in:
yml2213
2026-07-07 12:06:19 +08:00
parent c101b5f359
commit ddfa6c278f
35 changed files with 2627 additions and 365 deletions
@@ -2,6 +2,7 @@ import { createTask, listTasksByOrderId, updateTask } from '../../repositories/t
import { getFulfillmentProfileByKey } from '../../repositories/fulfillment-profile-repo.js'
import { createTaskClaimToken } from '../claim/claim-service.js'
import { notifyTaskAutoManualReview } from '../notification/domain-notifications.js'
import { prepareKuaishouFeifeiTask } from '../fulfillment/kuaishou-feifei/index.js'
import { nowIso } from '../../utils/time.js'
import { randomId } from '../../utils/random.js'
import {
@@ -120,7 +121,7 @@ export async function syncDeliveryTasksForOrderWithDeps(
const tasks: DeliveryTaskRow[] = []
for (const item of orderItems) {
const profile = await resolveDynamicCloudtentaclesProfile(item, getProfileByKey)
const profile = await resolveDynamicFulfillmentProfile(item, getProfileByKey)
if (!profile) {
continue
@@ -129,6 +130,7 @@ export async function syncDeliveryTasksForOrderWithDeps(
const quantity = Math.max(1, Number(item.quantity || 1))
const fulfillmentConfig = parseJsonObject(profile.config_json)
const cloudtentaclesConfig = parseJsonObject(fulfillmentConfig.cloudtentacles)
const kuaishouFeifeiConfig = parseJsonObject(fulfillmentConfig.kuaishouFeifei)
const kuaishouConsumeConfig = parseJsonObject(fulfillmentConfig.kuaishouConsume)
const kuaishouShopConfig = parseJsonObject(fulfillmentConfig.kuaishouShop)
const cloudSourceKeys = normalizeStringArray(cloudtentaclesConfig.cloudSourceKeys)
@@ -263,6 +265,25 @@ export async function syncDeliveryTasksForOrderWithDeps(
notes: String(fulfillmentConfig.notes || '').trim(),
}
: null,
kuaishouFeifei: isKuaishouFeifeiExecutor(profile.executor_key)
? {
flowType: 'kuaishou_feifei',
productCode: String(kuaishouFeifeiConfig.productCode || '').trim(),
productName: String(kuaishouFeifeiConfig.productName || item.sku_name || '').trim(),
platformOrderNo: '',
orderNo: '',
rechargeStatus: 0,
rechargeStatusLabel: '',
rechargeResultMessage: '',
claimUrl: '',
consumeStatus: 'pending',
h5: {
entryUrl: '',
rechargeUrl: '',
},
lastSyncedAt: null,
}
: null,
}),
createdAt,
updatedAt: createdAt,
@@ -330,6 +351,28 @@ async function preparePaidTask(
})
}
if (isKuaishouFeifeiExecutor(task.executor_key)) {
try {
return await prepareKuaishouFeifeiTask(task as TaskRow)
} catch (error) {
const message = error instanceof Error ? error.message : 'kuaishou-feifei 订单创建失败'
const updatedTask = await updateDeliveryTask(task.id, {
task_status: TASK_STATUS.MANUAL_REVIEW,
user_action_status: 'not_required',
last_error: message,
result_code: 'kuaishou_feifei_prepare_failed',
result_message: message,
updated_at: now,
})
await notifyManualReview({
task: updatedTask || task,
reason: message,
source: 'kuaishou_feifei_prepare_failed',
})
return updatedTask
}
}
if (!task.requires_claim || String(task.executor_key || '').trim() === 'manual_dispatch') {
const lastError = task.last_error || '当前任务需要人工履约处理'
const updatedTask = await updateDeliveryTask(task.id, {
@@ -462,6 +505,53 @@ async function resolveDynamicCloudtentaclesProfile(
}
}
async function resolveDynamicFulfillmentProfile(
item: OrderItemRow,
getProfileByKey: (profileKey: string) => Promise<FulfillmentBindingLike | null>,
): Promise<FulfillmentBindingLike | null> {
return (
await resolveDynamicCloudtentaclesProfile(item, getProfileByKey) ||
await resolveDynamicKuaishouFeifeiProfile(item, getProfileByKey)
)
}
async function resolveDynamicKuaishouFeifeiProfile(
item: OrderItemRow,
getProfileByKey: (profileKey: string) => Promise<FulfillmentBindingLike | null>,
): Promise<FulfillmentBindingLike | null> {
const snapshot = parseJsonObject(item.item_snapshot_json)
const feifei = parseJsonObject(snapshot.kuaishouFeifei)
const productCode = String(feifei.productCode || '').trim()
if (!productCode) {
return null
}
const profile = await getProfileByKey('kuaishou_feifei')
if (!profile) {
return null
}
return {
...profile,
profile_id: Number(profile.id || profile.profile_id || 0),
profile_key: 'kuaishou_feifei',
profile_name: String(profile.profile_name || profile.name || 'kuaishou-feifei 履约').trim(),
executor_key: 'kuaishou_feifei',
requires_claim: true,
auto_dispatch: false,
config_json: {
flowType: 'kuaishou_feifei',
configId: `kuaishou_feifei:${productCode}`,
kuaishouFeifei: {
productCode,
productName: String(feifei.skuName || feifei.productName || item.sku_name || '').trim(),
matchMode: String(feifei.matchMode || 'kuaishou_feifei_rule').trim(),
},
notes: '91卡券商品名命中 kuaishou-feifei 商品规则',
},
}
}
function normalizeStringArray(value: unknown): string[] {
if (!Array.isArray(value)) {
return []
@@ -542,3 +632,7 @@ function isTaskRow(task: TaskRow | DeliveryTaskRow | null | undefined): task is
function isKuaishouCloudExecutor(value: unknown): boolean {
return String(value || '').trim() === 'kuaishou_ct_assisted'
}
function isKuaishouFeifeiExecutor(value: unknown): boolean {
return String(value || '').trim() === 'kuaishou_feifei'
}