Răsfoiți Sursa

fix(payment): 优化订单支付状态查询及幂等性处理

- 修改UserOrderInfoServiceImpl中orderQuery方法,增加事务和数据库行锁保证并发安全
- 添加幂等性检查,避免支付回调和查询逻辑重复处理订单支付状态
- WFTOrderService中支付成功流程增加数据库行锁和幂等性检查,防止并发更新冲突
- 调整订单按创建时间排序策略,优先处理早期订单退款
- 修改Dockerfile中维护者信息和应用包名更新
- 日志中增加退款接口金额记录,提高操作透明度
wzq 2 săptămâni în urmă
părinte
comite
a527f90995

+ 2 - 2
Dockerfile

@@ -2,7 +2,7 @@
 FROM openjdk:17
 
 # 维护者信息
-MAINTAINER youlai <youlaitech@163.com>
+MAINTAINER zswl <youlaitech@163.com>
 
 # 设置时区(Debian直接使用环境变量)
 ENV TZ=Asia/Shanghai
@@ -11,7 +11,7 @@ ENV TZ=Asia/Shanghai
 VOLUME /tmp
 
 # 添加应用
-ADD target/youlai-boot.jar app.jar
+ADD target/zsElectric-boot.jar app.jar
 
 # 启动命令
 CMD java \

+ 19 - 5
src/main/java/com/zsElectric/boot/business/service/WFTOrderService.java

@@ -589,8 +589,21 @@ public class WFTOrderService {
             // pay_result: 0-成功
             boolean isPaid = "SUCCESS".equals(tradeState) || "0".equals(payResult);
 
-            // 8. 如果支付成功但本地订单状态未更新,同步更新订单状态
-            if (isPaid && !SystemConstants.STATUS_TWO.equals(orderInfo.getOrderStatus())) {
+            // 8. 如果支付成功,重新查询订单状态进行幂等性检查,避免与支付回调并发导致重复处理
+            if (isPaid) {
+                // 重新查询订单状态,使用数据库行锁保证并发安全
+                UserOrderInfo latestOrderInfo = userOrderInfoMapper.selectOne(
+                        Wrappers.lambdaQuery(UserOrderInfo.class)
+                                .eq(UserOrderInfo::getOrderNo, orderNo)
+                                .last("FOR UPDATE")
+                );
+                
+                // 幂等性检查:如果订单已经被支付回调处理,直接返回成功
+                if (SystemConstants.STATUS_TWO.equals(latestOrderInfo.getOrderStatus())) {
+                    log.info("订单已被支付回调处理,跳过重复处理: orderNo={}", orderNo);
+                    return true;
+                }
+                
                 log.info("同步更新订单支付状态: orderNo={}", orderNo);
                 String transactionId = queryResult.get("transaction_id");    // 平台订单号
                 String totalFeeStr = queryResult.get("total_fee");           // 总金额(分)
@@ -599,7 +612,7 @@ public class WFTOrderService {
                 //转localDateTime
                 LocalDateTime payTime = LocalDateTime.parse(timeEnd, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
                 //支付成功业务处理
-                successPayOrder(orderInfo, transactionId, payTime, payMoney);
+                successPayOrder(latestOrderInfo, transactionId, payTime, payMoney);
             }
             return isPaid;
 
@@ -776,6 +789,7 @@ public class WFTOrderService {
         //查询账户余额
         UserAccount userAccount =
                 userAccountService.getOne(Wrappers.<UserAccount>lambdaQuery().eq(UserAccount::getUserId, userId).last("limit 1"));
+        log.info("用户:{},申请退款:{}", userId, userAccount.getBalance());
         if (userAccount.getBalance().compareTo(BigDecimal.ZERO) == 0) {
             return "账户余额为 0";
         }
@@ -786,7 +800,7 @@ public class WFTOrderService {
                 .eq(UserOrderInfo::getUserId, userId)
                 .in(UserOrderInfo::getOrderStatus, SystemConstants.STATUS_TWO, SystemConstants.STATUS_FIVE)
                 .between(UserOrderInfo::getCreateTime, LocalDateTime.now().minusYears(1), LocalDateTime.now())
-                .orderByDesc(UserOrderInfo::getCreateTime) // 按创建时间升序,优先退早期订单
+                .orderByAsc(UserOrderInfo::getCreateTime) // 按创建时间升序,优先退早期订单
         );
 
         if (CollUtil.isEmpty(userOrderInfoList)) {
@@ -869,7 +883,7 @@ public class WFTOrderService {
     }
 
     public Long refundOrder(UserOrderInfo userOrderInfo, BigDecimal refundAmount, String reason, Integer type) throws Exception {
-        log.info("进入退款接口------>");
+        log.info("进入退款接口------>退款金额:{}",refundAmount);
         log.info("执行操作的 原支付交易对应的商户订单号:{}", userOrderInfo.getOrderNo());
 
         //退款单号

+ 20 - 2
src/main/java/com/zsElectric/boot/business/service/impl/UserOrderInfoServiceImpl.java

@@ -224,18 +224,36 @@ public class UserOrderInfoServiceImpl extends ServiceImpl<UserOrderInfoMapper, U
      * @return
      */
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public String orderQuery(String orderNo) throws Exception {
         //查询订单
         UserOrderInfo orderInfo = this.getById(Wrappers.<UserOrderInfo>lambdaQuery().eq(UserOrderInfo::getOrderNo, orderNo).last("limit 1"));
         if (ObjectUtil.isEmpty(orderInfo)) {
             throw new RuntimeException("当前订单不存在");
         }
+        
+        // 如果订单已支付,直接返回成功
+        if (Objects.equals(orderInfo.getOrderStatus(), SystemConstants.STATUS_TWO)) {
+            return "100001";//支付成功
+        }
+        
         //null代表查询失败 SUCCESS-成功 USERPAYING和ACCEPT为中间态 其他为支付失败
         JsonObject res = orderQueryByOutTradeNo(orderNo, WechatConstants.WECHAT_MCH_ID);
         String s = res == null ? null : res.get("trade_state").getAsString();
 //        String s = "SUCCESS";
         if ("SUCCESS".equals(s)) {
-            if (ObjectUtil.isNotEmpty(orderInfo) && Objects.equals(orderInfo.getOrderStatus(), SystemConstants.STATUS_ONE)) {
+            // 重新查询订单状态,使用数据库行锁保证并发安全,避免与支付回调重复处理
+            UserOrderInfo latestOrderInfo = this.getOne(Wrappers.<UserOrderInfo>lambdaQuery()
+                    .eq(UserOrderInfo::getOrderNo, orderNo)
+                    .last("FOR UPDATE"));
+            
+            // 幂等性检查:如果订单已经被支付回调处理,直接返回成功
+            if (Objects.equals(latestOrderInfo.getOrderStatus(), SystemConstants.STATUS_TWO)) {
+                log.info("订单已被支付回调处理,跳过重复处理: orderNo={}", orderNo);
+                return "100001";//支付成功
+            }
+            
+            if (Objects.equals(latestOrderInfo.getOrderStatus(), SystemConstants.STATUS_ONE)) {
                 String transactionId = res.get("transaction_id").getAsString();
                 LocalDateTime payTime = LocalDateTime.now();
                 // 微信返回的金额单位是分,需要转换为元
@@ -243,7 +261,7 @@ public class UserOrderInfoServiceImpl extends ServiceImpl<UserOrderInfoMapper, U
                         .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
 
                 //执行业务操作
-                this.successPayOrder(orderInfo, transactionId, payTime, payMoney);
+                this.successPayOrder(latestOrderInfo, transactionId, payTime, payMoney);
             }
             return "100001";//支付成功
         }