feat(distribution): 分站收号压价解耦买家价(方案 B 新口径)

核心语义反转:receive_cost_delta 为压价点数,收号比例 = 主站参考比例 R + 点数,
点数越大号主收号价越低;买家售价锚定主站参考比例推导,不再随收号价联动。

- 迁移 000072:distribution_sites 新增 receive_cost_delta(旧 receive_ratio_delta 保留只读)
- receive_pricing:新增 Normalize/ApplySiteReceiveCostDelta;分站定价买家侧改用
  R 锚点(referenceSellerCoinBasePrice),快照写入 site_receive_cost_delta 与 pricing_model 标记
- site_receive:新增 resolveSiteReceiveCostDelta 读取链路,旧 ResolveReceiveRatioDelta 保留
- 分成不变:压价差经结算层 profit 自动进入 35/40/25 三方分成池;
  货架自主加价仍 100% 归分站,两条边界均以主站为锚
- 权限对齐旧字段:主站全局管理员与分站操作员均可设置
- 前端三端同步:主站 pricing.ts/usePricingCalculator 买卖解耦试算、
  主站后台弹窗改「收号压价」价格语义文案、分销后台 SiteSettings 同步
- 测试:单测锚定 R=43 时 cost_delta=3 → 收号 217.4 / 卖价 263.2 不变 / markup 池 30.6→54.9
This commit is contained in:
yml2213
2026-09-17 15:54:56 +08:00
parent 8dc21d2757
commit 22fcaa5945
17 changed files with 253 additions and 62 deletions
@@ -19,11 +19,10 @@ import {
hasAcceleratedSaleRatioInput as hasAcceleratedSaleRatioValue,
isQuantityItemDisabledForInsurance,
readFinalSaleRatio,
applySiteReceiveRatioDelta,
applySiteReceiveCostDelta,
roundMoney,
roundRatio,
} from '@/shared/utils/pricing'
export function usePricingCalculator(options: {
publishOptions: Ref<ListingPublishOptions>
salePriceConfig: Ref<PublishSalePriceConfig>
@@ -34,6 +33,7 @@ export function usePricingCalculator(options: {
selectedSkins: Ref<string[]>
isDistribution?: Ref<boolean>
receiveRatioDelta?: Ref<number>
receiveCostDelta?: Ref<number>
}) {
const serverOptions = computed(() => options.publishOptions.value.server_options)
const faceOptions = computed(() => options.publishOptions.value.face_options)
@@ -91,12 +91,17 @@ export function usePricingCalculator(options: {
const siteReceiveRatioDelta = computed(() =>
Math.max(0, Number(options.receiveRatioDelta?.value || 0))
)
const siteReceiveCostDelta = computed(() =>
Math.max(0, Number(options.receiveCostDelta?.value || 0))
)
// 分站新口径:收号价按「R + 压价点数」计算(压价收号);买家定价仍按主站口径推导。
// 买卖两侧解耦:calculatedRatio 决定号主收号价,anchorRatio 决定买家价基准。
const calculatedRatio = computed(() => {
if (calculatedDefaultSaleRatio.value <= 0) return 0
if (options.isDistribution?.value) {
return applySiteReceiveRatioDelta(
return applySiteReceiveCostDelta(
calculatedDefaultSaleRatio.value,
siteReceiveRatioDelta.value
siteReceiveCostDelta.value
)
}
return readFinalSaleRatio(
@@ -105,10 +110,19 @@ export function usePricingCalculator(options: {
maxAcceleratedSaleRatio.value
)
})
const anchorRatio = computed(() => {
if (calculatedDefaultSaleRatio.value <= 0) return 0
if (options.isDistribution?.value) return calculatedDefaultSaleRatio.value
return calculatedRatio.value
})
const calculatedCoinBasePrice = computed(() => {
if (calculatedRatio.value <= 0) return 0
return roundMoney(coinWanAmount.value / calculatedRatio.value)
})
const anchorCoinBasePrice = computed(() => {
if (anchorRatio.value <= 0) return 0
return roundMoney(coinWanAmount.value / anchorRatio.value)
})
const calculatedConsumablePrice = computed(() =>
calculateConsumablePrice({
quantityItems: quantityItems.value,
@@ -122,13 +136,18 @@ export function usePricingCalculator(options: {
? roundMoney(calculatedCoinBasePrice.value + calculatedConsumablePrice.value)
: 0
)
const anchorSellerPrice = computed(() =>
anchorRatio.value > 0
? roundMoney(anchorCoinBasePrice.value + calculatedConsumablePrice.value)
: 0
)
const calculatedPlatformPricing = computed(() =>
calculatePlatformPricing({
coinMAmount: coinMAmount.value,
coinWanAmount: coinWanAmount.value,
sellerRatio: calculatedRatio.value,
sellerCoinBasePrice: calculatedCoinBasePrice.value,
sellerTotalPrice: calculatedSellerPrice.value,
sellerRatio: anchorRatio.value,
sellerCoinBasePrice: anchorCoinBasePrice.value,
sellerTotalPrice: anchorSellerPrice.value,
consumablePrice: calculatedConsumablePrice.value,
salePriceConfig: options.salePriceConfig.value,
})
@@ -212,6 +231,7 @@ export function usePricingCalculator(options: {
calculatedDefaultSaleRatio,
maxAcceleratedSaleRatio,
siteReceiveRatioDelta,
siteReceiveCostDelta,
calculatedRatio,
calculatedCoinBasePrice,
calculatedConsumablePrice,
+10
View File
@@ -165,6 +165,16 @@ export function applySiteReceiveRatioDelta(referenceRatio: number, delta: number
return roundRatio(Math.max(referenceRatio - safeDelta, 0.01))
}
/**
* 分站收号压价(新口径):收号比例 = 主站参考比例 R + 点数,比例变大 → 收号价更低。
* 买家价已解耦,不再从收号比例推导,始终锚定主站参考口径。
*/
export function applySiteReceiveCostDelta(referenceRatio: number, delta: number) {
if (referenceRatio <= 0) return 0
const safeDelta = Math.min(20, Math.max(0, Number(delta) || 0))
return roundRatio(referenceRatio + safeDelta)
}
export function hasAcceleratedSaleRatioInput(value: number | '' | null | undefined) {
const ratio = Number(value)
return (