feat(listing): 极品专区发布时增加比例加价

This commit is contained in:
yml2213
2026-11-04 20:54:11 +08:00
parent aeda6f4cd3
commit 5ac1afd573
11 changed files with 149 additions and 0 deletions
@@ -98,6 +98,28 @@ func applyMainSiteReceivePricing(req *CreateRequest, options systemconfig.Publis
buyerAnchorCoinBase = referenceSellerCoinBasePrice(coinWan, referenceRatio)
}
pricing := calculateExternalPlatformPricing(coinM, buyerAnchorRatio, buyerAnchorCoinBase, consumablePrice, config)
premiumRatioMarkup := 0.0
if req.IsPremium {
premiumRatioMarkup = NormalizePremiumReviewRatioMarkup(
options.RatioConfig.PremiumReviewRatioMarkup,
)
}
if premiumRatioMarkup > 0 && pricing.BuyerRatio > premiumRatioMarkup {
beforeRatio := pricing.BuyerRatio
afterRatio := roundRatio(beforeRatio - premiumRatioMarkup)
buyerCoinBasePrice := roundMoney(coinWan / afterRatio)
pricing = buildExternalPlatformPricing(
coinWan,
sellerTotalPrice,
consumablePrice,
buyerCoinBasePrice,
pricing.RuleType,
)
breakdown["premium_review_ratio_markup"] = premiumRatioMarkup
breakdown["premium_review_ratio_before"] = beforeRatio
breakdown["premium_review_ratio_after"] = pricing.BuyerRatio
breakdown["premium_review_ratio_markup_applied"] = true
}
breakdown["seller_reference_ratio"] = referenceRatio
breakdown["seller_ratio"] = sellerRatio
breakdown["seller_coin_base_price"] = sellerCoinBasePrice
@@ -170,6 +192,17 @@ func NormalizeQualityOwnerRatioDiscount(value float64) float64 {
return roundRatio(value)
}
// NormalizePremiumReviewRatioMarkup 限制极品专区发布时额外增加的比例加价点数,最大 10。
func NormalizePremiumReviewRatioMarkup(value float64) float64 {
if value < 0 || math.IsNaN(value) || math.IsInf(value, 0) {
return 0
}
if value > maxAcceleratedSaleRatioIncrement {
return maxAcceleratedSaleRatioIncrement
}
return roundRatio(value)
}
// referenceSellerCoinBasePrice 计算主站参考比例下的号主基础价,用作买家定价锚点。
func referenceSellerCoinBasePrice(coinWan, referenceRatio float64) float64 {
if referenceRatio <= 0 {
@@ -82,6 +82,28 @@ func TestApplyMainSiteReceivePricingKeepsAcceleratedRatioWithinMainSiteRange(t *
}
}
func TestApplyMainSiteReceivePricingIncludesPremiumReviewMarkup(t *testing.T) {
req := fullReceivePricingRequest(50)
req.IsPremium = true
applyMainSiteReceivePricing(&req, systemconfig.DefaultPublishOptions(), defaultSalePriceConfig(), 0, 0, false)
breakdown := ensurePriceBreakdown(req.AssetSummary)
if got := readSummaryNumber(breakdown["premium_review_ratio_markup"]); got != 1 {
t.Fatalf("premium review markup = %v, want 1", got)
}
if got := readSummaryNumber(breakdown["premium_review_ratio_before"]); got != 45 {
t.Fatalf("premium buyer ratio before = %v, want 45", got)
}
if got := readSummaryNumber(breakdown["premium_review_ratio_after"]); got != 44 {
t.Fatalf("premium buyer ratio after = %v, want 44", got)
}
if applied, _ := breakdown["premium_review_ratio_markup_applied"].(bool); !applied {
t.Fatal("premium review markup should be applied before review")
}
if req.PriceCent != 22730 {
t.Fatalf("premium PriceCent = %d, want final 22730", req.PriceCent)
}
}
func TestApplyMainSiteReceivePricingAllowsQualityOwnerExclusiveRatio(t *testing.T) {
req := fullReceivePricingRequest(42)
req.OwnerIsQualityOwner = true
@@ -183,6 +183,7 @@ type PublishDepositSkinGroupRule struct {
type PublishRatioConfig struct {
QualityOwnerRatioDiscount float64 `json:"quality_owner_ratio_discount"`
PremiumReviewRatioMarkup float64 `json:"premium_review_ratio_markup"`
InsuranceBaseRatios []PublishInsuranceBaseRatio `json:"insurance_base_ratios"`
ConfigItems []PublishRatioConfigItem `json:"config_items"`
CoinCorrections []PublishCoinCorrection `json:"coin_corrections"`
@@ -252,6 +252,19 @@ func normalizePublishOptions(options *PublishOptionsDTO) {
if len(options.RatioConfig.CoinCorrections) == 0 {
options.RatioConfig.CoinCorrections = defaults.RatioConfig.CoinCorrections
}
options.RatioConfig.PremiumReviewRatioMarkup = normalizePremiumReviewRatioMarkup(
options.RatioConfig.PremiumReviewRatioMarkup,
)
}
func normalizePremiumReviewRatioMarkup(value float64) float64 {
if value < 0 || math.IsNaN(value) || math.IsInf(value, 0) {
return 0
}
if value > 10 {
return 10
}
return math.Round(value*10) / 10
}
func normalizeSalePriceConfig(config *PublishSalePriceConfig) {
@@ -106,6 +106,7 @@ func DefaultPublishOptions() PublishOptionsDTO {
},
RatioConfig: PublishRatioConfig{
QualityOwnerRatioDiscount: 1,
PremiumReviewRatioMarkup: 1,
InsuranceBaseRatios: []PublishInsuranceBaseRatio{
{Insurance: "3*3", Ratio: 40},
{Insurance: "2*3", Ratio: 42},
@@ -128,6 +128,36 @@ func TestNormalizePublishOptionsValueDefaultsQualityOwnerRatioDiscount(t *testin
got.RatioConfig.QualityOwnerRatioDiscount,
)
}
if got.RatioConfig.PremiumReviewRatioMarkup != 1 {
t.Fatalf(
"premium_review_ratio_markup = %v, want 1",
got.RatioConfig.PremiumReviewRatioMarkup,
)
}
}
func TestNormalizePublishOptionsValuePreservesExplicitZeroPremiumReviewRatioMarkup(t *testing.T) {
options := DefaultPublishOptions()
options.RatioConfig.PremiumReviewRatioMarkup = 0
raw, err := json.Marshal(options)
if err != nil {
t.Fatalf("构造配置失败: %v", err)
}
normalized, err := normalizePublishOptionsValue(string(raw))
if err != nil {
t.Fatalf("规范化失败: %v", err)
}
var got PublishOptionsDTO
if err := json.Unmarshal([]byte(normalized), &got); err != nil {
t.Fatalf("解析规范化结果失败: %v", err)
}
if got.RatioConfig.PremiumReviewRatioMarkup != 0 {
t.Fatalf(
"premium_review_ratio_markup = %v, want explicit 0",
got.RatioConfig.PremiumReviewRatioMarkup,
)
}
}
func TestNormalizePublishOptionsValuePreservesExplicitZeroQualityOwnerDiscount(t *testing.T) {
@@ -337,6 +337,10 @@ func normalizePublishOptionsValue(value string) (string, error) {
options.RatioConfig.QualityOwnerRatioDiscount =
DefaultPublishOptions().RatioConfig.QualityOwnerRatioDiscount
}
if !hasNestedJSONField(fields, "ratio_config", "premium_review_ratio_markup") {
options.RatioConfig.PremiumReviewRatioMarkup =
DefaultPublishOptions().RatioConfig.PremiumReviewRatioMarkup
}
originalVersion := options.SchemaVersion
if options.SchemaVersion < publishOptionsSchemaVersion {
if hasLegacyPublishQuantityItem(options.QuantityItems) {
@@ -412,6 +412,17 @@ async function handleSave() {
默认 1,表示优质号主可在基础比例上降低 1;普通号主不能使用该区间。
</span>
</el-form-item>
<el-form-item label="极品专区比例加价(发布时生效)">
<el-input-number
v-model="publishOptionsDraft.ratio_config.premium_review_ratio_markup"
:min="0"
:max="10"
:step="0.5"
/>
<span class="inline-config-hint">
默认 1;极品专区商品发布时预加该比例点数,审核页直接展示最终价格,审核通过不再改价。
</span>
</el-form-item>
<div class="editor-block-title subtle-title">
<span>保险基础比例</span>
<el-button size="small" @click="addInsuranceBaseRatio">添加保险比例</el-button>
@@ -630,6 +630,11 @@
font-weight: 700;
}
.premium-review-note {
color: #c2410c !important;
font-weight: 800;
}
.pipeline-card--buyer {
border: 2px solid #2563eb;
background: linear-gradient(135deg, #eff6ff 0%, #e0f2fe 100%);
@@ -558,6 +558,18 @@ function adminAdjustReason(row: Listing) {
return breakdownText(row, 'admin_adjust_reason')
}
function premiumReviewMarkupApplied(row: Listing) {
return breakdownNumber(row, 'premium_review_ratio_markup')
}
function premiumReviewRatioBefore(row: Listing) {
return breakdownNumber(row, 'premium_review_ratio_before')
}
function premiumReviewRatioAfter(row: Listing) {
return breakdownNumber(row, 'premium_review_ratio_after')
}
function priceRuleText(row: Listing) {
const markup = platformMarkupAmount(row)
if (priceRuleType(row) === 'admin_adjusted') {
@@ -1206,6 +1218,9 @@ async function handleScreenshotSaved(payload: { oldUrl: string; newUrl: string }
<h2 class="hero-title">{{ selectedListing.title }}</h2>
<div class="risk-tag-strip">
<el-tag v-if="selectedListing.is_premium" type="warning" effect="dark" round>
极品专区
</el-tag>
<el-tag
v-for="risk in activeRisks"
:key="risk.label"
@@ -1266,6 +1281,14 @@ async function handleScreenshotSaved(payload: { oldUrl: string; newUrl: string }
</div>
<div class="card-breakdown">
<span>毛利率 ~{{ platformMarkupPercentage(selectedListing) }}%</span>
<span
v-if="premiumReviewMarkupApplied(selectedListing) > 0"
class="premium-review-note"
>
已含审核加价 +{{ quantity(premiumReviewMarkupApplied(selectedListing)) }} ·
{{ ratioText(premiumReviewRatioBefore(selectedListing)) }} →
{{ ratioText(premiumReviewRatioAfter(selectedListing)) }}
</span>
<span
v-if="adminAdjustReason(selectedListing)"
class="admin-reason"
@@ -79,6 +79,7 @@ export interface PublishSaleRatioAdjustmentRule {
export interface PublishRatioConfig {
quality_owner_ratio_discount: number
premium_review_ratio_markup: number
insurance_base_ratios: PublishInsuranceBaseRatio[]
config_items: PublishRatioConfigItem[]
coin_corrections: PublishCoinCorrection[]
@@ -153,6 +154,7 @@ export const emptyListingPublishOptions: ListingPublishOptions = {
},
ratio_config: {
quality_owner_ratio_discount: 1,
premium_review_ratio_markup: 1,
insurance_base_ratios: [],
config_items: [],
coin_corrections: [],
@@ -369,6 +371,10 @@ function normalizeRatioConfig(value?: unknown): PublishRatioConfig {
10,
Math.max(0, readNumber(row.quality_owner_ratio_discount ?? 1))
),
premium_review_ratio_markup: Math.min(
10,
Math.max(0, readNumber(row.premium_review_ratio_markup ?? 1))
),
insurance_base_ratios: normalizeInsuranceBaseRatios(
Array.isArray(row.insurance_base_ratios) ? row.insurance_base_ratios : []
),