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)
|
||
}
|
||
}
|