Browse Source

trade:增加自动取消的 job

YunaiV 1 year ago
parent
commit
826d792ff2

+ 1 - 2
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderCancelTypeEnum.java

@@ -17,8 +17,7 @@ public enum TradeOrderCancelTypeEnum implements IntArrayValuable {
 
 
     PAY_TIMEOUT(10, "超时未支付"),
     PAY_TIMEOUT(10, "超时未支付"),
     AFTER_SALE_CLOSE(20, "退款关闭"),
     AFTER_SALE_CLOSE(20, "退款关闭"),
-    MEMBER_CANCEL(30, "买家取消"),
-    PAY_ON_DELIVERY(40, "已通过货到付款交易"),; // TODO 芋艿:这个类型,是不是可以去掉
+    MEMBER_CANCEL(30, "买家取消");
 
 
     public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TradeOrderCancelTypeEnum::getType).toArray();
     public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TradeOrderCancelTypeEnum::getType).toArray();
 
 

+ 4 - 2
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderOperateTypeEnum.java

@@ -16,8 +16,10 @@ public enum TradeOrderOperateTypeEnum {
     MEMBER_CREATE(1, "用户下单"),
     MEMBER_CREATE(1, "用户下单"),
     MEMBER_RECEIVE(30, "用户已收货"),
     MEMBER_RECEIVE(30, "用户已收货"),
     MEMBER_COMMENT(31, "用户评价"),
     MEMBER_COMMENT(31, "用户评价"),
-    MEMBER_CANCEL(40, "取消订单"),
-    MEMBER_DELETE(41, "删除订单"),
+    MEMBER_CANCEL(40, "手动取消订单"),
+    SYSTEM_CANCEL(41, "系统取消订单"),
+    // 42 预留:管理员取消订单
+    MEMBER_DELETE(43, "删除订单"),
     ;
     ;
 
 
     /**
     /**

+ 1 - 1
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java

@@ -90,7 +90,7 @@ public interface TradeOrderConvert {
 
 
     default ProductSkuUpdateStockReqDTO convert(List<TradeOrderItemDO> list) {
     default ProductSkuUpdateStockReqDTO convert(List<TradeOrderItemDO> list) {
         List<ProductSkuUpdateStockReqDTO.Item> items = CollectionUtils.convertList(list, item ->
         List<ProductSkuUpdateStockReqDTO.Item> items = CollectionUtils.convertList(list, item ->
-                new ProductSkuUpdateStockReqDTO.Item().setId(item.getSkuId()).setIncrCount(-item.getCount()));
+                new ProductSkuUpdateStockReqDTO.Item().setId(item.getSkuId()).setIncrCount(item.getCount()));
         return new ProductSkuUpdateStockReqDTO(items);
         return new ProductSkuUpdateStockReqDTO(items);
     }
     }
     default ProductSkuUpdateStockReqDTO convertNegative(List<AppTradeOrderSettlementReqVO.Item> list) {
     default ProductSkuUpdateStockReqDTO convertNegative(List<AppTradeOrderSettlementReqVO.Item> list) {

+ 9 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderMapper.java

@@ -9,6 +9,8 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Mapper;
 
 
+import java.time.LocalDateTime;
+import java.util.List;
 import java.util.Set;
 import java.util.Set;
 
 
 @Mapper
 @Mapper
@@ -58,4 +60,11 @@ public interface TradeOrderMapper extends BaseMapperX<TradeOrderDO> {
                 .eq(TradeOrderDO::getId, orderId)
                 .eq(TradeOrderDO::getId, orderId)
                 .eq(TradeOrderDO::getUserId, loginUserId));
                 .eq(TradeOrderDO::getUserId, loginUserId));
     }
     }
+
+    default List<TradeOrderDO> selectListByStatusAndCreateTimeLt(Integer status, LocalDateTime expireTime) {
+        return selectList(new LambdaUpdateWrapper<TradeOrderDO>()
+                .eq(TradeOrderDO::getStatus, status)
+                .lt(TradeOrderDO::getCreateTime, expireTime));
+    }
+
 }
 }

+ 28 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/job/order/TradeOrderAutoCancelJob.java

@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.trade.job.order;
+
+import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
+import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
+import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+
+/**
+ * 交易订单的自动过期 Job
+ *
+ * @author 芋道源码
+ */
+@Component
+@TenantJob
+public class TradeOrderAutoCancelJob implements JobHandler {
+
+    @Resource
+    private TradeOrderUpdateService tradeOrderUpdateService;
+
+    @Override
+    public String execute(String param) {
+        int count = tradeOrderUpdateService.autoCancelOrder();
+        return String.format("过期订单 %s 个", count);
+    }
+
+}

+ 7 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateService.java

@@ -70,6 +70,13 @@ public interface TradeOrderUpdateService {
      */
      */
     void cancelOrder(Long userId, Long id);
     void cancelOrder(Long userId, Long id);
 
 
+    /**
+     * 【系统】自动取消订单
+     *
+     * @return 取消数量
+     */
+    int autoCancelOrder();
+
     /**
     /**
      * 【会员】删除订单
      * 【会员】删除订单
      *
      *

+ 59 - 9
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java

@@ -76,6 +76,7 @@ import java.util.Set;
 
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
+import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime;
 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;
 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;
 
 
 /**
 /**
@@ -629,6 +630,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
 
 
     @Override
     @Override
     @Transactional(rollbackFor = Exception.class)
     @Transactional(rollbackFor = Exception.class)
+    // TODO 芋艿:
     public void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus,
     public void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus,
                                                Long afterSaleId, Integer refundPrice) {
                                                Long afterSaleId, Integer refundPrice) {
         // 如果退款成功,则 refundPrice 非空
         // 如果退款成功,则 refundPrice 非空
@@ -663,12 +665,15 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
 
 
             // TODO 芋艿:记录订单日志
             // TODO 芋艿:记录订单日志
 
 
+            // TODO 芋艿:要不要退优惠劵
+
             // TODO 芋艿:站内信?
             // TODO 芋艿:站内信?
         } else { // 如果部分售后,则更新退款金额
         } else { // 如果部分售后,则更新退款金额
             tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
             tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
                     .setRefundStatus(TradeOrderRefundStatusEnum.PART.getStatus()).setRefundPrice(orderRefundPrice));
                     .setRefundStatus(TradeOrderRefundStatusEnum.PART.getStatus()).setRefundPrice(orderRefundPrice));
         }
         }
 
 
+        // TODO 芋艿:这块扣减规则,需要在考虑下
         // 售后成功后,执行数据回滚逻辑
         // 售后成功后,执行数据回滚逻辑
         if (Objects.equals(newAfterSaleStatus, TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus())) {
         if (Objects.equals(newAfterSaleStatus, TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus())) {
             // 扣减用户积分(赠送的)
             // 扣减用户积分(赠送的)
@@ -732,31 +737,76 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
             throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
             throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
         }
         }
 
 
-        // 2. 更新 TradeOrderDO 状态为已取消
+        // 2. 取消订单
+        cancelOrder0(order, TradeOrderCancelTypeEnum.MEMBER_CANCEL);
+    }
+
+    @Override
+    public int autoCancelOrder() {
+        // 1. 查询过期的待支付订单
+        LocalDateTime expireTime = addTime(tradeOrderProperties.getExpireTime());
+        List<TradeOrderDO> orders = tradeOrderMapper.selectListByStatusAndCreateTimeLt(
+                TradeOrderStatusEnum.UNPAID.getStatus(), expireTime);
+        if (CollUtil.isEmpty(orders)) {
+            return 0;
+        }
+
+        // 2. 遍历执行,逐个取消
+        int count = 0;
+        for (TradeOrderDO order : orders) {
+            try {
+                getSelf().autoCancelOrder(order);
+                count ++;
+            } catch (Throwable e) {
+                log.error("[autoCancelOrder][order({}) 过期订单异常]", order.getId(), e);
+            }
+        }
+        return count;
+    }
+
+    /**
+     * 自动取消单个订单
+     *
+     * @param order 订单
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_CANCEL)
+    public void autoCancelOrder(TradeOrderDO order) {
+        cancelOrder0(order, TradeOrderCancelTypeEnum.PAY_TIMEOUT);
+    }
+
+    /**
+     * 取消订单的核心实现
+     *
+     * @param order 订单
+     * @param cancelType 取消类型
+     */
+    private void cancelOrder0(TradeOrderDO order, TradeOrderCancelTypeEnum cancelType) {
+        Long id = order.getId();
+        // 1. 更新 TradeOrderDO 状态为已取消
         int updateCount = tradeOrderMapper.updateByIdAndStatus(id, order.getStatus(),
         int updateCount = tradeOrderMapper.updateByIdAndStatus(id, order.getStatus(),
                 new TradeOrderDO().setStatus(TradeOrderStatusEnum.CANCELED.getStatus())
                 new TradeOrderDO().setStatus(TradeOrderStatusEnum.CANCELED.getStatus())
-                        .setCancelTime(LocalDateTime.now())
-                        .setCancelType(TradeOrderCancelTypeEnum.MEMBER_CANCEL.getType()));
+                        .setCancelType(cancelType.getType()).setCancelTime(LocalDateTime.now()));
         if (updateCount == 0) {
         if (updateCount == 0) {
             throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
             throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
         }
         }
 
 
-        // 3. TODO 活动相关库存回滚需要活动 id,活动 id 怎么获取?app 端能否传过来;回复:从订单里拿呀
+        // 2. TODO 活动相关库存回滚需要活动 id,活动 id 怎么获取?app 端能否传过来;回复:从订单里拿呀
         tradeOrderHandlers.forEach(handler -> handler.rollback());
         tradeOrderHandlers.forEach(handler -> handler.rollback());
 
 
-        // 4. 回滚库存
+        // 3. 回滚库存
         List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id);
         List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id);
         productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(orderItems));
         productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(orderItems));
 
 
-        // 5. 回滚优惠券
-        if (order.getCouponId() > 0) {
+        // 4. 回滚优惠券
+        if (order.getCouponId() != null && order.getCouponId() > 0) {
             couponApi.returnUsedCoupon(order.getCouponId());
             couponApi.returnUsedCoupon(order.getCouponId());
         }
         }
 
 
-        // 6. 回滚积分(抵扣的)
+        // 5. 回滚积分(抵扣的)
         addUserPoint(order.getUserId(), order.getUsePoint(), MemberPointBizTypeEnum.ORDER_CANCEL, order.getId());
         addUserPoint(order.getUserId(), order.getUsePoint(), MemberPointBizTypeEnum.ORDER_CANCEL, order.getId());
 
 
-        // 7. 增加订单日志
+        // 6. 增加订单日志
         TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.CANCELED.getStatus());
         TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.CANCELED.getStatus());
     }
     }