Quellcode durchsuchen

fix bxj余额退款现金

zoujiajian vor 1 Tag
Ursprung
Commit
55f6a72359

+ 1 - 1
netflix-dao/src/main/java/com/cyksj/model/request/OrderBalanceRefundReq.java

@@ -19,7 +19,7 @@ public class OrderBalanceRefundReq {
 	private Long refundOrderId;
 
 	/**
-	 * 退款金额
+	 * 退款余额转为现金的金额;不传时按渠道剩余可退额度转换。
 	 */
 	private BigDecimal refundMoney;
 }

+ 114 - 26
netflix-service/src/main/java/com/cyksj/service/mange/impl/CmsOrderDonServiceImpl.java

@@ -2258,46 +2258,116 @@ public class CmsOrderDonServiceImpl extends ServiceImpl<OrderDonMapper,OrderDon>
     @Transactional(rollbackFor = Throwable.class)
     public void clearBalanceToRefundMoney(OrderBalanceRefundReq refundReq) throws Exception {
         Long refundOrderId = refundReq.getRefundOrderId();
-        BigDecimal refundMoney = refundReq.getRefundMoney();
         RefundOrderDon refundOrderDon = refundOrderDonMapper.selectById(refundOrderId);
-        if (!"balance".equals(refundOrderDon.getRefundMethod())) {
-            throw BusinessRuntimeException.getInstance("该记录不是余额退款类型");
+        if (refundOrderDon == null) {
+            throw BusinessRuntimeException.getInstance("退款记录不存在");
+        }
+        String refundMethod = refundOrderDon.getRefundMethod();
+        boolean balanceRefund = OrderRefundReq.RefundType.balance.name().equals(refundMethod);
+        boolean mixedRefund = OrderRefundReq.RefundType.bxj.name().equals(refundMethod);
+        if (!balanceRefund && !mixedRefund) {
+            throw BusinessRuntimeException.getInstance("该记录不包含可转现金的退款余额");
         }
+        if (Boolean.TRUE.equals(refundOrderDon.getIsClearBalance())) {
+            throw BusinessRuntimeException.getInstance("该笔退款余额记录已转为现金");
+        }
+
+        // balance 类型的余额金额保存在 refundFee;bxj 只转换其中的 refundBalance,
+        // 不能把 bxj 已经退过的现金 refundFee 再退一次。
+        BigDecimal refundableBalance = balanceRefund
+                ? Optional.ofNullable(refundOrderDon.getRefundFee()).orElse(BigDecimal.ZERO)
+                : Optional.ofNullable(refundOrderDon.getRefundBalance()).orElse(BigDecimal.ZERO);
+        if (refundableBalance.compareTo(BigDecimal.ZERO) <= 0) {
+            throw BusinessRuntimeException.getInstance("该记录没有可转现金的退款余额");
+        }
+
         Long orderId = refundOrderDon.getOrderId();
         OrderDon orderDon = orderDonMapper.selectById(orderId);
         if (orderDon == null) {
-            throw BusinessRuntimeException.getInstance("Order does not exist");
+            throw BusinessRuntimeException.getInstance("订单不存在");
+        }
+        OrderDon.Type paymentType = orderDon.getType();
+        if (paymentType != OrderDon.Type.WeChat
+                && paymentType != OrderDon.Type.ALI_PAY
+                && paymentType != OrderDon.Type.PAYPAL
+                && paymentType != OrderDon.Type.STRIPE) {
+            throw BusinessRuntimeException.getInstance("该订单支付方式不支持原路退款");
         }
         if (orderDonTicketRecordService.exists(orderId)) {
             throw BusinessRuntimeException.getInstance("Multi-quantity orders only support whole-order refunds");
         }
-        if (orderDon.getMoney().compareTo(BigDecimal.ZERO) == 0) {
+        BigDecimal orderMoney = Optional.ofNullable(orderDon.getMoney()).orElse(BigDecimal.ZERO);
+        if (orderMoney.compareTo(BigDecimal.ZERO) <= 0) {
             throw BusinessRuntimeException.getInstance("该记录对应的订单无实际支付金额");
         }
-        if (refundOrderDon.getIsClearBalance()) {
-            throw BusinessRuntimeException.getInstance("该笔退款余额记录已退款");
+
+        BigDecimal orderRefundMoney = Optional.ofNullable(orderDon.getRefundMoney()).orElse(BigDecimal.ZERO);
+        BigDecimal orderRefundBalance = Optional.ofNullable(orderDon.getRefundBalance()).orElse(BigDecimal.ZERO);
+        BigDecimal remainingCash = orderMoney.subtract(orderRefundMoney);
+        if (remainingCash.compareTo(BigDecimal.ZERO) <= 0) {
+            throw BusinessRuntimeException.getInstance("该订单实际支付金额已全部退款");
         }
-        BigDecimal refundFee = refundOrderDon.getRefundFee();
-        //默认退全部余额
-        if (refundMoney != null) {
-            refundFee = refundMoney;
+
+        BigDecimal requestedMoney = refundReq.getRefundMoney();
+        if (requestedMoney != null && requestedMoney.compareTo(BigDecimal.ZERO) <= 0) {
+            throw BusinessRuntimeException.getInstance("退款金额必须大于0");
         }
-        BigDecimal money = orderDon.getMoney();
-        //若退款余额 大于退款金额 将退款余额至为 退款金额
-        if (refundFee.compareTo(money) > 0) {
-            refundFee = money;
+        if (requestedMoney != null && requestedMoney.compareTo(refundableBalance) > 0) {
+            throw BusinessRuntimeException.getInstance("退款金额不能大于该记录的退款余额");
+        }
+        if (requestedMoney != null && requestedMoney.compareTo(remainingCash) > 0) {
+            throw BusinessRuntimeException.getInstance("退款金额不能大于订单剩余可退现金金额");
         }
+
+        // 未指定金额时尽量转换整笔,但不得超过支付渠道剩余可退现金。
+        BigDecimal refundFee = requestedMoney == null
+                ? refundableBalance.min(remainingCash)
+                : requestedMoney;
+        if (refundFee.compareTo(orderRefundBalance) > 0) {
+            throw BusinessRuntimeException.getInstance("订单剩余退款余额不足,无法转为现金");
+        }
+
         User user = userMapper.selectById(orderDon.getUserId());
-        if (user.getBalance().compareTo(refundFee) < 0) {
+        if (user == null) {
+            throw BusinessRuntimeException.getInstance("用户不存在");
+        }
+        BigDecimal userBalance = Optional.ofNullable(user.getBalance()).orElse(BigDecimal.ZERO);
+        if (userBalance.compareTo(refundFee) < 0) {
             throw BusinessRuntimeException.getInstance("用户余额不足,无法扣除对应退款余额");
         }
         int update = userMapper.update(null, Wrappers.lambdaUpdate(User.class)
-                .set(User::getBalance, user.getBalance().subtract(refundFee))
+                .set(User::getBalance, userBalance.subtract(refundFee))
                 .eq(User::getId, user.getId())
-                .eq(User::getBalance, user.getBalance()));
+                .eq(User::getBalance, userBalance));
         if (update == 0) {
             throw BusinessRuntimeException.getInstance("扣除用户退款余额失败,请重试...");
         }
+
+        // 先用条件更新占住退款记录和订单金额;后续渠道退款失败时由事务统一回滚。
+        int refundRecordUpdate = refundOrderDonMapper.update(null, Wrappers.lambdaUpdate(RefundOrderDon.class)
+                .set(RefundOrderDon::getIsClearBalance, true)
+                .set(mixedRefund, RefundOrderDon::getRefundBalance, refundableBalance.subtract(refundFee))
+                .eq(RefundOrderDon::getId, refundOrderId)
+                .and(wrapper -> wrapper.eq(RefundOrderDon::getIsClearBalance, false)
+                        .or().isNull(RefundOrderDon::getIsClearBalance)));
+        if (refundRecordUpdate == 0) {
+            throw BusinessRuntimeException.getInstance("该笔退款余额已处理,请勿重复操作");
+        }
+
+        BigDecimal newRefundBalance = orderRefundBalance.subtract(refundFee);
+        BigDecimal newRefundMoney = orderRefundMoney.add(refundFee);
+        int orderUpdate = orderDonMapper.update(null, Wrappers.lambdaUpdate(OrderDon.class)
+                .set(OrderDon::getRefundBalance, newRefundBalance)
+                .set(OrderDon::getRefundMoney, newRefundMoney)
+                .eq(OrderDon::getId, orderId)
+                .eq(orderDon.getRefundBalance() != null, OrderDon::getRefundBalance, orderDon.getRefundBalance())
+                .isNull(orderDon.getRefundBalance() == null, OrderDon::getRefundBalance)
+                .eq(orderDon.getRefundMoney() != null, OrderDon::getRefundMoney, orderDon.getRefundMoney())
+                .isNull(orderDon.getRefundMoney() == null, OrderDon::getRefundMoney));
+        if (orderUpdate == 0) {
+            throw BusinessRuntimeException.getInstance("订单退款金额已发生变化,请重试");
+        }
+
         OrderRefundReq orderRefundReq = new OrderRefundReq();
         orderRefundReq.setOrderId(orderId);
         orderRefundReq.setRefundMoney(refundFee);
@@ -2305,14 +2375,32 @@ public class CmsOrderDonServiceImpl extends ServiceImpl<OrderDonMapper,OrderDon>
 
         String outNo = orderRefundService.centerRefund(orderRefundReq, orderDon);
 
-        orderDon.setRefundBalance(orderDon.getRefundBalance().subtract(refundFee));
-        BigDecimal orderRefundMoney = Optional.ofNullable(orderDon.getRefundMoney()).orElse(BigDecimal.ZERO);
-        orderDon.setRefundMoney(orderRefundMoney.add(refundFee));
-        orderDonMapper.updateById(orderDon);
-
-        refundOrderDon.setOutRefundNo(outNo);
-        refundOrderDon.setIsClearBalance(true);
-        refundOrderDonMapper.updateById(refundOrderDon);
+        if (mixedRefund) {
+            // bxj 原记录的 outRefundNo 属于首次现金退款,不能覆盖;单独记录本次转换流水。
+            RefundOrderDon conversionRecord = new RefundOrderDon();
+            conversionRecord.setUserId(refundOrderDon.getUserId());
+            conversionRecord.setOrderId(orderId);
+            conversionRecord.setGoodsType(refundOrderDon.getGoodsType());
+            conversionRecord.setOutRefundNo(outNo);
+            conversionRecord.setGoodsId(refundOrderDon.getGoodsId());
+            conversionRecord.setSkuId(refundOrderDon.getSkuId());
+            conversionRecord.setGoodsTitle(refundOrderDon.getGoodsTitle());
+            conversionRecord.setSpecVal(refundOrderDon.getSpecVal());
+            conversionRecord.setTotalFee(refundOrderDon.getTotalFee());
+            conversionRecord.setRefundFee(refundFee);
+            conversionRecord.setRefundBalance(BigDecimal.ZERO);
+            conversionRecord.setRefundDesc("混合退款余额转现金");
+            // 现金退款流水记录实际原支付渠道,与普通现金退款记录保持一致。
+            conversionRecord.setRefundMethod(paymentType.name());
+            conversionRecord.setIsClearBalance(true);
+            conversionRecord.setOperator(refundOrderDon.getOperator());
+            conversionRecord.setChatImg(refundOrderDon.getChatImg());
+            refundOrderDonMapper.insert(conversionRecord);
+        } else {
+            refundOrderDonMapper.update(null, Wrappers.lambdaUpdate(RefundOrderDon.class)
+                    .set(RefundOrderDon::getOutRefundNo, outNo)
+                    .eq(RefundOrderDon::getId, refundOrderId));
+        }
     }
 
     /**

+ 1 - 1
netflix-web/src/main/java/com/cyksj/web/controller/manage/CmsOrderController.java

@@ -1100,7 +1100,7 @@ public class CmsOrderController {
 	}
 
 	/**
-	 * 将余额清零 退款用户支付金额
+	 * 将 balance/bxj 退款记录中的余额扣回并转退至原支付渠道
 	 */
 	@PutMapping("/clear/balance/refundMoney")
 	@NoSubmit