ソースを参照

活动自动启停

skyline 2 年 前
コミット
bb5295d1f6

+ 1 - 2
admin/src/main/java/com/kym/admin/AdminApplication.java

@@ -15,8 +15,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
 @Controller
 @EnableScheduling
 @SpringBootApplication
-@ComponentScan(value = {"com.kym"}, excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE)
-        , @ComponentScan.Filter(type = FilterType.REGEX, pattern = {"com.kym.service.miniapp.impl.StartDelayServiceImpl","com.kym.service.miniapp.impl.StopDelayServiceImpl"})})
+@ComponentScan(value = "com.kym")
 @MapperScan(basePackages = {"com.kym.mapper"})
 public class AdminApplication {
 

+ 148 - 0
admin/src/main/java/com/kym/admin/jobs/ActivityDelayJob.java

@@ -0,0 +1,148 @@
+package com.kym.admin.jobs;
+
+import com.baomidou.dynamic.datasource.annotation.DS;
+import com.kym.common.utils.CommUtil;
+import com.kym.entity.admin.Activity;
+import com.kym.entity.admin.Banner;
+import com.kym.entity.admin.delay.DelayActivity;
+import com.kym.service.admin.ActivityService;
+import com.kym.service.admin.BannerService;
+import com.kym.service.jobs.DelayService;
+import com.kym.service.jobs.DelayedItem;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.context.annotation.Scope;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.stereotype.Service;
+
+import java.util.concurrent.DelayQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * @author skyline
+ * @description 活动启闭延迟任务
+ * @date 2023-10-08 22:11
+ */
+@Service
+@Slf4j
+@DS("db-admin")
+@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) // 设置成单例
+public class ActivityDelayJob implements DelayService<DelayActivity> {
+
+    /**
+     * 预约订单队列
+     */
+    private final static DelayQueue<DelayedItem<DelayActivity>> DELAY_QUEUE = new DelayQueue<>();
+
+    private final ActivityService activityService;
+    private final BannerService bannerService;
+    /**
+     * 线程池
+     */
+    private final ExecutorService executor = Executors.newFixedThreadPool(1);
+
+    public ActivityDelayJob(ActivityService activityService, BannerService bannerService) {
+        this.activityService = activityService;
+        this.bannerService = bannerService;
+    }
+
+    public void initActivityDelayJob(Activity activity) {
+        // 将启动时间和结束时间组装延迟任务放入队列
+        var startJob = new DelayActivity(activity.getId(), activity.getName(), activity.getStartTime(), DelayActivity.TYPE_启动);
+        var endJob = new DelayActivity(activity.getId(), activity.getName(), activity.getEndTime(), DelayActivity.TYPE_结束);
+        addToDelayQueue(startJob);
+        addToDelayQueue(endJob);
+    }
+
+
+    @DS("db-admin")
+    // 这里不能使用@PostConstruct,在初始化完成后, bean 进入增强阶段, 所以这个阶段的任何AOP都是无效的,https://www.cnblogs.com/eternityz/p/15330069.html
+    @EventListener
+    public void init(ContextRefreshedEvent event) {
+        // 队列加载所有进行中和未开始的活动
+        var activity = activityService.lambdaQuery().in(Activity::getStatus, Activity.STATUS_进行中, Activity.STATUS_未开始).list();
+
+        // 启动队列
+        var delayStartActivityList = activity.stream().filter(a -> a.getStatus().equals(Activity.STATUS_未开始)).map(act -> new DelayActivity()
+                        .setId(act.getId())
+                        .setName(act.getName())
+                        .setExecuteTime(act.getStartTime())
+                        .setType(DelayActivity.TYPE_启动))
+                .toList();
+
+        // 结束队列
+        var delayEndActivityList = activity.stream().map(act -> new DelayActivity()
+                        .setId(act.getId())
+                        .setName(act.getName())
+                        .setExecuteTime(act.getEndTime())
+                        .setType(DelayActivity.TYPE_结束))
+                .toList();
+
+        var delayStartList = delayStartActivityList.stream().map(delay -> new DelayedItem<>(delay, delay.getExecuteTime())).toList();
+        var delayEndList = delayEndActivityList.stream().map(delay -> new DelayedItem<>(delay, delay.getExecuteTime())).toList();
+
+        DELAY_QUEUE.addAll(delayStartList);
+        DELAY_QUEUE.addAll(delayEndList);
+
+        // 开启线程处理队列消息
+        executor.execute(() -> {
+            ThreadLocal<Long> threadLocal = new ThreadLocal<>();
+            log.info("活动延迟启闭处理线程:{}", Thread.currentThread().getName());
+            DelayedItem<DelayActivity> delayedItem;
+            while (true) {
+                try {
+                    delayedItem = DELAY_QUEUE.take();
+                    var delayActivity = delayedItem.data;
+                    threadLocal.set(delayActivity.getId());
+                    if (delayActivity.getType().equals(DelayActivity.TYPE_启动)) {
+                        // 开始活动
+                        // 修改活动状态为已结束
+                        activityService.lambdaUpdate().set(Activity::getStatus, Activity.STATUS_进行中).eq(Activity::getId, delayActivity.getId()).update();
+                        // 修改banner状态为失效
+                        bannerService.lambdaUpdate().set(Banner::getStatus, Banner.STATUS_有效).eq(Banner::getActivityId, delayActivity.getId()).update();
+                    } else {
+                        // 停止活动
+                        // 修改活动状态为已结束
+                        activityService.lambdaUpdate().set(Activity::getStatus, Activity.STATUS_已结束).eq(Activity::getId, delayActivity.getId()).update();
+                        // 修改banner状态为失效
+                        bannerService.lambdaUpdate().set(Banner::getStatus, Banner.STATUS_无效).eq(Banner::getActivityId, delayActivity.getId()).update();
+                    }
+                    // 线程休眠100ms
+                    Thread.sleep(100);
+                } catch (Exception e) {
+                    if (e instanceof InterruptedException) {
+                        log.error("活动到期停止队列take异常", e);
+                    } else {
+                        log.info("活动到期停止,主活动id:{}", threadLocal.get(), e);
+                    }
+                } finally {
+                    threadLocal.remove();
+                }
+            }
+        });
+
+    }
+
+
+    @Override
+    public boolean addToOrderDelayQueue(DelayedItem<DelayActivity> delayedItem) {
+        return DELAY_QUEUE.add(delayedItem);
+    }
+
+    @Override
+    public boolean addToDelayQueue(DelayActivity activity) {
+        DelayedItem<DelayActivity> orderDelayed = new DelayedItem<>(activity, activity.getExecuteTime());
+        return DELAY_QUEUE.add(orderDelayed);
+    }
+
+
+    @Override
+    public boolean removeFromOrderDelayQueue(Object activityId) {
+        if (CommUtil.isEmptyOrNull(activityId)) {
+            return false;
+        }
+        return DELAY_QUEUE.removeIf(queue -> queue.data.getId().equals(activityId));
+    }
+}

+ 47 - 0
entity/src/main/java/com/kym/entity/admin/delay/DelayActivity.java

@@ -0,0 +1,47 @@
+package com.kym.entity.admin.delay;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.*;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 延迟队列使用活动对象
+ *
+ * @author skyline
+ * @since 2023-08-08
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Accessors(chain = true)
+public class DelayActivity implements Serializable {
+
+    public static String TYPE_启动 = "start";
+    public static String TYPE_结束 = "end";
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主活动id
+     */
+    private Long id;
+
+    /**
+     * 活动名称
+     */
+    private String name;
+    /**
+     * 开始或者结束时间
+     */
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime executeTime;
+
+    /**
+     * 类型:启动或结束
+     */
+    private String type;
+
+}

+ 7 - 7
service/src/main/java/com/kym/service/miniapp/impl/StartDelayServiceImpl.java → miniapp/src/main/java/com/kym/miniapp/jobs/StartChargeDelayJob.java

@@ -1,13 +1,13 @@
-package com.kym.service.miniapp.impl;
+package com.kym.miniapp.jobs;
 
 import com.baomidou.dynamic.datasource.annotation.DS;
 import com.kym.common.utils.CommUtil;
 import com.kym.entity.miniapp.ChargeOrder;
 import com.kym.entity.miniapp.delay.DelayChargeOrder;
+import com.kym.service.jobs.DelayService;
+import com.kym.service.jobs.DelayedItem;
 import com.kym.service.miniapp.ChargeOrderService;
 import com.kym.service.miniapp.ChargeService;
-import com.kym.service.miniapp.DelayService;
-import com.kym.service.queue.DelayedItem;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.config.ConfigurableBeanFactory;
 import org.springframework.context.annotation.Scope;
@@ -21,14 +21,14 @@ import java.util.concurrent.Executors;
 
 /**
  * @author skyline
- * @description
+ * @description 启动充电延迟任务(预约充电)
  * @date 2023-10-08 22:11
  */
 @Service
 @Slf4j
 @DS("db-miniapp")
 @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) // 设置成单例
-public class StartDelayServiceImpl implements DelayService<DelayChargeOrder> {
+public class StartChargeDelayJob implements DelayService<DelayChargeOrder> {
 
 
     /**
@@ -42,7 +42,7 @@ public class StartDelayServiceImpl implements DelayService<DelayChargeOrder> {
      */
     private final ExecutorService executor = Executors.newFixedThreadPool(2);
 
-    public StartDelayServiceImpl(ChargeOrderService chargeOrderService, ChargeService chargeService) {
+    public StartChargeDelayJob(ChargeOrderService chargeOrderService, ChargeService chargeService) {
         this.chargeOrderService = chargeOrderService;
         this.chargeService = chargeService;
     }
@@ -110,7 +110,7 @@ public class StartDelayServiceImpl implements DelayService<DelayChargeOrder> {
     }
 
     @Override
-    public boolean removeFromOrderDelayQueue(String startChargeSeq) {
+    public boolean removeFromOrderDelayQueue(Object startChargeSeq) {
         if (CommUtil.isEmptyOrNull(startChargeSeq)) {
             return false;
         }

+ 7 - 7
service/src/main/java/com/kym/service/miniapp/impl/StopDelayServiceImpl.java → miniapp/src/main/java/com/kym/miniapp/jobs/StopChargeDelayJob.java

@@ -1,4 +1,4 @@
-package com.kym.service.miniapp.impl;
+package com.kym.miniapp.jobs;
 
 import com.baomidou.dynamic.datasource.annotation.DS;
 import com.kym.common.exception.BusinessException;
@@ -7,8 +7,8 @@ import com.kym.entity.miniapp.ChargeOrder;
 import com.kym.entity.miniapp.delay.DelayChargeOrder;
 import com.kym.service.miniapp.ChargeOrderService;
 import com.kym.service.miniapp.ChargeService;
-import com.kym.service.miniapp.DelayService;
-import com.kym.service.queue.DelayedItem;
+import com.kym.service.jobs.DelayService;
+import com.kym.service.jobs.DelayedItem;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.config.ConfigurableBeanFactory;
 import org.springframework.context.annotation.Scope;
@@ -22,14 +22,14 @@ import java.util.concurrent.Executors;
 
 /**
  * @author skyline
- * @description
+ * @description 停止充电延迟任务(预约充电)
  * @date 2023-10-08 22:11
  */
 @Service
 @Slf4j
 @DS("db-miniapp")
 @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) // 设置成单例
-public class StopDelayServiceImpl implements DelayService<DelayChargeOrder> {
+public class StopChargeDelayJob implements DelayService<DelayChargeOrder> {
 
 
     /**
@@ -43,7 +43,7 @@ public class StopDelayServiceImpl implements DelayService<DelayChargeOrder> {
      */
     private final ExecutorService executor = Executors.newFixedThreadPool(2);
 
-    public StopDelayServiceImpl(ChargeOrderService chargeOrderService, ChargeService chargeService) {
+    public StopChargeDelayJob(ChargeOrderService chargeOrderService, ChargeService chargeService) {
         this.chargeOrderService = chargeOrderService;
         this.chargeService = chargeService;
     }
@@ -122,7 +122,7 @@ public class StopDelayServiceImpl implements DelayService<DelayChargeOrder> {
     }
 
     @Override
-    public boolean removeFromOrderDelayQueue(String startChargeSeq) {
+    public boolean removeFromOrderDelayQueue(Object startChargeSeq) {
         if (CommUtil.isEmptyOrNull(startChargeSeq)) {
             return false;
         }

+ 3 - 5
service/src/main/java/com/kym/service/miniapp/DelayService.java → service/src/main/java/com/kym/service/jobs/DelayService.java

@@ -1,6 +1,4 @@
-package com.kym.service.miniapp;
-
-import com.kym.service.queue.DelayedItem;
+package com.kym.service.jobs;
 
 /**
  * @author skyline
@@ -27,8 +25,8 @@ public interface DelayService<T> {
     /**
      * 移除指定的延迟对象从延时队列中
      *
-     * @param startChargeSeq
+     * @param id
      */
-    boolean removeFromOrderDelayQueue(String startChargeSeq);
+    boolean removeFromOrderDelayQueue(Object id);
 
 }

+ 1 - 1
service/src/main/java/com/kym/service/queue/DelayedItem.java → service/src/main/java/com/kym/service/jobs/DelayedItem.java

@@ -1,4 +1,4 @@
-package com.kym.service.queue;
+package com.kym.service.jobs;
 
 import jakarta.validation.constraints.NotNull;
 

+ 1 - 0
service/src/main/java/com/kym/service/miniapp/impl/ChargeServiceImpl.java

@@ -19,6 +19,7 @@ import com.kym.service.admin.EquipmentInfoService;
 import com.kym.service.admin.EquipmentRelationService;
 import com.kym.service.enplus.EnPlusService;
 import com.kym.service.miniapp.*;
+import com.kym.service.jobs.DelayService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeanUtils;

+ 0 - 14
service/src/main/java/com/kym/service/queue/ChargeDelayConsumer.java

@@ -1,14 +0,0 @@
-package com.kym.service.queue;
-
-/**
- * @author skyline
- * @description
- * @date 2023-10-08 19:20
- */
-
-public class ChargeDelayConsumer implements Runnable{
-    @Override
-    public void run() {
-
-    }
-}