This commit is contained in:
2026-05-08 11:30:05 +08:00
commit c0ac84fac8
563 changed files with 64232 additions and 0 deletions

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