package service import ( "bd-server/app/main/api/internal/config" "bd-server/app/main/model" "bd-server/common/globalkey" "context" "database/sql" "fmt" "math" "github.com/Masterminds/squirrel" "github.com/pkg/errors" "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/stores/sqlx" ) type AgentService struct { config config.Config OrderModel model.OrderModel AgentModel model.AgentModel AgentAuditModel model.AgentAuditModel AgentCommissionModel model.AgentCommissionModel AgentWalletModel model.AgentWalletModel AgentLinkModel model.AgentLinkModel AgentOrderModel model.AgentOrderModel AgentProductConfigModel model.AgentProductConfigModel AgentWithdrawalModel model.AgentWithdrawalModel AgentWalletTransactionModel model.AgentWalletTransactionModel AgentConfigModel model.AgentConfigModel AsynqService *AsynqService } func NewAgentService(c config.Config, orderModel model.OrderModel, agentModel model.AgentModel, agentAuditModel model.AgentAuditModel, agentCommissionModel model.AgentCommissionModel, agentWalletModel model.AgentWalletModel, agentLinkModel model.AgentLinkModel, agentOrderModel model.AgentOrderModel, agentProductConfigModel model.AgentProductConfigModel, agentWithdrawalModel model.AgentWithdrawalModel, agentWalletTransactionModel model.AgentWalletTransactionModel, agentConfigModel model.AgentConfigModel, asynqService *AsynqService) *AgentService { return &AgentService{ config: c, OrderModel: orderModel, AgentModel: agentModel, AgentAuditModel: agentAuditModel, AgentCommissionModel: agentCommissionModel, AgentWalletModel: agentWalletModel, AgentLinkModel: agentLinkModel, AgentOrderModel: agentOrderModel, AgentProductConfigModel: agentProductConfigModel, AgentWithdrawalModel: agentWithdrawalModel, AgentWalletTransactionModel: agentWalletTransactionModel, AgentConfigModel: agentConfigModel, AsynqService: asynqService, } } // 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 productConfig, findProductConfigErr := l.AgentProductConfigModel.FindOneByProductId(transCtx, order.ProductId) if findProductConfigErr != nil { return findProductConfigErr } commission := order.Amount - productConfig.CostPrice if commission < 0 { commission = 0 } agentWallet, findWalletErr := l.AgentWalletModel.FindOneByAgentId(transCtx, agentID) if findWalletErr != nil { return findWalletErr } balanceBefore := agentWallet.Balance frozenBalanceBefore := agentWallet.FrozenBalance var commissionStatus int64 if l.AgentConfigModel.IsCommissionSafeMode(ctx) { agentWallet.FrozenBalance += commission commissionStatus = 1 } else { agentWallet.Balance += commission commissionStatus = 0 } agentWallet.TotalEarnings += commission agentCommission := model.AgentCommission{ AgentId: agentID, OrderId: order.Id, Amount: commission, ProductId: order.ProductId, Status: commissionStatus, } insertResult, insertCommissionErr := l.AgentCommissionModel.Insert(transCtx, session, &agentCommission) if insertCommissionErr != nil { return insertCommissionErr } updateWalletErr := l.AgentWalletModel.UpdateWithVersion(transCtx, session, agentWallet) if updateWalletErr != nil { return updateWalletErr } commissionID, _ := insertResult.LastInsertId() commissionIDStr := fmt.Sprintf("%d", commissionID) transErr := l.CreateWalletTransaction( transCtx, session, agentID, model.WalletTransactionTypeCommission, commission, balanceBefore, agentWallet.Balance, frozenBalanceBefore, agentWallet.FrozenBalance, order.OrderNo, 0, fmt.Sprintf("订单佣金收入,佣金记录ID: %s", commissionIDStr), ) if transErr != nil { return transErr } return nil }) if transErr != nil { return transErr } if l.AsynqService != nil && l.AgentConfigModel.IsCommissionSafeMode(ctx) { builder := l.AgentCommissionModel.SelectBuilder(). Where("order_id = ?", order.Id). Where("status = ?", 1). Where("del_state = ?", globalkey.DelStateNo) commissions, findErr := l.AgentCommissionModel.FindAll(ctx, builder, "") if findErr != nil { logx.Errorf("查询刚创建的佣金记录失败,订单ID: %d, 错误: %v", order.Id, findErr) return findErr } if len(commissions) > 0 { for _, commission := range commissions { sendTaskErr := l.AsynqService.SendUnfreezeCommissionTask(commission.Id) if sendTaskErr != nil { logx.Errorf("发送佣金解冻任务失败,佣金ID: %d, 错误: %v", commission.Id, sendTaskErr) } else { logx.Infof("已发送佣金解冻任务,佣金ID: %d, 代理ID: %d, 金额: %.2f", commission.Id, commission.AgentId, commission.Amount) } } } } 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 := 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) } // CreateWalletTransaction 创建代理钱包流水记录 func (l *AgentService) CreateWalletTransaction(ctx context.Context, session sqlx.Session, agentID int64, transactionType string, amount float64, balanceBefore, balanceAfter, frozenBalanceBefore, frozenBalanceAfter float64, transactionID string, relatedUserID int64, remark string) error { var transactionIDField sql.NullString if transactionID != "" { transactionIDField = sql.NullString{String: transactionID, Valid: true} } var relatedUserIDField sql.NullInt64 if relatedUserID > 0 { relatedUserIDField = sql.NullInt64{Int64: relatedUserID, Valid: true} } var remarkField sql.NullString if remark != "" { remarkField = sql.NullString{String: remark, Valid: true} } transaction := &model.AgentWalletTransaction{ AgentId: agentID, TransactionType: transactionType, Amount: amount, BalanceBefore: balanceBefore, BalanceAfter: balanceAfter, FrozenBalanceBefore: frozenBalanceBefore, FrozenBalanceAfter: frozenBalanceAfter, TransactionId: transactionIDField, RelatedUserId: relatedUserIDField, Remark: remarkField, } _, err := l.AgentWalletTransactionModel.Insert(ctx, session, transaction) if err != nil { return errors.Wrapf(err, "创建代理钱包流水记录失败,agentID: %d, type: %s, amount: %.2f", agentID, transactionType, amount) } return nil } // HandleOrderRefundDeduction 处理订单退款后的佣金扣款流程 // 简化版:只扣减推广代理佣金,不再处理上级抽佣和奖励 func (l *AgentService) HandleOrderRefundDeduction(ctx context.Context, session sqlx.Session, order *model.Order, refundAmount float64) { refundAmount = roundRefundMoney(refundAmount) if refundAmount <= 0 { return } remainRefundAmount := refundAmount 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 // 扣减推广代理佣金 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) } // 不足部分从推广代理钱包扣 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) } }