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
@@ -13,6 +13,7 @@ export interface DistributionSite {
settlement_hold_days: number
listing_qrcode_url?: string
receive_ratio_delta?: number
receive_cost_delta?: number
created_at: string
}
@@ -154,6 +155,7 @@ export async function updateDistributionSite(
| 'settlement_hold_days'
| 'listing_qrcode_url'
| 'receive_ratio_delta'
| 'receive_cost_delta'
>
>
) {
@@ -59,7 +59,7 @@ const receiveRatioSubmitting = ref(false)
const receiveRatioForm = reactive({
site_id: 0,
site_name: '',
receive_ratio_delta: 0,
receive_cost_delta: 0,
})
const qrDialogVisible = ref(false)
@@ -136,7 +136,7 @@ async function handleSettlementSubmit() {
function handleOpenReceiveRatio(row: DistributionSite) {
receiveRatioForm.site_id = row.id
receiveRatioForm.site_name = row.site_name
receiveRatioForm.receive_ratio_delta = Number(row.receive_ratio_delta || 0)
receiveRatioForm.receive_cost_delta = Number(row.receive_cost_delta || 0)
receiveRatioDialogVisible.value = true
}
@@ -144,9 +144,9 @@ async function handleReceiveRatioSubmit() {
receiveRatioSubmitting.value = true
try {
await updateDistributionSite(receiveRatioForm.site_id, {
receive_ratio_delta: Number(receiveRatioForm.receive_ratio_delta),
receive_cost_delta: Number(receiveRatioForm.receive_cost_delta),
})
ElMessage.success(`【${receiveRatioForm.site_name}】收号比例下调已更新`)
ElMessage.success(`【${receiveRatioForm.site_name}】收号压价已更新`)
receiveRatioDialogVisible.value = false
await loadSites()
} catch (err) {
@@ -530,15 +530,15 @@ onMounted(() => {
</el-tag>
</template>
</el-table-column>
<el-table-column label="收号下调" width="110">
<el-table-column label="收号压价" width="110">
<template #default="{ row }">
<el-tag
:type="Number(row.receive_ratio_delta || 0) > 0 ? 'warning' : 'info'"
:type="Number(row.receive_cost_delta || 0) > 0 ? 'warning' : 'info'"
size="small"
>
{{
Number(row.receive_ratio_delta || 0) > 0
? `-${Number(row.receive_ratio_delta).toFixed(1)} 点`
Number(row.receive_cost_delta || 0) > 0
? `低 ${Number(row.receive_cost_delta).toFixed(1)} 点`
: '与主站一致'
}}
</el-tag>
@@ -758,10 +758,10 @@ onMounted(() => {
</template>
</el-dialog>
<!-- 分站收号比例(主站为上限) -->
<!-- 分站收号压价(收号价始终不高于主站参考价) -->
<el-dialog
v-model="receiveRatioDialogVisible"
title="分站收号比例"
title="分站收号压价"
width="520px"
destroy-on-close
>
@@ -769,16 +769,16 @@ onMounted(() => {
type="info"
:closable="false"
show-icon
title="主站回收比例是上限。下调点数越大,号主底价越高、分站利润空间越大,但不能高于主站比例。0 表示与主站一致。"
title="分站收号价比主站更低:压价点数越大,号主在分站拿到的收号价越低。买家售价保持与主站一致,压价差进入三方分成(35% 收号 / 40% 卖出 / 25% 服务费)。0 表示与主站同价收号。"
style="margin-bottom: 16px"
/>
<el-form label-width="110px">
<el-form-item label="分销站点">
<el-input :model-value="receiveRatioForm.site_name" disabled />
</el-form-item>
<el-form-item label="相对主站下调">
<el-form-item label="收号价比主站低">
<el-input-number
v-model="receiveRatioForm.receive_ratio_delta"
v-model="receiveRatioForm.receive_cost_delta"
:min="0"
:max="20"
:step="0.1"
@@ -58,6 +58,7 @@ export function usePublishForm(options: UsePublishFormOptions) {
const siteStore = useDistributionSiteStore()
const isDistribution = computed(() => siteStore.isDistribution)
const receiveRatioDelta = computed(() => Number(siteStore.receiveRatioDelta || 0))
const receiveCostDelta = computed(() => Number(siteStore.receiveCostDelta || 0))
const loading = ref(false)
const uploading = ref(false)
const suppressDraftSave = ref(false)
@@ -95,6 +96,7 @@ export function usePublishForm(options: UsePublishFormOptions) {
selectedSkins,
isDistribution,
receiveRatioDelta,
receiveCostDelta,
})
// ========== 上号方式互斥 ==========