fix: 按实际纯币结算金额计算保险费用

This commit is contained in:
yml2213
2026-11-05 19:19:12 +08:00
parent 53574236ef
commit b884d01855
14 changed files with 184 additions and 25 deletions
+1 -1
View File
@@ -406,7 +406,7 @@ func calculateCheckoutSettlement(order model.RentalOrder, consumableAmountCent i
// 可完结时租客实际承担的租金向金额;有 shortfall 时仍展示完整应付便于协商
actualRentAmountCent := actualBuyerRentCent
ownerRentIncomeCent := actualOwnerRentCent
insuranceFeeCent := calculateInsuranceFeeCent(ownerRentIncomeCent, order.InsuranceFeeBps)
insuranceFeeCent := calculateInsuranceFeeCent(usedOwnerCoinPriceCent, order.InsuranceFeeBps)
ownerRentIncomeCent = maxCent(ownerRentIncomeCent-insuranceFeeCent, 0)
platformFeeCent := maxCent(actualRentAmountCent-ownerRentIncomeCent, 0)
depositCompensationCent := damageAppliedCent
@@ -49,13 +49,48 @@ func TestCalculateCheckoutSettlementDeductsPremiumInsuranceFee(t *testing.T) {
}
settlement := calculateCheckoutSettlement(order, 12000, 100, 0)
if settlement.InsuranceFeeBps != 400 || settlement.InsuranceFeeCent != 1412 {
if settlement.InsuranceFeeBps != 400 || settlement.InsuranceFeeCent != 932 {
t.Fatalf("保险费用快照不正确: bps=%d fee=%d", settlement.InsuranceFeeBps, settlement.InsuranceFeeCent)
}
if settlement.OwnerRentIncomeCent != 33888 {
t.Fatalf("号主净租金 = %d, want 33888", settlement.OwnerRentIncomeCent)
if settlement.OwnerRentIncomeCent != 34368 {
t.Fatalf("号主净租金 = %d, want 34368", settlement.OwnerRentIncomeCent)
}
if settlement.PlatformFeeCent != 4412 {
t.Fatalf("平台收入 = %d, want 4412", settlement.PlatformFeeCent)
if settlement.PlatformFeeCent != 3932 {
t.Fatalf("平台收入 = %d, want 3932", settlement.PlatformFeeCent)
}
}
func TestCalculateCheckoutSettlementUsesActualPureCoinAsInsuranceBase(t *testing.T) {
order := model.RentalOrder{
RentAmountCent: 25000,
OwnerRentAmountCent: 25000,
DepositAmountCent: 15000,
InsuranceFeeBps: 800,
AccountSnapshot: datatypes.JSON([]byte(`{
"haf_coin_amount": 100000000,
"asset_summary": {
"price_breakdown": {
"buyer_ratio": 40,
"seller_ratio": 40,
"buyer_coin_base_price": 250,
"seller_coin_base_price": 250,
"consumable_price": 0
}
}
}`)),
}
settlement := calculateCheckoutSettlement(order, 5000, 50, 0)
if settlement.PureCoinAmountCent != 12500 {
t.Fatalf("实际纯币原价 = %d, want 12500", settlement.PureCoinAmountCent)
}
if settlement.InsuranceFeeCent != 1000 {
t.Fatalf("保险费用 = %d, want 1000", settlement.InsuranceFeeCent)
}
if settlement.ExtraItemAmountCent != 5000 {
t.Fatalf("额外物品 = %d, want 5000", settlement.ExtraItemAmountCent)
}
if settlement.OwnerRentIncomeCent != 16500 {
t.Fatalf("号主净租金 = %d, want 16500", settlement.OwnerRentIncomeCent)
}
}
@@ -9,6 +9,21 @@ import (
const defaultBannerHotspotHoverScale = 1.06
const maxBannerHotspotHoverScale = 1.5
func migrateInsuranceBillingCopy(value string) string {
value = strings.Replace(
value,
"注:选择此方案,需额外抽取订单佣金的8.0%作为服务费:押金须达到推荐押金方可购买。",
"无忧保险服务费按实际纯币结算金额计算,额外物品不计入计费基数;具体费率以当前配置为准。押金须达到推荐押金方可购买。",
1,
)
return strings.Replace(
value,
"并从卖家实际到手租金中扣除。",
"并按实际纯币结算金额计算,额外物品不计入保险基数。",
1,
)
}
func normalizeAnnouncements(items []string) []string {
normalized := make([]string, 0, len(items))
for _, item := range items {
@@ -236,9 +251,15 @@ func normalizePublishOptions(options *PublishOptionsDTO) {
}
if options.PriceConfig.PremiumZoneTooltip == "" {
options.PriceConfig.PremiumZoneTooltip = defaults.PriceConfig.PremiumZoneTooltip
} else {
options.PriceConfig.PremiumZoneTooltip =
migrateInsuranceBillingCopy(options.PriceConfig.PremiumZoneTooltip)
}
if options.PriceConfig.CompensationNote == "" {
options.PriceConfig.CompensationNote = defaults.PriceConfig.CompensationNote
} else {
options.PriceConfig.CompensationNote =
migrateInsuranceBillingCopy(options.PriceConfig.CompensationNote)
}
if options.PriceConfig.PricePlaceholder == "" {
options.PriceConfig.PricePlaceholder = defaults.PriceConfig.PricePlaceholder
@@ -120,9 +120,9 @@ func DefaultPublishOptions() PublishOptionsDTO {
1. 发布条件:仅已认证且有效的优质号主可以开启;未开通时需先缴纳平台要求的信用保证金并完成优质号主认证。
2. 专区展示:商品审核通过后将在首页“极品专区”展示,获得更聚焦的高端账号曝光。
3. 价格规则:系统会按后台配置在基础比例上计算极品专区专享比例,审核页展示的最终价格通过后不再改价。
4. 保险服务:可选择无忧租号保险,服务费率按优质号主及极品专区的当前配置执行,并从卖家实际到手租金中扣除。
4. 保险服务:可选择无忧租号保险,服务费率按优质号主及极品专区的当前配置执行,并按实际纯币结算金额计算,额外物品不计入保险基数。
5. 责任说明:开启专区不会免除商品审核、资料真实性及发布协议中的责任,请如实填写账号资产与出租条件。`,
CompensationNote: "普通赔付方案如下:账号在租期内或祖期外出现封禁,追回、洗号等情况,最高可获赔全款相金+押金\n\n无忧保险赔付方案:按账号市场价值进行赔付(可参照螃蟹,盼之等主流交易平台)。注:选择此方案,需额外抽取订单佣金的8.0%作为服务费:押金须达到推荐押金方可购买。",
CompensationNote: "普通赔付方案如下:账号在租期内或祖期外出现封禁,追回、洗号等情况,最高可获赔全款相金+押金\n\n无忧保险赔付方案:按账号市场价值进行赔付(可参照螃蟹,盼之等主流交易平台)。无忧保险服务费按实际纯币结算金额计算,额外物品不计入计费基数;具体费率以当前配置为准。押金须达到推荐押金方可购买。",
PricePlaceholder: "填写币数、保险、体力和负重后自动计算;发布总价=纯币基础价+额外消耗品",
RatioDescription: "1:xx 表示 1 元人民币(RMB)等价兑换 xx 万哈夫币",
},
@@ -178,6 +178,33 @@ func TestNormalizePublishOptionsValueDefaultsPremiumZoneTooltip(t *testing.T) {
}
}
func TestNormalizePublishOptionsValueMigratesInsuranceBillingBaseCopy(t *testing.T) {
options := DefaultPublishOptions()
options.PriceConfig.CompensationNote = "注:选择此方案,需额外抽取订单佣金的8.0%作为服务费:押金须达到推荐押金方可购买。"
options.PriceConfig.PremiumZoneTooltip = "4. 保险服务:服务费率按当前配置执行,并从卖家实际到手租金中扣除。"
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 !strings.Contains(got.PriceConfig.CompensationNote, "按实际纯币结算金额计算") {
t.Fatalf("赔付说明未迁移纯币计费口径: %q", got.PriceConfig.CompensationNote)
}
if strings.Contains(got.PriceConfig.CompensationNote, "订单佣金") {
t.Fatalf("赔付说明仍含旧计费口径: %q", got.PriceConfig.CompensationNote)
}
if !strings.Contains(got.PriceConfig.PremiumZoneTooltip, "额外物品不计入保险基数") {
t.Fatalf("专区说明未迁移纯币计费口径: %q", got.PriceConfig.PremiumZoneTooltip)
}
}
func TestNormalizePublishOptionsValuePreservesExplicitZeroPremiumReviewRatioMarkup(t *testing.T) {
options := DefaultPublishOptions()
options.RatioConfig.PremiumReviewRatioMarkup = 0