feat(distribution): 补齐退款加价回退、本站自卖与开通默认规则

退款时压缩卖方加价,人工/自动批次仅完结单可入账,号主收入进主站钱包;仲裁后重算释放并支持作废未入账 DS。开通时四种模式按默认生效,对外口径合并为 35/40/25,不再单独展示通道费。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
yml2213
2026-09-10 18:13:46 +08:00
co-authored by Cursor
parent e621722218
commit 768902f2f7
28 changed files with 627 additions and 263 deletions
@@ -97,13 +97,46 @@ func DefaultRule(mode string) (Rule, error) {
base.DistributorARatio = DefaultReceivePercent
base.DistributorBRatio = DefaultSellPercent
case ModeSiteSelfSell:
return Rule{}, ErrUnsupportedMode
base.DistributorSellRatio = DefaultReceivePercent + DefaultSellPercent
default:
return Rule{}, ErrUnsupportedMode
}
return base, nil
}
// ValidateRule 校验模式,并在百分比服务费下要求收号+卖出+服务费合计为 100。
func ValidateRule(rule Rule) error {
normalizeRulePercents(&rule)
if _, err := DefaultRule(rule.Mode); err != nil {
return ErrUnsupportedMode
}
unit := rule.ServiceFeeUnit
if unit == "" || unit == ServiceFeeUnitRatio {
unit = ServiceFeeUnitPercent
}
if unit != ServiceFeeUnitPercent && unit != ServiceFeeUnitFixedAmount {
return ErrInvalidServiceUnit
}
if unit != ServiceFeeUnitPercent {
return nil
}
var sum float64
switch rule.Mode {
case ModeTwoPartyDistributorReceive:
sum = rule.DistributorReceiveRatio + rule.PlatformSellRatio + rule.ServiceFeeValue
case ModeTwoPartyPlatformReceive:
sum = rule.PlatformReceiveRatio + rule.DistributorSellRatio + rule.ServiceFeeValue
case ModeThreeParty:
sum = rule.DistributorARatio + rule.DistributorBRatio + rule.ServiceFeeValue
case ModeSiteSelfSell:
sum = rule.DistributorSellRatio + rule.ServiceFeeValue
}
if math.Abs(sum-100) > 0.15 {
return ErrInvalidRate
}
return nil
}
// NormalizeSplitPercent 把口头 3.5/4/2.5 换成 35/40/25;已经是百分数的原样保留。
func NormalizeSplitPercent(value float64) float64 {
if value <= 0 || math.IsNaN(value) || math.IsInf(value, 0) {
@@ -147,7 +180,8 @@ func CalculateSettlement(rule Rule, input SettlementInput) (SettlementResult, er
}
netPaid := input.GrossPaidAmountCent - input.RefundAmountCent
paymentFee := roundHalfUp(float64(netPaid) * float64(rule.PaymentFeeBPS) / 10000)
profit := netPaid - input.OwnerRentAmountCent - input.MarkupAmountCent
effectiveMarkup := EffectiveMarkupCent(netPaid, input.OwnerRentAmountCent, input.MarkupAmountCent)
profit := netPaid - input.OwnerRentAmountCent - effectiveMarkup
if profit < 0 {
profit = 0
}
@@ -164,7 +198,7 @@ func CalculateSettlement(rule Rule, input SettlementInput) (SettlementResult, er
NetPaidAmountCent: netPaid,
PaymentFeeCent: paymentFee,
ServiceFeeCent: serviceFee,
MarkupAmountCent: input.MarkupAmountCent,
MarkupAmountCent: effectiveMarkup,
SplitBaseAmountCent: profit,
}
if rule.TaxEnabled {
@@ -206,6 +240,25 @@ func maxInt64(a, b int64) int64 {
return b
}
func minInt64(a, b int64) int64 {
if a < b {
return a
}
return b
}
// EffectiveMarkupCent 按剩余净付款压缩加价:全额退租金后卖方不得再留加价。
func EffectiveMarkupCent(netPaid, ownerRent, markup int64) int64 {
if markup <= 0 {
return 0
}
remain := netPaid - ownerRent
if remain <= 0 {
return 0
}
return minInt64(markup, remain)
}
// PartySplit 是一次分成拆分中单个参与方的金额。
type PartySplit struct {
PartyRole string
@@ -236,6 +289,11 @@ func SplitSettlement(rule Rule, profitCent int64, markupCent int64) []PartySplit
role string
ratio float64
}{{"distributor_a", rule.DistributorARatio}, {"distributor_b", rule.DistributorBRatio}}
case ModeSiteSelfSell:
roles = []struct {
role string
ratio float64
}{{"distributor_sell", rule.DistributorSellRatio}}
default:
return nil
}