Przeglądaj źródła

任务分组, 支持在线维护

xueli.xue 8 lat temu
rodzic
commit
204d7848fe

+ 7 - 0
db/tables_xxl_job.sql

@@ -203,5 +203,12 @@ CREATE TABLE XXL_JOB_QRTZ_TRIGGER_REGISTRY (
   PRIMARY KEY (`id`)
 ) 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;
 

+ 80 - 0
xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobGroupController.java

@@ -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;
+	}
+
+}

+ 35 - 0
xxl-job-admin/src/main/java/com/xxl/job/admin/core/model/XxlJobGroup.java

@@ -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;
+    }
+}

+ 21 - 0
xxl-job-admin/src/main/java/com/xxl/job/admin/dao/IXxlJobGroupDao.java

@@ -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);
+}

+ 46 - 0
xxl-job-admin/src/main/java/com/xxl/job/admin/dao/impl/XxlJobGroupDaoImpl.java

@@ -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);
+    }
+
+
+}

+ 47 - 0
xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobGroupMapper.xml

@@ -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>

+ 1 - 0
xxl-job-admin/src/main/webapp/WEB-INF/template/common/common.macro.ftl

@@ -85,6 +85,7 @@
 				<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}/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>
 			</ul>
 		</section>

+ 148 - 0
xxl-job-admin/src/main/webapp/WEB-INF/template/jobgroup/jobgroup.index.ftl

@@ -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>

+ 3 - 3
xxl-job-admin/src/main/webapp/WEB-INF/template/jobinfo/jobinfo.index.ftl

@@ -25,7 +25,7 @@
 	<div class="content-wrapper">
 		<!-- Content Header (Page header) -->
 		<section class="content-header">
-			<h1>任务调度中心<small>调度管理</small></h1>
+			<h1>调度管理任务调度中心<small></small></h1>
 			<!--
 			<ol class="breadcrumb">
 				<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 {
 	private static transient Logger logger = LoggerFactory.getLogger(DemoGlueJobHandler.class);
-	
+
 	@Override
 	public void execute(String... params) throws Exception {
 		logger.info("XXL-JOB, Hello World.");
 	}
-	
+
 }
 </textarea>
 					

+ 2 - 2
xxl-job-admin/src/main/webapp/WEB-INF/template/joblog/joblog.index.ftl

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

+ 185 - 0
xxl-job-admin/src/main/webapp/static/js/jobgroup.index.1.js

@@ -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");
+	});
+
+	
+});

+ 1 - 1
xxl-job-admin/src/main/webapp/static/js/jobinfo.index.1.js

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