Browse Source

feate: 设备点位缓存数据获取,提升效率,优化时序数据库添加年份字段

jackzhou 4 months ago
parent
commit
f4833b4a4d
13 changed files with 205 additions and 24 deletions
  1. 4 1
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/bean/SensorAlarmUser.java
  2. 2 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/check/DefaultSensorAlarmChecker.java
  3. 3 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/config/JfcloudColdChainConstants.java
  4. 42 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/config/JfcloudRedisCacheConfig.java
  5. 32 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/config/JfcloudRedisCacheService.java
  6. 26 3
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/service/JfcloudSensorDataService.java
  7. 3 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitortarget/service/MonitorTargetService.java
  8. 14 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitortarget/service/impl/MonitorTargetServiceImpl.java
  9. 7 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitortargetregion/entity/MonitorTargetRegion.java
  10. 1 0
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitortargetregion/mapper/MonitorTargetRegionMapper.java
  11. 38 6
      snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitortargetregion/service/impl/MonitorTargetRegionServiceImpl.java
  12. 2 14
      snowy-web-app/src/main/java/vip/xiaonuo/Application.java
  13. 31 0
      snowy-web-app/src/main/java/vip/xiaonuo/core/config/JfcloudApplication.java

+ 4 - 1
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/bean/SensorAlarmUser.java

@@ -2,6 +2,8 @@ package vip.xiaonuo.coldchain.core.alarm.bean;
 
 import lombok.Data;
 
+import java.io.Serializable;
+
 /**
  * @author jackzhou
  * @version 1.0
@@ -10,7 +12,8 @@ import lombok.Data;
  * @date 2024/11/26 11:05:33
  */
 @Data
-public class SensorAlarmUser {
+public class SensorAlarmUser implements Serializable {
+    private static final long serialVersionUID = 1L;
     public SensorAlarmUser() {
     }
 

+ 2 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/alarm/service/check/DefaultSensorAlarmChecker.java

@@ -126,6 +126,8 @@ public class DefaultSensorAlarmChecker implements SensorAlarmChecker {
         // 设置报警人机构 所属用户和机构
         sensorAlarm.setCreateUser(monitorTargetRegion.getCreateUser());
         sensorAlarm.setCreateOrg(monitorTargetRegion.getCreateOrg());
+        // 添加房间字段
+        sensorAlarm.setRoomName(monitorTargetRegion.getRoomName());
         log.warn("触发报警: 类型: {},详细报警内容 : {}", alarmType, alarmMessage);
         // 发布报警事件
         applicationEventPublisher.publishEvent(new SensorAlarmEvent(this, sensorAlarm));

+ 3 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/config/JfcloudColdChainConstants.java

@@ -34,6 +34,9 @@ public interface JfcloudColdChainConstants {
      */
     String INFLUXDB_DEFAULT_MEASUREMENT_NAME = "sensor_data";
 
+
+    String MONITORTARGETREGION_CACHE_NAME = "monitorTargetRegion";
+
     /**
      * 数据上报队列最大容量
      */

+ 42 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/config/JfcloudRedisCacheConfig.java

@@ -0,0 +1,42 @@
+package vip.xiaonuo.coldchain.core.config;
+
+/**
+ * @author jackzhou
+ * @version 1.0
+ * @project jfcloud-coldchain
+ * @description
+ * @date 2025/1/6 12:41:47
+ */
+
+import org.springframework.cache.CacheManager;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.cache.RedisCacheConfiguration;
+import org.springframework.data.redis.cache.RedisCacheManager;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+
+import java.time.Duration;
+
+@Configuration
+@EnableCaching
+public class JfcloudRedisCacheConfig {
+
+    @Bean
+    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
+        // 定义缓存过期策略
+        RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
+                .entryTtl(Duration.ofHours(48))  // 默认24小时过期
+                .disableCachingNullValues();  // 禁用缓存null值
+
+        // 针对不同缓存名称设置不同过期时间
+        RedisCacheConfiguration monitorTargetRegionCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
+                .entryTtl(Duration.ofMinutes(24))  // 特定缓存的过期时间为 30 分钟
+                .disableCachingNullValues();
+
+        return RedisCacheManager.builder(redisConnectionFactory)
+                .cacheDefaults(defaultCacheConfig)
+                .withCacheConfiguration("monitorTargetRegion", monitorTargetRegionCacheConfig)
+                .build();
+    }
+}

+ 32 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/config/JfcloudRedisCacheService.java

@@ -0,0 +1,32 @@
+package vip.xiaonuo.coldchain.core.config;
+
+/**
+ * @author jackzhou
+ * @version 1.0
+ * @project jfcloud-coldchain
+ * @description
+ * @date 2025/1/6 12:48:37
+ */
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.Cache;
+import org.springframework.cache.CacheManager;
+import org.springframework.stereotype.Service;
+
+@Service
+@Slf4j
+public class JfcloudRedisCacheService {
+    @Autowired
+    private CacheManager cacheManager;
+
+    public void evictMonitorTargetRegionCache(String deviceCode, Integer sensorNo) {
+        // 获取指定缓存
+        Cache cache = cacheManager.getCache(JfcloudColdChainConstants.MONITORTARGETREGION_CACHE_NAME);
+        if (cache != null) {
+            String key = deviceCode + ":" + sensorNo;
+            cache.evict(key);
+            log.info("Cache evicted for key: " + key);
+        }
+    }
+}
+

+ 26 - 3
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/core/service/JfcloudSensorDataService.java

@@ -102,23 +102,46 @@ public class JfcloudSensorDataService extends JfcloudFluxDataService<SensorData>
      * 格式化开始时间,若只有日期部分则补充时间部分为 00:00:00
      */
     private String formatStartTime(String startTimeStr) {
-        if (startTimeStr.length() == 10) {
-            return startTimeStr + " 00:00:00";
+        if (startTimeStr == null || startTimeStr.isEmpty()) {
+            throw new IllegalArgumentException("startTimeStr cannot be null or empty");
+        }
+        if (startTimeStr.length() == 4 && startTimeStr.matches("\\d{4}")) {
+            return startTimeStr + "-01-01 00:00:00";  // Default to January 1st, 00:00:00
+        }
+        if (startTimeStr.length() == 7 && startTimeStr.matches("\\d{4}-\\d{2}")) {
+            return startTimeStr + "-01 00:00:00";  // Default to the first day of the month, 00:00:00
+        }
+        if (startTimeStr.length() == 10 && startTimeStr.matches("\\d{4}-\\d{2}-\\d{2}")) {
+            return startTimeStr + " 00:00:00";  // Add time if it's missing
         }
         return startTimeStr;
     }
 
+
+    /**
+     * 格式化结束时间,若只有日期部分则补充时间部分为 23:59:59
+     */
     /**
      * 格式化结束时间,若只有日期部分则补充时间部分为 23:59:59
      */
     private String formatEndTime(String endTimeStr) {
-        if (endTimeStr.length() == 10) {
+        if (endTimeStr == null || endTimeStr.isEmpty()) {
+            throw new IllegalArgumentException("endTimeStr cannot be null or empty");
+        }
+        if (endTimeStr.length() == 4 && endTimeStr.matches("\\d{4}")) {
+            return endTimeStr + "-12-31 23:59:59";
+        }
+        if (endTimeStr.length() == 7 && endTimeStr.matches("\\d{4}-\\d{2}")) {
+            return endTimeStr + "-31 23:59:59";
+        }
+        if (endTimeStr.length() == 10 && endTimeStr.matches("\\d{4}-\\d{2}-\\d{2}")) {
             return endTimeStr + " 23:59:59";
         }
         return endTimeStr;
     }
 
 
+
     /**
      * Converts a time string in "yyyy-MM-dd HH:mm:ss" format to ISO 8601 format.
      *

+ 3 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitortarget/service/MonitorTargetService.java

@@ -17,6 +17,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
 import vip.xiaonuo.coldchain.modular.monitortarget.entity.MonitorTarget;
 import vip.xiaonuo.coldchain.modular.monitortarget.enums.MonitorStatusEnum;
 import vip.xiaonuo.coldchain.modular.monitortarget.param.*;
+import vip.xiaonuo.coldchain.modular.targetroom.entity.TargetRoom;
 
 import java.util.Date;
 import java.util.List;
@@ -135,4 +136,6 @@ public interface MonitorTargetService extends IService<MonitorTarget> {
      * 带房间的编辑监控对象
      */
     void editWithRoom(MonitorTargetEditWithRoomParam monitorTargetEditWithRoomParam);
+
+    TargetRoom getRoomByMonitorTargetId(String monitorTargetId);
 }

+ 14 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitortarget/service/impl/MonitorTargetServiceImpl.java

@@ -35,6 +35,7 @@ import vip.xiaonuo.coldchain.modular.monitortarget.param.*;
 import vip.xiaonuo.coldchain.modular.monitortarget.service.MonitorTargetService;
 import vip.xiaonuo.coldchain.modular.monitortargetregion.entity.MonitorTargetRegion;
 import vip.xiaonuo.coldchain.modular.monitortargetregion.service.MonitorTargetRegionService;
+import vip.xiaonuo.coldchain.modular.targetroom.entity.TargetRoom;
 import vip.xiaonuo.coldchain.modular.targetroom.service.TargetRoomService;
 import vip.xiaonuo.common.enums.CommonDeleteFlagEnum;
 import vip.xiaonuo.common.enums.CommonSortOrderEnum;
@@ -144,6 +145,19 @@ public class MonitorTargetServiceImpl extends ServiceImpl<MonitorTargetMapper, M
         this.updateById(monitorTarget);
     }
 
+    @Override
+    public TargetRoom getRoomByMonitorTargetId(String monitorTargetId) {
+        if(Objects.nonNull(monitorTargetId)){
+            MonitorTarget monitorTarget = this.getById(monitorTargetId);
+            if(Objects.nonNull(monitorTarget)){
+                String roomId = monitorTarget.getRoomId();
+                return targetRoomService.getById(roomId);
+            }
+        }
+        return null;
+    }
+
+
     @Transactional(rollbackFor = Exception.class)
     @Override
     public void delete(List<MonitorTargetIdParam> monitorTargetIdParamList) {

+ 7 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitortargetregion/entity/MonitorTargetRegion.java

@@ -195,6 +195,13 @@ public class MonitorTargetRegion extends OrgEntity {
     @Schema(description = "勿扰结束时间")
     private String ignoreEndTime;
 
+    /**
+     * 房间名
+     */
+    @Schema(description = "房间名")
+    @TableField(exist = false)
+    private String roomName;
+
     public void setTemperatureUp(Float temperatureUp) {
         this.temperatureUp = temperatureUp;
     }

+ 1 - 0
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitortargetregion/mapper/MonitorTargetRegionMapper.java

@@ -13,6 +13,7 @@
 package vip.xiaonuo.coldchain.modular.monitortargetregion.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Param;
 import vip.xiaonuo.coldchain.modular.monitortargetregion.entity.MonitorTargetRegion;
 
 /**

+ 38 - 6
snowy-plugin/snowy-plugin-coldchain/src/main/java/vip/xiaonuo/coldchain/modular/monitortargetregion/service/impl/MonitorTargetRegionServiceImpl.java

@@ -26,13 +26,16 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import vip.xiaonuo.auth.core.pojo.SaBaseLoginUser;
 import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
+import vip.xiaonuo.coldchain.core.config.JfcloudColdChainConstants;
+import vip.xiaonuo.coldchain.core.config.JfcloudRedisCacheService;
 import vip.xiaonuo.coldchain.modular.monitordevice.entity.MonitorDevice;
 import vip.xiaonuo.coldchain.modular.monitordevice.service.MonitorDeviceService;
-import vip.xiaonuo.coldchain.modular.monitortarget.entity.MonitorTarget;
 import vip.xiaonuo.coldchain.modular.monitortarget.service.MonitorTargetService;
 import vip.xiaonuo.coldchain.modular.monitortargetregion.entity.MonitorTargetRegion;
 import vip.xiaonuo.coldchain.modular.monitortargetregion.enums.MonitorTargetRegionEvent;
@@ -42,6 +45,7 @@ import vip.xiaonuo.coldchain.modular.monitortargetregion.param.MonitorTargetRegi
 import vip.xiaonuo.coldchain.modular.monitortargetregion.param.MonitorTargetRegionIdParam;
 import vip.xiaonuo.coldchain.modular.monitortargetregion.param.MonitorTargetRegionPageParam;
 import vip.xiaonuo.coldchain.modular.monitortargetregion.service.MonitorTargetRegionService;
+import vip.xiaonuo.coldchain.modular.targetroom.entity.TargetRoom;
 import vip.xiaonuo.common.enums.CommonDeleteFlagEnum;
 import vip.xiaonuo.common.enums.CommonSortOrderEnum;
 import vip.xiaonuo.common.exception.CommonException;
@@ -50,6 +54,7 @@ import vip.xiaonuo.common.page.CommonPageRequest;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Function;
@@ -63,11 +68,14 @@ import java.util.stream.Collectors;
  * @date 2024/11/13 16:57
  **/
 @Service
+@Slf4j
 public class MonitorTargetRegionServiceImpl extends ServiceImpl<MonitorTargetRegionMapper, MonitorTargetRegion> implements MonitorTargetRegionService {
     @Resource
     private MonitorDeviceService monitorDeviceService;
     @Resource
     private MonitorTargetService monitorTargetService;
+    @Resource
+    private JfcloudRedisCacheService jfcloudRedisCacheService;
 
     @Override
     public Page<MonitorTargetRegion> page(MonitorTargetRegionPageParam monitorTargetRegionPageParam) {
@@ -103,7 +111,6 @@ public class MonitorTargetRegionServiceImpl extends ServiceImpl<MonitorTargetReg
     public void add(MonitorTargetRegionAddParam monitorTargetRegionAddParam) {
         MonitorDevice monitorDevice = monitorDeviceService.queryEntity(monitorTargetRegionAddParam.getMonitorDeviceId());
         Integer sensorCount = monitorDevice.getSensorCount();
-        MonitorTarget monitorTarget = monitorTargetService.queryEntity(monitorTargetRegionAddParam.getMonitorTargetId());
         LambdaQueryWrapper<MonitorTargetRegion> queryWrapper = new LambdaQueryWrapper<>();
         queryWrapper.eq(MonitorTargetRegion::getMonitorDeviceId,monitorTargetRegionAddParam.getMonitorDeviceId());
         List<MonitorTargetRegion> list = list(queryWrapper);
@@ -124,8 +131,12 @@ public class MonitorTargetRegionServiceImpl extends ServiceImpl<MonitorTargetReg
     @Override
     public void edit(MonitorTargetRegionEditParam monitorTargetRegionEditParam) {
         MonitorTargetRegion monitorTargetRegion = this.queryEntity(monitorTargetRegionEditParam.getId());
+        if(Objects.isNull(monitorTargetRegion)) {
+            return;
+        }
         BeanUtil.copyProperties(monitorTargetRegionEditParam, monitorTargetRegion);
         this.updateById(monitorTargetRegion);
+        jfcloudRedisCacheService.evictMonitorTargetRegionCache(monitorTargetRegion.getDeviceCode(),monitorTargetRegion.getSensorRoute());
         CommonDataChangeEventCenter.doUpdateWithData(MonitorTargetRegionEvent.MONITOR_TARGET_REGION_EDIT_EVENT, JSONUtil.createArray().put(monitorTargetRegion));
     }
 
@@ -217,13 +228,35 @@ public class MonitorTargetRegionServiceImpl extends ServiceImpl<MonitorTargetReg
     }
 
     @Override
+    @Cacheable(value = JfcloudColdChainConstants.MONITORTARGETREGION_CACHE_NAME, key = "#deviceCode + ':' + #sensorNo", unless = "#result == null")
     public MonitorTargetRegion findOneByDeviceCodeAndSensorNo(String deviceCode, Integer sensorNo) {
+        // 查询条件封装
         LambdaQueryWrapper<MonitorTargetRegion> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.eq(MonitorTargetRegion::getDeviceCode, deviceCode).eq(MonitorTargetRegion::getSensorRoute,sensorNo)
-            .eq(MonitorTargetRegion::getDeleteFlag,CommonDeleteFlagEnum.NOT_DELETE);;
-        return getOne(queryWrapper);
+        queryWrapper.eq(MonitorTargetRegion::getDeviceCode, deviceCode)
+                .eq(MonitorTargetRegion::getSensorRoute, sensorNo)
+                .eq(MonitorTargetRegion::getDeleteFlag, CommonDeleteFlagEnum.NOT_DELETE);
+        MonitorTargetRegion monitorTargetRegion = getOne(queryWrapper);
+        if (Objects.isNull(monitorTargetRegion)) {
+            log.info("未找到设备编号 [{}] 和传感器编号 [{}] 对应的监控目标区域。", deviceCode, sensorNo);
+            return null;
+        }
+        // 获取房间名称
+        String monitorTargetId = monitorTargetRegion.getMonitorTargetId();
+        if (Objects.nonNull(monitorTargetId)) {
+            TargetRoom roomName = monitorTargetService.getRoomByMonitorTargetId(monitorTargetId);
+            if (Objects.nonNull(roomName)) {
+                monitorTargetRegion.setRoomName(roomName.getName());
+                log.info("成功获取房间名称 [{}],并设置到监控目标区域。", roomName.getName());
+            } else {
+                log.info("未找到监控目标 ID [{}] 对应的房间名称。", monitorTargetId);
+            }
+        } else {
+            log.info("监控目标区域 [{}] 的监控目标 ID 为空,跳过房间名称查询。", monitorTargetRegion.getId());
+        }
+        return monitorTargetRegion;
     }
 
+
     @Override
     public Integer getUsedSensorCount() {
         SaBaseLoginUser loginUser = StpLoginUserUtil.getLoginUser();
@@ -256,7 +289,6 @@ public class MonitorTargetRegionServiceImpl extends ServiceImpl<MonitorTargetReg
     private void fillParentLocationInfo(List<MonitorTargetRegion> resourceList) {
         if (CollUtil.isNotEmpty(resourceList)) {
             List<MonitorTargetRegion> locationTypes = resourceList.stream().filter(distinctByKey(MonitorTargetRegion::getParentId)).collect(Collectors.toList());
-
             List<String> parentIds = null;
             if (CollUtil.isNotEmpty(locationTypes)) {
                 parentIds = CollUtil.newArrayList();

+ 2 - 14
snowy-web-app/src/main/java/vip/xiaonuo/Application.java

@@ -14,17 +14,12 @@ package vip.xiaonuo;
 
 import lombok.SneakyThrows;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.Banner;
 import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.context.ConfigurableApplicationContext;
 import org.springframework.core.env.Environment;
-import org.springframework.scheduling.annotation.EnableAsync;
-import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RestController;
-import vip.xiaonuo.coldchain.core.alarm.mapper.SensorAlarmMapper;
+import vip.xiaonuo.core.config.JfcloudApplication;
 
 /**
  * SpringBoot方式启动类
@@ -33,15 +28,8 @@ import vip.xiaonuo.coldchain.core.alarm.mapper.SensorAlarmMapper;
  * @date 2021/12/18 16:57
  */
 @Slf4j
-@RestController
-@SpringBootApplication(scanBasePackages = {"vip.xiaonuo", "com.github.jfcloud"})
-@EnableAsync
-@EnableScheduling
+@JfcloudApplication
 public class Application {
-
-    @Autowired
-    SensorAlarmMapper sensorAlarmMapper;
-
     /* 解决druid 日志报错:discard long time none received connection:xxx */
     static {
         System.setProperty("druid.mysql.usePingMethod", "false");

+ 31 - 0
snowy-web-app/src/main/java/vip/xiaonuo/core/config/JfcloudApplication.java

@@ -0,0 +1,31 @@
+package vip.xiaonuo.core.config;
+
+/**
+ * @author jackzhou
+ * @version 1.0
+ * @project jfcloud-coldchain
+ * @description
+ * @date 2025/1/6 12:39:35
+ */
+
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@RestController
+@SpringBootApplication(scanBasePackages = {"vip.xiaonuo", "com.github.jfcloud"})
+@EnableAsync
+@EnableScheduling
+@EnableCaching
+public @interface JfcloudApplication {
+}
+