chenken 7 жил өмнө
commit
aa35c8d6c7
27 өөрчлөгдсөн 1256 нэмэгдсэн , 0 устгасан
  1. 10 0
      .gitignore
  2. 17 0
      examcloud-task-api-provider/pom.xml
  3. 95 0
      examcloud-task-api-provider/src/main/java/cn/com/qmth/task/api/provider/TaskScheduleApi.java
  4. 28 0
      examcloud-task-base/pom.xml
  5. 50 0
      examcloud-task-base/src/main/java/cn/com/qmth/task/base/AbstractTask.java
  6. 53 0
      examcloud-task-base/src/main/java/cn/com/qmth/task/base/DistributionJob.java
  7. 308 0
      examcloud-task-base/src/main/java/cn/com/qmth/task/base/QuartzManager.java
  8. 121 0
      examcloud-task-base/src/main/java/cn/com/qmth/task/base/ScheduleJob.java
  9. 18 0
      examcloud-task-base/src/main/java/cn/com/qmth/task/base/StatefulDistributionJob.java
  10. 18 0
      examcloud-task-base/src/main/java/cn/com/qmth/task/base/Task.java
  11. 34 0
      examcloud-task-base/src/main/java/cn/com/qmth/task/base/enums/JobStatus.java
  12. 17 0
      examcloud-task-dao/pom.xml
  13. 171 0
      examcloud-task-dao/src/main/java/cn/com/qmth/task/entity/ScheduleJobEntity.java
  14. 16 0
      examcloud-task-dao/src/main/java/cn/com/qmth/task/repository/ScheduleJobEntityRepo.java
  15. 22 0
      examcloud-task-service/pom.xml
  16. 24 0
      examcloud-task-service/src/main/java/cn/com/qmth/task/service/ScheduleJobEntityService.java
  17. 53 0
      examcloud-task-service/src/main/java/cn/com/qmth/task/service/impl/ScheduleJobEntityServiceImpl.java
  18. 27 0
      examcloud-task-service/src/main/java/cn/com/qmth/task/service/job/OeCleanExamRecordTask.java
  19. 21 0
      examcloud-task-starter/pom.xml
  20. 13 0
      examcloud-task-starter/src/main/java/cn/com/qmth/task/starter/QuartzConfigration.java
  21. 23 0
      examcloud-task-starter/src/main/java/cn/com/qmth/task/starter/TaskApplication.java
  22. 30 0
      examcloud-task-starter/src/main/resources/application.properties
  23. 3 0
      examcloud-task-starter/src/main/resources/quartz.properties
  24. 31 0
      examcloud-task-starter/src/test/java/cn/com/qmth/task/TestTaskSchedule.java
  25. 30 0
      examcloud-task-starter/target/classes/application.properties
  26. 3 0
      examcloud-task-starter/target/classes/quartz.properties
  27. 20 0
      pom.xml

+ 10 - 0
.gitignore

@@ -0,0 +1,10 @@
+/*.project
+/.settings
+/examcloud-task-api-provider/*.classpath
+*.project
+/examcloud-task-api-provider/.settings
+*.classpath
+org.eclipse.core.resources.prefs
+org.eclipse.jdt.core.prefs
+org.eclipse.m2e.core.prefs
+*.class

+ 17 - 0
examcloud-task-api-provider/pom.xml

@@ -0,0 +1,17 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>cn.com.qmth.examcloud.task</groupId>
+    <artifactId>examcloud-task</artifactId>
+    <version>2.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>examcloud-task-api-provider</artifactId>
+  
+  <dependencies>
+		<dependency>
+			<groupId>cn.com.qmth.examcloud.task</groupId>
+			<artifactId>examcloud-task-service</artifactId>
+			<version>${examcloud.version}</version>
+		</dependency>
+  </dependencies>
+</project>

+ 95 - 0
examcloud-task-api-provider/src/main/java/cn/com/qmth/task/api/provider/TaskScheduleApi.java

@@ -0,0 +1,95 @@
+package cn.com.qmth.task.api.provider;
+
+import io.swagger.annotations.ApiOperation;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.com.qmth.task.base.QuartzManager;
+import cn.com.qmth.task.base.ScheduleJob;
+import cn.com.qmth.task.base.enums.JobStatus;
+import cn.com.qmth.task.entity.ScheduleJobEntity;
+import cn.com.qmth.task.service.ScheduleJobEntityService;
+
+@RestController
+@RequestMapping("${app.api.task}/taskScheduleApi")
+public class TaskScheduleApi {
+	
+	@Autowired
+	private ScheduleJobEntityService scheduleJobEntityService;
+	
+	@Autowired
+	private QuartzManager quartzManager;
+	
+	@ApiOperation(value="启动job")
+	@PostMapping("/executeJob/{jobName}")
+	public void executeJob(@PathVariable String jobName){
+		ScheduleJobEntity scheduleJobEntity = scheduleJobEntityService.findByJobName(jobName);
+		ScheduleJob scheduleJob = scheduleJobEntityService.transformationToScheduleJob(scheduleJobEntity);
+		try{
+			quartzManager.addJob(scheduleJob);
+			scheduleJobEntity.setJobStatus(JobStatus.RUNNING.name());
+			scheduleJobEntity.setOperateResult(JobStatus.RUNNING.name()+" SUCCESS");
+			scheduleJobEntityService.saveScheduleJobEntity(scheduleJobEntity);
+		}catch(Exception e){
+			scheduleJobEntity.setOperateResult(JobStatus.RUNNING.name()+" FAILED");
+			scheduleJobEntityService.saveScheduleJobEntity(scheduleJobEntity);
+			e.printStackTrace();
+		}
+	}
+	
+	@ApiOperation(value="暂停job")
+	@PostMapping("/pauseJob/{jobName}")
+	public void pauseJob(@PathVariable String jobName){
+		ScheduleJobEntity scheduleJobEntity = scheduleJobEntityService.findByJobName(jobName);
+		ScheduleJob scheduleJob = scheduleJobEntityService.transformationToScheduleJob(scheduleJobEntity);
+		try{
+			quartzManager.pauseJob(scheduleJob);
+			scheduleJobEntity.setJobStatus(JobStatus.PAUSE.name());
+			scheduleJobEntity.setOperateResult(JobStatus.PAUSE.name()+" SUCCESS");
+			scheduleJobEntityService.saveScheduleJobEntity(scheduleJobEntity);
+		}catch(Exception e){
+			scheduleJobEntity.setOperateResult(JobStatus.PAUSE.name()+" FAILED");
+			scheduleJobEntityService.saveScheduleJobEntity(scheduleJobEntity);
+			e.printStackTrace();
+		}
+	}
+	
+	@ApiOperation(value="从暂停中恢复job")
+	@PostMapping("/resumeJob/{jobName}")
+	public void resumeJob(@PathVariable String jobName){
+		ScheduleJobEntity scheduleJobEntity = scheduleJobEntityService.findByJobName(jobName);
+		ScheduleJob scheduleJob = scheduleJobEntityService.transformationToScheduleJob(scheduleJobEntity);
+		try{
+			quartzManager.resumeJob(scheduleJob);
+			scheduleJobEntity.setJobStatus(JobStatus.RESUME.name());
+			scheduleJobEntity.setOperateResult(JobStatus.RESUME.name()+" SUCCESS");
+			scheduleJobEntityService.saveScheduleJobEntity(scheduleJobEntity);
+		}catch(Exception e){
+			scheduleJobEntity.setOperateResult(JobStatus.RESUME.name()+" FAILED");
+			scheduleJobEntityService.saveScheduleJobEntity(scheduleJobEntity);
+			e.printStackTrace();
+		}
+	}
+	
+	@ApiOperation(value="删除job")
+	@PostMapping("/deleteJob/{jobName}")
+	public void deleteJob(@PathVariable String jobName){
+		ScheduleJobEntity scheduleJobEntity = scheduleJobEntityService.findByJobName(jobName);
+		ScheduleJob scheduleJob = scheduleJobEntityService.transformationToScheduleJob(scheduleJobEntity);
+		try{
+			quartzManager.deleteJob(scheduleJob);
+			scheduleJobEntity.setJobStatus(JobStatus.DELETE.name());
+			scheduleJobEntity.setOperateResult(JobStatus.DELETE.name()+" SUCCESS");
+			scheduleJobEntityService.saveScheduleJobEntity(scheduleJobEntity);
+		}catch(Exception e){
+			scheduleJobEntity.setOperateResult(JobStatus.DELETE.name()+" FAILED");
+			scheduleJobEntityService.saveScheduleJobEntity(scheduleJobEntity);
+			e.printStackTrace();
+		}
+	}
+	
+}

+ 28 - 0
examcloud-task-base/pom.xml

@@ -0,0 +1,28 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>cn.com.qmth.examcloud.task</groupId>
+    <artifactId>examcloud-task</artifactId>
+    <version>2.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>examcloud-task-base</artifactId>
+  
+  <dependencies>
+		<dependency>
+			<groupId>cn.com.qmth.examcloud.commons</groupId>
+			<artifactId>examcloud-commons-web</artifactId>
+			<version>${examcloud.version}</version>
+		</dependency>
+		<dependency>
+		    <groupId>org.quartz-scheduler</groupId>
+		    <artifactId>quartz</artifactId>
+		    <version>2.2.1</version>
+		 </dependency>
+		 <dependency>
+		    <groupId>org.quartz-scheduler</groupId>
+		    <artifactId>quartz-jobs</artifactId>
+		    <version>2.2.1</version>
+		 </dependency> 
+  </dependencies>		
+  
+</project>

+ 50 - 0
examcloud-task-base/src/main/java/cn/com/qmth/task/base/AbstractTask.java

@@ -0,0 +1,50 @@
+package cn.com.qmth.task.base;
+
+import cn.com.qmth.examcloud.commons.base.exception.ExamCloudRuntimeException;
+import cn.com.qmth.examcloud.commons.base.logging.ExamCloudLog;
+import cn.com.qmth.examcloud.commons.base.logging.ExamCloudLogFactory;
+import cn.com.qmth.examcloud.commons.base.util.JsonUtil;
+
+
+/**
+ * 任务调度抽象类
+ *
+ * @author WANGWEI
+ * @date 2018年3月7日
+ * @Copyright (c) 2018-2020 WANGWEI [QQ:522080330] All Rights Reserved.
+ */
+public abstract class AbstractTask implements Task
+{
+	
+	private static final ExamCloudLog taskLog = ExamCloudLogFactory.getLog("TASK_LOGGER");
+
+	@Override
+	public void execute(ScheduleJob scheduleJob)
+	{
+		if (taskLog.isDebugEnabled())
+		{
+			taskLog.debug("[TASK IN]. detail: " + JsonUtil.toJson(scheduleJob));
+		}
+		try
+		{
+			run(scheduleJob);
+		}
+		catch (Exception e)
+		{
+			if (taskLog.isErrorEnabled())
+			{
+				taskLog.error("[TASK EXCEPTION]. detail: " + JsonUtil.toJson(scheduleJob), e);
+			}
+			throw new ExamCloudRuntimeException(e);
+		}
+
+		if (taskLog.isDebugEnabled())
+		{
+			taskLog.debug("[TASK OUT]. detail: " + JsonUtil.toJson(scheduleJob));
+		}
+
+	}
+
+	public abstract void run(ScheduleJob scheduleJob) throws Exception;
+
+}

+ 53 - 0
examcloud-task-base/src/main/java/cn/com/qmth/task/base/DistributionJob.java

@@ -0,0 +1,53 @@
+package cn.com.qmth.task.base;
+
+import org.quartz.Job;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+
+import cn.com.qmth.examcloud.commons.base.logging.ExamCloudLog;
+import cn.com.qmth.examcloud.commons.base.logging.ExamCloudLogFactory;
+import cn.com.qmth.examcloud.commons.base.util.JsonUtil;
+import cn.com.qmth.examcloud.commons.web.support.SpringContextHolder;
+
+
+/**
+ * 并行任务分发器
+ *
+ * @author WANGWEI
+ * @date 2018年3月7日
+ * @Copyright (c) 2018-2020 WANGWEI [QQ:522080330] All Rights Reserved.
+ */
+public class DistributionJob implements Job
+{
+	private static final ExamCloudLog TASK_LOG = ExamCloudLogFactory.getLog("TASK_LOGGER");
+
+	@Override
+	public void execute(JobExecutionContext context) throws JobExecutionException
+	{
+		ScheduleJob scheduleJob = (ScheduleJob) context.getMergedJobDataMap().get("scheduleJob");
+
+		if (TASK_LOG.isDebugEnabled())
+		{
+			TASK_LOG.debug("distribute job. job detail :" + JsonUtil.toJson(scheduleJob));
+		}
+
+		try
+		{
+			Object bean = SpringContextHolder.getBean(scheduleJob.getSpringBean());
+
+			Task task = (Task) bean;
+			task.execute(scheduleJob);
+		}
+		catch (Exception e)
+		{
+			if (TASK_LOG.isErrorEnabled())
+			{
+				TASK_LOG.error(
+						"fail to distribute job. job detail :" + JsonUtil.toJson(scheduleJob), e);
+			}
+			throw new JobExecutionException(e);
+		}
+
+	}
+
+}

+ 308 - 0
examcloud-task-base/src/main/java/cn/com/qmth/task/base/QuartzManager.java

@@ -0,0 +1,308 @@
+package cn.com.qmth.task.base;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.quartz.CronScheduleBuilder;
+import org.quartz.CronTrigger;
+import org.quartz.Job;
+import org.quartz.JobBuilder;
+import org.quartz.JobDetail;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobKey;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.quartz.Trigger;
+import org.quartz.TriggerBuilder;
+import org.quartz.TriggerKey;
+import org.quartz.impl.matchers.GroupMatcher;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.quartz.SchedulerFactoryBean;
+import org.springframework.stereotype.Component;
+
+import cn.com.qmth.examcloud.commons.base.exception.ExamCloudRuntimeException;
+import cn.com.qmth.examcloud.commons.base.logging.ExamCloudLog;
+import cn.com.qmth.examcloud.commons.base.logging.ExamCloudLogFactory;
+import cn.com.qmth.examcloud.commons.base.util.JsonUtil;
+
+
+/**
+ * Quartz 管理器
+ *
+ * @author WANGWEI
+ * @date 2018年3月7日
+ * @Copyright (c) 2018-2020 WANGWEI [QQ:522080330] All Rights Reserved.
+ */
+@Component
+public class QuartzManager
+{
+	private static final ExamCloudLog TASK_LOG = ExamCloudLogFactory.getLog("TASK_LOGGER");
+
+	@Autowired
+	private SchedulerFactoryBean schedulerFactoryBean;
+
+	/**
+	 * 添加任务
+	 *
+	 * @author WANGWEI
+	 * @param job
+	 */
+	public void addJob(ScheduleJob job)
+	{
+		if (TASK_LOG.isDebugEnabled())
+		{
+			TASK_LOG.debug("add a job. job detail: " + JsonUtil.toJson(job));
+		}
+
+		try
+		{
+			Scheduler scheduler = schedulerFactoryBean.getScheduler();
+			TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());
+
+			CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
+
+			if (null == trigger)
+			{
+				Class<? extends Job> jobClass = job.isStateful()
+						? StatefulDistributionJob.class
+						: DistributionJob.class;
+				JobDetail jobDetail = JobBuilder.newJob(jobClass)
+						.withIdentity(job.getJobName(), job.getJobGroup()).build();
+
+				jobDetail.getJobDataMap().put("scheduleJob", job);
+
+				CronScheduleBuilder scheduleBuilder = CronScheduleBuilder
+						.cronSchedule(job.getCronExpression());
+
+				trigger = TriggerBuilder.newTrigger()
+						.withIdentity(job.getJobName(), job.getJobGroup())
+						.withSchedule(scheduleBuilder).build();
+
+				scheduler.scheduleJob(jobDetail, trigger);
+			}
+			else
+			{
+				CronScheduleBuilder scheduleBuilder = CronScheduleBuilder
+						.cronSchedule(job.getCronExpression());
+
+				trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
+						.withSchedule(scheduleBuilder).build();
+
+				scheduler.rescheduleJob(triggerKey, trigger);
+			}
+		}
+		catch (SchedulerException e)
+		{
+			TASK_LOG.error("Fail to add a job. job detail: " + JsonUtil.toJson(job), e);
+			throw new ExamCloudRuntimeException(e);
+		}
+
+		if (TASK_LOG.isDebugEnabled())
+		{
+			TASK_LOG.debug("add a job successfully. job detail: " + JsonUtil.toJson(job));
+		}
+	}
+
+	/**
+	 * 获取所有计划中的任务列表
+	 *
+	 * @author WANGWEI
+	 * @return
+	 */
+	public List<ScheduleJob> getAllJobs()
+	{
+		try
+		{
+			Scheduler scheduler = schedulerFactoryBean.getScheduler();
+			GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
+			Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
+			List<ScheduleJob> jobList = new ArrayList<ScheduleJob>();
+			for (JobKey jobKey : jobKeys)
+			{
+				List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
+				for (Trigger trigger : triggers)
+				{
+					ScheduleJob job = new ScheduleJob();
+					job.setJobName(jobKey.getName());
+					job.setJobGroup(jobKey.getGroup());
+					job.setDescription("触发器:" + trigger.getKey());
+					Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
+					job.setJobStatus(triggerState.name());
+					if (trigger instanceof CronTrigger)
+					{
+						CronTrigger cronTrigger = (CronTrigger) trigger;
+						String cronExpression = cronTrigger.getCronExpression();
+						job.setCronExpression(cronExpression);
+					}
+					jobList.add(job);
+				}
+			}
+			return jobList;
+		}
+		catch (SchedulerException e)
+		{
+			TASK_LOG.error("Fail to get all jobs.", e);
+			throw new ExamCloudRuntimeException(e);
+		}
+	}
+
+	/**
+	 * 所有正在运行的job
+	 *
+	 * @author WANGWEI
+	 * @return
+	 */
+	public List<ScheduleJob> getRunningJobs()
+	{
+		try
+		{
+			Scheduler scheduler = schedulerFactoryBean.getScheduler();
+			List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
+			List<ScheduleJob> jobList = new ArrayList<ScheduleJob>(executingJobs.size());
+			for (JobExecutionContext executingJob : executingJobs)
+			{
+				ScheduleJob job = new ScheduleJob();
+				JobDetail jobDetail = executingJob.getJobDetail();
+				JobKey jobKey = jobDetail.getKey();
+				Trigger trigger = executingJob.getTrigger();
+				job.setJobName(jobKey.getName());
+				job.setJobGroup(jobKey.getGroup());
+				job.setDescription("触发器:" + trigger.getKey());
+				Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
+				job.setJobStatus(triggerState.name());
+				if (trigger instanceof CronTrigger)
+				{
+					CronTrigger cronTrigger = (CronTrigger) trigger;
+					String cronExpression = cronTrigger.getCronExpression();
+					job.setCronExpression(cronExpression);
+				}
+				jobList.add(job);
+			}
+			return jobList;
+		}
+		catch (Exception e)
+		{
+			TASK_LOG.error("Fail to get running jobs.", e);
+			throw new ExamCloudRuntimeException(e);
+		}
+	}
+
+	/**
+	 * 暂停一个job
+	 *
+	 * @author WANGWEI
+	 * @param scheduleJob
+	 */
+	public void pauseJob(ScheduleJob scheduleJob)
+	{
+		try
+		{
+			Scheduler scheduler = schedulerFactoryBean.getScheduler();
+			JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
+			scheduler.pauseJob(jobKey);
+		}
+		catch (SchedulerException e)
+		{
+			TASK_LOG.error("Fail to pause Job. job detail: " + JsonUtil.toJson(scheduleJob), e);
+			throw new ExamCloudRuntimeException(e);
+		}
+	}
+
+	/**
+	 * 恢复一个job
+	 *
+	 * @author WANGWEI
+	 * @param scheduleJob
+	 */
+	public void resumeJob(ScheduleJob scheduleJob)
+	{
+		try
+		{
+			Scheduler scheduler = schedulerFactoryBean.getScheduler();
+			JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
+			scheduler.resumeJob(jobKey);
+		}
+		catch (SchedulerException e)
+		{
+			TASK_LOG.error("Fail to resume job. job detail: " + JsonUtil.toJson(scheduleJob), e);
+			throw new ExamCloudRuntimeException(e);
+		}
+	}
+
+	/**
+	 * 删除一个job
+	 *
+	 * @author WANGWEI
+	 * @param scheduleJob
+	 */
+	public void deleteJob(ScheduleJob scheduleJob)
+	{
+		try
+		{
+			Scheduler scheduler = schedulerFactoryBean.getScheduler();
+			JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
+			scheduler.deleteJob(jobKey);
+		}
+		catch (SchedulerException e)
+		{
+			TASK_LOG.error("Fail to delete job. job detail: " + JsonUtil.toJson(scheduleJob), e);
+			throw new ExamCloudRuntimeException(e);
+		}
+
+	}
+
+	/**
+	 * 执行job
+	 *
+	 * @author WANGWEI
+	 * @param scheduleJob
+	 */
+	public void runJob(ScheduleJob scheduleJob)
+	{
+		try
+		{
+			Scheduler scheduler = schedulerFactoryBean.getScheduler();
+			JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
+			scheduler.triggerJob(jobKey);
+		}
+		catch (SchedulerException e)
+		{
+			TASK_LOG.error("Fail to run job. job detail: " + JsonUtil.toJson(scheduleJob), e);
+			throw new ExamCloudRuntimeException(e);
+		}
+	}
+
+	/**
+	 * 更新job时间表达式
+	 *
+	 * @author WANGWEI
+	 * @param scheduleJob
+	 */
+	public void updateJobCronExpression(ScheduleJob scheduleJob)
+	{
+		try
+		{
+			Scheduler scheduler = schedulerFactoryBean.getScheduler();
+
+			TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(),
+					scheduleJob.getJobGroup());
+
+			CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
+
+			CronScheduleBuilder scheduleBuilder = CronScheduleBuilder
+					.cronSchedule(scheduleJob.getCronExpression());
+
+			trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
+					.withSchedule(scheduleBuilder).build();
+
+			scheduler.rescheduleJob(triggerKey, trigger);
+		}
+		catch (SchedulerException e)
+		{
+			TASK_LOG.error("Fail to update job cron expression. job detail :"
+					+ JsonUtil.toJson(scheduleJob), e);
+			throw new ExamCloudRuntimeException(e);
+		}
+	}
+}

+ 121 - 0
examcloud-task-base/src/main/java/cn/com/qmth/task/base/ScheduleJob.java

@@ -0,0 +1,121 @@
+package cn.com.qmth.task.base;
+
+import java.io.Serializable;
+
+/**
+ * 任务配置
+ *
+ * @author WANGWEI
+ * @date 2018年3月7日
+ * @Copyright (c) 2018-2020 WANGWEI [QQ:522080330] All Rights Reserved.
+ */
+public class ScheduleJob implements Serializable
+{
+	private static final long serialVersionUID = -1638993747996917970L;
+
+	/**
+	 * 任务名称
+	 */
+	private String jobName;
+
+	/**
+	 * 任务分组
+	 */
+	private String jobGroup;
+
+	/**
+	 * 任务状态
+	 */
+	private String jobStatus;
+
+	/**
+	 * cron表达式
+	 */
+	private String cronExpression;
+
+	/**
+	 * 描述
+	 */
+	private String description;
+
+	/**
+	 * Spring bean
+	 */
+	private String springBean;
+
+	/**
+	 * 是否顺序执行
+	 */
+	private boolean stateful = true;
+
+	public String getJobName()
+	{
+		return jobName;
+	}
+
+	public void setJobName(String jobName)
+	{
+		this.jobName = jobName;
+	}
+
+	public String getJobGroup()
+	{
+		return jobGroup;
+	}
+
+	public void setJobGroup(String jobGroup)
+	{
+		this.jobGroup = jobGroup;
+	}
+
+	public String getJobStatus()
+	{
+		return jobStatus;
+	}
+
+	public void setJobStatus(String jobStatus)
+	{
+		this.jobStatus = jobStatus;
+	}
+
+	public String getCronExpression()
+	{
+		return cronExpression;
+	}
+
+	public void setCronExpression(String cronExpression)
+	{
+		this.cronExpression = cronExpression;
+	}
+
+	public String getDescription()
+	{
+		return description;
+	}
+
+	public void setDescription(String description)
+	{
+		this.description = description;
+	}
+
+	public String getSpringBean()
+	{
+		return springBean;
+	}
+
+	public void setSpringBean(String springBean)
+	{
+		this.springBean = springBean;
+	}
+
+	public boolean isStateful()
+	{
+		return stateful;
+	}
+
+	public void setStateful(boolean stateful)
+	{
+		this.stateful = stateful;
+	}
+
+}

+ 18 - 0
examcloud-task-base/src/main/java/cn/com/qmth/task/base/StatefulDistributionJob.java

@@ -0,0 +1,18 @@
+package cn.com.qmth.task.base;
+
+import org.quartz.DisallowConcurrentExecution;
+import org.quartz.PersistJobDataAfterExecution;
+
+/**
+ * 顺序执行任务分发器
+ *
+ * @author WANGWEI
+ * @date 2018年3月7日
+ * @Copyright (c) 2018-2020 WANGWEI [QQ:522080330] All Rights Reserved.
+ */
+@DisallowConcurrentExecution
+@PersistJobDataAfterExecution
+public class StatefulDistributionJob extends DistributionJob
+{
+
+}

+ 18 - 0
examcloud-task-base/src/main/java/cn/com/qmth/task/base/Task.java

@@ -0,0 +1,18 @@
+package cn.com.qmth.task.base;
+
+/**
+ * 任务接口
+ *
+ * @author WANGWEI
+ * @date 2018年3月7日
+ * @Copyright (c) 2018-2020 WANGWEI [QQ:522080330] All Rights Reserved.
+ */
+public interface Task
+{
+	/**
+	 * 执行任务
+	 *
+	 * @author WANGWEI
+	 */
+	void execute(ScheduleJob scheduleJob);
+}

+ 34 - 0
examcloud-task-base/src/main/java/cn/com/qmth/task/base/enums/JobStatus.java

@@ -0,0 +1,34 @@
+package cn.com.qmth.task.base.enums;
+
+/**
+ * @author  	chenken
+ * @date    	2018年7月11日 上午11:28:26
+ * @company 	QMTH
+ * @description JobStatus.java
+ */
+public enum JobStatus {
+
+	RUNNING("运行中"),
+	
+	PAUSE("暂停"),
+	
+	RESUME("从暂停中恢复"),
+	
+	DELETE("已删除");
+	
+	private String name;
+	
+
+	private JobStatus(String name) {
+		this.name = name;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+	
+}

+ 17 - 0
examcloud-task-dao/pom.xml

@@ -0,0 +1,17 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>cn.com.qmth.examcloud.task</groupId>
+    <artifactId>examcloud-task</artifactId>
+    <version>2.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>examcloud-task-dao</artifactId>
+  
+  <dependencies>
+		<dependency>
+			<groupId>cn.com.qmth.examcloud.task</groupId>
+			<artifactId>examcloud-task-base</artifactId>
+			<version>${examcloud.version}</version>
+		</dependency>
+  </dependencies>
+</project>

+ 171 - 0
examcloud-task-dao/src/main/java/cn/com/qmth/task/entity/ScheduleJobEntity.java

@@ -0,0 +1,171 @@
+package cn.com.qmth.task.entity;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import javax.validation.constraints.NotNull;
+
+/**
+ * 任务配置
+ *
+ * @author WANGWEI
+ * @date 2018年3月7日
+ * @Copyright (c) 2018-2020 WANGWEI [QQ:522080330] All Rights Reserved.
+ */
+@Table(name="ec_b_schedule_job",uniqueConstraints = {@UniqueConstraint(columnNames={"jobName"})})
+public class ScheduleJobEntity implements Serializable
+{
+	private static final long serialVersionUID = -1638993747996917970L;
+	
+	@Id
+	@GeneratedValue
+	private Long id;
+
+	/**
+	 * 任务名称
+	 */
+	@NotNull
+	private String jobName;
+
+	/**
+	 * 任务分组
+	 */
+	@NotNull
+	private String jobGroup;
+
+	/**
+	 * 任务当前状态
+	 */
+	private String jobStatus;
+
+	/**
+	 * cron表达式
+	 */
+	@NotNull
+	private String cronExpression;
+
+	/**
+	 * 描述
+	 */
+	private String description;
+
+	/**
+	 * Spring bean
+	 */
+	@NotNull
+	private String springBean;
+
+	/**
+	 * 是否顺序执行
+	 */
+	private boolean stateful;
+	
+	/**
+	 * job操作时间
+	 */
+	private Date operateTime;
+
+	/**
+	 * job操作是否成功
+	 */
+	private String operateResult;
+	
+	public String getJobName()
+	{
+		return jobName;
+	}
+
+	public void setJobName(String jobName)
+	{
+		this.jobName = jobName;
+	}
+
+	public String getJobGroup()
+	{
+		return jobGroup;
+	}
+
+	public void setJobGroup(String jobGroup)
+	{
+		this.jobGroup = jobGroup;
+	}
+
+	public String getJobStatus()
+	{
+		return jobStatus;
+	}
+
+	public void setJobStatus(String jobStatus)
+	{
+		this.jobStatus = jobStatus;
+	}
+
+	public String getCronExpression()
+	{
+		return cronExpression;
+	}
+
+	public void setCronExpression(String cronExpression)
+	{
+		this.cronExpression = cronExpression;
+	}
+
+	public String getDescription()
+	{
+		return description;
+	}
+
+	public void setDescription(String description)
+	{
+		this.description = description;
+	}
+
+	public String getSpringBean()
+	{
+		return springBean;
+	}
+
+	public void setSpringBean(String springBean)
+	{
+		this.springBean = springBean;
+	}
+
+	public boolean isStateful()
+	{
+		return stateful;
+	}
+
+	public void setStateful(boolean stateful)
+	{
+		this.stateful = stateful;
+	}
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public Date getOperateTime() {
+		return operateTime;
+	}
+
+	public void setOperateTime(Date operateTime) {
+		this.operateTime = operateTime;
+	}
+
+	public String getOperateResult() {
+		return operateResult;
+	}
+
+	public void setOperateResult(String operateResult) {
+		this.operateResult = operateResult;
+	}
+	
+}

+ 16 - 0
examcloud-task-dao/src/main/java/cn/com/qmth/task/repository/ScheduleJobEntityRepo.java

@@ -0,0 +1,16 @@
+package cn.com.qmth.task.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.stereotype.Repository;
+
+import cn.com.qmth.task.entity.ScheduleJobEntity;
+
+@Repository
+public interface ScheduleJobEntityRepo  extends JpaRepository<ScheduleJobEntity,Long>,
+												JpaSpecificationExecutor<ScheduleJobEntity>{
+
+	public ScheduleJobEntity findByJobName(String jobName);
+	
+}
+

+ 22 - 0
examcloud-task-service/pom.xml

@@ -0,0 +1,22 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>cn.com.qmth.examcloud.task</groupId>
+    <artifactId>examcloud-task</artifactId>
+    <version>2.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>examcloud-task-service</artifactId>
+  
+  <dependencies>
+		<dependency>
+			<groupId>cn.com.qmth.examcloud.task</groupId>
+			<artifactId>examcloud-task-base</artifactId>
+			<version>${examcloud.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>cn.com.qmth.examcloud.task</groupId>
+			<artifactId>examcloud-task-dao</artifactId>
+			<version>${examcloud.version}</version>
+		</dependency>
+  </dependencies>	
+</project>

+ 24 - 0
examcloud-task-service/src/main/java/cn/com/qmth/task/service/ScheduleJobEntityService.java

@@ -0,0 +1,24 @@
+package cn.com.qmth.task.service;
+
+import java.util.List;
+
+import cn.com.qmth.task.base.ScheduleJob;
+import cn.com.qmth.task.entity.ScheduleJobEntity;
+
+
+public interface ScheduleJobEntityService {
+
+	public List<ScheduleJobEntity> findAll();
+	
+	
+	public void saveScheduleJobEntity(ScheduleJobEntity scheduleJobEntity);
+	
+	/**
+	 * 根据jobName查询
+	 * @param jobName
+	 * @return
+	 */
+	public ScheduleJobEntity findByJobName(String jobName);
+	
+	public ScheduleJob transformationToScheduleJob(ScheduleJobEntity scheduleJobEntity);
+}

+ 53 - 0
examcloud-task-service/src/main/java/cn/com/qmth/task/service/impl/ScheduleJobEntityServiceImpl.java

@@ -0,0 +1,53 @@
+package cn.com.qmth.task.service.impl;
+
+import java.util.Date;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import cn.com.qmth.task.base.ScheduleJob;
+import cn.com.qmth.task.entity.ScheduleJobEntity;
+import cn.com.qmth.task.repository.ScheduleJobEntityRepo;
+import cn.com.qmth.task.service.ScheduleJobEntityService;
+
+@Service("scheduleJobEntityService")
+public class ScheduleJobEntityServiceImpl implements ScheduleJobEntityService{
+	
+	@Autowired
+	private ScheduleJobEntityRepo scheduleJobEntityRepo;
+
+	@Override
+	public List<ScheduleJobEntity> findAll() {
+		return scheduleJobEntityRepo.findAll();
+	}
+
+	@Override
+	public void saveScheduleJobEntity(ScheduleJobEntity scheduleJobEntity) {
+		scheduleJobEntity.setOperateTime(new Date());
+		scheduleJobEntityRepo.save(scheduleJobEntity);
+	}
+
+	@Override
+	public ScheduleJobEntity findByJobName(String jobName) {
+		if(StringUtils.isBlank(jobName)){
+			return null;
+		}
+		return scheduleJobEntityRepo.findByJobName(jobName);
+	}
+
+	@Override
+	public ScheduleJob transformationToScheduleJob(ScheduleJobEntity scheduleJobEntity) {
+		if(scheduleJobEntity==null){
+			return null;
+		}
+		ScheduleJob scheduleJob = new ScheduleJob();
+		scheduleJob.setJobName(scheduleJobEntity.getJobName());
+		scheduleJob.setJobGroup(scheduleJobEntity.getJobGroup());
+		scheduleJob.setSpringBean(scheduleJobEntity.getSpringBean());
+		scheduleJob.setCronExpression(scheduleJobEntity.getCronExpression());
+		return scheduleJob;
+	}
+
+}

+ 27 - 0
examcloud-task-service/src/main/java/cn/com/qmth/task/service/job/OeCleanExamRecordTask.java

@@ -0,0 +1,27 @@
+package cn.com.qmth.task.service.job;
+
+import java.util.Date;
+
+import org.springframework.stereotype.Component;
+
+import cn.com.qmth.examcloud.commons.base.util.DateFormat;
+import cn.com.qmth.task.base.AbstractTask;
+import cn.com.qmth.task.base.ScheduleJob;
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年7月11日 上午11:02:50
+ * @company 	QMTH
+ * @description 网考考试记录清理job
+ */
+@Component("oeCleanExamRecordTask")
+public class OeCleanExamRecordTask extends AbstractTask{
+
+	@Override
+	public void run(ScheduleJob scheduleJob)
+			throws Exception {
+		String now = DateFormat.dateFormat(new Date());
+		System.out.println(now+"oeCleanExamRecordTask Run");
+	}
+}

+ 21 - 0
examcloud-task-starter/pom.xml

@@ -0,0 +1,21 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>cn.com.qmth.examcloud.task</groupId>
+    <artifactId>examcloud-task</artifactId>
+    <version>2.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>examcloud-task-starter</artifactId>
+  
+  <dependencies>
+		<dependency>
+			<groupId>cn.com.qmth.examcloud.task</groupId>
+			<artifactId>examcloud-task-api-provider</artifactId>
+			<version>${examcloud.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-test</artifactId>
+		</dependency>
+  </dependencies>
+</project>

+ 13 - 0
examcloud-task-starter/src/main/java/cn/com/qmth/task/starter/QuartzConfigration.java

@@ -0,0 +1,13 @@
+package cn.com.qmth.task.starter;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.quartz.SchedulerFactoryBean;
+
+@Configuration
+public class QuartzConfigration {
+	@Bean(name = "schedulerFactoryBean")
+    public SchedulerFactoryBean schedulerFactory() {
+        return new SchedulerFactoryBean();
+	}
+}

+ 23 - 0
examcloud-task-starter/src/main/java/cn/com/qmth/task/starter/TaskApplication.java

@@ -0,0 +1,23 @@
+package cn.com.qmth.task.starter;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.domain.EntityScan;
+import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+@SpringBootApplication(scanBasePackages = {"cn.com.qmth"})
+@EnableJpaRepositories(basePackages = {"cn.com.qmth"})
+@EntityScan(basePackages = {"cn.com.qmth"})
+@EnableEurekaClient
+@EnableScheduling
+@EnableAutoConfiguration
+public class TaskApplication {
+
+    public static void main(String[] args) throws Exception {
+        SpringApplication.run(TaskApplication.class);
+    }
+    
+}

+ 30 - 0
examcloud-task-starter/src/main/resources/application.properties

@@ -0,0 +1,30 @@
+spring.profiles.active=dev
+spring.datasource.url=jdbc:mysql://192.168.10.30:3306/exam_cloud_test?useUnicode=true&characterEncoding=UTF-8
+spring.datasource.username=root
+spring.datasource.password=root
+spring.datasource.validation-query=SELECT 1 FROM DUAL
+spring.datasource.test-on-borrow=true
+
+spring.http.multipart.max-file-size=50Mb
+spring.http.multipart.max-request-size=50Mb
+
+spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
+spring.jackson.time-zone=GMT+8
+
+spring.jpa.show-sql=true
+spring.jpa.hibernate.ddl-auto=update
+app.oe.examcontrol.session.expire=30
+app.oe.examcontrol.session.captureCycle=300
+
+#springcloud
+spring.application.name=EXAMCLOUD-SERVICE-TASK
+server.port=8011
+
+eureka.client.serviceUrl.defaultZone=http://192.168.10.30:1111/eureka/
+hystrix.command.default.execution.timeout.enabled=false
+hystrix.threadpool.default.coreSize = 500
+
+feign.httpclient.enabled=false
+feign.okhttp.enabled=true
+
+app.api.task=/api/task

+ 3 - 0
examcloud-task-starter/src/main/resources/quartz.properties

@@ -0,0 +1,3 @@
+org.quartz.scheduler.instanceName = MyScheduler
+org.quartz.threadPool.threadCount = 3
+org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

+ 31 - 0
examcloud-task-starter/src/test/java/cn/com/qmth/task/TestTaskSchedule.java

@@ -0,0 +1,31 @@
+package cn.com.qmth.task;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import cn.com.qmth.task.base.QuartzManager;
+import cn.com.qmth.task.base.ScheduleJob;
+
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes={cn.com.qmth.task.starter.TaskApplication.class})
+public class TestTaskSchedule {
+
+	@Autowired
+	private QuartzManager quartzManager;
+	
+	@Test
+	public void executeTaskTest(){
+		ScheduleJob scheduleJob = new ScheduleJob();
+		scheduleJob.setJobName("clean exam");
+		scheduleJob.setJobGroup("OE");
+		scheduleJob.setSpringBean("oeCleanExamRecordTask");
+		scheduleJob.setCronExpression("0/10 * * * * ?");
+		scheduleJob.setStateful(true);
+		quartzManager.addJob(scheduleJob);
+	}
+	
+}

+ 30 - 0
examcloud-task-starter/target/classes/application.properties

@@ -0,0 +1,30 @@
+spring.profiles.active=dev
+spring.datasource.url=jdbc:mysql://192.168.10.30:3306/exam_cloud_test?useUnicode=true&characterEncoding=UTF-8
+spring.datasource.username=root
+spring.datasource.password=root
+spring.datasource.validation-query=SELECT 1 FROM DUAL
+spring.datasource.test-on-borrow=true
+
+spring.http.multipart.max-file-size=50Mb
+spring.http.multipart.max-request-size=50Mb
+
+spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
+spring.jackson.time-zone=GMT+8
+
+spring.jpa.show-sql=true
+spring.jpa.hibernate.ddl-auto=update
+app.oe.examcontrol.session.expire=30
+app.oe.examcontrol.session.captureCycle=300
+
+#springcloud
+spring.application.name=EXAMCLOUD-SERVICE-TASK
+server.port=8011
+
+eureka.client.serviceUrl.defaultZone=http://192.168.10.30:1111/eureka/
+hystrix.command.default.execution.timeout.enabled=false
+hystrix.threadpool.default.coreSize = 500
+
+feign.httpclient.enabled=false
+feign.okhttp.enabled=true
+
+app.api.task=/api/task

+ 3 - 0
examcloud-task-starter/target/classes/quartz.properties

@@ -0,0 +1,3 @@
+org.quartz.scheduler.instanceName = MyScheduler
+org.quartz.threadPool.threadCount = 3
+org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

+ 20 - 0
pom.xml

@@ -0,0 +1,20 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+	<groupId>cn.com.qmth.examcloud</groupId>
+	<artifactId>examcloud-parent</artifactId>
+	<version>2.0-SNAPSHOT</version>
+  </parent>
+  <groupId>cn.com.qmth.examcloud.task</groupId>
+  <artifactId>examcloud-task</artifactId>
+  <packaging>pom</packaging>
+  <name>cn.com.qmth.examcloud.task</name>
+  
+  <modules>
+  	<module>examcloud-task-service</module>
+  	<module>examcloud-task-base</module>
+  	<module>examcloud-task-starter</module>
+  	<module>examcloud-task-api-provider</module>
+  	<module>examcloud-task-dao</module>
+  </modules>
+</project>