| @@ -40,6 +40,12 @@ | |||||
| <artifactId>alipay-easysdk</artifactId> | <artifactId>alipay-easysdk</artifactId> | ||||
| <version>2.2.0</version> | <version>2.2.0</version> | ||||
| </dependency> | </dependency> | ||||
| <!--TTL--> | |||||
| <dependency> | |||||
| <groupId>com.alibaba</groupId> | |||||
| <artifactId>transmittable-thread-local</artifactId> | |||||
| <version>2.11.4</version> | |||||
| </dependency> | |||||
| </dependencies> | </dependencies> | ||||
| </project> | </project> | ||||
| @@ -198,7 +198,7 @@ public interface WxOrderService { | |||||
| WxOrderCouponPressVo detailPressVo(WxOrder record); | WxOrderCouponPressVo detailPressVo(WxOrder record); | ||||
| void updateStatusByPressCouponId(Long couponId, TenantEntity tenantEntity,int orderStatus); | |||||
| void updateStatusByPressCouponId(Long couponId, TenantEntity tenantEntity, int orderStatus); | |||||
| /** | /** | ||||
| * 通过优惠券id更新订单状态 | * 通过优惠券id更新订单状态 | ||||
| @@ -2,7 +2,6 @@ package com.iformall.service.helper; | |||||
| import com.iformall.common.ErrorCode; | import com.iformall.common.ErrorCode; | ||||
| import com.iformall.domain.po.WxCoupon; | import com.iformall.domain.po.WxCoupon; | ||||
| import com.iformall.domain.po.base.TenantEntity; | |||||
| import com.iformall.enums.EnumCacheKey; | import com.iformall.enums.EnumCacheKey; | ||||
| import com.iformall.enums.EnumOrderStatus; | import com.iformall.enums.EnumOrderStatus; | ||||
| import com.iformall.enums.EnumYesOrNo; | import com.iformall.enums.EnumYesOrNo; | ||||
| @@ -10,6 +9,8 @@ import com.iformall.exception.MallinkException; | |||||
| import com.iformall.service.WxCouponChannelService; | import com.iformall.service.WxCouponChannelService; | ||||
| import com.iformall.service.WxCouponService; | import com.iformall.service.WxCouponService; | ||||
| import com.iformall.service.WxOrderService; | import com.iformall.service.WxOrderService; | ||||
| import com.iformall.service.thread.CompletableFutureWithExecutor; | |||||
| import com.iformall.service.thread.ThreadPoolService; | |||||
| import com.iformall.utils.RedisLock; | import com.iformall.utils.RedisLock; | ||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
| @@ -17,8 +18,6 @@ import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.context.annotation.Lazy; | import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||
| import java.util.concurrent.CompletableFuture; | |||||
| @Service | @Service | ||||
| public class WxOrderServiceHelper { | public class WxOrderServiceHelper { | ||||
| @@ -39,6 +38,9 @@ public class WxOrderServiceHelper { | |||||
| @Autowired | @Autowired | ||||
| RedisLock redisLock; | RedisLock redisLock; | ||||
| @Autowired | |||||
| private ThreadPoolService threadPoolService; | |||||
| /** | /** | ||||
| * 微信订单是否可以支付 | * 微信订单是否可以支付 | ||||
| */ | */ | ||||
| @@ -69,11 +71,9 @@ public class WxOrderServiceHelper { | |||||
| } | } | ||||
| // 砍价订单,支付时扣库存,库存不足 则修改砍价订单状态 | // 砍价订单,支付时扣库存,库存不足 则修改砍价订单状态 | ||||
| CompletableFuture.runAsync(() -> { | |||||
| if (isPressOrder && null != coupon.getPressPayReduceStock() && coupon.getPressPayReduceStock().intValue() == EnumYesOrNo.YES.getCode().intValue() && stock <= 0 ) { | |||||
| wxOrderService.updateStatusByPressCouponId(coupon.getTenantId(), coupon.getCouponId(), EnumOrderStatus.ORDER_STATUS_PRESS_CANCEL.getCode()); | |||||
| } | |||||
| }); | |||||
| if (isPressOrder && null != coupon.getPressPayReduceStock() && coupon.getPressPayReduceStock().intValue() == EnumYesOrNo.YES.getCode().intValue() && stock <= 0 ) { | |||||
| CompletableFutureWithExecutor.runAsync(() -> wxOrderService.updateStatusByPressCouponId(coupon.getTenantId(), coupon.getCouponId(), EnumOrderStatus.ORDER_STATUS_PRESS_CANCEL.getCode())); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,38 @@ | |||||
| package com.iformall.service.thread; | |||||
| import com.alibaba.ttl.threadpool.TtlExecutors; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.stereotype.Component; | |||||
| import javax.annotation.PostConstruct; | |||||
| import java.util.concurrent.CompletableFuture; | |||||
| import java.util.function.Supplier; | |||||
| /** | |||||
| * 使用自定义线程池实现异步编程 | |||||
| * @author xmzhao71 | |||||
| * @date 2023-09-06 | |||||
| */ | |||||
| @Component | |||||
| public class CompletableFutureWithExecutor { | |||||
| private static final Integer CORE_SIZE = 2; | |||||
| private static final Integer MAX_SIZE = 2 * Runtime.getRuntime().availableProcessors(); | |||||
| private static CompletableFutureWithExecutor future; | |||||
| @Autowired | |||||
| private ThreadPoolService threadPoolService; | |||||
| @PostConstruct | |||||
| public void init() { | |||||
| future = this; | |||||
| } | |||||
| public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) { | |||||
| return CompletableFuture.supplyAsync(supplier, TtlExecutors.getTtlExecutorService(future.threadPoolService.getExecutorService(CORE_SIZE, MAX_SIZE))); | |||||
| } | |||||
| public static CompletableFuture<Void> runAsync(Runnable runnable) { | |||||
| return CompletableFuture.runAsync(runnable, TtlExecutors.getTtlExecutorService(future.threadPoolService.getExecutorService(CORE_SIZE, MAX_SIZE))); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,81 @@ | |||||
| package com.iformall.service.thread; | |||||
| import org.springframework.stereotype.Component; | |||||
| import java.util.concurrent.*; | |||||
| import java.util.concurrent.atomic.AtomicInteger; | |||||
| /** | |||||
| * 自定义线程池 | |||||
| * @author xmzhao71 | |||||
| * @date 2023-09-06 | |||||
| */ | |||||
| @Component | |||||
| public class ThreadPoolService { | |||||
| private final Integer coreSize = 2; | |||||
| private final Integer maxSize = 2 * Runtime.getRuntime().availableProcessors(); | |||||
| private final String prefix = "fm-thread-pool-"; | |||||
| private final AtomicInteger nextId = new AtomicInteger(); | |||||
| /** | |||||
| * 线程池对象 | |||||
| */ | |||||
| private volatile ThreadPoolExecutor service; | |||||
| private ThreadPoolService() { | |||||
| getExecutorService(Integer.MAX_VALUE, Integer.MAX_VALUE); | |||||
| } | |||||
| /** | |||||
| * 自定义线程名称,方便的出错的时候溯源 | |||||
| */ | |||||
| private final ThreadFactory namedThreadFactory = r -> new Thread(r, prefix + nextId.getAndIncrement()); | |||||
| /** | |||||
| * 创建获取线程池对象 | |||||
| * corePoolSize 线程池核心池的大小 | |||||
| * maximumPoolSize 线程池中允许的最大线程数量 | |||||
| * keepAliveTime 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间 | |||||
| * unit keepAliveTime 的时间单位 | |||||
| * workQueue 用来储存等待执行任务的队列 | |||||
| * threadFactory 创建线程的工厂类 | |||||
| * handler 拒绝策略类,当线程池数量达到上线并且workQueue队列长度达到上限时就需要对到来的任务做拒绝处理 | |||||
| * | |||||
| * @return 线程池 | |||||
| */ | |||||
| public synchronized ExecutorService getExecutorService(Integer coreSize, Integer maxSize) { | |||||
| if (service == null) { | |||||
| synchronized (ThreadPoolService.class) { | |||||
| if (service == null) { | |||||
| ThreadPoolExecutor executorService = new ThreadPoolExecutor( | |||||
| coreSize, | |||||
| maxSize, | |||||
| 1000L, | |||||
| TimeUnit.MILLISECONDS, | |||||
| new LinkedBlockingQueue<>(1024), | |||||
| namedThreadFactory, | |||||
| new ThreadPoolExecutor.AbortPolicy() | |||||
| ); | |||||
| executorService.allowCoreThreadTimeOut(true); | |||||
| this.service = executorService; | |||||
| } | |||||
| } | |||||
| } | |||||
| return service; | |||||
| } | |||||
| /** | |||||
| * 使用线程池创建线程并异步执行任务 | |||||
| * | |||||
| * @param r 任务 | |||||
| */ | |||||
| public void submitTask(Runnable r) { | |||||
| service.execute(r); | |||||
| } | |||||
| } | |||||
| @@ -308,7 +308,7 @@ | |||||
| <update id="updateStatusByPressCouponId" parameterType="com.iformall.domain.po.WxOrder"> | <update id="updateStatusByPressCouponId" parameterType="com.iformall.domain.po.WxOrder"> | ||||
| update wx_order set order_status=#{orderStatus} | update wx_order set order_status=#{orderStatus} | ||||
| where product_id=#{productId} | where product_id=#{productId} | ||||
| and press_end_date is not null and order_status in (6,7) | |||||
| and press_end_date is not null and order_status = 6 | |||||
| <if test=" null != tenantId and '' != tenantId"> | <if test=" null != tenantId and '' != tenantId"> | ||||
| and `tenant_id` = #{tenantId} | and `tenant_id` = #{tenantId} | ||||
| </if> | </if> | ||||