├── .gitignore ├── README.md ├── demo-images ├── httpjob-add.png ├── httpjob-logs.png └── httpjob-manage.png ├── pom.xml ├── sql └── create_table.sql └── src ├── main ├── java │ └── com │ │ └── example │ │ └── quartz │ │ ├── QuartzApplication.java │ │ ├── config │ │ ├── datasource │ │ │ └── DataSourceConfig.java │ │ ├── exception │ │ │ └── GlobalExceptionHandler.java │ │ ├── fastjson │ │ │ └── FastJsonConfig.java │ │ ├── quartz │ │ │ ├── JobFactory.java │ │ │ └── QuartzConfig.java │ │ └── response │ │ │ ├── Response.java │ │ │ └── ResultEnum.java │ │ ├── constants │ │ └── Constant.java │ │ ├── controller │ │ ├── HttpJobController.java │ │ └── JobController.java │ │ ├── entity │ │ ├── HttpJobDetails.java │ │ ├── HttpJobLogs.java │ │ ├── Page.java │ │ ├── param │ │ │ └── AddHttpJobParam.java │ │ └── vo │ │ │ └── HttpJobDetailVO.java │ │ ├── job │ │ ├── HttpGetJob.java │ │ ├── HttpPostFormDataJob.java │ │ └── HttpPostJsonJob.java │ │ ├── mapper │ │ ├── HttpJobDetailsMapper.java │ │ └── HttpJobLogsMapper.java │ │ ├── service │ │ ├── HttpJobService.java │ │ ├── JobManageService.java │ │ └── impl │ │ │ ├── HttpJobServiceImpl.java │ │ │ └── JobManageServiceImpl.java │ │ └── util │ │ ├── HttpClientUtil.java │ │ ├── JobUtil.java │ │ └── JsonValidUtil.java └── resources │ ├── application.properties │ ├── mappings │ ├── HttpJobDetailsMapper.xml │ └── HttpJobLogsMapper.xml │ ├── quartz.properties │ └── static │ ├── historyHttpJob.html │ ├── httpJob.html │ ├── httpJobLog.html │ └── js │ ├── axios.min.js │ └── vue.min.js └── test └── java └── com └── example └── quartz └── QuartzApplicationTests.java /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | 30 | ### VS Code ### 31 | .vscode/ 32 | 33 | ### mvn ### 34 | mvnw* 35 | .mvn -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## quartz + springboot + httpclient 2 | 3 | ### 项目功能 4 | 1、springboot集成quartz,使用druid连接池 5 | 2、支持http请求任务定时调度,当前支持get、post表单(formdata)、post Json三种请求类型,并记录返回内容 6 | 3、通过web界面进行任务管理,包括任务暂停、恢复、修改、历史记录、历史任务查看功能 7 | 4、支持调用接口(/quartz/httpJob/add)进行http任务添加 8 | 5、根据jobname或jobgroup进行查询 9 | 10 | ### 部署方式 11 | 1、执行sql目录下的create_table.sql文件,建立quartz以及httpjob需要的数据库表 12 | 2、修改application.properties中的数据库连接方式 13 | 3、访问http://localhost:8080/httpJob.html可通过web界面进行httpjob管理 14 | 15 | ### 组件版本 16 | 1、quartz 2.2.3 17 | 2、springboot 2.1.0 18 | 3、jdbc 5.1.46 19 | 4、httpclient 4.5.6 20 | 21 | ### 注意事项 22 | `1、调用接口添加http定时请求任务时,requestType(请求类型)必填,只支持填入"GET"、 "POST_JSON"、 "POST_FORM",分别对应get, post json和post form-data三种类型;` 23 | `2、调用接口添加http定时请求任务时,params(请求参数)选填,若填写,必须组装为合法的json字符串格式。` 24 | 25 | ### demo 26 | HTTP请求任务管理 27 | ![image](https://github.com/helloflygit/springboot-quartz/raw/master/demo-images/httpjob-manage.png) 28 | 29 | HTTP请求任务添加 30 | ![image](https://github.com/helloflygit/springboot-quartz/raw/master/demo-images/httpjob-add.png) 31 | 32 | HTTP请求任务执行记录 33 | ![image](https://github.com/helloflygit/springboot-quartz/raw/master/demo-images/httpjob-logs.png) 34 | -------------------------------------------------------------------------------- /demo-images/httpjob-add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helloflygit/springboot-quartz/0df2e7d3b76919460699473739ebac70a29149e9/demo-images/httpjob-add.png -------------------------------------------------------------------------------- /demo-images/httpjob-logs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helloflygit/springboot-quartz/0df2e7d3b76919460699473739ebac70a29149e9/demo-images/httpjob-logs.png -------------------------------------------------------------------------------- /demo-images/httpjob-manage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helloflygit/springboot-quartz/0df2e7d3b76919460699473739ebac70a29149e9/demo-images/httpjob-manage.png -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.example 7 | quartz 8 | 0.0.1-SNAPSHOT 9 | quartz 10 | Quartz for Spring Boot 11 | 12 | 13 | org.springframework.boot 14 | spring-boot-starter-parent 15 | 2.1.0.RELEASE 16 | 17 | 18 | 19 | 20 | UTF-8 21 | UTF-8 22 | 1.8 23 | 24 | 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter 30 | 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-web 35 | 36 | 37 | 38 | org.springframework 39 | spring-context-support 40 | 41 | 42 | 43 | org.springframework.boot 44 | spring-boot-starter-test 45 | test 46 | 47 | 48 | 49 | mysql 50 | mysql-connector-java 51 | 5.1.46 52 | runtime 53 | 54 | 55 | 56 | org.mybatis.spring.boot 57 | mybatis-spring-boot-starter 58 | 1.3.2 59 | 60 | 61 | 62 | 63 | org.quartz-scheduler 64 | quartz 65 | 2.2.3 66 | 67 | 68 | org.quartz-scheduler 69 | quartz-jobs 70 | 2.2.3 71 | 72 | 73 | 74 | 75 | org.apache.httpcomponents 76 | httpclient 77 | 4.5.6 78 | 79 | 80 | 81 | 82 | org.apache.commons 83 | commons-lang3 84 | 3.8.1 85 | 86 | 87 | 88 | 89 | com.alibaba 90 | fastjson 91 | 1.2.53 92 | 93 | 94 | 95 | 96 | com.alibaba 97 | druid 98 | 1.1.9 99 | 100 | 101 | 102 | 103 | 104 | 105 | org.springframework.boot 106 | spring-boot-maven-plugin 107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /sql/create_table.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE `quartz` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'; 2 | USE quartz; 3 | -- QUARTZ_TABLE 4 | DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS; 5 | DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS; 6 | DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE; 7 | DROP TABLE IF EXISTS QRTZ_LOCKS; 8 | DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS; 9 | DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS; 10 | DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS; 11 | DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS; 12 | DROP TABLE IF EXISTS QRTZ_TRIGGERS; 13 | DROP TABLE IF EXISTS QRTZ_JOB_DETAILS; 14 | DROP TABLE IF EXISTS QRTZ_CALENDARS; 15 | 16 | CREATE TABLE QRTZ_JOB_DETAILS( 17 | SCHED_NAME VARCHAR(120) NOT NULL, 18 | JOB_NAME VARCHAR(200) NOT NULL, 19 | JOB_GROUP VARCHAR(200) NOT NULL, 20 | DESCRIPTION VARCHAR(250) NULL, 21 | JOB_CLASS_NAME VARCHAR(250) NOT NULL, 22 | IS_DURABLE VARCHAR(1) NOT NULL, 23 | IS_NONCONCURRENT VARCHAR(1) NOT NULL, 24 | IS_UPDATE_DATA VARCHAR(1) NOT NULL, 25 | REQUESTS_RECOVERY VARCHAR(1) NOT NULL, 26 | JOB_DATA BLOB NULL, 27 | PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)) 28 | ENGINE=InnoDB; 29 | 30 | CREATE TABLE QRTZ_TRIGGERS ( 31 | SCHED_NAME VARCHAR(120) NOT NULL, 32 | TRIGGER_NAME VARCHAR(200) NOT NULL, 33 | TRIGGER_GROUP VARCHAR(200) NOT NULL, 34 | JOB_NAME VARCHAR(200) NOT NULL, 35 | JOB_GROUP VARCHAR(200) NOT NULL, 36 | DESCRIPTION VARCHAR(250) NULL, 37 | NEXT_FIRE_TIME BIGINT(13) NULL, 38 | PREV_FIRE_TIME BIGINT(13) NULL, 39 | PRIORITY INTEGER NULL, 40 | TRIGGER_STATE VARCHAR(16) NOT NULL, 41 | TRIGGER_TYPE VARCHAR(8) NOT NULL, 42 | START_TIME BIGINT(13) NOT NULL, 43 | END_TIME BIGINT(13) NULL, 44 | CALENDAR_NAME VARCHAR(200) NULL, 45 | MISFIRE_INSTR SMALLINT(2) NULL, 46 | JOB_DATA BLOB NULL, 47 | PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), 48 | FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) 49 | REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)) 50 | ENGINE=InnoDB; 51 | 52 | CREATE TABLE QRTZ_SIMPLE_TRIGGERS ( 53 | SCHED_NAME VARCHAR(120) NOT NULL, 54 | TRIGGER_NAME VARCHAR(200) NOT NULL, 55 | TRIGGER_GROUP VARCHAR(200) NOT NULL, 56 | REPEAT_COUNT BIGINT(7) NOT NULL, 57 | REPEAT_INTERVAL BIGINT(12) NOT NULL, 58 | TIMES_TRIGGERED BIGINT(10) NOT NULL, 59 | PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), 60 | FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) 61 | REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)) 62 | ENGINE=InnoDB; 63 | 64 | CREATE TABLE QRTZ_CRON_TRIGGERS ( 65 | SCHED_NAME VARCHAR(120) NOT NULL, 66 | TRIGGER_NAME VARCHAR(200) NOT NULL, 67 | TRIGGER_GROUP VARCHAR(200) NOT NULL, 68 | CRON_EXPRESSION VARCHAR(120) NOT NULL, 69 | TIME_ZONE_ID VARCHAR(80), 70 | PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), 71 | FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) 72 | REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)) 73 | ENGINE=InnoDB; 74 | 75 | CREATE TABLE QRTZ_SIMPROP_TRIGGERS 76 | ( 77 | SCHED_NAME VARCHAR(120) NOT NULL, 78 | TRIGGER_NAME VARCHAR(200) NOT NULL, 79 | TRIGGER_GROUP VARCHAR(200) NOT NULL, 80 | STR_PROP_1 VARCHAR(512) NULL, 81 | STR_PROP_2 VARCHAR(512) NULL, 82 | STR_PROP_3 VARCHAR(512) NULL, 83 | INT_PROP_1 INT NULL, 84 | INT_PROP_2 INT NULL, 85 | LONG_PROP_1 BIGINT NULL, 86 | LONG_PROP_2 BIGINT NULL, 87 | DEC_PROP_1 NUMERIC(13,4) NULL, 88 | DEC_PROP_2 NUMERIC(13,4) NULL, 89 | BOOL_PROP_1 VARCHAR(1) NULL, 90 | BOOL_PROP_2 VARCHAR(1) NULL, 91 | PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), 92 | FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) 93 | REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)) 94 | ENGINE=InnoDB; 95 | 96 | CREATE TABLE QRTZ_BLOB_TRIGGERS ( 97 | SCHED_NAME VARCHAR(120) NOT NULL, 98 | TRIGGER_NAME VARCHAR(200) NOT NULL, 99 | TRIGGER_GROUP VARCHAR(200) NOT NULL, 100 | BLOB_DATA BLOB NULL, 101 | PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), 102 | INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP), 103 | FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) 104 | REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)) 105 | ENGINE=InnoDB; 106 | 107 | CREATE TABLE QRTZ_CALENDARS ( 108 | SCHED_NAME VARCHAR(120) NOT NULL, 109 | CALENDAR_NAME VARCHAR(200) NOT NULL, 110 | CALENDAR BLOB NOT NULL, 111 | PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)) 112 | ENGINE=InnoDB; 113 | 114 | CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS ( 115 | SCHED_NAME VARCHAR(120) NOT NULL, 116 | TRIGGER_GROUP VARCHAR(200) NOT NULL, 117 | PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)) 118 | ENGINE=InnoDB; 119 | 120 | CREATE TABLE QRTZ_FIRED_TRIGGERS ( 121 | SCHED_NAME VARCHAR(120) NOT NULL, 122 | ENTRY_ID VARCHAR(95) NOT NULL, 123 | TRIGGER_NAME VARCHAR(200) NOT NULL, 124 | TRIGGER_GROUP VARCHAR(200) NOT NULL, 125 | INSTANCE_NAME VARCHAR(200) NOT NULL, 126 | FIRED_TIME BIGINT(13) NOT NULL, 127 | SCHED_TIME BIGINT(13) NOT NULL, 128 | PRIORITY INTEGER NOT NULL, 129 | STATE VARCHAR(16) NOT NULL, 130 | JOB_NAME VARCHAR(200) NULL, 131 | JOB_GROUP VARCHAR(200) NULL, 132 | IS_NONCONCURRENT VARCHAR(1) NULL, 133 | REQUESTS_RECOVERY VARCHAR(1) NULL, 134 | PRIMARY KEY (SCHED_NAME,ENTRY_ID)) 135 | ENGINE=InnoDB; 136 | 137 | CREATE TABLE QRTZ_SCHEDULER_STATE ( 138 | SCHED_NAME VARCHAR(120) NOT NULL, 139 | INSTANCE_NAME VARCHAR(200) NOT NULL, 140 | LAST_CHECKIN_TIME BIGINT(13) NOT NULL, 141 | CHECKIN_INTERVAL BIGINT(13) NOT NULL, 142 | PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)) 143 | ENGINE=InnoDB; 144 | 145 | CREATE TABLE QRTZ_LOCKS ( 146 | SCHED_NAME VARCHAR(120) NOT NULL, 147 | LOCK_NAME VARCHAR(40) NOT NULL, 148 | PRIMARY KEY (SCHED_NAME,LOCK_NAME)) 149 | ENGINE=InnoDB; 150 | 151 | CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY); 152 | CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP); 153 | 154 | CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP); 155 | CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP); 156 | CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME); 157 | CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP); 158 | CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE); 159 | CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE); 160 | CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE); 161 | CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME); 162 | CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME); 163 | CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME); 164 | CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE); 165 | CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE); 166 | 167 | CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME); 168 | CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY); 169 | CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP); 170 | CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP); 171 | CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP); 172 | CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP); 173 | 174 | -- HTTPJOB_DETAILS TABLE 175 | DROP TABLE IF EXISTS HTTPJOB_DETAILS; 176 | CREATE TABLE HTTPJOB_DETAILS( 177 | ID INT(11) NOT NULL AUTO_INCREMENT, 178 | JOB_NAME VARCHAR(200) NOT NULL, 179 | JOB_GROUP VARCHAR(200) NOT NULL, 180 | DESCRIPTION varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL, 181 | REQUEST_TYPE varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, 182 | HTTP_URL VARCHAR(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL, 183 | HTTP_PARAMS VARCHAR(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL, 184 | CREATE_TIME TIMESTAMP(0) NULL DEFAULT CURRENT_TIMESTAMP, 185 | UPDATE_TIME TIMESTAMP(0) NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0), 186 | PRIMARY KEY (ID)) 187 | ENGINE=InnoDB; 188 | 189 | -- HTTPJOB_LOGS TABLE 190 | DROP TABLE IF EXISTS HTTPJOB_LOGS; 191 | CREATE TABLE HTTPJOB_LOGS( 192 | ID INT(11) NOT NULL AUTO_INCREMENT, 193 | JOB_NAME VARCHAR(200) NOT NULL, 194 | JOB_GROUP VARCHAR(200) NOT NULL, 195 | REQUEST_TYPE varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, 196 | HTTP_URL VARCHAR(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL, 197 | HTTP_PARAMS VARCHAR(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL, 198 | FIRE_TIME TIMESTAMP(0) NULL DEFAULT CURRENT_TIMESTAMP, 199 | RESULT VARCHAR(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL, 200 | PRIMARY KEY (ID)) 201 | ENGINE=InnoDB; 202 | 203 | ALTER TABLE HTTPJOB_DETAILS ADD UNIQUE INDEX `UNIQUEIDX_HTTPJOB_JN_JG`(`JOB_NAME`, `JOB_GROUP`); 204 | ALTER TABLE HTTPJOB_LOGS ADD INDEX `IDX_HTTPJOBHISTORY_JN_JG`(`JOB_NAME`, `JOB_GROUP`); 205 | 206 | commit; 207 | -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/QuartzApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz; 2 | 3 | import org.mybatis.spring.annotation.MapperScan; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | @SpringBootApplication 8 | @MapperScan("com.example.quartz.mapper") 9 | public class QuartzApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(QuartzApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/config/datasource/DataSourceConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.config.datasource; 2 | 3 | import com.alibaba.druid.filter.Filter; 4 | import com.alibaba.druid.pool.DruidDataSource; 5 | import com.alibaba.druid.wall.WallConfig; 6 | import com.alibaba.druid.wall.WallFilter; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.context.annotation.Primary; 11 | 12 | import javax.sql.DataSource; 13 | import java.sql.SQLException; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | /** 18 | * druid数据源配置类 19 | * 20 | * @author hellofly 21 | * @date 2019/4/9 22 | */ 23 | @Configuration 24 | public class DataSourceConfig { 25 | 26 | @Value("${spring.datasource.url}") 27 | private String dbUrl; 28 | 29 | @Value("${spring.datasource.username}") 30 | private String userName; 31 | 32 | @Value("${spring.datasource.password}") 33 | private String password; 34 | 35 | @Value("${spring.datasource.driverClassName}") 36 | private String driverClassName; 37 | 38 | @Value("${spring.datasource.initialSize}") 39 | private int initialSize; 40 | 41 | @Value("${spring.datasource.minIdle}") 42 | private int minIdle; 43 | 44 | @Value("${spring.datasource.maxActive}") 45 | private int maxActive; 46 | 47 | @Value("${spring.datasource.maxWait}") 48 | private int maxWait; 49 | 50 | @Value("${spring.datasource.timeBetweenEvictionRunsMillis}") 51 | private int timeBetweenEvictionRunsMillis; 52 | 53 | @Value("${spring.datasource.minEvictableIdleTimeMillis}") 54 | private int minEvictableIdleTimeMillis; 55 | 56 | @Value("${spring.datasource.validationQuery}") 57 | private String validationQuery; 58 | 59 | @Value("${spring.datasource.testWhileIdle}") 60 | private boolean testWhileIdle; 61 | 62 | @Value("${spring.datasource.testOnBorrow}") 63 | private boolean testOnBorrow; 64 | 65 | @Value("${spring.datasource.testOnReturn}") 66 | private boolean testOnReturn; 67 | 68 | @Value("${spring.datasource.poolPreparedStatements}") 69 | private boolean poolPreparedStatements; 70 | 71 | @Value("${spring.datasource.maxPoolPreparedStatementPerConnectionSize}") 72 | private int maxPoolPreparedStatementPerConnectionSize; 73 | 74 | @Value("${spring.datasource.filters}") 75 | private String filters; 76 | 77 | @Value("{spring.datasource.connectionProperties}") 78 | private String connectionProperties; 79 | 80 | 81 | @Primary 82 | @Bean 83 | public DataSource dataSource() { 84 | DruidDataSource datasource = new DruidDataSource(); 85 | datasource.setUrl(this.dbUrl); 86 | datasource.setUsername(userName); 87 | datasource.setPassword(password); 88 | datasource.setDriverClassName(driverClassName); 89 | 90 | //configuration 91 | datasource.setInitialSize(initialSize); 92 | datasource.setMinIdle(minIdle); 93 | datasource.setMaxActive(maxActive); 94 | datasource.setMaxWait(maxWait); 95 | datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); 96 | datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); 97 | datasource.setValidationQuery(validationQuery); 98 | datasource.setTestWhileIdle(testWhileIdle); 99 | datasource.setTestOnBorrow(testOnBorrow); 100 | datasource.setTestOnReturn(testOnReturn); 101 | datasource.setPoolPreparedStatements(poolPreparedStatements); 102 | datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize); 103 | 104 | WallConfig wallConfig = new WallConfig(); 105 | wallConfig.setMultiStatementAllow(true); 106 | WallFilter wallFilter= new WallFilter(); 107 | wallFilter.setConfig(wallConfig); 108 | List filterList = new ArrayList<>(); 109 | filterList.add(wallFilter); 110 | datasource.setProxyFilters(filterList); 111 | 112 | try { 113 | datasource.setFilters(filters); 114 | } catch (SQLException e) { 115 | e.printStackTrace(); 116 | } 117 | datasource.setConnectionProperties(connectionProperties); 118 | 119 | return datasource; 120 | } 121 | 122 | } -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/config/exception/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.config.exception; 2 | 3 | import com.example.quartz.config.response.Response; 4 | import com.example.quartz.config.response.ResultEnum; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.apache.logging.log4j.LogManager; 7 | import org.apache.logging.log4j.Logger; 8 | import org.springframework.http.HttpStatus; 9 | import org.springframework.validation.BindException; 10 | import org.springframework.web.bind.annotation.ControllerAdvice; 11 | import org.springframework.web.bind.annotation.ExceptionHandler; 12 | import org.springframework.web.bind.annotation.ResponseStatus; 13 | import org.springframework.web.bind.annotation.RestController; 14 | 15 | /** 16 | * 全局异常处理 17 | * 18 | * @author hellofly 19 | * @date 2019/4/11 20 | */ 21 | @ControllerAdvice 22 | @RestController 23 | public class GlobalExceptionHandler { 24 | 25 | private static final Logger logger = LogManager.getLogger(GlobalExceptionHandler.class); 26 | 27 | 28 | /** 29 | * BindingResult参数校验 30 | * 31 | * @param e 32 | * @return 33 | */ 34 | @ResponseStatus(HttpStatus.OK) 35 | @ExceptionHandler(BindException.class) 36 | public Response handleBindException(BindException e) { 37 | String bindingResultError = e.getBindingResult().getFieldError().getDefaultMessage(); 38 | String errorMessage = StringUtils.isEmpty(bindingResultError) ? ResultEnum.ERROR.getMessage() : bindingResultError; 39 | return Response.error(errorMessage); 40 | } 41 | 42 | /** 43 | * 全局异常 44 | * 45 | * @param e 46 | * @return 47 | */ 48 | @ResponseStatus(HttpStatus.OK) 49 | @ExceptionHandler(Exception.class) 50 | public Response handleException(Exception e) { 51 | logger.error("Exception happened: ", e); 52 | String errorMessage = StringUtils.isEmpty(e.getMessage()) ? ResultEnum.ERROR.getMessage() : e.getMessage(); 53 | return Response.error(errorMessage); 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/config/fastjson/FastJsonConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.config.fastjson; 2 | 3 | import com.alibaba.fastjson.serializer.SerializerFeature; 4 | import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; 5 | import org.springframework.boot.autoconfigure.http.HttpMessageConverters; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.http.converter.HttpMessageConverter; 9 | 10 | /** 11 | * fastJson配置类 12 | * 13 | * @author hellofly 14 | * @date 2019/4/9 15 | */ 16 | @Configuration 17 | public class FastJsonConfig { 18 | 19 | @Bean 20 | public HttpMessageConverters fastJsonHttpMessageConverters() { 21 | FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter(); 22 | com.alibaba.fastjson.support.config.FastJsonConfig fastJsonConfig = new com.alibaba.fastjson.support.config.FastJsonConfig(); 23 | fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat); 24 | fastConverter.setFastJsonConfig(fastJsonConfig); 25 | HttpMessageConverter converter = fastConverter; 26 | return new HttpMessageConverters(converter); 27 | 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/config/quartz/JobFactory.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.config.quartz; 2 | 3 | import org.quartz.spi.TriggerFiredBundle; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.beans.factory.config.AutowireCapableBeanFactory; 6 | import org.springframework.scheduling.quartz.AdaptableJobFactory; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | * QuartzJob工厂类 11 | * 12 | * @author hellofly 13 | * @date 2019/4/9 14 | */ 15 | @Component 16 | public class JobFactory extends AdaptableJobFactory { 17 | 18 | @Autowired 19 | private AutowireCapableBeanFactory beanFactory; 20 | 21 | @Override 22 | protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception { 23 | final Object job = super.createJobInstance(bundle); 24 | beanFactory.autowireBean(job); 25 | return job; 26 | } 27 | } -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/config/quartz/QuartzConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.config.quartz; 2 | 3 | import org.quartz.Scheduler; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.core.io.ClassPathResource; 8 | import org.springframework.scheduling.quartz.SchedulerFactoryBean; 9 | 10 | import javax.sql.DataSource; 11 | import java.io.IOException; 12 | import java.util.Properties; 13 | 14 | /** 15 | * Quartz配置类 16 | * 17 | * @author hellofly 18 | * @date 2019/4/9 19 | */ 20 | @Configuration 21 | public class QuartzConfig { 22 | 23 | @Autowired 24 | private DataSource dataSource; 25 | 26 | @Autowired 27 | private JobFactory jobFactory; 28 | 29 | @Bean 30 | public SchedulerFactoryBean schedulerFactoryBean() { 31 | SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean(); 32 | try { 33 | schedulerFactoryBean.setAutoStartup(true); 34 | schedulerFactoryBean.setDataSource(dataSource); 35 | schedulerFactoryBean.setJobFactory(jobFactory); 36 | schedulerFactoryBean.setQuartzProperties(properties()); 37 | schedulerFactoryBean.setOverwriteExistingJobs(true); 38 | // 延迟3s启动quartz 39 | schedulerFactoryBean.setStartupDelay(3); 40 | schedulerFactoryBean.setWaitForJobsToCompleteOnShutdown(true); 41 | } catch (IOException e) { 42 | e.printStackTrace(); 43 | } 44 | return schedulerFactoryBean; 45 | 46 | } 47 | 48 | @Bean 49 | public Properties properties() throws IOException { 50 | Properties prop = new Properties(); 51 | prop.load(new ClassPathResource("/quartz.properties").getInputStream()); 52 | return prop; 53 | } 54 | 55 | @Bean(name = "scheduler") 56 | public Scheduler scheduler() { 57 | return schedulerFactoryBean().getScheduler(); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/config/response/Response.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.config.response; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 接口返回结果封装类 7 | * 8 | * @author hellofly 9 | * @date 2019/4/9 10 | */ 11 | public class Response implements Serializable { 12 | 13 | private static final long serialVersionUID = 676473785338095291L; 14 | 15 | private int status; 16 | 17 | private String message; 18 | 19 | private T data; 20 | 21 | public Response() { 22 | } 23 | 24 | public Response(int status, String message) { 25 | this.status = status; 26 | this.message = message; 27 | } 28 | 29 | public Response(ResultEnum resultEnum) { 30 | this.status = resultEnum.getStatus(); 31 | this.message = resultEnum.getMessage(); 32 | } 33 | 34 | public Response(ResultEnum resultEnum, T data) { 35 | this.status = resultEnum.getStatus(); 36 | this.message = resultEnum.getMessage(); 37 | this.data = data; 38 | } 39 | 40 | public static Response error() { 41 | return new Response(ResultEnum.ERROR); 42 | } 43 | 44 | public static Response error(String errorMessage) { 45 | return new Response(ResultEnum.ERROR.getStatus(), errorMessage); 46 | } 47 | 48 | public static Response success() { 49 | return new Response(ResultEnum.SUCCESS); 50 | } 51 | 52 | public static Response success(T data) { 53 | return new Response(ResultEnum.SUCCESS, data); 54 | } 55 | 56 | public int getStatus() { 57 | return status; 58 | } 59 | 60 | public void setStatus(int status) { 61 | this.status = status; 62 | } 63 | 64 | public String getMessage() { 65 | return message; 66 | } 67 | 68 | public void setMessage(String message) { 69 | this.message = message; 70 | } 71 | 72 | public T getData() { 73 | return data; 74 | } 75 | 76 | public void setData(T data) { 77 | this.data = data; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/config/response/ResultEnum.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.config.response; 2 | 3 | /** 4 | * 接口返回状态码及异常信息枚举类 5 | * 6 | * @author hellofly 7 | * @date 2019/4/9 8 | */ 9 | public enum ResultEnum { 10 | 11 | SUCCESS(0, "Success"), 12 | ERROR(-1, "Error"); 13 | 14 | private int status; 15 | 16 | private String message; 17 | 18 | ResultEnum(int status, String message) { 19 | this.status = status; 20 | this.message = message; 21 | } 22 | 23 | public int getStatus() { 24 | return status; 25 | } 26 | 27 | public void setStatus(int status) { 28 | this.status = status; 29 | } 30 | 31 | public String getMessage() { 32 | return message; 33 | } 34 | 35 | public void setMessage(String message) { 36 | this.message = message; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/constants/Constant.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.constants; 2 | 3 | /** 4 | * 常量类 5 | * 6 | * @author hellofly 7 | * @date 2019/4/9 8 | */ 9 | public class Constant { 10 | 11 | /** 12 | * trigger名称前缀 13 | */ 14 | public static final String TRIGGER_PREFIX = "Trigger_"; 15 | 16 | public static final String URL = "url"; 17 | 18 | public static final String PARAMS = "params"; 19 | 20 | public static final String REQUEST_TYPE = "requestType"; 21 | 22 | /** 23 | * RequestType请求类型 24 | */ 25 | public static final String POST_JSON = "POST_JSON"; 26 | 27 | public static final String POST_FORM_DATA = "POST_FORM"; 28 | 29 | public static final String GET = "GET"; 30 | 31 | /** 32 | * job状态 NORMAL 33 | */ 34 | public static final String JOB_STATUS_NORMAL = "NORMAL"; 35 | 36 | /** 37 | * job状态 PAUSED 38 | */ 39 | public static final String JOB_STATUS_PAUSED = "PAUSED"; 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/controller/HttpJobController.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.controller; 2 | 3 | import com.example.quartz.config.response.Response; 4 | import com.example.quartz.entity.HttpJobLogs; 5 | import com.example.quartz.entity.Page; 6 | import com.example.quartz.entity.param.AddHttpJobParam; 7 | import com.example.quartz.entity.vo.HttpJobDetailVO; 8 | import com.example.quartz.service.HttpJobService; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.http.ResponseEntity; 11 | import org.springframework.ui.ModelMap; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | import javax.validation.Valid; 15 | 16 | /** 17 | * Http类型任务Controller 18 | * 19 | * @author hellofly 20 | * @date 2019/4/9 21 | */ 22 | @RestController 23 | @RequestMapping(value = "/quartz/httpJob") 24 | public class HttpJobController { 25 | 26 | @Autowired 27 | private HttpJobService httpJobService; 28 | 29 | @RequestMapping(value = "/add", method = RequestMethod.POST) 30 | public Response addPostJsonJob(@RequestBody @Valid AddHttpJobParam addHttpJobParam) { 31 | 32 | httpJobService.addHttpJob(addHttpJobParam); 33 | return Response.success(); 34 | } 35 | 36 | @RequestMapping(value = "/jobs") 37 | public Response> getJobs(@RequestParam(name = "searchParam", required = false) String searchParam, 38 | @RequestParam(name = "pageSize", required = false, defaultValue = "15") Integer pageSize, 39 | @RequestParam(name = "pageNum", required = false, defaultValue = "1") Integer pageNum) { 40 | 41 | Page result = httpJobService.getHttpJobs(searchParam, pageSize, pageNum); 42 | return Response.success(result); 43 | } 44 | 45 | @RequestMapping(value = "/historyJobs") 46 | public Response> getHistoryJobs(@RequestParam(name = "searchParam", required = false) String searchParam, 47 | @RequestParam(name = "pageSize", required = false, defaultValue = "15") Integer pageSize, 48 | @RequestParam(name = "pageNum", required = false, defaultValue = "1") Integer pageNum) { 49 | 50 | Page result = httpJobService.getHistoryHttpJobs(searchParam, pageSize, pageNum); 51 | return Response.success(result); 52 | 53 | } 54 | 55 | @RequestMapping(value = "/jobLogs") 56 | public Response> getJobLogs(@RequestParam(name = "jobName", required = false) String jobName, 57 | @RequestParam(name = "jobGroup", required = false) String jobGroup, 58 | @RequestParam(name = "searchParam", required = false) String searchParam, 59 | @RequestParam(name = "pageSize", required = false, defaultValue = "15") Integer pageSize, 60 | @RequestParam(name = "pageNum", required = false, defaultValue = "1") Integer pageNum) { 61 | 62 | Page result = httpJobService.getHttpJobLogs(jobName, jobGroup, searchParam, pageSize, pageNum); 63 | return Response.success(result); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/controller/JobController.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.controller; 2 | 3 | import com.example.quartz.config.response.Response; 4 | import com.example.quartz.service.JobManageService; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.bind.annotation.RequestMethod; 8 | import org.springframework.web.bind.annotation.RequestParam; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | /** 12 | * job操作Controller 13 | * 14 | * @author hellofly 15 | * @date 2019/4/9 16 | */ 17 | @RestController 18 | @RequestMapping(value = "/quartz/job") 19 | public class JobController { 20 | 21 | @Autowired 22 | private JobManageService jobManageService; 23 | 24 | @RequestMapping(value = "/pause", method = RequestMethod.POST) 25 | public Response pauseJob(@RequestParam(name = "jobName") String jobName, 26 | @RequestParam(name = "jobGroup") String jobGroup) { 27 | 28 | jobManageService.pauseJob(jobName, jobGroup); 29 | return Response.success(); 30 | } 31 | 32 | @RequestMapping(value = "/resume", method = RequestMethod.POST) 33 | public Response resumeJob(@RequestParam(name = "jobName") String jobName, 34 | @RequestParam(name = "jobGroup") String jobGroup) { 35 | 36 | jobManageService.resumeJob(jobName, jobGroup); 37 | return Response.success(); 38 | } 39 | 40 | @RequestMapping(value = "/delete", method = RequestMethod.POST) 41 | public Response deleteJob(@RequestParam(name = "jobName") String jobName, 42 | @RequestParam(name = "jobGroup") String jobGroup) { 43 | 44 | jobManageService.deleteJob(jobName, jobGroup); 45 | return Response.success(); 46 | } 47 | 48 | @RequestMapping(value = "/update", method = RequestMethod.POST) 49 | public Response updateJob(@RequestParam(name = "jobName") String jobName, 50 | @RequestParam(name = "jobGroup") String jobGroup, 51 | @RequestParam(name = "cronExpression") String cronExpression) { 52 | 53 | jobManageService.updateCronExpression(jobName, jobGroup, cronExpression); 54 | return Response.success(); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/entity/HttpJobDetails.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.entity; 2 | 3 | import com.alibaba.fastjson.annotation.JSONField; 4 | 5 | import java.io.Serializable; 6 | import java.util.Date; 7 | 8 | public class HttpJobDetails implements Serializable { 9 | 10 | private static final long serialVersionUID = -50190044894125802L; 11 | 12 | private Integer id; 13 | 14 | private String jobName; 15 | 16 | private String jobGroup; 17 | 18 | private String description; 19 | 20 | private String requestType; 21 | 22 | private String httpUrl; 23 | 24 | private String httpParams; 25 | 26 | @JSONField(format = "yyyy-MM-dd HH:mm:ss") 27 | private Date createTime; 28 | 29 | @JSONField(format = "yyyy-MM-dd HH:mm:ss") 30 | private Date updateTime; 31 | 32 | public Integer getId() { 33 | return id; 34 | } 35 | 36 | public void setId(Integer id) { 37 | this.id = id; 38 | } 39 | 40 | public String getJobName() { 41 | return jobName; 42 | } 43 | 44 | public void setJobName(String jobName) { 45 | this.jobName = jobName; 46 | } 47 | 48 | public String getJobGroup() { 49 | return jobGroup; 50 | } 51 | 52 | public void setJobGroup(String jobGroup) { 53 | this.jobGroup = jobGroup; 54 | } 55 | 56 | public String getDescription() { 57 | return description; 58 | } 59 | 60 | public void setDescription(String description) { 61 | this.description = description; 62 | } 63 | 64 | public String getRequestType() { 65 | return requestType; 66 | } 67 | 68 | public void setRequestType(String requestType) { 69 | this.requestType = requestType; 70 | } 71 | 72 | public String getHttpUrl() { 73 | return httpUrl; 74 | } 75 | 76 | public void setHttpUrl(String httpUrl) { 77 | this.httpUrl = httpUrl; 78 | } 79 | 80 | public String getHttpParams() { 81 | return httpParams; 82 | } 83 | 84 | public void setHttpParams(String httpParams) { 85 | this.httpParams = httpParams; 86 | } 87 | 88 | public Date getCreateTime() { 89 | return createTime; 90 | } 91 | 92 | public void setCreateTime(Date createTime) { 93 | this.createTime = createTime; 94 | } 95 | 96 | public Date getUpdateTime() { 97 | return updateTime; 98 | } 99 | 100 | public void setUpdateTime(Date updateTime) { 101 | this.updateTime = updateTime; 102 | } 103 | 104 | @Override 105 | public String toString() { 106 | return "HttpJobDetails{" + 107 | "id=" + id + 108 | ", jobName='" + jobName + '\'' + 109 | ", jobGroup='" + jobGroup + '\'' + 110 | ", description='" + description + '\'' + 111 | ", requestType='" + requestType + '\'' + 112 | ", httpUrl='" + httpUrl + '\'' + 113 | ", httpParams='" + httpParams + '\'' + 114 | ", createTime=" + createTime + 115 | ", updateTime=" + updateTime + 116 | '}'; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/entity/HttpJobLogs.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.entity; 2 | 3 | import com.alibaba.fastjson.annotation.JSONField; 4 | 5 | import java.io.Serializable; 6 | import java.util.Date; 7 | 8 | public class HttpJobLogs implements Serializable { 9 | 10 | private static final long serialVersionUID = 2259203435361774854L; 11 | 12 | private Integer id; 13 | 14 | private String jobName; 15 | 16 | private String jobGroup; 17 | 18 | private String requestType; 19 | 20 | private String httpUrl; 21 | 22 | private String httpParams; 23 | 24 | @JSONField(format = "yyyy-MM-dd HH:mm:ss") 25 | private Date fireTime; 26 | 27 | private String result; 28 | 29 | public Integer getId() { 30 | return id; 31 | } 32 | 33 | public void setId(Integer id) { 34 | this.id = id; 35 | } 36 | 37 | public String getJobName() { 38 | return jobName; 39 | } 40 | 41 | public void setJobName(String jobName) { 42 | this.jobName = jobName; 43 | } 44 | 45 | public String getJobGroup() { 46 | return jobGroup; 47 | } 48 | 49 | public void setJobGroup(String jobGroup) { 50 | this.jobGroup = jobGroup; 51 | } 52 | 53 | public String getRequestType() { 54 | return requestType; 55 | } 56 | 57 | public void setRequestType(String requestType) { 58 | this.requestType = requestType; 59 | } 60 | 61 | public String getHttpUrl() { 62 | return httpUrl; 63 | } 64 | 65 | public void setHttpUrl(String httpUrl) { 66 | this.httpUrl = httpUrl; 67 | } 68 | 69 | public String getHttpParams() { 70 | return httpParams; 71 | } 72 | 73 | public void setHttpParams(String httpParams) { 74 | this.httpParams = httpParams; 75 | } 76 | 77 | public Date getFireTime() { 78 | return fireTime; 79 | } 80 | 81 | public void setFireTime(Date fireTime) { 82 | this.fireTime = fireTime; 83 | } 84 | 85 | public String getResult() { 86 | return result; 87 | } 88 | 89 | public void setResult(String result) { 90 | this.result = result; 91 | } 92 | 93 | @Override 94 | public String toString() { 95 | return "HttpJobLogs{" + 96 | "id=" + id + 97 | ", jobName='" + jobName + '\'' + 98 | ", jobGroup='" + jobGroup + '\'' + 99 | ", requestType='" + requestType + '\'' + 100 | ", httpUrl='" + httpUrl + '\'' + 101 | ", httpParams='" + httpParams + '\'' + 102 | ", fireTime=" + fireTime + 103 | ", result='" + result + '\'' + 104 | '}'; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/entity/Page.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.entity; 2 | 3 | import java.util.List; 4 | 5 | public class Page { 6 | 7 | private Integer pageNum; 8 | 9 | private Integer pageSize; 10 | 11 | private Integer count; 12 | 13 | private Integer pageCount; 14 | 15 | private Integer totalCount; 16 | 17 | private List resultList; 18 | 19 | public Integer getPageNum() { 20 | return pageNum; 21 | } 22 | 23 | public void setPageNum(Integer pageNum) { 24 | this.pageNum = pageNum; 25 | } 26 | 27 | public Integer getPageSize() { 28 | return pageSize; 29 | } 30 | 31 | public void setPageSize(Integer pageSize) { 32 | this.pageSize = pageSize; 33 | } 34 | 35 | public Integer getCount() { 36 | return count; 37 | } 38 | 39 | public void setCount(Integer count) { 40 | this.count = count; 41 | } 42 | 43 | public Integer getPageCount() { 44 | return pageCount; 45 | } 46 | 47 | public void setPageCount(Integer pageCount) { 48 | this.pageCount = pageCount; 49 | } 50 | 51 | public Integer getTotalCount() { 52 | return totalCount; 53 | } 54 | 55 | public void setTotalCount(Integer totalCount) { 56 | this.totalCount = totalCount; 57 | 58 | if (totalCount % pageSize == 0) { 59 | this.pageCount = totalCount / pageSize; 60 | } else { 61 | this.pageCount = totalCount / pageSize + 1; 62 | } 63 | } 64 | 65 | public List getResultList() { 66 | return resultList; 67 | } 68 | 69 | public void setResultList(List resultList) { 70 | this.resultList = resultList; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/entity/param/AddHttpJobParam.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.entity.param; 2 | 3 | 4 | import javax.validation.constraints.NotEmpty; 5 | 6 | public class AddHttpJobParam { 7 | 8 | @NotEmpty(message = "任务名称不能为空") 9 | private String jobName; 10 | 11 | @NotEmpty(message = "任务分组不能为空") 12 | private String jobGroup; 13 | 14 | private String description; 15 | 16 | @NotEmpty(message = "请求类型不能为空") 17 | private String requestType; 18 | 19 | @NotEmpty(message = "请求URL不能为空") 20 | private String url; 21 | 22 | private String params; 23 | 24 | @NotEmpty(message = "cron表达式不能为空") 25 | private String cronExpression; 26 | 27 | public String getJobName() { 28 | return jobName; 29 | } 30 | 31 | public void setJobName(String jobName) { 32 | this.jobName = jobName; 33 | } 34 | 35 | public String getJobGroup() { 36 | return jobGroup; 37 | } 38 | 39 | public void setJobGroup(String jobGroup) { 40 | this.jobGroup = jobGroup; 41 | } 42 | 43 | public String getDescription() { 44 | return description; 45 | } 46 | 47 | public void setDescription(String description) { 48 | this.description = description; 49 | } 50 | 51 | public String getRequestType() { 52 | return requestType; 53 | } 54 | 55 | public void setRequestType(String requestType) { 56 | this.requestType = requestType; 57 | } 58 | 59 | public String getUrl() { 60 | return url; 61 | } 62 | 63 | public void setUrl(String url) { 64 | this.url = url; 65 | } 66 | 67 | public String getParams() { 68 | return params; 69 | } 70 | 71 | public void setParams(String params) { 72 | this.params = params; 73 | } 74 | 75 | public String getCronExpression() { 76 | return cronExpression; 77 | } 78 | 79 | public void setCronExpression(String cronExpression) { 80 | this.cronExpression = cronExpression; 81 | } 82 | 83 | @Override 84 | public String toString() { 85 | return "AddHttpJobParam{" + 86 | "jobName='" + jobName + '\'' + 87 | ", jobGroup='" + jobGroup + '\'' + 88 | ", requestType='" + requestType + '\'' + 89 | ", url='" + url + '\'' + 90 | ", params='" + params + '\'' + 91 | ", cronExpression='" + cronExpression + '\'' + 92 | '}'; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/entity/vo/HttpJobDetailVO.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.entity.vo; 2 | 3 | import com.alibaba.fastjson.annotation.JSONField; 4 | 5 | import java.util.Date; 6 | 7 | public class HttpJobDetailVO { 8 | 9 | private String jobName; 10 | 11 | private String jobGroup; 12 | 13 | private String description; 14 | 15 | private String jobStatusInfo; 16 | 17 | private String requestType; 18 | 19 | private String httpUrl; 20 | 21 | private String httpParams; 22 | 23 | @JSONField(format = "yyyy-MM-dd HH:mm:ss") 24 | private Date nextFireTime; 25 | 26 | @JSONField(format = "yyyy-MM-dd HH:mm:ss") 27 | private Date createTime; 28 | 29 | private String cronExpression; 30 | 31 | public String getJobName() { 32 | return jobName; 33 | } 34 | 35 | public void setJobName(String jobName) { 36 | this.jobName = jobName; 37 | } 38 | 39 | public String getJobGroup() { 40 | return jobGroup; 41 | } 42 | 43 | public void setJobGroup(String jobGroup) { 44 | this.jobGroup = jobGroup; 45 | } 46 | 47 | public String getDescription() { 48 | return description; 49 | } 50 | 51 | public void setDescription(String description) { 52 | this.description = description; 53 | } 54 | 55 | public String getJobStatusInfo() { 56 | return jobStatusInfo; 57 | } 58 | 59 | public void setJobStatusInfo(String jobStatusInfo) { 60 | this.jobStatusInfo = jobStatusInfo; 61 | } 62 | 63 | public String getRequestType() { 64 | return requestType; 65 | } 66 | 67 | public void setRequestType(String requestType) { 68 | this.requestType = requestType; 69 | } 70 | 71 | public String getHttpUrl() { 72 | return httpUrl; 73 | } 74 | 75 | public void setHttpUrl(String httpUrl) { 76 | this.httpUrl = httpUrl; 77 | } 78 | 79 | public String getHttpParams() { 80 | return httpParams; 81 | } 82 | 83 | public void setHttpParams(String httpParams) { 84 | this.httpParams = httpParams; 85 | } 86 | 87 | public Date getNextFireTime() { 88 | return nextFireTime; 89 | } 90 | 91 | public void setNextFireTime(Date nextFireTime) { 92 | this.nextFireTime = nextFireTime; 93 | } 94 | 95 | public Date getCreateTime() { 96 | return createTime; 97 | } 98 | 99 | public void setCreateTime(Date createTime) { 100 | this.createTime = createTime; 101 | } 102 | 103 | public String getCronExpression() { 104 | return cronExpression; 105 | } 106 | 107 | public void setCronExpression(String cronExpression) { 108 | this.cronExpression = cronExpression; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/job/HttpGetJob.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.job; 2 | 3 | import com.example.quartz.constants.Constant; 4 | import com.example.quartz.entity.HttpJobLogs; 5 | import com.example.quartz.mapper.HttpJobLogsMapper; 6 | import com.example.quartz.util.HttpClientUtil; 7 | import org.apache.logging.log4j.LogManager; 8 | import org.apache.logging.log4j.Logger; 9 | import org.quartz.*; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | 12 | import java.util.Map; 13 | 14 | @DisallowConcurrentExecution 15 | public class HttpGetJob implements Job { 16 | 17 | private static final Logger logger = LogManager.getLogger(HttpPostJsonJob.class); 18 | 19 | @Autowired 20 | private HttpJobLogsMapper httpJobLogsMapper; 21 | 22 | @Override 23 | public void execute(JobExecutionContext jobExecutionContext) { 24 | JobDetail jobDetail = jobExecutionContext.getJobDetail(); 25 | String jobName = jobDetail.getKey().getName(); 26 | String jobGroup = jobDetail.getKey().getGroup(); 27 | 28 | Map jobParamsMap = jobDetail.getJobDataMap(); 29 | 30 | String requestType = (String) jobParamsMap.get(Constant.REQUEST_TYPE); 31 | String url = (String) jobParamsMap.get(Constant.URL); 32 | Map paramMap = (Map) jobParamsMap.get(Constant.PARAMS); 33 | 34 | HttpJobLogs httpJobLogs = new HttpJobLogs(); 35 | httpJobLogs.setJobName(jobName); 36 | httpJobLogs.setJobGroup(jobGroup); 37 | httpJobLogs.setRequestType(requestType); 38 | httpJobLogs.setHttpUrl(url); 39 | if (null != paramMap && paramMap.size() > 0) { 40 | httpJobLogs.setHttpParams(paramMap.toString()); 41 | } 42 | 43 | String result = HttpClientUtil.getMap(url, paramMap); 44 | httpJobLogs.setResult(result); 45 | 46 | logger.info("Success in execute [{}_{}]", jobName, jobGroup); 47 | 48 | httpJobLogsMapper.insertSelective(httpJobLogs); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/job/HttpPostFormDataJob.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.job; 2 | 3 | import com.example.quartz.constants.Constant; 4 | import com.example.quartz.entity.HttpJobLogs; 5 | import com.example.quartz.mapper.HttpJobLogsMapper; 6 | import com.example.quartz.util.HttpClientUtil; 7 | import org.apache.logging.log4j.LogManager; 8 | import org.apache.logging.log4j.Logger; 9 | import org.quartz.*; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | 12 | import java.util.Map; 13 | 14 | @DisallowConcurrentExecution 15 | public class HttpPostFormDataJob implements Job { 16 | 17 | private static final Logger logger = LogManager.getLogger(HttpPostJsonJob.class); 18 | 19 | @Autowired 20 | private HttpJobLogsMapper httpJobLogsMapper; 21 | 22 | @Override 23 | public void execute(JobExecutionContext jobExecutionContext) { 24 | JobDetail jobDetail = jobExecutionContext.getJobDetail(); 25 | String jobName = jobDetail.getKey().getName(); 26 | String jobGroup = jobDetail.getKey().getGroup(); 27 | 28 | Map jobParamsMap = jobDetail.getJobDataMap(); 29 | 30 | String requestType = (String) jobParamsMap.get(Constant.REQUEST_TYPE); 31 | String url = (String) jobParamsMap.get(Constant.URL); 32 | Map formDataParamMap = (Map) jobParamsMap.get(Constant.PARAMS); 33 | 34 | HttpJobLogs httpJobLogs = new HttpJobLogs(); 35 | httpJobLogs.setJobName(jobName); 36 | httpJobLogs.setJobGroup(jobGroup); 37 | httpJobLogs.setRequestType(requestType); 38 | httpJobLogs.setHttpUrl(url); 39 | if (null != formDataParamMap && formDataParamMap.size() > 0) { 40 | httpJobLogs.setHttpParams(formDataParamMap.toString()); 41 | } 42 | 43 | String result = HttpClientUtil.postFormData(url, formDataParamMap); 44 | httpJobLogs.setResult(result); 45 | 46 | logger.info("Success in execute [{}_{}]", jobName, jobGroup); 47 | 48 | httpJobLogsMapper.insertSelective(httpJobLogs); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/job/HttpPostJsonJob.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.job; 2 | 3 | import com.example.quartz.constants.Constant; 4 | import com.example.quartz.entity.HttpJobLogs; 5 | import com.example.quartz.mapper.HttpJobLogsMapper; 6 | import com.example.quartz.util.HttpClientUtil; 7 | import org.apache.logging.log4j.LogManager; 8 | import org.apache.logging.log4j.Logger; 9 | import org.quartz.*; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | 12 | import java.util.Map; 13 | 14 | @DisallowConcurrentExecution 15 | public class HttpPostJsonJob implements Job { 16 | 17 | private static final Logger logger = LogManager.getLogger(HttpPostJsonJob.class); 18 | 19 | @Autowired 20 | private HttpJobLogsMapper httpJobLogsMapper; 21 | 22 | @Override 23 | public void execute(JobExecutionContext jobExecutionContext) { 24 | JobDetail jobDetail = jobExecutionContext.getJobDetail(); 25 | String jobName = jobDetail.getKey().getName(); 26 | String jobGroup = jobDetail.getKey().getGroup(); 27 | 28 | Map jobParamsMap = jobDetail.getJobDataMap(); 29 | 30 | String requestType = (String) jobParamsMap.get(Constant.REQUEST_TYPE); 31 | String url = (String) jobParamsMap.get(Constant.URL); 32 | String jsonParam = (String) jobParamsMap.get(Constant.PARAMS); 33 | 34 | HttpJobLogs httpJobLogs = new HttpJobLogs(); 35 | httpJobLogs.setJobName(jobName); 36 | httpJobLogs.setJobGroup(jobGroup); 37 | httpJobLogs.setRequestType(requestType); 38 | httpJobLogs.setHttpUrl(url); 39 | httpJobLogs.setHttpParams(jsonParam); 40 | 41 | String result = HttpClientUtil.postJson(url, jsonParam); 42 | httpJobLogs.setResult(result); 43 | 44 | logger.info("Success in execute [{}_{}]", jobName, jobGroup); 45 | 46 | httpJobLogsMapper.insertSelective(httpJobLogs); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/mapper/HttpJobDetailsMapper.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.mapper; 2 | 3 | import com.example.quartz.entity.HttpJobDetails; 4 | import com.example.quartz.entity.vo.HttpJobDetailVO; 5 | import org.apache.ibatis.annotations.Param; 6 | import org.springframework.stereotype.Repository; 7 | 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | @Repository 12 | public interface HttpJobDetailsMapper { 13 | 14 | HttpJobDetails selectByJobNameAndJobGroup(@Param("jobName") String jobName, @Param("jobGroup") String jobGroup); 15 | 16 | int insertSelective(HttpJobDetails httpJobDetails); 17 | 18 | List selectHttpJobs(Map map); 19 | 20 | Integer selectHttpJobsCount(Map map); 21 | 22 | List selectHistoryHttpJobs(Map map); 23 | 24 | Integer selectHistoryHttpJobsCount(Map map); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/mapper/HttpJobLogsMapper.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.mapper; 2 | 3 | import com.example.quartz.entity.HttpJobLogs; 4 | import org.springframework.stereotype.Repository; 5 | 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | @Repository 10 | public interface HttpJobLogsMapper { 11 | 12 | int insertSelective(HttpJobLogs httpJobLogs); 13 | 14 | List selectHttpJobLogs(Map map); 15 | 16 | Integer selectHttpJobLogsCount(Map map); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/service/HttpJobService.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.service; 2 | 3 | import com.example.quartz.entity.HttpJobLogs; 4 | import com.example.quartz.entity.Page; 5 | import com.example.quartz.entity.param.AddHttpJobParam; 6 | import com.example.quartz.entity.vo.HttpJobDetailVO; 7 | 8 | /** 9 | * Http类型任务Service 10 | * 11 | * @author hellofly 12 | * @date 2019/4/9 13 | */ 14 | public interface HttpJobService { 15 | 16 | /** 17 | * 添加http类型job 18 | * 19 | * @param addHttpJobParam 20 | */ 21 | void addHttpJob(AddHttpJobParam addHttpJobParam); 22 | 23 | /** 24 | * 查看正在进行的http类型job 25 | * 26 | * @param searchParam 27 | * @param pageSize 28 | * @param pageNum 29 | * @return 30 | */ 31 | Page getHttpJobs(String searchParam, Integer pageSize, Integer pageNum); 32 | 33 | /** 34 | * 查看历史http类型job 35 | * 36 | * @param searchParam 37 | * @param pageSize 38 | * @param pageNum 39 | * @return 40 | */ 41 | Page getHistoryHttpJobs(String searchParam, Integer pageSize, Integer pageNum); 42 | 43 | /** 44 | * 查看http类型的job执行记录 45 | * 46 | * @param jobName 47 | * @param jobGroup 48 | * @param searchParam 49 | * @param pageSize 50 | * @param pageNum 51 | * @return 52 | */ 53 | Page getHttpJobLogs(String jobName, String jobGroup, String searchParam, Integer pageSize, Integer pageNum); 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/service/JobManageService.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.service; 2 | 3 | public interface JobManageService { 4 | 5 | /** 6 | * 暂停任务 7 | * 8 | * @param jobName 9 | * @param jobGroup 10 | */ 11 | void pauseJob(String jobName, String jobGroup); 12 | 13 | /** 14 | * 恢复任务 15 | * 16 | * @param jobName 17 | * @param jobGroup 18 | */ 19 | void resumeJob(String jobName, String jobGroup); 20 | 21 | /** 22 | * 删除任务 23 | * 24 | * @param jobName 25 | * @param jobGroup 26 | */ 27 | void deleteJob(String jobName, String jobGroup); 28 | 29 | /** 30 | * 更新任务cron表达式 31 | * 32 | * @param jobName 33 | * @param jobGroup 34 | * @param cronExpression 35 | */ 36 | void updateCronExpression(String jobName, String jobGroup, String cronExpression); 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/service/impl/HttpJobServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.service.impl; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.example.quartz.constants.Constant; 5 | import com.example.quartz.entity.HttpJobDetails; 6 | import com.example.quartz.entity.HttpJobLogs; 7 | import com.example.quartz.entity.Page; 8 | import com.example.quartz.entity.param.AddHttpJobParam; 9 | import com.example.quartz.entity.vo.HttpJobDetailVO; 10 | import com.example.quartz.job.HttpGetJob; 11 | import com.example.quartz.job.HttpPostFormDataJob; 12 | import com.example.quartz.job.HttpPostJsonJob; 13 | import com.example.quartz.mapper.HttpJobDetailsMapper; 14 | import com.example.quartz.mapper.HttpJobLogsMapper; 15 | import com.example.quartz.service.HttpJobService; 16 | import com.example.quartz.util.JobUtil; 17 | import com.example.quartz.util.JsonValidUtil; 18 | import org.apache.commons.lang3.StringUtils; 19 | import org.apache.logging.log4j.LogManager; 20 | import org.apache.logging.log4j.Logger; 21 | import org.quartz.*; 22 | import org.springframework.beans.factory.annotation.Autowired; 23 | import org.springframework.stereotype.Service; 24 | import org.springframework.transaction.annotation.Transactional; 25 | 26 | import javax.annotation.Resource; 27 | import java.util.HashMap; 28 | import java.util.List; 29 | import java.util.Map; 30 | 31 | /** 32 | * Http类型任务Service 33 | * 34 | * @author hellofly 35 | * @date 2019/4/9 36 | */ 37 | @Service 38 | public class HttpJobServiceImpl implements HttpJobService { 39 | 40 | private static final Logger logger = LogManager.getLogger(HttpJobServiceImpl.class); 41 | 42 | @Autowired 43 | private Scheduler scheduler; 44 | 45 | @Autowired 46 | private HttpJobLogsMapper httpJobLogsMapper; 47 | 48 | @Autowired 49 | private HttpJobDetailsMapper httpJobDetailsMapper; 50 | 51 | @Resource 52 | private JobUtil jobUtil; 53 | 54 | @Override 55 | @Transactional(rollbackFor = Exception.class) 56 | public void addHttpJob(AddHttpJobParam addHttpJobParam) { 57 | 58 | String jobName = addHttpJobParam.getJobName(); 59 | String jobGroup = addHttpJobParam.getJobGroup(); 60 | HttpJobDetails httpJobDetails = httpJobDetailsMapper.selectByJobNameAndJobGroup(jobName, jobGroup); 61 | if (httpJobDetails != null) { 62 | //通过jobName和jobGroup确保任务的唯一性 63 | throw new RuntimeException("任务名称重复!"); 64 | } 65 | 66 | String requestType = addHttpJobParam.getRequestType(); 67 | String description = addHttpJobParam.getDescription(); 68 | String cronExpression = addHttpJobParam.getCronExpression(); 69 | String url = addHttpJobParam.getUrl(); 70 | String jsonParamsStr = addHttpJobParam.getParams(); 71 | 72 | httpJobDetails = new HttpJobDetails(); 73 | httpJobDetails.setJobName(jobName); 74 | httpJobDetails.setJobGroup(jobGroup); 75 | httpJobDetails.setDescription(description); 76 | httpJobDetails.setRequestType(requestType); 77 | httpJobDetails.setHttpUrl(url); 78 | if (!JsonValidUtil.isJson(jsonParamsStr)) { 79 | throw new RuntimeException("请将请求参数转为合法的json字符串!"); 80 | } 81 | 82 | Map jobParamsMap = new HashMap<>(); 83 | jobParamsMap.put(Constant.URL, url); 84 | jobParamsMap.put(Constant.PARAMS, jsonParamsStr); 85 | 86 | JobDetail jobDetail = null; 87 | //根据不同类型的job构建job信息 88 | switch (requestType) { 89 | //postJson 90 | case Constant.POST_JSON: 91 | jobDetail = JobBuilder.newJob(HttpPostJsonJob.class) 92 | .withIdentity(jobName, jobGroup) 93 | .build(); 94 | 95 | //jsonStr的参数直接用 96 | if (StringUtils.isNotEmpty(jsonParamsStr)) { 97 | httpJobDetails.setHttpParams(jsonParamsStr); 98 | } 99 | break; 100 | 101 | //postFormData 102 | case Constant.POST_FORM_DATA: 103 | jobDetail = JobBuilder.newJob(HttpPostFormDataJob.class) 104 | .withIdentity(jobName, jobGroup) 105 | .build(); 106 | 107 | //jsonStr参数转为formData的Map 108 | Map formDataParamMap; 109 | if (StringUtils.isEmpty(jsonParamsStr)) { 110 | formDataParamMap = null; 111 | } else { 112 | formDataParamMap = JSON.parseObject(jsonParamsStr, Map.class); 113 | httpJobDetails.setHttpParams(formDataParamMap.toString()); 114 | } 115 | jobParamsMap.put(Constant.PARAMS, formDataParamMap); 116 | 117 | break; 118 | 119 | //get 120 | case Constant.GET: 121 | jobDetail = JobBuilder.newJob(HttpGetJob.class) 122 | .withIdentity(jobName, jobGroup) 123 | .build(); 124 | 125 | //jsonStr参数转为formData的Map 126 | Map paramMap; 127 | if (StringUtils.isEmpty(jsonParamsStr)) { 128 | paramMap = null; 129 | } else { 130 | paramMap = JSON.parseObject(jsonParamsStr, Map.class); 131 | httpJobDetails.setHttpParams(paramMap.toString()); 132 | } 133 | jobParamsMap.put(Constant.PARAMS, paramMap); 134 | 135 | break; 136 | } 137 | 138 | //任务信息 139 | jobDetail.getJobDataMap().putAll(jobParamsMap); 140 | jobDetail.getJobDataMap().put(Constant.REQUEST_TYPE, requestType); 141 | 142 | //表达式调度构建器(即任务执行的时间) 143 | CronScheduleBuilder scheduleBuilder; 144 | try { 145 | scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression); 146 | } catch (Exception e) { 147 | throw new RuntimeException("Illegal CronExpression!"); 148 | } 149 | 150 | TriggerKey triggerKey = jobUtil.getTriggerKeyByJob(jobName, jobGroup); 151 | 152 | //构建一个trigger 153 | CronTrigger trigger = TriggerBuilder.newTrigger() 154 | .withIdentity(triggerKey) 155 | .startNow() 156 | .withSchedule(scheduleBuilder).build(); 157 | 158 | try { 159 | // 调度容器设置JobDetail和Trigger 160 | scheduler.scheduleJob(jobDetail, trigger); 161 | // 启动 162 | if (!scheduler.isShutdown()) { 163 | scheduler.start(); 164 | } 165 | } catch (Exception e) { 166 | throw new RuntimeException("Schedule Exception.", e); 167 | } 168 | 169 | httpJobDetailsMapper.insertSelective(httpJobDetails); 170 | logger.info("Success in addJob, [{}]-[{}]", jobName, jobGroup); 171 | 172 | } 173 | 174 | @Override 175 | public Page getHttpJobs(String searchParam, Integer pageSize, Integer pageNum) { 176 | Integer beginIndex = (pageNum - 1) * pageSize; 177 | 178 | Map sqlMap = new HashMap<>(); 179 | sqlMap.put("searchParam", searchParam); 180 | sqlMap.put("pageSize", pageSize); 181 | sqlMap.put("beginIndex", beginIndex); 182 | 183 | List httpJobDetailVOList = httpJobDetailsMapper.selectHttpJobs(sqlMap); 184 | 185 | for (HttpJobDetailVO httpJobDetailVO : httpJobDetailVOList) { 186 | //设置jobStatusInfo 187 | String jobStatusInfo = jobUtil.getJobStatusInfo(httpJobDetailVO.getJobName(), httpJobDetailVO.getJobGroup()); 188 | httpJobDetailVO.setJobStatusInfo(jobStatusInfo); 189 | //任务状态正常,根据cron表达式计算下次运行时间 190 | if (StringUtils.equals(jobStatusInfo, Constant.JOB_STATUS_NORMAL)) { 191 | httpJobDetailVO.setNextFireTime(jobUtil.getNextFireDate(httpJobDetailVO.getCronExpression())); 192 | } 193 | } 194 | 195 | Page httpJobDetailVOPageVO = new Page<>(); 196 | httpJobDetailVOPageVO.setPageNum(pageNum); 197 | httpJobDetailVOPageVO.setPageSize(pageSize); 198 | httpJobDetailVOPageVO.setCount(httpJobDetailVOList.size()); 199 | httpJobDetailVOPageVO.setTotalCount(httpJobDetailsMapper.selectHttpJobsCount(sqlMap)); 200 | httpJobDetailVOPageVO.setResultList(httpJobDetailVOList); 201 | 202 | return httpJobDetailVOPageVO; 203 | } 204 | 205 | @Override 206 | public Page getHistoryHttpJobs(String searchParam, Integer pageSize, Integer pageNum) { 207 | Integer beginIndex = (pageNum - 1) * pageSize; 208 | 209 | Map sqlMap = new HashMap<>(); 210 | sqlMap.put("searchParam", searchParam); 211 | sqlMap.put("pageSize", pageSize); 212 | sqlMap.put("beginIndex", beginIndex); 213 | 214 | List httpJobDetailVOList = httpJobDetailsMapper.selectHistoryHttpJobs(sqlMap); 215 | 216 | Page httpJobDetailVOPageVO = new Page<>(); 217 | httpJobDetailVOPageVO.setPageNum(pageNum); 218 | httpJobDetailVOPageVO.setPageSize(pageSize); 219 | httpJobDetailVOPageVO.setCount(httpJobDetailVOList.size()); 220 | httpJobDetailVOPageVO.setTotalCount(httpJobDetailsMapper.selectHistoryHttpJobsCount(sqlMap)); 221 | httpJobDetailVOPageVO.setResultList(httpJobDetailVOList); 222 | 223 | return httpJobDetailVOPageVO; 224 | } 225 | 226 | @Override 227 | public Page getHttpJobLogs(String jobName, String jobGroup, String searchParam, Integer pageSize, Integer pageNum) { 228 | Integer beginIndex = (pageNum - 1) * pageSize; 229 | 230 | Map sqlMap = new HashMap<>(); 231 | sqlMap.put("jobName", jobName); 232 | sqlMap.put("jobGroup", jobGroup); 233 | sqlMap.put("searchParam", searchParam); 234 | sqlMap.put("pageSize", pageSize); 235 | sqlMap.put("beginIndex", beginIndex); 236 | 237 | List httpJobLogsList = httpJobLogsMapper.selectHttpJobLogs(sqlMap); 238 | 239 | Page httpJobDetailVOPageVO = new Page<>(); 240 | httpJobDetailVOPageVO.setPageNum(pageNum); 241 | httpJobDetailVOPageVO.setPageSize(pageSize); 242 | httpJobDetailVOPageVO.setCount(httpJobLogsList.size()); 243 | httpJobDetailVOPageVO.setTotalCount(httpJobLogsMapper.selectHttpJobLogsCount(sqlMap)); 244 | httpJobDetailVOPageVO.setResultList(httpJobLogsList); 245 | 246 | return httpJobDetailVOPageVO; 247 | } 248 | 249 | } 250 | -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/service/impl/JobManageServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.service.impl; 2 | 3 | import com.example.quartz.constants.Constant; 4 | import com.example.quartz.service.JobManageService; 5 | import com.example.quartz.util.JobUtil; 6 | import org.apache.commons.lang3.StringUtils; 7 | import org.quartz.*; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Service; 10 | 11 | import javax.annotation.Resource; 12 | 13 | @Service 14 | public class JobManageServiceImpl implements JobManageService { 15 | 16 | @Autowired 17 | private Scheduler scheduler; 18 | 19 | @Resource 20 | private JobUtil jobUtil; 21 | 22 | @Override 23 | public void pauseJob(String jobName, String jobGroup) { 24 | String jobStatusInfo = jobUtil.getJobStatusInfo(jobName, jobGroup); 25 | if (StringUtils.equals(jobStatusInfo, Constant.JOB_STATUS_PAUSED)) { 26 | throw new RuntimeException("当前任务已是暂停状态!"); 27 | } 28 | JobKey jobKey = JobKey.jobKey(jobName, jobGroup); 29 | try { 30 | scheduler.pauseJob(jobKey); 31 | } catch (SchedulerException e) { 32 | throw new RuntimeException(e); 33 | } 34 | } 35 | 36 | @Override 37 | public void resumeJob(String jobName, String jobGroup) { 38 | String jobStatusInfo = jobUtil.getJobStatusInfo(jobName, jobGroup); 39 | if (!StringUtils.equals(jobStatusInfo, Constant.JOB_STATUS_PAUSED)) { 40 | throw new RuntimeException("任务仅在暂停状态时才能恢复!"); 41 | } 42 | JobKey jobKey = JobKey.jobKey(jobName, jobGroup); 43 | try { 44 | scheduler.resumeJob(jobKey); 45 | } catch (SchedulerException e) { 46 | throw new RuntimeException(e); 47 | } 48 | } 49 | 50 | @Override 51 | public void deleteJob(String jobName, String jobGroup) { 52 | JobKey jobKey = JobKey.jobKey(jobName, jobGroup); 53 | TriggerKey triggerKey = jobUtil.getTriggerKeyByJob(jobName, jobGroup); 54 | try { 55 | scheduler.pauseTrigger(triggerKey); 56 | scheduler.unscheduleJob(triggerKey); 57 | scheduler.deleteJob(jobKey); 58 | } catch (SchedulerException e) { 59 | throw new RuntimeException(e); 60 | } 61 | } 62 | 63 | @Override 64 | public void updateCronExpression(String jobName, String jobGroup, String cronExpression) { 65 | TriggerKey triggerKey = jobUtil.getTriggerKeyByJob(jobName, jobGroup); 66 | 67 | //表达式调度构建器(即任务执行的时间) 68 | CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression); 69 | 70 | //按新的cronExpression重新构建trigger 71 | CronTrigger trigger = TriggerBuilder.newTrigger() 72 | .withIdentity(triggerKey) 73 | .withSchedule(scheduleBuilder).build(); 74 | try { 75 | scheduler.rescheduleJob(triggerKey, trigger); 76 | } catch (SchedulerException e) { 77 | throw new RuntimeException(e); 78 | } 79 | 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/util/HttpClientUtil.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.util; 2 | 3 | import org.apache.commons.codec.Charsets; 4 | import org.apache.commons.lang3.StringUtils; 5 | import org.apache.http.NameValuePair; 6 | import org.apache.http.client.config.RequestConfig; 7 | import org.apache.http.client.entity.UrlEncodedFormEntity; 8 | import org.apache.http.client.methods.CloseableHttpResponse; 9 | import org.apache.http.client.methods.HttpGet; 10 | import org.apache.http.client.methods.HttpPost; 11 | import org.apache.http.client.utils.HttpClientUtils; 12 | import org.apache.http.client.utils.URIBuilder; 13 | import org.apache.http.entity.StringEntity; 14 | import org.apache.http.impl.client.CloseableHttpClient; 15 | import org.apache.http.impl.client.HttpClients; 16 | import org.apache.http.message.BasicNameValuePair; 17 | import org.apache.http.util.EntityUtils; 18 | import org.apache.logging.log4j.LogManager; 19 | import org.apache.logging.log4j.Logger; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | import java.util.Map; 24 | import java.util.Map.Entry; 25 | 26 | /** 27 | * Httpclient请求工具类 28 | * 29 | * @author hellofly 30 | * @date 2019/4/9 31 | */ 32 | public class HttpClientUtil { 33 | 34 | /** 35 | * 连接主机超时(30s) 36 | */ 37 | public static final int HTTP_CONNECT_TIMEOUT_30S = 30 * 1000; 38 | 39 | /** 40 | * 从主机读取数据超时(3min) 41 | */ 42 | public static final int HTTP_READ_TIMEOUT_3MIN = 180 * 1000; 43 | 44 | /** 45 | * HTTP成功状态码(200) 46 | */ 47 | public static final int HTTP_SUCCESS_STATUS_CODE = 200; 48 | 49 | private static final Logger logger = LogManager.getLogger(HttpClientUtil.class); 50 | 51 | private HttpClientUtil() { 52 | } 53 | 54 | /** 55 | * get请求 56 | * 57 | * @param url 58 | * @param formDataParam 59 | * @return 60 | */ 61 | public static String getMap(String url, Map formDataParam) { 62 | CloseableHttpClient httpclient = HttpClients.createDefault(); 63 | CloseableHttpResponse response = null; 64 | String result = ""; 65 | // 超时时间设置 66 | RequestConfig requestConfig = RequestConfig.custom() 67 | .setSocketTimeout(HTTP_READ_TIMEOUT_3MIN) 68 | .setConnectTimeout(HTTP_CONNECT_TIMEOUT_30S).build(); 69 | 70 | try { 71 | URIBuilder builder = new URIBuilder(url); 72 | if (null != formDataParam && formDataParam.size() > 0) { 73 | // 创建参数队列 74 | List formParams = new ArrayList<>(); 75 | for (Entry entry : formDataParam.entrySet()) { 76 | formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString())); 77 | } 78 | builder.setParameters(formParams); 79 | } 80 | // 设置参数 81 | HttpGet httpGet = new HttpGet(builder.build()); 82 | httpGet.setConfig(requestConfig); 83 | 84 | // 发送请求 85 | response = httpclient.execute(httpGet); 86 | result = EntityUtils.toString(response.getEntity(), Charsets.UTF_8); 87 | if (response.getStatusLine().getStatusCode() != HTTP_SUCCESS_STATUS_CODE) { 88 | logger.error("Error in getMap. Request URL is [{}], params [{}]. Result:[{}]", url, formDataParam, result); 89 | } 90 | } catch (Exception e) { 91 | logger.error("Error in getMap", e); 92 | } finally { 93 | HttpClientUtils.closeQuietly(httpclient); 94 | HttpClientUtils.closeQuietly(response); 95 | } 96 | return result; 97 | } 98 | 99 | /** 100 | * postFormData 101 | * 102 | * @param url 103 | * @param formDataParam 104 | * @return 105 | */ 106 | public static String postFormData(String url, Map formDataParam) { 107 | CloseableHttpClient httpclient = HttpClients.createDefault(); 108 | CloseableHttpResponse response = null; 109 | String result = ""; 110 | // 超时时间设置 111 | RequestConfig requestConfig = RequestConfig.custom() 112 | .setSocketTimeout(HTTP_READ_TIMEOUT_3MIN) 113 | .setConnectTimeout(HTTP_CONNECT_TIMEOUT_30S).build(); 114 | 115 | HttpPost httpPost = new HttpPost(url); 116 | httpPost.setConfig(requestConfig); 117 | try { 118 | if (null != formDataParam && formDataParam.size() > 0) { 119 | // 创建参数队列 120 | List formParams = new ArrayList<>(); 121 | for (Entry entry : formDataParam.entrySet()) { 122 | formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString())); 123 | } 124 | // 设置参数 125 | UrlEncodedFormEntity urlEntity = new UrlEncodedFormEntity(formParams, Charsets.UTF_8); 126 | httpPost.setEntity(urlEntity); 127 | } 128 | 129 | // 发送请求 130 | response = httpclient.execute(httpPost); 131 | result = EntityUtils.toString(response.getEntity(), Charsets.UTF_8); 132 | if (response.getStatusLine().getStatusCode() != HTTP_SUCCESS_STATUS_CODE) { 133 | logger.error("Error in postFormData. Request URL is [{}], params [{}]. Result:[{}]", url, formDataParam, result); 134 | } 135 | } catch (Exception e) { 136 | logger.error("Error in postFormData", e); 137 | } finally { 138 | HttpClientUtils.closeQuietly(httpclient); 139 | HttpClientUtils.closeQuietly(response); 140 | } 141 | return result; 142 | } 143 | 144 | /** 145 | * postJson 146 | * 147 | * @param url 148 | * @param jsonParam 149 | * @return 150 | */ 151 | public static String postJson(String url, String jsonParam) { 152 | CloseableHttpClient httpclient = HttpClients.createDefault(); 153 | CloseableHttpResponse response = null; 154 | String result = ""; 155 | // 超时时间设置 156 | RequestConfig requestConfig = RequestConfig.custom() 157 | .setSocketTimeout(HTTP_READ_TIMEOUT_3MIN) 158 | .setConnectTimeout(HTTP_CONNECT_TIMEOUT_30S).build(); 159 | 160 | HttpPost httpPost = new HttpPost(url); 161 | httpPost.setConfig(requestConfig); 162 | // 设置请求头和请求参数 163 | if (StringUtils.isNotEmpty(jsonParam)) { 164 | StringEntity entity = new StringEntity(jsonParam, "utf-8"); 165 | entity.setContentEncoding("UTF-8"); 166 | entity.setContentType("application/json"); 167 | httpPost.setEntity(entity); 168 | } 169 | 170 | try { 171 | // 发送请求 172 | response = httpclient.execute(httpPost); 173 | result = EntityUtils.toString(response.getEntity(), Charsets.UTF_8); 174 | if (response.getStatusLine().getStatusCode() != HTTP_SUCCESS_STATUS_CODE) { 175 | logger.error("Error in postJson. Request URL is [{}], params [{}]. Result:[{}]", url, jsonParam, result); 176 | } 177 | } catch (Exception e) { 178 | logger.error("Error in postJson", e); 179 | } finally { 180 | HttpClientUtils.closeQuietly(httpclient); 181 | HttpClientUtils.closeQuietly(response); 182 | } 183 | return result; 184 | } 185 | 186 | } 187 | -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/util/JobUtil.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.util; 2 | 3 | import com.example.quartz.constants.Constant; 4 | import org.quartz.CronExpression; 5 | import org.quartz.Scheduler; 6 | import org.quartz.SchedulerException; 7 | import org.quartz.TriggerKey; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Component; 10 | 11 | import java.text.ParseException; 12 | import java.util.Date; 13 | 14 | /** 15 | * job工具类 16 | * 17 | * @author hellofly 18 | * @date 2019/4/9 19 | */ 20 | @Component 21 | public class JobUtil { 22 | 23 | @Autowired 24 | private Scheduler scheduler; 25 | 26 | /** 27 | * 根据jobName和jobGroup生成triggerKey 28 | * 29 | * @param jobName 30 | * @param jobGroup 31 | * @return 32 | */ 33 | public TriggerKey getTriggerKeyByJob(String jobName, String jobGroup) { 34 | String triggerName = Constant.TRIGGER_PREFIX + jobName; 35 | String triggerGroup = Constant.TRIGGER_PREFIX + jobGroup; 36 | return TriggerKey.triggerKey(triggerName, triggerGroup); 37 | } 38 | 39 | /** 40 | * 获取job状态 41 | * 42 | * @param jobName 43 | * @param jobGroup 44 | * @return 45 | */ 46 | public String getJobStatusInfo(String jobName, String jobGroup) { 47 | String jobStatusInfo = ""; 48 | TriggerKey triggerKey = getTriggerKeyByJob(jobName, jobGroup); 49 | try { 50 | jobStatusInfo = scheduler.getTriggerState(triggerKey).name(); 51 | } catch (SchedulerException e) { 52 | throw new RuntimeException(e); 53 | } 54 | return jobStatusInfo; 55 | } 56 | 57 | /** 58 | * 根据cron表达式获取下次执行时间 59 | * 60 | * @param cronExpression 61 | * @return 62 | */ 63 | public Date getNextFireDate(String cronExpression) { 64 | try { 65 | CronExpression cron = new CronExpression(cronExpression); 66 | Date nextFireDate = cron.getNextValidTimeAfter(new Date()); 67 | return nextFireDate; 68 | } catch (ParseException e) { 69 | throw new RuntimeException(e); 70 | } 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/example/quartz/util/JsonValidUtil.java: -------------------------------------------------------------------------------- 1 | package com.example.quartz.util; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | 5 | /** 6 | * JSON校验工具类 7 | * 8 | * @author hellofly 9 | * @date 2019/4/9 10 | */ 11 | public class JsonValidUtil { 12 | 13 | /** 14 | * 判断字符串是否为json格式 15 | * 16 | * @param jsonStr 17 | * @return 18 | */ 19 | public static boolean isJson(String jsonStr) { 20 | try { 21 | JSONObject.parseObject(jsonStr); 22 | return true; 23 | } catch (Exception e) { 24 | return false; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=8080 2 | 3 | #UTF-8字符过滤 4 | spring.http.encoding.charset=UTF-8 5 | spring.http.encoding.enabled=true 6 | spring.http.encoding.force=true 7 | 8 | # mysql 9 | spring.datasource.url=jdbc:mysql://your_db/quartz?useUnicode=true&characterEncoding=utf-8&useSSL=false 10 | spring.datasource.username=username 11 | spring.datasource.password=password 12 | spring.datasource.driverClassName=com.mysql.jdbc.Driver 13 | 14 | # druid连接池的配置信息 15 | spring.datasource.type=com.alibaba.druid.pool.DruidDataSource 16 | spring.datasource.initialSize=5 17 | spring.datasource.minIdle=5 18 | spring.datasource.maxActive=20 19 | spring.datasource.maxWait=60000 20 | spring.datasource.timeBetweenEvictionRunsMillis=60000 21 | spring.datasource.minEvictableIdleTimeMillis=300000 22 | spring.datasource.validationQuery=SELECT 1 FROM DUAL 23 | spring.datasource.testWhileIdle=true 24 | spring.datasource.testOnBorrow=false 25 | spring.datasource.testOnReturn=false 26 | spring.datasource.poolPreparedStatements=true 27 | spring.datasource.maxPoolPreparedStatementPerConnectionSize=20 28 | spring.datasource.filters=stat 29 | spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 30 | 31 | # mybatis Mapper文件 32 | mybatis.mapper-locations=classpath*:mappings/*.xml 33 | # mybatis别名配置 34 | mybatis.type-aliases-package=com.example.quartz.model 35 | -------------------------------------------------------------------------------- /src/main/resources/mappings/HttpJobDetailsMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | ID,JOB_NAME,JOB_GROUP,DESCRIPTION,REQUEST_TYPE,HTTP_URL,HTTP_PARAMS,CREATE_TIME,UPDATE_TIME 19 | 20 | 21 | 27 | 28 | 29 | insert into HTTPJOB_DETAILS 30 | 31 | 32 | JOB_NAME, 33 | 34 | 35 | JOB_GROUP, 36 | 37 | 38 | DESCRIPTION, 39 | 40 | 41 | REQUEST_TYPE, 42 | 43 | 44 | HTTP_URL, 45 | 46 | 47 | HTTP_PARAMS, 48 | 49 | 50 | 51 | 52 | #{jobName,jdbcType=VARCHAR}, 53 | 54 | 55 | #{jobGroup,jdbcType=VARCHAR}, 56 | 57 | 58 | #{description,jdbcType=VARCHAR}, 59 | 60 | 61 | #{requestType,jdbcType=VARCHAR}, 62 | 63 | 64 | #{httpUrl,jdbcType=VARCHAR}, 65 | 66 | 67 | #{httpParams,jdbcType=VARCHAR}, 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 107 | 108 | 121 | 122 | 144 | 145 | 157 | 158 | -------------------------------------------------------------------------------- /src/main/resources/mappings/HttpJobLogsMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ID,JOB_NAME,JOB_GROUP,REQUEST_TYPE,HTTP_URL,HTTP_PARAMS,FIRE_TIME,RESULT 18 | 19 | 20 | 21 | insert into HTTPJOB_LOGS 22 | 23 | 24 | JOB_NAME, 25 | 26 | 27 | JOB_GROUP, 28 | 29 | 30 | REQUEST_TYPE, 31 | 32 | 33 | HTTP_URL, 34 | 35 | 36 | HTTP_PARAMS, 37 | 38 | 39 | FIRE_TIME, 40 | 41 | 42 | RESULT, 43 | 44 | 45 | 46 | 47 | #{jobName,jdbcType=VARCHAR}, 48 | 49 | 50 | #{jobGroup,jdbcType=VARCHAR}, 51 | 52 | 53 | #{requestType,jdbcType=VARCHAR}, 54 | 55 | 56 | #{httpUrl,jdbcType=VARCHAR}, 57 | 58 | 59 | #{httpParams,jdbcType=VARCHAR}, 60 | 61 | 62 | #{fireTime,jdbcType=TIMESTAMP}, 63 | 64 | 65 | #{result,jdbcType=VARCHAR}, 66 | 67 | 68 | 69 | 70 | 91 | 92 | 109 | 110 | -------------------------------------------------------------------------------- /src/main/resources/quartz.properties: -------------------------------------------------------------------------------- 1 | #quartz集群配置 2 | # =========================================================================== 3 | # Configure Main Scheduler Properties 调度器属性 4 | # =========================================================================== 5 | #调度标识名 集群中每一个实例都必须使用相同的名称 6 | org.quartz.scheduler.instanceName: MyQuartzScheduler 7 | #ID设置为自动获取 每一个必须不同 8 | org.quartz.scheduler.instanceId: AUTO 9 | #============================================================================ 10 | # Configure ThreadPool 11 | #============================================================================ 12 | #线程池的实现类(一般使用SimpleThreadPool即可满足几乎所有用户的需求) 13 | org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool 14 | #指定线程数,至少为1(无默认值)(一般设置为1-100直接的整数合适) 15 | org.quartz.threadPool.threadCount: 25 16 | #设置线程的优先级(最大为java.lang.Thread.MAX_PRIORITY 10,最小为Thread.MIN_PRIORITY 1,默认为5) 17 | org.quartz.threadPool.threadPriority: 5 18 | #============================================================================ 19 | # Configure JobStore 20 | #============================================================================ 21 | # 信息保存时间 默认值60秒,misfire任务为错过调度触发时间的任务,超过60000ms后被判定为misfire 22 | org.quartz.jobStore.misfireThreshold: 60000 23 | #数据保存方式为数据库持久化 24 | org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX 25 | #数据库代理类,一般org.quartz.impl.jdbcjobstore.StdJDBCDelegate可以满足大部分数据库 26 | org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate 27 | #JobDataMaps是否都为String类型 28 | org.quartz.jobStore.useProperties: false 29 | #数据库别名 随便取 30 | org.quartz.jobStore.dataSource: myDS 31 | #表的前缀,默认QRTZ_ 32 | org.quartz.jobStore.tablePrefix: QRTZ_ 33 | #是否加入集群 34 | org.quartz.jobStore.isClustered: true 35 | #调度实例失效的检查时间间隔 36 | org.quartz.jobStore.clusterCheckinInterval: 20000 37 | #避免集群环境下job重复执行 38 | org.quartz.jobStore.acquireTriggersWithinLock: true 39 | -------------------------------------------------------------------------------- /src/main/resources/static/historyHttpJob.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 历史任务 6 | 7 | 8 | 9 | 10 | 11 | 22 | 23 | 24 | 25 |
26 | 27 |
28 | 29 | 查询 30 | 31 | 执行记录 32 | 33 |
34 | 35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 |
54 | 62 | 63 |
64 |
65 | 66 |
67 | 68 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /src/main/resources/static/httpJob.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 定时任务管理 6 | 7 | 8 | 9 | 10 | 11 | 41 | 42 | 43 | 44 |
45 | 46 |
47 | 添加任务 48 | 50 | 查询 51 | 52 | 执行记录 53 | 54 | 55 | 历史任务 56 | 57 |
58 | 59 |
60 | 61 | 62 | 63 | 64 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 88 | 89 | 90 | 91 |
92 | 100 | 101 |
102 |
103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 148 | 149 | 150 |
151 | 152 | 392 | 393 | 394 | -------------------------------------------------------------------------------- /src/main/resources/static/httpJobLog.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 任务执行记录 6 | 7 | 8 | 9 | 10 | 11 | 22 | 23 | 24 | 25 |
26 | 27 |
28 | 29 | 查询 30 | 31 | 返回 32 | 33 |
34 | 35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
47 | 55 | 56 |
57 |
58 | 59 |
60 | 61 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /src/main/resources/static/js/axios.min.js: -------------------------------------------------------------------------------- 1 | /* axios v0.19.0 | (c) 2019 by Matt Zabriskie */ 2 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.axios=t():e.axios=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){e.exports=n(1)},function(e,t,n){"use strict";function r(e){var t=new i(e),n=s(i.prototype.request,t);return o.extend(n,i.prototype,t),o.extend(n,t),n}var o=n(2),s=n(3),i=n(5),a=n(22),u=n(11),c=r(u);c.Axios=i,c.create=function(e){return r(a(c.defaults,e))},c.Cancel=n(23),c.CancelToken=n(24),c.isCancel=n(10),c.all=function(e){return Promise.all(e)},c.spread=n(25),e.exports=c,e.exports.default=c},function(e,t,n){"use strict";function r(e){return"[object Array]"===j.call(e)}function o(e){return"[object ArrayBuffer]"===j.call(e)}function s(e){return"undefined"!=typeof FormData&&e instanceof FormData}function i(e){var t;return t="undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&e.buffer instanceof ArrayBuffer}function a(e){return"string"==typeof e}function u(e){return"number"==typeof e}function c(e){return"undefined"==typeof e}function f(e){return null!==e&&"object"==typeof e}function p(e){return"[object Date]"===j.call(e)}function d(e){return"[object File]"===j.call(e)}function l(e){return"[object Blob]"===j.call(e)}function h(e){return"[object Function]"===j.call(e)}function m(e){return f(e)&&h(e.pipe)}function y(e){return"undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams}function g(e){return e.replace(/^\s*/,"").replace(/\s*$/,"")}function x(){return("undefined"==typeof navigator||"ReactNative"!==navigator.product&&"NativeScript"!==navigator.product&&"NS"!==navigator.product)&&("undefined"!=typeof window&&"undefined"!=typeof document)}function v(e,t){if(null!==e&&"undefined"!=typeof e)if("object"!=typeof e&&(e=[e]),r(e))for(var n=0,o=e.length;n 6 | * @license MIT 7 | */ 8 | e.exports=function(e){return null!=e&&null!=e.constructor&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}},function(e,t,n){"use strict";function r(e){this.defaults=e,this.interceptors={request:new i,response:new i}}var o=n(2),s=n(6),i=n(7),a=n(8),u=n(22);r.prototype.request=function(e){"string"==typeof e?(e=arguments[1]||{},e.url=arguments[0]):e=e||{},e=u(this.defaults,e),e.method=e.method?e.method.toLowerCase():"get";var t=[a,void 0],n=Promise.resolve(e);for(this.interceptors.request.forEach(function(e){t.unshift(e.fulfilled,e.rejected)}),this.interceptors.response.forEach(function(e){t.push(e.fulfilled,e.rejected)});t.length;)n=n.then(t.shift(),t.shift());return n},r.prototype.getUri=function(e){return e=u(this.defaults,e),s(e.url,e.params,e.paramsSerializer).replace(/^\?/,"")},o.forEach(["delete","get","head","options"],function(e){r.prototype[e]=function(t,n){return this.request(o.merge(n||{},{method:e,url:t}))}}),o.forEach(["post","put","patch"],function(e){r.prototype[e]=function(t,n,r){return this.request(o.merge(r||{},{method:e,url:t,data:n}))}}),e.exports=r},function(e,t,n){"use strict";function r(e){return encodeURIComponent(e).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}var o=n(2);e.exports=function(e,t,n){if(!t)return e;var s;if(n)s=n(t);else if(o.isURLSearchParams(t))s=t.toString();else{var i=[];o.forEach(t,function(e,t){null!==e&&"undefined"!=typeof e&&(o.isArray(e)?t+="[]":e=[e],o.forEach(e,function(e){o.isDate(e)?e=e.toISOString():o.isObject(e)&&(e=JSON.stringify(e)),i.push(r(t)+"="+r(e))}))}),s=i.join("&")}if(s){var a=e.indexOf("#");a!==-1&&(e=e.slice(0,a)),e+=(e.indexOf("?")===-1?"?":"&")+s}return e}},function(e,t,n){"use strict";function r(){this.handlers=[]}var o=n(2);r.prototype.use=function(e,t){return this.handlers.push({fulfilled:e,rejected:t}),this.handlers.length-1},r.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},r.prototype.forEach=function(e){o.forEach(this.handlers,function(t){null!==t&&e(t)})},e.exports=r},function(e,t,n){"use strict";function r(e){e.cancelToken&&e.cancelToken.throwIfRequested()}var o=n(2),s=n(9),i=n(10),a=n(11),u=n(20),c=n(21);e.exports=function(e){r(e),e.baseURL&&!u(e.url)&&(e.url=c(e.baseURL,e.url)),e.headers=e.headers||{},e.data=s(e.data,e.headers,e.transformRequest),e.headers=o.merge(e.headers.common||{},e.headers[e.method]||{},e.headers||{}),o.forEach(["delete","get","head","post","put","patch","common"],function(t){delete e.headers[t]});var t=e.adapter||a.adapter;return t(e).then(function(t){return r(e),t.data=s(t.data,t.headers,e.transformResponse),t},function(t){return i(t)||(r(e),t&&t.response&&(t.response.data=s(t.response.data,t.response.headers,e.transformResponse))),Promise.reject(t)})}},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t,n){return r.forEach(n,function(n){e=n(e,t)}),e}},function(e,t){"use strict";e.exports=function(e){return!(!e||!e.__CANCEL__)}},function(e,t,n){"use strict";function r(e,t){!s.isUndefined(e)&&s.isUndefined(e["Content-Type"])&&(e["Content-Type"]=t)}function o(){var e;return"undefined"!=typeof process&&"[object process]"===Object.prototype.toString.call(process)?e=n(13):"undefined"!=typeof XMLHttpRequest&&(e=n(13)),e}var s=n(2),i=n(12),a={"Content-Type":"application/x-www-form-urlencoded"},u={adapter:o(),transformRequest:[function(e,t){return i(t,"Accept"),i(t,"Content-Type"),s.isFormData(e)||s.isArrayBuffer(e)||s.isBuffer(e)||s.isStream(e)||s.isFile(e)||s.isBlob(e)?e:s.isArrayBufferView(e)?e.buffer:s.isURLSearchParams(e)?(r(t,"application/x-www-form-urlencoded;charset=utf-8"),e.toString()):s.isObject(e)?(r(t,"application/json;charset=utf-8"),JSON.stringify(e)):e}],transformResponse:[function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(e){}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,validateStatus:function(e){return e>=200&&e<300}};u.headers={common:{Accept:"application/json, text/plain, */*"}},s.forEach(["delete","get","head"],function(e){u.headers[e]={}}),s.forEach(["post","put","patch"],function(e){u.headers[e]=s.merge(a)}),e.exports=u},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t){r.forEach(e,function(n,r){r!==t&&r.toUpperCase()===t.toUpperCase()&&(e[t]=n,delete e[r])})}},function(e,t,n){"use strict";var r=n(2),o=n(14),s=n(6),i=n(17),a=n(18),u=n(15);e.exports=function(e){return new Promise(function(t,c){var f=e.data,p=e.headers;r.isFormData(f)&&delete p["Content-Type"];var d=new XMLHttpRequest;if(e.auth){var l=e.auth.username||"",h=e.auth.password||"";p.Authorization="Basic "+btoa(l+":"+h)}if(d.open(e.method.toUpperCase(),s(e.url,e.params,e.paramsSerializer),!0),d.timeout=e.timeout,d.onreadystatechange=function(){if(d&&4===d.readyState&&(0!==d.status||d.responseURL&&0===d.responseURL.indexOf("file:"))){var n="getAllResponseHeaders"in d?i(d.getAllResponseHeaders()):null,r=e.responseType&&"text"!==e.responseType?d.response:d.responseText,s={data:r,status:d.status,statusText:d.statusText,headers:n,config:e,request:d};o(t,c,s),d=null}},d.onabort=function(){d&&(c(u("Request aborted",e,"ECONNABORTED",d)),d=null)},d.onerror=function(){c(u("Network Error",e,null,d)),d=null},d.ontimeout=function(){c(u("timeout of "+e.timeout+"ms exceeded",e,"ECONNABORTED",d)),d=null},r.isStandardBrowserEnv()){var m=n(19),y=(e.withCredentials||a(e.url))&&e.xsrfCookieName?m.read(e.xsrfCookieName):void 0;y&&(p[e.xsrfHeaderName]=y)}if("setRequestHeader"in d&&r.forEach(p,function(e,t){"undefined"==typeof f&&"content-type"===t.toLowerCase()?delete p[t]:d.setRequestHeader(t,e)}),e.withCredentials&&(d.withCredentials=!0),e.responseType)try{d.responseType=e.responseType}catch(t){if("json"!==e.responseType)throw t}"function"==typeof e.onDownloadProgress&&d.addEventListener("progress",e.onDownloadProgress),"function"==typeof e.onUploadProgress&&d.upload&&d.upload.addEventListener("progress",e.onUploadProgress),e.cancelToken&&e.cancelToken.promise.then(function(e){d&&(d.abort(),c(e),d=null)}),void 0===f&&(f=null),d.send(f)})}},function(e,t,n){"use strict";var r=n(15);e.exports=function(e,t,n){var o=n.config.validateStatus;!o||o(n.status)?e(n):t(r("Request failed with status code "+n.status,n.config,null,n.request,n))}},function(e,t,n){"use strict";var r=n(16);e.exports=function(e,t,n,o,s){var i=new Error(e);return r(i,t,n,o,s)}},function(e,t){"use strict";e.exports=function(e,t,n,r,o){return e.config=t,n&&(e.code=n),e.request=r,e.response=o,e.isAxiosError=!0,e.toJSON=function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:this.config,code:this.code}},e}},function(e,t,n){"use strict";var r=n(2),o=["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"];e.exports=function(e){var t,n,s,i={};return e?(r.forEach(e.split("\n"),function(e){if(s=e.indexOf(":"),t=r.trim(e.substr(0,s)).toLowerCase(),n=r.trim(e.substr(s+1)),t){if(i[t]&&o.indexOf(t)>=0)return;"set-cookie"===t?i[t]=(i[t]?i[t]:[]).concat([n]):i[t]=i[t]?i[t]+", "+n:n}}),i):i}},function(e,t,n){"use strict";var r=n(2);e.exports=r.isStandardBrowserEnv()?function(){function e(e){var t=e;return n&&(o.setAttribute("href",t),t=o.href),o.setAttribute("href",t),{href:o.href,protocol:o.protocol?o.protocol.replace(/:$/,""):"",host:o.host,search:o.search?o.search.replace(/^\?/,""):"",hash:o.hash?o.hash.replace(/^#/,""):"",hostname:o.hostname,port:o.port,pathname:"/"===o.pathname.charAt(0)?o.pathname:"/"+o.pathname}}var t,n=/(msie|trident)/i.test(navigator.userAgent),o=document.createElement("a");return t=e(window.location.href),function(n){var o=r.isString(n)?e(n):n;return o.protocol===t.protocol&&o.host===t.host}}():function(){return function(){return!0}}()},function(e,t,n){"use strict";var r=n(2);e.exports=r.isStandardBrowserEnv()?function(){return{write:function(e,t,n,o,s,i){var a=[];a.push(e+"="+encodeURIComponent(t)),r.isNumber(n)&&a.push("expires="+new Date(n).toGMTString()),r.isString(o)&&a.push("path="+o),r.isString(s)&&a.push("domain="+s),i===!0&&a.push("secure"),document.cookie=a.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}}():function(){return{write:function(){},read:function(){return null},remove:function(){}}}()},function(e,t){"use strict";e.exports=function(e){return/^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(e)}},function(e,t){"use strict";e.exports=function(e,t){return t?e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""):e}},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t){t=t||{};var n={};return r.forEach(["url","method","params","data"],function(e){"undefined"!=typeof t[e]&&(n[e]=t[e])}),r.forEach(["headers","auth","proxy"],function(o){r.isObject(t[o])?n[o]=r.deepMerge(e[o],t[o]):"undefined"!=typeof t[o]?n[o]=t[o]:r.isObject(e[o])?n[o]=r.deepMerge(e[o]):"undefined"!=typeof e[o]&&(n[o]=e[o])}),r.forEach(["baseURL","transformRequest","transformResponse","paramsSerializer","timeout","withCredentials","adapter","responseType","xsrfCookieName","xsrfHeaderName","onUploadProgress","onDownloadProgress","maxContentLength","validateStatus","maxRedirects","httpAgent","httpsAgent","cancelToken","socketPath"],function(r){"undefined"!=typeof t[r]?n[r]=t[r]:"undefined"!=typeof e[r]&&(n[r]=e[r])}),n}},function(e,t){"use strict";function n(e){this.message=e}n.prototype.toString=function(){return"Cancel"+(this.message?": "+this.message:"")},n.prototype.__CANCEL__=!0,e.exports=n},function(e,t,n){"use strict";function r(e){if("function"!=typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise(function(e){t=e});var n=this;e(function(e){n.reason||(n.reason=new o(e),t(n.reason))})}var o=n(23);r.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},r.source=function(){var e,t=new r(function(t){e=t});return{token:t,cancel:e}},e.exports=r},function(e,t){"use strict";e.exports=function(e){return function(t){return e.apply(null,t)}}}])}); 9 | //# sourceMappingURL=axios.min.map -------------------------------------------------------------------------------- /src/main/resources/static/js/vue.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Vue.js v2.5.16 3 | * (c) 2014-2018 Evan You 4 | * Released under the MIT License. 5 | */ 6 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Vue=t()}(this,function(){"use strict";var y=Object.freeze({});function M(e){return null==e}function D(e){return null!=e}function S(e){return!0===e}function T(e){return"string"==typeof e||"number"==typeof e||"symbol"==typeof e||"boolean"==typeof e}function P(e){return null!==e&&"object"==typeof e}var r=Object.prototype.toString;function l(e){return"[object Object]"===r.call(e)}function i(e){var t=parseFloat(String(e));return 0<=t&&Math.floor(t)===t&&isFinite(e)}function t(e){return null==e?"":"object"==typeof e?JSON.stringify(e,null,2):String(e)}function F(e){var t=parseFloat(e);return isNaN(t)?e:t}function s(e,t){for(var n=Object.create(null),r=e.split(","),i=0;ie.id;)n--;bt.splice(n+1,0,e)}else bt.push(e);Ct||(Ct=!0,Ze(At))}}(this)},St.prototype.run=function(){if(this.active){var e=this.get();if(e!==this.value||P(e)||this.deep){var t=this.value;if(this.value=e,this.user)try{this.cb.call(this.vm,e,t)}catch(e){Fe(e,this.vm,'callback for watcher "'+this.expression+'"')}else this.cb.call(this.vm,e,t)}}},St.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},St.prototype.depend=function(){for(var e=this.deps.length;e--;)this.deps[e].depend()},St.prototype.teardown=function(){if(this.active){this.vm._isBeingDestroyed||f(this.vm._watchers,this);for(var e=this.deps.length;e--;)this.deps[e].removeSub(this);this.active=!1}};var Tt={enumerable:!0,configurable:!0,get:$,set:$};function Et(e,t,n){Tt.get=function(){return this[t][n]},Tt.set=function(e){this[t][n]=e},Object.defineProperty(e,n,Tt)}function jt(e){e._watchers=[];var t=e.$options;t.props&&function(n,r){var i=n.$options.propsData||{},o=n._props={},a=n.$options._propKeys=[];n.$parent&&ge(!1);var e=function(e){a.push(e);var t=Ie(e,r,i,n);Ce(o,e,t),e in n||Et(n,"_props",e)};for(var t in r)e(t);ge(!0)}(e,t.props),t.methods&&function(e,t){e.$options.props;for(var n in t)e[n]=null==t[n]?$:v(t[n],e)}(e,t.methods),t.data?function(e){var t=e.$options.data;l(t=e._data="function"==typeof t?function(e,t){se();try{return e.call(t,t)}catch(e){return Fe(e,t,"data()"),{}}finally{ce()}}(t,e):t||{})||(t={});var n=Object.keys(t),r=e.$options.props,i=(e.$options.methods,n.length);for(;i--;){var o=n[i];r&&p(r,o)||(void 0,36!==(a=(o+"").charCodeAt(0))&&95!==a&&Et(e,"_data",o))}var a;we(t,!0)}(e):we(e._data={},!0),t.computed&&function(e,t){var n=e._computedWatchers=Object.create(null),r=Y();for(var i in t){var o=t[i],a="function"==typeof o?o:o.get;r||(n[i]=new St(e,a||$,$,Nt)),i in e||Lt(e,i,o)}}(e,t.computed),t.watch&&t.watch!==G&&function(e,t){for(var n in t){var r=t[n];if(Array.isArray(r))for(var i=0;iparseInt(this.max)&&bn(a,s[0],s,this._vnode)),t.data.keepAlive=!0}return t||e&&e[0]}}};$n=hn,Cn={get:function(){return j}},Object.defineProperty($n,"config",Cn),$n.util={warn:re,extend:m,mergeOptions:Ne,defineReactive:Ce},$n.set=xe,$n.delete=ke,$n.nextTick=Ze,$n.options=Object.create(null),k.forEach(function(e){$n.options[e+"s"]=Object.create(null)}),m(($n.options._base=$n).options.components,kn),$n.use=function(e){var t=this._installedPlugins||(this._installedPlugins=[]);if(-1=a&&l()};setTimeout(function(){c\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,oo="[a-zA-Z_][\\w\\-\\.]*",ao="((?:"+oo+"\\:)?"+oo+")",so=new RegExp("^<"+ao),co=/^\s*(\/?)>/,lo=new RegExp("^<\\/"+ao+"[^>]*>"),uo=/^]+>/i,fo=/^",""":'"',"&":"&"," ":"\n"," ":"\t"},go=/&(?:lt|gt|quot|amp);/g,_o=/&(?:lt|gt|quot|amp|#10|#9);/g,bo=s("pre,textarea",!0),$o=function(e,t){return e&&bo(e)&&"\n"===t[0]};var wo,Co,xo,ko,Ao,Oo,So,To,Eo=/^@|^v-on:/,jo=/^v-|^@|^:/,No=/([^]*?)\s+(?:in|of)\s+([^]*)/,Lo=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,Io=/^\(|\)$/g,Mo=/:(.*)$/,Do=/^:|^v-bind:/,Po=/\.[^.]+/g,Fo=e(eo);function Ro(e,t,n){return{type:1,tag:e,attrsList:t,attrsMap:function(e){for(var t={},n=0,r=e.length;n]*>)","i")),n=i.replace(t,function(e,t,n){return r=n.length,ho(o)||"noscript"===o||(t=t.replace(//g,"$1").replace(//g,"$1")),$o(o,t)&&(t=t.slice(1)),d.chars&&d.chars(t),""});a+=i.length-n.length,i=n,A(o,a-r,a)}else{var s=i.indexOf("<");if(0===s){if(fo.test(i)){var c=i.indexOf("--\x3e");if(0<=c){d.shouldKeepComment&&d.comment(i.substring(4,c)),C(c+3);continue}}if(po.test(i)){var l=i.indexOf("]>");if(0<=l){C(l+2);continue}}var u=i.match(uo);if(u){C(u[0].length);continue}var f=i.match(lo);if(f){var p=a;C(f[0].length),A(f[1],p,a);continue}var _=x();if(_){k(_),$o(v,i)&&C(1);continue}}var b=void 0,$=void 0,w=void 0;if(0<=s){for($=i.slice(s);!(lo.test($)||so.test($)||fo.test($)||po.test($)||(w=$.indexOf("<",1))<0);)s+=w,$=i.slice(s);b=i.substring(0,s),C(s)}s<0&&(b=i,i=""),d.chars&&b&&d.chars(b)}if(i===e){d.chars&&d.chars(i);break}}function C(e){a+=e,i=i.substring(e)}function x(){var e=i.match(so);if(e){var t,n,r={tagName:e[1],attrs:[],start:a};for(C(e[0].length);!(t=i.match(co))&&(n=i.match(io));)C(n[0].length),r.attrs.push(n);if(t)return r.unarySlash=t[1],C(t[0].length),r.end=a,r}}function k(e){var t=e.tagName,n=e.unarySlash;m&&("p"===v&&ro(t)&&A(v),g(t)&&v===t&&A(t));for(var r,i,o,a=y(t)||!!n,s=e.attrs.length,c=new Array(s),l=0;l-1"+("true"===d?":("+l+")":":_q("+l+","+d+")")),Ar(c,"change","var $$a="+l+",$$el=$event.target,$$c=$$el.checked?("+d+"):("+v+");if(Array.isArray($$a)){var $$v="+(f?"_n("+p+")":p)+",$$i=_i($$a,$$v);if($$el.checked){$$i<0&&("+Er(l,"$$a.concat([$$v])")+")}else{$$i>-1&&("+Er(l,"$$a.slice(0,$$i).concat($$a.slice($$i+1))")+")}}else{"+Er(l,"$$c")+"}",null,!0);else if("input"===$&&"radio"===w)r=e,i=_,a=(o=b)&&o.number,s=Or(r,"value")||"null",Cr(r,"checked","_q("+i+","+(s=a?"_n("+s+")":s)+")"),Ar(r,"change",Er(i,s),null,!0);else if("input"===$||"textarea"===$)!function(e,t,n){var r=e.attrsMap.type,i=n||{},o=i.lazy,a=i.number,s=i.trim,c=!o&&"range"!==r,l=o?"change":"range"===r?Pr:"input",u="$event.target.value";s&&(u="$event.target.value.trim()"),a&&(u="_n("+u+")");var f=Er(t,u);c&&(f="if($event.target.composing)return;"+f),Cr(e,"value","("+t+")"),Ar(e,l,f,null,!0),(s||a)&&Ar(e,"blur","$forceUpdate()")}(e,_,b);else if(!j.isReservedTag($))return Tr(e,_,b),!1;return!0},text:function(e,t){t.value&&Cr(e,"textContent","_s("+t.value+")")},html:function(e,t){t.value&&Cr(e,"innerHTML","_s("+t.value+")")}},isPreTag:function(e){return"pre"===e},isUnaryTag:to,mustUseProp:Sn,canBeLeftOpenTag:no,isReservedTag:Un,getTagNamespace:Vn,staticKeys:(Go=Wo,Go.reduce(function(e,t){return e.concat(t.staticKeys||[])},[]).join(","))},Qo=e(function(e){return s("type,tag,attrsList,attrsMap,plain,parent,children,attrs"+(e?","+e:""))});function ea(e,t){e&&(Zo=Qo(t.staticKeys||""),Xo=t.isReservedTag||O,function e(t){t.static=function(e){if(2===e.type)return!1;if(3===e.type)return!0;return!(!e.pre&&(e.hasBindings||e.if||e.for||c(e.tag)||!Xo(e.tag)||function(e){for(;e.parent;){if("template"!==(e=e.parent).tag)return!1;if(e.for)return!0}return!1}(e)||!Object.keys(e).every(Zo)))}(t);if(1===t.type){if(!Xo(t.tag)&&"slot"!==t.tag&&null==t.attrsMap["inline-template"])return;for(var n=0,r=t.children.length;n|^function\s*\(/,na=/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/,ra={esc:27,tab:9,enter:13,space:32,up:38,left:37,right:39,down:40,delete:[8,46]},ia={esc:"Escape",tab:"Tab",enter:"Enter",space:" ",up:["Up","ArrowUp"],left:["Left","ArrowLeft"],right:["Right","ArrowRight"],down:["Down","ArrowDown"],delete:["Backspace","Delete"]},oa=function(e){return"if("+e+")return null;"},aa={stop:"$event.stopPropagation();",prevent:"$event.preventDefault();",self:oa("$event.target !== $event.currentTarget"),ctrl:oa("!$event.ctrlKey"),shift:oa("!$event.shiftKey"),alt:oa("!$event.altKey"),meta:oa("!$event.metaKey"),left:oa("'button' in $event && $event.button !== 0"),middle:oa("'button' in $event && $event.button !== 1"),right:oa("'button' in $event && $event.button !== 2")};function sa(e,t,n){var r=t?"nativeOn:{":"on:{";for(var i in e)r+='"'+i+'":'+ca(i,e[i])+",";return r.slice(0,-1)+"}"}function ca(t,e){if(!e)return"function(){}";if(Array.isArray(e))return"["+e.map(function(e){return ca(t,e)}).join(",")+"]";var n=na.test(e.value),r=ta.test(e.value);if(e.modifiers){var i="",o="",a=[];for(var s in e.modifiers)if(aa[s])o+=aa[s],ra[s]&&a.push(s);else if("exact"===s){var c=e.modifiers;o+=oa(["ctrl","shift","alt","meta"].filter(function(e){return!c[e]}).map(function(e){return"$event."+e+"Key"}).join("||"))}else a.push(s);return a.length&&(i+="if(!('button' in $event)&&"+a.map(la).join("&&")+")return null;"),o&&(i+=o),"function($event){"+i+(n?"return "+e.value+"($event)":r?"return ("+e.value+")($event)":e.value)+"}"}return n||r?e.value:"function($event){"+e.value+"}"}function la(e){var t=parseInt(e,10);if(t)return"$event.keyCode!=="+t;var n=ra[e],r=ia[e];return"_k($event.keyCode,"+JSON.stringify(e)+","+JSON.stringify(n)+",$event.key,"+JSON.stringify(r)+")"}var ua={on:function(e,t){e.wrapListeners=function(e){return"_g("+e+","+t.value+")"}},bind:function(t,n){t.wrapData=function(e){return"_b("+e+",'"+t.tag+"',"+n.value+","+(n.modifiers&&n.modifiers.prop?"true":"false")+(n.modifiers&&n.modifiers.sync?",true":"")+")"}},cloak:$},fa=function(e){this.options=e,this.warn=e.warn||$r,this.transforms=wr(e.modules,"transformCode"),this.dataGenFns=wr(e.modules,"genData"),this.directives=m(m({},ua),e.directives);var t=e.isReservedTag||O;this.maybeComponent=function(e){return!t(e.tag)},this.onceId=0,this.staticRenderFns=[]};function pa(e,t){var n=new fa(t);return{render:"with(this){return "+(e?da(e,n):'_c("div")')+"}",staticRenderFns:n.staticRenderFns}}function da(e,t){if(e.staticRoot&&!e.staticProcessed)return va(e,t);if(e.once&&!e.onceProcessed)return ha(e,t);if(e.for&&!e.forProcessed)return f=t,v=(u=e).for,h=u.alias,m=u.iterator1?","+u.iterator1:"",y=u.iterator2?","+u.iterator2:"",u.forProcessed=!0,(d||"_l")+"(("+v+"),function("+h+m+y+"){return "+(p||da)(u,f)+"})";if(e.if&&!e.ifProcessed)return ma(e,t);if("template"!==e.tag||e.slotTarget){if("slot"===e.tag)return function(e,t){var n=e.slotName||'"default"',r=_a(e,t),i="_t("+n+(r?","+r:""),o=e.attrs&&"{"+e.attrs.map(function(e){return g(e.name)+":"+e.value}).join(",")+"}",a=e.attrsMap["v-bind"];!o&&!a||r||(i+=",null");o&&(i+=","+o);a&&(i+=(o?"":",null")+","+a);return i+")"}(e,t);var n;if(e.component)a=e.component,c=t,l=(s=e).inlineTemplate?null:_a(s,c,!0),n="_c("+a+","+ya(s,c)+(l?","+l:"")+")";else{var r=e.plain?void 0:ya(e,t),i=e.inlineTemplate?null:_a(e,t,!0);n="_c('"+e.tag+"'"+(r?","+r:"")+(i?","+i:"")+")"}for(var o=0;o':'
',0