t
This commit is contained in:
379
app/main/api/internal/service/agentService.go
Normal file
379
app/main/api/internal/service/agentService.go
Normal file
@@ -0,0 +1,379 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user