fix(invitation): 邀请返利改用实际结算利润并修正提号基准

This commit is contained in:
yml2213
2026-10-29 11:49:08 +08:00
parent e57c9df6f4
commit 86fbc05e9b
7 changed files with 277 additions and 13 deletions
+17 -2
View File
@@ -308,8 +308,9 @@ func (r *Repository) Complete(ctx context.Context, pickupID uint64, req Complete
}
// 邀请返利:线下提号不走正常订单结账流程,必须在这里补发号主(卖家)侧返利,
// 否则号主邀请人永远拿不到提号产生的分成。基数按提号利润对半(与租赁订单卖家侧口径一致)。
if grossBasisCent := pickup.ProfitAmountCent; grossBasisCent > 0 {
// 否则号主邀请人永远拿不到提号产生的分成。
// 基准取「网站加价」与「线下利润」的较小值,避免按虚高的单一利润口径多算。
if grossBasisCent := pickupRebateGrossBasis(pickup.WebsiteProfitCent, pickup.ProfitAmountCent); grossBasisCent > 0 {
sellerSiteID := uint64(0)
if listing.DistributionSiteID != nil {
sellerSiteID = *listing.DistributionSiteID
@@ -1923,6 +1924,20 @@ func maxPickupCent(a, b int64) int64 {
return b
}
// pickupRebateGrossBasis 取网站加价与线下利润的较小值,负数按 0 处理。
func pickupRebateGrossBasis(websiteProfitCent, offlineProfitCent int64) int64 {
if websiteProfitCent < 0 {
websiteProfitCent = 0
}
if offlineProfitCent < 0 {
offlineProfitCent = 0
}
if websiteProfitCent < offlineProfitCent {
return websiteProfitCent
}
return offlineProfitCent
}
func normalizePaging(page, pageSize int) (int, int) {
if page < 1 {
page = 1
@@ -376,6 +376,22 @@ func TestRepositoryCompletePickupAwardsSellerInvitationRebate(t *testing.T) {
}
listing := seedAvailableListing(t, db, "LST-REBATE-001", "返利提号账号", false)
// 网站加价 40 元、线下利润 100 元:返利基准必须取较小的 40 元。
priceSummary, err := json.Marshal(map[string]any{
"price_breakdown": map[string]any{
"seller_coin_base_price": 60,
"seller_total_price": 60,
"platform_markup_amount": 40,
"consumable_price": 0,
},
})
if err != nil {
t.Fatalf("构造价格快照失败: %v", err)
}
if err := db.Model(&model.GameAccount{}).Where("id = ?", listing.AccountID).
Update("asset_summary", datatypes.JSON(priceSummary)).Error; err != nil {
t.Fatalf("更新账号价格快照失败: %v", err)
}
// 距提号完成 2 天 => 首月 60% 档;Truncate 到整秒避免 DATETIME(0) 四舍五入影响天数计算。
verifiedAt := time.Now().AddDate(0, 0, -2).Truncate(time.Second)
if err := db.Create(&model.UserInvitation{
@@ -420,9 +436,9 @@ func TestRepositoryCompletePickupAwardsSellerInvitationRebate(t *testing.T) {
if rebate.OrderNo != pickup.PickupNo {
t.Fatalf("返利单据号 = %q, want %q", rebate.OrderNo, pickup.PickupNo)
}
// 基数 = 提号利润 10000 对半 = 5000,首月 60% = 3000
assertInt64(t, "提号返利单边基数", rebate.SingleSideBasisCent, 5000)
assertInt64(t, "提号返利金额", rebate.RebateAmountCent, 3000)
// 基数 = min(网站加价 4000, 线下利润 10000) 对半 = 2000,首月 60% = 1200
assertInt64(t, "提号返利单边基数", rebate.SingleSideBasisCent, 2000)
assertInt64(t, "提号返利金额", rebate.RebateAmountCent, 1200)
var wallet model.WalletLedger
if err := db.Where("biz_type = ? AND biz_no = ?", "invite_rebate", pickup.PickupNo).First(&wallet).Error; err != nil {
@@ -431,7 +447,29 @@ func TestRepositoryCompletePickupAwardsSellerInvitationRebate(t *testing.T) {
if wallet.UserID != inviter.ID {
t.Fatalf("返利入账用户 = %d, want %d", wallet.UserID, inviter.ID)
}
assertInt64(t, "提号返利入账金额", wallet.AmountCent, 3000)
assertInt64(t, "提号返利入账金额", wallet.AmountCent, 1200)
}
func TestPickupRebateGrossBasisTakesLowerProfit(t *testing.T) {
cases := []struct {
name string
websiteProfit int64
offlineProfit int64
wantGrossBasis int64
}{
{name: "网站加价更低", websiteProfit: 4000, offlineProfit: 10000, wantGrossBasis: 4000},
{name: "线下利润更低", websiteProfit: 10000, offlineProfit: 4000, wantGrossBasis: 4000},
{name: "线下利润为零", websiteProfit: 10000, offlineProfit: 0, wantGrossBasis: 0},
{name: "负数归零", websiteProfit: -1, offlineProfit: 4000, wantGrossBasis: 0},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if got := pickupRebateGrossBasis(tc.websiteProfit, tc.offlineProfit); got != tc.wantGrossBasis {
t.Fatalf("pickupRebateGrossBasis(%d, %d) = %d, want %d",
tc.websiteProfit, tc.offlineProfit, got, tc.wantGrossBasis)
}
})
}
}
func TestRepositoryCompletePlatformManagedPickupUsesOfflineSettlement(t *testing.T) {
@@ -1145,4 +1183,3 @@ func TestRepositoryUpdateThirdPartyOrderNo(t *testing.T) {
}
})
}