Переглянути джерело

退款时权益优惠金额处理

skyline 2 роки тому
батько
коміт
4b1701668f

+ 5 - 0
entity/src/main/java/com/kym/entity/miniapp/RefundLog.java

@@ -89,6 +89,11 @@ public class RefundLog extends BaseEntity {
      */
     private Integer refund;
 
+    /**
+     * 不可退金额(分)
+     */
+    private Integer discountAmount;
+
     /**
      * 用户支付币种
      */

+ 2 - 1
mapper/src/main/resources/mappers/miniapp/RefundLogMapper.xml

@@ -16,6 +16,7 @@
         <result column="funds_account" property="fundsAccount" />
         <result column="total" property="total" />
         <result column="refund" property="refund" />
+        <result column="discount_amount" property="discountAmount" />
         <result column="currency" property="currency" />
         <result column="reason" property="reason" />
     </resultMap>
@@ -40,7 +41,7 @@
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        id,user_id, refund_id, out_refund_no, transaction_id, out_trade_no, channel, user_received_account, success_time, status, funds_account, total, refund, currency,reason,create_time,update_time
+        id,user_id, refund_id, out_refund_no, transaction_id, out_trade_no, channel, user_received_account, success_time, status, funds_account, total, refund, discount_amount,currency,reason,create_time,update_time
     </sql>
 
 

+ 2 - 2
service/src/main/java/com/kym/service/enplus/impl/EnNotifyServiceImpl.java

@@ -315,8 +315,8 @@ public class EnNotifyServiceImpl implements EnNotifyService {
                                 .setStatus(UserRechargeRights.STATUS_无效);
 
                         orderRechargeRights.setDiscountAmount(realDiscountAmount);
-                        // 此权益包消耗完毕,账户设置优惠不可退金额,将次权益金产生的优惠全部扣除
-                        account.setDiscountAmount(account.getDiscountAmount() + realDiscountAmount - userRechargeRights.getDiscountAmount());
+                        // 此权益包消耗完毕,账户设置优惠不可退金额
+                        account.setDiscountAmount(account.getDiscountAmount() + realDiscountAmount);
                     }
                 }
 

+ 41 - 24
service/src/main/java/com/kym/service/wechat/impl/WxPayServiceImpl.java

@@ -365,16 +365,21 @@ public class WxPayServiceImpl implements WxPayService {
         if (chargeOrder != null) {
             throw new BusinessException("存在未完结的订单,请等待所有订单完结之后重试");
         }
+
         var account = accountService.getAccountByUserId(userId);
         if (account.getBalance() <= 0) {
             throw new BusinessException("账户余额不足,无需退款");
         }
+        // 校验余额大于优惠金额
+        if (account.getBalance() <= account.getDiscountAmount()) {
+            throw new BusinessException("不可退金额高于钱包余额,不支持退款");
+        }
 
         // 将余额转移至冻结余额
-        accountService.lambdaUpdate().setSql(" frozen_amount = (frozen_amount + balance) ,balance = 0").eq(Account::getUserId, userId).update();
+        accountService.lambdaUpdate().setSql(" frozen_amount = (frozen_amount + (balance - discount_amount)) ,balance = 0").eq(Account::getUserId, userId).update();
 
-        // 余额作为退款金额
-        AtomicInteger refundAmount = new AtomicInteger(account.getBalance());
+        // 余减去优惠金额作为退款金额
+        AtomicInteger refundAmount = new AtomicInteger(account.getBalance() - account.getDiscountAmount());
         // 充值记录
         var payLogs = payLogService.lambdaQuery().eq(PayLog::getUserId, userId).eq(PayLog::getTradeState, RefundLog.STATUS_退款成功).orderByDesc(PayLog::getSuccessTime).list();
         // 充值金额综合不足以支付余额退款
@@ -388,7 +393,10 @@ public class WxPayServiceImpl implements WxPayService {
         // 最后一次的充值金额可以覆盖退款金额
         if (!CommUtil.isEmptyOrNull(payLogs) && payLogs.get(0).getTotal() >= refundAmount.get()) {
             // 退款日志
-            createRefundLog(payLogs.get(0));
+            new RefundLog().setUserId(payLogs.get(0).getUserId()).setOutTradeNo(payLogs.get(0).getOutTradeNo())
+                    .setTotal(payLogs.get(0).getTotal()).setRefund(payLogs.get(0).getTotal() - account.getDiscountAmount())
+                    .setDiscountAmount(account.getDiscountAmount()).setOutRefundNo(OrderUtils.getOrderNo());
+
         } else if (!CommUtil.isEmptyOrNull(payLogs) && payLogs.get(0).getTotal() < refundAmount.get()) {
             // 最后一次的充值金额不能覆盖退款金额,拆分成多笔退款
             int amount = 0;
@@ -402,16 +410,37 @@ public class WxPayServiceImpl implements WxPayService {
             }
             // 需要退款的记录数量
             var size = refundPayLogs.size();
+
+            var refundLogList = new ArrayList<RefundLog>(size);
+
             refundPayLogs.forEach(LambadaTools.forEachWithIndex((item, index) -> {
                 // 如果不是最后一笔,金额全退,最后一笔退剩余的金额
                 if (index < size) {
-                    createRefundLog(item);
+                    refundLogList.add(new RefundLog().setUserId(item.getUserId()).setOutTradeNo(item.getOutTradeNo())
+                            .setTotal(item.getTotal()).setRefund(item.getTotal()).setOutRefundNo(OrderUtils.getOrderNo()));
                     refundAmount.addAndGet(-item.getTotal());
                 } else {
                     item.setTotal(refundAmount.get());
-                    createRefundLog(item);
+                    refundLogList.add(new RefundLog().setUserId(item.getUserId()).setOutTradeNo(item.getOutTradeNo())
+                            .setTotal(item.getTotal()).setRefund(refundAmount.get()).setOutRefundNo(OrderUtils.getOrderNo()));
                 }
             }));
+
+            // 不可退金额按退款金额比例放入每笔退款中
+            refundLogList.forEach(LambadaTools.forEachWithIndex((refundLog, index) -> {
+                refundLogList.remove(index.intValue());
+                int discountAmount;
+                if (index < size) {
+                    discountAmount = account.getDiscountAmount() * (refundLog.getRefund() / refundAmount.get());
+                } else {
+                    // 签名存在精度误差,最后一个元素用总数详见最精确
+                    discountAmount = account.getDiscountAmount() - refundLogList.stream().mapToInt(RefundLog::getDiscountAmount).sum();
+                }
+                refundLog.setDiscountAmount(discountAmount);
+                refundLogList.add(index, refundLog);
+            }));
+
+            refundLogService.saveBatch(refundLogList);
         } else {
             // 退款异常
             LOGGER.error("退款异常:userId:{},余额:{},payLogs:{}", userId, refundAmount.get(), payLogs);
@@ -419,22 +448,6 @@ public class WxPayServiceImpl implements WxPayService {
         }
     }
 
-    /**
-     * 创建退款申请记录
-     *
-     * @param payLog
-     */
-    public void createRefundLog(PayLog payLog) {
-        // 退款日志
-        var refundLog = new RefundLog()
-                .setUserId(payLog.getUserId())
-                .setOutTradeNo(payLog.getOutTradeNo())
-                .setOutRefundNo(OrderUtils.getOrderNo())
-                .setTotal(payLog.getTotal())
-                .setRefund(payLog.getTotal());
-        refundLogService.save(refundLog);
-    }
-
 
     /**
      * 处理退款
@@ -513,6 +526,7 @@ public class WxPayServiceImpl implements WxPayService {
 
             //退款日志在申请时插入,接收通知时更新
             var refundLog = refundLogService.lambdaQuery().eq(RefundLog::getOutRefundNo, refundNotification.getOutRefundNo()).one();
+            // 防止重复处理消息
             if (RefundLog.STATUS_退款成功.equals(refundLog.getStatus())) {
                 return;
             }
@@ -532,8 +546,11 @@ public class WxPayServiceImpl implements WxPayService {
             refundLogService.updateById(refundLog);
 
             if (RefundLog.STATUS_退款成功.equals(refundNotification.getRefundStatus().name())) {
-                // 冻结金额扣减此次退款金额
-                accountService.lambdaUpdate().setSql(" frozen_amount = (frozen_amount - %d)".formatted(refundNotification.getAmount().getRefund().intValue())).eq(Account::getUserId, refundLog.getUserId()).update();
+                // 冻结金额扣减此次退款金额,优惠金额字段减去申请退款时的优惠金额
+                accountService.lambdaUpdate().setSql("frozen_amount = (frozen_amount - %d) , discount_amount = (discount_amount - %d)"
+                                .formatted(refundNotification.getAmount().getRefund().intValue()), refundLog.getDiscountAmount())
+                        .eq(Account::getUserId, refundLog.getUserId()).update();
+
                 // 更新资金流水
                 var walletDetail = walletDetailService.getWalletDetailByOrderNo(refundNotification.getOutRefundNo(), WalletDetail.TYPE_提现);
                 walletDetail.setStatus(WalletDetail.STATUS_已确认)  //已确认