From 2cc14e5f5623dbd672100512f07097491bcf731d Mon Sep 17 00:00:00 2001
From: RuoYi <yzz_ivy@163.com>
Date: 星期三, 18 三月 2020 09:43:17 +0800
Subject: [PATCH] 若依 2.2
---
ruoyi/src/main/java/com/ruoyi/project/monitor/mapper/SysJobMapper.java | 67 +
ruoyi/src/main/java/com/ruoyi/common/utils/job/ScheduleUtils.java | 113 ++
ruoyi-ui/src/views/monitor/job/log.vue | 295 +++++
ruoyi/src/main/java/com/ruoyi/RuoYiApplication.java | 2
ruoyi/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java | 40
ruoyi/pom.xml | 15
ruoyi/src/main/java/com/ruoyi/project/monitor/service/impl/SysJobLogServiceImpl.java | 87 +
ruoyi/src/main/java/com/ruoyi/framework/web/domain/Server.java | 1
ruoyi/src/main/java/com/ruoyi/framework/config/SecurityConfig.java | 1
ruoyi/src/main/java/com/ruoyi/project/monitor/domain/SysJob.java | 170 +++
ruoyi/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java | 110 +
ruoyi/src/main/java/com/ruoyi/common/utils/job/AbstractQuartzJob.java | 107 +
ruoyi/src/main/java/com/ruoyi/framework/config/ScheduleConfig.java | 57 +
ruoyi-ui/package.json | 2
ruoyi/src/main/java/com/ruoyi/project/monitor/controller/SysJobLogController.java | 87 +
ruoyi/src/main/java/com/ruoyi/common/exception/job/TaskException.java | 34
ruoyi-ui/src/layout/components/TagsView/index.vue | 2
ruoyi-ui/src/router/index.js | 13
ruoyi/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java | 1
ruoyi/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java | 2
ruoyi/src/main/java/com/ruoyi/project/monitor/mapper/SysJobLogMapper.java | 64 +
ruoyi/src/main/java/com/ruoyi/project/monitor/service/ISysJobService.java | 102 +
ruoyi-ui/src/store/modules/tagsView.js | 19
ruoyi/src/main/java/com/ruoyi/project/monitor/controller/SysJobController.java | 130 ++
ruoyi/src/main/java/com/ruoyi/common/utils/job/JobInvokeUtil.java | 182 +++
ruoyi/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java | 50
ruoyi/sql/quartz.sql | 170 +++
ruoyi/src/main/java/com/ruoyi/common/utils/job/QuartzDisallowConcurrentExecution.java | 21
ruoyi/src/main/java/com/ruoyi/framework/task/RyTask.java | 28
ruoyi/src/main/resources/mybatis/system/SysJobLogMapper.xml | 93 +
ruoyi/src/main/java/com/ruoyi/common/utils/job/CronUtils.java | 63 +
ruoyi-ui/src/views/monitor/job/index.vue | 487 ++++++++
ruoyi-ui/src/api/monitor/job.js | 80 +
ruoyi/src/main/java/com/ruoyi/common/utils/job/QuartzJobExecution.java | 19
ruoyi/src/main/java/com/ruoyi/project/monitor/service/ISysJobLogService.java | 56 +
ruoyi-ui/src/components/SvgIcon/index.vue | 2
ruoyi/src/main/java/com/ruoyi/project/common/CommonController.java | 20
ruoyi/src/main/resources/application.yml | 3
ruoyi/src/main/resources/mybatis/system/SysJobMapper.xml | 111 +
ruoyi-ui/src/api/monitor/jobLog.js | 35
ruoyi/src/main/java/com/ruoyi/project/monitor/service/impl/SysJobServiceImpl.java | 254 ++++
ruoyi/src/main/java/com/ruoyi/project/monitor/domain/SysJobLog.java | 155 ++
42 files changed, 3,325 insertions(+), 25 deletions(-)
diff --git a/ruoyi-ui/package.json b/ruoyi-ui/package.json
index b16824b..923126c 100644
--- a/ruoyi-ui/package.json
+++ b/ruoyi-ui/package.json
@@ -1,6 +1,6 @@
{
"name": "ruoyi",
- "version": "2.1.0",
+ "version": "2.2.0",
"description": "鑻ヤ緷绠$悊绯荤粺",
"author": "鑻ヤ緷",
"license": "MIT",
diff --git a/ruoyi-ui/src/api/monitor/job.js b/ruoyi-ui/src/api/monitor/job.js
new file mode 100644
index 0000000..58c4343
--- /dev/null
+++ b/ruoyi-ui/src/api/monitor/job.js
@@ -0,0 +1,80 @@
+import request from '@/utils/request'
+
+// 鏌ヨ瀹氭椂浠诲姟璋冨害鍒楄〃
+export function listJob(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 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 exportJob(query) {
+ return request({
+ url: '/monitor/job/export',
+ method: 'get',
+ params: query
+ })
+}
+
+// 浠诲姟鐘舵�佷慨鏀�
+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
+ })
+}
\ No newline at end of file
diff --git a/ruoyi-ui/src/api/monitor/jobLog.js b/ruoyi-ui/src/api/monitor/jobLog.js
new file mode 100644
index 0000000..be1fffd
--- /dev/null
+++ b/ruoyi-ui/src/api/monitor/jobLog.js
@@ -0,0 +1,35 @@
+import request from '@/utils/request'
+
+// 鏌ヨ璋冨害鏃ュ織鍒楄〃
+export function listJobLog(query) {
+ return request({
+ url: '/monitor/jobLog/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 鍒犻櫎璋冨害鏃ュ織
+export function delJobLog(jobLogId) {
+ return request({
+ url: '/monitor/jobLog/' + jobLogId,
+ method: 'delete'
+ })
+}
+
+// 娓呯┖璋冨害鏃ュ織
+export function cleanJobLog() {
+ return request({
+ url: '/monitor/jobLog/clean',
+ method: 'delete'
+ })
+}
+
+// 瀵煎嚭璋冨害鏃ュ織
+export function exportJobLog(query) {
+ return request({
+ url: '/monitor/jobLog/export',
+ method: 'get',
+ params: query
+ })
+}
\ No newline at end of file
diff --git a/ruoyi-ui/src/components/SvgIcon/index.vue b/ruoyi-ui/src/components/SvgIcon/index.vue
index 340ace4..6cd3ab9 100644
--- a/ruoyi-ui/src/components/SvgIcon/index.vue
+++ b/ruoyi-ui/src/components/SvgIcon/index.vue
@@ -1,7 +1,7 @@
<template>
<div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
<svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
- <use :xlink:href="iconName" />
+ <use :href="iconName" />
</svg>
</template>
diff --git a/ruoyi-ui/src/layout/components/TagsView/index.vue b/ruoyi-ui/src/layout/components/TagsView/index.vue
index bb215c8..19e1912 100644
--- a/ruoyi-ui/src/layout/components/TagsView/index.vue
+++ b/ruoyi-ui/src/layout/components/TagsView/index.vue
@@ -158,7 +158,7 @@
toLastView(visitedViews, view) {
const latestView = visitedViews.slice(-1)[0]
if (latestView) {
- this.$router.push(latestView)
+ this.$router.push(latestView.fullPath)
} else {
// now the default is to redirect to the home page if there is no tags-view,
// you can adjust it according to your needs.
diff --git a/ruoyi-ui/src/router/index.js b/ruoyi-ui/src/router/index.js
index 65557f9..ec7bc7c 100644
--- a/ruoyi-ui/src/router/index.js
+++ b/ruoyi-ui/src/router/index.js
@@ -93,6 +93,19 @@
]
},
{
+ path: '/job',
+ component: Layout,
+ hidden: true,
+ children: [
+ {
+ path: 'log',
+ component: () => import('@/views/monitor/job/log'),
+ name: 'JobLog',
+ meta: { title: '璋冨害鏃ュ織' }
+ }
+ ]
+ },
+ {
path: '/gen',
component: Layout,
hidden: true,
diff --git a/ruoyi-ui/src/store/modules/tagsView.js b/ruoyi-ui/src/store/modules/tagsView.js
index e128d2a..476e555 100644
--- a/ruoyi-ui/src/store/modules/tagsView.js
+++ b/ruoyi-ui/src/store/modules/tagsView.js
@@ -28,13 +28,8 @@
}
},
DEL_CACHED_VIEW: (state, view) => {
- for (const i of state.cachedViews) {
- if (i === view.name) {
- const index = state.cachedViews.indexOf(i)
- state.cachedViews.splice(index, 1)
- break
- }
- }
+ const index = state.cachedViews.indexOf(view.name)
+ index > -1 && state.cachedViews.splice(index, 1)
},
DEL_OTHERS_VISITED_VIEWS: (state, view) => {
@@ -43,12 +38,10 @@
})
},
DEL_OTHERS_CACHED_VIEWS: (state, view) => {
- for (const i of state.cachedViews) {
- if (i === view.name) {
- const index = state.cachedViews.indexOf(i)
- state.cachedViews = state.cachedViews.slice(index, index + 1)
- break
- }
+ if (index > -1) {
+ state.cachedViews = state.cachedViews.slice(index, index + 1)
+ } else {
+ state.cachedViews = []
}
},
diff --git a/ruoyi-ui/src/views/monitor/job/index.vue b/ruoyi-ui/src/views/monitor/job/index.vue
index 2467772..7c01fd7 100644
--- a/ruoyi-ui/src/views/monitor/job/index.vue
+++ b/ruoyi-ui/src/views/monitor/job/index.vue
@@ -1,5 +1,488 @@
<template>
<div class="app-container">
- 瀹氭椂浠诲姟
+ <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
+ <el-form-item label="浠诲姟鍚嶇О" prop="jobName">
+ <el-input
+ v-model="queryParams.jobName"
+ placeholder="璇疯緭鍏ヤ换鍔″悕绉�"
+ clearable
+ size="small"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="浠诲姟缁勫悕" prop="jobGroup">
+ <el-select v-model="queryParams.jobGroup" placeholder="璇烽�夋嫨浠诲姟缁勫悕" clearable size="small">
+ <el-option
+ v-for="dict in jobGroupOptions"
+ :key="dict.dictValue"
+ :label="dict.dictLabel"
+ :value="dict.dictValue"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="浠诲姟鐘舵��" prop="status">
+ <el-select v-model="queryParams.status" placeholder="璇烽�夋嫨浠诲姟鐘舵��" clearable size="small">
+ <el-option
+ v-for="dict in statusOptions"
+ :key="dict.dictValue"
+ :label="dict.dictLabel"
+ :value="dict.dictValue"
+ />
+ </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"
+ 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"
+ 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"
+ 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"
+ 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"
+ icon="el-icon-s-operation"
+ size="mini"
+ @click="handleJobLog"
+ v-hasPermi="['monitor:job:query']"
+ >鏃ュ織</el-button>
+ </el-col>
+ </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="浠诲姟缂栧彿" align="center" prop="jobId" />
+ <el-table-column label="浠诲姟鍚嶇О" align="center" prop="jobName" :show-overflow-tooltip="true" />
+ <el-table-column label="浠诲姟缁勫悕" align="center" prop="jobGroup" :formatter="jobGroupFormat" />
+ <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-caret-right"
+ @click="handleRun(scope.row)"
+ v-hasPermi="['monitor:job:edit']"
+ >鎵ц涓�娆�</el-button>
+ <el-button
+ size="mini"
+ type="text"
+ icon="el-icon-view"
+ @click="handleView(scope.row)"
+ v-hasPermi="['monitor:job:query']"
+ >璇︾粏</el-button>
+ </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="700px">
+ <el-form ref="form" :model="form" :rules="rules" label-width="100px">
+ <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 jobGroupOptions"
+ :key="dict.dictValue"
+ :label="dict.dictLabel"
+ :value="dict.dictValue"
+ ></el-option>
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="24">
+ <el-form-item prop="invokeTarget">
+ <span slot="label">
+ 璋冪敤鏂规硶
+ <el-tooltip placement="top">
+ <div slot="content">
+ Bean璋冪敤绀轰緥锛歳yTask.ryParams('ry')
+ <br />Class绫昏皟鐢ㄧず渚嬶細com.ruoyi.quartz.task.RyTask.ryParams('ry')
+ <br />鍙傛暟璇存槑锛氭敮鎸佸瓧绗︿覆锛屽竷灏旂被鍨嬶紝闀挎暣鍨嬶紝娴偣鍨嬶紝鏁村瀷
+ </div>
+ <i class="el-icon-question"></i>
+ </el-tooltip>
+ </span>
+ <el-input v-model="form.invokeTarget" placeholder="璇疯緭鍏ヨ皟鐢ㄧ洰鏍囧瓧绗︿覆" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="cron琛ㄨ揪寮�" prop="cronExpression">
+ <el-input v-model="form.cronExpression" placeholder="璇疯緭鍏ron鎵ц琛ㄨ揪寮�" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鏄惁骞跺彂" prop="concurrent">
+ <el-radio-group v-model="form.concurrent" size="small">
+ <el-radio-button label="0">鍏佽</el-radio-button>
+ <el-radio-button label="1">绂佹</el-radio-button>
+ </el-radio-group>
+ </el-form-item>
+ </el-col>
+ <el-col :span="24">
+ <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="24">
+ <el-form-item label="鐘舵��">
+ <el-radio-group v-model="form.status">
+ <el-radio
+ v-for="dict in statusOptions"
+ :key="dict.dictValue"
+ :label="dict.dictValue"
+ >{{dict.dictLabel}}</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="浠诲姟璇︾粏" :visible.sync="openView" width="700px">
+ <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">姝e父</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.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.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>
\ No newline at end of file
+</template>
+
+<script>
+import { listJob, getJob, delJob, addJob, updateJob, exportJob, runJob, changeJobStatus } from "@/api/monitor/job";
+
+export default {
+ name: "Job",
+ data() {
+ return {
+ // 閬僵灞�
+ loading: true,
+ // 閫変腑鏁扮粍
+ ids: [],
+ // 闈炲崟涓鐢�
+ single: true,
+ // 闈炲涓鐢�
+ multiple: true,
+ // 鎬绘潯鏁�
+ total: 0,
+ // 瀹氭椂浠诲姟琛ㄦ牸鏁版嵁
+ jobList: [],
+ // 寮瑰嚭灞傛爣棰�
+ title: "",
+ // 鏄惁鏄剧ず寮瑰嚭灞�
+ open: false,
+ // 鏄惁鏄剧ず璇︾粏寮瑰嚭灞�
+ openView: false,
+ // 浠诲姟缁勫悕瀛楀吀
+ jobGroupOptions: [],
+ // 鐘舵�佸瓧鍏�
+ statusOptions: [],
+ // 鏌ヨ鍙傛暟
+ 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" }
+ ]
+ }
+ };
+ },
+ created() {
+ this.getList();
+ this.getDicts("sys_job_group").then(response => {
+ this.jobGroupOptions = response.data;
+ });
+ this.getDicts("sys_job_status").then(response => {
+ this.statusOptions = response.data;
+ });
+ },
+ methods: {
+ /** 鏌ヨ瀹氭椂浠诲姟鍒楄〃 */
+ getList() {
+ this.loading = true;
+ listJob(this.queryParams).then(response => {
+ this.jobList = response.rows;
+ this.total = response.total;
+ this.loading = false;
+ });
+ },
+ // 浠诲姟缁勫悕瀛楀吀缈昏瘧
+ jobGroupFormat(row, column) {
+ return this.selectDictLabel(this.jobGroupOptions, row.jobGroup);
+ },
+ // 鐘舵�佸瓧鍏哥炕璇�
+ statusFormat(row, column) {
+ return this.selectDictLabel(this.statusOptions, row.status);
+ },
+ // 鍙栨秷鎸夐挳
+ cancel() {
+ this.open = false;
+ this.reset();
+ },
+ // 琛ㄥ崟閲嶇疆
+ reset() {
+ this.form = {
+ jobId: undefined,
+ jobName: undefined,
+ jobGroup: undefined,
+ invokeTarget: undefined,
+ cronExpression: undefined,
+ misfirePolicy: 1,
+ concurrent: 1,
+ 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;
+ },
+ // 浠诲姟鐘舵�佷慨鏀�
+ handleStatusChange(row) {
+ let text = row.status === "0" ? "鍚敤" : "鍋滅敤";
+ this.$confirm('纭瑕�"' + text + '""' + row.jobName + '"浠诲姟鍚�?', "璀﹀憡", {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning"
+ }).then(function() {
+ return changeJobStatus(row.jobId, row.status);
+ }).then(() => {
+ this.msgSuccess(text + "鎴愬姛");
+ }).catch(function() {
+ row.status = row.status === "0" ? "1" : "0";
+ });
+ },
+ /* 绔嬪嵆鎵ц涓�娆� */
+ handleRun(row) {
+ this.$confirm('纭瑕佺珛鍗虫墽琛屼竴娆�"' + row.jobName + '"浠诲姟鍚�?', "璀﹀憡", {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning"
+ }).then(function() {
+ return runJob(row.jobId, row.jobGroup);
+ }).then(function() {
+ this.msgSuccess("鎵ц鎴愬姛");
+ }).catch(function() {});
+ },
+ /** 浠诲姟璇︾粏淇℃伅 */
+ handleView(row) {
+ getJob(row.jobId).then(response => {
+ this.form = response.data;
+ this.openView = true;
+ });
+ },
+ /** 浠诲姟鏃ュ織鍒楄〃鏌ヨ */
+ handleJobLog() {
+ this.$router.push("/job/log");
+ },
+ /** 鏂板鎸夐挳鎿嶄綔 */
+ 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 => {
+ if (response.code === 200) {
+ this.msgSuccess("淇敼鎴愬姛");
+ this.open = false;
+ this.getList();
+ } else {
+ this.msgError(response.msg);
+ }
+ });
+ } else {
+ addJob(this.form).then(response => {
+ if (response.code === 200) {
+ this.msgSuccess("鏂板鎴愬姛");
+ this.open = false;
+ this.getList();
+ } else {
+ this.msgError(response.msg);
+ }
+ });
+ }
+ }
+ });
+ },
+ /** 鍒犻櫎鎸夐挳鎿嶄綔 */
+ handleDelete(row) {
+ const jobIds = row.jobId || this.ids;
+ this.$confirm('鏄惁纭鍒犻櫎瀹氭椂浠诲姟缂栧彿涓�"' + jobIds + '"鐨勬暟鎹」?', "璀﹀憡", {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning"
+ }).then(function() {
+ return delJob(jobIds);
+ }).then(() => {
+ this.getList();
+ this.msgSuccess("鍒犻櫎鎴愬姛");
+ }).catch(function() {});
+ },
+ /** 瀵煎嚭鎸夐挳鎿嶄綔 */
+ handleExport() {
+ const queryParams = this.queryParams;
+ this.$confirm("鏄惁纭瀵煎嚭鎵�鏈夊畾鏃朵换鍔℃暟鎹」?", "璀﹀憡", {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning"
+ }).then(function() {
+ return exportJob(queryParams);
+ }).then(response => {
+ this.download(response.msg);
+ }).catch(function() {});
+ }
+ }
+};
+</script>
\ No newline at end of file
diff --git a/ruoyi-ui/src/views/monitor/job/log.vue b/ruoyi-ui/src/views/monitor/job/log.vue
new file mode 100644
index 0000000..7346801
--- /dev/null
+++ b/ruoyi-ui/src/views/monitor/job/log.vue
@@ -0,0 +1,295 @@
+<template>
+ <div class="app-container">
+ <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
+ <el-form-item label="浠诲姟鍚嶇О" prop="jobName">
+ <el-input
+ v-model="queryParams.jobName"
+ placeholder="璇疯緭鍏ヤ换鍔″悕绉�"
+ clearable
+ size="small"
+ style="width: 240px"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="浠诲姟缁勫悕" prop="jobGroup">
+ <el-select
+ v-model="queryParams.jobGroup"
+ placeholder="璇蜂换鍔$粍鍚�"
+ clearable
+ size="small"
+ style="width: 240px"
+ >
+ <el-option
+ v-for="dict in jobGroupOptions"
+ :key="dict.dictValue"
+ :label="dict.dictLabel"
+ :value="dict.dictValue"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鎵ц鐘舵��" prop="status">
+ <el-select
+ v-model="queryParams.status"
+ placeholder="璇烽�夋嫨鎵ц鐘舵��"
+ clearable
+ size="small"
+ style="width: 240px"
+ >
+ <el-option
+ v-for="dict in statusOptions"
+ :key="dict.dictValue"
+ :label="dict.dictLabel"
+ :value="dict.dictValue"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鎵ц鏃堕棿">
+ <el-date-picker
+ v-model="dateRange"
+ size="small"
+ style="width: 240px"
+ value-format="yyyy-MM-dd"
+ type="daterange"
+ range-separator="-"
+ start-placeholder="寮�濮嬫棩鏈�"
+ end-placeholder="缁撴潫鏃ユ湡"
+ ></el-date-picker>
+ </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="danger"
+ 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="danger"
+ icon="el-icon-delete"
+ size="mini"
+ @click="handleClean"
+ v-hasPermi="['monitor:job:remove']"
+ >娓呯┖</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="warning"
+ icon="el-icon-download"
+ size="mini"
+ @click="handleExport"
+ v-hasPermi="['monitor:jobLog:export']"
+ >瀵煎嚭</el-button>
+ </el-col>
+ </el-row>
+
+ <el-table v-loading="loading" :data="jobLogList" @selection-change="handleSelectionChange">
+ <el-table-column type="selection" width="55" align="center" />
+ <el-table-column label="鏃ュ織缂栧彿" width="80" align="center" prop="jobLogId" />
+ <el-table-column label="浠诲姟鍚嶇О" align="center" prop="jobName" :show-overflow-tooltip="true" />
+ <el-table-column label="浠诲姟缁勫悕" align="center" prop="jobGroup" :formatter="jobGroupFormat" :show-overflow-tooltip="true" />
+ <el-table-column label="璋冪敤鐩爣瀛楃涓�" align="center" prop="invokeTarget" :show-overflow-tooltip="true" />
+ <el-table-column label="鏃ュ織淇℃伅" align="center" prop="jobMessage" :show-overflow-tooltip="true" />
+ <el-table-column label="鎵ц鐘舵��" align="center" prop="status" :formatter="statusFormat" />
+ <el-table-column label="鎵ц鏃堕棿" align="center" prop="createTime" width="180">
+ <template slot-scope="scope">
+ <span>{{ parseTime(scope.row.createTime) }}</span>
+ </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-view"
+ @click="handleView(scope.row)"
+ v-hasPermi="['monitor:job:query']"
+ >璇︾粏</el-button>
+ </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="璋冨害鏃ュ織璇︾粏" :visible.sync="open" width="700px">
+ <el-form ref="form" :model="form" label-width="100px" size="mini">
+ <el-row>
+ <el-col :span="12">
+ <el-form-item label="鏃ュ織搴忓彿锛�">{{ form.jobLogId }}</el-form-item>
+ <el-form-item label="浠诲姟鍚嶇О锛�">{{ form.jobName }}</el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="浠诲姟鍒嗙粍锛�">{{ form.jobGroup }}</el-form-item>
+ <el-form-item label="鎵ц鏃堕棿锛�">{{ form.createTime }}</el-form-item>
+ </el-col>
+ <el-col :span="24">
+ <el-form-item label="璋冪敤鏂规硶锛�">{{ form.invokeTarget }}</el-form-item>
+ </el-col>
+ <el-col :span="24">
+ <el-form-item label="鏃ュ織淇℃伅锛�">{{ form.jobMessage }}</el-form-item>
+ </el-col>
+ <el-col :span="24">
+ <el-form-item label="鎵ц鐘舵�侊細">
+ <div v-if="form.status == 0">姝e父</div>
+ <div v-else-if="form.status == 1">澶辫触</div>
+ </el-form-item>
+ </el-col>
+ <el-col :span="24">
+ <el-form-item label="寮傚父淇℃伅锛�" v-if="form.status == 1">{{ form.exceptionInfo }}</el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+ <div slot="footer" class="dialog-footer">
+ <el-button @click="open = false">鍏� 闂�</el-button>
+ </div>
+ </el-dialog>
+ </div>
+</template>
+
+<script>
+import { listJobLog, delJobLog, exportJobLog, cleanJobLog } from "@/api/monitor/jobLog";
+
+export default {
+ name: "JobLog",
+ data() {
+ return {
+ // 閬僵灞�
+ loading: true,
+ // 閫変腑鏁扮粍
+ ids: [],
+ // 闈炲涓鐢�
+ multiple: true,
+ // 鎬绘潯鏁�
+ total: 0,
+ // 璋冨害鏃ュ織琛ㄦ牸鏁版嵁
+ jobLogList: [],
+ // 鏄惁鏄剧ず寮瑰嚭灞�
+ open: false,
+ // 鏃ユ湡鑼冨洿
+ dateRange: [],
+ // 琛ㄥ崟鍙傛暟
+ form: {},
+ // 鎵ц鐘舵�佸瓧鍏�
+ statusOptions: [],
+ // 浠诲姟缁勫悕瀛楀吀
+ jobGroupOptions: [],
+ // 鏌ヨ鍙傛暟
+ queryParams: {
+ pageNum: 1,
+ pageSize: 10,
+ jobName: undefined,
+ jobGroup: undefined,
+ status: undefined
+ },
+ // 琛ㄥ崟鍙傛暟
+ form: {}
+ };
+ },
+ created() {
+ this.getList();
+ this.getDicts("sys_job_status").then(response => {
+ this.statusOptions = response.data;
+ });
+ this.getDicts("sys_job_group").then(response => {
+ this.jobGroupOptions = response.data;
+ });
+ },
+ methods: {
+ /** 鏌ヨ璋冨害鏃ュ織鍒楄〃 */
+ getList() {
+ this.loading = true;
+ listJobLog(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
+ this.jobLogList = response.rows;
+ this.total = response.total;
+ this.loading = false;
+ }
+ );
+ },
+ // 鎵ц鐘舵�佸瓧鍏哥炕璇�
+ statusFormat(row, column) {
+ return this.selectDictLabel(this.statusOptions, row.status);
+ },
+ // 浠诲姟缁勫悕瀛楀吀缈昏瘧
+ jobGroupFormat(row, column) {
+ return this.selectDictLabel(this.jobGroupOptions, row.jobGroup);
+ },
+ /** 鎼滅储鎸夐挳鎿嶄綔 */
+ handleQuery() {
+ this.queryParams.pageNum = 1;
+ this.getList();
+ },
+ /** 閲嶇疆鎸夐挳鎿嶄綔 */
+ resetQuery() {
+ this.dateRange = [];
+ this.resetForm("queryForm");
+ this.handleQuery();
+ },
+ // 澶氶�夋閫変腑鏁版嵁
+ handleSelectionChange(selection) {
+ this.ids = selection.map(item => item.jobLogId);
+ this.multiple = !selection.length;
+ },
+ /** 璇︾粏鎸夐挳鎿嶄綔 */
+ handleView(row) {
+ this.open = true;
+ this.form = row;
+ },
+ /** 鍒犻櫎鎸夐挳鎿嶄綔 */
+ handleDelete(row) {
+ const jobLogIds = this.ids;
+ this.$confirm('鏄惁纭鍒犻櫎璋冨害鏃ュ織缂栧彿涓�"' + jobLogIds + '"鐨勬暟鎹」?', "璀﹀憡", {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning"
+ }).then(function() {
+ return delJobLog(jobLogIds);
+ }).then(() => {
+ this.getList();
+ this.msgSuccess("鍒犻櫎鎴愬姛");
+ }).catch(function() {});
+ },
+ /** 娓呯┖鎸夐挳鎿嶄綔 */
+ handleClean() {
+ this.$confirm("鏄惁纭娓呯┖鎵�鏈夎皟搴︽棩蹇楁暟鎹」?", "璀﹀憡", {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning"
+ }).then(function() {
+ return cleanJobLog();
+ }).then(() => {
+ this.getList();
+ this.msgSuccess("娓呯┖鎴愬姛");
+ }).catch(function() {});
+ },
+ /** 瀵煎嚭鎸夐挳鎿嶄綔 */
+ handleExport() {
+ const queryParams = this.queryParams;
+ this.$confirm("鏄惁纭瀵煎嚭鎵�鏈夎皟搴︽棩蹇楁暟鎹」?", "璀﹀憡", {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning"
+ }).then(function() {
+ return exportJobLog(queryParams);
+ }).then(response => {
+ this.download(response.msg);
+ }).catch(function() {});
+ }
+ }
+};
+</script>
\ No newline at end of file
diff --git a/ruoyi/pom.xml b/ruoyi/pom.xml
index e32da8a..8cc8223 100644
--- a/ruoyi/pom.xml
+++ b/ruoyi/pom.xml
@@ -5,7 +5,7 @@
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId>
- <version>2.1.0</version>
+ <version>2.2.0</version>
<packaging>jar</packaging>
<name>ruoyi</name>
@@ -224,12 +224,25 @@
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
+
<!--velocity浠g爜鐢熸垚浣跨敤妯℃澘 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>${velocity.version}</version>
</dependency>
+
+ <!-- 瀹氭椂浠诲姟 -->
+ <dependency>
+ <groupId>org.quartz-scheduler</groupId>
+ <artifactId>quartz</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>com.mchange</groupId>
+ <artifactId>c3p0</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
</dependencies>
diff --git a/ruoyi/sql/quartz.sql b/ruoyi/sql/quartz.sql
new file mode 100644
index 0000000..55665e2
--- /dev/null
+++ b/ruoyi/sql/quartz.sql
@@ -0,0 +1,170 @@
+-- ----------------------------
+-- 1銆佸瓨鍌ㄦ瘡涓�涓凡閰嶇疆鐨� jobDetail 鐨勮缁嗕俊鎭�
+-- ----------------------------
+drop table if exists QRTZ_JOB_DETAILS;
+create table QRTZ_JOB_DETAILS (
+ sched_name varchar(120) not null,
+ job_name varchar(200) not null,
+ job_group varchar(200) not null,
+ description varchar(250) null,
+ job_class_name varchar(250) not null,
+ is_durable varchar(1) not null,
+ is_nonconcurrent varchar(1) not null,
+ is_update_data varchar(1) not null,
+ requests_recovery varchar(1) not null,
+ job_data blob null,
+ primary key (sched_name,job_name,job_group)
+) engine=innodb;
+
+-- ----------------------------
+-- 2銆� 瀛樺偍宸查厤缃殑 Trigger 鐨勪俊鎭�
+-- ----------------------------
+drop table if exists QRTZ_TRIGGERS;
+create table QRTZ_TRIGGERS (
+ sched_name varchar(120) not null,
+ trigger_name varchar(200) not null,
+ trigger_group varchar(200) not null,
+ job_name varchar(200) not null,
+ job_group varchar(200) not null,
+ description varchar(250) null,
+ next_fire_time bigint(13) null,
+ prev_fire_time bigint(13) null,
+ priority integer null,
+ trigger_state varchar(16) not null,
+ trigger_type varchar(8) not null,
+ start_time bigint(13) not null,
+ end_time bigint(13) null,
+ calendar_name varchar(200) null,
+ misfire_instr smallint(2) null,
+ job_data blob null,
+ primary key (sched_name,trigger_name,trigger_group),
+ foreign key (sched_name,job_name,job_group) references QRTZ_JOB_DETAILS(sched_name,job_name,job_group)
+) engine=innodb;
+
+-- ----------------------------
+-- 3銆� 瀛樺偍绠�鍗曠殑 Trigger锛屽寘鎷噸澶嶆鏁帮紝闂撮殧锛屼互鍙婂凡瑙﹀彂鐨勬鏁�
+-- ----------------------------
+drop table if exists QRTZ_SIMPLE_TRIGGERS;
+create table QRTZ_SIMPLE_TRIGGERS (
+ sched_name varchar(120) not null,
+ trigger_name varchar(200) not null,
+ trigger_group varchar(200) not null,
+ repeat_count bigint(7) not null,
+ repeat_interval bigint(12) not null,
+ times_triggered bigint(10) not null,
+ primary key (sched_name,trigger_name,trigger_group),
+ foreign key (sched_name,trigger_name,trigger_group) references QRTZ_TRIGGERS(sched_name,trigger_name,trigger_group)
+) engine=innodb;
+
+-- ----------------------------
+-- 4銆� 瀛樺偍 Cron Trigger锛屽寘鎷� Cron 琛ㄨ揪寮忓拰鏃跺尯淇℃伅
+-- ----------------------------
+drop table if exists QRTZ_CRON_TRIGGERS;
+create table QRTZ_CRON_TRIGGERS (
+ sched_name varchar(120) not null,
+ trigger_name varchar(200) not null,
+ trigger_group varchar(200) not null,
+ cron_expression varchar(200) not null,
+ time_zone_id varchar(80),
+ primary key (sched_name,trigger_name,trigger_group),
+ foreign key (sched_name,trigger_name,trigger_group) references QRTZ_TRIGGERS(sched_name,trigger_name,trigger_group)
+) engine=innodb;
+
+-- ----------------------------
+-- 5銆� Trigger 浣滀负 Blob 绫诲瀷瀛樺偍(鐢ㄤ簬 Quartz 鐢ㄦ埛鐢� JDBC 鍒涘缓浠栦滑鑷繁瀹氬埗鐨� Trigger 绫诲瀷锛孞obStore 骞朵笉鐭ラ亾濡備綍瀛樺偍瀹炰緥鐨勬椂鍊�)
+-- ----------------------------
+drop table if exists QRTZ_BLOB_TRIGGERS;
+create table QRTZ_BLOB_TRIGGERS (
+ sched_name varchar(120) not null,
+ trigger_name varchar(200) not null,
+ trigger_group varchar(200) not null,
+ blob_data blob null,
+ primary key (sched_name,trigger_name,trigger_group),
+ foreign key (sched_name,trigger_name,trigger_group) references QRTZ_TRIGGERS(sched_name,trigger_name,trigger_group)
+) engine=innodb;
+
+-- ----------------------------
+-- 6銆� 浠� Blob 绫诲瀷瀛樺偍瀛樻斁鏃ュ巻淇℃伅锛� quartz鍙厤缃竴涓棩鍘嗘潵鎸囧畾涓�涓椂闂磋寖鍥�
+-- ----------------------------
+drop table if exists QRTZ_CALENDARS;
+create table QRTZ_CALENDARS (
+ sched_name varchar(120) not null,
+ calendar_name varchar(200) not null,
+ calendar blob not null,
+ primary key (sched_name,calendar_name)
+) engine=innodb;
+
+-- ----------------------------
+-- 7銆� 瀛樺偍宸叉殏鍋滅殑 Trigger 缁勭殑淇℃伅
+-- ----------------------------
+drop table if exists QRTZ_PAUSED_TRIGGER_GRPS;
+create table QRTZ_PAUSED_TRIGGER_GRPS (
+ sched_name varchar(120) not null,
+ trigger_group varchar(200) not null,
+ primary key (sched_name,trigger_group)
+) engine=innodb;
+
+-- ----------------------------
+-- 8銆� 瀛樺偍涓庡凡瑙﹀彂鐨� Trigger 鐩稿叧鐨勭姸鎬佷俊鎭紝浠ュ強鐩歌仈 Job 鐨勬墽琛屼俊鎭�
+-- ----------------------------
+drop table if exists QRTZ_FIRED_TRIGGERS;
+create table QRTZ_FIRED_TRIGGERS (
+ sched_name varchar(120) not null,
+ entry_id varchar(95) not null,
+ trigger_name varchar(200) not null,
+ trigger_group varchar(200) not null,
+ instance_name varchar(200) not null,
+ fired_time bigint(13) not null,
+ sched_time bigint(13) not null,
+ priority integer not null,
+ state varchar(16) not null,
+ job_name varchar(200) null,
+ job_group varchar(200) null,
+ is_nonconcurrent varchar(1) null,
+ requests_recovery varchar(1) null,
+ primary key (sched_name,entry_id)
+) engine=innodb;
+
+-- ----------------------------
+-- 9銆� 瀛樺偍灏戦噺鐨勬湁鍏� Scheduler 鐨勭姸鎬佷俊鎭紝鍋囧鏄敤浜庨泦缇や腑锛屽彲浠ョ湅鍒板叾浠栫殑 Scheduler 瀹炰緥
+-- ----------------------------
+drop table if exists QRTZ_SCHEDULER_STATE;
+create table QRTZ_SCHEDULER_STATE (
+ sched_name varchar(120) not null,
+ instance_name varchar(200) not null,
+ last_checkin_time bigint(13) not null,
+ checkin_interval bigint(13) not null,
+ primary key (sched_name,instance_name)
+) engine=innodb;
+
+-- ----------------------------
+-- 10銆� 瀛樺偍绋嬪簭鐨勬偛瑙傞攣鐨勪俊鎭�(鍋囧浣跨敤浜嗘偛瑙傞攣)
+-- ----------------------------
+drop table if exists QRTZ_LOCKS;
+create table QRTZ_LOCKS (
+ sched_name varchar(120) not null,
+ lock_name varchar(40) not null,
+ primary key (sched_name,lock_name)
+) engine=innodb;
+
+drop table if exists QRTZ_SIMPROP_TRIGGERS;
+create table QRTZ_SIMPROP_TRIGGERS (
+ sched_name varchar(120) not null,
+ trigger_name varchar(200) not null,
+ trigger_group varchar(200) not null,
+ str_prop_1 varchar(512) null,
+ str_prop_2 varchar(512) null,
+ str_prop_3 varchar(512) null,
+ int_prop_1 int null,
+ int_prop_2 int null,
+ long_prop_1 bigint null,
+ long_prop_2 bigint null,
+ dec_prop_1 numeric(13,4) null,
+ dec_prop_2 numeric(13,4) null,
+ bool_prop_1 varchar(1) null,
+ bool_prop_2 varchar(1) null,
+ primary key (sched_name,trigger_name,trigger_group),
+ foreign key (sched_name,trigger_name,trigger_group) references QRTZ_TRIGGERS(sched_name,trigger_name,trigger_group)
+) engine=innodb;
+
+commit;
\ No newline at end of file
diff --git a/ruoyi/src/main/java/com/ruoyi/RuoYiApplication.java b/ruoyi/src/main/java/com/ruoyi/RuoYiApplication.java
index e3c56ee..f1ad924 100644
--- a/ruoyi/src/main/java/com/ruoyi/RuoYiApplication.java
+++ b/ruoyi/src/main/java/com/ruoyi/RuoYiApplication.java
@@ -14,7 +14,7 @@
{
public static void main(String[] args)
{
- // System.setProperty("spring.devtools.restart.enabled", "false");
+ System.setProperty("spring.devtools.restart.enabled", "false");
SpringApplication.run(RuoYiApplication.class, args);
System.out.println("(鈾モ棤鈥库棤)锞夛緸 鑻ヤ緷鍚姩鎴愬姛 醿�(麓凇`醿�)锞� \n" +
" .-------. ____ __ \n" +
diff --git a/ruoyi/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java b/ruoyi/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java
new file mode 100644
index 0000000..005e176
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java
@@ -0,0 +1,50 @@
+package com.ruoyi.common.constant;
+
+/**
+ * 浠诲姟璋冨害閫氱敤甯搁噺
+ *
+ * @author ruoyi
+ */
+public interface 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
+ {
+ /**
+ * 姝e父
+ */
+ NORMAL("0"),
+ /**
+ * 鏆傚仠
+ */
+ PAUSE("1");
+
+ private String value;
+
+ private Status(String value)
+ {
+ this.value = value;
+ }
+
+ public String getValue()
+ {
+ return value;
+ }
+ }
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/common/exception/job/TaskException.java b/ruoyi/src/main/java/com/ruoyi/common/exception/job/TaskException.java
new file mode 100644
index 0000000..a567b40
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/common/exception/job/TaskException.java
@@ -0,0 +1,34 @@
+package com.ruoyi.common.exception.job;
+
+/**
+ * 璁″垝绛栫暐寮傚父
+ *
+ * @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
+ }
+}
\ No newline at end of file
diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java b/ruoyi/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java
new file mode 100644
index 0000000..ad57439
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java
@@ -0,0 +1,40 @@
+package com.ruoyi.common.utils;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+
+/**
+ * 閿欒淇℃伅澶勭悊绫汇��
+ *
+ * @author ruoyi
+ */
+public class ExceptionUtil
+{
+ /**
+ * 鑾峰彇exception鐨勮缁嗛敊璇俊鎭��
+ */
+ public static String getExceptionMessage(Throwable e)
+ {
+ StringWriter sw = new StringWriter();
+ e.printStackTrace(new PrintWriter(sw, true));
+ String str = sw.toString();
+ return str;
+ }
+
+ public static String getRootErrorMseeage(Exception e)
+ {
+ Throwable root = ExceptionUtils.getRootCause(e);
+ root = (root == null ? e : root);
+ if (root == null)
+ {
+ return "";
+ }
+ String msg = root.getMessage();
+ if (msg == null)
+ {
+ return "null";
+ }
+ return StringUtils.defaultString(msg);
+ }
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java b/ruoyi/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java
new file mode 100644
index 0000000..4463662
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java
@@ -0,0 +1,110 @@
+package com.ruoyi.common.utils.bean;
+
+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();
+ }
+ }
+
+ /**
+ * 鑾峰彇瀵硅薄鐨剆etter鏂规硶銆�
+ *
+ * @param obj 瀵硅薄
+ * @return 瀵硅薄鐨剆etter鏂规硶鍒楄〃
+ */
+ 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;
+ }
+
+ /**
+ * 鑾峰彇瀵硅薄鐨刧etter鏂规硶銆�
+ *
+ * @param obj 瀵硅薄
+ * @return 瀵硅薄鐨刧etter鏂规硶鍒楄〃
+ */
+
+ 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;
+ }
+
+ /**
+ * 妫�鏌ean鏂规硶鍚嶄腑鐨勫睘鎬у悕鏄惁鐩哥瓑銆�<br>
+ * 濡俫etName()鍜宻etName()灞炴�у悕涓�鏍凤紝getName()鍜宻etAge()灞炴�у悕涓嶄竴鏍枫��
+ *
+ * @param m1 鏂规硶鍚�1
+ * @param m2 鏂规硶鍚�2
+ * @return 灞炴�у悕涓�鏍疯繑鍥瀟rue锛屽惁鍒欒繑鍥瀎alse
+ */
+
+ public static boolean isMethodPropEquals(String m1, String m2)
+ {
+ return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX));
+ }
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java b/ruoyi/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java
index e7f50b7..dcf1b8f 100644
--- a/ruoyi/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java
+++ b/ruoyi/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java
@@ -21,7 +21,6 @@
public static String getBodyString(ServletRequest request)
{
-
StringBuilder sb = new StringBuilder();
BufferedReader reader = null;
try (InputStream inputStream = request.getInputStream())
diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/job/AbstractQuartzJob.java b/ruoyi/src/main/java/com/ruoyi/common/utils/job/AbstractQuartzJob.java
new file mode 100644
index 0000000..ec7e622
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/common/utils/job/AbstractQuartzJob.java
@@ -0,0 +1,107 @@
+package com.ruoyi.common.utils.job;
+
+import java.util.Date;
+import org.quartz.Job;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.ruoyi.common.constant.Constants;
+import com.ruoyi.common.constant.ScheduleConstants;
+import com.ruoyi.common.utils.ExceptionUtil;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.bean.BeanUtils;
+import com.ruoyi.common.utils.spring.SpringUtils;
+import com.ruoyi.project.monitor.domain.SysJob;
+import com.ruoyi.project.monitor.domain.SysJobLog;
+import com.ruoyi.project.monitor.service.ISysJobLogService;
+
+/**
+ * 鎶借薄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 sysScheduleJob 绯荤粺璁″垝浠诲姟
+ */
+ 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.setStopTime(new Date());
+ long runMs = sysJobLog.getStopTime().getTime() - sysJobLog.getStartTime().getTime();
+ sysJobLog.setJobMessage(sysJobLog.getJobName() + " 鎬诲叡鑰楁椂锛�" + runMs + "姣");
+ if (e != null)
+ {
+ sysJobLog.setStatus(Constants.FAIL);
+ String errorMsg = StringUtils.substring(ExceptionUtil.getExceptionMessage(e), 0, 2000);
+ sysJobLog.setExceptionInfo(errorMsg);
+ }
+ else
+ {
+ sysJobLog.setStatus(Constants.SUCCESS);
+ }
+
+ // 鍐欏叆鏁版嵁搴撳綋涓�
+ SpringUtils.getBean(ISysJobLogService.class).addJobLog(sysJobLog);
+ }
+
+ /**
+ * 鎵ц鏂规硶锛岀敱瀛愮被閲嶈浇
+ *
+ * @param context 宸ヤ綔鎵ц涓婁笅鏂囧璞�
+ * @param sysJob 绯荤粺璁″垝浠诲姟
+ * @throws Exception 鎵ц杩囩▼涓殑寮傚父
+ */
+ protected abstract void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception;
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/job/CronUtils.java b/ruoyi/src/main/java/com/ruoyi/common/utils/job/CronUtils.java
new file mode 100644
index 0000000..0763a8d
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/common/utils/job/CronUtils.java
@@ -0,0 +1,63 @@
+package com.ruoyi.common.utils.job;
+
+import java.text.ParseException;
+import java.util.Date;
+import org.quartz.CronExpression;
+
+/**
+ * cron琛ㄨ揪寮忓伐鍏风被
+ *
+ * @author ruoyi
+ *
+ */
+public class CronUtils
+{
+ /**
+ * 杩斿洖涓�涓竷灏斿�间唬琛ㄤ竴涓粰瀹氱殑Cron琛ㄨ揪寮忕殑鏈夋晥鎬�
+ *
+ * @param cronExpression Cron琛ㄨ揪寮�
+ * @return boolean 琛ㄨ揪寮忔槸鍚︽湁鏁�
+ */
+ public static boolean isValid(String cronExpression)
+ {
+ return CronExpression.isValidExpression(cronExpression);
+ }
+
+ /**
+ * 杩斿洖涓�涓瓧绗︿覆鍊�,琛ㄧず璇ユ秷鎭棤鏁圕ron琛ㄨ揪寮忕粰鍑烘湁鏁堟��
+ *
+ * @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());
+ }
+ }
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/job/JobInvokeUtil.java b/ruoyi/src/main/java/com/ruoyi/common/utils/job/JobInvokeUtil.java
new file mode 100644
index 0000000..1670ee5
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/common/utils/job/JobInvokeUtil.java
@@ -0,0 +1,182 @@
+package com.ruoyi.common.utils.job;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.LinkedList;
+import java.util.List;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.spring.SpringUtils;
+import com.ruoyi.project.monitor.domain.SysJob;
+
+/**
+ * 浠诲姟鎵ц宸ュ叿
+ *
+ * @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 = SpringUtils.getBean(beanName);
+ invokeMethod(bean, methodName, methodParams);
+ }
+ else
+ {
+ Object bean = Class.forName(beanName).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 (StringUtils.isNotNull(methodParams) && methodParams.size() > 0)
+ {
+ Method method = bean.getClass().getDeclaredMethod(methodName, getMethodParamsType(methodParams));
+ method.invoke(bean, getMethodParamsValue(methodParams));
+ }
+ else
+ {
+ Method method = bean.getClass().getDeclaredMethod(methodName);
+ method.invoke(bean);
+ }
+ }
+
+ /**
+ * 鏍¢獙鏄惁涓轰负class鍖呭悕
+ *
+ * @param str 鍚嶇О
+ * @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.contains(str, "'"))
+ {
+ classs.add(new Object[] { StringUtils.replace(str, "'", ""), String.class });
+ }
+ // boolean甯冨皵绫诲瀷锛岀瓑浜巘rue鎴栬�協alse
+ else if (StringUtils.equals(str, "true") || StringUtils.equalsIgnoreCase(str, "false"))
+ {
+ classs.add(new Object[] { Boolean.valueOf(str), Boolean.class });
+ }
+ // long闀挎暣褰紝鍖呭惈L
+ else if (StringUtils.containsIgnoreCase(str, "L"))
+ {
+ classs.add(new Object[] { Long.valueOf(StringUtils.replaceIgnoreCase(str, "L", "")), Long.class });
+ }
+ // double娴偣绫诲瀷锛屽寘鍚獶
+ else if (StringUtils.containsIgnoreCase(str, "D"))
+ {
+ classs.add(new Object[] { Double.valueOf(StringUtils.replaceIgnoreCase(str, "D", "")), 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;
+ }
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/job/QuartzDisallowConcurrentExecution.java b/ruoyi/src/main/java/com/ruoyi/common/utils/job/QuartzDisallowConcurrentExecution.java
new file mode 100644
index 0000000..7097088
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/common/utils/job/QuartzDisallowConcurrentExecution.java
@@ -0,0 +1,21 @@
+package com.ruoyi.common.utils.job;
+
+import org.quartz.DisallowConcurrentExecution;
+import org.quartz.JobExecutionContext;
+import com.ruoyi.project.monitor.domain.SysJob;
+
+/**
+ * 瀹氭椂浠诲姟澶勭悊锛堢姝㈠苟鍙戞墽琛岋級
+ *
+ * @author ruoyi
+ *
+ */
+@DisallowConcurrentExecution
+public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob
+{
+ @Override
+ protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception
+ {
+ JobInvokeUtil.invokeMethod(sysJob);
+ }
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/job/QuartzJobExecution.java b/ruoyi/src/main/java/com/ruoyi/common/utils/job/QuartzJobExecution.java
new file mode 100644
index 0000000..55aa7e0
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/common/utils/job/QuartzJobExecution.java
@@ -0,0 +1,19 @@
+package com.ruoyi.common.utils.job;
+
+import org.quartz.JobExecutionContext;
+import com.ruoyi.project.monitor.domain.SysJob;
+
+/**
+ * 瀹氭椂浠诲姟澶勭悊锛堝厑璁稿苟鍙戞墽琛岋級
+ *
+ * @author ruoyi
+ *
+ */
+public class QuartzJobExecution extends AbstractQuartzJob
+{
+ @Override
+ protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception
+ {
+ JobInvokeUtil.invokeMethod(sysJob);
+ }
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/job/ScheduleUtils.java b/ruoyi/src/main/java/com/ruoyi/common/utils/job/ScheduleUtils.java
new file mode 100644
index 0000000..3e107c6
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/common/utils/job/ScheduleUtils.java
@@ -0,0 +1,113 @@
+package com.ruoyi.common.utils.job;
+
+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.ruoyi.common.constant.ScheduleConstants;
+import com.ruoyi.common.exception.job.TaskException;
+import com.ruoyi.common.exception.job.TaskException.Code;
+import com.ruoyi.project.monitor.domain.SysJob;
+
+/**
+ * 瀹氭椂浠诲姟宸ュ叿绫�
+ *
+ * @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);
+
+ // 鎸夋柊鐨刢ronExpression琛ㄨ揪寮忔瀯寤轰竴涓柊鐨則rigger
+ 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));
+ }
+
+ 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/ruoyi/src/main/java/com/ruoyi/framework/config/ScheduleConfig.java b/ruoyi/src/main/java/com/ruoyi/framework/config/ScheduleConfig.java
new file mode 100644
index 0000000..a607e85
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/framework/config/ScheduleConfig.java
@@ -0,0 +1,57 @@
+package com.ruoyi.framework.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.quartz.SchedulerFactoryBean;
+import javax.sql.DataSource;
+import java.util.Properties;
+
+/**
+ * 瀹氭椂浠诲姟閰嶇疆
+ *
+ * @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.quartz.impl.jdbcjobstore.JobStoreTX");
+ // 闆嗙兢閰嶇疆
+ prop.put("org.quartz.jobStore.isClustered", "true");
+ prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
+ prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
+ 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
+ // 鍚姩鏃舵洿鏂板繁瀛樺湪鐨凧ob锛岃繖鏍峰氨涓嶇敤姣忔淇敼targetObject鍚庡垹闄rtz_job_details琛ㄥ搴旇褰曚簡
+ factory.setOverwriteExistingJobs(true);
+ // 璁剧疆鑷姩鍚姩锛岄粯璁や负true
+ factory.setAutoStartup(true);
+
+ return factory;
+ }
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/ruoyi/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
index 4e0eca4..b27b165 100644
--- a/ruoyi/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
+++ b/ruoyi/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
@@ -99,6 +99,7 @@
).permitAll()
.antMatchers("/profile/**").anonymous()
.antMatchers("/common/download**").anonymous()
+ .antMatchers("/common/download/resource**").anonymous()
.antMatchers("/swagger-ui.html").anonymous()
.antMatchers("/swagger-resources/**").anonymous()
.antMatchers("/webjars/**").anonymous()
diff --git a/ruoyi/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java b/ruoyi/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java
index fd5fa19..05f59ee 100644
--- a/ruoyi/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java
+++ b/ruoyi/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java
@@ -59,7 +59,7 @@
nowDataMap.put(REPEAT_PARAMS, nowParams);
nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());
- // 璇锋眰鍦板潃锛堜綔涓哄瓨鏀緎ession鐨刱ey鍊硷級
+ // 璇锋眰鍦板潃锛堜綔涓哄瓨鏀綾ache鐨刱ey鍊硷級
String url = request.getRequestURI();
Object sessionObj = redisCache.getCacheObject(CACHE_REPEAT_KEY);
diff --git a/ruoyi/src/main/java/com/ruoyi/framework/task/RyTask.java b/ruoyi/src/main/java/com/ruoyi/framework/task/RyTask.java
new file mode 100644
index 0000000..eb7f5b9
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/framework/task/RyTask.java
@@ -0,0 +1,28 @@
+package com.ruoyi.framework.task;
+
+import org.springframework.stereotype.Component;
+import com.ruoyi.common.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/ruoyi/src/main/java/com/ruoyi/framework/web/domain/Server.java b/ruoyi/src/main/java/com/ruoyi/framework/web/domain/Server.java
index 79e174b..93e411b 100644
--- a/ruoyi/src/main/java/com/ruoyi/framework/web/domain/Server.java
+++ b/ruoyi/src/main/java/com/ruoyi/framework/web/domain/Server.java
@@ -28,7 +28,6 @@
*/
public class Server
{
-
private static final int OSHI_WAIT_SECOND = 1000;
/**
diff --git a/ruoyi/src/main/java/com/ruoyi/project/common/CommonController.java b/ruoyi/src/main/java/com/ruoyi/project/common/CommonController.java
index 3bb81d0..b59fb29 100644
--- a/ruoyi/src/main/java/com/ruoyi/project/common/CommonController.java
+++ b/ruoyi/src/main/java/com/ruoyi/project/common/CommonController.java
@@ -9,6 +9,7 @@
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
+import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.file.FileUtils;
@@ -86,4 +87,23 @@
return AjaxResult.error(e.getMessage());
}
}
+
+ /**
+ * 鏈湴璧勬簮閫氱敤涓嬭浇
+ */
+ @GetMapping("/common/download/resource")
+ public void resourceDownload(String name, HttpServletRequest request, HttpServletResponse response) throws Exception
+ {
+ // 鏈湴璧勬簮璺緞
+ String localPath = RuoYiConfig.getProfile();
+ // 鏁版嵁搴撹祫婧愬湴鍧�
+ String downloadPath = localPath + StringUtils.substringAfter(name, Constants.RESOURCE_PREFIX);
+ // 涓嬭浇鍚嶇О
+ String downloadName = StringUtils.substringAfterLast(downloadPath, "/");
+ response.setCharacterEncoding("utf-8");
+ response.setContentType("multipart/form-data");
+ response.setHeader("Content-Disposition",
+ "attachment;fileName=" + FileUtils.setFileDownloadHeader(request, downloadName));
+ FileUtils.writeBytes(downloadPath, response.getOutputStream());
+ }
}
diff --git a/ruoyi/src/main/java/com/ruoyi/project/monitor/controller/SysJobController.java b/ruoyi/src/main/java/com/ruoyi/project/monitor/controller/SysJobController.java
new file mode 100644
index 0000000..9894cd8
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/project/monitor/controller/SysJobController.java
@@ -0,0 +1,130 @@
+package com.ruoyi.project.monitor.controller;
+
+import java.util.List;
+import org.quartz.SchedulerException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.ruoyi.common.exception.job.TaskException;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.controller.BaseController;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.framework.web.page.TableDataInfo;
+import com.ruoyi.project.monitor.domain.SysJob;
+import com.ruoyi.project.monitor.service.ISysJobService;
+
+/**
+ * 璋冨害浠诲姟淇℃伅鎿嶄綔澶勭悊
+ *
+ * @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)
+ @GetMapping("/export")
+ public AjaxResult export(SysJob sysJob)
+ {
+ List<SysJob> list = jobService.selectJobList(sysJob);
+ ExcelUtil<SysJob> util = new ExcelUtil<SysJob>(SysJob.class);
+ return util.exportExcel(list, "瀹氭椂浠诲姟");
+ }
+
+ /**
+ * 鑾峰彇瀹氭椂浠诲姟璇︾粏淇℃伅
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:query')")
+ @GetMapping(value = "/{jobId}")
+ public AjaxResult getInfo(@PathVariable("jobId") Long jobId)
+ {
+ return AjaxResult.success(jobService.selectJobById(jobId));
+ }
+
+ /**
+ * 鏂板瀹氭椂浠诲姟
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:add')")
+ @Log(title = "瀹氭椂浠诲姟", businessType = BusinessType.INSERT)
+ @PostMapping
+ public AjaxResult add(@RequestBody SysJob sysJob) throws SchedulerException, TaskException
+ {
+ return toAjax(jobService.insertJob(sysJob));
+ }
+
+ /**
+ * 淇敼瀹氭椂浠诲姟
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:edit')")
+ @Log(title = "瀹氭椂浠诲姟", businessType = BusinessType.UPDATE)
+ @PutMapping
+ public AjaxResult edit(@RequestBody SysJob sysJob) throws SchedulerException, TaskException
+ {
+ return toAjax(jobService.updateJob(sysJob));
+ }
+
+ /**
+ * 瀹氭椂浠诲姟鐘舵�佷慨鏀�
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')")
+ @Log(title = "瀹氭椂浠诲姟", businessType = BusinessType.UPDATE)
+ @PutMapping("/changeStatus")
+ public AjaxResult changeStatus(@RequestBody SysJob job) throws SchedulerException
+ {
+ SysJob newJob = jobService.selectJobById(job.getJobId());
+ 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) throws SchedulerException
+ {
+ jobService.run(job);
+ return AjaxResult.success();
+ }
+
+ /**
+ * 鍒犻櫎瀹氭椂浠诲姟
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
+ @Log(title = "瀹氭椂浠诲姟", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{jobIds}")
+ public AjaxResult remove(@PathVariable Long[] jobIds) throws SchedulerException, TaskException
+ {
+ jobService.deleteJobByIds(jobIds);
+ return AjaxResult.success();
+ }
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/project/monitor/controller/SysJobLogController.java b/ruoyi/src/main/java/com/ruoyi/project/monitor/controller/SysJobLogController.java
new file mode 100644
index 0000000..dc287de
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/project/monitor/controller/SysJobLogController.java
@@ -0,0 +1,87 @@
+package com.ruoyi.project.monitor.controller;
+
+import java.util.List;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.project.monitor.domain.SysJobLog;
+import com.ruoyi.project.monitor.service.ISysJobLogService;
+import com.ruoyi.framework.web.controller.BaseController;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.framework.web.page.TableDataInfo;
+
+/**
+ * 璋冨害鏃ュ織鎿嶄綔澶勭悊
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/monitor/jobLog")
+public class SysJobLogController extends BaseController
+{
+ @Autowired
+ private ISysJobLogService jobLogService;
+
+ /**
+ * 鏌ヨ瀹氭椂浠诲姟璋冨害鏃ュ織鍒楄〃
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:list')")
+ @GetMapping("/list")
+ public TableDataInfo list(SysJobLog sysJobLog)
+ {
+ startPage();
+ List<SysJobLog> list = jobLogService.selectJobLogList(sysJobLog);
+ return getDataTable(list);
+ }
+
+ /**
+ * 瀵煎嚭瀹氭椂浠诲姟璋冨害鏃ュ織鍒楄〃
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:export')")
+ @Log(title = "浠诲姟璋冨害鏃ュ織", businessType = BusinessType.EXPORT)
+ @GetMapping("/export")
+ public AjaxResult export(SysJobLog sysJobLog)
+ {
+ List<SysJobLog> list = jobLogService.selectJobLogList(sysJobLog);
+ ExcelUtil<SysJobLog> util = new ExcelUtil<SysJobLog>(SysJobLog.class);
+ return util.exportExcel(list, "璋冨害鏃ュ織");
+ }
+
+ /**
+ * 鏍规嵁璋冨害缂栧彿鑾峰彇璇︾粏淇℃伅
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:query')")
+ @GetMapping(value = "/{configId}")
+ public AjaxResult getInfo(@PathVariable Long jobLogId)
+ {
+ return AjaxResult.success(jobLogService.selectJobLogById(jobLogId));
+ }
+
+
+ /**
+ * 鍒犻櫎瀹氭椂浠诲姟璋冨害鏃ュ織
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
+ @Log(title = "瀹氭椂浠诲姟璋冨害鏃ュ織", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{jobLogIds}")
+ public AjaxResult remove(@PathVariable Long[] jobLogIds)
+ {
+ return toAjax(jobLogService.deleteJobLogByIds(jobLogIds));
+ }
+
+ @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
+ @Log(title = "璋冨害鏃ュ織", businessType = BusinessType.CLEAN)
+ @DeleteMapping("/clean")
+ public AjaxResult clean()
+ {
+ jobLogService.cleanJobLog();
+ return AjaxResult.success();
+ }
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/project/monitor/domain/SysJob.java b/ruoyi/src/main/java/com/ruoyi/project/monitor/domain/SysJob.java
new file mode 100644
index 0000000..850543a
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/project/monitor/domain/SysJob.java
@@ -0,0 +1,170 @@
+package com.ruoyi.project.monitor.domain;
+
+import java.io.Serializable;
+import java.util.Date;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.common.constant.ScheduleConstants;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.job.CronUtils;
+import com.ruoyi.framework.aspectj.lang.annotation.Excel;
+import com.ruoyi.framework.aspectj.lang.annotation.Excel.ColumnType;
+import com.ruoyi.framework.web.domain.BaseEntity;
+
+/**
+ * 瀹氭椂浠诲姟璋冨害琛� sys_job
+ *
+ * @author ruoyi
+ */
+public class SysJob extends BaseEntity implements Serializable
+{
+ 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 = ScheduleConstants.MISFIRE_DEFAULT;
+
+ /** 鏄惁骞跺彂鎵ц锛�0鍏佽 1绂佹锛� */
+ @Excel(name = "骞跺彂鎵ц", readConverterExp = "0=鍏佽,1=绂佹")
+ private String concurrent;
+
+ /** 浠诲姟鐘舵�侊紙0姝e父 1鏆傚仠锛� */
+ @Excel(name = "浠诲姟鐘舵��", readConverterExp = "0=姝e父,1=鏆傚仠")
+ private String status;
+
+ public Long getJobId()
+ {
+ return jobId;
+ }
+
+ public void setJobId(Long jobId)
+ {
+ this.jobId = jobId;
+ }
+
+ @NotBlank(message = "浠诲姟鍚嶇О涓嶈兘涓虹┖")
+ @Size(min = 0, max = 64, message = "浠诲姟鍚嶇О涓嶈兘瓒呰繃64涓瓧绗�")
+ public String getJobName()
+ {
+ return jobName;
+ }
+
+ public void setJobName(String jobName)
+ {
+ this.jobName = jobName;
+ }
+
+ public String getJobGroup()
+ {
+ return jobGroup;
+ }
+
+ public void setJobGroup(String jobGroup)
+ {
+ this.jobGroup = jobGroup;
+ }
+
+ @NotBlank(message = "璋冪敤鐩爣瀛楃涓蹭笉鑳戒负绌�")
+ @Size(min = 0, max = 1000, message = "璋冪敤鐩爣瀛楃涓查暱搴︿笉鑳借秴杩�500涓瓧绗�")
+ public String getInvokeTarget()
+ {
+ return invokeTarget;
+ }
+
+ public void setInvokeTarget(String invokeTarget)
+ {
+ this.invokeTarget = invokeTarget;
+ }
+
+ @NotBlank(message = "Cron鎵ц琛ㄨ揪寮忎笉鑳戒负绌�")
+ @Size(min = 0, max = 255, message = "Cron鎵ц琛ㄨ揪寮忎笉鑳借秴杩�255涓瓧绗�")
+ public String getCronExpression()
+ {
+ return cronExpression;
+ }
+
+ public void setCronExpression(String cronExpression)
+ {
+ this.cronExpression = cronExpression;
+ }
+
+ public Date getNextValidTime()
+ {
+ if (StringUtils.isNotEmpty(cronExpression))
+ {
+ return CronUtils.getNextExecution(cronExpression);
+ }
+ return null;
+ }
+
+ public String getMisfirePolicy()
+ {
+ return misfirePolicy;
+ }
+
+ public void setMisfirePolicy(String misfirePolicy)
+ {
+ this.misfirePolicy = misfirePolicy;
+ }
+
+ public String getConcurrent()
+ {
+ return concurrent;
+ }
+
+ public void setConcurrent(String concurrent)
+ {
+ this.concurrent = concurrent;
+ }
+
+ public String getStatus()
+ {
+ return status;
+ }
+
+ public void setStatus(String status)
+ {
+ this.status = status;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("jobId", getJobId())
+ .append("jobName", getJobName())
+ .append("jobGroup", getJobGroup())
+ .append("cronExpression", getCronExpression())
+ .append("nextValidTime", getNextValidTime())
+ .append("misfirePolicy", getMisfirePolicy())
+ .append("concurrent", getConcurrent())
+ .append("status", getStatus())
+ .append("createBy", getCreateBy())
+ .append("createTime", getCreateTime())
+ .append("updateBy", getUpdateBy())
+ .append("updateTime", getUpdateTime())
+ .append("remark", getRemark())
+ .toString();
+ }
+}
\ No newline at end of file
diff --git a/ruoyi/src/main/java/com/ruoyi/project/monitor/domain/SysJobLog.java b/ruoyi/src/main/java/com/ruoyi/project/monitor/domain/SysJobLog.java
new file mode 100644
index 0000000..c7a3cd2
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/project/monitor/domain/SysJobLog.java
@@ -0,0 +1,155 @@
+package com.ruoyi.project.monitor.domain;
+
+import java.util.Date;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.framework.aspectj.lang.annotation.Excel;
+import com.ruoyi.framework.web.domain.BaseEntity;
+
+/**
+ * 瀹氭椂浠诲姟璋冨害鏃ュ織琛� sys_job_log
+ *
+ * @author ruoyi
+ */
+public class SysJobLog extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+ /** ID */
+ @Excel(name = "鏃ュ織搴忓彿")
+ private Long jobLogId;
+
+ /** 浠诲姟鍚嶇О */
+ @Excel(name = "浠诲姟鍚嶇О")
+ private String jobName;
+
+ /** 浠诲姟缁勫悕 */
+ @Excel(name = "浠诲姟缁勫悕")
+ private String jobGroup;
+
+ /** 璋冪敤鐩爣瀛楃涓� */
+ @Excel(name = "璋冪敤鐩爣瀛楃涓�")
+ private String invokeTarget;
+
+ /** 鏃ュ織淇℃伅 */
+ @Excel(name = "鏃ュ織淇℃伅")
+ private String jobMessage;
+
+ /** 鎵ц鐘舵�侊紙0姝e父 1澶辫触锛� */
+ @Excel(name = "鎵ц鐘舵��", readConverterExp = "0=姝e父,1=澶辫触")
+ private String status;
+
+ /** 寮傚父淇℃伅 */
+ @Excel(name = "寮傚父淇℃伅")
+ private String exceptionInfo;
+
+ /** 寮�濮嬫椂闂� */
+ private Date startTime;
+
+ /** 鍋滄鏃堕棿 */
+ private Date stopTime;
+
+ public Long getJobLogId()
+ {
+ return jobLogId;
+ }
+
+ public void setJobLogId(Long jobLogId)
+ {
+ this.jobLogId = jobLogId;
+ }
+
+ public String getJobName()
+ {
+ return jobName;
+ }
+
+ public void setJobName(String jobName)
+ {
+ this.jobName = jobName;
+ }
+
+ public String getJobGroup()
+ {
+ return jobGroup;
+ }
+
+ public void setJobGroup(String jobGroup)
+ {
+ this.jobGroup = jobGroup;
+ }
+
+ public String getInvokeTarget()
+ {
+ return invokeTarget;
+ }
+
+ public void setInvokeTarget(String invokeTarget)
+ {
+ this.invokeTarget = invokeTarget;
+ }
+
+ public String getJobMessage()
+ {
+ return jobMessage;
+ }
+
+ public void setJobMessage(String jobMessage)
+ {
+ this.jobMessage = jobMessage;
+ }
+
+ public String getStatus()
+ {
+ return status;
+ }
+
+ public void setStatus(String status)
+ {
+ this.status = status;
+ }
+
+ public String getExceptionInfo()
+ {
+ return exceptionInfo;
+ }
+
+ public void setExceptionInfo(String exceptionInfo)
+ {
+ this.exceptionInfo = exceptionInfo;
+ }
+
+ public Date getStartTime()
+ {
+ return startTime;
+ }
+
+ public void setStartTime(Date startTime)
+ {
+ this.startTime = startTime;
+ }
+
+ public Date getStopTime()
+ {
+ return stopTime;
+ }
+
+ public void setStopTime(Date stopTime)
+ {
+ this.stopTime = stopTime;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("jobLogId", getJobLogId())
+ .append("jobName", getJobName())
+ .append("jobGroup", getJobGroup())
+ .append("jobMessage", getJobMessage())
+ .append("status", getStatus())
+ .append("exceptionInfo", getExceptionInfo())
+ .append("startTime", getStartTime())
+ .append("stopTime", getStopTime())
+ .toString();
+ }
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/project/monitor/mapper/SysJobLogMapper.java b/ruoyi/src/main/java/com/ruoyi/project/monitor/mapper/SysJobLogMapper.java
new file mode 100644
index 0000000..39137dd
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/project/monitor/mapper/SysJobLogMapper.java
@@ -0,0 +1,64 @@
+package com.ruoyi.project.monitor.mapper;
+
+import java.util.List;
+import com.ruoyi.project.monitor.domain.SysJobLog;
+
+/**
+ * 璋冨害浠诲姟鏃ュ織淇℃伅 鏁版嵁灞�
+ *
+ * @author ruoyi
+ */
+public interface SysJobLogMapper
+{
+ /**
+ * 鑾峰彇quartz璋冨害鍣ㄦ棩蹇楃殑璁″垝浠诲姟
+ *
+ * @param jobLog 璋冨害鏃ュ織淇℃伅
+ * @return 璋冨害浠诲姟鏃ュ織闆嗗悎
+ */
+ public List<SysJobLog> selectJobLogList(SysJobLog jobLog);
+
+ /**
+ * 鏌ヨ鎵�鏈夎皟搴︿换鍔℃棩蹇�
+ *
+ * @return 璋冨害浠诲姟鏃ュ織鍒楄〃
+ */
+ public List<SysJobLog> selectJobLogAll();
+
+ /**
+ * 閫氳繃璋冨害浠诲姟鏃ュ織ID鏌ヨ璋冨害淇℃伅
+ *
+ * @param jobLogId 璋冨害浠诲姟鏃ュ織ID
+ * @return 璋冨害浠诲姟鏃ュ織瀵硅薄淇℃伅
+ */
+ public SysJobLog selectJobLogById(Long jobLogId);
+
+ /**
+ * 鏂板浠诲姟鏃ュ織
+ *
+ * @param jobLog 璋冨害鏃ュ織淇℃伅
+ * @return 缁撴灉
+ */
+ public int insertJobLog(SysJobLog jobLog);
+
+ /**
+ * 鎵归噺鍒犻櫎璋冨害鏃ュ織淇℃伅
+ *
+ * @param logIds 闇�瑕佸垹闄ょ殑鏁版嵁ID
+ * @return 缁撴灉
+ */
+ public int deleteJobLogByIds(Long[] logIds);
+
+ /**
+ * 鍒犻櫎浠诲姟鏃ュ織
+ *
+ * @param jobId 璋冨害鏃ュ織ID
+ * @return 缁撴灉
+ */
+ public int deleteJobLogById(Long jobId);
+
+ /**
+ * 娓呯┖浠诲姟鏃ュ織
+ */
+ public void cleanJobLog();
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/project/monitor/mapper/SysJobMapper.java b/ruoyi/src/main/java/com/ruoyi/project/monitor/mapper/SysJobMapper.java
new file mode 100644
index 0000000..ab11461
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/project/monitor/mapper/SysJobMapper.java
@@ -0,0 +1,67 @@
+package com.ruoyi.project.monitor.mapper;
+
+import java.util.List;
+import com.ruoyi.project.monitor.domain.SysJob;
+
+/**
+ * 璋冨害浠诲姟淇℃伅 鏁版嵁灞�
+ *
+ * @author ruoyi
+ */
+public interface SysJobMapper
+{
+ /**
+ * 鏌ヨ璋冨害浠诲姟鏃ュ織闆嗗悎
+ *
+ * @param job 璋冨害淇℃伅
+ * @return 鎿嶄綔鏃ュ織闆嗗悎
+ */
+ public List<SysJob> selectJobList(SysJob job);
+
+ /**
+ * 鏌ヨ鎵�鏈夎皟搴︿换鍔�
+ *
+ * @return 璋冨害浠诲姟鍒楄〃
+ */
+ public List<SysJob> selectJobAll();
+
+ /**
+ * 閫氳繃璋冨害ID鏌ヨ璋冨害浠诲姟淇℃伅
+ *
+ * @param jobId 璋冨害ID
+ * @return 瑙掕壊瀵硅薄淇℃伅
+ */
+ public SysJob selectJobById(Long jobId);
+
+ /**
+ * 閫氳繃璋冨害ID鍒犻櫎璋冨害浠诲姟淇℃伅
+ *
+ * @param jobId 璋冨害ID
+ * @return 缁撴灉
+ */
+ public int deleteJobById(Long jobId);
+
+ /**
+ * 鎵归噺鍒犻櫎璋冨害浠诲姟淇℃伅
+ *
+ * @param ids 闇�瑕佸垹闄ょ殑鏁版嵁ID
+ * @return 缁撴灉
+ */
+ public int deleteJobByIds(Long[] ids);
+
+ /**
+ * 淇敼璋冨害浠诲姟淇℃伅
+ *
+ * @param job 璋冨害浠诲姟淇℃伅
+ * @return 缁撴灉
+ */
+ public int updateJob(SysJob job);
+
+ /**
+ * 鏂板璋冨害浠诲姟淇℃伅
+ *
+ * @param job 璋冨害浠诲姟淇℃伅
+ * @return 缁撴灉
+ */
+ public int insertJob(SysJob job);
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/project/monitor/service/ISysJobLogService.java b/ruoyi/src/main/java/com/ruoyi/project/monitor/service/ISysJobLogService.java
new file mode 100644
index 0000000..85561e7
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/project/monitor/service/ISysJobLogService.java
@@ -0,0 +1,56 @@
+package com.ruoyi.project.monitor.service;
+
+import java.util.List;
+import com.ruoyi.project.monitor.domain.SysJobLog;
+
+/**
+ * 瀹氭椂浠诲姟璋冨害鏃ュ織淇℃伅淇℃伅 鏈嶅姟灞�
+ *
+ * @author ruoyi
+ */
+public interface ISysJobLogService
+{
+ /**
+ * 鑾峰彇quartz璋冨害鍣ㄦ棩蹇楃殑璁″垝浠诲姟
+ *
+ * @param jobLog 璋冨害鏃ュ織淇℃伅
+ * @return 璋冨害浠诲姟鏃ュ織闆嗗悎
+ */
+ public List<SysJobLog> selectJobLogList(SysJobLog jobLog);
+
+ /**
+ * 閫氳繃璋冨害浠诲姟鏃ュ織ID鏌ヨ璋冨害淇℃伅
+ *
+ * @param jobLogId 璋冨害浠诲姟鏃ュ織ID
+ * @return 璋冨害浠诲姟鏃ュ織瀵硅薄淇℃伅
+ */
+ public SysJobLog selectJobLogById(Long jobLogId);
+
+ /**
+ * 鏂板浠诲姟鏃ュ織
+ *
+ * @param jobLog 璋冨害鏃ュ織淇℃伅
+ */
+ public void addJobLog(SysJobLog jobLog);
+
+ /**
+ * 鎵归噺鍒犻櫎璋冨害鏃ュ織淇℃伅
+ *
+ * @param logIds 闇�瑕佸垹闄ょ殑鏃ュ織ID
+ * @return 缁撴灉
+ */
+ public int deleteJobLogByIds(Long[] logIds);
+
+ /**
+ * 鍒犻櫎浠诲姟鏃ュ織
+ *
+ * @param jobId 璋冨害鏃ュ織ID
+ * @return 缁撴灉
+ */
+ public int deleteJobLogById(Long jobId);
+
+ /**
+ * 娓呯┖浠诲姟鏃ュ織
+ */
+ public void cleanJobLog();
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/project/monitor/service/ISysJobService.java b/ruoyi/src/main/java/com/ruoyi/project/monitor/service/ISysJobService.java
new file mode 100644
index 0000000..f7da1ac
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/project/monitor/service/ISysJobService.java
@@ -0,0 +1,102 @@
+package com.ruoyi.project.monitor.service;
+
+import java.util.List;
+import org.quartz.SchedulerException;
+import com.ruoyi.common.exception.job.TaskException;
+import com.ruoyi.project.monitor.domain.SysJob;
+
+/**
+ * 瀹氭椂浠诲姟璋冨害淇℃伅淇℃伅 鏈嶅姟灞�
+ *
+ * @author ruoyi
+ */
+public interface ISysJobService
+{
+ /**
+ * 鑾峰彇quartz璋冨害鍣ㄧ殑璁″垝浠诲姟
+ *
+ * @param job 璋冨害淇℃伅
+ * @return 璋冨害浠诲姟闆嗗悎
+ */
+ public List<SysJob> selectJobList(SysJob job);
+
+ /**
+ * 閫氳繃璋冨害浠诲姟ID鏌ヨ璋冨害淇℃伅
+ *
+ * @param jobId 璋冨害浠诲姟ID
+ * @return 璋冨害浠诲姟瀵硅薄淇℃伅
+ */
+ public SysJob selectJobById(Long jobId);
+
+ /**
+ * 鏆傚仠浠诲姟
+ *
+ * @param job 璋冨害淇℃伅
+ * @return 缁撴灉
+ */
+ public int pauseJob(SysJob job) throws SchedulerException;
+
+ /**
+ * 鎭㈠浠诲姟
+ *
+ * @param job 璋冨害淇℃伅
+ * @return 缁撴灉
+ */
+ public int resumeJob(SysJob job) throws SchedulerException;
+
+ /**
+ * 鍒犻櫎浠诲姟鍚庯紝鎵�瀵瑰簲鐨則rigger涔熷皢琚垹闄�
+ *
+ * @param job 璋冨害淇℃伅
+ * @return 缁撴灉
+ */
+ public int deleteJob(SysJob job) throws SchedulerException;
+
+ /**
+ * 鎵归噺鍒犻櫎璋冨害淇℃伅
+ *
+ * @param jobIds 闇�瑕佸垹闄ょ殑浠诲姟ID
+ * @return 缁撴灉
+ */
+ public void deleteJobByIds(Long[] jobIds) throws SchedulerException;
+
+ /**
+ * 浠诲姟璋冨害鐘舵�佷慨鏀�
+ *
+ * @param job 璋冨害淇℃伅
+ * @return 缁撴灉
+ */
+ public int changeStatus(SysJob job) throws SchedulerException;
+
+ /**
+ * 绔嬪嵆杩愯浠诲姟
+ *
+ * @param job 璋冨害淇℃伅
+ * @return 缁撴灉
+ */
+ public void run(SysJob job) throws SchedulerException;
+
+ /**
+ * 鏂板浠诲姟
+ *
+ * @param job 璋冨害淇℃伅
+ * @return 缁撴灉
+ */
+ public int insertJob(SysJob job) throws SchedulerException, TaskException;
+
+ /**
+ * 鏇存柊浠诲姟
+ *
+ * @param job 璋冨害淇℃伅
+ * @return 缁撴灉
+ */
+ public int updateJob(SysJob job) throws SchedulerException, TaskException;
+
+ /**
+ * 鏍¢獙cron琛ㄨ揪寮忔槸鍚︽湁鏁�
+ *
+ * @param cronExpression 琛ㄨ揪寮�
+ * @return 缁撴灉
+ */
+ public boolean checkCronExpressionIsValid(String cronExpression);
+}
\ No newline at end of file
diff --git a/ruoyi/src/main/java/com/ruoyi/project/monitor/service/impl/SysJobLogServiceImpl.java b/ruoyi/src/main/java/com/ruoyi/project/monitor/service/impl/SysJobLogServiceImpl.java
new file mode 100644
index 0000000..991cf81
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/project/monitor/service/impl/SysJobLogServiceImpl.java
@@ -0,0 +1,87 @@
+package com.ruoyi.project.monitor.service.impl;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.project.monitor.domain.SysJobLog;
+import com.ruoyi.project.monitor.mapper.SysJobLogMapper;
+import com.ruoyi.project.monitor.service.ISysJobLogService;
+
+/**
+ * 瀹氭椂浠诲姟璋冨害鏃ュ織淇℃伅 鏈嶅姟灞�
+ *
+ * @author ruoyi
+ */
+@Service
+public class SysJobLogServiceImpl implements ISysJobLogService
+{
+ @Autowired
+ private SysJobLogMapper jobLogMapper;
+
+ /**
+ * 鑾峰彇quartz璋冨害鍣ㄦ棩蹇楃殑璁″垝浠诲姟
+ *
+ * @param jobLog 璋冨害鏃ュ織淇℃伅
+ * @return 璋冨害浠诲姟鏃ュ織闆嗗悎
+ */
+ @Override
+ public List<SysJobLog> selectJobLogList(SysJobLog jobLog)
+ {
+ return jobLogMapper.selectJobLogList(jobLog);
+ }
+
+ /**
+ * 閫氳繃璋冨害浠诲姟鏃ュ織ID鏌ヨ璋冨害淇℃伅
+ *
+ * @param jobLogId 璋冨害浠诲姟鏃ュ織ID
+ * @return 璋冨害浠诲姟鏃ュ織瀵硅薄淇℃伅
+ */
+ @Override
+ public SysJobLog selectJobLogById(Long jobLogId)
+ {
+ return jobLogMapper.selectJobLogById(jobLogId);
+ }
+
+ /**
+ * 鏂板浠诲姟鏃ュ織
+ *
+ * @param jobLog 璋冨害鏃ュ織淇℃伅
+ */
+ @Override
+ public void addJobLog(SysJobLog jobLog)
+ {
+ jobLogMapper.insertJobLog(jobLog);
+ }
+
+ /**
+ * 鎵归噺鍒犻櫎璋冨害鏃ュ織淇℃伅
+ *
+ * @param logIds 闇�瑕佸垹闄ょ殑鏁版嵁ID
+ * @return 缁撴灉
+ */
+ @Override
+ public int deleteJobLogByIds(Long[] logIds)
+ {
+ return jobLogMapper.deleteJobLogByIds(logIds);
+ }
+
+ /**
+ * 鍒犻櫎浠诲姟鏃ュ織
+ *
+ * @param jobId 璋冨害鏃ュ織ID
+ */
+ @Override
+ public int deleteJobLogById(Long jobId)
+ {
+ return jobLogMapper.deleteJobLogById(jobId);
+ }
+
+ /**
+ * 娓呯┖浠诲姟鏃ュ織
+ */
+ @Override
+ public void cleanJobLog()
+ {
+ jobLogMapper.cleanJobLog();
+ }
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/project/monitor/service/impl/SysJobServiceImpl.java b/ruoyi/src/main/java/com/ruoyi/project/monitor/service/impl/SysJobServiceImpl.java
new file mode 100644
index 0000000..c53cd3c
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/project/monitor/service/impl/SysJobServiceImpl.java
@@ -0,0 +1,254 @@
+package com.ruoyi.project.monitor.service.impl;
+
+import java.util.List;
+import javax.annotation.PostConstruct;
+import org.quartz.JobDataMap;
+import org.quartz.JobKey;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import com.ruoyi.common.constant.ScheduleConstants;
+import com.ruoyi.common.exception.job.TaskException;
+import com.ruoyi.common.utils.job.CronUtils;
+import com.ruoyi.common.utils.job.ScheduleUtils;
+import com.ruoyi.project.monitor.domain.SysJob;
+import com.ruoyi.project.monitor.mapper.SysJobMapper;
+import com.ruoyi.project.monitor.service.ISysJobService;
+
+/**
+ * 瀹氭椂浠诲姟璋冨害淇℃伅 鏈嶅姟灞�
+ *
+ * @author ruoyi
+ */
+@Service
+public class SysJobServiceImpl implements ISysJobService
+{
+ @Autowired
+ private Scheduler scheduler;
+
+ @Autowired
+ private SysJobMapper jobMapper;
+
+ /**
+ * 椤圭洰鍚姩鏃讹紝鍒濆鍖栧畾鏃跺櫒 涓昏鏄槻姝㈡墜鍔ㄤ慨鏀规暟鎹簱瀵艰嚧鏈悓姝ュ埌瀹氭椂浠诲姟澶勭悊锛堟敞锛氫笉鑳芥墜鍔ㄤ慨鏀规暟鎹簱ID鍜屼换鍔$粍鍚嶏紝鍚﹀垯浼氬鑷磋剰鏁版嵁锛�
+ */
+ @PostConstruct
+ public void init() throws SchedulerException, TaskException
+ {
+ scheduler.clear();
+ List<SysJob> jobList = jobMapper.selectJobAll();
+ for (SysJob job : jobList)
+ {
+ ScheduleUtils.createScheduleJob(scheduler, job);
+ }
+ }
+
+ /**
+ * 鑾峰彇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
+ public int pauseJob(SysJob job) throws SchedulerException
+ {
+ Long jobId = job.getJobId();
+ String jobGroup = job.getJobGroup();
+ job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
+ int rows = jobMapper.updateJob(job);
+ if (rows > 0)
+ {
+ scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
+ }
+ return rows;
+ }
+
+ /**
+ * 鎭㈠浠诲姟
+ *
+ * @param job 璋冨害淇℃伅
+ */
+ @Override
+ @Transactional
+ public int resumeJob(SysJob job) throws SchedulerException
+ {
+ Long jobId = job.getJobId();
+ String jobGroup = job.getJobGroup();
+ job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
+ int rows = jobMapper.updateJob(job);
+ if (rows > 0)
+ {
+ scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup));
+ }
+ return rows;
+ }
+
+ /**
+ * 鍒犻櫎浠诲姟鍚庯紝鎵�瀵瑰簲鐨則rigger涔熷皢琚垹闄�
+ *
+ * @param job 璋冨害淇℃伅
+ */
+ @Override
+ @Transactional
+ public int deleteJob(SysJob job) throws SchedulerException
+ {
+ Long jobId = job.getJobId();
+ String jobGroup = job.getJobGroup();
+ int rows = jobMapper.deleteJobById(jobId);
+ if (rows > 0)
+ {
+ scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup));
+ }
+ return rows;
+ }
+
+ /**
+ * 鎵归噺鍒犻櫎璋冨害淇℃伅
+ *
+ * @param jobIds 闇�瑕佸垹闄ょ殑浠诲姟ID
+ * @return 缁撴灉
+ */
+ @Override
+ @Transactional
+ public void deleteJobByIds(Long[] jobIds) throws SchedulerException
+ {
+ for (Long jobId : jobIds)
+ {
+ SysJob job = jobMapper.selectJobById(jobId);
+ deleteJob(job);
+ }
+ }
+
+ /**
+ * 浠诲姟璋冨害鐘舵�佷慨鏀�
+ *
+ * @param job 璋冨害淇℃伅
+ */
+ @Override
+ @Transactional
+ public int changeStatus(SysJob job) throws SchedulerException
+ {
+ int rows = 0;
+ String status = job.getStatus();
+ if (ScheduleConstants.Status.NORMAL.getValue().equals(status))
+ {
+ rows = resumeJob(job);
+ }
+ else if (ScheduleConstants.Status.PAUSE.getValue().equals(status))
+ {
+ rows = pauseJob(job);
+ }
+ return rows;
+ }
+
+ /**
+ * 绔嬪嵆杩愯浠诲姟
+ *
+ * @param job 璋冨害淇℃伅
+ */
+ @Override
+ @Transactional
+ public void run(SysJob job) throws SchedulerException
+ {
+ Long jobId = job.getJobId();
+ String jobGroup = job.getJobGroup();
+ SysJob properties = selectJobById(job.getJobId());
+ // 鍙傛暟
+ JobDataMap dataMap = new JobDataMap();
+ dataMap.put(ScheduleConstants.TASK_PROPERTIES, properties);
+ scheduler.triggerJob(ScheduleUtils.getJobKey(jobId, jobGroup), dataMap);
+ }
+
+ /**
+ * 鏂板浠诲姟
+ *
+ * @param job 璋冨害淇℃伅 璋冨害淇℃伅
+ */
+ @Override
+ @Transactional
+ public int insertJob(SysJob job) throws SchedulerException, TaskException
+ {
+ job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
+ int rows = jobMapper.insertJob(job);
+ if (rows > 0)
+ {
+ ScheduleUtils.createScheduleJob(scheduler, job);
+ }
+ return rows;
+ }
+
+ /**
+ * 鏇存柊浠诲姟鐨勬椂闂磋〃杈惧紡
+ *
+ * @param job 璋冨害淇℃伅
+ */
+ @Override
+ @Transactional
+ public int updateJob(SysJob job) throws SchedulerException, TaskException
+ {
+ SysJob properties = selectJobById(job.getJobId());
+ int rows = jobMapper.updateJob(job);
+ if (rows > 0)
+ {
+ updateSchedulerJob(job, properties.getJobGroup());
+ }
+ return rows;
+ }
+
+ /**
+ * 鏇存柊浠诲姟
+ *
+ * @param job 浠诲姟瀵硅薄
+ * @param jobGroup 浠诲姟缁勫悕
+ */
+ public void updateSchedulerJob(SysJob job, String jobGroup) throws SchedulerException, TaskException
+ {
+ Long jobId = job.getJobId();
+ // 鍒ゆ柇鏄惁瀛樺湪
+ JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
+ if (scheduler.checkExists(jobKey))
+ {
+ // 闃叉鍒涘缓鏃跺瓨鍦ㄦ暟鎹棶棰� 鍏堢Щ闄わ紝鐒跺悗鍦ㄦ墽琛屽垱寤烘搷浣�
+ scheduler.deleteJob(jobKey);
+ }
+ ScheduleUtils.createScheduleJob(scheduler, job);
+ }
+
+ /**
+ * 鏍¢獙cron琛ㄨ揪寮忔槸鍚︽湁鏁�
+ *
+ * @param cronExpression 琛ㄨ揪寮�
+ * @return 缁撴灉
+ */
+ @Override
+ public boolean checkCronExpressionIsValid(String cronExpression)
+ {
+ return CronUtils.isValid(cronExpression);
+ }
+}
\ No newline at end of file
diff --git a/ruoyi/src/main/resources/application.yml b/ruoyi/src/main/resources/application.yml
index 6725ccf..8658f87 100644
--- a/ruoyi/src/main/resources/application.yml
+++ b/ruoyi/src/main/resources/application.yml
@@ -3,7 +3,7 @@
# 鍚嶇О
name: RuoYi
# 鐗堟湰
- version: 2.1.0
+ version: 2.2.0
# 鐗堟潈骞翠唤
copyrightYear: 2019
# 瀹炰緥婕旂ず寮�鍏�
@@ -27,7 +27,6 @@
max-threads: 800
# Tomcat鍚姩鍒濆鍖栫殑绾跨▼鏁帮紝榛樿鍊�25
min-spare-threads: 30
-
# 鏃ュ織閰嶇疆
logging:
diff --git a/ruoyi/src/main/resources/mybatis/system/SysJobLogMapper.xml b/ruoyi/src/main/resources/mybatis/system/SysJobLogMapper.xml
new file mode 100644
index 0000000..bf81575
--- /dev/null
+++ b/ruoyi/src/main/resources/mybatis/system/SysJobLogMapper.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.project.monitor.mapper.SysJobLogMapper">
+
+ <resultMap type="SysJobLog" id="SysJobLogResult">
+ <id property="jobLogId" column="job_log_id" />
+ <result property="jobName" column="job_name" />
+ <result property="jobGroup" column="job_group" />
+ <result property="invokeTarget" column="invoke_target" />
+ <result property="jobMessage" column="job_message" />
+ <result property="status" column="status" />
+ <result property="exceptionInfo" column="exception_info" />
+ <result property="createTime" column="create_time" />
+ </resultMap>
+
+ <sql id="selectJobLogVo">
+ select job_log_id, job_name, job_group, invoke_target, job_message, status, exception_info, create_time
+ from sys_job_log
+ </sql>
+
+ <select id="selectJobLogList" parameterType="SysJobLog" resultMap="SysJobLogResult">
+ <include refid="selectJobLogVo"/>
+ <where>
+ <if test="jobName != null and jobName != ''">
+ AND job_name like concat('%', #{jobName}, '%')
+ </if>
+ <if test="jobGroup != null and jobGroup != ''">
+ AND job_group = #{jobGroup}
+ </if>
+ <if test="status != null and status != ''">
+ AND status = #{status}
+ </if>
+ <if test="invokeTarget != null and invokeTarget != ''">
+ AND invoke_target like concat('%', #{invokeTarget}, '%')
+ </if>
+ <if test="beginTime != null and beginTime != ''"><!-- 寮�濮嬫椂闂存绱� -->
+ and date_format(create_time,'%y%m%d') >= date_format(#{beginTime},'%y%m%d')
+ </if>
+ <if test="endTime != null and endTime != ''"><!-- 缁撴潫鏃堕棿妫�绱� -->
+ and date_format(create_time,'%y%m%d') <= date_format(#{endTime},'%y%m%d')
+ </if>
+ </where>
+ </select>
+
+ <select id="selectJobLogAll" resultMap="SysJobLogResult">
+ <include refid="selectJobLogVo"/>
+ </select>
+
+ <select id="selectJobLogById" parameterType="Long" resultMap="SysJobLogResult">
+ <include refid="selectJobLogVo"/>
+ where job_log_id = #{jobLogId}
+ </select>
+
+ <delete id="deleteJobLogById" parameterType="Long">
+ delete from sys_job_log where job_log_id = #{jobLogId}
+ </delete>
+
+ <delete id="deleteJobLogByIds" parameterType="Long">
+ delete from sys_job_log where job_log_id in
+ <foreach collection="array" item="jobLogId" open="(" separator="," close=")">
+ #{jobLogId}
+ </foreach>
+ </delete>
+
+ <update id="cleanJobLog">
+ truncate table sys_job_log
+ </update>
+
+ <insert id="insertJobLog" parameterType="SysJobLog">
+ insert into sys_job_log(
+ <if test="jobLogId != null and jobLogId != 0">job_log_id,</if>
+ <if test="jobName != null and jobName != ''">job_name,</if>
+ <if test="jobGroup != null and jobGroup != ''">job_group,</if>
+ <if test="invokeTarget != null and invokeTarget != ''">invoke_target,</if>
+ <if test="jobMessage != null and jobMessage != ''">job_message,</if>
+ <if test="status != null and status != ''">status,</if>
+ <if test="exceptionInfo != null and exceptionInfo != ''">exception_info,</if>
+ create_time
+ )values(
+ <if test="jobLogId != null and jobLogId != 0">#{jobLogId},</if>
+ <if test="jobName != null and jobName != ''">#{jobName},</if>
+ <if test="jobGroup != null and jobGroup != ''">#{jobGroup},</if>
+ <if test="invokeTarget != null and invokeTarget != ''">#{invokeTarget},</if>
+ <if test="jobMessage != null and jobMessage != ''">#{jobMessage},</if>
+ <if test="status != null and status != ''">#{status},</if>
+ <if test="exceptionInfo != null and exceptionInfo != ''">#{exceptionInfo},</if>
+ sysdate()
+ )
+ </insert>
+
+</mapper>
\ No newline at end of file
diff --git a/ruoyi/src/main/resources/mybatis/system/SysJobMapper.xml b/ruoyi/src/main/resources/mybatis/system/SysJobMapper.xml
new file mode 100644
index 0000000..47f61c9
--- /dev/null
+++ b/ruoyi/src/main/resources/mybatis/system/SysJobMapper.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.project.monitor.mapper.SysJobMapper">
+
+ <resultMap type="SysJob" id="SysJobResult">
+ <id property="jobId" column="job_id" />
+ <result property="jobName" column="job_name" />
+ <result property="jobGroup" column="job_group" />
+ <result property="invokeTarget" column="invoke_target" />
+ <result property="cronExpression" column="cron_expression" />
+ <result property="misfirePolicy" column="misfire_policy" />
+ <result property="concurrent" column="concurrent" />
+ <result property="status" column="status" />
+ <result property="createBy" column="create_by" />
+ <result property="createTime" column="create_time" />
+ <result property="updateBy" column="update_by" />
+ <result property="updateTime" column="update_time" />
+ <result property="remark" column="remark" />
+ </resultMap>
+
+ <sql id="selectJobVo">
+ select job_id, job_name, job_group, invoke_target, cron_expression, misfire_policy, concurrent, status, create_by, create_time, remark
+ from sys_job
+ </sql>
+
+ <select id="selectJobList" parameterType="SysJob" resultMap="SysJobResult">
+ <include refid="selectJobVo"/>
+ <where>
+ <if test="jobName != null and jobName != ''">
+ AND job_name like concat('%', #{jobName}, '%')
+ </if>
+ <if test="jobGroup != null and jobGroup != ''">
+ AND job_group = #{jobGroup}
+ </if>
+ <if test="status != null and status != ''">
+ AND status = #{status}
+ </if>
+ <if test="invokeTarget != null and invokeTarget != ''">
+ AND invoke_target like concat('%', #{invokeTarget}, '%')
+ </if>
+ </where>
+ </select>
+
+ <select id="selectJobAll" resultMap="SysJobResult">
+ <include refid="selectJobVo"/>
+ </select>
+
+ <select id="selectJobById" parameterType="Long" resultMap="SysJobResult">
+ <include refid="selectJobVo"/>
+ where job_id = #{jobId}
+ </select>
+
+ <delete id="deleteJobById" parameterType="Long">
+ delete from sys_job where job_id = #{jobId}
+ </delete>
+
+ <delete id="deleteJobByIds" parameterType="Long">
+ delete from sys_job where job_id in
+ <foreach collection="array" item="jobId" open="(" separator="," close=")">
+ #{jobId}
+ </foreach>
+ </delete>
+
+ <update id="updateJob" parameterType="SysJob">
+ update sys_job
+ <set>
+ <if test="jobName != null and jobName != ''">job_name = #{jobName},</if>
+ <if test="jobGroup != null and jobGroup != ''">job_group = #{jobGroup},</if>
+ <if test="invokeTarget != null and invokeTarget != ''">invoke_target = #{invokeTarget},</if>
+ <if test="cronExpression != null and cronExpression != ''">cron_expression = #{cronExpression},</if>
+ <if test="misfirePolicy != null and misfirePolicy != ''">misfire_policy = #{misfirePolicy},</if>
+ <if test="concurrent != null and concurrent != ''">concurrent = #{concurrent},</if>
+ <if test="status !=null">status = #{status},</if>
+ <if test="remark != null and remark != ''">remark = #{remark},</if>
+ <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+ update_time = sysdate()
+ </set>
+ where job_id = #{jobId}
+ </update>
+
+ <insert id="insertJob" parameterType="SysJob" useGeneratedKeys="true" keyProperty="jobId">
+ insert into sys_job(
+ <if test="jobId != null and jobId != 0">job_id,</if>
+ <if test="jobName != null and jobName != ''">job_name,</if>
+ <if test="jobGroup != null and jobGroup != ''">job_group,</if>
+ <if test="invokeTarget != null and invokeTarget != ''">invoke_target,</if>
+ <if test="cronExpression != null and cronExpression != ''">cron_expression,</if>
+ <if test="misfirePolicy != null and misfirePolicy != ''">misfire_policy,</if>
+ <if test="concurrent != null and concurrent != ''">concurrent,</if>
+ <if test="status != null and status != ''">status,</if>
+ <if test="remark != null and remark != ''">remark,</if>
+ <if test="createBy != null and createBy != ''">create_by,</if>
+ create_time
+ )values(
+ <if test="jobId != null and jobId != 0">#{jobId},</if>
+ <if test="jobName != null and jobName != ''">#{jobName},</if>
+ <if test="jobGroup != null and jobGroup != ''">#{jobGroup},</if>
+ <if test="invokeTarget != null and invokeTarget != ''">#{invokeTarget},</if>
+ <if test="cronExpression != null and cronExpression != ''">#{cronExpression},</if>
+ <if test="misfirePolicy != null and misfirePolicy != ''">#{misfirePolicy},</if>
+ <if test="concurrent != null and concurrent != ''">#{concurrent},</if>
+ <if test="status != null and status != ''">#{status},</if>
+ <if test="remark != null and remark != ''">#{remark},</if>
+ <if test="createBy != null and createBy != ''">#{createBy},</if>
+ sysdate()
+ )
+ </insert>
+
+</mapper>
\ No newline at end of file
--
Gitblit v1.9.3