@@ -18,24 +18,61 @@ import (
)
// HandleCommissionAndWalletDeduction 处理退款后的佣金状态更新和钱包金额扣除
// 这是一个公共函数,可以被支付宝和微信退款逻辑共享使用
// 只有当订单状态不为已退款时,才会处理佣金状态更新和钱包金额 扣除
func HandleCommissionAndWalletDeduction ( ctx context . Context , svcCtx * svc . ServiceContext , session sqlx . Session , order * model . Order ) error {
// 查询非已退款的佣金
// refundAmount 为本次实际退款金额(单位:元),从代理侧总共需要承担的金额
// 该函数会优先冲减当前订单相关的佣金(基于 RefundedAmount) , 不足部分再从钱包余额/冻结余额中 扣除
func HandleCommissionAndWalletDeduction ( ctx context . Context , svcCtx * svc . ServiceContext , session sqlx . Session , order * model . Order , refundAmount float64 ) error {
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 } , // 只查询非已 退款的佣金
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 {
// 记录更新佣金状态前的状态值,用于后续判断
oldCommissionStatus : = commission . Status
commission . Status = 2 // 设置为已退款
available := commission . Amount - commission . RefundedAmount
if available < = 0 {
continue
}
if remainRefundAmount <= 0 {
break
}
// 当前这条佣金最多可冲减 available, 本次实际冲减 currentRefund
currentRefund := available
if currentRefund > remainRefundAmount {
currentRefund = remainRefundAmount
}
// 更新佣金的已退款金额
commission . RefundedAmount += currentRefund
// 如果这条佣金已经被完全冲减,则标记为已退款
if commission . RefundedAmount >= commission . Amount {
commission . Status = 2
}
// 更新佣金状态到数据库
var updateCommissionErr error
@@ -46,13 +83,30 @@ func HandleCommissionAndWalletDeduction(ctx context.Context, svcCtx *svc.Service
}
if updateCommissionErr != nil {
logx . Errorf ( "更新代理佣金状态失败, 佣金ID: %d, 订单ID: %d, 错误: %v" , commission . Id , order . Id , updateCommissionErr )
continue // 如果佣金状态更新失败,就不继续处理钱包
continue // 如果佣金状态更新失败,就不继续计入本次冲减
}
// 记录该代理需要从钱包扣除的金额(可能后续还有其他佣金叠加)
wa , ok := walletAdjustMap [ commission . AgentId ]
if ! ok {
wa = & walletAdjust { agentId : commission . AgentId }
walletAdjustMap [ commission . AgentId ] = wa
}
wa . amount += currentRefund
remainRefundAmount -= currentRefund
}
// 2. 再按代理维度,从钱包(冻结余额/可用余额)中扣除对应金额
for _ , wa := range walletAdjustMap {
if wa . amount <= 0 {
continue
}
// 处理用户钱包的金额扣除
wallet , err := svcCtx . AgentWalletModel . FindOneByAgentId ( ctx , commission . A gentId)
wallet , err := svcCtx . AgentWalletModel . FindOneByAgentId ( ctx , wa . a gentId)
if err != nil {
logx . Errorf ( "查询代理钱包失败, 代理ID: %d, 错误: %v" , commission . A gentId, err )
logx . Errorf ( "查询代理钱包失败, 代理ID: %d, 错误: %v" , wa . a gentId, err )
continue
}
@@ -60,22 +114,17 @@ func HandleCommissionAndWalletDeduction(ctx context.Context, svcCtx *svc.Service
balanceBefore := wallet . Balance
frozenBalanceBefore := wallet . FrozenBalance
// 如果是冻结状态的佣金,直接扣减冻结余额
if oldCommissionStatus = = 1 {
// 冻结状态的佣金,根据订单金额优先减少冻结金额,如果冻结金额不足则减少钱包余额
if wallet . FrozenBalance > = order . Amount {
// 冻结余额足够,优先减少冻结金额
wallet . FrozenBalance -= order . Amount
} else {
// 冻结余额不足,先扣减所有冻结金额,再扣减余额
remaining := order . Amount - wallet . FrozenBalance
wallet . FrozenBalance = 0
wallet . Balance -= remaining
}
// 其他状态的佣金, 比如已结算状态( status = 0) , 直接扣减钱包余额
// 优先从冻结余额中扣除(与原先“冻结佣金优先使用冻结余额”的设计一致)
deduct : = wa . amount
if wallet . FrozenBalance >= deduct {
wallet . FrozenBalance - = deduct
} else {
wallet . Balance -= order . Amount
remaining := deduct - wallet . FrozenBalance
wallet . FrozenBalance = 0
// 可用余额可以为负数,由业务承担风险
wallet . Balance -= remaining
}
// 变动后余额和冻结余额
balanceAfter := wallet . Balance
frozenBalanceAfter := wallet . FrozenBalance
@@ -87,29 +136,28 @@ func HandleCommissionAndWalletDeduction(ctx context.Context, svcCtx *svc.Service
updateWalletErr = svcCtx . AgentWalletModel . UpdateWithVersion ( ctx , nil , wallet )
}
if updateWalletErr != nil {
logx . Errorf ( "更新代理钱包失败, 代理ID: %d, 错误: %v" , commission . A gentId, updateWalletErr )
logx . Errorf ( "更新代理钱包失败, 代理ID: %d, 错误: %v" , wa . a gentId, updateWalletErr )
continue
}
// 创建钱包交易流水记录(退款)
transErr := svcCtx . AgentService . CreateWalletTransaction (
ctx ,
session ,
commission . A gentId,
wa . a gentId,
model . WalletTransactionTypeRefund ,
- order . A mount,
- wa . a mount* - 1 , // 钱包流水金额为负数
balanceBefore ,
balanceAfter ,
frozenBalanceBefore ,
frozenBalanceAfter ,
order . OrderNo ,
commission . Id ,
0 , // 这里不强绑到某一条具体佣金记录,按订单维度记录
"订单退款,佣金已扣除" ,
)
if transErr != nil {
logx . Errorf ( "创建代理钱包流水记录失败, 代理ID: %d, 错误: %v" , commission . A gentId, transErr )
logx . Errorf ( "创建代理钱包流水记录失败, 代理ID: %d, 错误: %v" , wa . a gentId, transErr )
continue
}
}
return nil
}
@@ -129,7 +177,8 @@ func NewWechatPayRefundCallbackLogic(ctx context.Context, svcCtx *svc.ServiceCon
}
// handleQueryOrderRefund 处理查询订单退款
func ( l * WechatPayRefundCallbackLogic ) handleQueryOrderRefund ( orderNo string , status refunddomestic . Status ) error {
// refundAmountYuan 表示微信本次实际退款金额(单位:元)
func ( l * WechatPayRefundCallbackLogic ) handleQueryOrderRefund ( orderNo string , status refunddomestic . Status , refundAmountYuan float64 ) error {
order , err := l . svcCtx . OrderModel . FindOneByOrderNo ( l . ctx , orderNo )
if err != nil {
return errors . Wrapf ( err , "查找查询订单信息失败: %s" , orderNo )
@@ -171,8 +220,8 @@ func (l *WechatPayRefundCallbackLogic) handleQueryOrderRefund(orderNo string, st
return errors . Wrapf ( err , "更新查询订单状态失败: %s" , orderNo )
}
// 退款成功时,更新代理佣金状态并扣除钱包金额
HandleCommissionAndWalletDeduction ( ctx , l . svcCtx , session , order )
// 退款成功时,按本次实际退款金额 更新代理佣金状态并扣除钱包金额
_ = HandleCommissionAndWalletDeduction ( ctx , l . svcCtx , session , order , refundAmountYuan )
}
// 查找最新的pending状态的退款记录
@@ -295,15 +344,22 @@ func (l *WechatPayRefundCallbackLogic) WechatPayRefundCallback(w http.ResponseWr
var processErr error
// 计算本次实际退款金额(单位:元),用于后续佣金和钱包扣减
var refundAmountYuan float64
if notification . Amount != nil && notification . Amount . Refund != nil {
// 微信退款金额单位为分,这里转换为元
refundAmountYuan = float64 ( * notification . Amount . Refund ) / 100.0
}
// 4. 根据订单号前缀处理不同类型的订单
switch {
case strings . HasPrefix ( orderNo , "Q_" ) :
processErr = l . handleQueryOrderRefund ( orderNo , status )
processErr = l . handleQueryOrderRefund ( orderNo , status , refundAmountYuan )
case strings . HasPrefix ( orderNo , "A_" ) :
processErr = l . handleAgentOrderRefund ( orderNo , status )
default :
// 兼容旧订单,假设没有前缀的是查询订单
processErr = l . handleQueryOrderRefund ( orderNo , status )
processErr = l . handleQueryOrderRefund ( orderNo , status , refundAmountYuan )
}
// 5. 处理错误并响应