380 lines
12 KiB
Go
380 lines
12 KiB
Go
|
|
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)
|
|||
|
|
}
|
|||
|
|
}
|