Jelajahi Sumber

邮箱配置模块功能开发,定时任务开发

chenjianhua 7 bulan lalu
induk
melakukan
84285d8896
19 mengubah file dengan 854 tambahan dan 4 penghapusan
  1. 4 0
      service-base/pom.xml
  2. 0 2
      service-base/src/main/java/com/simuwang/base/common/conts/Constants.java
  3. 50 0
      service-base/src/main/java/com/simuwang/base/common/conts/ScheduleConstants.java
  4. 1 0
      service-base/src/main/java/com/simuwang/base/common/util/StringUtil.java
  5. 2 2
      service-base/src/main/java/com/simuwang/base/mapper/MailboxInfoMapper.java
  6. 46 0
      service-base/src/main/java/com/simuwang/schedule/domain/SysJob.java
  7. 39 0
      service-base/src/main/java/com/simuwang/schedule/domain/SysJobLog.java
  8. 34 0
      service-base/src/main/java/com/simuwang/schedule/exception/TaskException.java
  9. 103 0
      service-base/src/main/java/com/simuwang/schedule/util/AbstractQuartzJob.java
  10. 110 0
      service-base/src/main/java/com/simuwang/schedule/util/BeanUtils.java
  11. 95 0
      service-base/src/main/java/com/simuwang/schedule/util/CronUtils.java
  12. 185 0
      service-base/src/main/java/com/simuwang/schedule/util/JobInvokeUtil.java
  13. 23 0
      service-base/src/main/java/com/simuwang/schedule/util/QuartzDisallowConcurrentExecution.java
  14. 21 0
      service-base/src/main/java/com/simuwang/schedule/util/QuartzJobExecution.java
  15. 108 0
      service-base/src/main/java/com/simuwang/schedule/util/ScheduleUtils.java
  16. 8 0
      service-base/src/main/resources/mapper/MailBoxInfoMapper.xml
  17. 3 0
      service-manage/src/main/java/com/simuwang/manage/api/email/EmailConfigController.java
  18. 2 0
      service-manage/src/main/java/com/simuwang/manage/service/EmailConfigService.java
  19. 20 0
      service-manage/src/main/java/com/simuwang/manage/service/impl/EmailConfigServiceImpl.java

+ 4 - 0
service-base/pom.xml

@@ -183,5 +183,9 @@
             <artifactId>jjwt</artifactId>
             <version>0.12.1</version>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-quartz</artifactId>
+        </dependency>
     </dependencies>
 </project>

+ 0 - 2
service-base/src/main/java/com/simuwang/base/common/conts/Constants.java

@@ -143,6 +143,4 @@ public class Constants
      */
     public static final String LOOKUP_LDAPS = "ldaps:";
 
-
-
 }

+ 50 - 0
service-base/src/main/java/com/simuwang/base/common/conts/ScheduleConstants.java

@@ -0,0 +1,50 @@
+package com.simuwang.base.common.conts;
+
+/**
+ * 任务调度通用常量
+ * 
+ * @author ruoyi
+ */
+public class ScheduleConstants
+{
+    public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME";
+
+    /** 执行目标key */
+    public static final String TASK_PROPERTIES = "TASK_PROPERTIES";
+
+    /** 默认 */
+    public static final String MISFIRE_DEFAULT = "0";
+
+    /** 立即触发执行 */
+    public static final String MISFIRE_IGNORE_MISFIRES = "1";
+
+    /** 触发一次执行 */
+    public static final String MISFIRE_FIRE_AND_PROCEED = "2";
+
+    /** 不触发立即执行 */
+    public static final String MISFIRE_DO_NOTHING = "3";
+
+    public enum Status
+    {
+        /**
+         * 正常
+         */
+        NORMAL("0"),
+        /**
+         * 暂停
+         */
+        PAUSE("1");
+
+        private String value;
+
+        private Status(String value)
+        {
+            this.value = value;
+        }
+
+        public String getValue()
+        {
+            return value;
+        }
+    }
+}

+ 1 - 0
service-base/src/main/java/com/simuwang/base/common/util/StringUtil.java

@@ -8,6 +8,7 @@ import java.util.List;
 
 import cn.hutool.core.text.StrFormatter;
 import com.simuwang.base.common.conts.Constants;
+import org.apache.commons.lang3.CharSequenceUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.util.AntPathMatcher;
 

+ 2 - 2
service-base/src/main/java/com/simuwang/base/mapper/MailboxInfoMapper.java

@@ -20,7 +20,7 @@ public interface MailboxInfoMapper extends BaseMapper<MailboxInfoDO> {
 
     List<MailboxInfoTableVO> searchEmailConfigList(String email);
 
-    void saveEmailConfig(MailboxInfoDO mailboxInfoDO);
-
     void deleteEmailConfigByIds(@Param("ids") String[] split);
+
+    MailboxInfoDO checkEmailUnique(@Param("email") String email);
 }

+ 46 - 0
service-base/src/main/java/com/simuwang/schedule/domain/SysJob.java

@@ -0,0 +1,46 @@
+package com.simuwang.schedule.domain;
+
+
+import com.simuwang.base.common.conts.ScheduleConstants;
+import lombok.Data;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 定时任务调度表 sys_job
+ * 
+ * @author ruoyi
+ */
+@Data
+public class SysJob implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 任务ID */
+    private Long jobId;
+
+    /** 任务名称 */
+    private String jobName;
+
+    /** 任务组名 */
+    private String jobGroup;
+
+    /** 调用目标字符串 */
+    private String invokeTarget;
+
+    /** cron执行表达式 */
+    private String cronExpression;
+
+    /** cron计划策略 */
+    private String misfirePolicy = ScheduleConstants.MISFIRE_DEFAULT;
+
+    /** 是否并发执行(0允许 1禁止) */
+    private String concurrent;
+
+    /** 任务状态(0正常 1暂停) */
+    private String status;
+
+}

+ 39 - 0
service-base/src/main/java/com/simuwang/schedule/domain/SysJobLog.java

@@ -0,0 +1,39 @@
+package com.simuwang.schedule.domain;
+import lombok.Data;
+import java.util.Date;
+
+/**
+ *
+ */
+@Data
+public class SysJobLog
+{
+    private static final long serialVersionUID = 1L;
+
+    /** ID */
+    private Long jobLogId;
+
+    /** 任务名称 */
+    private String jobName;
+
+    /** 任务组名 */
+    private String jobGroup;
+
+    /** 调用目标字符串 */
+    private String invokeTarget;
+
+    /** 日志信息 */
+    private String jobMessage;
+
+    /** 执行状态(0正常 1失败) */
+    private String status;
+
+    /** 异常信息 */
+    private String exceptionInfo;
+
+    /** 开始时间 */
+    private Date startTime;
+
+    /** 结束时间 */
+    private Date endTime;
+}

+ 34 - 0
service-base/src/main/java/com/simuwang/schedule/exception/TaskException.java

@@ -0,0 +1,34 @@
+package com.simuwang.schedule.exception;
+
+/**
+ * 计划策略异常
+ * 
+ * @author ruoyi
+ */
+public class TaskException extends Exception
+{
+    private static final long serialVersionUID = 1L;
+
+    private Code code;
+
+    public TaskException(String msg, Code code)
+    {
+        this(msg, code, null);
+    }
+
+    public TaskException(String msg, Code code, Exception nestedEx)
+    {
+        super(msg, nestedEx);
+        this.code = code;
+    }
+
+    public Code getCode()
+    {
+        return code;
+    }
+
+    public enum Code
+    {
+        TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE
+    }
+}

+ 103 - 0
service-base/src/main/java/com/simuwang/schedule/util/AbstractQuartzJob.java

@@ -0,0 +1,103 @@
+package com.simuwang.schedule.util;
+
+import cn.hutool.extra.spring.SpringUtil;
+import com.simuwang.base.common.conts.Constants;
+import com.simuwang.base.common.conts.ScheduleConstants;
+import com.simuwang.schedule.domain.SysJob;
+import com.simuwang.schedule.domain.SysJobLog;
+import org.apache.commons.lang3.StringUtils;
+import org.quartz.Job;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Date;
+
+/**
+ * 抽象quartz调用
+ *
+ * @author ruoyi
+ */
+public abstract class AbstractQuartzJob implements Job
+{
+    private static final Logger log = LoggerFactory.getLogger(AbstractQuartzJob.class);
+
+    /**
+     * 线程本地变量
+     */
+    private static ThreadLocal<Date> threadLocal = new ThreadLocal<>();
+
+    @Override
+    public void execute(JobExecutionContext context) throws JobExecutionException
+    {
+        SysJob sysJob = new SysJob();
+        BeanUtils.copyBeanProp(sysJob, context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES));
+        try
+        {
+            before(context, sysJob);
+            if (sysJob != null)
+            {
+                doExecute(context, sysJob);
+            }
+            after(context, sysJob, null);
+        }
+        catch (Exception e)
+        {
+            log.error("任务执行异常  - :", e);
+            after(context, sysJob, e);
+        }
+    }
+
+    /**
+     * 执行前
+     *
+     * @param context 工作执行上下文对象
+     * @param sysJob 系统计划任务
+     */
+    protected void before(JobExecutionContext context, SysJob sysJob)
+    {
+        threadLocal.set(new Date());
+    }
+
+    /**
+     * 执行后
+     *
+     * @param context 工作执行上下文对象
+     * @param sysJob 系统计划任务
+     */
+    protected void after(JobExecutionContext context, SysJob sysJob, Exception e)
+    {
+        Date startTime = threadLocal.get();
+        threadLocal.remove();
+        final SysJobLog sysJobLog = new SysJobLog();
+        sysJobLog.setJobName(sysJob.getJobName());
+        sysJobLog.setJobGroup(sysJob.getJobGroup());
+        sysJobLog.setInvokeTarget(sysJob.getInvokeTarget());
+        sysJobLog.setStartTime(startTime);
+        sysJobLog.setEndTime(new Date());
+        long runMs = sysJobLog.getEndTime().getTime() - sysJobLog.getStartTime().getTime();
+        sysJobLog.setJobMessage(sysJobLog.getJobName() + " 总共耗时:" + runMs + "毫秒");
+        if (e != null)
+        {
+            sysJobLog.setStatus(Constants.FAIL);
+            String errorMsg = StringUtils.substring(e.getMessage(), 0, 2000);
+            sysJobLog.setExceptionInfo(errorMsg);
+        }
+        else
+        {
+            sysJobLog.setStatus(Constants.SUCCESS);
+        }
+        // 写入数据库当中
+//        SpringUtil.getBean(ISysJobLogService.class).addJobLog(sysJobLog);
+    }
+
+    /**
+     * 执行方法,由子类重载
+     *
+     * @param context 工作执行上下文对象
+     * @param sysJob 系统计划任务
+     * @throws Exception 执行过程中的异常
+     */
+    protected abstract void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception;
+}

+ 110 - 0
service-base/src/main/java/com/simuwang/schedule/util/BeanUtils.java

@@ -0,0 +1,110 @@
+package com.simuwang.schedule.util;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Bean 工具类
+ * 
+ * @author ruoyi
+ */
+public class BeanUtils extends org.springframework.beans.BeanUtils
+{
+    /** Bean方法名中属性名开始的下标 */
+    private static final int BEAN_METHOD_PROP_INDEX = 3;
+
+    /** * 匹配getter方法的正则表达式 */
+    private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)");
+
+    /** * 匹配setter方法的正则表达式 */
+    private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)");
+
+    /**
+     * Bean属性复制工具方法。
+     * 
+     * @param dest 目标对象
+     * @param src 源对象
+     */
+    public static void copyBeanProp(Object dest, Object src)
+    {
+        try
+        {
+            copyProperties(src, dest);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 获取对象的setter方法。
+     * 
+     * @param obj 对象
+     * @return 对象的setter方法列表
+     */
+    public static List<Method> getSetterMethods(Object obj)
+    {
+        // setter方法列表
+        List<Method> setterMethods = new ArrayList<Method>();
+
+        // 获取所有方法
+        Method[] methods = obj.getClass().getMethods();
+
+        // 查找setter方法
+
+        for (Method method : methods)
+        {
+            Matcher m = SET_PATTERN.matcher(method.getName());
+            if (m.matches() && (method.getParameterTypes().length == 1))
+            {
+                setterMethods.add(method);
+            }
+        }
+        // 返回setter方法列表
+        return setterMethods;
+    }
+
+    /**
+     * 获取对象的getter方法。
+     * 
+     * @param obj 对象
+     * @return 对象的getter方法列表
+     */
+
+    public static List<Method> getGetterMethods(Object obj)
+    {
+        // getter方法列表
+        List<Method> getterMethods = new ArrayList<Method>();
+        // 获取所有方法
+        Method[] methods = obj.getClass().getMethods();
+        // 查找getter方法
+        for (Method method : methods)
+        {
+            Matcher m = GET_PATTERN.matcher(method.getName());
+            if (m.matches() && (method.getParameterTypes().length == 0))
+            {
+                getterMethods.add(method);
+            }
+        }
+        // 返回getter方法列表
+        return getterMethods;
+    }
+
+    /**
+     * 检查Bean方法名中的属性名是否相等。<br>
+     * 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。
+     * 
+     * @param m1 方法名1
+     * @param m2 方法名2
+     * @return 属性名一样返回true,否则返回false
+     */
+
+    public static boolean isMethodPropEquals(String m1, String m2)
+    {
+        return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX));
+    }
+}

+ 95 - 0
service-base/src/main/java/com/simuwang/schedule/util/CronUtils.java

@@ -0,0 +1,95 @@
+package com.simuwang.schedule.util;
+
+import com.simuwang.base.common.util.DateUtils;
+import org.quartz.CronExpression;
+import org.quartz.TriggerUtils;
+import org.quartz.impl.triggers.CronTriggerImpl;
+
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * cron表达式工具类
+ * 
+ * @author ruoyi
+ *
+ */
+public class CronUtils
+{
+    /**
+     * 返回一个布尔值代表一个给定的Cron表达式的有效性
+     *
+     * @param cronExpression Cron表达式
+     * @return boolean 表达式是否有效
+     */
+    public static boolean isValid(String cronExpression)
+    {
+        return CronExpression.isValidExpression(cronExpression);
+    }
+
+    /**
+     * 返回一个字符串值,表示该消息无效Cron表达式给出有效性
+     *
+     * @param cronExpression Cron表达式
+     * @return String 无效时返回表达式错误描述,如果有效返回null
+     */
+    public static String getInvalidMessage(String cronExpression)
+    {
+        try
+        {
+            new CronExpression(cronExpression);
+            return null;
+        }
+        catch (ParseException pe)
+        {
+            return pe.getMessage();
+        }
+    }
+
+    /**
+     * 返回下一个执行时间根据给定的Cron表达式
+     *
+     * @param cronExpression Cron表达式
+     * @return Date 下次Cron表达式执行时间
+     */
+    public static Date getNextExecution(String cronExpression)
+    {
+        try
+        {
+            CronExpression cron = new CronExpression(cronExpression);
+            return cron.getNextValidTimeAfter(new Date(System.currentTimeMillis()));
+        }
+        catch (ParseException e)
+        {
+            throw new IllegalArgumentException(e.getMessage());
+        }
+    }
+
+    /**
+     * 通过表达式获取近10次的执行时间
+     * 
+     * @param cron 表达式
+     * @return 时间列表
+     */
+    public static List<String> getRecentTriggerTime(String cron)
+    {
+        List<String> list = new ArrayList<String>();
+        try
+        {
+            CronTriggerImpl cronTriggerImpl = new CronTriggerImpl();
+            cronTriggerImpl.setCronExpression(cron);
+            List<Date> dates = TriggerUtils.computeFireTimes(cronTriggerImpl, null, 10);
+            for (Date date : dates)
+            {
+                list.add(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, date));
+            }
+        }
+        catch (ParseException e)
+        {
+            return null;
+        }
+        return list;
+    }
+}

+ 185 - 0
service-base/src/main/java/com/simuwang/schedule/util/JobInvokeUtil.java

@@ -0,0 +1,185 @@
+package com.simuwang.schedule.util;
+
+
+import cn.hutool.extra.spring.SpringUtil;
+import com.simuwang.base.common.util.StringUtil;
+import com.simuwang.schedule.domain.SysJob;
+import org.apache.commons.lang3.StringUtils;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * 任务执行工具
+ *
+ * @author ruoyi
+ */
+public class JobInvokeUtil
+{
+    /**
+     * 执行方法
+     *
+     * @param sysJob 系统任务
+     */
+    public static void invokeMethod(SysJob sysJob) throws Exception
+    {
+        String invokeTarget = sysJob.getInvokeTarget();
+        String beanName = getBeanName(invokeTarget);
+        String methodName = getMethodName(invokeTarget);
+        List<Object[]> methodParams = getMethodParams(invokeTarget);
+
+        if (!isValidClassName(beanName))
+        {
+            Object bean = SpringUtil.getBean(beanName);
+            invokeMethod(bean, methodName, methodParams);
+        }
+        else
+        {
+            Object bean = Class.forName(beanName).getDeclaredConstructor().newInstance();
+            invokeMethod(bean, methodName, methodParams);
+        }
+    }
+
+    /**
+     * 调用任务方法
+     *
+     * @param bean 目标对象
+     * @param methodName 方法名称
+     * @param methodParams 方法参数
+     */
+    private static void invokeMethod(Object bean, String methodName, List<Object[]> methodParams)
+            throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException,
+            InvocationTargetException
+    {
+        if (StringUtil.isNotNull(methodParams) && methodParams.size() > 0)
+        {
+            Method method = bean.getClass().getMethod(methodName, getMethodParamsType(methodParams));
+            method.invoke(bean, getMethodParamsValue(methodParams));
+        }
+        else
+        {
+            Method method = bean.getClass().getMethod(methodName);
+            method.invoke(bean);
+        }
+    }
+
+    /**
+     * 校验是否为为class包名
+     * 
+     * @param invokeTarget 名称
+     * @return true是 false否
+     */
+    public static boolean isValidClassName(String invokeTarget)
+    {
+        return StringUtils.countMatches(invokeTarget, ".") > 1;
+    }
+
+    /**
+     * 获取bean名称
+     * 
+     * @param invokeTarget 目标字符串
+     * @return bean名称
+     */
+    public static String getBeanName(String invokeTarget)
+    {
+        String beanName = StringUtils.substringBefore(invokeTarget, "(");
+        return StringUtils.substringBeforeLast(beanName, ".");
+    }
+
+    /**
+     * 获取bean方法
+     * 
+     * @param invokeTarget 目标字符串
+     * @return method方法
+     */
+    public static String getMethodName(String invokeTarget)
+    {
+        String methodName = StringUtils.substringBefore(invokeTarget, "(");
+        return StringUtils.substringAfterLast(methodName, ".");
+    }
+
+    /**
+     * 获取method方法参数相关列表
+     * 
+     * @param invokeTarget 目标字符串
+     * @return method方法相关参数列表
+     */
+    public static List<Object[]> getMethodParams(String invokeTarget)
+    {
+        String methodStr = StringUtils.substringBetween(invokeTarget, "(", ")");
+        if (StringUtils.isEmpty(methodStr))
+        {
+            return null;
+        }
+        String[] methodParams = methodStr.split(",(?=([^\"']*[\"'][^\"']*[\"'])*[^\"']*$)");
+        List<Object[]> classs = new LinkedList<>();
+        for (int i = 0; i < methodParams.length; i++)
+        {
+            String str = StringUtils.trimToEmpty(methodParams[i]);
+            // String字符串类型,以'或"开头
+            if (StringUtils.startsWithAny(str, "'", "\""))
+            {
+                classs.add(new Object[] { StringUtils.substring(str, 1, str.length() - 1), String.class });
+            }
+            // boolean布尔类型,等于true或者false
+            else if ("true".equalsIgnoreCase(str) || "false".equalsIgnoreCase(str))
+            {
+                classs.add(new Object[] { Boolean.valueOf(str), Boolean.class });
+            }
+            // long长整形,以L结尾
+            else if (StringUtils.endsWith(str, "L"))
+            {
+                classs.add(new Object[] { Long.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Long.class });
+            }
+            // double浮点类型,以D结尾
+            else if (StringUtils.endsWith(str, "D"))
+            {
+                classs.add(new Object[] { Double.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Double.class });
+            }
+            // 其他类型归类为整形
+            else
+            {
+                classs.add(new Object[] { Integer.valueOf(str), Integer.class });
+            }
+        }
+        return classs;
+    }
+
+    /**
+     * 获取参数类型
+     * 
+     * @param methodParams 参数相关列表
+     * @return 参数类型列表
+     */
+    public static Class<?>[] getMethodParamsType(List<Object[]> methodParams)
+    {
+        Class<?>[] classs = new Class<?>[methodParams.size()];
+        int index = 0;
+        for (Object[] os : methodParams)
+        {
+            classs[index] = (Class<?>) os[1];
+            index++;
+        }
+        return classs;
+    }
+
+    /**
+     * 获取参数值
+     * 
+     * @param methodParams 参数相关列表
+     * @return 参数值列表
+     */
+    public static Object[] getMethodParamsValue(List<Object[]> methodParams)
+    {
+        Object[] classs = new Object[methodParams.size()];
+        int index = 0;
+        for (Object[] os : methodParams)
+        {
+            classs[index] = (Object) os[0];
+            index++;
+        }
+        return classs;
+    }
+}

+ 23 - 0
service-base/src/main/java/com/simuwang/schedule/util/QuartzDisallowConcurrentExecution.java

@@ -0,0 +1,23 @@
+package com.simuwang.schedule.util;
+
+import com.simuwang.schedule.domain.SysJob;
+import com.simuwang.schedule.util.AbstractQuartzJob;
+import com.simuwang.schedule.util.JobInvokeUtil;
+import org.quartz.DisallowConcurrentExecution;
+import org.quartz.JobExecutionContext;
+
+/**
+ * 定时任务处理(禁止并发执行)
+ * 
+ * @author ruoyi
+ *
+ */
+@DisallowConcurrentExecution
+public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob
+{
+    @Override
+    protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception
+    {
+        JobInvokeUtil.invokeMethod(sysJob);
+    }
+}

+ 21 - 0
service-base/src/main/java/com/simuwang/schedule/util/QuartzJobExecution.java

@@ -0,0 +1,21 @@
+package com.simuwang.schedule.util;
+
+import com.simuwang.schedule.domain.SysJob;
+import com.simuwang.schedule.util.AbstractQuartzJob;
+import com.simuwang.schedule.util.JobInvokeUtil;
+import org.quartz.JobExecutionContext;
+
+/**
+ * 定时任务处理(允许并发执行)
+ * 
+ * @author ruoyi
+ *
+ */
+public class QuartzJobExecution extends AbstractQuartzJob
+{
+    @Override
+    protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception
+    {
+        JobInvokeUtil.invokeMethod(sysJob);
+    }
+}

+ 108 - 0
service-base/src/main/java/com/simuwang/schedule/util/ScheduleUtils.java

@@ -0,0 +1,108 @@
+package com.simuwang.schedule.util;
+import com.simuwang.base.common.conts.ScheduleConstants;
+import com.simuwang.base.common.util.StringUtil;
+import com.simuwang.schedule.domain.SysJob;
+import com.simuwang.schedule.exception.TaskException;
+import org.quartz.*;
+
+/**
+ * 定时任务工具类
+ * 
+ * @author ruoyi
+ *
+ */
+public class ScheduleUtils
+{
+    /**
+     * 得到quartz任务类
+     *
+     * @param sysJob 执行计划
+     * @return 具体执行任务类
+     */
+    private static Class<? extends Job> getQuartzJobClass(SysJob sysJob)
+    {
+        boolean isConcurrent = "0".equals(sysJob.getConcurrent());
+        return isConcurrent ? QuartzJobExecution.class : QuartzDisallowConcurrentExecution.class;
+    }
+
+    /**
+     * 构建任务触发对象
+     */
+    public static TriggerKey getTriggerKey(Long jobId, String jobGroup)
+    {
+        return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
+    }
+
+    /**
+     * 构建任务键对象
+     */
+    public static JobKey getJobKey(Long jobId, String jobGroup)
+    {
+        return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
+    }
+
+    /**
+     * 创建定时任务
+     */
+    public static void createScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, TaskException
+    {
+        Class<? extends Job> jobClass = getQuartzJobClass(job);
+        // 构建job信息
+        Long jobId = job.getJobId();
+        String jobGroup = job.getJobGroup();
+        JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup)).build();
+
+        // 表达式调度构建器
+        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
+        cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder);
+
+        // 按新的cronExpression表达式构建一个新的trigger
+        CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup))
+                .withSchedule(cronScheduleBuilder).build();
+
+        // 放入参数,运行时的方法可以获取
+        jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job);
+
+        // 判断是否存在
+        if (scheduler.checkExists(getJobKey(jobId, jobGroup)))
+        {
+            // 防止创建时存在数据问题 先移除,然后在执行创建操作
+            scheduler.deleteJob(getJobKey(jobId, jobGroup));
+        }
+
+        // 判断任务是否过期
+        if (StringUtil.isNotNull(CronUtils.getNextExecution(job.getCronExpression())))
+        {
+            // 执行调度任务
+            scheduler.scheduleJob(jobDetail, trigger);
+        }
+
+        // 暂停任务
+        if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue()))
+        {
+            scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
+        }
+    }
+
+    /**
+     * 设置定时任务策略
+     */
+    public static CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob job, CronScheduleBuilder cb)
+            throws TaskException
+    {
+        switch (job.getMisfirePolicy())
+        {
+            case ScheduleConstants.MISFIRE_DEFAULT:
+                return cb;
+            case ScheduleConstants.MISFIRE_IGNORE_MISFIRES:
+                return cb.withMisfireHandlingInstructionIgnoreMisfires();
+            case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED:
+                return cb.withMisfireHandlingInstructionFireAndProceed();
+            case ScheduleConstants.MISFIRE_DO_NOTHING:
+                return cb.withMisfireHandlingInstructionDoNothing();
+            default:
+                throw new TaskException("The task misfire policy '" + job.getMisfirePolicy()
+                        + "' cannot be used in cron schedule tasks", TaskException.Code.CONFIG_ERROR);
+        }
+    }
+}

+ 8 - 0
service-base/src/main/resources/mapper/MailBoxInfoMapper.xml

@@ -47,5 +47,13 @@
             and email=#{email}
         </if>
     </select>
+    <sql id="selectConfigVo">
+        select id, user_id, type, email, password, protocol, server, port, cron, open_status,description,isvalid,creatorid,createtime,updaterid,updatetime
+        from PPW_EMAIL.mailbox_info
+    </sql>
+    <select id="checkEmailUnique" resultType="com.simuwang.base.pojo.dos.MailboxInfoDO">
+        <include refid="selectConfigVo"/>
+        where email = #{email} limit 1
+    </select>
 
 </mapper>

+ 3 - 0
service-manage/src/main/java/com/simuwang/manage/api/email/EmailConfigController.java

@@ -48,6 +48,9 @@ public class EmailConfigController extends BaseController {
     @RequestMapping("save-email-config")
     public AjaxResult saveEmailConfig(@RequestBody MailboxInfoVO mailboxInfoVO){
         try{
+            if(mailboxInfoVO.getId() == null && emailConfigService.checkEmailUnique(mailboxInfoVO.getEmail())){
+                return AjaxResult.error("邮箱地址已存在,无需添加");
+            }
             emailConfigService.saveEmailConfig(mailboxInfoVO);
         }catch (Exception e){
             logger.error(e.getMessage(),e);

+ 2 - 0
service-manage/src/main/java/com/simuwang/manage/service/EmailConfigService.java

@@ -19,4 +19,6 @@ public interface EmailConfigService {
     String connectTest(MailboxInfoVO mailboxInfoVO);
 
     void deleteEmailConfig(String ids);
+
+    boolean checkEmailUnique(String email);
 }

+ 20 - 0
service-manage/src/main/java/com/simuwang/manage/service/impl/EmailConfigServiceImpl.java

@@ -1,8 +1,10 @@
 package com.simuwang.manage.service.impl;
 
+import com.simuwang.base.common.conts.UserConstants;
 import com.simuwang.base.common.enums.EmailCron;
 import com.simuwang.base.common.enums.ResultCode;
 import com.simuwang.base.common.util.EmailUtil;
+import com.simuwang.base.common.util.StringUtil;
 import com.simuwang.base.mapper.MailboxInfoMapper;
 import com.simuwang.base.pojo.dos.MailboxInfoDO;
 import com.simuwang.base.pojo.dto.MailboxInfoDTO;
@@ -11,8 +13,10 @@ import com.simuwang.base.pojo.vo.MailboxInfoVO;
 import com.simuwang.manage.service.EmailConfigService;
 import jakarta.mail.MessagingException;
 import jakarta.mail.Store;
+import org.quartz.Scheduler;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.util.Date;
 import java.util.List;
@@ -27,6 +31,9 @@ import java.util.List;
 public class EmailConfigServiceImpl implements EmailConfigService {
     @Autowired
     private MailboxInfoMapper emailConfigMapper;
+
+    @Autowired
+    private Scheduler scheduler;
     @Override
     public List<MailboxInfoTableVO> searchEmailConfigList(String email) {
         List<MailboxInfoTableVO> mailboxInfoTableVOS = emailConfigMapper.searchEmailConfigList(email);
@@ -37,6 +44,7 @@ public class EmailConfigServiceImpl implements EmailConfigService {
     }
 
     @Override
+    @Transactional
     public void saveEmailConfig(MailboxInfoVO mailboxInfoVO) {
         MailboxInfoDO mailboxInfoDO = toMailboxInfoDO(mailboxInfoVO);
         if(mailboxInfoVO.getId() == null){
@@ -45,6 +53,8 @@ public class EmailConfigServiceImpl implements EmailConfigService {
         }else{
             emailConfigMapper.updateById(mailboxInfoDO);
         }
+        //添加定时任务
+
     }
 
     @Override
@@ -71,6 +81,16 @@ public class EmailConfigServiceImpl implements EmailConfigService {
         emailConfigMapper.deleteEmailConfigByIds(ids.split(","));
     }
 
+    @Override
+    public boolean checkEmailUnique(String email) {
+        MailboxInfoDO mailboxInfoDO = emailConfigMapper.checkEmailUnique(email);
+        if (StringUtil.isNotNull(mailboxInfoDO))
+        {
+            return UserConstants.NOT_UNIQUE;
+        }
+        return UserConstants.UNIQUE;
+    }
+
     private MailboxInfoDO toMailboxInfoDO(MailboxInfoVO mailboxInfoVO) {
         MailboxInfoDO mailboxInfoDO = new MailboxInfoDO();
         mailboxInfoDO.setIsvalid(1);