Merge branch 'main' into dev
This commit is contained in:
@@ -108,6 +108,7 @@ function buildWorkOrderPlan(
|
||||
skuQuantity,
|
||||
unitPriceFen,
|
||||
fixedRewardAmount: Number(rule.reward_amount || 0),
|
||||
pricingMode: rule.pricing_mode,
|
||||
sharingEnabled: rule.sharing_enabled === true,
|
||||
sharingAutoFromOrder: rule.sharing_auto_from_order === true,
|
||||
sharingTotalQuantity: Number(rule.sharing_total_quantity || 1),
|
||||
@@ -120,7 +121,7 @@ function buildWorkOrderPlan(
|
||||
return {
|
||||
skuQuantity,
|
||||
unitPriceFen,
|
||||
pricingMode: unitPriceFen > 0 ? 'unit' : 'fixed',
|
||||
pricingMode: rule.pricing_mode === 'unit' ? 'unit' : 'fixed',
|
||||
rewardAmount: pricing.rewardAmount,
|
||||
sharingEnabled,
|
||||
sharingTotalQuantity: sharingEnabled ? pricing.sharingTotalQuantity : 1,
|
||||
|
||||
@@ -27,7 +27,12 @@ import {
|
||||
upsertWorkProductRule,
|
||||
} from '../../repositories/worker-platform/work-product-rule-repo.js'
|
||||
import type { WorkOrderRow, WorkProductRuleRow } from '../../repositories/worker-platform/types.js'
|
||||
import { syncAdminWorkOrderTemplateCostVersions } from '../cost-center/work-order-product-service.js'
|
||||
import { query, withTransaction } from '../../db/client.js'
|
||||
import {
|
||||
archiveDeletedWorkOrderTemplateProduct,
|
||||
assertWorkOrderTemplateCanEnable,
|
||||
syncWorkOrderTemplateProduct,
|
||||
} from '../cost-center/work-order-template-product-service.js'
|
||||
import {
|
||||
normalizeWorkOrderAcceptanceMode,
|
||||
normalizeWorkOrderGiftCooldownHours,
|
||||
@@ -379,35 +384,76 @@ export async function saveAdminWorkProductRule(payload: JsonObject = {}) {
|
||||
})
|
||||
}
|
||||
|
||||
const rewardAmount = normalizeAmountFen(payload.rewardAmount ?? payload.rewardAmountYuan, 0)
|
||||
const unitPriceFen =
|
||||
payload.unitPrice === undefined && payload.unitPriceYuan === undefined
|
||||
? 0
|
||||
: normalizeAmountFen(payload.unitPrice ?? payload.unitPriceYuan, 0)
|
||||
|
||||
// 全系统只有【商品与成本中心】是标准价格,接单模板价格禁止在其他地方修改
|
||||
if (existingRule && !payload.internalAllowPriceChange) {
|
||||
const existingRewardAmount = Number(existingRule.reward_amount || 0)
|
||||
const existingUnitPrice = Number(existingRule.unit_price_fen || 0)
|
||||
const hasPricePayload =
|
||||
payload.rewardAmount !== undefined ||
|
||||
payload.rewardAmountYuan !== undefined ||
|
||||
payload.unitPrice !== undefined ||
|
||||
payload.unitPriceYuan !== undefined
|
||||
if (
|
||||
hasPricePayload &&
|
||||
(rewardAmount !== existingRewardAmount || unitPriceFen !== existingUnitPrice)
|
||||
) {
|
||||
throw createHttpError(
|
||||
'接单模板价格已由【商品与成本中心】统一定价,价格更新只能走商品与成本中心。请前往成本中心修改标准价格',
|
||||
{
|
||||
statusCode: 400,
|
||||
errorCode: 'price_update_restricted_to_cost_center',
|
||||
},
|
||||
)
|
||||
}
|
||||
const hasRewardPayload =
|
||||
payload.rewardAmount !== undefined || payload.rewardAmountYuan !== undefined
|
||||
const hasUnitPricePayload = payload.unitPrice !== undefined || payload.unitPriceYuan !== undefined
|
||||
const incomingRewardAmount = normalizeAmountFen(
|
||||
payload.rewardAmount ?? payload.rewardAmountYuan,
|
||||
0,
|
||||
)
|
||||
const incomingUnitPriceFen = normalizeAmountFen(payload.unitPrice ?? payload.unitPriceYuan, 0)
|
||||
const existingRewardAmount = Number(existingRule?.reward_amount || 0)
|
||||
const existingUnitPriceFen = Number(existingRule?.unit_price_fen || 0)
|
||||
const rawPricingMode = String(payload.pricingMode ?? payload.pricing_mode ?? '').trim()
|
||||
if (rawPricingMode && rawPricingMode !== 'fixed' && rawPricingMode !== 'unit') {
|
||||
throw createHttpError('接单模板计价方式无效', {
|
||||
statusCode: 400,
|
||||
errorCode: 'work_product_rule_pricing_mode_invalid',
|
||||
})
|
||||
}
|
||||
const sharingEnabled = normalizeBoolean(payload.sharingEnabled ?? payload.sharing_enabled, false)
|
||||
const existingPricingMode = existingRule?.pricing_mode === 'unit' ? 'unit' : 'fixed'
|
||||
const pricingMode: 'fixed' | 'unit' =
|
||||
rawPricingMode === 'unit' ? 'unit' : rawPricingMode === 'fixed' ? 'fixed' : existingPricingMode
|
||||
let carriedPricingAmount =
|
||||
existingPricingMode === 'unit' ? existingUnitPriceFen : existingRewardAmount
|
||||
if (existingRule && pricingMode !== existingPricingMode) {
|
||||
const activeCostResult = await query<{ amount_fen: number }>(
|
||||
`SELECT amount_fen
|
||||
FROM cost_versions
|
||||
WHERE work_product_rule_id = $1
|
||||
AND scope_type = 'rule'
|
||||
AND status = 'active'
|
||||
AND effective_from <= NOW()
|
||||
AND (effective_to IS NULL OR effective_to > NOW())
|
||||
ORDER BY effective_from DESC, id DESC
|
||||
LIMIT 1`,
|
||||
[existingRule.id],
|
||||
)
|
||||
const activeCostAmount = Number(activeCostResult.rows[0]?.amount_fen || 0)
|
||||
if (activeCostAmount > 0) carriedPricingAmount = activeCostAmount
|
||||
}
|
||||
|
||||
// 模板保存只维护业务配置。价格只能由商品与成本中心的统一命令写入。
|
||||
if (
|
||||
(hasRewardPayload && incomingRewardAmount !== existingRewardAmount) ||
|
||||
(hasUnitPricePayload && incomingUnitPriceFen !== existingUnitPriceFen) ||
|
||||
(!existingRule && (incomingRewardAmount > 0 || incomingUnitPriceFen > 0))
|
||||
) {
|
||||
throw createHttpError(
|
||||
'接单模板价格只能由【商品与成本中心】维护。请先保存模板,再到成本中心配置价格',
|
||||
{
|
||||
statusCode: 400,
|
||||
errorCode: 'price_update_restricted_to_cost_center',
|
||||
},
|
||||
)
|
||||
}
|
||||
const rewardAmount =
|
||||
pricingMode !== existingPricingMode || !existingRule
|
||||
? pricingMode === 'fixed'
|
||||
? carriedPricingAmount
|
||||
: 0
|
||||
: existingRewardAmount
|
||||
const unitPriceFen =
|
||||
pricingMode !== existingPricingMode || !existingRule
|
||||
? pricingMode === 'unit'
|
||||
? carriedPricingAmount
|
||||
: 0
|
||||
: existingUnitPriceFen
|
||||
const sharingEnabled = hasOwnPayload(payload, 'sharingEnabled')
|
||||
? normalizeBoolean(payload.sharingEnabled, false)
|
||||
: hasOwnPayload(payload, 'sharing_enabled')
|
||||
? normalizeBoolean(payload.sharing_enabled, false)
|
||||
: existingRule?.sharing_enabled === true
|
||||
const acceptanceMode = normalizeWorkOrderAcceptanceMode(
|
||||
hasOwnPayload(payload, 'acceptanceMode') || hasOwnPayload(payload, 'acceptance_mode')
|
||||
? payload.acceptanceMode || payload.acceptance_mode
|
||||
@@ -423,11 +469,14 @@ export async function saveAdminWorkProductRule(payload: JsonObject = {}) {
|
||||
hasConfiguredSharingValue(payload.sharingTotalQuantity ?? payload.sharing_total_quantity) ||
|
||||
hasConfiguredSharingValue(payload.sharingUnitReward ?? payload.sharingUnitRewardYuan) ||
|
||||
hasConfiguredSharingValue(payload.sharingTotalAmount ?? payload.sharingTotalAmountYuan)
|
||||
const requestedSharingAutoFromOrder = hasOwnPayload(payload, 'sharingAutoFromOrder')
|
||||
? normalizeBoolean(payload.sharingAutoFromOrder, false)
|
||||
: hasOwnPayload(payload, 'sharing_auto_from_order')
|
||||
? normalizeBoolean(payload.sharing_auto_from_order, false)
|
||||
: (existingRule?.sharing_auto_from_order === true && !hasManualSharingConfig) ||
|
||||
(!existingRule && sharingEnabled && pricingMode === 'unit' && !hasManualSharingConfig)
|
||||
const sharingAutoFromOrder =
|
||||
sharingEnabled &&
|
||||
unitPriceFen > 0 &&
|
||||
(normalizeBoolean(payload.sharingAutoFromOrder ?? payload.sharing_auto_from_order, false) ||
|
||||
!hasManualSharingConfig)
|
||||
pricingMode === 'unit' && requestedSharingAutoFromOrder && !hasManualSharingConfig
|
||||
const hasManualSharingQuantity = hasConfiguredSharingValue(
|
||||
payload.sharingTotalQuantity ?? payload.sharing_total_quantity,
|
||||
)
|
||||
@@ -436,7 +485,7 @@ export async function saveAdminWorkProductRule(payload: JsonObject = {}) {
|
||||
)
|
||||
if (
|
||||
sharingEnabled &&
|
||||
unitPriceFen > 0 &&
|
||||
pricingMode === 'unit' &&
|
||||
!sharingAutoFromOrder &&
|
||||
hasManualSharingQuantity !== hasManualSharingUnitReward
|
||||
) {
|
||||
@@ -445,17 +494,19 @@ export async function saveAdminWorkProductRule(payload: JsonObject = {}) {
|
||||
errorCode: 'work_product_rule_sharing_override_incomplete',
|
||||
})
|
||||
}
|
||||
const sharingUnitReward = normalizeAmountFen(
|
||||
payload.sharingUnitReward ?? payload.sharingUnitRewardYuan,
|
||||
0,
|
||||
)
|
||||
const sharingUnitReward =
|
||||
payload.sharingUnitReward !== undefined || payload.sharingUnitRewardYuan !== undefined
|
||||
? normalizeAmountFen(payload.sharingUnitReward ?? payload.sharingUnitRewardYuan, 0)
|
||||
: Number(existingRule?.sharing_unit_reward || 0)
|
||||
const sharingTotalAmount = normalizeAmountFen(
|
||||
payload.sharingTotalAmount ?? payload.sharingTotalAmountYuan,
|
||||
0,
|
||||
)
|
||||
const sharingTotalQuantity = resolveSharingQuantity(
|
||||
sharingTotalAmount,
|
||||
normalizePositiveInteger(payload.sharingTotalQuantity ?? payload.sharing_total_quantity, 1),
|
||||
payload.sharingTotalQuantity !== undefined || payload.sharing_total_quantity !== undefined
|
||||
? normalizePositiveInteger(payload.sharingTotalQuantity ?? payload.sharing_total_quantity, 1)
|
||||
: normalizePositiveInteger(existingRule?.sharing_total_quantity, 1),
|
||||
sharingUnitReward,
|
||||
)
|
||||
const resolvedSharingTotalAmount =
|
||||
@@ -473,77 +524,90 @@ export async function saveAdminWorkProductRule(payload: JsonObject = {}) {
|
||||
? 0
|
||||
: resolvedSharingTotalAmount
|
||||
: rewardAmount
|
||||
const requestedEnabled = hasOwnPayload(payload, 'enabled')
|
||||
? normalizeBoolean(payload.enabled, false)
|
||||
: existingRule?.enabled === true
|
||||
const enabled = existingRule ? requestedEnabled : false
|
||||
if (existingRule && enabled) {
|
||||
await assertWorkOrderTemplateCanEnable(existingRule.id)
|
||||
}
|
||||
const fields = normalizeRequirementFieldsFromPayload(payload)
|
||||
const ruleKey = existingRule?.rule_key || randomId('rule-')
|
||||
const rule = await upsertWorkProductRule({
|
||||
ruleKey,
|
||||
provider: hasOwnPayload(payload, 'provider')
|
||||
? String(payload.provider || '').trim()
|
||||
: String(existingRule?.provider || '').trim(),
|
||||
platform: hasOwnPayload(payload, 'platform')
|
||||
? String(payload.platform || '').trim()
|
||||
: String(existingRule?.platform || '').trim(),
|
||||
shopId:
|
||||
hasOwnPayload(payload, 'shopId') || hasOwnPayload(payload, 'shop_id')
|
||||
? String(payload.shopId || payload.shop_id || '').trim()
|
||||
: String(existingRule?.shop_id || '').trim(),
|
||||
skuCode:
|
||||
hasOwnPayload(payload, 'skuCode') || hasOwnPayload(payload, 'sku_code')
|
||||
? skuCode
|
||||
: String(existingRule?.sku_code || '').trim(),
|
||||
productName: productName || matchedProductName,
|
||||
matchType:
|
||||
hasOwnPayload(payload, 'matchType') || hasOwnPayload(payload, 'match_type')
|
||||
? normalizeMatchType(payload.matchType || payload.match_type)
|
||||
: normalizeMatchType(existingRule?.match_type),
|
||||
categoryId:
|
||||
normalizeOptionalId(payload.categoryId || payload.category_id) ||
|
||||
defaults.category?.id ||
|
||||
null,
|
||||
enabled: normalizeBoolean(payload.enabled, true),
|
||||
autoCreate: normalizeBoolean(payload.autoCreate ?? payload.auto_create, true),
|
||||
rewardAmount: finalRewardAmount,
|
||||
unitPriceFen,
|
||||
// 商品规则不配置押金;工单创建后由运营按需手工填写。
|
||||
requiredDepositAmount: 0,
|
||||
depositThresholdAmount: 0,
|
||||
sharingEnabled,
|
||||
sharingTotalQuantity,
|
||||
sharingUnitReward,
|
||||
sharingAutoFromOrder,
|
||||
timeoutMinutes: Math.max(0, normalizeInteger(payload.timeoutMinutes, 0)),
|
||||
timeoutPolicy: normalizeWorkOrderTimeoutPolicy(payload.timeoutPolicy),
|
||||
acceptanceMode,
|
||||
giftCooldownHours:
|
||||
hasOwnPayload(payload, 'giftCooldownHours') || hasOwnPayload(payload, 'gift_cooldown_hours')
|
||||
? normalizeWorkOrderGiftCooldownHours(
|
||||
payload.giftCooldownHours ?? payload.gift_cooldown_hours,
|
||||
)
|
||||
: normalizeWorkOrderGiftCooldownHours(existingRule?.gift_cooldown_hours),
|
||||
giftCooldownMinutes:
|
||||
hasOwnPayload(payload, 'giftCooldownMinutes') ||
|
||||
hasOwnPayload(payload, 'gift_cooldown_minutes')
|
||||
? normalizeWorkOrderGiftCooldownMinutes(
|
||||
payload.giftCooldownMinutes ?? payload.gift_cooldown_minutes,
|
||||
)
|
||||
: normalizeWorkOrderGiftCooldownMinutes(existingRule?.gift_cooldown_minutes),
|
||||
requirementJson: JSON.stringify({ fields }),
|
||||
matchJson: JSON.stringify(match),
|
||||
sortOrder: normalizeInteger(payload.sortOrder, 100),
|
||||
now: nowIso(),
|
||||
const hasCategoryPayload =
|
||||
hasOwnPayload(payload, 'categoryId') || hasOwnPayload(payload, 'category_id')
|
||||
const categoryId = hasCategoryPayload
|
||||
? normalizeOptionalId(payload.categoryId ?? payload.category_id)
|
||||
: existingRule?.category_id || defaults.category?.id || null
|
||||
const projection = await withTransaction(async (client) => {
|
||||
const savedRule = await upsertWorkProductRule({
|
||||
ruleKey,
|
||||
provider: hasOwnPayload(payload, 'provider')
|
||||
? String(payload.provider || '').trim()
|
||||
: String(existingRule?.provider || '').trim(),
|
||||
platform: hasOwnPayload(payload, 'platform')
|
||||
? String(payload.platform || '').trim()
|
||||
: String(existingRule?.platform || '').trim(),
|
||||
shopId:
|
||||
hasOwnPayload(payload, 'shopId') || hasOwnPayload(payload, 'shop_id')
|
||||
? String(payload.shopId || payload.shop_id || '').trim()
|
||||
: String(existingRule?.shop_id || '').trim(),
|
||||
skuCode:
|
||||
hasOwnPayload(payload, 'skuCode') || hasOwnPayload(payload, 'sku_code')
|
||||
? skuCode
|
||||
: String(existingRule?.sku_code || '').trim(),
|
||||
productName: productName || matchedProductName,
|
||||
matchType:
|
||||
hasOwnPayload(payload, 'matchType') || hasOwnPayload(payload, 'match_type')
|
||||
? normalizeMatchType(payload.matchType || payload.match_type)
|
||||
: normalizeMatchType(existingRule?.match_type),
|
||||
categoryId,
|
||||
enabled,
|
||||
autoCreate: normalizeBoolean(payload.autoCreate ?? payload.auto_create, true),
|
||||
rewardAmount: finalRewardAmount,
|
||||
unitPriceFen,
|
||||
pricingMode,
|
||||
// 商品规则不配置押金;工单创建后由运营按需手工填写。
|
||||
requiredDepositAmount: 0,
|
||||
depositThresholdAmount: 0,
|
||||
sharingEnabled,
|
||||
sharingTotalQuantity,
|
||||
sharingUnitReward,
|
||||
sharingAutoFromOrder,
|
||||
timeoutMinutes: Math.max(0, normalizeInteger(payload.timeoutMinutes, 0)),
|
||||
timeoutPolicy: normalizeWorkOrderTimeoutPolicy(payload.timeoutPolicy),
|
||||
acceptanceMode,
|
||||
giftCooldownHours:
|
||||
hasOwnPayload(payload, 'giftCooldownHours') || hasOwnPayload(payload, 'gift_cooldown_hours')
|
||||
? normalizeWorkOrderGiftCooldownHours(
|
||||
payload.giftCooldownHours ?? payload.gift_cooldown_hours,
|
||||
)
|
||||
: normalizeWorkOrderGiftCooldownHours(existingRule?.gift_cooldown_hours),
|
||||
giftCooldownMinutes:
|
||||
hasOwnPayload(payload, 'giftCooldownMinutes') ||
|
||||
hasOwnPayload(payload, 'gift_cooldown_minutes')
|
||||
? normalizeWorkOrderGiftCooldownMinutes(
|
||||
payload.giftCooldownMinutes ?? payload.gift_cooldown_minutes,
|
||||
)
|
||||
: normalizeWorkOrderGiftCooldownMinutes(existingRule?.gift_cooldown_minutes),
|
||||
requirementJson: JSON.stringify({ fields }),
|
||||
matchJson: JSON.stringify(match),
|
||||
sortOrder: normalizeInteger(payload.sortOrder, 100),
|
||||
now: nowIso(),
|
||||
client,
|
||||
})
|
||||
if (!savedRule) {
|
||||
throw createHttpError('接单模板保存失败', { statusCode: 500 })
|
||||
}
|
||||
const productProjection = await syncWorkOrderTemplateProduct(savedRule.id, client)
|
||||
return { rule: savedRule, productProjection }
|
||||
})
|
||||
|
||||
// 模板价格变更即同步成本版本(事件驱动);失败不阻塞模板保存,
|
||||
// 夜间巡检与后台「同步发单金额」按钮仍可修复。
|
||||
if (rule) {
|
||||
try {
|
||||
await syncAdminWorkOrderTemplateCostVersions(rule.id)
|
||||
} catch {
|
||||
// 成本版本同步失败不允许影响模板本身;由巡检补齐。
|
||||
}
|
||||
return {
|
||||
rule: mapWorkProductRule(projection.rule),
|
||||
productId: projection.productProjection.productId,
|
||||
productStatus: projection.productProjection.status,
|
||||
costConfigurationRequired: !projection.productProjection.hasActiveCost,
|
||||
}
|
||||
|
||||
return { rule: mapWorkProductRule(rule) }
|
||||
}
|
||||
|
||||
/** 根据工单创建时的规格,计算当前模板可同步的价格。 */
|
||||
@@ -562,6 +626,7 @@ export function resolveWorkOrderTemplateSyncPrice(input: {
|
||||
skuQuantity,
|
||||
unitPriceFen: Number(input.rule.unit_price_fen || 0),
|
||||
fixedRewardAmount: Number(input.rule.reward_amount || 0),
|
||||
pricingMode: input.rule.pricing_mode,
|
||||
sharingEnabled: input.rule.sharing_enabled === true,
|
||||
sharingAutoFromOrder: input.rule.sharing_auto_from_order === true,
|
||||
sharingTotalQuantity: Number(input.rule.sharing_total_quantity || 1),
|
||||
@@ -669,5 +734,6 @@ export async function deleteAdminWorkProductRule(ruleId: number | string) {
|
||||
errorCode: 'work_product_rule_not_found',
|
||||
})
|
||||
}
|
||||
await archiveDeletedWorkOrderTemplateProduct(rule.rule_key)
|
||||
return { deleted, retiredCostVersionCount: detached.updated }
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ function rule(
|
||||
sort_order: input.sort_order ?? 100,
|
||||
unit_price_fen: input.unit_price_fen ?? 0,
|
||||
reward_amount: input.reward_amount ?? 0,
|
||||
pricing_mode: input.pricing_mode ?? 'fixed',
|
||||
sharing_enabled: input.sharing_enabled ?? false,
|
||||
sharing_total_quantity: input.sharing_total_quantity ?? 1,
|
||||
sharing_unit_reward: input.sharing_unit_reward ?? 0,
|
||||
|
||||
@@ -50,7 +50,13 @@ import { assignPendingOrderItemChannels } from '../fulfillment/channel-assign-se
|
||||
export async function syncWorkerOrdersForSourceOrder(
|
||||
order: OrderRow,
|
||||
orderItems: OrderItemRow[],
|
||||
options: { source?: string; autoOnly?: boolean; sourceMetadata?: JsonObject } = {},
|
||||
options: {
|
||||
source?: string
|
||||
autoOnly?: boolean
|
||||
sourceMetadata?: JsonObject
|
||||
skipChannelAssign?: boolean
|
||||
preferredWorkRuleKey?: string
|
||||
} = {},
|
||||
) {
|
||||
const [rules, mappings] = await Promise.all([
|
||||
listWorkProductRules({ enabled: true }),
|
||||
@@ -75,9 +81,36 @@ export async function syncWorkerOrdersForSourceOrder(
|
||||
continue
|
||||
}
|
||||
|
||||
const match = resolveMatchingProductRuleDecision(order, item, rules, mappings, {
|
||||
legacyFallbackEnabled: productMatchConfig.legacyFallbackEnabled,
|
||||
})
|
||||
const preferredRuleKey = String(options.preferredWorkRuleKey || '').trim()
|
||||
const preferredRule = preferredRuleKey
|
||||
? rules.find((rule) => String(rule.rule_key || '').trim() === preferredRuleKey) || null
|
||||
: null
|
||||
const match =
|
||||
preferredRuleKey && !preferredRule
|
||||
? {
|
||||
rule: null,
|
||||
mappingId: null,
|
||||
reason: 'unmatched' as const,
|
||||
score: 0,
|
||||
candidates: [],
|
||||
diagnosis: [
|
||||
{
|
||||
dimension: 'internal_sku_channel',
|
||||
reason: 'configured_work_rule_missing',
|
||||
},
|
||||
],
|
||||
}
|
||||
: preferredRule
|
||||
? {
|
||||
rule: preferredRule,
|
||||
mappingId: null,
|
||||
reason: 'matched' as const,
|
||||
score: 1,
|
||||
candidates: [{ ruleKey: preferredRuleKey, score: 1, mappingId: null }],
|
||||
}
|
||||
: resolveMatchingProductRuleDecision(order, item, rules, mappings, {
|
||||
legacyFallbackEnabled: productMatchConfig.legacyFallbackEnabled,
|
||||
})
|
||||
const matchContext = resolveKuaishouWorkProductMatchContext(item)
|
||||
const snapshot = safeParseJson(item.item_snapshot_json)
|
||||
const sellerId =
|
||||
@@ -198,6 +231,7 @@ export async function syncWorkerOrdersForSourceOrder(
|
||||
skuQuantity,
|
||||
unitPriceFen: Number(rule.unit_price_fen || 0),
|
||||
fixedRewardAmount: Number(rule.reward_amount || 0),
|
||||
pricingMode: rule.pricing_mode,
|
||||
sharingEnabled: rule.sharing_enabled === true,
|
||||
sharingAutoFromOrder: rule.sharing_auto_from_order === true,
|
||||
sharingTotalQuantity: Number(rule.sharing_total_quantity || 1),
|
||||
@@ -309,15 +343,17 @@ export async function syncWorkerOrdersForSourceOrder(
|
||||
}
|
||||
|
||||
// 订单处理完成后立即跑一次渠道判定;失败不阻断下单,由定时任务兜底。
|
||||
try {
|
||||
await assignPendingOrderItemChannels({ batchSize: 200, maxBatches: 1 })
|
||||
} catch (error) {
|
||||
logWarn('[worker-order-consumer]', '履约方式归集失败,等待定时任务兜底', {
|
||||
orderId: order.id,
|
||||
platformOrderId: order.platform_order_id,
|
||||
orderItemCount: orderItems.length,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
})
|
||||
if (!options.skipChannelAssign) {
|
||||
try {
|
||||
await assignPendingOrderItemChannels({ batchSize: 200, maxBatches: 1 })
|
||||
} catch (error) {
|
||||
logWarn('[worker-order-consumer]', '履约方式归集失败,等待定时任务兜底', {
|
||||
orderId: order.id,
|
||||
platformOrderId: order.platform_order_id,
|
||||
orderItemCount: orderItems.length,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const summary = {
|
||||
|
||||
@@ -64,6 +64,7 @@ export function mapWorkProductRule(rule: WorkProductRuleRow | null | undefined)
|
||||
autoCreate: Boolean(rule.auto_create),
|
||||
rewardAmount: Number(rule.reward_amount || 0),
|
||||
unitPriceFen: Number(rule.unit_price_fen || 0),
|
||||
pricingMode: rule.pricing_mode === 'unit' ? 'unit' : 'fixed',
|
||||
sharing: {
|
||||
enabled: rule.sharing_enabled === true,
|
||||
totalQuantity: Number(rule.sharing_total_quantity || 1),
|
||||
|
||||
@@ -745,6 +745,7 @@ function buildProductRule(overrides: Partial<WorkProductRuleRow> = {}): WorkProd
|
||||
auto_create: true,
|
||||
reward_amount: 2500,
|
||||
unit_price_fen: 0,
|
||||
pricing_mode: 'fixed',
|
||||
required_deposit_amount: 0,
|
||||
deposit_threshold_amount: 20000,
|
||||
sharing_enabled: false,
|
||||
@@ -1624,6 +1625,7 @@ test('按件拼单未手动覆盖时,使用匹配数量与模板单价', () =>
|
||||
skuQuantity: 10,
|
||||
unitPriceFen: 20,
|
||||
fixedRewardAmount: 0,
|
||||
pricingMode: 'unit',
|
||||
sharingEnabled: true,
|
||||
sharingAutoFromOrder: true,
|
||||
sharingTotalQuantity: 1,
|
||||
@@ -1642,6 +1644,7 @@ test('按件拼单手动覆盖数量和单价时,保留手工配置', () => {
|
||||
skuQuantity: 10,
|
||||
unitPriceFen: 20,
|
||||
fixedRewardAmount: 0,
|
||||
pricingMode: 'unit',
|
||||
sharingEnabled: true,
|
||||
sharingAutoFromOrder: false,
|
||||
sharingTotalQuantity: 3,
|
||||
@@ -1657,7 +1660,7 @@ test('按件拼单手动覆盖数量和单价时,保留手工配置', () => {
|
||||
|
||||
test('模板同步价格按工单来源规格重新计算普通按件单', () => {
|
||||
const price = resolveWorkOrderTemplateSyncPrice({
|
||||
rule: buildProductRule({ unit_price_fen: 200 }),
|
||||
rule: buildProductRule({ unit_price_fen: 200, pricing_mode: 'unit' }),
|
||||
workOrder: buildWorkOrderRow({
|
||||
product_name: '指挥官秘钥15个',
|
||||
material_json: { source: { skuQuantity: 8 } },
|
||||
@@ -1667,10 +1670,39 @@ test('模板同步价格按工单来源规格重新计算普通按件单', () =>
|
||||
assert.deepEqual(price, { rewardAmount: 1600 })
|
||||
})
|
||||
|
||||
test('模板同步价格以显式计价方式为准,不按两个金额字段反推', () => {
|
||||
const workOrder = buildWorkOrderRow({ product_name: '指挥官秘钥15个' })
|
||||
|
||||
assert.deepEqual(
|
||||
resolveWorkOrderTemplateSyncPrice({
|
||||
rule: buildProductRule({
|
||||
reward_amount: 2500,
|
||||
unit_price_fen: 200,
|
||||
pricing_mode: 'fixed',
|
||||
}),
|
||||
workOrder,
|
||||
}),
|
||||
{ rewardAmount: 2500 },
|
||||
)
|
||||
|
||||
assert.deepEqual(
|
||||
resolveWorkOrderTemplateSyncPrice({
|
||||
rule: buildProductRule({
|
||||
reward_amount: 2500,
|
||||
unit_price_fen: 200,
|
||||
pricing_mode: 'unit',
|
||||
}),
|
||||
workOrder,
|
||||
}),
|
||||
{ rewardAmount: 3000 },
|
||||
)
|
||||
})
|
||||
|
||||
test('模板同步价格为拼单返回新的份额单价', () => {
|
||||
const price = resolveWorkOrderTemplateSyncPrice({
|
||||
rule: buildProductRule({
|
||||
unit_price_fen: 200,
|
||||
pricing_mode: 'unit',
|
||||
sharing_enabled: true,
|
||||
sharing_auto_from_order: true,
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user