Kaynağa Gözat

退款申请,微信退款处理

skyline 2 yıl önce
ebeveyn
işleme
22dfc8477b

+ 26 - 0
common/src/main/java/com/kym/common/utils/LambadaTools.java

@@ -0,0 +1,26 @@
+package com.kym.common.utils;
+
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+public class LambadaTools {
+    /**
+     * 利用BiConsumer实现foreach循环支持index
+     *
+     * @param biConsumer
+     * @param <T>
+     * @return
+     */
+    public static <T> Consumer<T> forEachWithIndex(BiConsumer<T, Integer> biConsumer) {
+        /*这里说明一下,我们每次传入forEach都是一个重新实例化的Consumer对象,在lambada表达式中我们无法对int进行++操作,
+        我们模拟AtomicInteger对象,写个getAndIncrement方法,不能直接使用AtomicInteger哦*/
+        class IncrementInt {
+            int i = 0;
+            public int getAndIncrement() {
+                return i++;
+            }
+        }
+        IncrementInt incrementInt = new IncrementInt();
+        return t -> biConsumer.accept(t, incrementInt.getAndIncrement());
+    }
+}

+ 3 - 2
miniapp/src/main/java/com/kym/miniapp/controller/PaymentController.java

@@ -65,8 +65,9 @@ public class PaymentController {
     @ApiLog("微信退款")
     @PostMapping("/wxRefund")
     @ResponseBody
-    R wxRefund(@RequestBody JSONObject params) {
-        return R.success(wxPayService.wxRefund(params));
+    R wxRefund() {
+        wxPayService.applyWxRefund();
+        return R.success();
     }
 
     @ApiLog(value = "微信退款回调", ignoreParams = true)

+ 9 - 2
service/src/main/java/com/kym/service/miniapp/impl/ChargeServiceImpl.java

@@ -167,12 +167,18 @@ public class ChargeServiceImpl implements ChargeService {
     public Map<String, String> getConnectorIdAndStationId(String connectorId) {
         var stationId = "";
         if (connectorId.length() == 17) {
-            var equipmentRelation = equipmentRelationService.getByEquipmentId(connectorId.substring(0,16));
+            var equipmentRelation = equipmentRelationService.getByEquipmentId(connectorId.substring(0, 16));
             stationId = equipmentRelation.getStationId();
         } else if (connectorId.length() == 6) {
             // 查询EN+设备SN
             var equipmentRelation = equipmentRelationService.getByShortId(connectorId);
-            connectorId = equipmentRelation.getEquipmentId();
+            var conId = equipmentRelation.getEquipmentId();
+            if (conId == null) {
+                // 编码输入错误
+                LOGGER.error("设备编码错误:{}", connectorId);
+                throw new BusinessException(ResponseEnum.EQUIP_CONNECTOR_ID_ERROR);
+            }
+            connectorId = conId;
             stationId = equipmentRelation.getStationId();
             if (connectorId.length() == 16) {
                 // 如果是16位,末尾补1作为单枪枪号
@@ -215,6 +221,7 @@ public class ChargeServiceImpl implements ChargeService {
     public void queryStopCharge(String connectorId) {
         var userId = StpUtil.getLoginIdAsLong();
         connectorId = getConnectorId(connectorId);
+        LOGGER.info("用户:{}请求停止充电,设备:{}", userId, connectorId);
         // 查询充电订单
         var chargeOrder = chargeOrderService.lambdaQuery()
                 .eq(ChargeOrder::getUserId, userId)

+ 3 - 1
service/src/main/java/com/kym/service/wechat/WxPayService.java

@@ -14,7 +14,9 @@ import java.io.IOException;
  * @date 2023-08-11 19:05
  */
 public interface WxPayService {
-    Refund wxRefund(JSONObject params);
+    void applyWxRefund();
+
+    Refund wxRefund(long refundLogId);
 
     Refund queryByOutRefundNo(String outRefundNo);
 

+ 94 - 35
service/src/main/java/com/kym/service/wechat/impl/WxPayServiceImpl.java

@@ -10,6 +10,8 @@ import com.alibaba.fastjson2.JSONObject;
 import com.kym.common.config.WxPayConfig;
 import com.kym.common.constant.ResponseEnum;
 import com.kym.common.exception.BusinessException;
+import com.kym.common.utils.CommUtil;
+import com.kym.common.utils.LambadaTools;
 import com.kym.common.utils.OrderUtils;
 import com.kym.entity.miniapp.Account;
 import com.kym.entity.miniapp.PayLog;
@@ -45,6 +47,8 @@ import java.io.FileInputStream;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicInteger;
 
 
 /**
@@ -198,8 +202,6 @@ public class WxPayServiceImpl implements WxPayService {
         return service.prepayWithRequestPayment(request);
     }
 
-    // TODO: 2023-09-11  判断退款金额需要几笔充值订单进行退款操作 这个放在外层做,这里只支持单笔订单退款
-
     /**
      * 微信支付订单号查询订单
      */
@@ -296,16 +298,10 @@ public class WxPayServiceImpl implements WxPayService {
     }
 
     /**
-     * 创建退款申请
-     *
-     * @return
+     * 申请退款
      */
     @Override
-    public Refund wxRefund(JSONObject params) {
-        // 查询用户是否有未完结的订单,如果有则提示等待订单完成后再申请,如果无则查询可用余额,将金额转至冻结金额
-        // 生成订单号
-        String outRefundNo = OrderUtils.getOrderNo();
-
+    public void applyWxRefund() {
         var userId = StpUtil.getLoginIdAsLong();
         var chargeOrder = chargeOrderService.getChargingOrderByUserId(userId);
         if (chargeOrder != null) {
@@ -315,32 +311,97 @@ public class WxPayServiceImpl implements WxPayService {
         if (account.getBalance() <= 0) {
             throw new BusinessException("账户余额不足,无需退款");
         }
+
         // 将余额转移至冻结余额
         accountService.lambdaUpdate().setSql(" frozen_amount = balance ,balance = 0").eq(Account::getUserId, userId);
 
+        // 余额作为退款金额
+        AtomicInteger refundAmount = new AtomicInteger(account.getBalance());
+        // 充值记录
+        var payLogs = payLogService.lambdaQuery().eq(PayLog::getUserId, userId).eq(PayLog::getTradeState, "SUCCESS").orderByDesc(PayLog::getSuccessTime).list();
+        // 最后一次的充值金额可以覆盖退款金额
+        if (!CommUtil.isEmptyOrNull(payLogs) && payLogs.get(0).getTotal() >= refundAmount.get()) {
+            // 退款日志
+            createRefundLog(payLogs.get(0));
+        } else if (!CommUtil.isEmptyOrNull(payLogs) && payLogs.get(0).getTotal() < refundAmount.get()) {
+            // 最后一次的充值金额不能覆盖退款金额,拆分成多笔退款
+            int amount = 0;
+            var refundPayLogs = new ArrayList<PayLog>();
+            for (PayLog payLog : payLogs) {
+                amount += payLog.getTotal();
+                refundPayLogs.add(payLog);
+                if (amount >= refundAmount.get()) {
+                    break;
+                }
+            }
+            // 需要退款的记录数量
+            var size = refundPayLogs.size();
+            refundPayLogs.forEach(LambadaTools.forEachWithIndex((item, index) -> {
+                // 如果不是最后一笔,金额全退,最后一笔退剩余的金额
+                if (index < size) {
+                    createRefundLog(item);
+                    refundAmount.addAndGet(-item.getTotal());
+                } else {
+                    item.setTotal(refundAmount.get());
+                    createRefundLog(item);
+                }
+            }));
+        } else {
+            // 退款异常
+            LOGGER.error("退款异常:userId:{},余额:{},payLogs:{}", userId, refundAmount.get(), payLogs);
+            throw new BusinessException("退款异常");
+        }
+    }
+
+    /**
+     * 创建退款申请记录
+     *
+     * @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);
+    }
+
+
+    /**
+     * 处理退款
+     *
+     * @return
+     */
+    @Override
+    public Refund wxRefund(long refundLogId) {
+        // 通过退款申请id获取退款申请记录
+        var refundLog = refundLogService.getById(refundLogId);
+
         // 钱包流水
         var walletDetail = new WalletDetail()
                 .setType(WalletDetail.TYPE_提现)
                 .setStatus(WalletDetail.STATUS_待确认)
-                .setUserId(userId)
-                .setAmount(params.getIntValue("amount"))
-                .setOrderNo(outRefundNo);
+                .setUserId(refundLog.getUserId())
+                .setAmount(refundLog.getRefund())
+                .setOrderNo(refundLog.getOutRefundNo());
         walletDetailService.save(walletDetail);
 
-
         CreateRequest request = new CreateRequest();
         // 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
         // 原始第三方订单号
-        request.setOutTradeNo(params.getString("outTradeNo"));
+        request.setOutTradeNo(refundLog.getOutTradeNo());
         // 退款订单号
-        request.setOutRefundNo(outRefundNo);
+        request.setOutRefundNo(refundLog.getOutRefundNo());
         // 退款原因
         request.setReason("用户申请退款");
         // 回调通知地址
         request.setNotifyUrl(conf.getRefundNotifyUrl());
         var amount = new AmountReq();
         // 退款金额
-        amount.setRefund(params.getLong("amount"));
+        amount.setRefund((long) refundLog.getRefund());
         request.setAmount(amount);
         return refundService.create(request);
     }
@@ -352,7 +413,6 @@ public class WxPayServiceImpl implements WxPayService {
      */
     @Override
     public Refund queryByOutRefundNo(String outRefundNo) {
-
         QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest();
         // 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
         request.setOutRefundNo(outRefundNo);
@@ -377,21 +437,11 @@ public class WxPayServiceImpl implements WxPayService {
             DateTime dt = DateUtil.parse(refundNotification.getSuccessTime());
             LocalDateTime successTime = LocalDateTimeUtil.of(dt);
 
-            // 资金流水
-            var walletDetail = walletDetailService.getWalletDetailByOrderNo(refundNotification.getOutTradeNo(), WalletDetail.TYPE_提现);
-            walletDetail.setStatus(WalletDetail.STATUS_已确认)  //已确认
-                    .setCurrency(refundNotification.getAmount().getCurrency())
-                    .setAmount(refundNotification.getAmount().getRefund().intValue())
-                    .setTransactionTime(successTime);
-            walletDetailService.updateById(walletDetail);
-
-            // 退款日志
-            var refundLog = new RefundLog()
-                    .setUserId(walletDetail.getUserId())
+            //退款日志在申请时插入,接收通知时更新
+            var refundLog = refundLogService.lambdaQuery().eq(RefundLog::getOutRefundNo, refundNotification.getOutRefundNo()).one();
+            refundLog
                     .setRefundId(refundNotification.getRefundId())
                     .setTransactionId(refundNotification.getTransactionId())
-                    .setOutTradeNo(walletDetail.getOrderNo())
-                    .setOutRefundNo(refundNotification.getOutRefundNo())
                     .setChannel(refundNotification.getChannel().name())
                     .setUserReceivedAccount(refundNotification.getUserReceivedAccount())
                     .setSuccessTime(successTime)
@@ -401,14 +451,23 @@ public class WxPayServiceImpl implements WxPayService {
                     .setRefund(refundNotification.getAmount().getRefund().intValue())
                     .setCurrency(refundNotification.getAmount().getCurrency());
             refundLog.setCreateTime(LocalDateTimeUtil.of(DateUtil.parse(refundNotification.getCreateTime())));
-            refundLogService.save(refundLog);
+            refundLogService.updateById(refundLog);
+
             if ("SUCCESS".equals(refundNotification.getRefundStatus().name())) {
-                // 退款成功将冻结余额清零,新增退款钱包流水,更新退款流水
-                accountService.lambdaUpdate().setSql("frozen_amount = 0").eq(Account::getUserId, walletDetail.getUserId());
+                // 冻结金额扣减此次退款金额
+                accountService.lambdaUpdate().setSql(" frozen_amount = (frozen_amount - %d)".formatted(refundNotification.getAmount().getRefund().intValue())).eq(Account::getUserId, refundLog.getUserId());
+                // 更新资金流水
+                var walletDetail = walletDetailService.getWalletDetailByOrderNo(refundNotification.getOutTradeNo(), WalletDetail.TYPE_提现);
+                walletDetail.setStatus(WalletDetail.STATUS_已确认)  //已确认
+                        .setCurrency(refundNotification.getAmount().getCurrency())
+                        .setAmount(refundNotification.getAmount().getRefund().intValue())
+                        .setTransactionTime(successTime);
+                walletDetailService.updateById(walletDetail);
+
                 LOGGER.info("微信退款回调{}:业务处理结束", notifyRes[2]);
             } else {
                 // 退款失败
-                LOGGER.error("微信退款失败,用户id:{},退款状态:{} \n 退款结果通知详情:{}", walletDetail.getUserId(), refundNotification.getRefundStatus().name(), refundNotification);
+                LOGGER.error("微信退款失败,用户id:{},退款状态:{} \n 退款结果通知详情:{}", refundLog.getUserId(), refundNotification.getRefundStatus().name(), refundNotification);
                 throw new BusinessException("处理异常");
             }
         } catch (ValidationException e) {