zoujiajian 2 周之前
父節點
當前提交
6727b5cb6f

+ 41 - 3
netflix-service/src/main/java/com/cyksj/service/mange/impl/CmsOrderDonServiceImpl.java

@@ -1612,6 +1612,7 @@ public class CmsOrderDonServiceImpl extends ServiceImpl<OrderDonMapper,OrderDon>
         if (orderDon == null) {
             throw BusinessRuntimeException.getInstance("订单不存在");
         }
+        validateOrdinaryRefundRequest(orderDon, refundReq);
         BigDecimal orderDonRefundMoney = Optional.ofNullable(orderDon.getRefundMoney()).orElse(BigDecimal.ZERO);
         BigDecimal orderDonRefundBalance = Optional.ofNullable(orderDon.getRefundBalance()).orElse(BigDecimal.ZERO);
         String refundType = refundReq.getRefundType();
@@ -1741,6 +1742,40 @@ public class CmsOrderDonServiceImpl extends ServiceImpl<OrderDonMapper,OrderDon>
 //        checkMirrorRenewSku(orderDon);
     }
 
+    /**
+     * 普通订单退款仍由管理端选择退款方式,但金额字段必须与退款方式一致。
+     * 否则错误地把纯余额订单按 money 提交时,会清票并关闭订单,却不会退回余额。
+     */
+    private void validateOrdinaryRefundRequest(OrderDon orderDon, OrderRefundReq refundReq) {
+        String refundType = refundReq.getRefundType();
+        boolean supportedType = OrderRefundReq.RefundType.money.name().equals(refundType)
+                || OrderRefundReq.RefundType.balance.name().equals(refundType)
+                || OrderRefundReq.RefundType.bxj.name().equals(refundType);
+        if (!supportedType) {
+            throw BusinessRuntimeException.getInstance("退款类型不正确");
+        }
+
+        BigDecimal refundMoney = Optional.ofNullable(refundReq.getRefundMoney()).orElse(BigDecimal.ZERO);
+        BigDecimal refundBalance = Optional.ofNullable(refundReq.getRefundBalance()).orElse(BigDecimal.ZERO);
+        if (refundMoney.signum() < 0 || refundBalance.signum() < 0) {
+            throw BusinessRuntimeException.getInstance("退款金额不能小于0");
+        }
+        // Existing user-side expired-ticket refunds use money + refundBalance with
+        // isOnly=true to return the balance-funded portion while refunding cash.
+        // Reject only the malformed management request that carries a balance
+        // amount without opting into that established mixed-refund path.
+        if (OrderRefundReq.RefundType.money.name().equals(refundType)
+                && !Boolean.TRUE.equals(refundReq.getIsOnly())
+                && (refundBalance.signum() > 0 || orderDon.getType() == OrderDon.Type.BALANCE)) {
+            throw BusinessRuntimeException.getInstance("退款类型与余额退款金额不匹配");
+        }
+        if (OrderRefundReq.RefundType.balance.name().equals(refundType)
+                && !Boolean.TRUE.equals(refundReq.getIsOnly())
+                && refundMoney.signum() <= 0 && refundBalance.signum() > 0) {
+            throw BusinessRuntimeException.getInstance("退至余额的金额字段不正确");
+        }
+    }
+
     /**
      * 退款后 处理关联
      */
@@ -1832,9 +1867,12 @@ public class CmsOrderDonServiceImpl extends ServiceImpl<OrderDonMapper,OrderDon>
             log.error("wx减少抽奖机会错误,orderId:{},error:{}", orderDon.getId(), e);
         }
         if (OrderRefundReq.RefundType.balance.name().equals(refundType)) {
-            if (isOnly == null) {
-                BigDecimal balanceAmount = Optional.ofNullable(refundBalance)
-                        .orElse(Optional.ofNullable(refundMoney).orElse(BigDecimal.ZERO));
+            if (!Boolean.TRUE.equals(isOnly)) {
+                // 普通订单选择“退还至余额”时,退款数额放在 refundMoney;
+                // 多件整单退款的余额部分由服务端固定写入 refundBalance。
+                BigDecimal balanceAmount = multiQuantityOrder
+                        ? Optional.ofNullable(refundBalance).orElse(BigDecimal.ZERO)
+                        : Optional.ofNullable(refundMoney).orElse(BigDecimal.ZERO);
                 if (balanceAmount.compareTo(BigDecimal.ZERO) > 0) {
                     userBenefitsService.addUserBalance(orderDon.getUserId(), balanceAmount,
                             UserBalanceSourceRecord.Source.refund, orderDon.getYhsId(),

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

@@ -278,13 +278,14 @@ public class CmsOrderController {
 					&& Objects.equals(ticketCount.getTicketTotalNum(), data.getNum());
 			int successNum = ticketCount == null || ticketCount.getTicketSuccessNum() == null
 					? 0 : ticketCount.getTicketSuccessNum();
-			int undeliveredNum = multiQuantityOrder ? Math.max(0, data.getNum() - successNum) : 0;
 			boolean terminalTicketState = ticketCount != null
 					&& (positive(ticketCount.getTicketRefundingNum())
 					|| positive(ticketCount.getTicketRefundedNum())
 					|| positive(ticketCount.getTicketClearedNum()));
 			boolean paidOrder = data.getStatus() == OrderDon.Status.hasPayment
 					|| data.getStatus() == OrderDon.Status.complete;
+			int undeliveredNum = multiQuantityOrder && paidOrder && !terminalTicketState
+					? Math.max(0, data.getNum() - successNum) : 0;
 			boolean hasRedeliverableSlot = ticketCount != null
 					&& positive(ticketCount.getTicketRedeliverableNum());
 			data.setMultiQuantityOrder(multiQuantityOrder);