This commit is contained in:
2026-04-29 11:38:59 +08:00
parent e96e3f9583
commit 7d363f4e8a
30 changed files with 1135 additions and 215 deletions

View File

@@ -1,14 +1,17 @@
package service
import (
"context"
"database/sql"
"fmt"
"bdrp-server/app/main/api/internal/config"
"bdrp-server/app/main/model"
"bdrp-server/common/globalkey"
"bdrp-server/pkg/lzkit/lzUtils"
"context"
"database/sql"
"fmt"
"math"
"time"
"github.com/Masterminds/squirrel"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
@@ -34,6 +37,7 @@ type AgentService struct {
AgentActiveStatModel model.AgentActiveStatModel
AgentWithdrawalModel model.AgentWithdrawalModel
AgentWalletTransactionModel model.AgentWalletTransactionModel
AgentConfigModel model.AgentConfigModel
AsynqService *AsynqService
}
@@ -44,7 +48,7 @@ func NewAgentService(c config.Config, orderModel model.OrderModel, agentModel mo
agentMembershipRechargeOrderModel model.AgentMembershipRechargeOrderModel,
agentMembershipUserConfigModel model.AgentMembershipUserConfigModel,
agentProductConfigModel model.AgentProductConfigModel, agentPlatformDeductionModel model.AgentPlatformDeductionModel,
agentActiveStatModel model.AgentActiveStatModel, agentWithdrawalModel model.AgentWithdrawalModel, agentWalletTransactionModel model.AgentWalletTransactionModel, asynqService *AsynqService) *AgentService {
agentActiveStatModel model.AgentActiveStatModel, agentWithdrawalModel model.AgentWithdrawalModel, agentWalletTransactionModel model.AgentWalletTransactionModel, agentConfigModel model.AgentConfigModel, asynqService *AsynqService) *AgentService {
return &AgentService{
config: c,
@@ -66,6 +70,7 @@ func NewAgentService(c config.Config, orderModel model.OrderModel, agentModel mo
AgentActiveStatModel: agentActiveStatModel,
AgentWithdrawalModel: agentWithdrawalModel,
AgentWalletTransactionModel: agentWalletTransactionModel,
AgentConfigModel: agentConfigModel,
AsynqService: asynqService,
}
}
@@ -113,6 +118,9 @@ func (l *AgentService) AgentProcess(ctx context.Context, order *model.Order) err
return findAgentModelErr
}
if AncestorModel != nil {
if IsMembershipExpired(AncestorModel, time.Now()) {
AncestorModel.LevelName = model.AgentLeveNameNormal
}
if AncestorModel.LevelName == "" {
AncestorModel.LevelName = model.AgentLeveNameNormal
}
@@ -155,7 +163,7 @@ func (l *AgentService) AgentProcess(ctx context.Context, order *model.Order) err
// 根据安全防御模式配置决定佣金处理方式
var commissionStatus int64
if l.config.SystemConfig.CommissionSafeMode {
if l.AgentConfigModel.IsCommissionSafeMode(ctx) {
// 安全防御模式佣金冻结在frozen_balance中
ancestorWallet.FrozenBalance += ancestorCommissionAmount
commissionStatus = 1 // 冻结状态
@@ -227,7 +235,7 @@ func (l *AgentService) AgentProcess(ctx context.Context, order *model.Order) err
// 在事务提交后,仅在安全防御模式下触发解冻任务
// 注意:这里发送的是任务,实际解冻将在指定时间后由队列处理
if l.AsynqService != nil && l.config.SystemConfig.CommissionSafeMode {
if l.AsynqService != nil && l.AgentConfigModel.IsCommissionSafeMode(ctx) {
// 仅在安全防御模式下,才需要发送解冻任务
// 获取刚创建的佣金记录ID
// 由于我们需要佣金记录ID来触发解冻任务但事务中无法获取我们可以在事务后查询
@@ -275,7 +283,7 @@ func (l *AgentService) AgentCommission(ctx context.Context, agentID int64, order
frozenBalanceBefore := agentWalletModel.FrozenBalance
// 根据安全防御模式配置决定佣金状态和钱包操作
if l.config.SystemConfig.CommissionSafeMode {
if l.AgentConfigModel.IsCommissionSafeMode(ctx) {
// 安全防御模式佣金冻结在frozen_balance中
agentWalletModel.FrozenBalance += finalCommission
} else {
@@ -286,7 +294,7 @@ func (l *AgentService) AgentCommission(ctx context.Context, agentID int64, order
// 根据安全防御模式配置决定佣金状态
commissionStatus := int64(1) // 默认为冻结状态
if !l.config.SystemConfig.CommissionSafeMode {
if !l.AgentConfigModel.IsCommissionSafeMode(ctx) {
commissionStatus = 0 // 非安全模式直接设置为已结算
}
@@ -343,6 +351,9 @@ func (l *AgentService) AncestorCommission(ctx context.Context, descendantId int6
if err != nil {
return 0, err
}
if IsMembershipExpired(agentModel, time.Now()) {
agentModel.LevelName = model.AgentLeveNameNormal
}
if agentModel.LevelName == "" {
agentModel.LevelName = model.AgentLeveNameNormal
}
@@ -415,6 +426,17 @@ func (l *AgentService) PlatformPricing(ctx context.Context, agentID int64, order
// CommissionCost 上级底价成本
func (l *AgentService) CommissionCost(ctx context.Context, descendantId int64, AncestorId int64, agentMembershipConfigModel *model.AgentMembershipConfig, productID int64, orderId int64, session sqlx.Session) (float64, error) {
ancestorModel, findAncestorErr := l.AgentModel.FindOne(ctx, AncestorId)
if findAncestorErr != nil {
if errors.Is(findAncestorErr, model.ErrNotFound) {
return 0, nil
}
return 0, findAncestorErr
}
if IsMembershipExpired(ancestorModel, time.Now()) {
return 0, nil
}
if agentMembershipConfigModel.PriceIncreaseAmount.Valid {
// 拥有则查看该上级设定的成本
agentMembershipUserConfigModel, findAgentMembershipUserConfigModelErr := l.AgentMembershipUserConfigModel.FindOneByAgentIdProductId(ctx, AncestorId, productID)
@@ -449,6 +471,17 @@ func (l *AgentService) CommissionCost(ctx context.Context, descendantId int64, A
// CommissionPricing 上级提价成本
func (l *AgentService) CommissionPricing(ctx context.Context, descendantId int64, AncestorId int64, agentMembershipConfigModel *model.AgentMembershipConfig, productID int64, pricing float64, orderId int64, session sqlx.Session) (float64, error) {
ancestorModel, findAncestorErr := l.AgentModel.FindOne(ctx, AncestorId)
if findAncestorErr != nil {
if errors.Is(findAncestorErr, model.ErrNotFound) {
return 0, nil
}
return 0, findAncestorErr
}
if IsMembershipExpired(ancestorModel, time.Now()) {
return 0, nil
}
//看上级代理等级否有拥有定价标准收益功能
if agentMembershipConfigModel.PriceIncreaseMax.Valid && agentMembershipConfigModel.PriceRatio.Valid {
// 拥有则查看该上级设定的成本
@@ -517,6 +550,9 @@ func (l *AgentService) GiveUpgradeReward(ctx context.Context, agentID int64, old
}
// 获取上级代理的等级配置
if IsMembershipExpired(ancestorModel, time.Now()) {
ancestorModel.LevelName = model.AgentLeveNameNormal
}
if ancestorModel.LevelName == "" {
ancestorModel.LevelName = model.AgentLeveNameNormal
}
@@ -661,6 +697,9 @@ func (l *AgentService) GiveWithdrawReward(ctx context.Context, agentID int64, wi
}
// 获取上级代理的等级配置
if IsMembershipExpired(ancestorModel, time.Now()) {
ancestorModel.LevelName = model.AgentLeveNameNormal
}
if ancestorModel.LevelName == "" {
ancestorModel.LevelName = model.AgentLeveNameNormal
}
@@ -848,3 +887,211 @@ func (l *AgentService) CreateWalletTransaction(ctx context.Context, session sqlx
}
return nil
}
// HandleOrderRefundDeduction 处理订单退款后的佣金扣款流程
// refundAmount: 本次实际退款金额
// 扣款顺序:
// 1. 扣减推广代理佣金(先扣冻结,再扣余额)
// 2. 扣减上级抽佣
// 3. 取消上级推广奖励(无论退多少都取消)
// 4. 不足部分继续从推广代理钱包扣
// 平台抽佣不受影响
func (l *AgentService) HandleOrderRefundDeduction(ctx context.Context, session sqlx.Session, order *model.Order, refundAmount float64) {
refundAmount = roundRefundMoney(refundAmount)
if refundAmount <= 0 {
return
}
remainRefundAmount := refundAmount
// 查找订单关联的代理订单获取推广人代理ID
agentOrder, err := l.AgentOrderModel.FindOneByOrderId(ctx, order.Id)
if err != nil || agentOrder == nil {
logx.Errorf("退款扣款查询代理订单失败订单ID: %d, 错误: %v", order.Id, err)
return
}
promoterAgentId := agentOrder.AgentId
// 查找上级代理ID
var ancestorAgentId int64 = 0
agentClosure, closureErr := l.AgentClosureModel.FindOneByDescendantIdDepth(ctx, promoterAgentId, 1)
if closureErr == nil && agentClosure != nil {
ancestorAgentId = agentClosure.AncestorId
}
// 第1步扣减推广代理佣金
promoterCommissions, _ := l.AgentCommissionModel.FindAll(ctx,
l.AgentCommissionModel.SelectBuilder().Where(squirrel.And{
squirrel.Eq{"order_id": order.Id},
squirrel.Eq{"agent_id": promoterAgentId},
squirrel.NotEq{"status": 2},
}), "")
for _, commission := range promoterCommissions {
if remainRefundAmount <= 0 {
break
}
available := roundRefundMoney(commission.Amount - commission.RefundedAmount)
if available <= 0 {
continue
}
currentRefund := available
if currentRefund > remainRefundAmount {
currentRefund = remainRefundAmount
}
currentRefund = roundRefundMoney(currentRefund)
commission.RefundedAmount = roundRefundMoney(commission.RefundedAmount + currentRefund)
if commission.RefundedAmount >= roundRefundMoney(commission.Amount) {
commission.Status = 2
}
if err := l.AgentCommissionModel.UpdateWithVersion(ctx, session, commission); err != nil {
logx.Errorf("退款扣款更新推广佣金失败佣金ID: %d, 错误: %v", commission.Id, err)
continue
}
deductFromAgentWallet(ctx, l, session, promoterAgentId, currentRefund,
fmt.Sprintf("订单退款,推广佣金扣除,订单号: %s", order.OrderNo))
remainRefundAmount = roundRefundMoney(remainRefundAmount - currentRefund)
}
// 第2步扣减上级抽佣
if ancestorAgentId > 0 && remainRefundAmount > 0 {
deductions, _ := l.AgentCommissionDeductionModel.FindAll(ctx,
l.AgentCommissionDeductionModel.SelectBuilder().Where(map[string]interface{}{
"order_id": order.Id,
"agent_id": ancestorAgentId,
}), "")
for _, deduction := range deductions {
if deduction.Status == 2 {
continue
}
if remainRefundAmount <= 0 {
break
}
available := roundRefundMoney(deduction.Amount - deduction.RefundedAmount)
if available <= 0 {
continue
}
currentRefund := available
if currentRefund > remainRefundAmount {
currentRefund = remainRefundAmount
}
currentRefund = roundRefundMoney(currentRefund)
deduction.RefundedAmount = roundRefundMoney(deduction.RefundedAmount + currentRefund)
if deduction.RefundedAmount >= roundRefundMoney(deduction.Amount) {
deduction.Status = 2
}
if err := l.AgentCommissionDeductionModel.UpdateWithVersion(ctx, session, deduction); err != nil {
logx.Errorf("退款扣款更新上级抽佣失败ID: %d, 错误: %v", deduction.Id, err)
continue
}
deductFromAgentWallet(ctx, l, session, ancestorAgentId, currentRefund,
fmt.Sprintf("订单退款,上级抽佣扣除,订单号: %s", order.OrderNo))
remainRefundAmount = roundRefundMoney(remainRefundAmount - currentRefund)
}
}
// 第3步取消上级推广奖励无论退多少都取消
if ancestorAgentId > 0 {
rewards, _ := l.AgentRewardsModel.FindAll(ctx,
l.AgentRewardsModel.SelectBuilder().Where(map[string]interface{}{
"agent_id": ancestorAgentId,
"relation_agent_id": promoterAgentId,
"type": model.AgentRewardsTypeDescendantPromotion,
"status": 0,
}), "id DESC")
if len(rewards) > 0 {
reward := rewards[0]
rewardAmount := roundRefundMoney(reward.Amount)
reward.Status = 1
reward.Remark = fmt.Sprintf("订单退款取消奖励,订单号: %s", order.OrderNo)
if err := l.AgentRewardsModel.UpdateWithVersion(ctx, session, reward); err != nil {
logx.Errorf("退款扣款取消推广奖励失败奖励ID: %d, 错误: %v", reward.Id, err)
} else {
deductFromAgentWallet(ctx, l, session, ancestorAgentId, rewardAmount,
fmt.Sprintf("订单退款,取消推广奖励,订单号: %s", order.OrderNo))
}
}
}
// 第4步不足部分从推广代理钱包扣
if remainRefundAmount > 0 {
deductFromAgentWallet(ctx, l, session, promoterAgentId, remainRefundAmount,
fmt.Sprintf("订单退款,不足部分从钱包扣除,订单号: %s", order.OrderNo))
}
}
// roundRefundMoney 四舍五入到分
func roundRefundMoney(v float64) float64 {
return math.Round(v*100) / 100
}
// deductFromAgentWallet 从代理钱包扣除金额(先冻结后余额)
func deductFromAgentWallet(ctx context.Context, l *AgentService, session sqlx.Session, agentId int64, amount float64, remark string) {
amount = roundRefundMoney(amount)
if amount <= 0 {
return
}
wallet, err := l.AgentWalletModel.FindOneByAgentId(ctx, agentId)
if err != nil {
logx.Errorf("退款扣款查询代理钱包失败代理ID: %d, 错误: %v", agentId, err)
return
}
balanceBefore := wallet.Balance
frozenBalanceBefore := wallet.FrozenBalance
if wallet.FrozenBalance >= amount {
wallet.FrozenBalance = roundRefundMoney(wallet.FrozenBalance - amount)
} else {
remaining := roundRefundMoney(amount - wallet.FrozenBalance)
wallet.FrozenBalance = 0
wallet.Balance = roundRefundMoney(wallet.Balance - remaining)
}
balanceAfter := roundRefundMoney(wallet.Balance)
frozenBalanceAfter := roundRefundMoney(wallet.FrozenBalance)
var updateErr error
if session != nil {
updateErr = l.AgentWalletModel.UpdateWithVersion(ctx, session, wallet)
} else {
updateErr = l.AgentWalletModel.UpdateWithVersion(ctx, nil, wallet)
}
if updateErr != nil {
logx.Errorf("退款扣款更新代理钱包失败代理ID: %d, 错误: %v", agentId, updateErr)
return
}
transErr := l.CreateWalletTransaction(
ctx,
session,
agentId,
model.WalletTransactionTypeRefund,
roundRefundMoney(-amount),
balanceBefore,
balanceAfter,
frozenBalanceBefore,
frozenBalanceAfter,
"",
0,
remark,
)
if transErr != nil {
logx.Errorf("退款扣款创建钱包流水失败代理ID: %d, 错误: %v", agentId, transErr)
}
}