任务分组, 支持在线维护

v1.5
xueli.xue 8 years ago
parent caf817124e
commit 204d7848fe

@ -203,5 +203,12 @@ CREATE TABLE XXL_JOB_QRTZ_TRIGGER_REGISTRY (
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE XXL_JOB_QRTZ_TRIGGER_GROUP (
`group_name` varchar(255) NOT NULL,
`group_desc` varchar(255) NOT NULL,
`order` int(11) NOT NULL,
PRIMARY KEY (`group_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
commit; commit;

@ -0,0 +1,80 @@
package com.xxl.job.admin.controller;
import com.xxl.job.admin.core.model.ReturnT;
import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.dao.IXxlJobGroupDao;
import com.xxl.job.admin.dao.IXxlJobInfoDao;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import java.util.List;
/**
* job group controller
* @author xuxueli 2016-10-02 20:52:56
*/
@Controller
@RequestMapping("/jobgroup")
public class JobGroupController {
@Resource
public IXxlJobInfoDao xxlJobInfoDao;
@Resource
public IXxlJobGroupDao xxlJobGroupDao;
@RequestMapping
public String index(Model model) {
List<XxlJobGroup> list = xxlJobGroupDao.findAll();
model.addAttribute("list", list);
return "jobgroup/jobgroup.index";
}
@RequestMapping("/save")
@ResponseBody
public ReturnT<String> save(XxlJobGroup xxlJobGroup){
// valid
if (xxlJobGroup.getGroupName()==null || StringUtils.isBlank(xxlJobGroup.getGroupName())) {
return new ReturnT<String>(500, "请输入分组");
}
if (xxlJobGroup.getGroupDesc()==null || StringUtils.isBlank(xxlJobGroup.getGroupDesc())) {
return new ReturnT<String>(500, "请输入描述");
}
// check repeat
XxlJobGroup group = xxlJobGroupDao.load(xxlJobGroup.getGroupName());
if (group!=null) {
return new ReturnT<String>(500, "分组已存在, 请勿重复添加");
}
int ret = xxlJobGroupDao.save(xxlJobGroup);
return (ret>0)?ReturnT.SUCCESS:ReturnT.FAIL;
}
@RequestMapping("/update")
@ResponseBody
public ReturnT<String> update(XxlJobGroup xxlJobGroup){
int ret = xxlJobGroupDao.update(xxlJobGroup);
return (ret>0)?ReturnT.SUCCESS:ReturnT.FAIL;
}
@RequestMapping("/remove")
@ResponseBody
public ReturnT<String> remove(String groupName){
// valid
int count = xxlJobInfoDao.pageListCount(0, 10, groupName, null);
if (count > 0) {
return new ReturnT<String>(500, "该分组使用中, 不可删除");
}
int ret = xxlJobGroupDao.remove(groupName);
return (ret>0)?ReturnT.SUCCESS:ReturnT.FAIL;
}
}

@ -0,0 +1,35 @@
package com.xxl.job.admin.core.model;
/**
* Created by xuxueli on 16/9/30.
*/
public class XxlJobGroup {
private String groupName;
private String groupDesc;
private int order;
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public String getGroupDesc() {
return groupDesc;
}
public void setGroupDesc(String groupDesc) {
this.groupDesc = groupDesc;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
}

@ -0,0 +1,21 @@
package com.xxl.job.admin.dao;
import com.xxl.job.admin.core.model.XxlJobGroup;
import java.util.List;
/**
* Created by xuxueli on 16/9/30.
*/
public interface IXxlJobGroupDao {
public List<XxlJobGroup> findAll();
public int save(XxlJobGroup xxlJobGroup);
public int update(XxlJobGroup xxlJobGroup);
public int remove(String groupName);
public XxlJobGroup load(String groupName);
}

@ -0,0 +1,46 @@
package com.xxl.job.admin.dao.impl;
import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.dao.IXxlJobGroupDao;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import java.util.List;
/**
* Created by xuxueli on 16/9/30.
*/
@Repository
public class XxlJobGroupDaoImpl implements IXxlJobGroupDao {
@Resource
public SqlSessionTemplate sqlSessionTemplate;
@Override
public List<XxlJobGroup> findAll() {
return sqlSessionTemplate.selectList("XxlJobGroupMapper.findAll");
}
@Override
public int save(XxlJobGroup xxlJobGroup) {
return sqlSessionTemplate.update("XxlJobGroupMapper.save", xxlJobGroup);
}
@Override
public int update(XxlJobGroup xxlJobGroup) {
return sqlSessionTemplate.update("XxlJobGroupMapper.update", xxlJobGroup);
}
@Override
public int remove(String groupName) {
return sqlSessionTemplate.delete("XxlJobGroupMapper.remove", groupName);
}
@Override
public XxlJobGroup load(String groupName) {
return sqlSessionTemplate.selectOne("XxlJobGroupMapper.load", groupName);
}
}

@ -0,0 +1,47 @@
<?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="XxlJobGroupMapper">
<resultMap id="XxlJobGroup" type="com.xxl.job.admin.core.model.XxlJobGroup" >
<result column="group_name" property="groupName" />
<result column="group_desc" property="groupDesc" />
<result column="order" property="order" />
</resultMap>
<sql id="Base_Column_List">
t.group_name,
t.group_desc,
t.order
</sql>
<select id="findAll" parameterType="java.lang.Integer" resultMap="XxlJobGroup">
SELECT <include refid="Base_Column_List" />
FROM XXL_JOB_QRTZ_TRIGGER_GROUP AS t
ORDER BY t.order ASC
</select>
<insert id="save" parameterType="java.util.Map" >
INSERT INTO XXL_JOB_QRTZ_TRIGGER_GROUP ( `group_name`, `group_desc`, `order`)
values ( #{groupName}, #{groupDesc}, #{order});
</insert>
<update id="update" parameterType="java.util.Map" >
UPDATE XXL_JOB_QRTZ_TRIGGER_GROUP
SET `group_desc` = #{groupDesc},
`order` = #{order}
WHERE group_name = #{groupName}
</update>
<delete id="remove" parameterType="java.lang.String" >
DELETE FROM XXL_JOB_QRTZ_TRIGGER_GROUP
WHERE group_name = #{groupName}
</delete>
<select id="load" parameterType="java.lang.String" resultMap="XxlJobGroup">
SELECT <include refid="Base_Column_List" />
FROM XXL_JOB_QRTZ_TRIGGER_GROUP AS t
WHERE t.group_name = #{groupName}
</select>
</mapper>

@ -85,6 +85,7 @@
<li class="header">常用模块</li> <li class="header">常用模块</li>
<li class="nav-click" ><a href="${request.contextPath}/jobinfo"><i class="fa fa-circle-o text-red"></i> <span>调度管理</span></a></li> <li class="nav-click" ><a href="${request.contextPath}/jobinfo"><i class="fa fa-circle-o text-red"></i> <span>调度管理</span></a></li>
<li class="nav-click" ><a href="${request.contextPath}/joblog"><i class="fa fa-circle-o text-yellow"></i><span>调度日志</span></a></li> <li class="nav-click" ><a href="${request.contextPath}/joblog"><i class="fa fa-circle-o text-yellow"></i><span>调度日志</span></a></li>
<li class="nav-click" ><a href="${request.contextPath}/jobgroup"><i class="fa fa-circle-o text-red"></i> <span>分组管理</span></a></li>
<li class="nav-click" ><a href="${request.contextPath}/help"><i class="fa fa-circle-o text-yellow"></i><span>使用教程</span></a></li> <li class="nav-click" ><a href="${request.contextPath}/help"><i class="fa fa-circle-o text-yellow"></i><span>使用教程</span></a></li>
</ul> </ul>
</section> </section>

@ -0,0 +1,148 @@
<!DOCTYPE html>
<html>
<head>
<title>任务调度中心</title>
<#import "/common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle />
<!-- DataTables -->
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/datatables/dataTables.bootstrap.css">
<!-- daterangepicker -->
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/daterangepicker/daterangepicker-bs3.css">
</head>
<body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && "off" == cookieMap["adminlte_settings"].value >sidebar-collapse</#if> ">
<div class="wrapper">
<!-- header -->
<@netCommon.commonHeader />
<!-- left -->
<@netCommon.commonLeft />
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<section class="content-header">
<h1>分组管理<small>任务调度中心</small></h1>
</section>
<!-- Main content -->
<section class="content">
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="box-header">
<h3 class="box-title">分组管理</h3>
<button class="btn btn-success btn-xs pull-left2 add" >+新增分组</button>
</div>
<div class="box-body">
<table id="joblog_list" class="table table-bordered table-striped display" width="100%" >
<thead>
<tr>
<th name="groupDesc" >名称</th>
<th name="groupName" >AppName</th>
<th name="order" >排序</th>
<th name="operate" >操作</th>
</tr>
</thead>
<tbody>
<#if list?exists && list?size gt 0>
<#list list as group>
<tr>
<td>${group.groupDesc}</td>
<td>${group.groupName}</td>
<td>${group.order}</td>
<td>
<button class="btn btn-warning btn-xs update" groupName="${group.groupName}" groupDesc="${group.groupDesc}" order="${group.order}" >编辑</button>
<button class="btn btn-danger btn-xs remove" groupName="${group.groupName}" >删除</button>
</td>
</tr>
</#list>
</#if>
</tbody>
</table>
</div>
</div>
</div>
</div>
</section>
</div>
<!-- 新增.模态框 -->
<div class="modal fade" id="addModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog ">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" >新增分组</h4>
</div>
<div class="modal-body">
<form class="form-horizontal form" role="form" >
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">分组<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="groupName" placeholder="请输入“分组”" maxlength="200" ></div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">描述<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="groupDesc" placeholder="请输入“描述”" maxlength="200" ></div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">排序<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="order" placeholder="请输入“排序”" maxlength="50" ></div>
</div>
<hr>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-6">
<button type="submit" class="btn btn-primary" >保存</button>
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- 更新.模态框 -->
<div class="modal fade" id="updateModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog ">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" >编辑分组</h4>
</div>
<div class="modal-body">
<form class="form-horizontal form" role="form" >
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">分组<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="groupName" placeholder="请输入“分组”" maxlength="200" readonly ></div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">描述<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="groupDesc" placeholder="请输入“描述”" maxlength="200" ></div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">排序<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="order" placeholder="请输入“排序”" maxlength="50" ></div>
</div>
<hr>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-6">
<button type="submit" class="btn btn-primary" >保存</button>
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- footer -->
<@netCommon.commonFooter />
</div>
<@netCommon.commonScript />
<!-- DataTables -->
<script src="${request.contextPath}/static/adminlte/plugins/datatables/jquery.dataTables.min.js"></script>
<script src="${request.contextPath}/static/adminlte/plugins/datatables/dataTables.bootstrap.min.js"></script>
<script src="${request.contextPath}/static/plugins/jquery/jquery.validate.min.js"></script>
<script src="${request.contextPath}/static/js/jobgroup.index.1.js"></script>
</body>
</html>

@ -25,7 +25,7 @@
<div class="content-wrapper"> <div class="content-wrapper">
<!-- Content Header (Page header) --> <!-- Content Header (Page header) -->
<section class="content-header"> <section class="content-header">
<h1>任务调度中心<small>调度管理</small></h1> <h1>调度管理任务调度中心<small></small></h1>
<!-- <!--
<ol class="breadcrumb"> <ol class="breadcrumb">
<li><a><i class="fa fa-dashboard"></i>调度管理</a></li> <li><a><i class="fa fa-dashboard"></i>调度管理</a></li>
@ -182,12 +182,12 @@ import com.xxl.job.core.handler.IJobHandler;
public class DemoGlueJobHandler extends IJobHandler { public class DemoGlueJobHandler extends IJobHandler {
private static transient Logger logger = LoggerFactory.getLogger(DemoGlueJobHandler.class); private static transient Logger logger = LoggerFactory.getLogger(DemoGlueJobHandler.class);
@Override @Override
public void execute(String... params) throws Exception { public void execute(String... params) throws Exception {
logger.info("XXL-JOB, Hello World."); logger.info("XXL-JOB, Hello World.");
} }
} }
</textarea> </textarea>

@ -20,7 +20,7 @@
<div class="content-wrapper"> <div class="content-wrapper">
<!-- Content Header (Page header) --> <!-- Content Header (Page header) -->
<section class="content-header"> <section class="content-header">
<h1>任务调度中心<small>调度日志</small></h1> <h1>调度日志<small>任务调度中心</small></h1>
<!-- <!--
<ol class="breadcrumb"> <ol class="breadcrumb">
<li><a><i class="fa fa-dashboard"></i>调度日志</a></li> <li><a><i class="fa fa-dashboard"></i>调度日志</a></li>
@ -70,7 +70,7 @@
<div class="row"> <div class="row">
<div class="col-xs-12"> <div class="col-xs-12">
<div class="box"> <div class="box">
<div class="box-header"><h3 class="box-title">调度日志</h3></div> <div class="box-header hide"><h3 class="box-title">调度日志</h3></div>
<div class="box-body"> <div class="box-body">
<table id="joblog_list" class="table table-bordered table-striped display" width="100%" > <table id="joblog_list" class="table table-bordered table-striped display" width="100%" >
<thead> <thead>

@ -0,0 +1,185 @@
$(function() {
// remove
$('.remove').on('click', function(){
var groupName = $(this).attr('groupName');
ComConfirm.show("确认删除分组?", function(){
$.ajax({
type : 'POST',
url : base_url + '/jobgroup/remove',
data : {"groupName":groupName},
dataType : "json",
success : function(data){
if (data.code == 200) {
ComAlert.show(1, '删除成功');
window.location.reload();
} else {
ComAlert.show(2, '删除失败');
}
},
});
});
});
// jquery.validate 自定义校验 “英文字母开头,只含有英文字母、数字和下划线”
jQuery.validator.addMethod("myValid01", function(value, element) {
var length = value.length;
var valid = /^[a-z][a-zA-Z0-9-]*$/;
return this.optional(element) || valid.test(value);
}, "限制以小写字母开头,由小写字母、数字和下划线组成");
$('.add').on('click', function(){
$('#addModal').modal({backdrop: false, keyboard: false}).modal('show');
});
var addModalValidate = $("#addModal .form").validate({
errorElement : 'span',
errorClass : 'help-block',
focusInvalid : true,
rules : {
groupName : {
required : true,
rangelength:[4,200],
myValid01 : true
},
groupDesc : {
required : true,
rangelength:[4, 12]
},
order : {
required : true,
digits:true,
range:[1,1000]
}
},
messages : {
groupName : {
required :"请输入“分组”",
rangelength:"长度限制为4~200",
myValid01: "限制以小写字母开头,由小写字母、数字和中划线组成"
},
groupDesc : {
required :"请输入“描述”",
rangelength:"长度限制为4~12"
},
order : {
required :"请输入“排序”",
digits: "请输入整数",
range: "取值范围为1~1000"
}
},
highlight : function(element) {
$(element).closest('.form-group').addClass('has-error');
},
success : function(label) {
label.closest('.form-group').removeClass('has-error');
label.remove();
},
errorPlacement : function(error, element) {
element.parent('div').append(error);
},
submitHandler : function(form) {
$.post(base_url + "/jobgroup/save", $("#addModal .form").serialize(), function(data, status) {
if (data.code == "200") {
$('#addModal').modal('hide');
setTimeout(function () {
ComAlert.show(1, "新增成功", function(){
window.location.reload();
});
}, 315);
} else {
if (data.msg) {
ComAlert.show(2, data.msg);
} else {
ComAlert.show(2, "新增失败");
}
}
});
}
});
$("#addModal").on('hide.bs.modal', function () {
$("#addModal .form")[0].reset();
addModalValidate.resetForm();
$("#addModal .form .form-group").removeClass("has-error");
});
$('.update').on('click', function(){
$("#updateModal .form input[name='groupName']").val($(this).attr("groupName"));
$("#updateModal .form input[name='groupDesc']").val($(this).attr("groupDesc"));
$("#updateModal .form input[name='order']").val($(this).attr("order"));
$('#updateModal').modal({backdrop: false, keyboard: false}).modal('show');
});
var updateModalValidate = $("#updateModal .form").validate({
errorElement : 'span',
errorClass : 'help-block',
focusInvalid : true,
rules : {
groupName : {
required : true,
rangelength:[4,200],
myValid01 : true
},
groupDesc : {
required : true,
rangelength:[4, 12]
},
order : {
required : true,
digits:true,
range:[1,1000]
}
},
messages : {
groupName : {
required :"请输入“分组”",
rangelength:"长度限制为4~200",
myValid01: "限制以小写字母开头,由小写字母、数字和中划线组成"
},
groupDesc : {
required :"请输入“描述”",
rangelength:"长度限制为4~12"
},
order : {
required :"请输入“排序”",
digits: "请输入整数",
range: "取值范围为1~1000"
}
},
highlight : function(element) {
$(element).closest('.form-group').addClass('has-error');
},
success : function(label) {
label.closest('.form-group').removeClass('has-error');
label.remove();
},
errorPlacement : function(error, element) {
element.parent('div').append(error);
},
submitHandler : function(form) {
$.post(base_url + "/jobgroup/update", $("#updateModal .form").serialize(), function(data, status) {
if (data.code == "200") {
$('#addModal').modal('hide');
setTimeout(function () {
ComAlert.show(1, "更新成功", function(){
window.location.reload();
});
}, 315);
} else {
if (data.msg) {
ComAlert.show(2, data.msg);
} else {
ComAlert.show(2, "更新失败");
}
}
});
}
});
$("#updateModal").on('hide.bs.modal', function () {
$("#updateModal .form")[0].reset();
addModalValidate.resetForm();
$("#updateModal .form .form-group").removeClass("has-error");
});
});

@ -313,7 +313,7 @@ $(function() {
$("#addModal .form input[name='executorHandler']").removeAttr("readonly"); $("#addModal .form input[name='executorHandler']").removeAttr("readonly");
// 注册模式 reset // 注册模式
$("#addModal .form .executorAddress").show(); $("#addModal .form .executorAddress").show();
$("#addModal .form .executorAppname").hide(); $("#addModal .form .executorAppname").hide();
}); });

Loading…
Cancel
Save