fix(invitation): 返利归因统一为「订单所属站点」,买卖两端一致
- 买家端不再强制记主站(原 buyerSiteID=0),卖家端不再记拆分方站点,两端统一取 orderSiteID - orderSiteID = 订单所属站点(>0 时);订单未记站点但存在分销拆分时回退到拆分方站点,避免主站替分站订单买单 - 该站点同时用于取返利配置(GetRebateConfig)、返利流水与钱包流水的 distribution_site_id,分站后台由此能看到本站返利支出 - 基数口径未改动,非分销订单行为与改前一致;历史 9 笔数据不订正,规则自此生效 - 测试:更新跨站用例断言;新增订单站点归因用例(校验站点专属比例 0.5 与钱包流水站点维度)
This commit is contained in:
@@ -386,27 +386,30 @@ func (r *Repository) ProcessOrderInvitationRebate(ctx context.Context, tx *gorm.
|
||||
|
||||
now := time.Now()
|
||||
|
||||
// 返利归因站点(统一规则):成本一律由「订单所属站点」承担,并使用该站点自己的返利配置。
|
||||
// 订单未记录站点但存在分销拆分时,回退到拆分方站点(钱实际归它),
|
||||
// 避免再次出现「主站替分站订单买单」的倒贴。
|
||||
orderSiteID := distributorSiteID
|
||||
if order.DistributionSiteID != nil && *order.DistributionSiteID > 0 {
|
||||
orderSiteID = *order.DistributionSiteID
|
||||
}
|
||||
|
||||
// 1. 买家端处理(租客 RenterID)
|
||||
var buyerInvitation model.UserInvitation
|
||||
if err := tx.Where("invitee_id = ? AND status = ?", order.RenterID, StatusActive).First(&buyerInvitation).Error; err == nil {
|
||||
if buyerInvitation.VerifiedAt != nil {
|
||||
var inviter model.User
|
||||
if err := tx.First(&inviter, buyerInvitation.InviterID).Error; err == nil && inviter.RealnameStatus == "verified" && inviter.ID != order.RenterID {
|
||||
// 买家端基数:若有分销售卖拆分按 platformSellCent,否则按整单毛利对半拆
|
||||
// 买家端基数:有分销售卖拆分按 platformSellCent,否则按整单毛利对半拆;归因站点统一取 orderSiteID
|
||||
var buyerBasisCent int64
|
||||
var buyerSiteID uint64
|
||||
if platformSellCent > 0 {
|
||||
buyerBasisCent = int64(math.Floor(float64(platformSellCent) / 2.0))
|
||||
buyerSiteID = 0
|
||||
} else {
|
||||
buyerBasisCent = int64(math.Floor(float64(orderGrossProfitCent) / 2.0))
|
||||
if order.DistributionSiteID != nil {
|
||||
buyerSiteID = *order.DistributionSiteID
|
||||
}
|
||||
}
|
||||
|
||||
if buyerBasisCent > 0 {
|
||||
cfg, _ := r.GetRebateConfig(ctx, buyerSiteID)
|
||||
cfg, _ := r.GetRebateConfig(ctx, orderSiteID)
|
||||
if cfg == nil || cfg.Enabled {
|
||||
rate, cycle, days := CalculateRebateRate(*buyerInvitation.VerifiedAt, now, cfg)
|
||||
rebateCent := int64(math.Round(float64(buyerBasisCent) * rate))
|
||||
@@ -415,7 +418,7 @@ func (r *Repository) ProcessOrderInvitationRebate(ctx context.Context, tx *gorm.
|
||||
ledger := model.InvitationRebateLedger{
|
||||
OrderID: order.ID,
|
||||
OrderNo: order.OrderNo,
|
||||
DistributionSiteID: buyerSiteID,
|
||||
DistributionSiteID: orderSiteID,
|
||||
InviterID: buyerInvitation.InviterID,
|
||||
InviteeID: order.RenterID,
|
||||
TradeRole: "buyer",
|
||||
@@ -432,7 +435,7 @@ func (r *Repository) ProcessOrderInvitationRebate(ctx context.Context, tx *gorm.
|
||||
if err := tx.Clauses(clause.OnConflict{DoNothing: true}).Create(&ledger).Error; err == nil && ledger.ID > 0 {
|
||||
_ = wallet.AppendEntries(tx, wallet.Entry{
|
||||
UserID: buyerInvitation.InviterID,
|
||||
DistributionSiteID: buyerSiteID,
|
||||
DistributionSiteID: orderSiteID,
|
||||
Direction: "in",
|
||||
AmountCent: rebateCent,
|
||||
BalanceType: "available",
|
||||
@@ -454,21 +457,16 @@ func (r *Repository) ProcessOrderInvitationRebate(ctx context.Context, tx *gorm.
|
||||
if sellerInvitation.VerifiedAt != nil {
|
||||
var inviter model.User
|
||||
if err := tx.First(&inviter, sellerInvitation.InviterID).Error; err == nil && inviter.RealnameStatus == "verified" && inviter.ID != order.OwnerID {
|
||||
// 卖家端基数:若有分站收号拆分按 distributorReceiveCent(从分站出),否则按整单毛利对半拆
|
||||
// 卖家端基数:有分站收号拆分按 distributorReceiveCent,否则按整单毛利对半拆;归因站点统一取 orderSiteID
|
||||
var sellerBasisCent int64
|
||||
var sellerSiteID uint64
|
||||
if distributorReceiveCent > 0 {
|
||||
sellerBasisCent = int64(math.Floor(float64(distributorReceiveCent) / 2.0))
|
||||
sellerSiteID = distributorSiteID
|
||||
} else {
|
||||
sellerBasisCent = int64(math.Floor(float64(orderGrossProfitCent) / 2.0))
|
||||
if order.DistributionSiteID != nil {
|
||||
sellerSiteID = *order.DistributionSiteID
|
||||
}
|
||||
}
|
||||
|
||||
if sellerBasisCent > 0 {
|
||||
cfg, _ := r.GetRebateConfig(ctx, sellerSiteID)
|
||||
cfg, _ := r.GetRebateConfig(ctx, orderSiteID)
|
||||
if cfg == nil || cfg.Enabled {
|
||||
rate, cycle, days := CalculateRebateRate(*sellerInvitation.VerifiedAt, now, cfg)
|
||||
rebateCent := int64(math.Round(float64(sellerBasisCent) * rate))
|
||||
@@ -477,7 +475,7 @@ func (r *Repository) ProcessOrderInvitationRebate(ctx context.Context, tx *gorm.
|
||||
ledger := model.InvitationRebateLedger{
|
||||
OrderID: order.ID,
|
||||
OrderNo: order.OrderNo,
|
||||
DistributionSiteID: sellerSiteID,
|
||||
DistributionSiteID: orderSiteID,
|
||||
InviterID: sellerInvitation.InviterID,
|
||||
InviteeID: order.OwnerID,
|
||||
TradeRole: "seller",
|
||||
@@ -494,7 +492,7 @@ func (r *Repository) ProcessOrderInvitationRebate(ctx context.Context, tx *gorm.
|
||||
if err := tx.Clauses(clause.OnConflict{DoNothing: true}).Create(&ledger).Error; err == nil && ledger.ID > 0 {
|
||||
_ = wallet.AppendEntries(tx, wallet.Entry{
|
||||
UserID: sellerInvitation.InviterID,
|
||||
DistributionSiteID: sellerSiteID,
|
||||
DistributionSiteID: orderSiteID,
|
||||
Direction: "in",
|
||||
AmountCent: rebateCent,
|
||||
BalanceType: "available",
|
||||
|
||||
Reference in New Issue
Block a user