This commit is contained in:
2026-04-29 11:38:59 +08:00
parent e96e3f9583
commit 7d363f4e8a
30 changed files with 1135 additions and 215 deletions

View File

@@ -11,7 +11,6 @@ import (
"strings"
"time"
"github.com/Masterminds/squirrel"
"github.com/pkg/errors"
"github.com/wechatpay-apiv3/wechatpay-go/services/refunddomestic"
"github.com/zeromicro/go-zero/core/logx"
@@ -22,153 +21,6 @@ func roundMoney(v float64) float64 {
return math.Round(v*100) / 100
}
// HandleCommissionAndWalletDeduction 处理退款后的佣金状态更新和钱包金额扣除
// refundAmount 为本次实际退款金额(单位:元),从代理侧总共需要承担的金额
// 该函数会优先冲减当前订单相关的佣金(基于 RefundedAmount不足部分再从钱包余额/冻结余额中扣除
func HandleCommissionAndWalletDeduction(ctx context.Context, svcCtx *svc.ServiceContext, session sqlx.Session, order *model.Order, refundAmount float64) error {
refundAmount = roundMoney(refundAmount)
if refundAmount <= 0 {
return nil
}
// 查询当前订单关联的所有佣金记录(包括已结算和冻结),剔除已经完全退款的
commissionBuilder := svcCtx.AgentCommissionModel.SelectBuilder()
commissions, commissionsErr := svcCtx.AgentCommissionModel.FindAll(ctx, commissionBuilder.Where(squirrel.And{
squirrel.Eq{"order_id": order.Id},
squirrel.NotEq{"status": 2}, // 排除已全部退款的佣金
}), "")
if commissionsErr != nil {
logx.Errorf("查询代理佣金失败订单ID: %d, 错误: %v", order.Id, commissionsErr)
return nil // 返回 nil因为佣金更新失败不应影响退款流程
}
if len(commissions) == 0 {
return nil
}
// 剩余需要由佣金 + 钱包共同承担的退款金额
remainRefundAmount := refundAmount
// 记录每个代理本次需要从钱包扣除的金额,避免同一代理多条佣金时重复查钱包并产生多条流水
type walletAdjust struct {
agentId int64
amount float64 // 需要从该代理钱包扣除的金额(正数)
}
walletAdjustMap := make(map[int64]*walletAdjust)
// 1. 先在佣金记录上做冲减:增加 RefundedAmount必要时将状态置为已退款
for _, commission := range commissions {
available := roundMoney(commission.Amount - commission.RefundedAmount)
if available <= 0 {
continue
}
if remainRefundAmount <= 0 {
break
}
// 当前这条佣金最多可冲减 available本次实际冲减 currentRefund
currentRefund := available
if currentRefund > remainRefundAmount {
currentRefund = remainRefundAmount
}
currentRefund = roundMoney(currentRefund)
// 更新佣金的已退款金额
commission.RefundedAmount = roundMoney(commission.RefundedAmount + currentRefund)
// 如果这条佣金已经被完全冲减,则标记为已退款
if commission.RefundedAmount >= roundMoney(commission.Amount) {
commission.Status = 2
}
// 更新佣金状态到数据库
var updateCommissionErr error
if session != nil {
updateCommissionErr = svcCtx.AgentCommissionModel.UpdateWithVersion(ctx, session, commission)
} else {
updateCommissionErr = svcCtx.AgentCommissionModel.UpdateWithVersion(ctx, nil, commission)
}
if updateCommissionErr != nil {
logx.Errorf("更新代理佣金状态失败佣金ID: %d, 订单ID: %d, 错误: %v", commission.Id, order.Id, updateCommissionErr)
continue // 如果佣金状态更新失败,就不继续计入本次冲减
}
// 记录该代理需要从钱包扣除的金额(可能后续还有其他佣金叠加)
wa, ok := walletAdjustMap[commission.AgentId]
if !ok {
wa = &walletAdjust{agentId: commission.AgentId}
walletAdjustMap[commission.AgentId] = wa
}
wa.amount = roundMoney(wa.amount + currentRefund)
remainRefundAmount = roundMoney(remainRefundAmount - currentRefund)
}
// 2. 再按代理维度,从钱包(冻结余额/可用余额)中扣除对应金额
for _, wa := range walletAdjustMap {
if wa.amount <= 0 {
continue
}
// 处理用户钱包的金额扣除
wallet, err := svcCtx.AgentWalletModel.FindOneByAgentId(ctx, wa.agentId)
if err != nil {
logx.Errorf("查询代理钱包失败代理ID: %d, 错误: %v", wa.agentId, err)
continue
}
// 记录变动前的余额
balanceBefore := wallet.Balance
frozenBalanceBefore := wallet.FrozenBalance
// 优先从冻结余额中扣除(与原先“冻结佣金优先使用冻结余额”的设计一致)
deduct := roundMoney(wa.amount)
if wallet.FrozenBalance >= deduct {
wallet.FrozenBalance = roundMoney(wallet.FrozenBalance - deduct)
} else {
remaining := roundMoney(deduct - wallet.FrozenBalance)
wallet.FrozenBalance = 0
// 可用余额可以为负数,由业务承担风险
wallet.Balance = roundMoney(wallet.Balance - remaining)
}
// 变动后余额和冻结余额
balanceAfter := roundMoney(wallet.Balance)
frozenBalanceAfter := roundMoney(wallet.FrozenBalance)
// 更新钱包
var updateWalletErr error
if session != nil {
updateWalletErr = svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet)
} else {
updateWalletErr = svcCtx.AgentWalletModel.UpdateWithVersion(ctx, nil, wallet)
}
if updateWalletErr != nil {
logx.Errorf("更新代理钱包失败代理ID: %d, 错误: %v", wa.agentId, updateWalletErr)
continue
}
// 创建钱包交易流水记录(退款)
transErr := svcCtx.AgentService.CreateWalletTransaction(
ctx,
session,
wa.agentId,
model.WalletTransactionTypeRefund,
roundMoney(-wa.amount*-1), // 钱包流水金额为负数
balanceBefore,
balanceAfter,
frozenBalanceBefore,
frozenBalanceAfter,
order.OrderNo,
0, // 这里不强绑到某一条具体佣金记录,按订单维度记录
"订单退款,佣金已扣除",
)
if transErr != nil {
logx.Errorf("创建代理钱包流水记录失败代理ID: %d, 错误: %v", wa.agentId, transErr)
continue
}
}
return nil
}
type WechatPayRefundCallbackLogic struct {
logx.Logger
ctx context.Context
@@ -227,8 +79,8 @@ func (l *WechatPayRefundCallbackLogic) handleQueryOrderRefund(orderNo string, st
return errors.Wrapf(err, "更新查询订单状态失败: %s", orderNo)
}
// 退款成功时,按本次实际退款金额更新代理佣金状态并扣除钱包金额
_ = HandleCommissionAndWalletDeduction(ctx, l.svcCtx, session, order, refundAmountYuan)
// 退款成功时,按本次实际退款金额执行共用扣款流程
l.svcCtx.AgentService.HandleOrderRefundDeduction(ctx, session, order, refundAmountYuan)
}
// 查找最新的pending状态的退款记录