Files
hm-server-v2/app/main/api/internal/service/agentService.go
2025-10-23 19:17:27 +08:00

589 lines
21 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package service
import (
"context"
"hm-server/app/main/api/internal/config"
"hm-server/app/main/model"
"hm-server/pkg/lzkit/lzUtils"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
type AgentService struct {
config config.Config
OrderModel model.OrderModel
AgentModel model.AgentModel
AgentAuditModel model.AgentAuditModel
AgentClosureModel model.AgentClosureModel
AgentCommissionModel model.AgentCommissionModel
AgentCommissionDeductionModel model.AgentCommissionDeductionModel
AgentWalletModel model.AgentWalletModel
AgentLinkModel model.AgentLinkModel
AgentOrderModel model.AgentOrderModel
AgentRewardsModel model.AgentRewardsModel
AgentMembershipConfigModel model.AgentMembershipConfigModel
AgentMembershipRechargeOrderModel model.AgentMembershipRechargeOrderModel
AgentMembershipUserConfigModel model.AgentMembershipUserConfigModel
AgentProductConfigModel model.AgentProductConfigModel
AgentPlatformDeductionModel model.AgentPlatformDeductionModel
AgentActiveStatModel model.AgentActiveStatModel
AgentWithdrawalModel model.AgentWithdrawalModel
}
func NewAgentService(c config.Config, orderModel model.OrderModel, agentModel model.AgentModel, agentAuditModel model.AgentAuditModel,
agentClosureModel model.AgentClosureModel, agentCommissionModel model.AgentCommissionModel,
agentCommissionDeductionModel model.AgentCommissionDeductionModel, agentWalletModel model.AgentWalletModel, agentLinkModel model.AgentLinkModel, agentOrderModel model.AgentOrderModel, agentRewardsModel model.AgentRewardsModel,
agentMembershipConfigModel model.AgentMembershipConfigModel,
agentMembershipRechargeOrderModel model.AgentMembershipRechargeOrderModel,
agentMembershipUserConfigModel model.AgentMembershipUserConfigModel,
agentProductConfigModel model.AgentProductConfigModel, agentPlatformDeductionModel model.AgentPlatformDeductionModel,
agentActiveStatModel model.AgentActiveStatModel, agentWithdrawalModel model.AgentWithdrawalModel) *AgentService {
return &AgentService{
config: c,
OrderModel: orderModel,
AgentModel: agentModel,
AgentAuditModel: agentAuditModel,
AgentClosureModel: agentClosureModel,
AgentCommissionModel: agentCommissionModel,
AgentCommissionDeductionModel: agentCommissionDeductionModel,
AgentWalletModel: agentWalletModel,
AgentLinkModel: agentLinkModel,
AgentOrderModel: agentOrderModel,
AgentRewardsModel: agentRewardsModel,
AgentMembershipConfigModel: agentMembershipConfigModel,
AgentMembershipRechargeOrderModel: agentMembershipRechargeOrderModel,
AgentMembershipUserConfigModel: agentMembershipUserConfigModel,
AgentProductConfigModel: agentProductConfigModel,
AgentPlatformDeductionModel: agentPlatformDeductionModel,
AgentActiveStatModel: agentActiveStatModel,
AgentWithdrawalModel: agentWithdrawalModel,
}
}
// AgentProcess 推广单成功
func (l *AgentService) AgentProcess(ctx context.Context, order *model.Order) error {
// 获取是否该订单是代理推广订单
agentOrderModel, err := l.AgentOrderModel.FindOneByOrderId(ctx, order.Id)
if err != nil && !errors.Is(err, model.ErrNotFound) {
return err
}
if errors.Is(err, model.ErrNotFound) || agentOrderModel == nil {
return nil
}
// 事务
transErr := l.AgentWalletModel.Trans(ctx, func(transCtx context.Context, session sqlx.Session) error {
agentID := agentOrderModel.AgentId
agentProductConfigModel, findAgentProductConfigModelErr := l.AgentProductConfigModel.FindOneByProductId(transCtx, order.ProductId)
if findAgentProductConfigModelErr != nil {
return findAgentProductConfigModelErr
}
// 平台底价成本
PlatformCostAmount, platformCostErr := l.PlatformCost(transCtx, agentID, agentProductConfigModel, session)
if platformCostErr != nil {
return platformCostErr
}
// 平台提价成本
PlatformPricingAmount, platformPricingErr := l.PlatformPricing(transCtx, agentID, order.Amount, agentProductConfigModel, session)
if platformPricingErr != nil {
return platformPricingErr
}
// 查找上级
AgentClosureModel, findAgentClosureModelErr := l.AgentClosureModel.FindOneByDescendantIdDepth(transCtx, agentID, 1)
if findAgentClosureModelErr != nil && !errors.Is(findAgentClosureModelErr, model.ErrNotFound) {
return findAgentClosureModelErr
}
var descendantDeductedAmount = 0.00
if AgentClosureModel != nil {
AncestorId := AgentClosureModel.AncestorId
AncestorModel, findAgentModelErr := l.AgentModel.FindOne(transCtx, AncestorId)
if findAgentModelErr != nil && !errors.Is(findAgentModelErr, model.ErrNotFound) {
return findAgentModelErr
}
if AncestorModel != nil {
if AncestorModel.LevelName == "" {
AncestorModel.LevelName = model.AgentLeveNameNormal
}
AgentMembershipConfigModel, findAgentMembersipConfigModelErr := l.AgentMembershipConfigModel.FindOneByLevelName(ctx, AncestorModel.LevelName)
if findAgentMembersipConfigModelErr != nil {
return findAgentMembersipConfigModelErr
}
// 定价
commissionCost, commissionCostErr := l.CommissionCost(transCtx, agentID, AncestorId, AgentMembershipConfigModel, order.ProductId, session)
if commissionCostErr != nil {
return commissionCostErr
}
// 提价
commissionPricing, commissionPricingErr := l.CommissionPricing(transCtx, agentID, AncestorId, AgentMembershipConfigModel, order.ProductId, order.Amount, session)
if commissionPricingErr != nil {
return commissionPricingErr
}
// 上级克扣的成本
descendantDeductedAmount = commissionCost + commissionPricing
// 佣金
ancestorCommissionReward, ancestorCommissionErr := l.AncestorCommission(transCtx, agentID, AncestorId, session)
if ancestorCommissionErr != nil {
return ancestorCommissionErr
}
// 给上级成本以及佣金
ancestorCommissionAmount := commissionCost + commissionPricing + ancestorCommissionReward
ancestorWallet, findAgentWalletModelErr := l.AgentWalletModel.FindOneByAgentId(transCtx, AncestorId)
if findAgentWalletModelErr != nil {
return findAgentWalletModelErr
}
ancestorWallet.Balance += ancestorCommissionAmount
ancestorWallet.TotalEarnings += ancestorCommissionAmount
updateErr := l.AgentWalletModel.UpdateWithVersion(transCtx, session, ancestorWallet)
if updateErr != nil {
return updateErr
}
}
}
// 推广人扣除金额 = 平台成本价 + 平台提价成本 + 上级佣金
deductedAmount := PlatformCostAmount + PlatformPricingAmount + descendantDeductedAmount
agentCommissionErr := l.AgentCommission(transCtx, agentID, order, deductedAmount, session)
if agentCommissionErr != nil {
return agentCommissionErr
}
return nil
})
if transErr != nil {
return transErr
}
return nil
}
// AgentCommission 直推报告推广人佣金
func (l *AgentService) AgentCommission(ctx context.Context, agentID int64, order *model.Order, deductedAmount float64, session sqlx.Session) error {
agentWalletModel, findAgentWalletModelErr := l.AgentWalletModel.FindOneByAgentId(ctx, agentID)
if findAgentWalletModelErr != nil {
return findAgentWalletModelErr
}
// 推广人最终获得代理佣金
finalCommission := order.Amount - deductedAmount
agentWalletModel.Balance += finalCommission
agentWalletModel.TotalEarnings += finalCommission
agentCommission := model.AgentCommission{
AgentId: agentID,
OrderId: order.Id,
Amount: finalCommission,
ProductId: order.ProductId,
}
_, insertAgentCommissionErr := l.AgentCommissionModel.Insert(ctx, session, &agentCommission)
if insertAgentCommissionErr != nil {
return insertAgentCommissionErr
}
updateAgentWalletErr := l.AgentWalletModel.UpdateWithVersion(ctx, session, agentWalletModel)
if updateAgentWalletErr != nil {
return updateAgentWalletErr
}
return nil
}
// AncestorCommission 直推报告上级佣金(奖励型)
func (l *AgentService) AncestorCommission(ctx context.Context, descendantId int64, ancestorId int64, session sqlx.Session) (float64, error) {
agentModel, err := l.AgentModel.FindOne(ctx, ancestorId)
if err != nil {
return 0, err
}
if agentModel.LevelName == "" {
agentModel.LevelName = model.AgentLeveNameNormal
}
agentMembershipConfigModel, err := l.AgentMembershipConfigModel.FindOneByLevelName(ctx, agentModel.LevelName)
if err != nil {
return 0, err
}
if agentMembershipConfigModel.ReportCommission.Valid {
reportCommissionAmount := agentMembershipConfigModel.ReportCommission.Float64
agentRewards := model.AgentRewards{
AgentId: ancestorId,
Amount: reportCommissionAmount,
RelationAgentId: lzUtils.Int64ToNullInt64(descendantId),
Type: model.AgentRewardsTypeDescendantPromotion,
}
_, agentRewardsModelInsetErr := l.AgentRewardsModel.Insert(ctx, session, &agentRewards)
if agentRewardsModelInsetErr != nil {
return 0, agentRewardsModelInsetErr
}
return reportCommissionAmount, nil
}
return 0, nil
}
// PlatformCost 平台底价成本
func (l *AgentService) PlatformCost(ctx context.Context, agentID int64, agentProductConfigModel *model.AgentProductConfig, session sqlx.Session) (float64, error) {
costAgentPlatformDeductionModel := model.AgentPlatformDeduction{
AgentId: agentID,
Amount: agentProductConfigModel.CostPrice,
Type: model.AgentDeductionTypeCost,
}
_, err := l.AgentPlatformDeductionModel.Insert(ctx, session, &costAgentPlatformDeductionModel)
if err != nil {
return 0, err
}
return agentProductConfigModel.CostPrice, nil
}
// PlatformPricing 平台提价成本
func (l *AgentService) PlatformPricing(ctx context.Context, agentID int64, pricing float64, agentProductConfigModel *model.AgentProductConfig, session sqlx.Session) (float64, error) {
// 2. 计算平台提价成本
if pricing > agentProductConfigModel.PricingStandard {
// 超出部分
overpricing := pricing - agentProductConfigModel.PricingStandard
// 收取成本
overpricingCost := overpricing * agentProductConfigModel.OverpricingRatio
pricingAgentPlatformDeductionModel := model.AgentPlatformDeduction{
AgentId: agentID,
Amount: overpricingCost,
Type: model.AgentDeductionTypePricing,
}
_, err := l.AgentPlatformDeductionModel.Insert(ctx, session, &pricingAgentPlatformDeductionModel)
if err != nil {
return 0, err
}
return overpricingCost, nil
}
return 0, nil
}
// CommissionCost 上级底价成本
func (l *AgentService) CommissionCost(ctx context.Context, descendantId int64, AncestorId int64, agentMembershipConfigModel *model.AgentMembershipConfig, productID int64, session sqlx.Session) (float64, error) {
if agentMembershipConfigModel.PriceIncreaseAmount.Valid {
// 拥有则查看该上级设定的成本
agentMembershipUserConfigModel, findAgentMembershipUserConfigModelErr := l.AgentMembershipUserConfigModel.FindOneByAgentIdProductId(ctx, AncestorId, productID)
if findAgentMembershipUserConfigModelErr != nil {
// 如果上级没有配置该产品的定价规则,则跳过成本计算
if errors.Is(findAgentMembershipUserConfigModelErr, model.ErrNotFound) {
return 0, nil
}
return 0, findAgentMembershipUserConfigModelErr
}
deductCostAmount := agentMembershipUserConfigModel.PriceIncreaseAmount
agentCommissionDeductionModel := model.AgentCommissionDeduction{
AgentId: AncestorId,
DeductedAgentId: descendantId,
Amount: deductCostAmount,
Type: model.AgentDeductionTypeCost,
ProductId: productID,
}
_, insertAgentCommissionDeductionModelErr := l.AgentCommissionDeductionModel.Insert(ctx, session, &agentCommissionDeductionModel)
if insertAgentCommissionDeductionModelErr != nil {
return 0, insertAgentCommissionDeductionModelErr
}
return deductCostAmount, nil
}
return 0, nil
}
// CommissionPricing 上级提价成本
func (l *AgentService) CommissionPricing(ctx context.Context, descendantId int64, AncestorId int64, agentMembershipConfigModel *model.AgentMembershipConfig, productID int64, pricing float64, session sqlx.Session) (float64, error) {
//看上级代理等级否有拥有定价标准收益功能
if agentMembershipConfigModel.PriceIncreaseMax.Valid && agentMembershipConfigModel.PriceRatio.Valid {
// 拥有则查看该上级设定的成本
agentMembershipUserConfigModel, findAgentMembershipUserConfigModelErr := l.AgentMembershipUserConfigModel.FindOneByAgentIdProductId(ctx, AncestorId, productID)
if findAgentMembershipUserConfigModelErr != nil {
// 如果上级没有配置该产品的定价规则,则跳过成本计算
if errors.Is(findAgentMembershipUserConfigModelErr, model.ErrNotFound) {
return 0, nil
}
return 0, findAgentMembershipUserConfigModelErr
}
// 计算是否在范围内
var pricingRange float64
if pricing > agentMembershipUserConfigModel.PriceRangeFrom {
if pricing > agentMembershipUserConfigModel.PriceRangeTo {
pricingRange = agentMembershipUserConfigModel.PriceRangeTo - agentMembershipUserConfigModel.PriceRangeFrom
} else {
pricingRange = pricing - agentMembershipUserConfigModel.PriceRangeFrom
}
}
deductCostAmount := pricingRange * agentMembershipUserConfigModel.PriceRatio
agentCommissionDeductionModel := model.AgentCommissionDeduction{
AgentId: AncestorId,
DeductedAgentId: descendantId,
Amount: deductCostAmount,
Type: model.AgentDeductionTypePricing,
ProductId: productID,
}
_, insertAgentCommissionDeductionModelErr := l.AgentCommissionDeductionModel.Insert(ctx, session, &agentCommissionDeductionModel)
if insertAgentCommissionDeductionModelErr != nil {
return 0, insertAgentCommissionDeductionModelErr
}
return deductCostAmount, nil
}
return 0, nil
}
// GiveUpgradeReward 给上级代理发放下级升级奖励
func (l *AgentService) GiveUpgradeReward(ctx context.Context, agentID int64, oldLevel, newLevel string, session sqlx.Session) error {
// 查找上级代理
agentClosureModel, err := l.AgentClosureModel.FindOneByDescendantIdDepth(ctx, agentID, 1)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
// 没有上级代理,直接返回
return nil
}
return err
}
ancestorID := agentClosureModel.AncestorId
ancestorModel, err := l.AgentModel.FindOne(ctx, ancestorID)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
// 上级代理不存在,直接返回
return nil
}
return err
}
if ancestorModel == nil {
// 上级代理不存在,直接返回
return nil
}
// 获取上级代理的等级配置
if ancestorModel.LevelName == "" {
ancestorModel.LevelName = model.AgentLeveNameNormal
}
agentMembershipConfigModel, err := l.AgentMembershipConfigModel.FindOneByLevelName(ctx, ancestorModel.LevelName)
if err != nil {
return err
}
// 根据升级路径计算奖励金额差额
var rewardAmount float64
var rewardType string
// 获取各等级的奖励金额
var vipRewardAmount float64
var svipRewardAmount float64
if agentMembershipConfigModel.LowerConvertVipReward.Valid {
vipRewardAmount = agentMembershipConfigModel.LowerConvertVipReward.Float64
}
if agentMembershipConfigModel.LowerConvertSvipReward.Valid {
svipRewardAmount = agentMembershipConfigModel.LowerConvertSvipReward.Float64
}
// 根据升级路径计算实际奖励金额
switch {
case oldLevel == "" || oldLevel == model.AgentLeveNameNormal:
// 普通代理升级
switch newLevel {
case model.AgentLeveNameVIP:
rewardAmount = vipRewardAmount
rewardType = model.AgentRewardsTypeDescendantUpgradeVip
case model.AgentLeveNameSVIP:
rewardAmount = svipRewardAmount
rewardType = model.AgentRewardsTypeDescendantUpgradeSvip
default:
// 无效的升级路径,直接返回
return nil
}
case oldLevel == model.AgentLeveNameVIP && newLevel == model.AgentLeveNameSVIP:
// VIP升级到SVIP发放差额奖励
rewardAmount = svipRewardAmount - vipRewardAmount
rewardType = model.AgentRewardsTypeDescendantUpgradeSvip
// 如果差额为负数或零,不发放奖励
if rewardAmount <= 0 {
return nil
}
default:
// 其他无效的升级路径如SVIP降级等直接返回
return nil
}
// 如果有奖励金额,则发放奖励
if rewardAmount > 0 {
// 创建奖励记录
agentRewards := model.AgentRewards{
AgentId: ancestorID,
Amount: rewardAmount,
RelationAgentId: lzUtils.Int64ToNullInt64(agentID),
Type: rewardType,
}
_, err = l.AgentRewardsModel.Insert(ctx, session, &agentRewards)
if err != nil {
return err
}
// 更新上级代理钱包
ancestorWallet, err := l.AgentWalletModel.FindOneByAgentId(ctx, ancestorID)
if err != nil {
return err
}
ancestorWallet.Balance += rewardAmount
ancestorWallet.TotalEarnings += rewardAmount
err = l.AgentWalletModel.UpdateWithVersion(ctx, session, ancestorWallet)
if err != nil {
return err
}
}
return nil
}
// GiveWithdrawReward 给上级代理发放下级提现奖励
func (l *AgentService) GiveWithdrawReward(ctx context.Context, agentID int64, withdrawAmount float64, session sqlx.Session) error {
// 验证提现金额
if withdrawAmount <= 0 {
return nil
}
// 查找上级代理
agentClosureModel, err := l.AgentClosureModel.FindOneByDescendantIdDepth(ctx, agentID, 1)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
// 没有上级代理,直接返回
return nil
}
return err
}
ancestorID := agentClosureModel.AncestorId
ancestorModel, err := l.AgentModel.FindOne(ctx, ancestorID)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
// 上级代理不存在,直接返回
return nil
}
return err
}
if ancestorModel == nil {
// 上级代理不存在,直接返回
return nil
}
// 获取上级代理的等级配置
if ancestorModel.LevelName == "" {
ancestorModel.LevelName = model.AgentLeveNameNormal
}
agentMembershipConfigModel, err := l.AgentMembershipConfigModel.FindOneByLevelName(ctx, ancestorModel.LevelName)
if err != nil {
return err
}
// 计算提现奖励金额
if agentMembershipConfigModel.LowerWithdrawRewardRatio.Valid {
rewardRatio := agentMembershipConfigModel.LowerWithdrawRewardRatio.Float64
// 验证奖励比例的有效性0-1之间
if rewardRatio < 0 || rewardRatio > 1 {
// 无效的奖励比例,直接返回
return nil
}
rewardAmount := withdrawAmount * rewardRatio
if rewardAmount > 0 {
// 创建奖励记录
agentRewards := model.AgentRewards{
AgentId: ancestorID,
Amount: rewardAmount,
RelationAgentId: lzUtils.Int64ToNullInt64(agentID),
Type: model.AgentRewardsTypeDescendantWithdraw,
}
_, err = l.AgentRewardsModel.Insert(ctx, session, &agentRewards)
if err != nil {
return err
}
// 更新上级代理钱包
ancestorWallet, err := l.AgentWalletModel.FindOneByAgentId(ctx, ancestorID)
if err != nil {
return err
}
ancestorWallet.Balance += rewardAmount
ancestorWallet.TotalEarnings += rewardAmount
err = l.AgentWalletModel.UpdateWithVersion(ctx, session, ancestorWallet)
if err != nil {
return err
}
}
}
return nil
}
// CheckAgentProcessStatus 检查代理处理事务是否已成功
func (l *AgentService) CheckAgentProcessStatus(ctx context.Context, orderID int64) (bool, error) {
// 检查是否存在代理订单记录
_, err := l.AgentOrderModel.FindOneByOrderId(ctx, orderID)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
// 没有代理订单记录,说明不是代理推广订单
return true, nil
}
return false, err
}
// 检查是否存在代理佣金记录
// 使用SelectBuilder查询该订单的佣金记录
selectBuilder := l.AgentCommissionModel.SelectBuilder()
selectBuilder = selectBuilder.Where("order_id = ?", orderID)
selectBuilder = selectBuilder.Where("del_state = ?", 0) // 未删除
commissions, err := l.AgentCommissionModel.FindAll(ctx, selectBuilder, "")
if err != nil {
return false, err
}
// 如果存在佣金记录,说明代理处理已成功
return len(commissions) > 0, nil
}
// RetryAgentProcess 重新执行代理处理事务
func (l *AgentService) RetryAgentProcess(ctx context.Context, orderID int64) error {
// 首先检查订单是否存在
order, err := l.OrderModel.FindOne(ctx, orderID)
if err != nil {
return err
}
// 检查订单状态是否为已支付
if order.Status != "paid" {
return errors.New("订单状态不是已支付,无法执行代理处理")
}
// 检查代理处理是否已经成功
alreadyProcessed, err := l.CheckAgentProcessStatus(ctx, orderID)
if err != nil {
return err
}
if alreadyProcessed {
return errors.New("代理处理已经成功,无需重新执行")
}
// 执行代理处理
return l.AgentProcess(ctx, order)
}