فهرست منبع

实现一个定时任务,要求定时查询退款失败的订单进行退款

zhangchen 1 ماه پیش
والد
کامیت
a8ed73b9b3

+ 7 - 0
alien-entity/src/main/java/shop/alien/mapper/UserReservationOrderMapper.java

@@ -56,4 +56,11 @@ public interface UserReservationOrderMapper extends BaseMapper<UserReservationOr
      * @return 订单ID列表
      */
     List<Integer> listOrderIdsForArrivalReminder();
+
+    /**
+     * 查询支付状态为「退款中」(4) 的付费订单ID列表,用于定时任务重试退款
+     *
+     * @return 订单ID列表
+     */
+    List<Integer> listOrderIdsForRefundRetry();
 }

+ 12 - 0
alien-entity/src/main/resources/mapper/UserReservationOrderMapper.xml

@@ -82,4 +82,16 @@
           AND r.start_time IS NOT NULL AND TRIM(r.start_time) != ''
           AND STR_TO_DATE(TRIM(r.start_time), '%Y-%m-%d %H:%i') BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 30 MINUTE)
     </select>
+
+    <!-- 退款重试:支付状态为退款中(4)的付费订单 -->
+    <select id="listOrderIdsForRefundRetry" resultType="java.lang.Integer">
+        SELECT id
+        FROM user_reservation_order
+        WHERE delete_flag = 0
+          AND payment_status = 4
+          AND order_cost_type = 1
+          AND out_trade_no IS NOT NULL AND TRIM(out_trade_no) != ''
+          AND deposit_amount IS NOT NULL AND deposit_amount > 0
+        ORDER BY updated_time ASC
+    </select>
 </mapper>

+ 8 - 0
alien-job/src/main/java/shop/alien/job/feign/AlienStoreFeign.java

@@ -85,4 +85,12 @@ public interface AlienStoreFeign {
     @org.springframework.web.bind.annotation.PostMapping("/reservation/job/sendArrivalReminder")
     R<Integer> sendArrivalReminder();
 
+    /**
+     * 重试退款定时任务:查询支付状态为退款中的订单并重新发起退款
+     *
+     * @return R.data 为本次退款成功的订单数
+     */
+    @org.springframework.web.bind.annotation.PostMapping("/reservation/job/retryRefundFailed")
+    R<Integer> retryRefundFailed();
+
 }

+ 32 - 0
alien-job/src/main/java/shop/alien/job/store/RefundRetryJob.java

@@ -0,0 +1,32 @@
+package shop.alien.job.store;
+
+import com.xxl.job.core.handler.annotation.XxlJob;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import shop.alien.entity.result.R;
+import shop.alien.job.feign.AlienStoreFeign;
+
+/**
+ * 退款重试定时任务:查询支付状态为「退款中」的预订订单并重新发起退款(不发送短信和通知)
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class RefundRetryJob {
+
+    private final AlienStoreFeign alienStoreFeign;
+
+    @XxlJob("refundRetryJob")
+    public void refundRetryJob() {
+        log.info("【定时任务】退款重试:开始执行");
+        try {
+            R<Integer> result = alienStoreFeign.retryRefundFailed();
+            int count = (result != null && result.getData() != null) ? result.getData() : 0;
+            log.info("【定时任务】退款重试:执行完成,本次退款成功条数={}", count);
+        } catch (Exception e) {
+            log.error("【定时任务】退款重试:执行异常", e);
+            throw e;
+        }
+    }
+}

+ 11 - 0
alien-store/src/main/java/shop/alien/store/controller/ReservationJobController.java

@@ -8,6 +8,7 @@ import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 import shop.alien.entity.result.R;
+import shop.alien.store.service.StoreReservationService;
 import shop.alien.store.service.UserReservationService;
 
 /**
@@ -21,6 +22,7 @@ import shop.alien.store.service.UserReservationService;
 public class ReservationJobController {
 
     private final UserReservationService userReservationService;
+    private final StoreReservationService storeReservationService;
 
     @ApiOperation("标记「结束时间已过且订单待使用」的预订为未到店超时/已过期")
     @PostMapping("/markTimeout")
@@ -39,4 +41,13 @@ public class ReservationJobController {
         log.info("reservation job: sendArrivalReminder 结束,发送条数={}", count);
         return R.data(count);
     }
+
+    @ApiOperation("重试退款:查询支付状态为退款中的订单并重新发起退款(不发送短信和通知)")
+    @PostMapping("/retryRefundFailed")
+    public R<Integer> retryRefundFailed() {
+        log.info("reservation job: retryRefundFailed 开始");
+        int count = storeReservationService.retryRefundFailedOrders();
+        log.info("reservation job: retryRefundFailed 结束,成功退款条数={}", count);
+        return R.data(count);
+    }
 }

+ 7 - 0
alien-store/src/main/java/shop/alien/store/service/StoreReservationService.java

@@ -96,4 +96,11 @@ public interface StoreReservationService {
      * @return 退款结果
      */
     shop.alien.entity.result.R<String> refundByOrderId(Integer orderId, String refundReason, Integer refundType);
+
+    /**
+     * 定时任务:查询支付状态为「退款中」的订单并重试退款(不发送短信和通知)
+     *
+     * @return 本次退款成功的订单数
+     */
+    int retryRefundFailedOrders();
 }

+ 9 - 0
alien-store/src/main/java/shop/alien/store/service/UserReservationOrderService.java

@@ -3,6 +3,8 @@ package shop.alien.store.service;
 import com.baomidou.mybatisplus.extension.service.IService;
 import shop.alien.entity.store.UserReservationOrder;
 
+import java.util.List;
+
 /**
  * 用户预订订单表 服务类
  *
@@ -40,4 +42,11 @@ public interface UserReservationOrderService extends IService<UserReservationOrd
      * @return 删除行数
      */
     int physicalDeleteByReservationId(Integer reservationId);
+
+    /**
+     * 查询支付状态为「退款中」的付费订单ID列表,用于定时任务重试退款
+     *
+     * @return 订单ID列表
+     */
+    List<Integer> listOrderIdsForRefundRetry();
 }

+ 21 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreReservationServiceImpl.java

@@ -1407,4 +1407,25 @@ public class StoreReservationServiceImpl extends ServiceImpl<StoreReservationMap
             return shop.alien.entity.result.R.fail("退款失败:" + e.getMessage());
         }
     }
+
+    @Override
+    public int retryRefundFailedOrders() {
+        java.util.List<Integer> orderIds = userReservationOrderService.listOrderIdsForRefundRetry();
+        if (orderIds == null || orderIds.isEmpty()) {
+            return 0;
+        }
+        int successCount = 0;
+        for (Integer orderId : orderIds) {
+            try {
+                shop.alien.entity.result.R<String> result = refundByOrderId(orderId, "定时重试退款", 1);
+                if (result != null && shop.alien.entity.result.R.isSuccess(result)) {
+                    successCount++;
+                    log.info("定时重试退款成功,orderId={}", orderId);
+                }
+            } catch (Exception e) {
+                log.error("定时重试退款异常,orderId={}", orderId, e);
+            }
+        }
+        return successCount;
+    }
 }

+ 6 - 0
alien-store/src/main/java/shop/alien/store/service/impl/UserReservationOrderServiceImpl.java

@@ -14,6 +14,7 @@ import shop.alien.store.service.UserReservationOrderService;
 
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.List;
 import java.util.concurrent.ThreadLocalRandom;
 
 /**
@@ -65,4 +66,9 @@ public class UserReservationOrderServiceImpl extends ServiceImpl<UserReservation
         }
         return baseMapper.physicalDeleteByReservationId(reservationId);
     }
+
+    @Override
+    public List<Integer> listOrderIdsForRefundRetry() {
+        return baseMapper.listOrderIdsForRefundRetry();
+    }
 }