f
This commit is contained in:
@@ -1,15 +1,15 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bdqr-server/app/main/api/internal/config"
|
||||
"bdqr-server/app/main/model"
|
||||
"bdqr-server/common/globalkey"
|
||||
"bdqr-server/pkg/lzkit/lzUtils"
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
"bdqr-server/app/main/api/internal/config"
|
||||
"bdqr-server/app/main/model"
|
||||
"bdqr-server/common/globalkey"
|
||||
"bdqr-server/pkg/lzkit/lzUtils"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
@@ -168,7 +168,7 @@ func (s *AgentService) AgentProcess(ctx context.Context, order *model.Order) err
|
||||
func (s *AgentService) getLevelBonus(ctx context.Context, level int64) (int64, error) {
|
||||
var configKey string
|
||||
switch level {
|
||||
case 1: // 普通
|
||||
case 1: // 白银
|
||||
configKey = "level_1_bonus"
|
||||
case 2: // 黄金
|
||||
configKey = "level_2_bonus"
|
||||
@@ -346,36 +346,36 @@ func (s *AgentService) distributeLevelBonus(ctx context.Context, session sqlx.Se
|
||||
// 分配规则总览:
|
||||
// 1. 直接上级是钻石:等级加成全部给钻石
|
||||
// 2. 直接上级是黄金:一部分给黄金(配置:direct_parent_amount_gold,默认3元),剩余给钻石上级
|
||||
// 3. 直接上级是普通:一部分给直接上级(配置:direct_parent_amount_normal,默认2元),剩余给钻石/黄金上级
|
||||
// 3. 直接上级是白银:一部分给直接上级(配置:direct_parent_amount_normal,默认2元),剩余给钻石/黄金上级
|
||||
//
|
||||
// 覆盖的所有情况:
|
||||
//
|
||||
// 情况1:普通(推广人) -> 钻石(直接上级)
|
||||
// 情况1:白银(推广人) -> 钻石(直接上级)
|
||||
// => 全部给钻石
|
||||
//
|
||||
// 情况2:普通(推广人) -> 黄金(直接上级) -> 钻石
|
||||
// 情况2:白银(推广人) -> 黄金(直接上级) -> 钻石
|
||||
// => 一部分给黄金,剩余给钻石
|
||||
//
|
||||
// 情况3:普通(推广人) -> 黄金(直接上级) -> 无钻石上级
|
||||
// 情况3:白银(推广人) -> 黄金(直接上级) -> 无钻石上级
|
||||
// => 一部分给黄金,剩余归平台
|
||||
//
|
||||
// 情况4:普通(推广人) -> 普通(直接上级) -> 钻石
|
||||
// => 一部分给直接上级普通,剩余全部给钻石
|
||||
// 情况4:白银(推广人) -> 白银(直接上级) -> 钻石
|
||||
// => 一部分给直接上级白银,剩余全部给钻石
|
||||
//
|
||||
// 情况5:普通(推广人) -> 普通(直接上级) -> 黄金 -> 钻石
|
||||
// => 一部分给直接上级普通(例如2元),一部分给黄金(等级加成差减去给普通的,例如3-2=1元),剩余给钻石(例如3元)
|
||||
// 情况5:白银(推广人) -> 白银(直接上级) -> 黄金 -> 钻石
|
||||
// => 一部分给直接上级白银(例如2元),一部分给黄金(等级加成差减去给白银的,例如3-2=1元),剩余给钻石(例如3元)
|
||||
//
|
||||
// 情况6:普通(推广人) -> 普通(直接上级) -> 黄金(无钻石)
|
||||
// => 一部分给直接上级普通,剩余一部分给黄金(最多3元),超出归平台
|
||||
// 情况6:白银(推广人) -> 白银(直接上级) -> 黄金(无钻石)
|
||||
// => 一部分给直接上级白银,剩余一部分给黄金(最多3元),超出归平台
|
||||
//
|
||||
// 情况7:普通(推广人) -> 普通(直接上级) -> 普通 -> 钻石
|
||||
// => 一部分给直接上级普通,剩余全部给钻石(跳过中间白银代理)
|
||||
// 情况7:白银(推广人) -> 白银(直接上级) -> 白银 -> 钻石
|
||||
// => 一部分给直接上级白银,剩余全部给钻石(跳过中间白银代理)
|
||||
//
|
||||
// 情况8:普通(推广人) -> 普通(直接上级) -> 普通 -> 黄金(无钻石)
|
||||
// => 一部分给直接上级普通,剩余一部分给黄金(最多3元),超出归平台(跳过中间白银代理)
|
||||
// 情况8:白银(推广人) -> 白银(直接上级) -> 白银 -> 黄金(无钻石)
|
||||
// => 一部分给直接上级白银,剩余一部分给黄金(最多3元),超出归平台(跳过中间白银代理)
|
||||
//
|
||||
// 情况9:普通(推广人) -> 普通(直接上级) -> 普通 -> 普通...(全部是普通)
|
||||
// => 一部分给直接上级普通,剩余归平台
|
||||
// 情况9:白银(推广人) -> 白银(直接上级) -> 白银 -> 白银...(全部是白银)
|
||||
// => 一部分给直接上级白银,剩余归平台
|
||||
//
|
||||
// 注意:findDiamondParent 和 findGoldParent 会自动跳过中间的所有白银代理,
|
||||
//
|
||||
@@ -397,7 +397,7 @@ func (s *AgentService) distributeNormalAgentBonus(ctx context.Context, session s
|
||||
case 3: // 直接上级是钻石代理的情况
|
||||
// ========== 直接上级是钻石:等级加成全部给钻石上级 ==========
|
||||
// 场景示例:
|
||||
// - 普通(推广人) -> 钻石(直接上级):等级加成6元全部给钻石
|
||||
// - 白银(推广人) -> 钻石(直接上级):等级加成6元全部给钻石
|
||||
// 说明:如果直接上级就是钻石,不需要再向上查找,全部返佣给直接上级钻石
|
||||
// rebateType = 2:表示钻石上级返佣
|
||||
return s.giveRebate(ctx, session, parent.Id, agent.Id, orderId, productId, amount, levelBonusInt, 2)
|
||||
@@ -435,9 +435,9 @@ func (s *AgentService) distributeNormalAgentBonus(ctx context.Context, session s
|
||||
if remaining > 0 {
|
||||
// 从黄金上级开始向上查找钻石上级
|
||||
// 场景示例:
|
||||
// - 普通(推广人) -> 黄金(直接上级) -> 钻石:剩余3元给钻石
|
||||
// - 普通(推广人) -> 黄金(直接上级) -> 普通 -> 钻石:剩余3元给钻石(跳过中间白银代理)
|
||||
// - 普通(推广人) -> 黄金(直接上级) -> 无上级:剩余3元归平台(没有钻石上级)
|
||||
// - 白银(推广人) -> 黄金(直接上级) -> 钻石:剩余3元给钻石
|
||||
// - 白银(推广人) -> 黄金(直接上级) -> 白银 -> 钻石:剩余3元给钻石(跳过中间白银代理)
|
||||
// - 白银(推广人) -> 黄金(直接上级) -> 无上级:剩余3元归平台(没有钻石上级)
|
||||
diamondParent, err := s.findDiamondParent(ctx, parent.Id)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return errors.Wrapf(err, "查找钻石上级失败")
|
||||
@@ -459,7 +459,7 @@ func (s *AgentService) distributeNormalAgentBonus(ctx context.Context, session s
|
||||
// 说明:无论后续层级有多少白银代理,这部分金额只给推广人的直接上级
|
||||
normalRebateAmount, err := s.getRebateConfigFloat(ctx, "direct_parent_amount_normal", 2.0)
|
||||
if err != nil {
|
||||
logx.Errorf("获取普通返佣配置失败,使用默认值2元: %v", err)
|
||||
logx.Errorf("获取白银返佣配置失败,使用默认值2元: %v", err)
|
||||
normalRebateAmount = 2.0 // 配置读取失败时使用默认值2元
|
||||
}
|
||||
|
||||
@@ -490,11 +490,11 @@ func (s *AgentService) distributeNormalAgentBonus(ctx context.Context, session s
|
||||
// ========== 步骤3:从直接上级开始向上查找钻石和黄金代理 ==========
|
||||
// 注意:findDiamondParent 和 findGoldParent 会自动跳过中间的所有白银代理
|
||||
// 例如:
|
||||
// - 普通 -> 普通 -> 普通 -> 钻石:会跳过中间的白银代理,直接找到钻石
|
||||
// - 普通 -> 普通 -> 黄金 -> 钻石:会找到钻石(优先级更高)
|
||||
// - 普通 -> 黄金:会找到黄金
|
||||
diamondParent, _ := s.findDiamondParent(ctx, parent.Id) // 向上查找钻石上级(跳过所有普通和黄金)
|
||||
goldParent, _ := s.findGoldParent(ctx, parent.Id) // 向上查找黄金上级(跳过所有普通)
|
||||
// - 白银 -> 白银 -> 白银 -> 钻石:会跳过中间的白银代理,直接找到钻石
|
||||
// - 白银 -> 白银 -> 黄金 -> 钻石:会找到钻石(优先级更高)
|
||||
// - 白银 -> 黄金:会找到黄金
|
||||
diamondParent, _ := s.findDiamondParent(ctx, parent.Id) // 向上查找钻石上级(跳过所有白银和黄金)
|
||||
goldParent, _ := s.findGoldParent(ctx, parent.Id) // 向上查找黄金上级(跳过所有白银)
|
||||
|
||||
// ========== 步骤4:按优先级分配剩余金额 ==========
|
||||
// 优先级规则:
|
||||
@@ -505,14 +505,14 @@ func (s *AgentService) distributeNormalAgentBonus(ctx context.Context, session s
|
||||
if diamondParent != nil {
|
||||
// ========== 情况A:找到钻石上级 ==========
|
||||
// 场景示例:
|
||||
// - 普通(推广人) -> 普通(直接上级) -> 钻石:给普通2元,剩余4元给钻石
|
||||
// - 普通(推广人) -> 普通(直接上级) -> 黄金 -> 钻石:给普通2元,给黄金1元,剩余3元给钻石
|
||||
// - 普通(推广人) -> 普通(直接上级) -> 普通 -> 普通 -> 钻石:给普通2元,剩余4元给钻石(跳过中间白银代理)
|
||||
// - 白银(推广人) -> 白银(直接上级) -> 钻石:给白银2元,剩余4元给钻石
|
||||
// - 白银(推广人) -> 白银(直接上级) -> 黄金 -> 钻石:给白银2元,给黄金1元,剩余3元给钻石
|
||||
// - 白银(推广人) -> 白银(直接上级) -> 白银 -> 白银 -> 钻石:给白银2元,剩余4元给钻石(跳过中间白银代理)
|
||||
//
|
||||
// 分配规则:当有钻石上级时,需要给黄金上级一部分返佣
|
||||
// 1. 等级加成的差 = 普通等级加成 - 黄金等级加成(例如:6元 - 3元 = 3元)
|
||||
// 1. 等级加成的差 = 白银等级加成 - 黄金等级加成(例如:6元 - 3元 = 3元)
|
||||
// 2. 从这个差中,先给直接上级白银代理(已给,例如2元)
|
||||
// 3. 差减去给普通的部分,剩余给黄金代理(例如:3元 - 2元 = 1元)
|
||||
// 3. 差减去给白银的部分,剩余给黄金代理(例如:3元 - 2元 = 1元)
|
||||
// 4. 最后剩余部分给钻石(例如:6元 - 2元 - 1元 = 3元)
|
||||
|
||||
// 步骤A1:计算等级加成的差
|
||||
@@ -532,14 +532,14 @@ func (s *AgentService) distributeNormalAgentBonus(ctx context.Context, session s
|
||||
// - 等级加成的差(bonusDiff)代表白银代理和黄金代理的等级加成差异
|
||||
// - 从这个差中,先分配给直接上级白银代理(directAmount,已分配)
|
||||
// - 剩余的差分配给黄金代理:goldRebateAmount = bonusDiff - directAmount
|
||||
// - 如果差小于等于已给普通的部分,则不给黄金(这种情况理论上不应该发生,因为差应该>=给普通的部分)
|
||||
// - 如果差小于等于已给白银的部分,则不给黄金(这种情况理论上不应该发生,因为差应该>=给白银的部分)
|
||||
if goldParent != nil && bonusDiff > 0 {
|
||||
// 计算给黄金上级的金额 = 等级加成差 - 已给普通上级的金额
|
||||
// 例如:等级加成差3元 - 已给普通2元 = 给黄金1元
|
||||
// 计算给黄金上级的金额 = 等级加成差 - 已给白银上级的金额
|
||||
// 例如:等级加成差3元 - 已给白银2元 = 给黄金1元
|
||||
goldRebateAmount := bonusDiff - directAmount
|
||||
|
||||
// 如果计算出的金额小于等于0,说明差已经被白银代理全部占用了,不给黄金
|
||||
// 例如:如果差是2元,已给普通2元,则 goldRebateAmount = 0,不给黄金
|
||||
// 例如:如果差是2元,已给白银2元,则 goldRebateAmount = 0,不给黄金
|
||||
if goldRebateAmount > 0 {
|
||||
// 边界检查:确保不超过剩余金额(理论上应该不会超过,但保险起见)
|
||||
if goldRebateAmount > remaining {
|
||||
@@ -567,8 +567,8 @@ func (s *AgentService) distributeNormalAgentBonus(ctx context.Context, session s
|
||||
} else if goldParent != nil {
|
||||
// ========== 情况B:没有钻石上级,但有黄金上级 ==========
|
||||
// 场景示例:
|
||||
// - 普通(推广人) -> 普通(直接上级) -> 黄金(没有钻石):给黄金一部分,剩余归平台
|
||||
// - 普通(推广人) -> 普通(直接上级) -> 普通 -> 黄金(没有钻石):给黄金一部分,剩余归平台(跳过中间白银代理)
|
||||
// - 白银(推广人) -> 白银(直接上级) -> 黄金(没有钻石):给黄金一部分,剩余归平台
|
||||
// - 白银(推广人) -> 白银(直接上级) -> 白银 -> 黄金(没有钻石):给黄金一部分,剩余归平台(跳过中间白银代理)
|
||||
|
||||
// 配置键:max_gold_rebate_amount(白银代理给黄金上级的最大返佣金额)
|
||||
// 默认值:3.0元
|
||||
@@ -601,8 +601,8 @@ func (s *AgentService) distributeNormalAgentBonus(ctx context.Context, session s
|
||||
|
||||
// ========== 情况C:既没有钻石上级,也没有黄金上级 ==========
|
||||
// 场景示例:
|
||||
// - 普通(推广人) -> 普通(直接上级) -> 普通 -> 普通...(整个链路都是白银代理)
|
||||
// - 普通(推广人) -> 普通(直接上级) -> 无上级(已经是最顶层)
|
||||
// - 白银(推广人) -> 白银(直接上级) -> 白银 -> 白银...(整个链路都是白银代理)
|
||||
// - 白银(推广人) -> 白银(直接上级) -> 无上级(已经是最顶层)
|
||||
// 剩余金额全部归平台(不需要记录)
|
||||
return nil
|
||||
|
||||
@@ -630,18 +630,18 @@ func (s *AgentService) getRebateConfigFloat(ctx context.Context, configKey strin
|
||||
|
||||
// giveRebate 发放返佣
|
||||
func (s *AgentService) giveRebate(ctx context.Context, session sqlx.Session, agentId, sourceAgentId, orderId, productId string, amount float64, levelBonus int64, rebateType int64) error {
|
||||
// 1. 创建返佣记录
|
||||
rebate := &model.AgentRebate{
|
||||
Id: uuid.NewString(),
|
||||
AgentId: agentId,
|
||||
SourceAgentId: sourceAgentId,
|
||||
OrderId: orderId,
|
||||
ProductId: productId,
|
||||
RebateType: rebateType,
|
||||
LevelBonus: float64(levelBonus), // 等级加成金额
|
||||
RebateAmount: amount,
|
||||
Status: 1, // 已发放
|
||||
}
|
||||
// 1. 创建返佣记录
|
||||
rebate := &model.AgentRebate{
|
||||
Id: uuid.NewString(),
|
||||
AgentId: agentId,
|
||||
SourceAgentId: sourceAgentId,
|
||||
OrderId: orderId,
|
||||
ProductId: productId,
|
||||
RebateType: rebateType,
|
||||
LevelBonus: float64(levelBonus), // 等级加成金额
|
||||
RebateAmount: amount,
|
||||
Status: 1, // 已发放
|
||||
}
|
||||
if _, err := s.AgentRebateModel.Insert(ctx, session, rebate); err != nil {
|
||||
return errors.Wrapf(err, "创建返佣记录失败")
|
||||
}
|
||||
@@ -693,9 +693,9 @@ func (s *AgentService) findDirectParent(ctx context.Context, agentId string) (*m
|
||||
// - 如果没有找到钻石代理,返回 ErrNotFound
|
||||
//
|
||||
// 示例场景:
|
||||
// - 普通 -> 普通 -> 钻石:会找到钻石(跳过中间的白银代理)
|
||||
// - 普通 -> 黄金 -> 钻石:会找到钻石(跳过黄金代理)
|
||||
// - 普通 -> 普通 -> 黄金:返回 ErrNotFound(没有钻石)
|
||||
// - 白银 -> 白银 -> 钻石:会找到钻石(跳过中间的白银代理)
|
||||
// - 白银 -> 黄金 -> 钻石:会找到钻石(跳过黄金代理)
|
||||
// - 白银 -> 白银 -> 黄金:返回 ErrNotFound(没有钻石)
|
||||
func (s *AgentService) findDiamondParent(ctx context.Context, agentId string) (*model.Agent, error) {
|
||||
currentId := agentId
|
||||
maxDepth := 100 // 防止无限循环
|
||||
@@ -733,9 +733,9 @@ func (s *AgentService) findDiamondParent(ctx context.Context, agentId string) (*
|
||||
// - 如果没有找到黄金代理,返回 ErrNotFound
|
||||
//
|
||||
// 示例场景:
|
||||
// - 普通 -> 普通 -> 黄金:会找到黄金(跳过中间的白银代理)
|
||||
// - 普通 -> 黄金:会找到黄金
|
||||
// - 普通 -> 普通 -> 钻石:返回 ErrNotFound(跳过钻石,继续查找黄金,但找不到)
|
||||
// - 白银 -> 白银 -> 黄金:会找到黄金(跳过中间的白银代理)
|
||||
// - 白银 -> 黄金:会找到黄金
|
||||
// - 白银 -> 白银 -> 钻石:返回 ErrNotFound(跳过钻石,继续查找黄金,但找不到)
|
||||
func (s *AgentService) findGoldParent(ctx context.Context, agentId string) (*model.Agent, error) {
|
||||
currentId := agentId
|
||||
maxDepth := 100 // 防止无限循环
|
||||
@@ -971,12 +971,12 @@ func (s *AgentService) reconnectToGrandparent(ctx context.Context, session sqlx.
|
||||
}
|
||||
|
||||
// 创建新的关系连接到原上级的上级
|
||||
relation := &model.AgentRelation{
|
||||
Id: uuid.NewString(),
|
||||
ParentId: grandparent.Id,
|
||||
ChildId: agentId,
|
||||
RelationType: 1, // 直接关系
|
||||
}
|
||||
relation := &model.AgentRelation{
|
||||
Id: uuid.NewString(),
|
||||
ParentId: grandparent.Id,
|
||||
ChildId: agentId,
|
||||
RelationType: 1, // 直接关系
|
||||
}
|
||||
if _, err := s.AgentRelationModel.Insert(ctx, session, relation); err != nil {
|
||||
return errors.Wrapf(err, "创建新关系失败")
|
||||
}
|
||||
@@ -1053,7 +1053,7 @@ func (s *AgentService) giveRebateForUpgrade(ctx context.Context, session sqlx.Se
|
||||
// GetUpgradeFee 获取升级费用
|
||||
func (s *AgentService) GetUpgradeFee(ctx context.Context, fromLevel, toLevel int64) (float64, error) {
|
||||
if fromLevel == 1 && toLevel == 2 {
|
||||
// 普通→黄金:从配置获取
|
||||
// 白银→黄金:从配置获取
|
||||
return s.getRebateConfigFloat(ctx, "upgrade_to_gold_fee", 199)
|
||||
} else if toLevel == 3 {
|
||||
// 升级为钻石:从配置获取
|
||||
@@ -1065,7 +1065,7 @@ func (s *AgentService) GetUpgradeFee(ctx context.Context, fromLevel, toLevel int
|
||||
// GetUpgradeRebate 获取升级返佣金额
|
||||
func (s *AgentService) GetUpgradeRebate(ctx context.Context, fromLevel, toLevel int64) (float64, error) {
|
||||
if fromLevel == 1 && toLevel == 2 {
|
||||
// 普通→黄金返佣:从配置获取
|
||||
// 白银→黄金返佣:从配置获取
|
||||
return s.getRebateConfigFloat(ctx, "upgrade_to_gold_rebate", 139)
|
||||
} else if toLevel == 3 {
|
||||
// 升级为钻石返佣:从配置获取
|
||||
|
||||
Reference in New Issue
Block a user