Files
bd-server/app/main/api/internal/service/agentService.go
2026-05-08 11:30:05 +08:00

380 lines
12 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 (
"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)
}
}