Sfoglia il codice sorgente

fix(payment): 优化退款逻辑与代码格式

- 修复closeOrder方法中的空判断和格式问题
- 优化威富通支付通知回调及查询相关注释格式
- 统一HttpClient请求及响应处理代码风格
- 完善退款操作流程,支持部分退款与全额退款状态更新
- 添加账户余额及订单退款金额校验,避免退款异常
- 优化退款查询接口参数校验和结果解析
- 调整日志输出格式,提升调试友好性
- 修正局部变量命名和代码缩进,提高可读性
- 增加退款记录详细信息的提取与日志记录
wzq 18 ore fa
parent
commit
1acc105732

+ 65 - 58
src/main/java/com/zsElectric/boot/business/service/WFTOrderService.java

@@ -255,12 +255,12 @@ public class WFTOrderService {
 
     public Boolean closeOrder(String orderNo) {
         UserOrderInfo userOrderInfo = userOrderInfoMapper.selectOne(Wrappers.lambdaQuery(UserOrderInfo.class).eq(UserOrderInfo::getOrderNo, orderNo).last("limit 1"));
-        if(ObjectUtil.isNull(userOrderInfo)){
+        if (ObjectUtil.isNull(userOrderInfo)) {
             throw new BusinessException("订单不存在");
         }
         userOrderInfo.setOrderStatus(SystemConstants.STATUS_THREE);
         int i = userOrderInfoMapper.updateById(userOrderInfo);
-        if(i > 0){
+        if (i > 0) {
             return Boolean.TRUE;
         }
         return Boolean.FALSE;
@@ -268,7 +268,7 @@ public class WFTOrderService {
 
     /**
      * 处理威富通支付通知回调
-     *
+     * <p>
      * 重要说明:
      * 1. 接收威富通POST的XML数据流
      * 2. 验证签名(支持RSA_1_256、RSA_1_1、MD5)
@@ -277,7 +277,7 @@ public class WFTOrderService {
      * 5. 使用数据库锁避免并发问题
      * 6. 更新订单状态
      * 7. 必须返回纯字符串"success"或"fail"
-     *
+     * <p>
      * 通知重试机制:0/15/15/30/180/1800/1800/1800/1800/3600秒
      *
      * @param request HTTP请求对象
@@ -518,12 +518,12 @@ public class WFTOrderService {
 
     /**
      * 订单是否支付成功查询
-     *
+     * <p>
      * 应用场景:
      * 1. 用户支付后前端轮询查询订单状态
      * 2. 后台定时任务查询未支付订单
      * 3. 支付通知未收到时的补偿查询
-     *
+     * <p>
      * 查询策略建议:
      * - 方案一:以订单创建时间为基准,间隔5秒/30秒/1分钟/3分钟/5分钟/10分钟/30分钟查询
      * - 方案二:定时任务每30秒查询最近10分钟内未支付订单,最多查询10次
@@ -575,8 +575,8 @@ public class WFTOrderService {
             // 6. 解析查询结果
             String tradeState = queryResult.get("trade_state");
             String payResult = queryResult.get("pay_result");
-            
-            log.info("订单查询结果: orderNo={}, trade_state={}, pay_result={}", 
+
+            log.info("订单查询结果: orderNo={}, trade_state={}, pay_result={}",
                     orderNo, tradeState, payResult);
 
             // 7. 判断支付状态
@@ -636,14 +636,14 @@ public class WFTOrderService {
 
             // 发送HTTP请求
             String reqUrl = swiftpassConfig.getReq_url();
-            org.apache.http.client.methods.HttpPost httpPost = 
+            org.apache.http.client.methods.HttpPost httpPost =
                     new org.apache.http.client.methods.HttpPost(reqUrl);
-            org.apache.http.entity.StringEntity entityParams = 
+            org.apache.http.entity.StringEntity entityParams =
                     new org.apache.http.entity.StringEntity(XmlUtils.parseXML(queryParams), "utf-8");
             httpPost.setEntity(entityParams);
             httpPost.setHeader("Content-Type", "text/xml;utf-8");
 
-            org.apache.http.impl.client.CloseableHttpClient client = 
+            org.apache.http.impl.client.CloseableHttpClient client =
                     org.apache.http.impl.client.HttpClients.createDefault();
             org.apache.http.client.methods.CloseableHttpResponse response = client.execute(httpPost);
 
@@ -651,15 +651,15 @@ public class WFTOrderService {
                 if (response != null && response.getEntity() != null) {
                     // 解析响应
                     Map<String, String> resultMap = XmlUtils.toMap(
-                            org.apache.http.util.EntityUtils.toByteArray(response.getEntity()), 
+                            org.apache.http.util.EntityUtils.toByteArray(response.getEntity()),
                             "utf-8");
-                    
+
                     log.info("查询响应结果: {}", resultMap);
 
                     // 验证签名
                     String reSign = resultMap.get("sign");
                     String reSignType = resultMap.get("sign_type");
-                    
+
                     if (resultMap.containsKey("sign")) {
                         boolean signValid = SignUtil.verifySign(reSign, reSignType, resultMap, swiftpassConfig);
                         if (!signValid) {
@@ -671,8 +671,8 @@ public class WFTOrderService {
                     }
 
                     // 检查通信状态和业务结果
-                    if ("0".equals(resultMap.get("status")) && 
-                        "0".equals(resultMap.get("result_code"))) {
+                    if ("0".equals(resultMap.get("status")) &&
+                            "0".equals(resultMap.get("result_code"))) {
                         resultMap.put("status", "200");
                         return resultMap;
                     } else {
@@ -707,7 +707,7 @@ public class WFTOrderService {
      * 同步订单支付状态
      * 当查询到订单已支付但本地状态未更新时,同步更新本地订单状态
      *
-     * @param orderInfo 订单信息
+     * @param orderInfo   订单信息
      * @param queryResult 查询结果
      */
     @Transactional(rollbackFor = Exception.class)
@@ -744,13 +744,13 @@ public class WFTOrderService {
 
                     int updateCount = userOrderInfoMapper.updateById(orderInfo);
                     if (updateCount > 0) {
-                        log.info("订单状态同步成功: orderNo={}, transactionId={}", 
+                        log.info("订单状态同步成功: orderNo={}, transactionId={}",
                                 orderInfo.getOrderNo(), transactionId);
                     } else {
                         log.error("订单状态同步失败: orderNo={}", orderInfo.getOrderNo());
                     }
                 } else {
-                    log.error("订单金额不匹配,不同步状态: expected={}, actual={}", 
+                    log.error("订单金额不匹配,不同步状态: expected={}, actual={}",
                             expectedFee, totalFeeStr);
                 }
             }
@@ -766,21 +766,22 @@ public class WFTOrderService {
      * @return
      */
     @Transactional(rollbackFor = Exception.class)
-    public String refundOrder(Long userId,Integer type) throws Exception {
+    public String refundOrder(Long userId, Integer type) throws Exception {
 
         //查询账户余额
         UserAccount userAccount =
                 userAccountService.getOne(Wrappers.<UserAccount>lambdaQuery().eq(UserAccount::getUserId, userId).last("limit 1"));
         if (userAccount.getBalance().compareTo(BigDecimal.ZERO) == 0) {
-            return  "账户余额为 0";
+            return "账户余额为 0";
         }
         BigDecimal refundMoney = userAccount.getBalance();
 
-        //查询一年内已支付的所有券订单
+        //查询一年内已支付且可退款的所有券订单(已支付和部分退款状态)
         List<UserOrderInfo> userOrderInfoList = userOrderInfoMapper.selectList(Wrappers.<UserOrderInfo>lambdaQuery()
                 .eq(UserOrderInfo::getUserId, userId)
-                .in(UserOrderInfo::getOrderStatus, SystemConstants.STATUS_TWO,SystemConstants.STATUS_FIVE)
+                .in(UserOrderInfo::getOrderStatus, SystemConstants.STATUS_TWO, SystemConstants.STATUS_FIVE)
                 .between(UserOrderInfo::getCreateTime, LocalDateTime.now().minusYears(1), LocalDateTime.now())
+                .orderByAsc(UserOrderInfo::getCreateTime) // 按创建时间升序,优先退早期订单
         );
 
         if (CollUtil.isEmpty(userOrderInfoList)) {
@@ -788,17 +789,22 @@ public class WFTOrderService {
             throw new BusinessException("无法进行退款操作,请联系客服处理!");
         }
         for (UserOrderInfo userOrderInfo : userOrderInfoList) {
-            if(refundMoney.compareTo(BigDecimal.ZERO) == 0){
+            if (refundMoney.compareTo(BigDecimal.ZERO) == 0) {
                 break;
             }
             // 处理退款金额为null的情况,默认为0
             BigDecimal alreadyRefundMoney = userOrderInfo.getRefundMoney() == null ? BigDecimal.ZERO : userOrderInfo.getRefundMoney();
             BigDecimal canRefundMoney = userOrderInfo.getOrderMoney().subtract(alreadyRefundMoney);
-            
+
             if (canRefundMoney.compareTo(refundMoney) >= 0) {
-                //退款金额大于订单金额,则直接退退款金额
-                refundOrder(userOrderInfo,refundMoney, "账户退款", SystemConstants.STATUS_ONE);
-                //账户变动及日志记录(退款清空账户余额)
+                //可退款金额大于等于待退款金额,则直接退全部待退款金额
+                refundOrder(userOrderInfo, refundMoney, "账户退款", type);
+
+                //计算本次退款后的累计退款金额
+                BigDecimal totalRefundMoney = alreadyRefundMoney.add(refundMoney);
+                boolean isFullRefund = totalRefundMoney.compareTo(userOrderInfo.getOrderMoney()) >= 0;
+
+                //账户变动及日志记录
                 userAccountService.updateAccountBalanceAndLog(
                         userId,
                         refundMoney,
@@ -806,9 +812,9 @@ public class WFTOrderService {
                         SystemConstants.ACCOUNT_LOG_REFUND_NOTE,
                         userOrderInfo.getId()
                 );
+
                 //修改订单状态(判断是全额退款还是部分退款)
-                BigDecimal totalRefundMoney = alreadyRefundMoney.add(refundMoney);
-                if (totalRefundMoney.compareTo(userOrderInfo.getOrderMoney()) >= 0) {
+                if (isFullRefund) {
                     // 退款金额等于或大于订单金额,全额退款
                     userOrderInfo.setOrderStatus(SystemConstants.STATUS_FOUR);
                 } else {
@@ -851,7 +857,7 @@ public class WFTOrderService {
         log.info("执行操作的 原支付交易对应的商户订单号:{}", userOrderInfo.getOrderNo());
 
         //退款单号
-        String out_refund_no = createOrderNo("TK",userOrderInfo.getId());
+        String out_refund_no = createOrderNo("TK", userOrderInfo.getId());
         // 创建退款订单
         UserRefundsOrderInfo userRefundsOrderInfo = new UserRefundsOrderInfo();
         userRefundsOrderInfo.setOrderId(userOrderInfo.getId());
@@ -862,7 +868,7 @@ public class WFTOrderService {
         userRefundsOrderInfo.setType(type);
         userRefundsOrderInfo.setCreateTime(LocalDateTime.now());
 
-        SortedMap<String,String> params = new TreeMap<>();
+        SortedMap<String, String> params = new TreeMap<>();
 
         params.put("out_trade_no", userOrderInfo.getOrderNo());//商户订单号
         params.put("out_refund_no", out_refund_no);//商户退款单号
@@ -875,16 +881,16 @@ public class WFTOrderService {
         Map<String, String> refund = payUtill.refund(params, swiftpassConfig);
 
         log.info("最终拿到的微信支付通知数据:" + refund);
-        if (Objects.equals(refund.get("status"), "0")){
+        if (Objects.equals(refund.get("status"), "0")) {
 
             log.info("退款调用成功!");
-            if(Objects.equals(refund.get("result_code"), "0")){
+            if (Objects.equals(refund.get("result_code"), "0")) {
                 log.info("退款成功!");
                 log.info("订单:{},退款成功!原因:{}", userOrderInfo.getOrderNo(), reason);
                 //计算累计退款金额
                 BigDecimal currentRefundMoney = userOrderInfo.getRefundMoney() == null ? BigDecimal.ZERO : userOrderInfo.getRefundMoney();
                 BigDecimal totalRefundMoney = currentRefundMoney.add(refundAmount);
-                
+
                 //修改订单状态(判断是全额退款还是部分退款)
                 if (totalRefundMoney.compareTo(userOrderInfo.getOrderMoney()) >= 0) {
                     // 退款金额等于或大于订单金额,全额退款
@@ -896,7 +902,7 @@ public class WFTOrderService {
                 userOrderInfo.setRefundMoney(totalRefundMoney);
                 userOrderInfo.setRefundTime(LocalDateTime.now());
                 userOrderInfoMapper.updateById(userOrderInfo);
-            } else{
+            } else {
                 log.info("退款处理中");
                 //修改订单状态
                 BigDecimal currentRefundMoney = userOrderInfo.getRefundMoney() == null ? BigDecimal.ZERO : userOrderInfo.getRefundMoney();
@@ -912,31 +918,32 @@ public class WFTOrderService {
         }
 
     }
+
     /**
      * 退款查询
-     *
+     * <p>
      * 应用场景:
      * 1. 提交退款申请后,通过调用该接口查询退款状态
      * 2. 退款有一定延时,请在3个工作日后重新查询退款状态
-     *
+     * <p>
      * 参数优先级:
      * refund_id > out_refund_no > transaction_id > out_trade_no
-     *
+     * <p>
      * 退款状态:
      * - SUCCESS:退款成功
      * - FAIL:退款失败
      * - PROCESSING:退款处理中
      * - CHANGE:转入代发,退款到银行发现用户的卡作废或者冻结了,
-     *           导致原路退款银行卡失败,资金回流到商户的现金帐号,
-     *           需要商户人工干预,通过线下或者平台转账的方式进行退款
+     * 导致原路退款银行卡失败,资金回流到商户的现金帐号,
+     * 需要商户人工干预,通过线下或者平台转账的方式进行退款
      *
-     * @param outTradeNo 商户订单号(四选一)
+     * @param outTradeNo    商户订单号(四选一)
      * @param transactionId 平台订单号(四选一)
-     * @param outRefundNo 商户退款单号(四选一)
-     * @param refundId 平台退款单号(四选一,优先级最高)
+     * @param outRefundNo   商户退款单号(四选一)
+     * @param refundId      平台退款单号(四选一,优先级最高)
      * @return 退款查询结果
      */
-    public Map<String, Object> queryRefund(String outTradeNo, String transactionId, 
+    public Map<String, Object> queryRefund(String outTradeNo, String transactionId,
                                            String outRefundNo, String refundId) {
         log.info("========== 开始查询退款状态 ==========");
         log.info("查询参数: outTradeNo={}, transactionId={}, outRefundNo={}, refundId={}",
@@ -944,8 +951,8 @@ public class WFTOrderService {
 
         try {
             // 1. 参数校验 - 四个参数至少需要一个
-            if (outTradeNo == null && transactionId == null && 
-                outRefundNo == null && refundId == null) {
+            if (outTradeNo == null && transactionId == null &&
+                    outRefundNo == null && refundId == null) {
                 log.error("退款查询参数不能全为空");
                 Map<String, Object> errorResult = new HashMap<>();
                 errorResult.put("status", "500");
@@ -955,7 +962,7 @@ public class WFTOrderService {
 
             // 2. 构建查询请求参数
             SortedMap<String, String> queryParams = new TreeMap<>();
-            
+
             // 按优先级添加参数:refund_id > out_refund_no > transaction_id > out_trade_no
             if (refundId != null && !refundId.trim().isEmpty()) {
                 queryParams.put("refund_id", refundId);
@@ -966,7 +973,7 @@ public class WFTOrderService {
             } else if (outTradeNo != null && !outTradeNo.trim().isEmpty()) {
                 queryParams.put("out_trade_no", outTradeNo);
             }
-            
+
             queryParams.put("sign_type", "RSA_1_256"); // 签名方式
 
             // 3. 调用威富通退款查询API
@@ -979,7 +986,7 @@ public class WFTOrderService {
             if ("200".equals(queryResult.get("status"))) {
                 @SuppressWarnings("unchecked")
                 Map<String, String> dataMap = (Map<String, String>) queryResult.get("data");
-                
+
                 if (dataMap == null) {
                     log.error("退款查询结果数据为空");
                     queryResult.put("status", "500");
@@ -990,7 +997,7 @@ public class WFTOrderService {
                 // 5. 检查业务结果
                 String status = dataMap.get("status");
                 String resultCode = dataMap.get("result_code");
-                
+
                 if (!"0".equals(status) || !"0".equals(resultCode)) {
                     log.error("退款查询失败: status={}, result_code={}, err_code={}, err_msg={}",
                             status, resultCode, dataMap.get("err_code"), dataMap.get("err_msg"));
@@ -1016,11 +1023,11 @@ public class WFTOrderService {
                 // 8. 解析退款记录列表(支持多笔退款)
                 if (refundCount > 0) {
                     java.util.List<Map<String, Object>> refundList = new java.util.ArrayList<>();
-                    
+
                     for (int i = 0; i < refundCount; i++) {
                         Map<String, Object> refundRecord = new HashMap<>();
                         String suffix = "_" + i;
-                        
+
                         // 提取每笔退款的详细信息
                         refundRecord.put("out_refund_id", dataMap.get("out_refund_id" + suffix));
                         refundRecord.put("out_refund_no", dataMap.get("out_refund_no" + suffix));
@@ -1031,15 +1038,15 @@ public class WFTOrderService {
                         refundRecord.put("refund_time", dataMap.get("refund_time" + suffix));
                         refundRecord.put("refund_status", dataMap.get("refund_status" + suffix));
                         refundRecord.put("refund_status_info", dataMap.get("refund_status_info" + suffix));
-                        
+
                         refundList.add(refundRecord);
-                        
+
                         log.info("退款记录[{}]: refund_id={}, refund_status={}, refund_fee={}",
-                                i, refundRecord.get("refund_id"), 
-                                refundRecord.get("refund_status"), 
+                                i, refundRecord.get("refund_id"),
+                                refundRecord.get("refund_status"),
                                 refundRecord.get("refund_fee"));
                     }
-                    
+
                     result.put("refund_list", refundList);
                 }