diff --git a/README.md b/README.md index 26f2d9865..7c4d608f7 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,18 @@ - 密码: 123456 +#### 开发环境 + +- JDK:8 + +- IDE:IntelliJ IDEA (后端) + +- IDE:JetBrains WebStorm (前端) + +- 依赖管理:Maven + +- 数据库:MySQL 5.5.59 + #### 系统功能模块 - 用户管理 提供用户的相关配置 @@ -36,6 +48,7 @@ - redis管理 将redis的操作可视化,提供对redis的基本操作 - redis限流 对系统的流量进行控制,由[everhopingandwaiting](https://github.com/everhopingandwaiting)提供 - SQL监控 采用 druid 监控数据库访问性能 +- 定时任务管理 整合quartz做定时任务 - 三方工具: 邮件工具,sm.ms免费图床,支付宝支付,七牛云存储 - 富文本编辑器 diff --git a/sql/eladmin.sql b/sql/eladmin.sql index f11e8af41..68f040197 100644 --- a/sql/eladmin.sql +++ b/sql/eladmin.sql @@ -11,7 +11,7 @@ Target Server Version : 50559 File Encoding : 65001 - Date: 06/01/2019 13:09:59 + Date: 08/01/2019 16:17:02 */ SET NAMES utf8mb4; @@ -71,7 +71,7 @@ CREATE TABLE `log` ( `time` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 4553 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; +) ENGINE = InnoDB AUTO_INCREMENT = 5066 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; -- ---------------------------- -- Table structure for menu @@ -88,7 +88,7 @@ CREATE TABLE `menu` ( `icon` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `path` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 25 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; +) ENGINE = InnoDB AUTO_INCREMENT = 29 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; -- ---------------------------- -- Records of menu @@ -105,17 +105,19 @@ INSERT INTO `menu` VALUES (9, '2018-12-18 15:19:34', b'1', 'SQL监控', NULL, 6, INSERT INTO `menu` VALUES (10, '2018-12-19 13:38:16', b'0', '组件管理', NULL, 0, 50, 'zujian', 'components'); INSERT INTO `menu` VALUES (11, '2018-12-19 13:38:49', b'0', '图标库', 'components/IconSelect', 10, 51, 'icon', 'icon'); INSERT INTO `menu` VALUES (12, '2018-12-24 20:37:35', b'0', '实时控制台', 'monitor/log/msg', 6, 13, 'codeConsole', 'msg'); -INSERT INTO `menu` VALUES (13, '2018-12-27 10:11:26', b'0', '三方工具', '', 0, 20, 'tools', 'tools'); -INSERT INTO `menu` VALUES (14, '2018-12-27 10:13:09', b'0', '邮件工具', 'tools/email/index', 13, 21, 'email', 'email'); +INSERT INTO `menu` VALUES (13, '2018-12-27 10:11:26', b'0', '三方工具', '', 0, 30, 'tools', 'tools'); +INSERT INTO `menu` VALUES (14, '2018-12-27 10:13:09', b'0', '邮件工具', 'tools/email/index', 13, 31, 'email', 'email'); INSERT INTO `menu` VALUES (15, '2018-12-27 11:58:25', b'0', '富文本', 'components/Editor', 10, 52, 'fwb', 'tinymce'); -INSERT INTO `menu` VALUES (16, '2018-12-28 09:36:53', b'0', 'SM.MS图床', 'tools/picture/index', 13, 22, 'image', 'pictures'); +INSERT INTO `menu` VALUES (16, '2018-12-28 09:36:53', b'0', 'SM.MS图床', 'tools/picture/index', 13, 32, 'image', 'pictures'); INSERT INTO `menu` VALUES (17, '2018-12-28 15:09:49', b'1', '项目地址', '', 0, 0, 'github', 'https://github.com/elunez/eladmin'); -INSERT INTO `menu` VALUES (18, '2018-12-31 11:12:15', b'0', '七牛云存储', 'tools/qiniu/index', 13, 23, 'qiniu', 'qiniu'); -INSERT INTO `menu` VALUES (19, '2018-12-31 14:52:38', b'0', '支付宝工具', 'tools/aliPay/index', 13, 24, 'alipay', 'aliPay'); -INSERT INTO `menu` VALUES (21, '2019-01-04 16:22:03', b'0', '多级菜单', '', 0, 900, 'menu', 'menu1'); -INSERT INTO `menu` VALUES (22, '2019-01-04 16:23:29', b'0', '二级菜单1', '', 21, 999, 'menu', 'menu1-1'); -INSERT INTO `menu` VALUES (23, '2019-01-04 16:23:57', b'0', '二级菜单2', '', 21, 999, 'menu', 'menu1-2'); -INSERT INTO `menu` VALUES (24, '2019-01-04 16:24:48', b'1', '三级菜单', '', 22, 999, 'chain', 'https://github.com/elunez/eladmin'); +INSERT INTO `menu` VALUES (18, '2018-12-31 11:12:15', b'0', '七牛云存储', 'tools/qiniu/index', 13, 33, 'qiniu', 'qiniu'); +INSERT INTO `menu` VALUES (19, '2018-12-31 14:52:38', b'0', '支付宝工具', 'tools/aliPay/index', 13, 34, 'alipay', 'aliPay'); +INSERT INTO `menu` VALUES (21, '2019-01-04 16:22:03', b'0', '多级菜单', '', 0, 900, 'menu', 'nested'); +INSERT INTO `menu` VALUES (22, '2019-01-04 16:23:29', b'0', '二级菜单1', 'nested/menu1/index', 21, 999, 'menu', 'menu1'); +INSERT INTO `menu` VALUES (23, '2019-01-04 16:23:57', b'0', '二级菜单2', 'nested/menu2/index', 21, 999, 'menu', 'menu2'); +INSERT INTO `menu` VALUES (24, '2019-01-04 16:24:48', b'0', '三级菜单1', 'nested/menu1/menu1-1', 22, 999, 'menu', 'menu1-1'); +INSERT INTO `menu` VALUES (27, '2019-01-07 17:27:32', b'0', '三级菜单2', 'nested/menu1/menu1-2', 22, 999, 'menu', 'menu1-2'); +INSERT INTO `menu` VALUES (28, '2019-01-07 20:34:40', b'0', '定时任务', 'system/timing/index', 1, 6, 'timing', 'timing'); -- ---------------------------- -- Table structure for menus_roles @@ -156,6 +158,8 @@ INSERT INTO `menus_roles` VALUES (21, 1); INSERT INTO `menus_roles` VALUES (22, 1); INSERT INTO `menus_roles` VALUES (23, 1); INSERT INTO `menus_roles` VALUES (24, 1); +INSERT INTO `menus_roles` VALUES (27, 1); +INSERT INTO `menus_roles` VALUES (28, 1); INSERT INTO `menus_roles` VALUES (1, 2); INSERT INTO `menus_roles` VALUES (2, 2); INSERT INTO `menus_roles` VALUES (3, 2); @@ -174,6 +178,8 @@ INSERT INTO `menus_roles` VALUES (21, 2); INSERT INTO `menus_roles` VALUES (22, 2); INSERT INTO `menus_roles` VALUES (23, 2); INSERT INTO `menus_roles` VALUES (24, 2); +INSERT INTO `menus_roles` VALUES (27, 2); +INSERT INTO `menus_roles` VALUES (28, 2); -- ---------------------------- -- Table structure for permission @@ -186,7 +192,7 @@ CREATE TABLE `permission` ( `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `pid` int(11) NOT NULL, PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 35 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; +) ENGINE = InnoDB AUTO_INCREMENT = 40 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; -- ---------------------------- -- Records of permission @@ -221,6 +227,11 @@ INSERT INTO `permission` VALUES (30, '菜单查询', '2018-12-28 17:34:41', 'MEN INSERT INTO `permission` VALUES (31, '菜单创建', '2018-12-28 17:34:52', 'MENU_CREATE', 29); INSERT INTO `permission` VALUES (32, '菜单编辑', '2018-12-28 17:35:20', 'MENU_EDIT', 29); INSERT INTO `permission` VALUES (33, '菜单删除', '2018-12-28 17:35:29', 'MENU_DELETE', 29); +INSERT INTO `permission` VALUES (35, '定时任务管理', '2019-01-08 14:59:57', 'JOB_ALL', 0); +INSERT INTO `permission` VALUES (36, '任务查询', '2019-01-08 15:00:09', 'JOB_SELECT', 35); +INSERT INTO `permission` VALUES (37, '任务创建', '2019-01-08 15:00:20', 'JOB_CREATE', 35); +INSERT INTO `permission` VALUES (38, '任务编辑', '2019-01-08 15:00:33', 'JOB_EDIT', 35); +INSERT INTO `permission` VALUES (39, '任务删除', '2019-01-08 15:01:13', 'JOB_DELETE', 35); -- ---------------------------- -- Table structure for picture @@ -269,6 +280,48 @@ CREATE TABLE `qiniu_content` ( PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; +-- ---------------------------- +-- Table structure for quartz_job +-- ---------------------------- +DROP TABLE IF EXISTS `quartz_job`; +CREATE TABLE `quartz_job` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `bean_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `cron_expression` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `is_pause` bit(1) NULL DEFAULT NULL, + `jobName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `method_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `params` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `updateTime` datetime NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; + +-- ---------------------------- +-- Records of quartz_job +-- ---------------------------- +INSERT INTO `quartz_job` VALUES (1, 'visitsTask', '0 0 0 * * ?', b'0', '更新访客记录', 'run', NULL, '每日0点创建新的访客记录', '2019-01-08 14:53:31'); +INSERT INTO `quartz_job` VALUES (2, 'testTask', '0/5 * * * * ?', b'1', '测试1', 'run1', 'test', '带参测试,多参使用json', '2019-01-08 14:53:25'); +INSERT INTO `quartz_job` VALUES (3, 'testTask', '0/5 * * * * ?', b'1', '测试', 'run', '', '不带参测试', '2019-01-08 15:56:54'); + +-- ---------------------------- +-- Table structure for quartz_log +-- ---------------------------- +DROP TABLE IF EXISTS `quartz_log`; +CREATE TABLE `quartz_log` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `baen_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `createTime` datetime NULL DEFAULT NULL, + `cron_expression` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `exceptionDetail` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL, + `is_success` bit(1) NULL DEFAULT NULL, + `job_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `method_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `params` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `time` bigint(20) NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 90 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; + -- ---------------------------- -- Table structure for role -- ---------------------------- @@ -310,6 +363,7 @@ INSERT INTO `roles_permissions` VALUES (2, 14); INSERT INTO `roles_permissions` VALUES (2, 19); INSERT INTO `roles_permissions` VALUES (2, 23); INSERT INTO `roles_permissions` VALUES (2, 30); +INSERT INTO `roles_permissions` VALUES (2, 36); -- ---------------------------- -- Table structure for user @@ -332,7 +386,7 @@ CREATE TABLE `user` ( -- ---------------------------- -- Records of user -- ---------------------------- -INSERT INTO `user` VALUES (1, 'https://i.loli.net/2018/12/31/5c297270b20e2.jpg', '2018-08-23 09:11:56', 'admin@qq.com', 1, '14e1b600b1fd579f47433b88e8d85291', 'admin', '2018-11-23 10:12:36'); +INSERT INTO `user` VALUES (1, 'https://i.loli.net/2018/12/31/5c297270b20e2.jpg', '2018-08-23 09:11:56', 'zhengjie@tom.com', 1, '14e1b600b1fd579f47433b88e8d85291', 'admin', '2018-11-23 10:12:36'); INSERT INTO `user` VALUES (3, 'https://i.loli.net/2018/12/30/5c2871d6aa101.jpg', '2018-12-27 20:05:26', 'test@qq.com', 1, '14e1b600b1fd579f47433b88e8d85291', 'test', NULL); -- ---------------------------- @@ -367,7 +421,7 @@ CREATE TABLE `verification_code` ( `value` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `scenes` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 33 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; +) ENGINE = InnoDB AUTO_INCREMENT = 34 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; -- ---------------------------- -- Table structure for visits @@ -381,6 +435,6 @@ CREATE TABLE `visits` ( `weekDay` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `createTime` datetime NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 51 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; +) ENGINE = InnoDB AUTO_INCREMENT = 54 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; SET FOREIGN_KEY_CHECKS = 1; diff --git a/src/main/java/me/zhengjie/AppRun.java b/src/main/java/me/zhengjie/AppRun.java index 67ac0d323..1f329f15c 100644 --- a/src/main/java/me/zhengjie/AppRun.java +++ b/src/main/java/me/zhengjie/AppRun.java @@ -1,8 +1,9 @@ package me.zhengjie; +import me.zhengjie.common.utils.SpringContextHolder; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.context.annotation.Bean; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; @@ -11,7 +12,6 @@ * @date 2018/11/15 9:20:19 */ @SpringBootApplication -@EnableScheduling @EnableTransactionManagement @EnableWebSocketMessageBroker public class AppRun { @@ -19,4 +19,9 @@ public class AppRun { public static void main(String[] args) { SpringApplication.run(AppRun.class, args); } + + @Bean + public SpringContextHolder springContextHolder() { + return new SpringContextHolder(); + } } diff --git a/src/main/java/me/zhengjie/common/config/ThreadPoolConfig.java b/src/main/java/me/zhengjie/common/config/ThreadPoolConfig.java new file mode 100644 index 000000000..ed7690d60 --- /dev/null +++ b/src/main/java/me/zhengjie/common/config/ThreadPoolConfig.java @@ -0,0 +1,22 @@ +package me.zhengjie.common.config; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import java.util.concurrent.*; + +/** + * @author jie + * @date 2019-01-08 + */ +@Configuration +public class ThreadPoolConfig { + + @Bean + public ExecutorService getThreadPool(){ + ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("thread-call-runner-%d").build(); + int size = 2; + ExecutorService executorService = new ThreadPoolExecutor(size,size,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue(),namedThreadFactory); + return executorService; + } +} diff --git a/src/main/java/me/zhengjie/common/redis/RedisConfig.java b/src/main/java/me/zhengjie/common/redis/RedisConfig.java index 0f55051d4..bf66187fe 100644 --- a/src/main/java/me/zhengjie/common/redis/RedisConfig.java +++ b/src/main/java/me/zhengjie/common/redis/RedisConfig.java @@ -76,7 +76,7 @@ public JedisPool redisPoolFactory(){ public RedisCacheConfiguration redisCacheConfiguration(){ FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class); RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig(); - configuration = configuration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer)).entryTtl(Duration.ofDays(30)); + configuration = configuration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer)).entryTtl(Duration.ofHours(2)); return configuration; } @@ -95,6 +95,7 @@ public RedisTemplate redisTemplate(RedisConnectionFactory redisC ParserConfig.getGlobalInstance().addAccept("me.zhengjie.system.service.dto"); ParserConfig.getGlobalInstance().addAccept("me.zhengjie.system.domain"); ParserConfig.getGlobalInstance().addAccept("me.zhengjie.tools.domain"); + ParserConfig.getGlobalInstance().addAccept("me.zhengjie.quartz.domain"); // key的序列化采用StringRedisSerializer template.setKeySerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); diff --git a/src/main/java/me/zhengjie/common/swagger2/SwaggerConfig.java b/src/main/java/me/zhengjie/common/swagger2/SwaggerConfig.java index 39e5225a1..a9281e3ce 100644 --- a/src/main/java/me/zhengjie/common/swagger2/SwaggerConfig.java +++ b/src/main/java/me/zhengjie/common/swagger2/SwaggerConfig.java @@ -26,7 +26,12 @@ @Configuration @EnableSwagger2 -@ComponentScan(basePackages = {"me.zhengjie.core.rest","me.zhengjie.system.rest","me.zhengjie.system.monitor"}) +@ComponentScan(basePackages = { + "me.zhengjie.core.rest", + "me.zhengjie.system.rest", + "me.zhengjie.monitor.rest", + "me.zhengjie.quartz.rest", + "me.zhengjie.tools.rest",}) public class SwaggerConfig { @Bean diff --git a/src/main/java/me/zhengjie/common/utils/SpringContextHolder.java b/src/main/java/me/zhengjie/common/utils/SpringContextHolder.java new file mode 100644 index 000000000..bd75b2ce5 --- /dev/null +++ b/src/main/java/me/zhengjie/common/utils/SpringContextHolder.java @@ -0,0 +1,73 @@ +package me.zhengjie.common.utils; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +/** + * @author + * @date 2019-01-07 + */ +@Slf4j +public class SpringContextHolder implements ApplicationContextAware, DisposableBean { + + private static ApplicationContext applicationContext = null; + + /** + * 取得存储在静态变量中的ApplicationContext. + */ + public static ApplicationContext getApplicationContext() { + assertContextInjected(); + return applicationContext; + } + + /** + * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. + */ + public static T getBean(String name) { + assertContextInjected(); + return (T) applicationContext.getBean(name); + } + + /** + * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. + */ + public static T getBean(Class requiredType) { + assertContextInjected(); + return applicationContext.getBean(requiredType); + } + + /** + * 检查ApplicationContext不为空. + */ + private static void assertContextInjected() { + if (applicationContext == null) { + throw new IllegalStateException("applicaitonContext属性未注入, 请在applicationContext" + + ".xml中定义SpringContextHolder或在SpringBoot启动类中注册SpringContextHolder."); + } + } + + /** + * 清除SpringContextHolder中的ApplicationContext为Null. + */ + public static void clearHolder() { + log.debug("清除SpringContextHolder中的ApplicationContext:" + + applicationContext); + applicationContext = null; + } + + @Override + public void destroy() throws Exception { + SpringContextHolder.clearHolder(); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + if (SpringContextHolder.applicationContext != null) { + log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext); + } + SpringContextHolder.applicationContext = applicationContext; + } +} diff --git a/src/main/java/me/zhengjie/monitor/config/LoggerQueue.java b/src/main/java/me/zhengjie/monitor/config/LoggerQueue.java index c0deefb0d..5f4903131 100644 --- a/src/main/java/me/zhengjie/monitor/config/LoggerQueue.java +++ b/src/main/java/me/zhengjie/monitor/config/LoggerQueue.java @@ -27,7 +27,6 @@ public static LoggerQueue getInstance() { /** * 消息入队 - * * @param log * @return */ diff --git a/src/main/java/me/zhengjie/monitor/config/VisitsInitialization.java b/src/main/java/me/zhengjie/monitor/config/VisitsInitialization.java index 8ae173325..dbdfec1f7 100644 --- a/src/main/java/me/zhengjie/monitor/config/VisitsInitialization.java +++ b/src/main/java/me/zhengjie/monitor/config/VisitsInitialization.java @@ -1,18 +1,17 @@ package me.zhengjie.monitor.config; -import lombok.extern.slf4j.Slf4j; import me.zhengjie.monitor.service.VisitsService; import org.springframework.context.annotation.Configuration; /** * 初始化站点统计 + * @author jie */ -@Slf4j @Configuration public class VisitsInitialization { public VisitsInitialization(VisitsService visitsService){ - log.info("--------------- 初始化站点统计,如果存在今日统计则跳过 ---------------"); + System.out.println("--------------- 初始化站点统计,如果存在今日统计则跳过 ---------------"); visitsService.save(); } } diff --git a/src/main/java/me/zhengjie/monitor/config/WebSocketConfig.java b/src/main/java/me/zhengjie/monitor/config/WebSocketConfig.java index 343d27ac5..e2c671b3d 100644 --- a/src/main/java/me/zhengjie/monitor/config/WebSocketConfig.java +++ b/src/main/java/me/zhengjie/monitor/config/WebSocketConfig.java @@ -1,6 +1,7 @@ package me.zhengjie.monitor.config; +import com.google.common.util.concurrent.ThreadFactoryBuilder; import lombok.extern.slf4j.Slf4j; import me.zhengjie.monitor.domain.LogMessage; import org.springframework.beans.factory.annotation.Autowired; @@ -9,8 +10,7 @@ import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; import javax.annotation.PostConstruct; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.util.concurrent.*; /** * 配置WebSocket消息代理端点,即stomp服务端 @@ -24,6 +24,9 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Autowired private SimpMessagingTemplate messagingTemplate; + @Autowired + private ExecutorService executorService; + @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/websocket") @@ -36,9 +39,7 @@ public void registerStompEndpoints(StompEndpointRegistry registry) { */ @PostConstruct public void pushLogger(){ - ExecutorService executorService= Executors.newFixedThreadPool(2); Runnable runnable=new Runnable() { - @Override public void run() { while (true) { @@ -63,6 +64,5 @@ public void run() { } }; executorService.submit(runnable); - executorService.submit(runnable); } } \ No newline at end of file diff --git a/src/main/java/me/zhengjie/monitor/rest/RedisController.java b/src/main/java/me/zhengjie/monitor/rest/RedisController.java index f28c1de80..5b4c1dd2e 100644 --- a/src/main/java/me/zhengjie/monitor/rest/RedisController.java +++ b/src/main/java/me/zhengjie/monitor/rest/RedisController.java @@ -46,10 +46,10 @@ public ResponseEntity update(@Validated @RequestBody RedisVo resources){ } @Log(description = "删除Redis缓存") - @DeleteMapping(value = "/redis/{key}") + @DeleteMapping(value = "/redis") @PreAuthorize("hasAnyRole('ADMIN','REDIS_ALL','REDIS_DELETE')") - public ResponseEntity delete(@PathVariable String key){ - redisService.delete(key); + public ResponseEntity delete(@RequestBody RedisVo resources){ + redisService.delete(resources.getKey()); return new ResponseEntity(HttpStatus.OK); } diff --git a/src/main/java/me/zhengjie/quartz/config/JobRunner.java b/src/main/java/me/zhengjie/quartz/config/JobRunner.java new file mode 100644 index 000000000..1f8345324 --- /dev/null +++ b/src/main/java/me/zhengjie/quartz/config/JobRunner.java @@ -0,0 +1,39 @@ +package me.zhengjie.quartz.config; + +import me.zhengjie.quartz.domain.QuartzJob; +import me.zhengjie.quartz.repository.QuartzJobRepository; +import me.zhengjie.quartz.utils.QuartzManage; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; +import java.util.List; + +/** + * @author jie + * @date 2019-01-07 + */ +@Component +public class JobRunner implements ApplicationRunner { + + @Autowired + private QuartzJobRepository quartzJobRepository; + + @Autowired + private QuartzManage quartzManage; + + /** + * 项目启动时重新激活启用的定时任务 + * @param applicationArguments + * @throws Exception + */ + @Override + public void run(ApplicationArguments applicationArguments){ + System.out.println("--------------------注入定时任务---------------------"); + List quartzJobs = quartzJobRepository.findByIsPauseIsFalse(); + quartzJobs.forEach(quartzJob -> { + quartzManage.addJob(quartzJob); + }); + System.out.println("--------------------定时任务注入完成---------------------"); + } +} diff --git a/src/main/java/me/zhengjie/quartz/config/QuartzConfig.java b/src/main/java/me/zhengjie/quartz/config/QuartzConfig.java new file mode 100644 index 000000000..5ae865b0d --- /dev/null +++ b/src/main/java/me/zhengjie/quartz/config/QuartzConfig.java @@ -0,0 +1,55 @@ +package me.zhengjie.quartz.config; + +import org.quartz.Scheduler; +import org.quartz.spi.TriggerFiredBundle; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.quartz.AdaptableJobFactory; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; +import org.springframework.stereotype.Component; + +/** + * 定时任务配置 + * @author + * @date 2019-01-07 + */ +@Configuration +public class QuartzConfig { + + /** + * 解决Job中注入Spring Bean为null的问题 + */ + @Component("quartzJobFactory") + public class QuartzJobFactory extends AdaptableJobFactory { + + @Autowired + private AutowireCapableBeanFactory capableBeanFactory; + + @Override + protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { + + //调用父类的方法 + Object jobInstance = super.createJobInstance(bundle); + capableBeanFactory.autowireBean(jobInstance); + return jobInstance; + } + } + + /** + * 注入scheduler到spring + * @param quartzJobFactory + * @return + * @throws Exception + */ + @Bean(name = "scheduler") + public Scheduler scheduler(QuartzJobFactory quartzJobFactory) throws Exception { + SchedulerFactoryBean factoryBean=new SchedulerFactoryBean(); + factoryBean.setJobFactory(quartzJobFactory); + factoryBean.afterPropertiesSet(); + Scheduler scheduler=factoryBean.getScheduler(); + scheduler.start(); + return scheduler; + } +} diff --git a/src/main/java/me/zhengjie/quartz/domain/QuartzJob.java b/src/main/java/me/zhengjie/quartz/domain/QuartzJob.java new file mode 100644 index 000000000..90372a8ba --- /dev/null +++ b/src/main/java/me/zhengjie/quartz/domain/QuartzJob.java @@ -0,0 +1,75 @@ +package me.zhengjie.quartz.domain; + +import lombok.Data; +import org.hibernate.annotations.UpdateTimestamp; +import javax.persistence.*; +import javax.validation.constraints.NotBlank; +import java.io.Serializable; +import java.sql.Timestamp; + +/** + * @author jie + * @date 2019-01-07 + */ +@Data +@Entity +@Table(name = "quartz_job") +public class QuartzJob implements Serializable { + + public static final String JOB_KEY = "JOB_KEY"; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + /** + * 定时器名称 + */ + private String jobName; + + /** + * Bean名称 + */ + @Column(name = "bean_name") + @NotBlank + private String beanName; + + /** + * 方法名称 + */ + @Column(name = "method_name") + @NotBlank + private String methodName; + + /** + * 参数 + */ + @Column(name = "params") + private String params; + + /** + * cron表达式 + */ + @Column(name = "cron_expression") + @NotBlank + private String cronExpression; + + /** + * 状态 + */ + @Column(name = "is_pause") + private Boolean isPause = false; + + /** + * 备注 + */ + @Column(name = "remark") + @NotBlank + private String remark; + + /** + * 创建日期 + */ + @UpdateTimestamp + private Timestamp updateTime; +} \ No newline at end of file diff --git a/src/main/java/me/zhengjie/quartz/domain/QuartzLog.java b/src/main/java/me/zhengjie/quartz/domain/QuartzLog.java new file mode 100644 index 000000000..6f13fa1f1 --- /dev/null +++ b/src/main/java/me/zhengjie/quartz/domain/QuartzLog.java @@ -0,0 +1,74 @@ +package me.zhengjie.quartz.domain; + +import lombok.Data; +import org.hibernate.annotations.CreationTimestamp; +import javax.persistence.*; +import java.io.Serializable; +import java.sql.Timestamp; + +/** + * @author jie + * @date 2019-01-07 + */ +@Entity +@Data +@Table(name = "quartz_log") +public class QuartzLog implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + /** + * 任务名称 + */ + @Column(name = "job_name") + private String jobName; + + /** + * Bean名称 + */ + @Column(name = "baen_name") + private String beanName; + + /** + * 方法名称 + */ + @Column(name = "method_name") + private String methodName; + + /** + * 参数 + */ + @Column(name = "params") + private String params; + + /** + * cron表达式 + */ + @Column(name = "cron_expression") + private String cronExpression; + + /** + * 状态 + */ + @Column(name = "is_success") + private Boolean isSuccess; + + /** + * 异常详细 + */ + @Column(columnDefinition = "text") + private String exceptionDetail; + + /** + * 耗时(毫秒) + */ + private Long time; + + /** + * 创建日期 + */ + @CreationTimestamp + private Timestamp createTime; +} diff --git a/src/main/java/me/zhengjie/quartz/repository/QuartzJobRepository.java b/src/main/java/me/zhengjie/quartz/repository/QuartzJobRepository.java new file mode 100644 index 000000000..a147b58a8 --- /dev/null +++ b/src/main/java/me/zhengjie/quartz/repository/QuartzJobRepository.java @@ -0,0 +1,31 @@ +package me.zhengjie.quartz.repository; + +import me.zhengjie.quartz.domain.QuartzJob; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.transaction.annotation.Transactional; +import java.util.List; + +/** + * @author jie + * @date 2019-01-07 + */ +public interface QuartzJobRepository extends JpaRepository, JpaSpecificationExecutor { + + /** + * 更新状态 + * @param id + */ + @Transactional(rollbackFor = Exception.class) + @Modifying + @Query(value = "update quartz_job set is_pause = 1 where id = ?1",nativeQuery = true) + void updateIsPause(Long id); + + /** + * 查询不是启用的任务 + * @return + */ + List findByIsPauseIsFalse(); +} diff --git a/src/main/java/me/zhengjie/quartz/repository/QuartzLogRepository.java b/src/main/java/me/zhengjie/quartz/repository/QuartzLogRepository.java new file mode 100644 index 000000000..520ef16c2 --- /dev/null +++ b/src/main/java/me/zhengjie/quartz/repository/QuartzLogRepository.java @@ -0,0 +1,13 @@ +package me.zhengjie.quartz.repository; + +import me.zhengjie.quartz.domain.QuartzLog; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +/** + * @author jie + * @date 2019-01-07 + */ +public interface QuartzLogRepository extends JpaRepository, JpaSpecificationExecutor { + +} diff --git a/src/main/java/me/zhengjie/quartz/rest/QuartzJobController.java b/src/main/java/me/zhengjie/quartz/rest/QuartzJobController.java new file mode 100644 index 000000000..c3f6906f5 --- /dev/null +++ b/src/main/java/me/zhengjie/quartz/rest/QuartzJobController.java @@ -0,0 +1,102 @@ +package me.zhengjie.quartz.rest; + +import lombok.extern.slf4j.Slf4j; +import me.zhengjie.common.aop.log.Log; +import me.zhengjie.common.exception.BadRequestException; +import me.zhengjie.quartz.domain.QuartzJob; +import me.zhengjie.quartz.domain.QuartzLog; +import me.zhengjie.quartz.service.QuartzJobService; +import me.zhengjie.quartz.service.query.QuartzJobQueryService; +import me.zhengjie.quartz.service.query.QuartzLogQueryService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +/** + * @author jie + * @date 2019-01-07 + */ +@Slf4j +@RestController +@RequestMapping("/api") +public class QuartzJobController { + + private static final String ENTITY_NAME = "quartzJob"; + + @Autowired + private QuartzJobService quartzJobService; + + @Autowired + private QuartzJobQueryService quartzJobQueryService; + + @Autowired + private QuartzLogQueryService quartzLogQueryService; + + @Log(description = "查询定时任务") + @GetMapping(value = "/jobs") + @PreAuthorize("hasAnyRole('ADMIN','JOB_ALL','JOB_SELECT')") + public ResponseEntity getJobs(QuartzJob resources, Pageable pageable){ + return new ResponseEntity(quartzJobQueryService.queryAll(resources,pageable), HttpStatus.OK); + } + + /** + * 查询定时任务日志 + * @param resources + * @param pageable + * @return + */ + @GetMapping(value = "/jobLogs") + @PreAuthorize("hasAnyRole('ADMIN','JOB_ALL','JOB_SELECT')") + public ResponseEntity getJobLogs(QuartzLog resources, Pageable pageable){ + return new ResponseEntity(quartzLogQueryService.queryAll(resources,pageable), HttpStatus.OK); + } + + @Log(description = "新增定时任务") + @PostMapping(value = "/jobs") + @PreAuthorize("hasAnyRole('ADMIN','JOB_ALL','JOB_CREATE')") + public ResponseEntity create(@Validated @RequestBody QuartzJob resources){ + if (resources.getId() != null) { + throw new BadRequestException("A new "+ ENTITY_NAME +" cannot already have an ID"); + } + return new ResponseEntity(quartzJobService.create(resources),HttpStatus.CREATED); + } + + @Log(description = "修改定时任务") + @PutMapping(value = "/jobs") + @PreAuthorize("hasAnyRole('ADMIN','JOB_ALL','JOB_EDIT')") + public ResponseEntity update(@Validated @RequestBody QuartzJob resources){ + if (resources.getId() == null) { + throw new BadRequestException(ENTITY_NAME +" ID Can not be empty"); + } + quartzJobService.update(resources); + return new ResponseEntity(HttpStatus.NO_CONTENT); + } + + @Log(description = "更改定时任务状态") + @PutMapping(value = "/jobs/{id}") + @PreAuthorize("hasAnyRole('ADMIN','JOB_ALL','JOB_EDIT')") + public ResponseEntity updateIsPause(@PathVariable Long id){ + quartzJobService.updateIsPause(quartzJobService.findById(id)); + return new ResponseEntity(HttpStatus.NO_CONTENT); + } + + @Log(description = "执行定时任务") + @PutMapping(value = "/jobs/exec/{id}") + @PreAuthorize("hasAnyRole('ADMIN','JOB_ALL','JOB_EDIT')") + public ResponseEntity execution(@PathVariable Long id){ + quartzJobService.execution(quartzJobService.findById(id)); + return new ResponseEntity(HttpStatus.NO_CONTENT); + } + + @Log(description = "删除定时任务") + @DeleteMapping(value = "/jobs/{id}") + @PreAuthorize("hasAnyRole('ADMIN','JOB_ALL','JOB_DELETE')") + public ResponseEntity delete(@PathVariable Long id){ + quartzJobService.delete(quartzJobService.findById(id)); + return new ResponseEntity(HttpStatus.OK); + } +} diff --git a/src/main/java/me/zhengjie/quartz/service/QuartzJobService.java b/src/main/java/me/zhengjie/quartz/service/QuartzJobService.java new file mode 100644 index 000000000..c661577be --- /dev/null +++ b/src/main/java/me/zhengjie/quartz/service/QuartzJobService.java @@ -0,0 +1,58 @@ +package me.zhengjie.quartz.service; + +import me.zhengjie.quartz.domain.QuartzJob; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; + +/** + * @author 郑杰 + * @date 2018/10/05 19:17:38 + */ +@CacheConfig(cacheNames = "quartzJob") +public interface QuartzJobService { + + /** + * create + * @param resources + * @return + */ + @CacheEvict(allEntries = true) + QuartzJob create(QuartzJob resources); + + /** + * update + * @param resources + * @return + */ + @CacheEvict(allEntries = true) + void update(QuartzJob resources); + + /** + * del + * @param quartzJob + */ + @CacheEvict(allEntries = true) + void delete(QuartzJob quartzJob); + + /** + * findById + * @param id + * @return + */ + @Cacheable(key = "#p0") + QuartzJob findById(Long id); + + /** + * 更改定时任务状态 + * @param quartzJob + */ + @CacheEvict(allEntries = true) + void updateIsPause(QuartzJob quartzJob); + + /** + * 立即执行定时任务 + * @param quartzJob + */ + void execution(QuartzJob quartzJob); +} diff --git a/src/main/java/me/zhengjie/quartz/service/impl/QuartzJobServiceImpl.java b/src/main/java/me/zhengjie/quartz/service/impl/QuartzJobServiceImpl.java new file mode 100644 index 000000000..48b50c5dd --- /dev/null +++ b/src/main/java/me/zhengjie/quartz/service/impl/QuartzJobServiceImpl.java @@ -0,0 +1,94 @@ +package me.zhengjie.quartz.service.impl; + +import me.zhengjie.common.exception.BadRequestException; +import me.zhengjie.common.utils.ValidationUtil; +import me.zhengjie.quartz.domain.QuartzJob; +import me.zhengjie.quartz.repository.QuartzJobRepository; +import me.zhengjie.quartz.service.QuartzJobService; +import me.zhengjie.quartz.utils.QuartzManage; +import org.quartz.CronExpression; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Optional; + +/** + * @author jie + * @date 2019-01-07 + */ +@Service(value = "quartzJobService") +@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) +public class QuartzJobServiceImpl implements QuartzJobService { + + @Autowired + private QuartzJobRepository quartzJobRepository; + + @Autowired + private QuartzManage quartzManage; + + @Override + public QuartzJob findById(Long id) { + Optional quartzJob = quartzJobRepository.findById(id); + ValidationUtil.isNull(quartzJob,"QuartzJob","id",id); + return quartzJob.get(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public QuartzJob create(QuartzJob resources) { + if (!CronExpression.isValidExpression(resources.getCronExpression())){ + throw new BadRequestException("cron表达式格式错误"); + } + resources = quartzJobRepository.save(resources); + quartzManage.addJob(resources); + return resources; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(QuartzJob resources) { + if(resources.getId().equals(1L)){ + throw new BadRequestException("该任务不可操作"); + } + if (!CronExpression.isValidExpression(resources.getCronExpression())){ + throw new BadRequestException("cron表达式格式错误"); + } + resources = quartzJobRepository.save(resources); + quartzManage.updateJobCron(resources); + } + + @Override + public void updateIsPause(QuartzJob quartzJob) { + if(quartzJob.getId().equals(1L)){ + throw new BadRequestException("该任务不可操作"); + } + if (quartzJob.getIsPause()) { + quartzManage.resumeJob(quartzJob); + quartzJob.setIsPause(false); + } else { + quartzManage.pauseJob(quartzJob); + quartzJob.setIsPause(true); + } + quartzJobRepository.save(quartzJob); + } + + @Override + public void execution(QuartzJob quartzJob) { + if(quartzJob.getId().equals(1L)){ + throw new BadRequestException("该任务不可操作"); + } + quartzManage.runAJobNow(quartzJob); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(QuartzJob quartzJob) { + if(quartzJob.getId().equals(1L)){ + throw new BadRequestException("该任务不可操作"); + } + quartzManage.deleteJob(quartzJob); + quartzJobRepository.delete(quartzJob); + } +} diff --git a/src/main/java/me/zhengjie/quartz/service/query/QuartzJobQueryService.java b/src/main/java/me/zhengjie/quartz/service/query/QuartzJobQueryService.java new file mode 100644 index 000000000..49c8a947a --- /dev/null +++ b/src/main/java/me/zhengjie/quartz/service/query/QuartzJobQueryService.java @@ -0,0 +1,64 @@ +package me.zhengjie.quartz.service.query; + +import me.zhengjie.common.utils.PageUtil; +import me.zhengjie.quartz.domain.QuartzJob; +import me.zhengjie.quartz.repository.QuartzJobRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.ObjectUtils; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import java.util.ArrayList; +import java.util.List; + +/** + * @author jie + * @date 2019-01-07 + */ +@Service +@CacheConfig(cacheNames = "quartzJob") +@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) +public class QuartzJobQueryService { + + @Autowired + private QuartzJobRepository quartzJobRepository; + + @Cacheable(keyGenerator = "keyGenerator") + public Object queryAll(QuartzJob quartzJob, Pageable pageable){ + return PageUtil.toPage(quartzJobRepository.findAll(new Spec(quartzJob),pageable)); + } + + class Spec implements Specification { + + private QuartzJob quartzJob; + + public Spec(QuartzJob quartzJob){ + this.quartzJob = quartzJob; + } + + @Override + public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder cb) { + + List list = new ArrayList(); + + if(!ObjectUtils.isEmpty(quartzJob.getJobName())){ + + /** + * 模糊 + */ + list.add(cb.like(root.get("jobName").as(String.class),"%"+quartzJob.getJobName()+"%")); + } + + Predicate[] p = new Predicate[list.size()]; + return cb.and(list.toArray(p)); + } + } +} diff --git a/src/main/java/me/zhengjie/quartz/service/query/QuartzLogQueryService.java b/src/main/java/me/zhengjie/quartz/service/query/QuartzLogQueryService.java new file mode 100644 index 000000000..e5aa8e3f8 --- /dev/null +++ b/src/main/java/me/zhengjie/quartz/service/query/QuartzLogQueryService.java @@ -0,0 +1,64 @@ +package me.zhengjie.quartz.service.query; + +import me.zhengjie.common.utils.PageUtil; +import me.zhengjie.quartz.domain.QuartzLog; +import me.zhengjie.quartz.repository.QuartzLogRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.ObjectUtils; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import java.util.ArrayList; +import java.util.List; + +/** + * @author jie + * @date 2019-01-07 + */ +@Service +@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) +public class QuartzLogQueryService { + + @Autowired + private QuartzLogRepository quartzLogRepository; + + public Object queryAll(QuartzLog quartzLog, Pageable pageable){ + return PageUtil.toPage(quartzLogRepository.findAll(new Spec(quartzLog),pageable)); + } + + class Spec implements Specification { + + private QuartzLog quartzLog; + + public Spec(QuartzLog quartzLog){ + this.quartzLog = quartzLog; + } + + @Override + public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder cb) { + + List list = new ArrayList(); + + if(!ObjectUtils.isEmpty(quartzLog.getJobName())){ + + /** + * 模糊 + */ + list.add(cb.like(root.get("jobName").as(String.class),"%"+quartzLog.getJobName()+"%")); + } + + if (!ObjectUtils.isEmpty(quartzLog.getIsSuccess())) { + list.add(cb.equal(root.get("isSuccess").as(Boolean.class), quartzLog.getIsSuccess())); + } + + Predicate[] p = new Predicate[list.size()]; + return cb.and(list.toArray(p)); + } + } +} diff --git a/src/main/java/me/zhengjie/quartz/task/TestTask.java b/src/main/java/me/zhengjie/quartz/task/TestTask.java new file mode 100644 index 000000000..6c2115b70 --- /dev/null +++ b/src/main/java/me/zhengjie/quartz/task/TestTask.java @@ -0,0 +1,22 @@ +package me.zhengjie.quartz.task; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 测试用 + * @author jie + * @date 2019-01-08 + */ +@Slf4j +@Component +public class TestTask { + + public void run(){ + log.info("执行成功"); + } + + public void run1(String str){ + log.info("执行成功,参数为: {}" + str); + } +} diff --git a/src/main/java/me/zhengjie/monitor/config/VisitsScheduling.java b/src/main/java/me/zhengjie/quartz/task/VisitsTask.java similarity index 51% rename from src/main/java/me/zhengjie/monitor/config/VisitsScheduling.java rename to src/main/java/me/zhengjie/quartz/task/VisitsTask.java index c45425883..aeeec5570 100644 --- a/src/main/java/me/zhengjie/monitor/config/VisitsScheduling.java +++ b/src/main/java/me/zhengjie/quartz/task/VisitsTask.java @@ -1,9 +1,7 @@ -package me.zhengjie.monitor.config; +package me.zhengjie.quartz.task; import me.zhengjie.monitor.service.VisitsService; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.scheduling.annotation.Async; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; /** @@ -11,17 +9,12 @@ * @date 2018-12-25 */ @Component -@Async -public class VisitsScheduling { +public class VisitsTask { @Autowired private VisitsService visitsService; - /** - * 每天0点运行 - */ - @Scheduled(cron = "0 0 0 * * ?") - public void save(){ + public void run(){ visitsService.save(); } } diff --git a/src/main/java/me/zhengjie/quartz/utils/ExecutionJob.java b/src/main/java/me/zhengjie/quartz/utils/ExecutionJob.java new file mode 100644 index 000000000..834bb10fa --- /dev/null +++ b/src/main/java/me/zhengjie/quartz/utils/ExecutionJob.java @@ -0,0 +1,78 @@ +package me.zhengjie.quartz.utils; + +import me.zhengjie.common.utils.SpringContextHolder; +import me.zhengjie.common.utils.ThrowableUtil; +import me.zhengjie.quartz.domain.QuartzJob; +import me.zhengjie.quartz.domain.QuartzLog; +import me.zhengjie.quartz.repository.QuartzLogRepository; +import me.zhengjie.quartz.service.QuartzJobService; +import org.quartz.JobExecutionContext; +import org.quartz.Scheduler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.quartz.QuartzJobBean; +import javax.annotation.Resource; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +/** + * 参考人人开源,https://gitee.com/renrenio/renren-security + * @author + * @date 2019-01-07 + */ +@Async +public class ExecutionJob extends QuartzJobBean { + + @Resource(name = "scheduler") + private Scheduler scheduler; + + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Autowired + private ExecutorService executorService; + + @Override + protected void executeInternal(JobExecutionContext context) { + QuartzJob quartzJob = (QuartzJob) context.getMergedJobDataMap().get(QuartzJob.JOB_KEY); + // 获取spring bean + QuartzLogRepository quartzLogRepository = SpringContextHolder.getBean("quartzLogRepository"); + QuartzJobService quartzJobService = SpringContextHolder.getBean("quartzJobService"); + QuartzManage quartzManage = SpringContextHolder.getBean("quartzManage"); + + QuartzLog log = new QuartzLog(); + log.setJobName(quartzJob.getJobName()); + log.setBeanName(quartzJob.getBeanName()); + log.setMethodName(quartzJob.getMethodName()); + log.setParams(quartzJob.getParams()); + long startTime = System.currentTimeMillis(); + log.setCronExpression(quartzJob.getCronExpression()); + try { + // 执行任务 + logger.info("任务准备执行,任务名称:{}", quartzJob.getJobName()); + QuartzRunnable task = new QuartzRunnable(quartzJob.getBeanName(), quartzJob.getMethodName(), + quartzJob.getParams()); + Future future = executorService.submit(task); + future.get(); + long times = System.currentTimeMillis() - startTime; + log.setTime(times); + // 任务状态 + log.setIsSuccess(true); + logger.info("任务执行完毕,任务名称:{} 总共耗时:{} 毫秒", quartzJob.getJobName(), times); + } catch (Exception e) { + logger.error("任务执行失败,任务名称:{}" + quartzJob.getJobName(), e); + long times = System.currentTimeMillis() - startTime; + log.setTime(times); + // 任务状态 0:成功 1:失败 + log.setIsSuccess(false); + log.setExceptionDetail(ThrowableUtil.getStackTrace(e)); + //出错就暂停任务 + quartzManage.pauseJob(quartzJob); + //更新状态 + quartzJobService.updateIsPause(quartzJob); + } finally { + quartzLogRepository.save(log); + } + } +} diff --git a/src/main/java/me/zhengjie/quartz/utils/QuartzManage.java b/src/main/java/me/zhengjie/quartz/utils/QuartzManage.java new file mode 100644 index 000000000..f0a412415 --- /dev/null +++ b/src/main/java/me/zhengjie/quartz/utils/QuartzManage.java @@ -0,0 +1,156 @@ +package me.zhengjie.quartz.utils; + +import lombok.extern.slf4j.Slf4j; +import me.zhengjie.common.exception.BadRequestException; +import me.zhengjie.quartz.domain.QuartzJob; +import org.quartz.*; +import org.quartz.impl.triggers.CronTriggerImpl; +import org.springframework.stereotype.Component; +import javax.annotation.Resource; +import java.util.Date; +import static org.quartz.TriggerBuilder.newTrigger; + +/** + * @author jie + * @date 2019-01-07 + */ +@Slf4j +@Component +public class QuartzManage { + + private static final String JOB_NAME = "TASK_"; + + @Resource(name = "scheduler") + private Scheduler scheduler; + + public void addJob(QuartzJob quartzJob){ + try { + // 构建job信息 + JobDetail jobDetail = JobBuilder.newJob(ExecutionJob.class). + withIdentity(JOB_NAME + quartzJob.getId()).build(); + + //通过触发器名和cron 表达式创建 Trigger + Trigger cronTrigger = newTrigger() + .withIdentity(JOB_NAME + quartzJob.getId()) + .startNow() + .withSchedule(CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression())) + .build(); + + cronTrigger.getJobDataMap().put(QuartzJob.JOB_KEY, quartzJob); + + //重置启动时间 + ((CronTriggerImpl)cronTrigger).setStartTime(new Date()); + + //执行定时任务 + scheduler.scheduleJob(jobDetail,cronTrigger); + + // 暂停任务 + if (quartzJob.getIsPause()) { + pauseJob(quartzJob); + } + } catch (Exception e){ + log.error("创建定时任务失败", e); + throw new BadRequestException(e.getMessage()); + } + } + + /** + * 更新job cron表达式 + * @param quartzJob + * @throws SchedulerException + */ + public void updateJobCron(QuartzJob quartzJob){ + try { + TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId()); + CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); + CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression()); + trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); + //重置启动时间 + ((CronTriggerImpl)trigger).setStartTime(new Date()); + + scheduler.rescheduleJob(triggerKey, trigger); + // 暂停任务 + if (quartzJob.getIsPause()) { + pauseJob(quartzJob); + } + } catch (Exception e){ + log.error("更新定时任务失败", e); + throw new BadRequestException(e.getMessage()); + } + + } + + /** + * 删除一个job + * @param quartzJob + * @throws SchedulerException + */ + public void deleteJob(QuartzJob quartzJob){ + try { + JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); + scheduler.deleteJob(jobKey); + } catch (Exception e){ + log.error("删除定时任务失败", e); + throw new BadRequestException(e.getMessage()); + } + } + + /** + * 恢复一个job + * @param quartzJob + * @throws SchedulerException + */ + public void resumeJob(QuartzJob quartzJob){ + try { + TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId()); + CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); + // 如果不存在则创建一个定时任务 + if(trigger == null){ + addJob(quartzJob); + } + JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); + scheduler.resumeJob(jobKey); + } catch (Exception e){ + log.error("恢复定时任务失败", e); + throw new BadRequestException(e.getMessage()); + } + } + + /** + * 立即执行job + * @param quartzJob + * @throws SchedulerException + */ + public void runAJobNow(QuartzJob quartzJob){ + try { + TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId()); + CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); + // 如果不存在则创建一个定时任务 + if(trigger == null){ + addJob(quartzJob); + } + JobDataMap dataMap = new JobDataMap(); + dataMap.put(QuartzJob.JOB_KEY, quartzJob); + JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); + scheduler.triggerJob(jobKey,dataMap); + } catch (Exception e){ + log.error("定时任务执行失败", e); + throw new BadRequestException(e.getMessage()); + } + } + + /** + * 暂停一个job + * @param quartzJob + * @throws SchedulerException + */ + public void pauseJob(QuartzJob quartzJob){ + try { + JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); + scheduler.pauseJob(jobKey); + } catch (Exception e){ + log.error("定时任务暂停失败", e); + throw new BadRequestException(e.getMessage()); + } + } +} diff --git a/src/main/java/me/zhengjie/quartz/utils/QuartzRunnable.java b/src/main/java/me/zhengjie/quartz/utils/QuartzRunnable.java new file mode 100644 index 000000000..fbe8aa17b --- /dev/null +++ b/src/main/java/me/zhengjie/quartz/utils/QuartzRunnable.java @@ -0,0 +1,48 @@ +package me.zhengjie.quartz.utils; + +import lombok.extern.slf4j.Slf4j; +import me.zhengjie.common.utils.SpringContextHolder; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.ReflectionUtils; +import java.lang.reflect.Method; + +/** + * 执行定时任务 + * @author + */ +@Slf4j +public class QuartzRunnable implements Runnable { + + private Object target; + private Method method; + private String params; + + QuartzRunnable(String beanName, String methodName, String params) + throws NoSuchMethodException, SecurityException { + this.target = SpringContextHolder.getBean(beanName); + this.params = params; + + if (StringUtils.isNotBlank(params)) { + this.method = target.getClass().getDeclaredMethod(methodName, String.class); + } else { + this.method = target.getClass().getDeclaredMethod(methodName); + } + } + + @Override + public void run() { + try { + ReflectionUtils.makeAccessible(method); + if (StringUtils.isNotBlank(params)) { + method.invoke(target, params); + } else { + method.invoke(target); + } + } catch (Exception e) { + log.error("定时任务执行失败",e); + } + } + +} diff --git a/src/main/java/me/zhengjie/system/service/impl/MenuServiceImpl.java b/src/main/java/me/zhengjie/system/service/impl/MenuServiceImpl.java index 11d26bd44..77a9f88fc 100644 --- a/src/main/java/me/zhengjie/system/service/impl/MenuServiceImpl.java +++ b/src/main/java/me/zhengjie/system/service/impl/MenuServiceImpl.java @@ -162,13 +162,15 @@ public List buildMenus(List menuDTOS) { if(menuDTO.getPid().equals(0L)){ //一级目录需要加斜杠,不然访问不了 menuVo.setPath("/" + menuDTO.getPath()); - menuVo.setRedirect("noredirect"); + menuVo.setComponent(StrUtil.isEmpty(menuDTO.getComponent())?"Layout":menuDTO.getComponent()); + }else if(!StrUtil.isEmpty(menuDTO.getComponent())){ + menuVo.setComponent(menuDTO.getComponent()); } - menuVo.setComponent(StrUtil.isEmpty(menuDTO.getComponent())?"Layout":menuDTO.getComponent()); } menuVo.setMeta(new MenuMetaVo(menuDTO.getName(),menuDTO.getIcon())); if(menuDTOList!=null && menuDTOList.size()!=0){ menuVo.setAlwaysShow(true); + menuVo.setRedirect("noredirect"); menuVo.setChildren(buildMenus(menuDTOList)); // 处理是一级菜单并且没有子菜单的情况 } else if(menuDTO.getPid().equals(0L)){ diff --git a/src/main/java/me/zhengjie/system/service/impl/VerificationCodeServiceImpl.java b/src/main/java/me/zhengjie/system/service/impl/VerificationCodeServiceImpl.java index 8fe77fd38..386aad171 100644 --- a/src/main/java/me/zhengjie/system/service/impl/VerificationCodeServiceImpl.java +++ b/src/main/java/me/zhengjie/system/service/impl/VerificationCodeServiceImpl.java @@ -13,6 +13,7 @@ import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import java.util.Arrays; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index ff424c862..67fa32696 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -46,7 +46,6 @@ spring: jpa: properties: hibernate: - show_sql: true dialect: org.hibernate.dialect.MySQL5InnoDBDialect hibernate: ddl-auto: update