From b7150b6b293e9413a26f730148e9e08a2ed4d84d Mon Sep 17 00:00:00 2001 From: wxy <14293288+zysysys@user.noreply.gitee.com> Date: Fri, 24 May 2024 14:56:21 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/constant/ScheduleConstants.java | 50 +++++++ .../core/constant/SecurityConstants.java | 49 ++++++ .../core/context/SecurityContextHolder.java | 98 ++++++++++++ .../com/jing/job/config/ScheduleConfig.java | 57 +++++++ .../main/java/com/jing/job/task/RyTask.java | 28 ++++ .../java/com/jing/job/util/ScheduleUtils.java | 141 ++++++++++++++++++ jing-ui/src/assets/icons/svg/search.svg | 1 + jing-ui/src/components/Crontab/second.vue | 117 +++++++++++++++ .../layout/components/TagsView/ScrollPane.vue | 94 ++++++++++++ jing-ui/src/utils/scroll-to.js | 58 +++++++ 10 files changed, 693 insertions(+) create mode 100644 jing-common/jing-common-core/src/main/java/com/jing/common/core/constant/ScheduleConstants.java create mode 100644 jing-common/jing-common-core/src/main/java/com/jing/common/core/constant/SecurityConstants.java create mode 100644 jing-common/jing-common-core/src/main/java/com/jing/common/core/context/SecurityContextHolder.java create mode 100644 jing-modules/jing-job/src/main/java/com/jing/job/config/ScheduleConfig.java create mode 100644 jing-modules/jing-job/src/main/java/com/jing/job/task/RyTask.java create mode 100644 jing-modules/jing-job/src/main/java/com/jing/job/util/ScheduleUtils.java create mode 100644 jing-ui/src/assets/icons/svg/search.svg create mode 100644 jing-ui/src/components/Crontab/second.vue create mode 100644 jing-ui/src/layout/components/TagsView/ScrollPane.vue create mode 100644 jing-ui/src/utils/scroll-to.js diff --git a/jing-common/jing-common-core/src/main/java/com/jing/common/core/constant/ScheduleConstants.java b/jing-common/jing-common-core/src/main/java/com/jing/common/core/constant/ScheduleConstants.java new file mode 100644 index 0000000..3506f7a --- /dev/null +++ b/jing-common/jing-common-core/src/main/java/com/jing/common/core/constant/ScheduleConstants.java @@ -0,0 +1,50 @@ +package com.jing.common.core.constant; + +/** + * 任务调度通用常量 + * + * @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; + } + } +} diff --git a/jing-common/jing-common-core/src/main/java/com/jing/common/core/constant/SecurityConstants.java b/jing-common/jing-common-core/src/main/java/com/jing/common/core/constant/SecurityConstants.java new file mode 100644 index 0000000..8ba0afc --- /dev/null +++ b/jing-common/jing-common-core/src/main/java/com/jing/common/core/constant/SecurityConstants.java @@ -0,0 +1,49 @@ +package com.jing.common.core.constant; + +/** + * 权限相关通用常量 + * + * @author ruoyi + */ +public class SecurityConstants +{ + /** + * 用户ID字段 + */ + public static final String DETAILS_USER_ID = "user_id"; + + /** + * 用户名字段 + */ + public static final String DETAILS_USERNAME = "username"; + + /** + * 授权信息字段 + */ + public static final String AUTHORIZATION_HEADER = "authorization"; + + /** + * 请求来源 + */ + public static final String FROM_SOURCE = "from-source"; + + /** + * 内部请求 + */ + public static final String INNER = "inner"; + + /** + * 用户标识 + */ + public static final String USER_KEY = "user_key"; + + /** + * 登录用户 + */ + public static final String LOGIN_USER = "login_user"; + + /** + * 角色权限 + */ + public static final String ROLE_PERMISSION = "role_permission"; +} diff --git a/jing-common/jing-common-core/src/main/java/com/jing/common/core/context/SecurityContextHolder.java b/jing-common/jing-common-core/src/main/java/com/jing/common/core/context/SecurityContextHolder.java new file mode 100644 index 0000000..84f0382 --- /dev/null +++ b/jing-common/jing-common-core/src/main/java/com/jing/common/core/context/SecurityContextHolder.java @@ -0,0 +1,98 @@ +package com.jing.common.core.context; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import com.alibaba.ttl.TransmittableThreadLocal; +import com.jing.common.core.constant.SecurityConstants; +import com.jing.common.core.text.Convert; +import com.jing.common.core.utils.StringUtils; + +/** + * 获取当前线程变量中的 用户id、用户名称、Token等信息 + * 注意: 必须在网关通过请求头的方法传入,同时在HeaderInterceptor拦截器设置值。 否则这里无法获取 + * + * @author ruoyi + */ +public class SecurityContextHolder +{ + private static final TransmittableThreadLocal> THREAD_LOCAL = new TransmittableThreadLocal<>(); + + public static void set(String key, Object value) + { + Map map = getLocalMap(); + map.put(key, value == null ? StringUtils.EMPTY : value); + } + + public static String get(String key) + { + Map map = getLocalMap(); + return Convert.toStr(map.getOrDefault(key, StringUtils.EMPTY)); + } + + public static T get(String key, Class clazz) + { + Map map = getLocalMap(); + return StringUtils.cast(map.getOrDefault(key, null)); + } + + public static Map getLocalMap() + { + Map map = THREAD_LOCAL.get(); + if (map == null) + { + map = new ConcurrentHashMap(); + THREAD_LOCAL.set(map); + } + return map; + } + + public static void setLocalMap(Map threadLocalMap) + { + THREAD_LOCAL.set(threadLocalMap); + } + + public static Long getUserId() + { + return Convert.toLong(get(SecurityConstants.DETAILS_USER_ID), 0L); + } + + public static void setUserId(String account) + { + set(SecurityConstants.DETAILS_USER_ID, account); + } + + public static String getUserName() + { + return get(SecurityConstants.DETAILS_USERNAME); + } + + public static void setUserName(String username) + { + set(SecurityConstants.DETAILS_USERNAME, username); + } + + public static String getUserKey() + { + return get(SecurityConstants.USER_KEY); + } + + public static void setUserKey(String userKey) + { + set(SecurityConstants.USER_KEY, userKey); + } + + public static String getPermission() + { + return get(SecurityConstants.ROLE_PERMISSION); + } + + public static void setPermission(String permissions) + { + set(SecurityConstants.ROLE_PERMISSION, permissions); + } + + public static void remove() + { + THREAD_LOCAL.remove(); + } +} diff --git a/jing-modules/jing-job/src/main/java/com/jing/job/config/ScheduleConfig.java b/jing-modules/jing-job/src/main/java/com/jing/job/config/ScheduleConfig.java new file mode 100644 index 0000000..e558cc2 --- /dev/null +++ b/jing-modules/jing-job/src/main/java/com/jing/job/config/ScheduleConfig.java @@ -0,0 +1,57 @@ +//package com.jing.job.config; +// +//import java.util.Properties; +//import javax.sql.DataSource; +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.scheduling.quartz.SchedulerFactoryBean; +// +///** +// * 定时任务配置(单机部署建议删除此类和qrtz数据库表,默认走内存会最高效) +// * +// * @author ruoyi +// */ +//@Configuration +//public class ScheduleConfig +//{ +// @Bean +// public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) +// { +// SchedulerFactoryBean factory = new SchedulerFactoryBean(); +// factory.setDataSource(dataSource); +// +// // quartz参数 +// Properties prop = new Properties(); +// prop.put("org.quartz.scheduler.instanceName", "RuoyiScheduler"); +// prop.put("org.quartz.scheduler.instanceId", "AUTO"); +// // 线程池配置 +// prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool"); +// prop.put("org.quartz.threadPool.threadCount", "20"); +// prop.put("org.quartz.threadPool.threadPriority", "5"); +// // JobStore配置 +// prop.put("org.quartz.jobStore.class", "org.springframework.scheduling.quartz.LocalDataSourceJobStore"); +// // 集群配置 +// prop.put("org.quartz.jobStore.isClustered", "true"); +// prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000"); +// prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "10"); +// prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true"); +// +// // sqlserver 启用 +// // prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?"); +// prop.put("org.quartz.jobStore.misfireThreshold", "12000"); +// prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_"); +// factory.setQuartzProperties(prop); +// +// factory.setSchedulerName("RuoyiScheduler"); +// // 延时启动 +// factory.setStartupDelay(1); +// factory.setApplicationContextSchedulerContextKey("applicationContextKey"); +// // 可选,QuartzScheduler +// // 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 +// factory.setOverwriteExistingJobs(true); +// // 设置自动启动,默认为true +// factory.setAutoStartup(true); +// +// return factory; +// } +//} diff --git a/jing-modules/jing-job/src/main/java/com/jing/job/task/RyTask.java b/jing-modules/jing-job/src/main/java/com/jing/job/task/RyTask.java new file mode 100644 index 0000000..9792ca0 --- /dev/null +++ b/jing-modules/jing-job/src/main/java/com/jing/job/task/RyTask.java @@ -0,0 +1,28 @@ +package com.jing.job.task; + +import org.springframework.stereotype.Component; +import com.jing.common.core.utils.StringUtils; + +/** + * 定时任务调度测试 + * + * @author ruoyi + */ +@Component("ryTask") +public class RyTask +{ + public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i) + { + System.out.println(StringUtils.format("执行多参方法: 字符串类型{},布尔类型{},长整型{},浮点型{},整形{}", s, b, l, d, i)); + } + + public void ryParams(String params) + { + System.out.println("执行有参方法:" + params); + } + + public void ryNoParams() + { + System.out.println("执行无参方法"); + } +} diff --git a/jing-modules/jing-job/src/main/java/com/jing/job/util/ScheduleUtils.java b/jing-modules/jing-job/src/main/java/com/jing/job/util/ScheduleUtils.java new file mode 100644 index 0000000..97ba6a2 --- /dev/null +++ b/jing-modules/jing-job/src/main/java/com/jing/job/util/ScheduleUtils.java @@ -0,0 +1,141 @@ +package com.jing.job.util; + +import org.quartz.CronScheduleBuilder; +import org.quartz.CronTrigger; +import org.quartz.Job; +import org.quartz.JobBuilder; +import org.quartz.JobDetail; +import org.quartz.JobKey; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.TriggerBuilder; +import org.quartz.TriggerKey; +import com.jing.common.core.constant.Constants; +import com.jing.common.core.constant.ScheduleConstants; +import com.jing.common.core.exception.job.TaskException; +import com.jing.common.core.exception.job.TaskException.Code; +import com.jing.common.core.utils.SpringUtils; +import com.jing.common.core.utils.StringUtils; +import com.jing.job.domain.SysJob; + +/** + * 定时任务工具类 + * + * @author ruoyi + * + */ +public class ScheduleUtils +{ + /** + * 得到quartz任务类 + * + * @param sysJob 执行计划 + * @return 具体执行任务类 + */ + private static Class 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 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 (StringUtils.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", Code.CONFIG_ERROR); + } + } + + /** + * 检查包名是否为白名单配置 + * + * @param invokeTarget 目标字符串 + * @return 结果 + */ + public static boolean whiteList(String invokeTarget) + { + String packageName = StringUtils.substringBefore(invokeTarget, "("); + int count = StringUtils.countMatches(packageName, "."); + if (count > 1) + { + return StringUtils.containsAnyIgnoreCase(invokeTarget, Constants.JOB_WHITELIST_STR); + } + Object obj = SpringUtils.getBean(StringUtils.split(invokeTarget, ".")[0]); + String beanPackageName = obj.getClass().getPackage().getName(); + return StringUtils.containsAnyIgnoreCase(beanPackageName, Constants.JOB_WHITELIST_STR) + && !StringUtils.containsAnyIgnoreCase(beanPackageName, Constants.JOB_ERROR_STR); + } +} \ No newline at end of file diff --git a/jing-ui/src/assets/icons/svg/search.svg b/jing-ui/src/assets/icons/svg/search.svg new file mode 100644 index 0000000..84233dd --- /dev/null +++ b/jing-ui/src/assets/icons/svg/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/jing-ui/src/components/Crontab/second.vue b/jing-ui/src/components/Crontab/second.vue new file mode 100644 index 0000000..e7b7761 --- /dev/null +++ b/jing-ui/src/components/Crontab/second.vue @@ -0,0 +1,117 @@ + + + diff --git a/jing-ui/src/layout/components/TagsView/ScrollPane.vue b/jing-ui/src/layout/components/TagsView/ScrollPane.vue new file mode 100644 index 0000000..bb753a1 --- /dev/null +++ b/jing-ui/src/layout/components/TagsView/ScrollPane.vue @@ -0,0 +1,94 @@ + + + + + diff --git a/jing-ui/src/utils/scroll-to.js b/jing-ui/src/utils/scroll-to.js new file mode 100644 index 0000000..c5d8e04 --- /dev/null +++ b/jing-ui/src/utils/scroll-to.js @@ -0,0 +1,58 @@ +Math.easeInOutQuad = function(t, b, c, d) { + t /= d / 2 + if (t < 1) { + return c / 2 * t * t + b + } + t-- + return -c / 2 * (t * (t - 2) - 1) + b +} + +// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts +var requestAnimFrame = (function() { + return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) } +})() + +/** + * Because it's so fucking difficult to detect the scrolling element, just move them all + * @param {number} amount + */ +function move(amount) { + document.documentElement.scrollTop = amount + document.body.parentNode.scrollTop = amount + document.body.scrollTop = amount +} + +function position() { + return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop +} + +/** + * @param {number} to + * @param {number} duration + * @param {Function} callback + */ +export function scrollTo(to, duration, callback) { + const start = position() + const change = to - start + const increment = 20 + let currentTime = 0 + duration = (typeof (duration) === 'undefined') ? 500 : duration + var animateScroll = function() { + // increment the time + currentTime += increment + // find the value with the quadratic in-out easing function + var val = Math.easeInOutQuad(currentTime, start, change, duration) + // move the document.body + move(val) + // do the animation unless its over + if (currentTime < duration) { + requestAnimFrame(animateScroll) + } else { + if (callback && typeof (callback) === 'function') { + // the animation is done so lets callback + callback() + } + } + } + animateScroll() +}