动态定时任务-支持选择执行类和自定义参数(附前后端完整代码)

前言:

传统的定时任务,要么是使用@Scheduled在程序中写死的定时策略,要么是使用
Quartz或者xxl-job定时任务框架,就很重。
本文介绍的定时方案采用hutool工具包的CronUtil配合反射实现,支持选择定时任务类,自定义参数,主打轻量、灵活。
此方案也是借鉴的小诺开源框架,测试页面套用的若依的定时任务页面,在那基础上稍加改动就好
在这里插入图片描述

1,项目依赖

    <!-- hutool工具类-->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.8.12</version>
    </dependency>

2,定义一个定时任务类

@Data
public class SysJob extends BaseEntity
{
    private static final long serialVersionUID = 1L;

    /** 任务ID */
    @Excel(name = "任务序号", cellType = ColumnType.NUMERIC)
    private Long jobId;

    /** 任务名称 */
    @Excel(name = "任务名称")
    private String jobName;

    /** 任务组名 */
    @Excel(name = "任务组名")
    private String jobGroup;

    /** 调用目标字符串 */
    @Excel(name = "调用目标字符串")
    private String invokeTarget;

    /** cron执行表达式 */
    @Excel(name = "执行表达式 ")
    private String cronExpression;

    /** cron计划策略 */
    @Excel(name = "计划策略 ", readConverterExp = "0=默认,1=立即触发执行,2=触发一次执行,3=不触发立即执行")
    private String misfirePolicy;

    /** 是否并发执行(0允许 1禁止) */
    @Excel(name = "并发执行", readConverterExp = "0=允许,1=禁止")
    private String concurrent;

    /** 任务状态(0正常 1暂停) */
    @Excel(name = "任务状态", readConverterExp = "0=正常,1=暂停")
    private String status;
    
    /** 任务参数 */
    @Excel(name = "任务参数")
    private String remark;
}

3,编写定时任务

先写一个接口,所有定时任务都实现这个接口,方便我们遍历所有可用的定时任务类

public interface CommonTimerTaskRunner {

    /**
     * 任务执行的具体内容
     *
     * @author xuyuxiang
     * @date 2022/8/15 16:09
     **/
    void action(String param);
}

然后编写具体的定时任务,每个定时任务单独写一个类,不要混在一起,方便管理

@Slf4j
@Component
public class DevJobTimerTaskRunner implements CommonTimerTaskRunner {

    private static final String INITPARAM = "{\"KEY1\":200,\"第二个参数\":\"aaaaa\"}";
    private int n = 1;

    @Override
    public void action(String param) {
        JSONObject jsonObject = JSONUtil.parseObj(param);
        log.info("我是一个定时任务,正在在被执行第{}次,参数1:{},参数2:{}", n, jsonObject.getInt("KEY1"), jsonObject.getStr("第二个参数"));
        n = n + 1;
    }
}

这里的INITPARAM 属性是一个参数示例值,每个定时任务的可选参数可能都不一样,json结构不一致,这个参数会在添加和修改定时任务时同下卡框一起带出来

4,定时任务的增删改查实现

后台也是在若依框架基础上测试的,使用的mybatis框架,这里就忽略dao和mapper细节,直接贴servce

SysJobServiceImpl

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.cron.CronUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.ruoyi.common.constant.ScheduleConstants;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.quartz.domain.SelectOptionVO;
import com.ruoyi.quartz.domain.SysJob;
import com.ruoyi.quartz.mapper.SysJobMapper;
import com.ruoyi.quartz.service.CommonTimerTaskRunner;
import com.ruoyi.quartz.service.ISysJobService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.support.CronExpression;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 定时任务调度信息 服务层
 *
 * @author ruoyi
 */
@Service
public class SysJobServiceImpl implements ISysJobService {

    @Autowired
    private SysJobMapper jobMapper;

    /**
     * 获取quartz调度器的计划任务列表
     *
     * @param job 调度信息
     * @return
     */
    @Override
    public List<SysJob> selectJobList(SysJob job) {
        return jobMapper.selectJobList(job);
    }

    /**
     * 通过调度任务ID查询调度信息
     *
     * @param jobId 调度任务ID
     * @return 调度任务对象信息
     */
    @Override
    public SysJob selectJobById(Long jobId) {
        return jobMapper.selectJobById(jobId);
    }

    /**
     * 暂停任务
     *
     * @param job 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int pauseJob(SysJob job) {
        SysJob oldjob = jobMapper.selectJobById(job.getJobId());
        if (ScheduleConstants.Status.PAUSE.getValue().equals(oldjob.getStatus())) {
            throw new BaseException("该任务已处于暂停状态");
        }
        CronUtil.remove(job.getJobId() + "");
        job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
        return jobMapper.updateJob(job);
    }

    /**
     * 启动定时任务
     *
     * @param job 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int resumeJob(SysJob job) {
        Long jobId = job.getJobId();
        SysJob oldjob = jobMapper.selectJobById(jobId);
        if (ScheduleConstants.Status.NORMAL.getValue().equals(oldjob.getStatus()) && ScheduleConstants.MISFIRE_IGNORE_MISFIRES.equals(oldjob.getMisfirePolicy())) {
            throw new BaseException("该定时任务已处于运行状态,请勿重复执行");
        }
        //注册定时任务
        CronUtil.schedule(job.getJobId() + "", job.getCronExpression(), () -> {
            try {
                // 运行定时任务
                ((CommonTimerTaskRunner) SpringUtil.getBean(Class.forName(job.getInvokeTarget()))).action(job.getRemark());
            } catch (ClassNotFoundException e) {
                throw new BaseException("定时任务找不到对应的类,名称为:" + job.getInvokeTarget());
            }
        });
        job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
        return jobMapper.updateJob(job);
    }

    /**
     * 删除任务后,所对应的trigger也将被删除
     *
     * @param job 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteJob(SysJob job) {
        CronUtil.remove(job.getJobId() + "");
        return jobMapper.deleteJobById(job.getJobId());
    }

    /**
     * 批量删除调度信息
     *
     * @param jobIds 需要删除的任务ID
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteJobByIds(Long[] jobIds) {
        for (Long jobId : jobIds) {
            SysJob job = jobMapper.selectJobById(jobId);
            deleteJob(job);
        }
    }

    /**
     * 任务调度状态修改
     *
     * @param job 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int changeStatus(SysJob job) {
        int rows = 0;
        String status = job.getStatus();
        if (ScheduleConstants.Status.NORMAL.getValue().equals(status)) {
            if (ScheduleConstants.MISFIRE_IGNORE_MISFIRES.equals(job.getMisfirePolicy())) {
                rows = resumeJob(job);
            }
        } else if (ScheduleConstants.Status.PAUSE.getValue().equals(status)) {
            if (ScheduleConstants.MISFIRE_IGNORE_MISFIRES.equals(job.getMisfirePolicy())) {
                rows = pauseJob(job);
            }
        }
        jobMapper.updateJob(job);
        return rows;
    }

    /**
     * 立即运行任务
     *
     * @param job 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean runNow(SysJob job) {
        SysJob sysJob = jobMapper.selectJobById(job.getJobId());
        try {
            // 直接运行一次
            ((CommonTimerTaskRunner) SpringUtil.getBean(Class.forName(sysJob.getInvokeTarget()))).action(sysJob.getRemark());
        } catch (ClassNotFoundException e) {
            throw new BaseException("定时任务找不到对应的类,名称为:" + sysJob.getInvokeTarget());
        }
        return true;
    }

    /**
     * 新增任务
     *
     * @param job 调度信息 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertJob(SysJob job) {
        checkParam(job);
        job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
        jobMapper.insertJob(job);
        //立即启动定时任务
        if (ScheduleConstants.Status.NORMAL.getValue().equals(job.getStatus())) {
            if (ScheduleConstants.MISFIRE_IGNORE_MISFIRES.equals(job.getMisfirePolicy())) {
                //开启定时任务
                resumeJob(job);
            } else if (ScheduleConstants.MISFIRE_FIRE_AND_PROCEED.equals(job.getMisfirePolicy())) {
                try {
                    // 直接运行一次
                    ((CommonTimerTaskRunner) SpringUtil.getBean(Class.forName(job.getInvokeTarget()))).action(job.getRemark());
                } catch (ClassNotFoundException e) {
                    throw new BaseException("定时任务找不到对应的类,名称为:" + job.getInvokeTarget());
                }
            }
        }
        return 1;
    }

    /**
     * 任务参数校验
     *
     * @param job
     */
    void checkParam(SysJob job) {
        //校验表达式
        if (!CronExpression.isValidExpression(job.getCronExpression())) {
            throw new BaseException("cron表达式:" + job.getCronExpression() + "格式不正确");
        }
        //校验定时任务类
        try {
            Class<?> actionClass = Class.forName(job.getInvokeTarget());
            if (!CommonTimerTaskRunner.class.isAssignableFrom(actionClass)) {
                throw new BaseException("定时任务对应的类:" + job.getInvokeTarget() + "不符合要求");
            }
        } catch (ClassNotFoundException e) {
            throw new BaseException("定时任务找不到对应的类,名称为:" + job.getInvokeTarget());
        }
        SysJob sysUser2 = new SysJob();
        sysUser2.setInvokeTarget(job.getInvokeTarget());
        sysUser2.setCronExpression(job.getCronExpression());
        List<SysJob> jobList = jobMapper.selectJobList(sysUser2);
        if (!CollectionUtils.isEmpty(jobList)) {
            throw new BaseException("存在重复执行的定时任务,名称为:" + job.getJobName());
        }
    }

    /**
     * 更新任务的时间表达式
     *
     * @param job 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateJob(SysJob job) {
        //校验表达式
        if (!CronExpression.isValidExpression(job.getCronExpression())) {
            throw new BaseException("cron表达式:" + job.getCronExpression() + "格式不正确");
        }
        //校验定时任务类
        try {
            Class<?> actionClass = Class.forName(job.getInvokeTarget());
            if (!CommonTimerTaskRunner.class.isAssignableFrom(actionClass)) {
                throw new BaseException("定时任务对应的类:" + job.getInvokeTarget() + "不符合要求");
            }
        } catch (ClassNotFoundException e) {
            throw new BaseException("定时任务找不到对应的类,名称为:" + job.getInvokeTarget());
        }
        //立即启动定时任务
        SysJob oldjob = jobMapper.selectJobById(job.getJobId());
        //修改状态
        if (!oldjob.getStatus().equals(job.getStatus())) {
            if (ScheduleConstants.Status.NORMAL.getValue().equals(job.getStatus())) {
                //改为正常状态,检查策略
                if (ScheduleConstants.MISFIRE_IGNORE_MISFIRES.equals(job.getMisfirePolicy())) {
                    resumeJob(job);
                } else if (ScheduleConstants.MISFIRE_FIRE_AND_PROCEED.equals(job.getMisfirePolicy())) {
                    try {
                        //执行一次
                        ((CommonTimerTaskRunner) SpringUtil.getBean(Class.forName(job.getInvokeTarget()))).action(job.getRemark());
                    } catch (ClassNotFoundException e) {
                        throw new BaseException("定时任务找不到对应的类,名称为:" + job.getInvokeTarget());
                    }
                }
            } else {
                //改为禁用状态,并且修改前有任务时在运行时,停止任务
                if (ScheduleConstants.MISFIRE_IGNORE_MISFIRES.equals(oldjob.getMisfirePolicy())) {
                    CronUtil.remove(job.getJobId() + "");
                }
            }
        }
        if (ScheduleConstants.Status.NORMAL.getValue().equals(job.getStatus())) {
            if (ScheduleConstants.MISFIRE_IGNORE_MISFIRES.equals(job.getMisfirePolicy())) {
                //开启定时任务
                resumeJob(job);
            } else if (ScheduleConstants.MISFIRE_FIRE_AND_PROCEED.equals(job.getMisfirePolicy())) {
                try {
                    // 直接运行一次
                    ((CommonTimerTaskRunner) SpringUtil.getBean(Class.forName(job.getInvokeTarget()))).action(job.getRemark());
                } catch (ClassNotFoundException e) {
                    throw new BaseException("定时任务找不到对应的类,名称为:" + job.getInvokeTarget());
                }
            }
        }
        return jobMapper.updateJob(job);
    }

    /**
     * 查询所有实现了CommonTimerTaskRunner 的类名
     *
     * @return
     */
    @Override
    public List<SelectOptionVO> getActionClass() {
        Map<String, CommonTimerTaskRunner> commonTimerTaskRunnerMap = SpringUtil.getBeansOfType(CommonTimerTaskRunner.class);
        if (ObjectUtil.isNotEmpty(commonTimerTaskRunnerMap)) {
            Collection<CommonTimerTaskRunner> values = commonTimerTaskRunnerMap.values();
            return values.stream().map(commonTimerTaskRunner -> {
                String className = commonTimerTaskRunner.getClass().getName();
                String url = null;
                try {
                    Field urlField = commonTimerTaskRunner.getClass().getDeclaredField("INITPARAM");
                    urlField.setAccessible(true);
                    url = (String) urlField.get(commonTimerTaskRunner);
                } catch (NoSuchFieldException | IllegalAccessException e) {
                    e.printStackTrace();
                }
                SelectOptionVO  selectOptionVO = new SelectOptionVO();
                selectOptionVO.setName(className);
                selectOptionVO.setValue(url);
                return selectOptionVO;
            }).collect(Collectors.toList());
        } else {
            return CollectionUtil.newArrayList();
        }
    }
}

SelectOptionVO 是一个对应select组件的VO,就name和value两个属性,其他几个是枚举类,看job实体备注就清楚了

5,增删改查接口

AjaxResult 是一个通用返回VO,包含code,message,data这些通用属性

/**
 * 调度任务信息操作处理
 *
 * @author ruoyi
 */
@RestController
@RequestMapping("/monitor/job")
public class SysJobController extends BaseController {
    @Autowired
    private ISysJobService jobService;

    /**
     * 查询定时任务列表
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:list')")
    @GetMapping("/list")
    public TableDataInfo list(SysJob sysJob) {
        startPage();
        List<SysJob> list = jobService.selectJobList(sysJob);
        return getDataTable(list);
    }

    /**
     * 导出定时任务列表
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:export')")
    @Log(title = "定时任务", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, SysJob sysJob) {
        List<SysJob> list = jobService.selectJobList(sysJob);
        ExcelUtil<SysJob> util = new ExcelUtil<SysJob>(SysJob.class);
        util.exportExcel(response, list, "定时任务");
    }

    /**
     * 获取定时任务详细信息
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:query')")
    @GetMapping(value = "/{jobId}")
    public AjaxResult getInfo(@PathVariable("jobId") Long jobId) {
        return success(jobService.selectJobById(jobId));
    }

    /**
     * 新增定时任务
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:add')")
    @Log(title = "定时任务", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@RequestBody SysJob job) throws TaskException {
        job.setCreateBy(getUsername());
        return toAjax(jobService.insertJob(job));
    }

    /**
     * 修改定时任务
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:edit')")
    @Log(title = "定时任务", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@RequestBody SysJob job) throws TaskException {
        job.setUpdateBy(getUsername());
        return toAjax(jobService.updateJob(job));
    }

    /**
     * 定时任务状态修改
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')")
    @Log(title = "定时任务", businessType = BusinessType.UPDATE)
    @PutMapping("/changeStatus")
    public AjaxResult changeStatus(@RequestBody SysJob job) {
        SysJob newJob = jobService.selectJobById(job.getJobId());
        if (newJob.getStatus().equals(job.getStatus())) {
            return toAjax(1);
        }
        newJob.setStatus(job.getStatus());
        return toAjax(jobService.changeStatus(newJob));
    }

    /**
     * 定时任务立即执行一次
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')")
    @Log(title = "定时任务", businessType = BusinessType.UPDATE)
    @PutMapping("/run")
    public AjaxResult run(@RequestBody SysJob job) {
        boolean result = jobService.runNow(job);
        return result ? success() : error("任务不存在或已过期!");
    }

    /**
     * 删除定时任务
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
    @Log(title = "定时任务", businessType = BusinessType.DELETE)
    @DeleteMapping("/{jobIds}")
    public AjaxResult remove(@PathVariable Long[] jobIds) throws TaskException {
        jobService.deleteJobByIds(jobIds);
        return success();
    }

    /**
     * 删除定时任务
     */
    @GetMapping("/getJobclass")
    public AjaxResult getJobclass() {
        return success(jobService.getActionClass());
    }
}

6,vue页面

若依原本使用的quartz框架,有一个并发选项,我这里完全不用到,只是没清理,其他的都有注释自己看吧

<template>
  <div class="app-container">
    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
      <el-form-item label="任务名称" prop="jobName">
        <el-input
          v-model="queryParams.jobName"
          placeholder="请输入任务名称"
          clearable
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item label="任务组名" prop="jobGroup">
        <el-select v-model="queryParams.jobGroup" placeholder="请选择任务组名" clearable>
          <el-option
            v-for="dict in dict.type.sys_job_group"
            :key="dict.value"
            :label="dict.label"
            :value="dict.value"
          />
        </el-select>
      </el-form-item>
      <el-form-item label="任务状态" prop="status">
        <el-select v-model="queryParams.status" placeholder="请选择任务状态" clearable>
          <el-option
            v-for="dict in dict.type.sys_job_status"
            :key="dict.value"
            :label="dict.label"
            :value="dict.value"
          />
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>

    <el-row :gutter="10" class="mb8">
      <el-col :span="1.5">
        <el-button
          type="primary"
          plain
          icon="el-icon-plus"
          size="mini"
          @click="handleAdd"
          v-hasPermi="['monitor:job:add']"
        >新增
        </el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="success"
          plain
          icon="el-icon-edit"
          size="mini"
          :disabled="single"
          @click="handleUpdate"
          v-hasPermi="['monitor:job:edit']"
        >修改
        </el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="danger"
          plain
          icon="el-icon-delete"
          size="mini"
          :disabled="multiple"
          @click="handleDelete"
          v-hasPermi="['monitor:job:remove']"
        >删除
        </el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="warning"
          plain
          icon="el-icon-download"
          size="mini"
          @click="handleExport"
          v-hasPermi="['monitor:job:export']"
        >导出
        </el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="info"
          plain
          icon="el-icon-s-operation"
          size="mini"
          @click="handleJobLog"
          v-hasPermi="['monitor:job:query']"
        >日志
        </el-button>
      </el-col>
      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
    </el-row>

    <el-table v-loading="loading" :data="jobList" @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55" align="center"/>
      <el-table-column label="任务编号" width="100" align="center" prop="jobId"/>
      <el-table-column label="任务名称" align="center" prop="jobName" :show-overflow-tooltip="true"/>
      <el-table-column label="任务组名" align="center" prop="jobGroup">
        <template slot-scope="scope">
          <dict-tag :options="dict.type.sys_job_group" :value="scope.row.jobGroup"/>
        </template>
      </el-table-column>
      <el-table-column label="调用目标字符串" align="center" prop="invokeTarget" :show-overflow-tooltip="true"/>
      <el-table-column label="cron执行表达式" align="center" prop="cronExpression" :show-overflow-tooltip="true"/>
      <el-table-column label="状态" align="center">
        <template slot-scope="scope">
          <el-switch
            v-model="scope.row.status"
            active-value="0"
            inactive-value="1"
            @change="handleStatusChange(scope.row)"
          ></el-switch>
        </template>
      </el-table-column>
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
        <template slot-scope="scope">
          <el-button
            size="mini"
            type="text"
            icon="el-icon-edit"
            @click="handleUpdate(scope.row)"
            v-hasPermi="['monitor:job:edit']"
          >修改
          </el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-delete"
            @click="handleDelete(scope.row)"
            v-hasPermi="['monitor:job:remove']"
          >删除
          </el-button>
          <el-dropdown size="mini" @command="(command) => handleCommand(command, scope.row)"
                       v-hasPermi="['monitor:job:changeStatus', 'monitor:job:query']">
            <el-button size="mini" type="text" icon="el-icon-d-arrow-right">更多</el-button>
            <el-dropdown-menu slot="dropdown">
              <el-dropdown-item command="handleRun" icon="el-icon-caret-right"
                                v-hasPermi="['monitor:job:changeStatus']">执行一次
              </el-dropdown-item>
              <el-dropdown-item command="handleView" icon="el-icon-view"
                                v-hasPermi="['monitor:job:query']">任务详细
              </el-dropdown-item>
              <el-dropdown-item command="handleJobLog" icon="el-icon-s-operation"
                                v-hasPermi="['monitor:job:query']">调度日志
              </el-dropdown-item>
            </el-dropdown-menu>
          </el-dropdown>
        </template>
      </el-table-column>
    </el-table>

    <pagination
      v-show="total>0"
      :total="total"
      :page.sync="queryParams.pageNum"
      :limit.sync="queryParams.pageSize"
      @pagination="getList"
    />

    <!-- 添加或修改定时任务对话框 -->
    <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
        <el-row>
          <el-col :span="12">
            <el-form-item label="任务名称" prop="jobName">
              <el-input v-model="form.jobName" placeholder="请输入任务名称"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="任务分组" prop="jobGroup">
              <el-select v-model="form.jobGroup" placeholder="请选择任务分组">
                <el-option
                  v-for="dict in dict.type.sys_job_group"
                  :key="dict.value"
                  :label="dict.label"
                  :value="dict.value"
                ></el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="24">
            <el-form-item label="执行类" prop="invokeTarget">
              <el-select v-model="form.invokeTarget" placeholder="请选择定时任务的执行类" style="width: 100%"
                         @change="changeClass">
                <el-option
                  v-for="item in jobClassname"
                  :key="item.name"
                  :label="item.name"
                  :value="item.name">
                </el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="24">
            <el-form-item label="执行参数" prop="remark">
                <span slot="label">
                    执行参数
                <el-tooltip placement="top">
                  <div slot="content">
                    传入定时任务的json参数
                    <br/>调用示例:{"key1":"aaa","key2":123}
                  </div>
                  <i class="el-icon-question"></i>
                </el-tooltip>
              </span>
              <el-input v-model="form.remark" placeholder="请输入任务参数"/>
            </el-form-item>
          </el-col>
          <el-col :span="24">
            <el-form-item label="cron表达式" prop="cronExpression">
              <el-input v-model="form.cronExpression" placeholder="请输入cron执行表达式">
                <template slot="append">
                  <el-button type="primary" @click="handleShowCron">
                    生成表达式
                    <i class="el-icon-time el-icon--right"></i>
                  </el-button>
                </template>
              </el-input>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="执行策略" prop="misfirePolicy">
              <el-radio-group v-model="form.misfirePolicy" size="small">
                <el-radio-button label="1">立即执行</el-radio-button>
                <el-radio-button label="2">执行一次</el-radio-button>
                <el-radio-button label="3">放弃执行</el-radio-button>
              </el-radio-group>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="状态">
              <el-radio-group v-model="form.status">
                <el-radio
                  v-for="dict in dict.type.sys_job_status"
                  :key="dict.value"
                  :label="dict.value"
                >{{ dict.label }}
                </el-radio>
              </el-radio-group>
            </el-form-item>
          </el-col>

        </el-row>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="submitForm">确 定</el-button>
        <el-button @click="cancel">取 消</el-button>
      </div>
    </el-dialog>

    <el-dialog title="Cron表达式生成器" :visible.sync="openCron" append-to-body destroy-on-close class="scrollbar">
      <crontab @hide="openCron=false" @fill="crontabFill" :expression="expression"></crontab>
    </el-dialog>

    <!-- 任务日志详细 -->
    <el-dialog title="任务详细" :visible.sync="openView" width="700px" append-to-body>
      <el-form ref="form" :model="form" label-width="120px" size="mini">
        <el-row>
          <el-col :span="12">
            <el-form-item label="任务编号:">{{ form.jobId }}</el-form-item>
            <el-form-item label="任务名称:">{{ form.jobName }}</el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="任务分组:">{{ jobGroupFormat(form) }}</el-form-item>
            <el-form-item label="创建时间:">{{ form.createTime }}</el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="cron表达式:">{{ form.cronExpression }}</el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="下次执行时间:">{{ parseTime(form.nextValidTime) }}</el-form-item>
          </el-col>
          <el-col :span="24">
            <el-form-item label="调用目标方法:">{{ form.invokeTarget }}</el-form-item>

          </el-col>
          <el-col :span="12">
            <el-form-item label="任务状态:">
              <div v-if="form.status == 0">正常</div>
              <div v-else-if="form.status == 1">失败</div>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="执行参数:">
              <div v-if="form.concurrent == 0">允许</div>
              <div v-else-if="form.concurrent == 1">禁止</div>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="执行策略:">
              <div v-if="form.misfirePolicy == 0">默认策略</div>
              <div v-else-if="form.misfirePolicy == 1">立即执行</div>
              <div v-else-if="form.misfirePolicy == 2">执行一次</div>
              <div v-else-if="form.misfirePolicy == 3">放弃执行</div>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="openView = false">关 闭</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import {listJob, getJob, delJob, addJob, updateJob, runJob, changeJobStatus, jobClass} from "@/api/monitor/job";
import Crontab from '@/components/Crontab'

export default {
  components: {Crontab},
  name: "Job",
  dicts: ['sys_job_group', 'sys_job_status'],
  data() {
    return {
      // 遮罩层
      loading: true,
      // 选中数组
      ids: [],
      // 非单个禁用
      single: true,
      // 非多个禁用
      multiple: true,
      // 显示搜索条件
      showSearch: true,
      // 总条数
      total: 0,
      // 定时任务表格数据
      jobList: [],
      // 弹出层标题
      title: "",
      // 是否显示弹出层
      open: false,
      // 是否显示详细弹出层
      openView: false,
      // 是否显示Cron表达式弹出层
      openCron: false,
      // 传入的表达式
      expression: "",
      // 查询参数
      queryParams: {
        pageNum: 1,
        pageSize: 10,
        jobName: undefined,
        jobGroup: undefined,
        status: undefined
      },
      // 表单参数
      form: {},
      // 表单校验
      rules: {
        jobName: [
          {required: true, message: "任务名称不能为空", trigger: "blur"}
        ],
        invokeTarget: [
          {required: true, message: "调用目标字符串不能为空", trigger: "blur"}
        ],
        cronExpression: [
          {required: true, message: "cron执行表达式不能为空", trigger: "blur"}
        ]
      },
      jobClassname: [],
      //执行参数
      classparam: ''
    };
  },
  created() {
    this.getList();
    jobClass().then(response => {
      this.jobClassname = response.data;
    });
  },
  methods: {
    /** 查询定时任务列表 */
    getList() {
      this.loading = true;
      listJob(this.queryParams).then(response => {
        this.jobList = response.rows;
        this.total = response.total;
        this.loading = false;
      });
    },
    changeClass(item) {
      console.log("选中值,", item);
      const selectItem = this.jobClassname.find(item => item.name === this.form.invokeTarget);
      this.form.remark = selectItem ? selectItem.value : '';
    },
    // 任务组名字典翻译
    jobGroupFormat(row, column) {
      return this.selectDictLabel(this.dict.type.sys_job_group, row.jobGroup);
    },
    // 取消按钮
    cancel() {
      this.open = false;
      this.reset();
    },
    // 表单重置
    reset() {
      this.form = {
        jobId: undefined,
        jobName: undefined,
        jobGroup: undefined,
        invokeTarget: undefined,
        cronExpression: undefined,
        misfirePolicy: 1,
        remark: '',
        status: "0"
      };
      this.resetForm("form");
    },
    /** 搜索按钮操作 */
    handleQuery() {
      this.queryParams.pageNum = 1;
      this.getList();
    },
    /** 重置按钮操作 */
    resetQuery() {
      this.resetForm("queryForm");
      this.handleQuery();
    },
    // 多选框选中数据
    handleSelectionChange(selection) {
      this.ids = selection.map(item => item.jobId);
      this.single = selection.length != 1;
      this.multiple = !selection.length;
    },
    // 更多操作触发
    handleCommand(command, row) {
      switch (command) {
        case "handleRun":
          this.handleRun(row);
          break;
        case "handleView":
          this.handleView(row);
          break;
        case "handleJobLog":
          this.handleJobLog(row);
          break;
        default:
          break;
      }
    },
    // 任务状态修改
    handleStatusChange(row) {
      let text = row.status === "0" ? "启用" : "停用";
      this.$modal.confirm('确认要"' + text + '""' + row.jobName + '"任务吗?').then(function () {
        return changeJobStatus(row.jobId, row.status);
      }).then(() => {
        this.$modal.msgSuccess(text + "成功");
      }).catch(function () {
        row.status = row.status === "0" ? "1" : "0";
      });
    },
    /* 立即执行一次 */
    handleRun(row) {
      this.$modal.confirm('确认要立即执行一次"' + row.jobName + '"任务吗?').then(function () {
        return runJob(row.jobId, row.jobGroup);
      }).then(() => {
        this.$modal.msgSuccess("执行成功");
      }).catch(() => {
      });
    },
    /** 任务详细信息 */
    handleView(row) {
      getJob(row.jobId).then(response => {
        this.form = response.data;
        this.openView = true;
      });
    },
    /** cron表达式按钮操作 */
    handleShowCron() {
      this.expression = this.form.cronExpression;
      this.openCron = true;
    },
    /** 确定后回传值 */
    crontabFill(value) {
      this.form.cronExpression = value;
    },
    /** 任务日志列表查询 */
    handleJobLog(row) {
      const jobId = row.jobId || 0;
      this.$router.push('/monitor/job-log/index/' + jobId)
    },
    /** 新增按钮操作 */
    handleAdd() {
      this.reset();
      this.open = true;
      this.title = "添加任务";
    },
    /** 修改按钮操作 */
    handleUpdate(row) {
      this.reset();
      const jobId = row.jobId || this.ids;
      getJob(jobId).then(response => {
        this.form = response.data;
        this.open = true;
        this.title = "修改任务";
      });
    },
    /** 提交按钮 */
    submitForm: function () {
      this.$refs["form"].validate(valid => {
        if (valid) {
          if (this.form.jobId != undefined) {
            updateJob(this.form).then(response => {
              this.$modal.msgSuccess("修改成功");
              this.open = false;
              this.getList();
            });
          } else {
            console.log("新增参数", this.form);
            addJob(this.form).then(response => {
              this.$modal.msgSuccess("新增成功");
              this.open = false;
              this.getList();
            });
          }
        }
      });
    },
    /** 删除按钮操作 */
    handleDelete(row) {
      const jobIds = row.jobId || this.ids;
      this.$modal.confirm('是否确认删除定时任务编号为"' + jobIds + '"的数据项?').then(function () {
        return delJob(jobIds);
      }).then(() => {
        this.getList();
        this.$modal.msgSuccess("删除成功");
      }).catch(() => {
      });
    },
    /** 导出按钮操作 */
    handleExport() {
      this.download('monitor/job/export', {
        ...this.queryParams
      }, `job_${new Date().getTime()}.xlsx`)
    }
  }
};
</script>

7,vue需要的js

import request from '@/utils/request'

// 查询定时任务调度列表
export function listJob(query) {
  console.log("查询参数,",query);
  return request({
    url: '/monitor/job/list',
    method: 'get',
    params: query
  })
}

// 查询定时任务调度详细
export function getJob(jobId) {
  return request({
    url: '/monitor/job/' + jobId,
    method: 'get'
  })
}

// 查询定时任务调度列表
export function jobClass() {
  return request({
    url: '/monitor/job/getJobclass',
    method: 'get'
  })
}

// 新增定时任务调度
export function addJob(data) {
  return request({
    url: '/monitor/job',
    method: 'post',
    data: data
  })
}

// 修改定时任务调度
export function updateJob(data) {
  return request({
    url: '/monitor/job',
    method: 'put',
    data: data
  })
}

// 删除定时任务调度
export function delJob(jobId) {
  return request({
    url: '/monitor/job/' + jobId,
    method: 'delete'
  })
}

// 任务状态修改
export function changeJobStatus(jobId, status) {
  const data = {
    jobId,
    status
  }
  return request({
    url: '/monitor/job/changeStatus',
    method: 'put',
    data: data
  })
}


// 定时任务立即执行一次
export function runJob(jobId, jobGroup) {
  const data = {
    jobId,
    jobGroup
  }
  return request({
    url: '/monitor/job/run',
    method: 'put',
    data: data
  })
}

8,最后补上服务启动时启动开启状态的定时任务

@Slf4j
@Configuration
public class DevJobListener implements ApplicationListener<ApplicationStartedEvent>, Ordered {
    @SuppressWarnings("ALL")
    @Override
    public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) {
        SysJob job = new SysJob();
        job.setStatus("0");
        job.setMisfirePolicy("1");
        SpringUtil.getBean(ISysJobService.class).selectJobList(job)
                .forEach(devJob -> CronUtil.schedule(devJob.getJobId() + "", devJob.getCronExpression(), () -> {
                    try {
                        // 运行定时任务
                        ((CommonTimerTaskRunner) SpringUtil.getBean(Class.forName(devJob.getInvokeTarget()))).action(devJob.getRemark());
                    } catch (ClassNotFoundException e) {
                        throw new BaseException("定时任务找不到对应的类,名称为:{}", devJob.getInvokeTarget());
                    }
                }));
        // 设置秒级别的启用
        CronUtil.setMatchSecond(true);
        log.info("启动定时器执行器");
        CronUtil.restart();
    }

    @Override
    public int getOrder() {
        return LOWEST_PRECEDENCE;
    }
}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
Guns-Separation是Guns后台管理系统的前后端分离版本,项目采用前后端分离架构,代码简洁,功能丰富,开箱即用,开创快速开发平台新趋势。 Guns-Separation功能介绍: 1、主控面板:控制台页面,可进行工作台,分析页,统计等功能的展示。 2、用户管理:对企业用户和系统管理员用户的维护,可绑定用户职务,机构,角色,数据权限等。 3、应用管理:通过应用来控制不同维度的菜单展示。 4、机构管理:公司组织架构维护,支持多层级结构的树形结构。 5、职位管理:用户职务管理,职务可作为用户的一个标签,职务目前没有和权限等其他功能挂钩。 6、菜单管理:菜单目录,菜单,和按钮的维护是权限控制的基本单位。 7、角色管理:角色绑定菜单后,可限制相关角色的人员登录系统的功能范围。角色也可以绑定数据授权范围。 8、字典管理:系统内各种枚举型的维护。 9、访问日志:用户的登录和退出日志的查看和管理。 10、操作日志:用户的操作业务的日志的查看和管理。 11、服务监控:服务器的运行状态,Java虚拟机信息,jvm等数据的查看。 12、在线用户:当前系统在线用户的查看。 13、数据监控:druid控制台功能,可查看sql的运行信息。 14、公告管理:系统的公告的管理。 15、文件管理:文件的上传下载查看等操作,文件可使用本地存储,阿里云oss,腾讯cos接入,支持拓展。 16、定时任务定时任务的维护,通过cron表达式控制任务的执行频率。 17、系统配置:系统运行的参数的维护,参数的配置与系统运行机制息息相关。 18、邮件发送:发送邮件功能。 19、短信发送:短信发送功能,可使用阿里云sms,腾讯云sms,支持拓展。 Guns-Separation快速开始 准备以下环境: 1、npm,jdk1.8,maven 3.6或以上版本。 2、需要准备一个mysql 5.7数据库。 3、您的IDE需要安装lombok插件。 前端运行: 1、cd _web/ 2、npm install 3、npm run serve 后端运行: 1、将数据库文件_sql/guns-separation.sql导入到数据库 2、修改guns-main/src/main/resources/application-local.yml文件,修改为您的数据库ip,账号和密码 3、运行guns-main/src/main/java/cn/stylefeng/guns/GunsApplication,即可启动后端程序 框架优势: 1、模块化架构设计,层次清晰,业务层推荐写到单独模块,方便升级。 2、前后端分离架构,分离开发,分离部署,前后端互不影响。 3、前端技术采用vue + antdv + axios。 4、后端采用spring boot + mybatis-plus + hutool等,开源可靠。 5、基于spring security(jwt) + 用户UUID双重认证。 6、基于AOP实现的接口粒度的鉴权,最细粒度过滤权限资源。 7、基于hibernate validator实现的校验框架,支持自定义校验注解。 8、提供Request-No的响应header快速定位线上异常问题。 9、在线用户可查,可在线踢人,同账号登录可同时在线,可单独在线(通过系统参数配置)。 10、支持前端 + 后端在线代码生成(后续开放)。 11、支持jenkins一键部署,另自带docker maven插件,支持docker部署。 12、文件,短信,缓存,邮件等,利用接口封装,方便拓展。 13、文件默认使用本地文件,短信默认使用阿里云sms,缓存默认使用内存缓存。 14、文档齐全,持续更新,视频教程将发布到Bilibili(后续开放)。 演示账号密码:superAdmin/123456 Guns-Separation v1.1更新内容: 1、增加上传图片的预览功能 2、完善数据范围分配时候的判断逻辑 3、授权数据取消父级子级关联 4、【前端】工作台界面使用静态数据、环境显示抽屉默认设置为全显示 5、统一日志打印格式 6、修复邮件发送异常的问题 7、修复菜单遍历没有修改子应用的问题 8、默认去掉oss,cos,短信的依赖包,减少了默认打包体积 9、【pr合并】修改密码加密方式为bcrypt 10、修复定位bug
JooLun微信管理平台是一个 Java EE 企业级微信快速开发平台,基于经典技术组合(Spring Boot、Spring Security、MyBatis、Jwt、Vue),内置模块如:公众号管理、部门管理、角色用户、菜单及按钮授权、数据权限、系统参数、日志管理、代码生成等。在线定时任务配置;支持集群,支持多数据源。 在RuoYi-Vue基础上集成了MybatisPlus和Avue,微信接口统一使用了WxJava SDK。 系统需求: JDK >= 1.8 MySQL >= 5.8 Maven >= 3.0 内置功能: 1、公众号用户标签管理 2、公众号用户管理 3、公众号用户消息管理 4、公众号素材管理 5、公众号自定义菜单管理 6、公众号消息自动回复管理 7、公众号营销数据统计 8、用户管理:用户是系统操作者,该功能主要完成系统用户配置。 9、部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。 10、岗位管理:配置系统用户所属担任职务。 11、菜单管理:配置系统菜单,操作权限,按钮权限标识等。 12、角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。 13、字典管理:对系统中经常使用的一些较为固定的数据进行维护。 14、参数管理:对系统动态配置常用参数。 15、通知公告:系统通知公告信息发布维护。 16、操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。 17、登录日志:系统登录日志记录查询包含登录异常。 18、在线用户:当前系统中活跃用户状态监控。 19、定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。 20、代码生成:前后端代码的生成(java、html、xml、sql)支持CRUD下载 。 21、系统接口:根据业务代码自动生成相关的api接口文档。 22、服务监控:监视当前系统CPU、内存、磁盘、堆栈等相关信息。 23、在线构建器:拖动表单元素生成相应的HTML代码。 24、连接池监视:监视当前系统数据库连
## 项目功能 1. 架构潮流:系统采用SpringBoot+VUE前后端分离,前端单独部署,Nginx负载均衡 2. 接口友好:同时支持swagger2、knife4j两种可视化接口API调试,支持离线接口文档; 3. 任务管理:支持通过Web页面对任务进行CRUD操作,可视化界面,快速上手; 4. 执行记录:支持通过web页面在线查看调度结果、执行结果、下次执行时间; 5. 实时日志:支持通过web页面实时查看执行器输出的完整的执行日志; 6. 唯一搜索:支持通过web界面根据jobname或jobgroup进行全局唯一查询 7. 强自定义支持在线配置定时任务请求型、请求路径、请求参数、Cron表达式,即时生效; 8. 动态控制:支持动态修改任务状态、启动/停止任务,以及终止运行中任务,即时生效; 9. 执行策略:支持丰富的执行策略,包括:Get请求、PostJson请求、PostFrom表单请求; 10. 自动注册:周期性自动注册任务, 同时,也支持手动录入定时任务地址; 11. 自动执行:系统会自动发现注册的任务并触发执行,同时,也支持手动触发-立即执行; 12. 用户管理:支持在线管理系统用户、角色、菜单,默认管理员、开发者、普通用户三种角色; 13. 权限控制:支持在线权限控制,管理员拥有全量权限,开发者拥有除角色管理外的所有权限,普通用户仅支持任务管理相关权限; 14. 集群部署:支持分布式执行,系统支持集群部署,可保证任务执行的高可用; 15. 弹性调度:一旦有任务机器上线或者下线,下次调度时将会重新分配任务; 16. 路由策略:系统集群部署时提供丰富的路由策略,包括:轮询、随机、故障转移、忙碌转移等常用策略; 17. 故障转移:任务路由策略选择"故障转移"情况下,如果集群中某一台机器故障,将会自动切换到一台正常的执行器发送调度请求; 18. 阻塞策略:调度过于密集执行器来不及处理时的处理策略,策略包括:单机串行(默认)、丢弃后续调度、覆盖之前调度; 19. 超时控制:支持自定义任务超时时间,任务运行超时将会主动中断任务; 20. 重试机制:支持自定义任务重试次数,当任务失败时将会按照预设的失败重试次数主动进行重试; 21. 消息工厂:默认提供邮件工厂的方式推送消息,同时预留扩展接口,可方便的扩展短信、钉钉等消息方式; 22. 邮件告警:任务失败时支持邮件报警,支持配置多邮件地址群发报警邮件; 23. 运行报表:支持实时查看运行数据,以及调度报表,如调度日期分布图,任务组执行比例比例分布图等; 24. 事件触发:除了"Cron方式"和"任务依赖方式"触发任务执行之外,提供触发任务单次执行的API服务; 25. 脚本任务:支持以GLUE分布式平台开发和运行脚本任务,包括Shell、Python、NodeJS等型脚本; 26. 多线并发:系统支持多线程触发调度运行,确保调度精确执行,不被堵塞; 27. 降级隔离:调度线程池进行隔离拆分,慢任务自动降级进入"Slow"线程池,避免耗尽调度线程,提高系统稳定性; 28. Gradle: 将会把最新稳定版推送到gradle中央仓库, 方便用户接入和使用; 29. Maven: 将会把最新稳定版推送到maven中央仓库, 方便用户接入和使用; 30. 一致性:基于Redis分布式锁保证集群分布式调度的最终一致性, 一次任务调度只会触发一次执行; 31. 全异步:任务调度流程全异步化设计实现,如异步调度、异步运行、异步回调等,有效对密集调度进行流量削峰,理论上支持任意时长任务的运行; 32. 跨语言:系统提供语言无关的 RESTFUL API 服务,第三方任意语言可据此对接Task Manage; 33. 国际化:后管系统支持国际化设置,提供中文、英文两种可选语言,默认为中文; 34. 容器化:提供官方docker镜像,并实时更新推送dockerhub,进一步实现产品开箱即用; ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。
lamp-cloud的前身是zuihou-admin-cloud,从3.0.0版本开始,改名为lamp-cloud,它是lamp项目的其中一员。   lamp-cloud是一个基于SpringCloud(Hoxton.SR10) + SpringBoot(2.3.10.RELEASE)的SaaS微服务脚手架,具有统一授权、认证后台管理系统,其中包含具备用户管理、资源权限管理、网关API、分布式事务、大文件断点分片续传等多个模块,支持多业务系统并行开发,可以作为后端服务的开发脚手架。代码简洁,架构清晰,适合学习和直接项目中使用。核心技术采用Nacos、Fegin、Ribbon、Zuul、Hystrix、JWT Token、Mybatis、SpringBoot、Redis、RibbitMQ等主要框架和中间件。 lamp-cloud功能: 1、服务注册&发现与调用: 基于Nacos来实现的服务注册与发现,使用使用Feign来实现服务互调, 可以做到使用HTTP请求远程调用时能与调用本地方法一样的编码体验,开发者完全感知不到这是远程方法,更感知不到这是个HTTP请求。 2、服务鉴权: 通过JWT的方式来加强服务之间调度的权限验证,保证内部服务的安全性。 3、负载均衡: 将服务保留的rest进行代理和网关控制,除了平常经常使用的node.js、nginx外,Spring Cloud系列的zuul和ribbon,可以帮我们进行正常的网关管控和负载均衡。其中扩展和借鉴国外项目的扩展基于JWT的Zuul限流插件,方面进行限流。 4、熔断机制: 因为采取了服务的分布,为了避免服务之间的调用“雪崩”,采用了Hystrix的作为熔断器,避免了服务之间的“雪崩”。 5、监控: 利用Spring Boot Admin 来监控各个独立Service的运行状态;利用turbine来实时查看接口的运行状态和调用频率;通过Zipkin来查看各个服务之间的调用链等。 6、链路调用监控: 利用Zipkin实现微服务的全链路性能监控, 从整体维度到局部维度展示各项指标,将跨应用的所有调用链性能信息集中展现,可方便度量整体和局部性能,并且方便找到故障产生的源头,生产上可极大缩短故障排除时间。有了它,我们能做到: 请求链路追踪,故障快速定位:可以通过调用链结合业务日志快速定位错误信息。 可视化:各个阶段耗时,进行性能分析。 依赖优化:各个调用环节的可用性、梳理服务依赖关系以及优化。 数据分析,优化链路:可以得到用户的行为路径,汇总分析应用在很多业务场景。 7、数据权限 利用基于Mybatis的DataScopeInterceptor拦截器实现了简单的数据权限 8、SaaS(多租户)的无感解决方案 使用Mybatis拦截器实现对所有SQL的拦截,修改默认的Schema,从而实现多租户数据隔离的目的。 并且支持可插拔。 9、二级缓存 采用J2Cache操作缓存,第一级缓存使用内存(Caffeine),第二级缓存使用 Redis。 由于大量的缓存读取会导致 L2 的网络成为整个系统的瓶颈,因此 L1 的目标是降低对 L2 的读取次数。 该缓存框架主要用于集群环境中。单机也可使用,用于避免应用重启导致的缓存冷启动后对后端业务的冲击。 10、优雅的Bean转换 采用Dozer组件来对 DTO、DO、PO等对象的优化转换 11、前后端统一表单验证 严谨的表单验证通常需要 前端+后端同时验证, 但传统的项目,均只能前后端各做一次检验, 后期规则变更,又得前后端同时修改。 故在hibernate-validator的基础上封装了zuihou-validator-starter起步依赖,提供一个通用接口,可以获取需要校验表单的规则,然后前端使用后端返回的规则, 以后若规则改变,只需要后端修改即可。 12、防跨站脚本攻击(XSS) 通过过滤器对所有请求中的 表单参数 进行过滤 通过Json反序列化器实现对所有 application/json 型的参数 进行过滤 13、当前登录用户信息注入器 通过注解实现用户身份注入 14、在线API 由于原生swagger-ui某些功能支持不够友好,故采用了国内开源的swagger-bootstrap-ui,并制作了stater,方便springboot用户使用。 15、代码生成器 基于Mybatis-plus-generator自定义了一套代码生成器, 通过配置数据库字段的注释,自动生成枚举、数据字典注解、SaveDTO、UpdateDTO、表单验证规则注解、Swagger注解等。 16、定时任务调度器: 基于xxl-jobs进行了功能增强。(如:指定时间发送任务、执行器和调度器合并项目、多数据源) 17、大文件/断点/分片续传 前端采用w
若依定时任务可以实现自定义执行时间。通过使用Quartz.net作业调度框架,你可以根据自己的需求定义定时执行任务的时间。你可以使用cron表达式来指定任务的执行时间,cron表达式是一种灵活的时间表达方式,可以非常精确地指定任务的执行时间。你可以通过修改定时任务的cron表达式来改变任务的执行时间,也可以通过获取单个定时任务的接口来查看当前任务的执行时间。此外,还有一款名为《定时执行专家》的软件,它也提供了丰富的定时任务执行功能,可以按照你的需求自定义任务的执行时间。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Quartz.net作业调度自定义定时执行任务多任务执行c#](https://download.csdn.net/download/qq_34375579/11149510)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [DynamicSchedule:定时器,自定义corn表达式,动态...支持添加定时任务、取消定时任务、重置定时任务执行时间](https://download.csdn.net/download/weixin_42113552/19099762)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [V6.1 - 《定时执行专家》是一款制作精良、功能强大、简单易用、毫秒级精度、专业级的定时任务执行软件。](https://download.csdn.net/download/boomworks/86246080)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

占星安啦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值