Browse Source

延迟队列

bihs 3 weeks ago
parent
commit
52c6faafdf

+ 4 - 3
qmjszx-admin/src/main/resources/application-druid.yml

@@ -60,6 +60,7 @@ spring:
                     config:
                         multi-statement-allow: true
     redis:
-        host: 127.0.0.1
-        port: 16379
-        password: sooka123456
+      host: 127.0.0.1
+      port: 16379
+      password: sooka123456
+      database: 5

+ 2 - 2
qmjszx-admin/src/main/resources/application.yml

@@ -187,9 +187,9 @@ wx:
     publicKeyId: PUB_KEY_ID_0117303822532025111100191793000600
     appSecret: 228a6905484fbf9c3e571962c92c7e7e
     # 异步通知地址(必需)
-    notifyUrl: http://k99464d6.natappfree.cc:80/app/pay/notify
+    notifyUrl: http://k4ce3b94.natappfree.cc/app/pay/notify
     # 退款异步通知地址
-    refundNotifyUrl: http://k99464d6.natappfree.cc/app/notify/refund
+    refundNotifyUrl: http://k4ce3b94.natappfree.cc/app/notify/refund
 # 海康获取直播视频流配置
 hik:
   appKey: "28356728"

+ 0 - 3
qmjszx-business/src/main/resources/mapper/order/StoreRefundMapper.xml

@@ -33,10 +33,7 @@
                refund_status,
                wx_refund_id,
                success_time,
-               create_by,
                create_time,
-               update_by,
-               update_time,
                remark
         from store_refund
     </sql>

+ 1 - 1
qmjszx-framework/src/main/java/beilv/framework/config/ShiroConfig.java

@@ -297,7 +297,7 @@ public class ShiroConfig {
         // 微信支付成功回调接口
         filterChainDefinitionMap.put("/app/pay/notify", "anon,captchaValidate");
         //微信支付退款回调接口
-        filterChainDefinitionMap.put("/app/pay/refund", "anon,captchaValidate");
+        filterChainDefinitionMap.put("/app/notify/refund", "anon,captchaValidate");
         filterChainDefinitionMap.put("/profile/upload/**", "anon,captchaValidate");
         // 微信小程序直播请求接口
         filterChainDefinitionMap.put("/system/hik/getCameraPreviewURL", "anon,captchaValidate");

+ 72 - 2
qmjszx-pay/src/main/java/beilv/wx/pay/controller/WxPayController.java

@@ -2,6 +2,8 @@ package beilv.wx.pay.controller;
 
 import beilv.common.core.controller.BaseController;
 import beilv.common.core.domain.AjaxResult;
+import beilv.order.domain.StoreOrder;
+import beilv.order.mapper.StoreOrderMapper;
 import beilv.wx.pay.domain.vo.AppPayParam;
 import beilv.wx.pay.domain.vo.AppRefundParam;
 import beilv.wx.pay.service.IWxPayService;
@@ -9,15 +11,21 @@ import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result;
 import com.github.binarywang.wxpay.exception.WxPayException;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
 import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
 
+@Slf4j
 @Api("微信支付V3部分接口示例")
 @RestController
 @RequestMapping("/")
@@ -25,6 +33,9 @@ public class WxPayController extends BaseController {
 
     @Autowired
     private IWxPayService wxPayService;
+    
+    @Autowired
+    private StoreOrderMapper storeOrderMapper;
 
     /**
      * 调用统一下单接口(JSAPI)
@@ -33,7 +44,13 @@ public class WxPayController extends BaseController {
     @ApiOperation(value = "统一下单,并组装所需支付参数")
     @PostMapping("app-api/pay/unifiedOrder")
     public AjaxResult unifiedOrder(@RequestBody AppPayParam param) throws Exception {
-        return AjaxResult.success(wxPayService.unifiedOrder(param));
+        log.info("收到统一下单请求,订单号: {}", param.getOrderId());
+        try {
+            return AjaxResult.success(wxPayService.unifiedOrder(param));
+        } catch (Exception e) {
+            log.error("统一下单处理失败,订单号: {}", param.getOrderId(), e);
+            throw e;
+        }
     }
 
 
@@ -55,6 +72,18 @@ public class WxPayController extends BaseController {
     }
 
     /**
+     * 取消订单
+     *
+     * @param orderId 订单ID
+     * @return 处理结果
+     */
+    @ApiOperation(value = "取消订单")
+    @GetMapping("app-api/pay/cancel")
+    public AjaxResult cancelOrder(@RequestBody String orderId) {
+        return toAjax(wxPayService.cancelOrder(orderId));
+    }
+
+    /**
      * <pre>
      * 微信支付-申请退款
      * 详见 https://pay.weixin.qq.com/doc/v3/merchant/4012791862
@@ -65,7 +94,15 @@ public class WxPayController extends BaseController {
     @ApiOperation(value = "退款")
     @PostMapping("app-api/pay/refund")
     public WxPayRefundV3Result refund(@RequestBody AppRefundParam param) throws WxPayException {
-        return wxPayService.refund(param);
+        log.info("收到退款请求,订单号: {}", param.getOrderId());
+        try {
+            WxPayRefundV3Result result = wxPayService.refund(param);
+            log.info("退款处理完成,订单号: {}", param.getOrderId());
+            return result;
+        } catch (WxPayException e) {
+            log.error("退款处理失败,订单号: {}", param.getOrderId(), e);
+            throw e;
+        }
     }
 
     /**
@@ -84,5 +121,38 @@ public class WxPayController extends BaseController {
     public AjaxResult parseRefundNotifyResult(@RequestBody String notifyData, HttpServletRequest request) {
         return wxPayService.parseRefundNotify(notifyData, request);
     }
+    
+    /**
+     * 查询待支付订单信息
+     * 用于小程序在5分钟内重新拉取待支付订单的信息
+     *
+     * @param param 
+     * @return 订单信息
+     */
+    @ApiOperation(value = "查询待支付订单信息")
+    @GetMapping("app-api/pay/pending-order")
+    public AjaxResult getPendingOrder(@RequestBody AppPayParam param) throws Exception {
+        StoreOrder order = storeOrderMapper.selectStoreOrderByOrderId(param.getOrderId());
+        log.info("收到查询待支付订单请求,订单号: {}", param);
+        if (order == null) {
+            log.warn("订单不存在,订单号: {}", param.getOrderId());
+            return AjaxResult.error("订单不存在");
+        }
+        // 检查订单是否为待支付状态
+        if (order.getPaid() != null && order.getPaid() == 1) {
+            log.info("订单已支付,订单号: {}", param.getOrderId());
+            return AjaxResult.error("订单已支付");
+        }
+        // 检查订单是否已超时取消
+        if (order.getStatus() != null && order.getStatus() == 3) {
+            log.info("订单已取消,订单号: {}", param.getOrderId());
+            return AjaxResult.error("订单已取消");
+        }
+        // 返回订单信息和重新支付所需参数
+        Map<String, Object> result = new HashMap<>();
+        result.put("order", order);
+        // 如果需要重新支付,可以在这里调用统一下单接口生成新的支付参数
+        return AjaxResult.success(wxPayService.unifiedOrder(param));
+    }
 
 }

+ 8 - 1
qmjszx-pay/src/main/java/beilv/wx/pay/domain/vo/AppPayParam.java

@@ -2,7 +2,9 @@ package beilv.wx.pay.domain.vo;
 
 import beilv.system.domain.SysMember;
 import lombok.Data;
+import lombok.NonNull;
 
+import javax.validation.constraints.NotBlank;
 import java.io.Serializable;
 import java.math.BigDecimal;
 
@@ -13,14 +15,17 @@ public class AppPayParam implements Serializable {
     /**
      * 订单单号
      */
+    @NotBlank(message = "订单单号不能为空")
     private String orderId;
     /***
      * 订单总价
      */
+    @NotBlank(message = "订单总价不能为空")
     private BigDecimal totalPrice;
     /***
      * 实际支付金额
      */
+    @NotBlank(message = "实际支付金额不能为空")
     private BigDecimal payPrice;
     /***
      * 会员折扣金额
@@ -29,6 +34,7 @@ public class AppPayParam implements Serializable {
     /***
      * 登录人id
      */
+    @NotBlank(message = "登录人id不能为空")
     private Long userId;
     /***
      * 优惠券id
@@ -43,8 +49,9 @@ public class AppPayParam implements Serializable {
      */
     private BigDecimal useIntegral;
     /***
-     * 描述
+     * 购买商品描述
      */
+    @NotBlank(message = "购买商品描述不能为空")
     private String description;
     /***
      * 登录用户信息

+ 7 - 1
qmjszx-pay/src/main/java/beilv/wx/pay/domain/vo/AppRefundParam.java

@@ -4,6 +4,7 @@ import beilv.system.domain.SysMember;
 import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request;
 import lombok.Data;
 
+import javax.validation.constraints.NotBlank;
 import java.math.BigDecimal;
 
 
@@ -12,22 +13,27 @@ public class AppRefundParam extends WxPayRefundV3Request {
     /**
      * 订单单号
      */
+    @NotBlank(message = "订单单号不能为空")
     private String orderId;
     /***
      * 订单总价
      */
+    @NotBlank(message = "订单总价不能为空")
     private BigDecimal totalPrice;
     /***
      * 退款金额
      */
+    @NotBlank(message = "退款金额不能为空")
     private BigDecimal refundAmount;
     /***
      * 登录人id
      */
+    @NotBlank(message = "登录人id不能为空")
     private Long userId;
     /***
-     * 描述
+     * 退款原因
      */
+    @NotBlank(message = "退款原因不能为空")
     private String description;
 
     /***

+ 236 - 28
qmjszx-pay/src/main/java/beilv/wx/pay/service/IWxPayService.java

@@ -9,6 +9,8 @@ import beilv.order.mapper.StoreOrderMapper;
 import beilv.order.mapper.StoreRefundMapper;
 import beilv.system.domain.SysMember;
 import beilv.system.service.ISysMemberService;
+import beilv.vipCardLog.domain.VipCardLog;
+import beilv.vipCardLog.service.IVipCardLogService;
 import beilv.wx.pay.config.WxPayProperties;
 import beilv.wx.pay.domain.vo.AppPayParam;
 import beilv.wx.pay.domain.vo.AppRefundParam;
@@ -26,8 +28,10 @@ import com.github.binarywang.wxpay.exception.WxPayException;
 import com.github.binarywang.wxpay.service.WxPayService;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Service;
 
@@ -35,7 +39,9 @@ import javax.servlet.http.HttpServletRequest;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.text.SimpleDateFormat;
-import java.util.*;
+import java.util.Date;
+import java.util.Set;
+import java.util.UUID;
 
 @Slf4j
 @Service
@@ -54,19 +60,35 @@ public class IWxPayService {
     private ISysMemberService sysMemberService;
     @Autowired
     private StoreRefundMapper storeRefundMapper;
+    @Autowired
+    private StringRedisTemplate redisTemplate;
+    @Autowired
+    private IVipCardLogService vipCardLogService;
 
     private WxPayService wxService;
 
 
+    // Redis中订单延迟队列的key前缀
+    private static final String ORDER_DELAY_QUEUE_PREFIX = "order:delay:queue:";
+    // 订单超时时间(5分钟)
+    private static final long ORDER_TIMEOUT_SECONDS = 5 * 60;
+
     /**
      * 统一下单接口
      *
      * @return 预支付交易会话标识
      */
     public JsapiResult unifiedOrder(AppPayParam param) throws Exception {
+        log.info("开始处理统一下单请求,订单号: {}", param.getOrderId());
+        // 幂等性校验 - 检查订单是否已存在且已支付
+        StoreOrder existingOrder = storeOrderMapper.selectStoreOrderByOrderId(param.getOrderId());
+        if (existingOrder != null && existingOrder.getPaid() != null && existingOrder.getPaid() == 1) {
+            log.warn("订单 {} 已支付,不能重复下单", param.getOrderId());
+            throw new Exception("订单已支付,不能重复下单");
+        }
         SysMember user = getSysMember(param.getUserId());
         param.setSysMember(user);
-        //生成唯一标识商家订单号
+        //微信支付需要的参数
         WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
         request.setAppid(wxPayProperties.getAppId());
         request.setMchid(wxPayProperties.getMchId());
@@ -78,16 +100,24 @@ public class IWxPayService {
         amount.setCurrency("CNY");
         request.setAmount(amount);
         request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(user.getOpenId()));
+        log.info("调用微信统一下单接口,订单号: {}", param.getOrderId());
         JsapiResult jsapiResult = this.wxService.createOrderV3(TradeTypeEnum.JSAPI, request);
         if (StringUtils.isNotEmpty(jsapiResult.getPackageValue())) {
+            log.info("微信统一下单成功,订单号: {},开始创建本地订单", param.getOrderId());
             //成功之后创建订单
             generateOrder(param, jsapiResult.getPackageValue());
+            //将订单添加到延迟队列中
+            addOrderToDelayQueue(param.getOrderId());
+            log.info("订单创建完成并加入延迟队列,订单号: {}", param.getOrderId());
+        } else {
+            log.warn("微信统一下单失败,订单号: {}", param.getOrderId());
         }
         return jsapiResult;
     }
 
     //获取当前用户信息
     private SysMember getSysMember(Long userId) {
+        log.debug("获取用户信息,用户ID: {}", userId);
         return sysMemberService.selectSysMemberById(userId);
     }
 
@@ -112,19 +142,60 @@ public class IWxPayService {
      * @return 预支付交易会话标识
      */
     private void generateOrder(AppPayParam param, String payId) {
-        StoreOrder storeOrder = new StoreOrder();
-        storeOrder.setOrderId(param.getOrderId());
-        storeOrder.setUid(param.getSysMember().getId());
-        storeOrder.setRealName(param.getSysMember().getUsername());
-        storeOrder.setUserPhone(param.getSysMember().getMobile());
-        storeOrder.setUserAddress(param.getSysMember().getAddress());
-        storeOrder.setTotalNum(1);
-        storeOrder.setTotalPrice(param.getTotalPrice());
-        storeOrder.setPayPrice(param.getPayPrice());
-        storeOrder.setPayTime(new Date());
-        storeOrder.setPayId(payId);
-        storeOrder.setCreateTime(new Date());
-        storeOrderMapper.insertStoreOrder(storeOrder);
+        log.info("开始生成订单,订单号: {}", param.getOrderId());
+        //查看是否已经生成订单,但是未支付
+        if (!isOrderInDelayQueue(param.getOrderId())) {
+            // 订单不存在,创建新订单
+            log.info("订单不存在,创建新订单,订单号: {}", param.getOrderId());
+            StoreOrder storeOrder = new StoreOrder();
+            storeOrder.setOrderId(param.getOrderId());
+            storeOrder.setUid(param.getSysMember().getId());
+            storeOrder.setRealName(param.getSysMember().getUsername());
+            storeOrder.setUserPhone(param.getSysMember().getMobile());
+            storeOrder.setUserAddress(param.getSysMember().getAddress());
+            storeOrder.setTotalNum(1);
+            storeOrder.setTotalPrice(param.getTotalPrice());
+            storeOrder.setPayPrice(param.getPayPrice());
+            storeOrder.setPayTime(new Date());
+            storeOrder.setPayId(payId);
+            storeOrder.setCreateTime(new Date());
+            storeOrderMapper.insertStoreOrder(storeOrder);
+            log.info("新订单创建成功,订单号: {}", param.getOrderId());
+        } else {
+            StoreOrder order = storeOrderMapper.selectStoreOrderByOrderId(param.getOrderId());
+            // 订单已存在,更新支付相关信息
+            log.info("订单已存在,更新支付信息,订单号: {}", param.getOrderId());
+            order.setPayTime(new Date());
+            order.setPayId(payId);
+            storeOrderMapper.updateStoreOrder(order);
+            log.info("订单支付信息更新完成,订单号: {}", param.getOrderId());
+        }
+    }
+
+    /**
+     * 将订单添加到延迟队列
+     *
+     * @param orderId 订单ID
+     */
+    private void addOrderToDelayQueue(String orderId) {
+        log.info("将订单添加到延迟队列,订单号: {}", orderId);
+        // 计算订单超时时间戳(当前时间+5分钟)
+        long expireTime = System.currentTimeMillis() + (ORDER_TIMEOUT_SECONDS * 1000);
+        // 将订单添加到Redis有序集合中,以超时时间戳作为score
+        redisTemplate.opsForZSet().add(ORDER_DELAY_QUEUE_PREFIX + "orders", orderId, expireTime);
+        log.info("订单 {} 已添加到延迟队列,超时时间: {}", orderId, new Date(expireTime));
+    }
+
+    /**
+     * 从延迟队列中移除订单(支付成功时调用)
+     *
+     * @param orderId 订单ID
+     */
+    private void removeOrderFromDelayQueue(String orderId) {
+        log.info("从延迟队列中移除订单,订单号: {}", orderId);
+        // 从Redis有序集合中移除订单
+        redisTemplate.opsForZSet().remove(ORDER_DELAY_QUEUE_PREFIX + "orders", orderId);
+        log.info("订单 {} 已从延迟队列中移除", orderId);
     }
 
     //生成退款订单号
@@ -140,21 +211,30 @@ public class IWxPayService {
      * @return 处理结果
      */
     public AjaxResult handleNotify(String notifyData, HttpServletRequest request) {
+        log.info("收到微信支付回调通知");
         SignatureHeader header = getRequestHeader(request);
         try {
             WxPayNotifyV3Result res = this.wxService.parseOrderNotifyV3Result(notifyData, header);
             WxPayNotifyV3Result.DecryptNotifyResult decryptRes = res.getResult();
-            // TODO 根据自己业务场景需要构造返回对象
+            log.info("解析微信支付回调结果,订单号: {}, 交易状态: {}", decryptRes.getOutTradeNo(), decryptRes.getTradeState());
             if (WxPayConstants.WxpayTradeStatus.SUCCESS.equals(decryptRes.getTradeState())) {
+                log.info("支付成功,开始处理订单,订单号: {}", decryptRes.getOutTradeNo());
                 // 成功处理订单
                 processOrderPayment(decryptRes.getOutTradeNo());
+                //调用充值记录服务,更新充值记录状态
+                VipCardLog vipCardLog = new VipCardLog();
+                vipCardLog.setId(decryptRes.getOutTradeNo());
+                vipCardLogService.updateVipCardLogByOrderId(vipCardLog);
                 // 成功返回200/204,body无需有内容
+                log.info("订单处理完成,订单号: {}", decryptRes.getOutTradeNo());
                 return AjaxResult.success();
             } else {
+                log.warn("支付失败,订单号: {},状态: {}", decryptRes.getOutTradeNo(), decryptRes.getTradeState());
                 // 失败返回4xx或5xx,且需要构造body信息
                 return AjaxResult.error();
             }
         } catch (WxPayException e) {
+            log.error("处理微信支付回调失败", e);
             // 失败返回4xx或5xx,且需要构造body信息
             return AjaxResult.error("微信支付回调失败");
         }
@@ -163,19 +243,28 @@ public class IWxPayService {
     /**
      * 处理订单支付完成后的业务逻辑
      *
-     * @param outTradeNo 商户订单号
+     * @param orderId 订单号
      */
-    private void processOrderPayment(String outTradeNo) {
-        StoreOrder order = storeOrderMapper.selectStoreOrderByOrderId(outTradeNo);
+    private void processOrderPayment(String orderId) {
+        log.info("开始处理订单支付完成逻辑,订单号: {}", orderId);
+        StoreOrder order = storeOrderMapper.selectStoreOrderByOrderId(orderId);
         if (order != null) {
             // 检查订单是否已经处理过
             if (order.getPaid() == null || order.getPaid() != 1) {
+                log.info("更新订单状态为已支付,订单号: {}", orderId);
                 // 更新订单状态
                 order.setPaid(1); // 设置为已支付
                 order.setStatus(1); // 设置订单状态为已支付
-                order.setPayTime(new java.util.Date()); // 设置支付时间
+                order.setPayTime(new Date()); // 设置支付时间
                 storeOrderMapper.updateStoreOrder(order);
+                // 从延迟队列中移除订单
+                removeOrderFromDelayQueue(orderId);
+                log.info("订单状态更新完成,订单号: {}", orderId);
+            } else {
+                log.info("订单已处理过,无需重复处理,订单号: {}", orderId);
             }
+        } else {
+            log.warn("订单不存在,订单号: {}", orderId);
         }
     }
 
@@ -184,6 +273,31 @@ public class IWxPayService {
      *
      */
     public WxPayRefundV3Result refund(AppRefundParam param) throws WxPayException {
+        log.info("开始处理退款请求,订单号: {}", param.getOrderId());
+        // 检查订单是否存在
+        StoreOrder order = storeOrderMapper.selectStoreOrderByOrderId(param.getOrderId());
+        if (order == null) {
+            log.warn("退款失败,订单不存在,订单号: {}", param.getOrderId());
+            throw new WxPayException("订单不存在");
+        }
+
+        // 检查订单是否已支付
+        if (order.getPaid() == null || order.getPaid() != 1) {
+            log.warn("退款失败,订单未支付,订单号: {}", param.getOrderId());
+            throw new WxPayException("订单未支付,无法退款");
+        }
+
+        // 检查订单是否已退款
+        if (order.getRefundStatus() != null && order.getRefundStatus() == 2) {
+            log.warn("退款失败,订单已退款,订单号: {}", param.getOrderId());
+            throw new WxPayException("订单已退款");
+        }
+
+        // 检查退款金额是否合法
+        if (param.getRefundAmount().compareTo(order.getPayPrice()) > 0) {
+            log.warn("退款失败,退款金额大于支付金额,订单号: {}", param.getOrderId());
+            throw new WxPayException("退款金额不能大于支付金额");
+        }
         WxPayRefundV3Request request = new WxPayRefundV3Request();
         //商户退款单号
         request.setOutRefundNo(param.getOrderId());
@@ -201,8 +315,16 @@ public class IWxPayService {
         request.setAmount(amount);
         request.setNotifyUrl(wxPayProperties.getRefundNotifyUrl());
         param.setSysMember(getSysMember(param.getUserId()));
-        refundOrder(param);
-        return this.wxService.refundV3(request);
+        // 检查是否已存在退款记录
+        StoreRefund existingRefund = storeRefundMapper.selectStoreRefundByOrderId(param.getOrderId());
+        if (existingRefund == null) {
+            log.info("创建退款记录,订单号: {}", param.getOrderId());
+            refundOrder(param);
+        }
+        log.info("调用微信退款接口,订单号: {}", param.getOrderId());
+        WxPayRefundV3Result result = this.wxService.refundV3(request);
+        log.info("微信退款接口调用完成,订单号: {}", param.getOrderId());
+        return result;
     }
 
     /**
@@ -211,6 +333,7 @@ public class IWxPayService {
      * @return 预支付交易会话标识
      */
     private void refundOrder(AppRefundParam param) {
+        log.info("生成退款订单记录,订单号: {}", param.getOrderId());
         StoreRefund storeRefund = new StoreRefund();
         storeRefund.setUid(param.getSysMember().getId());
         storeRefund.setRefundNo(generateOutTradeNo());
@@ -219,6 +342,7 @@ public class IWxPayService {
         storeRefund.setRefundReason(param.getReason());
         storeRefund.setCreateTime(new Date());
         storeRefundMapper.insertStoreRefund(storeRefund);
+        log.info("退款订单记录创建完成,订单号: {}", param.getOrderId());
     }
 
     /**
@@ -227,21 +351,27 @@ public class IWxPayService {
      * @return 处理结果
      */
     public AjaxResult parseRefundNotify(String notifyData, HttpServletRequest request) {
+        log.info("收到微信退款回调通知");
         SignatureHeader header = getRequestHeader(request);
         try {
             WxPayRefundNotifyV3Result res = this.wxService.parseRefundNotifyV3Result(notifyData, header);
             WxPayRefundNotifyV3Result.DecryptNotifyResult decryptRes = res.getResult();
+            log.info("解析微信退款回调结果,订单号: {}, 退款状态: {}", decryptRes.getOutTradeNo(), decryptRes.getRefundStatus());
             // TODO 根据自己业务场景需要构造返回对象
             if (WxPayConstants.RefundStatus.SUCCESS.equals(decryptRes.getRefundStatus())) {
+                log.info("退款成功,开始处理订单,订单号: {}", decryptRes.getOutTradeNo());
                 // 成功处理订单
                 processOrderRefund(decryptRes.getOutTradeNo());
                 //成功返回200/204,body无需有内容
+                log.info("退款处理完成,订单号: {}", decryptRes.getOutTradeNo());
                 return AjaxResult.success();
             } else {
+                log.warn("退款失败,订单号: {},状态: {}", decryptRes.getOutTradeNo(), decryptRes.getRefundStatus());
                 //失败返回4xx或5xx,且需要构造body信息
                 return AjaxResult.error();
             }
         } catch (WxPayException e) {
+            log.error("处理微信退款回调失败", e);
             //失败返回4xx或5xx,且需要构造body信息
             return AjaxResult.error("微信退款回调失败");
         }
@@ -250,28 +380,41 @@ public class IWxPayService {
     /**
      * 退款完成后的业务逻辑
      *
-     * @param outTradeNo 商户订单号
+     * @param orderId 订单号
      */
-    private void processOrderRefund(String outTradeNo) {
-        StoreOrder order = storeOrderMapper.selectStoreOrderByOrderId(outTradeNo);
+    private void processOrderRefund(String orderId) {
+        log.info("开始处理订单退款完成逻辑,订单号: {}", orderId);
+        StoreOrder order = storeOrderMapper.selectStoreOrderByOrderId(orderId);
         if (order != null) {
             // 检查订单是否已经处理过
             if (order.getRefundStatus() != null && order.getPaid() == 1) {
-                // 更新订单状态
-                order.setStatus(2); // 设置订单状态为已退款
+                log.info("更新订单状态为已退款,订单号: {}", orderId);
+                //更新支付订单状态为退款
+                order.setStatus(2); // 3表示已取消
                 order.setUpdateTime(new Date());
                 storeOrderMapper.updateStoreOrder(order);
+                log.info("订单状态更新完成,订单号: {}", orderId);
+            } else {
+                log.info("订单退款状态无需更新,订单号: {}", orderId);
             }
+        } else {
+            log.warn("订单不存在,订单号: {}", orderId);
         }
-        StoreRefund refund = storeRefundMapper.selectStoreRefundByOrderId(outTradeNo);
+        StoreRefund refund = storeRefundMapper.selectStoreRefundByOrderId(orderId);
         if (refund != null) {
             // 检查订单是否已经处理过
             if (refund.getRefundStatus() != null && refund.getRefundStatus().equals("1")) {
+                log.info("更新退款记录状态为已退款,退款单号: {}", refund.getRefundNo());
                 // 更新订单状态
                 refund.setRefundStatus("2"); // 设置订单状态为已退款
                 refund.setSuccessTime(new Date());
                 storeRefundMapper.updateStoreRefund(refund);
+                log.info("退款记录状态更新完成,退款单号: {}", refund.getRefundNo());
+            } else {
+                log.info("退款记录状态无需更新,退款单号: {}", refund.getRefundNo());
             }
+        } else {
+            log.warn("退款记录不存在,订单号: {}", orderId);
         }
     }
 
@@ -283,6 +426,7 @@ public class IWxPayService {
      * @return
      */
     private SignatureHeader getRequestHeader(HttpServletRequest request) {
+        log.debug("解析微信回调请求头信息");
         // 获取通知签名
         String signature = request.getHeader("Wechatpay-Signature");
         String nonce = request.getHeader("Wechatpay-Nonce");
@@ -296,5 +440,69 @@ public class IWxPayService {
         return signatureHeader;
     }
 
+    /**
+     * 处理超时订单
+     * 定时任务调用此方法检查并处理超时订单
+     */
+    public void processTimeoutOrders() {
+        log.info("开始处理超时订单");
+        // 获取当前时间戳
+        long currentTime = System.currentTimeMillis();
+
+        // 从Redis有序集合中获取所有已超时的订单(score小于当前时间的元素)
+        redisTemplate.opsForZSet().rangeByScore(ORDER_DELAY_QUEUE_PREFIX + "orders", 0, currentTime)
+                .forEach(orderId -> {
+                    try {
+                        log.info("处理超时订单: {}", orderId);
+                        // 查询订单信息
+                        StoreOrder order = storeOrderMapper.selectStoreOrderByOrderId(orderId);
+                        if (order != null && (order.getPaid() == null || order.getPaid() != 1)) {
+                            // 订单未支付,执行删除操作或更新状态
+                            // 这里我们更新订单状态为已取消
+                            order.setStatus(3); // 3表示已取消
+                            order.setUpdateTime(new Date());
+                            storeOrderMapper.updateStoreOrder(order);
+                            log.info("订单 {} 已超时,状态已更新为已取消", orderId);
+                        } else if (order != null) {
+                            log.info("订单 {} 已支付,无需处理", orderId);
+                        } else {
+                            log.warn("订单 {} 不存在", orderId);
+                        }
+
+                        // 从延迟队列中移除订单
+                        redisTemplate.opsForZSet().remove(ORDER_DELAY_QUEUE_PREFIX + "orders", orderId);
+                        log.info("超时订单 {} 处理完成", orderId);
+                    } catch (Exception e) {
+                        log.error("处理超时订单 {} 时发生错误", orderId, e);
+                    }
+                });
+        log.info("超时订单处理完成");
+    }
+
+    /**
+     * 取消订单
+     *
+     * @param orderId 订单ID
+     * @return 处理结果
+     */
+    public int cancelOrder(String orderId) {
+        StoreOrder order = storeOrderMapper.selectStoreOrderByOrderId(orderId);
+        order.setStatus(3); // 3表示已取消
+        order.setUpdateTime(new Date());
+        return storeOrderMapper.updateStoreOrder(order);
+    }
+
+    /**
+     * 检查订单是否在延迟队列中
+     *
+     * @param orderId 订单ID
+     * @return 如果订单在延迟队列中返回true,否则返回false
+     */
+    public boolean isOrderInDelayQueue(String orderId) {
+        // 使用rank命令检查订单是否在有序集合中
+        // 如果返回null,说明订单不在集合中
+        Long rank = redisTemplate.opsForZSet().rank(ORDER_DELAY_QUEUE_PREFIX + "orders", orderId);
+        return rank != null;
+    }
 
 }

+ 33 - 0
qmjszx-pay/src/main/java/beilv/wx/pay/task/OrderTimeoutTask.java

@@ -0,0 +1,33 @@
+package beilv.wx.pay.task;
+
+import beilv.wx.pay.service.IWxPayService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+/**
+ * 订单超时处理任务
+ * 定期检查Redis中的延迟队列,处理超时订单
+ */
+@Slf4j
+@Component
+public class OrderTimeoutTask {
+
+    @Autowired
+    private IWxPayService wxPayService;
+
+    /**
+     * 每隔10秒检查一次超时订单
+     */
+    @Scheduled(fixedRate = 10000)
+    public void checkTimeoutOrders() {
+        try {
+            log.info("开始检查超时订单...");
+            wxPayService.processTimeoutOrders();
+            log.info("超时订单检查完成");
+        } catch (Exception e) {
+            log.error("检查超时订单时发生错误", e);
+        }
+    }
+}