package service import ( "context" "database/sql" "strconv" "time" "ycc-server/app/main/api/internal/config" "ycc-server/app/main/model" "ycc-server/common/globalkey" "ycc-server/pkg/lzkit/lzUtils" "github.com/pkg/errors" "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/stores/sqlx" ) // AgentService 新代理系统服务 type AgentService struct { config config.Config OrderModel model.OrderModel AgentModel model.AgentModel AgentWalletModel model.AgentWalletModel AgentRelationModel model.AgentRelationModel AgentLinkModel model.AgentLinkModel AgentOrderModel model.AgentOrderModel AgentCommissionModel model.AgentCommissionModel AgentRebateModel model.AgentRebateModel AgentUpgradeModel model.AgentUpgradeModel AgentWithdrawalModel model.AgentWithdrawalModel AgentConfigModel model.AgentConfigModel AgentProductConfigModel model.AgentProductConfigModel AgentRealNameModel model.AgentRealNameModel AgentWithdrawalTaxModel model.AgentWithdrawalTaxModel } // NewAgentService 创建新的代理服务 func NewAgentService( c config.Config, orderModel model.OrderModel, agentModel model.AgentModel, agentWalletModel model.AgentWalletModel, agentRelationModel model.AgentRelationModel, agentLinkModel model.AgentLinkModel, agentOrderModel model.AgentOrderModel, agentCommissionModel model.AgentCommissionModel, agentRebateModel model.AgentRebateModel, agentUpgradeModel model.AgentUpgradeModel, agentWithdrawalModel model.AgentWithdrawalModel, agentConfigModel model.AgentConfigModel, agentProductConfigModel model.AgentProductConfigModel, agentRealNameModel model.AgentRealNameModel, agentWithdrawalTaxModel model.AgentWithdrawalTaxModel, ) *AgentService { return &AgentService{ config: c, OrderModel: orderModel, AgentModel: agentModel, AgentWalletModel: agentWalletModel, AgentRelationModel: agentRelationModel, AgentLinkModel: agentLinkModel, AgentOrderModel: agentOrderModel, AgentCommissionModel: agentCommissionModel, AgentRebateModel: agentRebateModel, AgentUpgradeModel: agentUpgradeModel, AgentWithdrawalModel: agentWithdrawalModel, AgentConfigModel: agentConfigModel, AgentProductConfigModel: agentProductConfigModel, AgentRealNameModel: agentRealNameModel, AgentWithdrawalTaxModel: agentWithdrawalTaxModel, } } // AgentProcess 处理代理订单(新系统) // 根据新代理系统的收益分配规则处理订单 func (s *AgentService) AgentProcess(ctx context.Context, order *model.Order) error { // 1. 检查是否是代理推广订单 agentOrder, err := s.AgentOrderModel.FindOneByOrderId(ctx, order.Id) if err != nil { if errors.Is(err, model.ErrNotFound) { // 不是代理订单,直接返回 return nil } return errors.Wrapf(err, "查询代理订单失败, orderId: %d", order.Id) } // 2. 检查订单是否已处理 if agentOrder.ProcessStatus == 1 { logx.Infof("订单已处理, orderId: %d", order.Id) return nil } // 3. 获取代理信息 agent, err := s.AgentModel.FindOne(ctx, agentOrder.AgentId) if err != nil { return errors.Wrapf(err, "查询代理信息失败, agentId: %d", agentOrder.AgentId) } // 4. 获取系统配置 basePrice, err := s.getConfigFloat(ctx, "base_price") if err != nil { return errors.Wrapf(err, "获取基础底价配置失败") } // 6. 使用事务处理订单 return s.AgentWalletModel.Trans(ctx, func(transCtx context.Context, session sqlx.Session) error { // 6.1 计算实际底价和代理收益 levelBonus := s.getLevelBonus(agent.Level) actualBasePrice := basePrice + float64(levelBonus) // 6.2 计算提价成本 priceThreshold, _ := s.getConfigFloat(ctx, "price_threshold") priceFeeRate, _ := s.getConfigFloat(ctx, "price_fee_rate") priceCost := s.calculatePriceCost(agentOrder.SetPrice, priceThreshold, priceFeeRate) // 6.3 计算代理收益 agentProfit := agentOrder.SetPrice - actualBasePrice - priceCost // 6.4 更新代理订单记录 agentOrder.ProcessStatus = 1 agentOrder.ProcessTime = lzUtils.TimeToNullTime(time.Now()) agentOrder.ProcessRemark = lzUtils.StringToNullString("处理成功") if err := s.AgentOrderModel.UpdateWithVersion(transCtx, session, agentOrder); err != nil { return errors.Wrapf(err, "更新代理订单失败") } // 6.5 发放代理佣金 if err := s.giveAgentCommission(transCtx, session, agentOrder.AgentId, order.Id, order.ProductId, agentProfit); err != nil { return errors.Wrapf(err, "发放代理佣金失败") } // 6.6 分配等级加成返佣给上级链 if levelBonus > 0 { if err := s.distributeLevelBonus(transCtx, session, agent, order.Id, order.ProductId, float64(levelBonus), levelBonus); err != nil { return errors.Wrapf(err, "分配等级加成返佣失败") } } return nil }) } // getLevelBonus 获取等级加成 func (s *AgentService) getLevelBonus(level int64) int64 { switch level { case 1: // 普通 return 6 case 2: // 黄金 return 3 case 3: // 钻石 return 0 default: return 0 } } // calculatePriceCost 计算提价成本 func (s *AgentService) calculatePriceCost(setPrice, priceThreshold, priceFeeRate float64) float64 { if setPrice <= priceThreshold { return 0 } return (setPrice - priceThreshold) * priceFeeRate } // giveAgentCommission 发放代理佣金 func (s *AgentService) giveAgentCommission(ctx context.Context, session sqlx.Session, agentId, orderId, productId int64, amount float64) error { // 1. 创建佣金记录 commission := &model.AgentCommission{ AgentId: agentId, OrderId: orderId, ProductId: productId, Amount: amount, Status: 1, // 已发放 } if _, err := s.AgentCommissionModel.Insert(ctx, session, commission); err != nil { return errors.Wrapf(err, "创建佣金记录失败") } // 2. 更新钱包余额 wallet, err := s.AgentWalletModel.FindOneByAgentId(ctx, agentId) if err != nil { return errors.Wrapf(err, "查询钱包失败, agentId: %d", agentId) } wallet.Balance += amount wallet.TotalEarnings += amount if err := s.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil { return errors.Wrapf(err, "更新钱包失败") } return nil } // distributeLevelBonus 分配等级加成返佣给上级链 func (s *AgentService) distributeLevelBonus(ctx context.Context, session sqlx.Session, agent *model.Agent, orderId, productId int64, levelBonus float64, levelBonusInt int64) error { // 钻石代理:等级加成为0,无返佣分配 if agent.Level == 3 { return nil } // 黄金代理:等级加成3元,全部给钻石上级 if agent.Level == 2 { diamondParent, err := s.findDiamondParent(ctx, agent.Id) if err != nil { return errors.Wrapf(err, "查找钻石上级失败") } if diamondParent != nil { return s.giveRebate(ctx, session, diamondParent.Id, agent.Id, orderId, productId, levelBonus, levelBonusInt, 2) // 2=钻石上级返佣 } // 找不到钻石上级,返佣归平台(异常情况) return nil } // 普通代理:等级加成6元,按规则分配给上级链 if agent.Level == 1 { return s.distributeNormalAgentBonus(ctx, session, agent, orderId, productId, levelBonus, levelBonusInt) } return nil } // distributeNormalAgentBonus 普通代理的等级加成返佣分配(6元) func (s *AgentService) distributeNormalAgentBonus(ctx context.Context, session sqlx.Session, agent *model.Agent, orderId, productId int64, amount float64, levelBonusInt int64) error { // 1. 查找直接上级 parent, err := s.findDirectParent(ctx, agent.Id) if err != nil && !errors.Is(err, model.ErrNotFound) { return errors.Wrapf(err, "查找直接上级失败") } if parent == nil { // 无上级,全部归平台 return nil } // 2. 给直接上级分配固定金额 var directParentAmount float64 switch parent.Level { case 3: // 钻石 directParentAmount = 6 case 2: // 黄金 directParentAmount = 3 case 1: // 普通 directParentAmount = 2 default: directParentAmount = 0 } if directParentAmount > 0 { if err := s.giveRebate(ctx, session, parent.Id, agent.Id, orderId, productId, directParentAmount, levelBonusInt, 1); err != nil { return errors.Wrapf(err, "给直接上级返佣失败") } } remaining := amount - directParentAmount if remaining <= 0 { return nil } // 3. 分配剩余金额 // 确定查找起点:直接上级是普通时从直接上级开始查找,否则从直接上级的上级开始查找 searchStart := parent if parent.Level != 1 { searchStartParent, err := s.findDirectParent(ctx, parent.Id) if err != nil && !errors.Is(err, model.ErrNotFound) { return errors.Wrapf(err, "查找上级的上级失败") } if searchStartParent != nil { searchStart = searchStartParent } } if searchStart != nil { // 查找上级链中的钻石和黄金 diamondParent, _ := s.findDiamondParent(ctx, searchStart.Id) goldParent, _ := s.findGoldParent(ctx, searchStart.Id) // 按优先级分配剩余金额 if diamondParent != nil { // 优先级1:有钻石,剩余金额全部给钻石 return s.giveRebate(ctx, session, diamondParent.Id, agent.Id, orderId, productId, remaining, levelBonusInt, 2) } else if goldParent != nil { // 优先级2:只有黄金,最多3元给黄金,剩余归平台 goldAmount := remaining if goldAmount > 3 { goldAmount = 3 } if err := s.giveRebate(ctx, session, goldParent.Id, agent.Id, orderId, productId, goldAmount, levelBonusInt, 3); err != nil { return errors.Wrapf(err, "给黄金上级返佣失败") } // 剩余归平台(不需要记录) } // 优先级3:都没有,剩余金额归平台(不需要记录) } return nil } // giveRebate 发放返佣 func (s *AgentService) giveRebate(ctx context.Context, session sqlx.Session, agentId, sourceAgentId, orderId, productId int64, amount float64, levelBonus int64, rebateType int64) error { // 1. 创建返佣记录 rebate := &model.AgentRebate{ 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, "创建返佣记录失败") } // 2. 更新钱包余额 wallet, err := s.AgentWalletModel.FindOneByAgentId(ctx, agentId) if err != nil { return errors.Wrapf(err, "查询钱包失败, agentId: %d", agentId) } wallet.Balance += amount wallet.TotalEarnings += amount if err := s.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil { return errors.Wrapf(err, "更新钱包失败") } return nil } // FindDirectParent 查找直接上级(公开方法) func (s *AgentService) FindDirectParent(ctx context.Context, agentId int64) (*model.Agent, error) { return s.findDirectParent(ctx, agentId) } // findDirectParent 查找直接上级 func (s *AgentService) findDirectParent(ctx context.Context, agentId int64) (*model.Agent, error) { // 查找关系类型为1(直接关系)的上级 builder := s.AgentRelationModel.SelectBuilder() builder = builder.Where("child_id = ? AND relation_type = ? AND del_state = ?", agentId, 1, globalkey.DelStateNo) relations, err := s.AgentRelationModel.FindAll(ctx, builder, "") if err != nil { return nil, err } if len(relations) == 0 { return nil, model.ErrNotFound } // 返回第一个直接上级 return s.AgentModel.FindOne(ctx, relations[0].ParentId) } // findDiamondParent 向上查找钻石上级 func (s *AgentService) findDiamondParent(ctx context.Context, agentId int64) (*model.Agent, error) { currentId := agentId maxDepth := 100 // 防止无限循环 depth := 0 for depth < maxDepth { parent, err := s.findDirectParent(ctx, currentId) if err != nil { if errors.Is(err, model.ErrNotFound) { return nil, model.ErrNotFound } return nil, err } if parent.Level == 3 { // 钻石 return parent, nil } currentId = parent.Id depth++ } return nil, model.ErrNotFound } // findGoldParent 向上查找黄金上级 func (s *AgentService) findGoldParent(ctx context.Context, agentId int64) (*model.Agent, error) { currentId := agentId maxDepth := 100 // 防止无限循环 depth := 0 for depth < maxDepth { parent, err := s.findDirectParent(ctx, currentId) if err != nil { if errors.Is(err, model.ErrNotFound) { return nil, model.ErrNotFound } return nil, err } if parent.Level == 2 { // 黄金 return parent, nil } currentId = parent.Id depth++ } return nil, model.ErrNotFound } // getConfigFloat 获取配置值(浮点数) func (s *AgentService) getConfigFloat(ctx context.Context, configKey string) (float64, error) { config, err := s.AgentConfigModel.FindOneByConfigKey(ctx, configKey) if err != nil { return 0, err } value, err := strconv.ParseFloat(config.ConfigValue, 64) if err != nil { return 0, errors.Wrapf(err, "解析配置值失败, key: %s, value: %s", configKey, config.ConfigValue) } return value, nil } // getConfigInt 获取配置值(整数) func (s *AgentService) getConfigInt(ctx context.Context, configKey string) (int64, error) { config, err := s.AgentConfigModel.FindOneByConfigKey(ctx, configKey) if err != nil { return 0, err } value, err := strconv.ParseInt(config.ConfigValue, 10, 64) if err != nil { return 0, errors.Wrapf(err, "解析配置值失败, key: %s, value: %s", configKey, config.ConfigValue) } return value, nil } // ProcessUpgrade 处理代理升级 func (s *AgentService) ProcessUpgrade(ctx context.Context, agentId, toLevel int64, upgradeType int64, upgradeFee, rebateAmount float64, orderNo string, operatorAgentId int64) error { return s.AgentWalletModel.Trans(ctx, func(transCtx context.Context, session sqlx.Session) error { // 1. 获取代理信息 agent, err := s.AgentModel.FindOne(transCtx, agentId) if err != nil { return errors.Wrapf(err, "查询代理信息失败, agentId: %d", agentId) } // 2. 如果是自主付费升级,处理返佣 if upgradeType == 1 { // 自主付费 // 查找原直接上级 parent, err := s.findDirectParent(transCtx, agentId) if err != nil && !errors.Is(err, model.ErrNotFound) { return errors.Wrapf(err, "查找直接上级失败") } if parent != nil && rebateAmount > 0 { // 返佣给原直接上级 if err := s.giveRebateForUpgrade(transCtx, session, parent.Id, agentId, rebateAmount); err != nil { return errors.Wrapf(err, "返佣给上级失败") } } } // 3. 执行升级操作 agent.Level = toLevel // 4. 检查是否需要脱离直接上级关系 needDetach, err := s.needDetachFromParent(transCtx, agent, toLevel) if err != nil { return errors.Wrapf(err, "检查是否需要脱离关系失败") } if needDetach { // 脱离直接上级关系 if err := s.detachFromParent(transCtx, session, agentId); err != nil { return errors.Wrapf(err, "脱离直接上级关系失败") } } // 5. 如果升级为钻石,独立成新团队 if toLevel == 3 { agent.TeamLeaderId = sql.NullInt64{Int64: agentId, Valid: true} // 更新所有下级的团队首领 if err := s.updateChildrenTeamLeader(transCtx, session, agentId, agentId); err != nil { return errors.Wrapf(err, "更新下级团队首领失败") } } else { // 更新团队首领(查找上级链中的钻石代理) teamLeaderId, err := s.findTeamLeaderId(transCtx, agentId) if err != nil && !errors.Is(err, model.ErrNotFound) { return errors.Wrapf(err, "查找团队首领失败") } if teamLeaderId > 0 { agent.TeamLeaderId = sql.NullInt64{Int64: teamLeaderId, Valid: true} } } // 6. 更新代理记录 if err := s.AgentModel.UpdateWithVersion(transCtx, session, agent); err != nil { return errors.Wrapf(err, "更新代理记录失败") } // 7. 更新升级记录状态 // 这里需要先查询升级记录,暂时先跳过,在logic中处理 return nil }) } // needDetachFromParent 检查是否需要脱离直接上级关系 func (s *AgentService) needDetachFromParent(ctx context.Context, agent *model.Agent, newLevel int64) (bool, error) { parent, err := s.findDirectParent(ctx, agent.Id) if err != nil { if errors.Is(err, model.ErrNotFound) { return false, nil // 没有上级,不需要脱离 } return false, err } // 规则1:下级不能比上级等级高 if newLevel > parent.Level { return true, nil } // 规则2:同级不能作为上下级(除了普通代理) if newLevel == parent.Level { if newLevel == 2 || newLevel == 3 { // 黄金或钻石 return true, nil } } // 规则3:钻石 → 黄金禁止(特殊规则) if newLevel == 2 && parent.Level == 3 { return true, nil } return false, nil } // detachFromParent 脱离直接上级关系 func (s *AgentService) detachFromParent(ctx context.Context, session sqlx.Session, agentId int64) error { // 查找直接关系 builder := s.AgentRelationModel.SelectBuilder(). Where("child_id = ? AND relation_type = ? AND del_state = ?", agentId, 1, globalkey.DelStateNo) relations, err := s.AgentRelationModel.FindAll(ctx, builder, "") if err != nil { return err } if len(relations) == 0 { return nil // 没有关系,不需要脱离 } // 将直接关系标记为已脱离 relation := relations[0] relation.RelationType = 2 // 已脱离 relation.DetachReason = lzUtils.StringToNullString("upgrade") relation.DetachTime = lzUtils.TimeToNullTime(time.Now()) if err := s.AgentRelationModel.UpdateWithVersion(ctx, session, relation); err != nil { return errors.Wrapf(err, "更新关系记录失败") } return nil } // updateChildrenTeamLeader 更新所有下级的团队首领 func (s *AgentService) updateChildrenTeamLeader(ctx context.Context, session sqlx.Session, agentId, teamLeaderId int64) error { // 递归更新所有下级 var updateChildren func(int64) error updateChildren = func(parentId int64) error { // 查找直接下级 builder := s.AgentRelationModel.SelectBuilder(). Where("parent_id = ? AND relation_type = ? AND del_state = ?", parentId, 1, globalkey.DelStateNo) relations, err := s.AgentRelationModel.FindAll(ctx, builder, "") if err != nil { return err } for _, relation := range relations { child, err := s.AgentModel.FindOne(ctx, relation.ChildId) if err != nil { continue } child.TeamLeaderId = sql.NullInt64{Int64: teamLeaderId, Valid: true} if err := s.AgentModel.UpdateWithVersion(ctx, session, child); err != nil { return errors.Wrapf(err, "更新下级团队首领失败, childId: %d", child.Id) } // 递归更新下级的下级 if err := updateChildren(child.Id); err != nil { return err } } return nil } return updateChildren(agentId) } // findTeamLeaderId 查找团队首领ID(钻石代理) func (s *AgentService) findTeamLeaderId(ctx context.Context, agentId int64) (int64, error) { diamondParent, err := s.findDiamondParent(ctx, agentId) if err != nil { if errors.Is(err, model.ErrNotFound) { return 0, nil } return 0, err } return diamondParent.Id, nil } // giveRebateForUpgrade 发放升级返佣 func (s *AgentService) giveRebateForUpgrade(ctx context.Context, session sqlx.Session, parentAgentId, upgradeAgentId int64, amount float64) error { // 更新钱包余额 wallet, err := s.AgentWalletModel.FindOneByAgentId(ctx, parentAgentId) if err != nil { return errors.Wrapf(err, "查询钱包失败, agentId: %d", parentAgentId) } wallet.Balance += amount wallet.TotalEarnings += amount if err := s.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil { return errors.Wrapf(err, "更新钱包失败") } return nil } // GetUpgradeFee 获取升级费用 func (s *AgentService) GetUpgradeFee(fromLevel, toLevel int64) float64 { if fromLevel == 1 && toLevel == 2 { return 199 // 普通→黄金 } else if toLevel == 3 { return 980 // 升级为钻石 } return 0 } // GetUpgradeRebate 获取升级返佣金额 func (s *AgentService) GetUpgradeRebate(fromLevel, toLevel int64) float64 { if fromLevel == 1 && toLevel == 2 { return 139 // 普通→黄金返佣 } else if toLevel == 3 { return 680 // 升级为钻石返佣 } return 0 }