Pre Merge pull request !8 from 黄新宇/master

pull/8/MERGE
xinyu.huang 7 years ago
commit f7de23cc9f

@ -230,3 +230,19 @@ INSERT INTO `XXL_JOB_QRTZ_TRIGGER_INFO`(`id`, `job_group`, `job_cron`, `job_desc
commit;
CREATE TABLE `XXL_JOB_QRTZ_TRIGGER_USER` (
`user_name` varchar(16) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '账号' ,
`password` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '密码' ,
`authority` tinyint(1) NOT NULL DEFAULT 0 COMMENT '权限 0只读 1全部' ,
`last_login_time` varchar(19) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '最后登录时间' ,
PRIMARY KEY (`user_name`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
ROW_FORMAT=COMPACT
;
INSERT INTO `XXL_JOB_QRTZ_TRIGGER_USER` (`user_name`, `password`, `authority`, `last_login_time`) VALUES ('admin', 'e10adc3949ba59abbe56e057f20f883e', '1', '2019-03-11 10:14:00');

@ -1,7 +1,7 @@
package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.annotation.PermessionLimit;
import com.xxl.job.admin.controller.interceptor.PermissionInterceptor;
import com.xxl.job.admin.controller.interceptor.MorePermissionInterceptor;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.admin.service.XxlJobService;
import com.xxl.job.core.biz.model.ReturnT;
@ -37,7 +37,6 @@ public class IndexController {
Map<String, Object> dashboardMap = xxlJobService.dashboardInfo();
model.addAllAttributes(dashboardMap);
return "index";
}
@ -51,7 +50,7 @@ public class IndexController {
@RequestMapping("/toLogin")
@PermessionLimit(limit=false)
public String toLogin(Model model, HttpServletRequest request) {
if (PermissionInterceptor.ifLogin(request)) {
if (MorePermissionInterceptor.ifAuthority(request)>-1) {
return "redirect:/";
}
return "login";
@ -62,7 +61,7 @@ public class IndexController {
@PermessionLimit(limit=false)
public ReturnT<String> loginDo(HttpServletRequest request, HttpServletResponse response, String userName, String password, String ifRemember){
// valid
if (PermissionInterceptor.ifLogin(request)) {
if (MorePermissionInterceptor.ifAuthority(request)>-1) {
return ReturnT.SUCCESS;
}
@ -73,7 +72,7 @@ public class IndexController {
boolean ifRem = (StringUtils.isNotBlank(ifRemember) && "on".equals(ifRemember))?true:false;
// do login
boolean loginRet = PermissionInterceptor.login(response, userName, password, ifRem);
boolean loginRet = MorePermissionInterceptor.login(response, userName, password, ifRem);
if (!loginRet) {
return new ReturnT<String>(500, I18nUtil.getString("login_param_unvalid"));
}
@ -84,8 +83,8 @@ public class IndexController {
@ResponseBody
@PermessionLimit(limit=false)
public ReturnT<String> logout(HttpServletRequest request, HttpServletResponse response){
if (PermissionInterceptor.ifLogin(request)) {
PermissionInterceptor.logout(request, response);
if (MorePermissionInterceptor.ifAuthority(request)>-1) {
MorePermissionInterceptor.logout(request, response);
}
return ReturnT.SUCCESS;
}

@ -1,5 +1,6 @@
package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.annotation.PermessionLimit;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLogGlue;
import com.xxl.job.admin.core.util.I18nUtil;
@ -51,6 +52,7 @@ public class JobCodeController {
@RequestMapping("/save")
@ResponseBody
@PermessionLimit(write=true)
public ReturnT<String> save(Model model, int id, String glueSource, String glueRemark) {
// valid
if (glueRemark==null) {

@ -1,5 +1,6 @@
package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.annotation.PermessionLimit;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobRegistry;
@ -45,6 +46,7 @@ public class JobGroupController {
@RequestMapping("/save")
@ResponseBody
@PermessionLimit(write=true)
public ReturnT<String> save(XxlJobGroup xxlJobGroup){
// valid
@ -75,6 +77,7 @@ public class JobGroupController {
@RequestMapping("/update")
@ResponseBody
@PermessionLimit(write=true)
public ReturnT<String> update(XxlJobGroup xxlJobGroup){
// valid
if (xxlJobGroup.getAppName()==null || StringUtils.isBlank(xxlJobGroup.getAppName())) {
@ -136,6 +139,7 @@ public class JobGroupController {
@RequestMapping("/remove")
@ResponseBody
@PermessionLimit(write=true)
public ReturnT<String> remove(int id){
// valid

@ -1,5 +1,6 @@
package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.annotation.PermessionLimit;
import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.route.ExecutorRouteStrategyEnum;
@ -60,37 +61,42 @@ public class JobInfoController {
@RequestMapping("/add")
@ResponseBody
@PermessionLimit(write=true)
public ReturnT<String> add(XxlJobInfo jobInfo) {
return xxlJobService.add(jobInfo);
}
@RequestMapping("/update")
@ResponseBody
@PermessionLimit(write=true)
public ReturnT<String> update(XxlJobInfo jobInfo) {
return xxlJobService.update(jobInfo);
}
@RequestMapping("/remove")
@ResponseBody
@PermessionLimit(write=true)
public ReturnT<String> remove(int id) {
return xxlJobService.remove(id);
}
@RequestMapping("/stop")
@ResponseBody
@PermessionLimit(write=true)
public ReturnT<String> pause(int id) {
return xxlJobService.stop(id);
}
@RequestMapping("/start")
@ResponseBody
@PermessionLimit(write=true)
public ReturnT<String> start(int id) {
return xxlJobService.start(id);
}
@RequestMapping("/trigger")
@ResponseBody
//@PermessionLimit(limit = false)
@PermessionLimit(write=true)
public ReturnT<String> triggerJob(int id, String executorParam) {
// force cover job param
if (executorParam == null) {

@ -1,5 +1,6 @@
package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.annotation.PermessionLimit;
import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLog;
@ -140,6 +141,7 @@ public class JobLogController {
@RequestMapping("/logKill")
@ResponseBody
@PermessionLimit(write=true)
public ReturnT<String> logKill(int id){
// base check
XxlJobLog log = xxlJobLogDao.load(id);
@ -174,6 +176,7 @@ public class JobLogController {
@RequestMapping("/clearLog")
@ResponseBody
@PermessionLimit(write=true)
public ReturnT<String> clearLog(int jobGroup, int jobId, int type){
Date clearBeforeTime = null;

@ -0,0 +1,93 @@
package com.xxl.job.admin.controller;
import com.xxl.job.admin.controller.annotation.PermessionLimit;
import com.xxl.job.admin.core.model.XxlJobUser;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.admin.dao.XxlJobUserDao;
import com.xxl.job.core.biz.model.ReturnT;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* job group controller
* @author xuxueli 2016-10-02 20:52:56
*/
@Controller
@RequestMapping("/jobuser")
public class JobUserController {
@Resource
public XxlJobUserDao xxlJobUserDao;
@RequestMapping
public String index(Model model) {
List<XxlJobUser> list = xxlJobUserDao.findAll();
model.addAttribute("list", list);
return "jobuser/jobuser.index";
}
@RequestMapping("/save")
@ResponseBody
@PermessionLimit(write=true)
public ReturnT<String> save(XxlJobUser xxlJobUser){
// valid
if (xxlJobUser.getUserName()==null || StringUtils.isBlank(xxlJobUser.getUserName())) {
return new ReturnT<String>(500, (I18nUtil.getString("system_please_input")+"userName") );
}
if (xxlJobUser.getUserName().length()<5 || xxlJobUser.getUserName().length()>16) {
return new ReturnT<String>(500, I18nUtil.getString("user_name_error") );
}
if (xxlJobUser.getPassword().length()<5 || xxlJobUser.getPassword().length()>32) {
return new ReturnT<String>(500, I18nUtil.getString("user_password_error") );
}
String password = DigestUtils.md5DigestAsHex(String.valueOf(xxlJobUser.getPassword()).getBytes());
xxlJobUser.setPassword(password);
xxlJobUser.setLastLoginTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
int ret = xxlJobUserDao.save(xxlJobUser);
return (ret>0)?ReturnT.SUCCESS:ReturnT.FAIL;
}
@RequestMapping("/update")
@ResponseBody
@PermessionLimit(write=true)
public ReturnT<String> update(XxlJobUser xxlJobUser){
// valid
if (xxlJobUser.getUserName()==null || StringUtils.isBlank(xxlJobUser.getUserName())) {
return new ReturnT<String>(500, (I18nUtil.getString("system_please_input")+"userName") );
}
if (xxlJobUser.getUserName().length()<5 || xxlJobUser.getUserName().length()>16) {
return new ReturnT<String>(500, I18nUtil.getString("user_name_error") );
}
if (xxlJobUser.getPassword()!=null && StringUtils.isNotBlank(xxlJobUser.getPassword())) {
if (xxlJobUser.getPassword().length()<5 || xxlJobUser.getPassword().length()>32) {
return new ReturnT<String>(500, I18nUtil.getString("user_password_error") );
}
String password = DigestUtils.md5DigestAsHex(String.valueOf(xxlJobUser.getPassword()).getBytes());
xxlJobUser.setPassword(password);
}
xxlJobUserDao.update(xxlJobUser);
return ReturnT.SUCCESS;
}
@RequestMapping("/remove")
@ResponseBody
@PermessionLimit(write=true)
public ReturnT<String> remove(String username){
int ret = xxlJobUserDao.remove(username);
return (ret>0)?ReturnT.SUCCESS:ReturnT.FAIL;
}
}

@ -19,4 +19,10 @@ public @interface PermessionLimit {
*/
boolean limit() default true;
/**
*
* @return
*/
boolean write() default false;
}

@ -0,0 +1,107 @@
package com.xxl.job.admin.controller.interceptor;
import com.xxl.job.admin.controller.annotation.PermessionLimit;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import com.xxl.job.admin.core.model.XxlJobUser;
import com.xxl.job.admin.core.util.AESUtil;
import com.xxl.job.admin.core.util.CookieUtil;
import com.xxl.job.admin.core.util.SpringApplicationContextHolder;
import com.xxl.job.admin.dao.XxlJobUserDao;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* ,
*
* @author hxy 2019-03-09 15:06:24
*/
@Component
public class MorePermissionInterceptor extends PermissionInterceptor {
private static String startStr = "dpMwh2AsPgh";
public static String getAesKey() {
String str = SpringApplicationContextHolder.getSpringBeanForClass(XxlJobAdminConfig.class).getAesKey();
return str;
}
public static String getLoginIdentityToken( String username, int authority) {
String token = null;
try {
token = AESUtil.encrypt(startStr+username+"_"+authority,getAesKey());
} catch (Exception e) {
e.printStackTrace();
}
return token;
}
public static boolean login(HttpServletResponse response, String username, String password, boolean ifRemember){
XxlJobUserDao xxlJobUserDao = ((XxlJobUserDao) SpringApplicationContextHolder.getSpringBean("xxlJobUserDao"));
XxlJobUser xxlJobUser = xxlJobUserDao.findByUserName(username);
if(xxlJobUser==null||!xxlJobUser.getPassword().equals(DigestUtils.md5DigestAsHex(String.valueOf(password).getBytes()))){
return false;
}
// do login
CookieUtil.set(response, LOGIN_IDENTITY_KEY, getLoginIdentityToken(xxlJobUser.getUserName(),xxlJobUser.isAuthority()?1:0), ifRemember);
xxlJobUserDao.update(xxlJobUser.setPassword(null)
.setLastLoginTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
return true;
}
public static int ifAuthority(HttpServletRequest request){
String indentityInfo = CookieUtil.getValue(request, LOGIN_IDENTITY_KEY);
if(StringUtils.isEmpty(indentityInfo)) return -1;
String str = null;
try {
str = AESUtil.decrypt(indentityInfo,getAesKey());
} catch (Exception e) {
e.printStackTrace();
}
if (indentityInfo==null || !str.startsWith(startStr)) {
return -1;
}
return Integer.parseInt(str.split("_")[1]);
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!(handler instanceof HandlerMethod)) {
return super.preHandle(request, response, handler);
}
int authority = ifAuthority(request);
HandlerMethod method = (HandlerMethod)handler;
PermessionLimit permission = method.getMethodAnnotation(PermessionLimit.class);
//未登陆
if (authority==-1) {
if (permission == null || permission.limit()) {
response.sendRedirect(request.getContextPath() + "/toLogin");
return false;
}
}
//没有写权限
if(permission != null&&authority==0&&permission.write()){
response.sendRedirect(request.getContextPath() + "/toLogin");
return false;
}
request.setAttribute("authority",authority);
return true;
}
}

@ -15,13 +15,13 @@ import javax.annotation.Resource;
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Resource
private PermissionInterceptor permissionInterceptor;
private MorePermissionInterceptor morePermissionInterceptor;
@Resource
private CookieInterceptor cookieInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(permissionInterceptor).addPathPatterns("/**");
registry.addInterceptor(morePermissionInterceptor).addPathPatterns("/**");
registry.addInterceptor(cookieInterceptor).addPathPatterns("/**");
super.addInterceptors(registry);
}

@ -46,6 +46,9 @@ public class XxlJobAdminConfig implements InitializingBean{
@Value("${spring.mail.username}")
private String emailUserName;
@Value("${xxl.job.aes.key}")
private String aesKey ;
// dao, service
@Resource
@ -105,4 +108,7 @@ public class XxlJobAdminConfig implements InitializingBean{
return mailSender;
}
public String getAesKey() {
return aesKey;
}
}

@ -0,0 +1,65 @@
package com.xxl.job.admin.core.model;
public class XxlJobUser {
private String userName;
private String password;
private boolean authority;
private String lastLoginTime;
public XxlJobUser() {
}
public XxlJobUser(String userName, String password, boolean authority, String lastLoginTime) {
this.userName = userName;
this.password = password;
this.authority = authority;
this.lastLoginTime = lastLoginTime;
}
public String getUserName() {
return userName;
}
public XxlJobUser setUserName(String userName) {
this.userName = userName;
return this;
}
public String getPassword() {
return password;
}
public XxlJobUser setPassword(String password) {
this.password = password;
return this;
}
public boolean isAuthority() {
return authority;
}
public XxlJobUser setAuthority(boolean authority) {
this.authority = authority;
return this;
}
public String getLastLoginTime() {
return lastLoginTime;
}
public XxlJobUser setLastLoginTime(String lastLoginTime) {
this.lastLoginTime = lastLoginTime;
return this;
}
@Override
public String toString() {
return "XxlJobUser{" +
"userName='" + userName + '\'' +
", password='" + password + '\'' +
", authority=" + authority +
", lastLoginTime='" + lastLoginTime + '\'' +
'}';
}
}

@ -0,0 +1,94 @@
package com.xxl.job.admin.core.util;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
/**
* @Author
* @Date 2018/5/8 1:50
* @Description TODO
**/
public class AESUtil {
private static final String KEY_AES = "AES";
private static final String DEFULT_KEY = "NAFOS_KEY_DEFULT";
public static String encrypt(String src, String key) throws Exception {
if (key == null || key.length() != 16) {
throw new Exception("key不满足条件"+key);
}
byte[] raw = key.getBytes();
SecretKeySpec skeySpec = new SecretKeySpec(raw, KEY_AES);
Cipher cipher = Cipher.getInstance(KEY_AES);
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(src.getBytes());
return byte2hex(encrypted);
}
public static String decrypt(String src, String key) throws Exception {
if (key == null || key.length() != 16) {
throw new Exception("key不满足条件"+key);
}
byte[] raw = key.getBytes();
SecretKeySpec skeySpec = new SecretKeySpec(raw, KEY_AES);
Cipher cipher = Cipher.getInstance(KEY_AES);
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] encrypted1 = hex2byte(src);
byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original);
return originalString;
}
public static String encrypt(String src) throws Exception {
byte[] raw = DEFULT_KEY.getBytes();
SecretKeySpec skeySpec = new SecretKeySpec(raw, KEY_AES);
Cipher cipher = Cipher.getInstance(KEY_AES);
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(src.getBytes());
return byte2hex(encrypted);
}
public static String decrypt(String src) throws Exception {
byte[] raw = DEFULT_KEY.getBytes();
SecretKeySpec skeySpec = new SecretKeySpec(raw, KEY_AES);
Cipher cipher = Cipher.getInstance(KEY_AES);
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] encrypted1 = hex2byte(src);
byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original);
return originalString;
}
public static byte[] hex2byte(String strhex) {
if (strhex == null) {
return null;
}
int l = strhex.length();
if (l % 2 == 1) {
return null;
}
byte[] b = new byte[l / 2];
for (int i = 0; i != l / 2; i++) {
b[i] = (byte) Integer.parseInt(strhex.substring(i * 2, i * 2 + 2),
16);
}
return b;
}
public static String byte2hex(byte[] b) {
String hs = "";
String stmp = "";
for (int n = 0; n < b.length; n++) {
stmp = (Integer.toHexString(b[n] & 0XFF));
if (stmp.length() == 1) {
hs = hs + "0" + stmp;
} else {
hs = hs + stmp;
}
}
return hs.toUpperCase();
}
}

@ -0,0 +1,38 @@
package com.xxl.job.admin.core.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* @author huangxinyu
* @version 2018113 6:58:44
*
*/
@Component
public class SpringApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
SpringApplicationContextHolder.context = context;
}
public static ApplicationContext getContext(){
return context;
}
public static Object getSpringBean(String beanName) {
return context==null?null:context.getBean(beanName);
}
public static <T> T getSpringBeanForClass(Class<T> clazz) {
return context==null?null:context.getBean(clazz);
}
public static String[] getBeanDefinitionNames() {
return context.getBeanDefinitionNames();
}
}

@ -0,0 +1,25 @@
package com.xxl.job.admin.dao;
import com.xxl.job.admin.core.model.XxlJobUser;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* Created by xuxueli on 16/9/30.
*/
@Mapper
public interface XxlJobUserDao {
public List<XxlJobUser> findAll();
public XxlJobUser findByUserName(@Param("userName") String userName);
public int save(XxlJobUser xxlJobGroup);
public int update(XxlJobUser xxlJobGroup);
public int remove(@Param("userName") String userName);
}

@ -17,9 +17,9 @@ spring.freemarker.settings.number_format=0.##########
mybatis.mapper-locations=classpath:/mybatis-mapper/*Mapper.xml
### xxl-job, datasource
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl-job?Unicode=true&characterEncoding=UTF-8
spring.datasource.url=jdbc:mysql://127.0.01:3306/xxl-job?Unicode=true&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root_pwd
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.type=org.apache.tomcat.jdbc.pool.DataSource
@ -48,3 +48,6 @@ xxl.job.accessToken=
### xxl-job, i18n (default empty as chinese, "en" as english)
xxl.job.i18n=
### xxl-job cookie. AES-key
xxl.job.aes.key=XXLJOBKEY_COOKIE

@ -83,6 +83,7 @@ logout_confirm=确认注销登录?
logout_success=注销成功
logout_fail=注销失败
## dashboard
job_dashboard_name=运行报表
job_dashboard_job_num=任务数量
@ -96,6 +97,20 @@ job_dashboard_report_loaddata_fail=调度报表数据加载异常
job_dashboard_date_report=日期分布图
job_dashboard_rate_report=成功比例图
## job user
user_manager=用户管理
user_list=用户列表
user_add=添加用戶
user_login_username=用戶账号
user_login_password=用戶密码
user_authority=用戶权限
user_authority_0=只读
user_authority_1=读写
user_last_login_time=用户最后登录时间
user_name_error=用户账号应该高于5位低于32位
user_password_error=用户密码应该高于5位低于32位
user_del=删除用户
## job info
jobinfo_name=任务管理
jobinfo_job=任务

@ -83,6 +83,7 @@ logout_confirm=Confirm logout?
logout_success=Logout success
logout_fail=Logout fail
## dashboard
job_dashboard_name=Run report
job_dashboard_job_num=Job number
@ -96,6 +97,21 @@ job_dashboard_report_loaddata_fail=Scheduling report load data error
job_dashboard_date_report=Date distribution
job_dashboard_rate_report=Percentage distribution
## job user
user_manager=User Manager
user_list=User List
user_add=Add User
user_login_username=Login Username
user_login_password=Password
user_authority=User Authority
user_authority_0=Only Read
user_authority_1=All
user_last_login_time=Last Login Time
user_name_error=username length should less than 32 and more than 5
user_password_error=password length should less than 32 and more than 5
user_del=delete user
## job info
jobinfo_name=Job Manage
jobinfo_job=Job

@ -0,0 +1,59 @@
<?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.xxl.job.admin.dao.XxlJobUserDao">
<resultMap id="XxlJobUser" type="com.xxl.job.admin.core.model.XxlJobUser" >
<result column="user_name" property="userName" />
<result column="password" property="password" />
<result column="authority" property="authority" />
<result column="last_login_time" property="lastLoginTime" />
</resultMap>
<sql id="Base_Column_List">
t.user_name,
t.password,
t.authority,
t.last_login_time
</sql>
<select id="findAll" resultMap="XxlJobUser">
SELECT t.user_name,
t.authority,
t.last_login_time
FROM XXL_JOB_QRTZ_TRIGGER_USER AS t
ORDER BY t.last_login_time desc
</select>
<select id="findByUserName" parameterType="java.lang.String" resultMap="XxlJobUser">
SELECT <include refid="Base_Column_List" />
FROM XXL_JOB_QRTZ_TRIGGER_USER AS t
WHERE t.user_name = #{userName}
</select>
<insert id="save" parameterType="com.xxl.job.admin.core.model.XxlJobUser" >
INSERT INTO XXL_JOB_QRTZ_TRIGGER_USER ( `user_name`, `password`, `authority`, `last_login_time`)
values ( #{userName}, #{password}, #{authority}, #{lastLoginTime});
</insert>
<update id="update" parameterType="com.xxl.job.admin.core.model.XxlJobUser" >
UPDATE XXL_JOB_QRTZ_TRIGGER_USER
SET
<if test="password != null" >
`password` = #{password},
</if>
<if test="lastLoginTime != null" >
`last_login_time` = #{lastLoginTime},
</if>
`authority` = #{authority}
WHERE user_name = #{userName}
</update>
<delete id="remove" parameterType="java.lang.String" >
DELETE FROM XXL_JOB_QRTZ_TRIGGER_USER
WHERE user_name = #{userName}
</delete>
</mapper>

File diff suppressed because one or more lines are too long

@ -108,16 +108,20 @@ $(function() {
"render": function ( data, type, row ) {
return function(){
// status
var authority = $("#authority").val();
var start_stop = "";
if (row.jobStatus && row.jobStatus != 'NONE') {
if ('NORMAL' == row.jobStatus) {
start_stop = '<button class="btn btn-primary btn-xs job_operate" _type="job_pause" type="button">'+ I18n.jobinfo_opt_stop +'</button> ';
if(authority==1){
if (row.jobStatus && row.jobStatus != 'NONE') {
if ('NORMAL' == row.jobStatus) {
start_stop = '<button class="btn btn-primary btn-xs job_operate" _type="job_pause" type="button">'+ I18n.jobinfo_opt_stop +'</button> ';
} else {
start_stop = '<button class="btn btn-primary btn-xs job_operate" _type="job_pause" type="button">'+ I18n.jobinfo_opt_stop +'</button> ';
}
} else {
start_stop = '<button class="btn btn-primary btn-xs job_operate" _type="job_pause" type="button">'+ I18n.jobinfo_opt_stop +'</button> ';
start_stop = '<button class="btn btn-primary btn-xs job_operate" _type="job_resume" type="button">'+ I18n.jobinfo_opt_start +'</button> ';
}
} else {
start_stop = '<button class="btn btn-primary btn-xs job_operate" _type="job_resume" type="button">'+ I18n.jobinfo_opt_start +'</button> ';
}
}
// log url
var logUrl = base_url +'/joblog?jobId='+ row.id;
@ -131,13 +135,19 @@ $(function() {
// html
tableData['key'+row.id] = row;
var html = '<p id="'+ row.id +'" >'+
'<button class="btn btn-primary btn-xs job_trigger" type="button">'+ I18n.jobinfo_opt_run +'</button> '+
var html_button = "";
if(authority==1){
html_button = '<button class="btn btn-primary btn-xs job_trigger" type="button">'+ I18n.jobinfo_opt_run +'</button> '+
start_stop +
'<a href="'+ logUrl +'"> <button class="btn btn-primary btn-xs" type="job_del" type="button" >'+ I18n.jobinfo_opt_log +'</button> </a> <br> '+
'<button class="btn btn-warning btn-xs update" type="button">'+ I18n.system_opt_edit +'</button> '+
codeBtn +
'<button class="btn btn-danger btn-xs job_operate" _type="job_del" type="button">'+ I18n.system_opt_del +'</button> '+
'<a href="'+ logUrl +'"> <button class="btn btn-primary btn-xs" type="job_del" type="button" >'+ I18n.jobinfo_opt_log +'</button> </a> <br> '+
'<button class="btn btn-warning btn-xs update" type="button">'+ I18n.system_opt_edit +'</button> '+
codeBtn +
'<button class="btn btn-danger btn-xs job_operate" _type="job_del" type="button">'+ I18n.system_opt_del +'</button> '
}else{
html_button = '<a href="'+ logUrl +'"> <button class="btn btn-primary btn-xs" type="job_del" type="button" >'+ I18n.jobinfo_opt_log +'</button> </a> <br> ';
}
var html = '<p id="'+ row.id +'" >'+
html_button +
'</p>';
return html;

@ -0,0 +1,235 @@
$(function() {
// remove
$('.remove').on('click', function(){
var username = $(this).attr('username');
layer.confirm( (I18n.system_ok + I18n.user_del + '') , {
icon: 3,
title: I18n.system_tips ,
btn: [ I18n.system_ok, I18n.system_cancel ]
}, function(index){
layer.close(index);
$.ajax({
type : 'POST',
url : base_url + '/jobuser/remove',
data : {"username":username},
dataType : "json",
success : function(data){
if (data.code == 200) {
layer.open({
title: I18n.system_tips ,
btn: [ I18n.system_ok ],
content: (I18n.user_del + I18n.system_success),
icon: '1',
end: function(layero, index){
window.location.reload();
}
});
} else {
layer.open({
title: I18n.system_tips,
btn: [ I18n.system_ok ],
content: (data.msg || (I18n.user_del + I18n.system_fail)),
icon: '2'
});
}
},
});
});
});
// jquery.validate “low letters start, limit contants、 letters、numbers and line-through.”
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);
}, I18n.jobgroup_field_appName_limit );
$('.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 : {
appName : {
required : true,
rangelength:[4,64],
myValid01 : true
},
title : {
required : true,
rangelength:[4, 12]
},
order : {
required : true,
digits:true,
range:[1,1000]
}
},
messages : {
appName : {
required : I18n.system_please_input+"AppName",
rangelength: I18n.jobgroup_field_appName_length ,
myValid01: I18n.jobgroup_field_appName_limit
},
title : {
required : I18n.system_please_input + I18n.jobgroup_field_title ,
rangelength: I18n.jobgroup_field_title_length
},
order : {
required : I18n.system_please_input + I18n.jobgroup_field_order ,
digits: I18n.jobgroup_field_order_digits ,
range: I18n.jobgroup_field_orderrange
}
},
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 + "/jobuser/save", $("#addModal .form").serialize(), function(data, status) {
if (data.code == "200") {
$('#addModal').modal('hide');
layer.open({
title: I18n.system_tips ,
btn: [ I18n.system_ok ],
content: I18n.system_add_suc ,
icon: '1',
end: function(layero, index){
window.location.reload();
}
});
} else {
layer.open({
title: I18n.system_tips,
btn: [ I18n.system_ok ],
content: (data.msg || I18n.system_add_fail ),
icon: '2'
});
}
});
}
});
$("#addModal").on('hide.bs.modal', function () {
$("#addModal .form")[0].reset();
addModalValidate.resetForm();
$("#addModal .form .form-group").removeClass("has-error");
});
// addressType change
$("#addModal input[name=addressType], #updateModal input[name=addressType]").click(function(){
var addressType = $(this).val();
var $addressList = $(this).parents("form").find("textarea[name=addressList]");
if (addressType == 0) {
$addressList.css("background-color", "#eee"); // 自动注册
$addressList.attr("readonly","readonly");
$addressList.val("");
} else {
$addressList.css("background-color", "white");
$addressList.removeAttr("readonly");
}
});
// update
$('.update').on('click', function(){
$("#updateModal .form input[name='userName']").val($(this).attr("userName"));
$("#updateModal .form input[name='password']").val($(this).attr("password"));
$("#updateModal .form input[name='authority']").removeAttr('checked');
//$("#updateModal .form input[name='addressType'][value='"+ addressType +"']").attr('checked', 'true');
$("#updateModal .form input[name='authority'][value='"+ $(this).attr("authority") +"']").click();
// 机器地址
$("#updateModal .form textarea[name='authority']").val($(this).attr("authority"));
$('#updateModal').modal({backdrop: false, keyboard: false}).modal('show');
});
var updateModalValidate = $("#updateModal .form").validate({
errorElement : 'span',
errorClass : 'help-block',
focusInvalid : true,
rules : {
appName : {
required : true,
rangelength:[4,64],
myValid01 : true
},
title : {
required : true,
rangelength:[4, 12]
},
order : {
required : true,
digits:true,
range:[1,1000]
}
},
messages : {
appName : {
required : I18n.system_please_input+"AppName",
rangelength: I18n.jobgroup_field_appName_length ,
myValid01: I18n.jobgroup_field_appName_limit
},
title : {
required : I18n.system_please_input + I18n.jobgroup_field_title ,
rangelength: I18n.jobgroup_field_title_length
},
order : {
required : I18n.system_please_input + I18n.jobgroup_field_order ,
digits: I18n.jobgroup_field_order_digits ,
range: I18n.jobgroup_field_orderrange
}
},
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 + "/jobuser/update", $("#updateModal .form").serialize(), function(data, status) {
if (data.code == "200") {
$('#addModal').modal('hide');
layer.open({
title: I18n.system_tips ,
btn: [ I18n.system_ok ],
content: I18n.system_update_suc ,
icon: '1',
end: function(layero, index){
window.location.reload();
}
});
} else {
layer.open({
title: I18n.system_tips,
btn: [ I18n.system_ok ],
content: (data.msg || I18n.system_update_fail ),
icon: '2'
});
}
});
}
});
$("#updateModal").on('hide.bs.modal', function () {
$("#updateModal .form")[0].reset();
addModalValidate.resetForm();
$("#updateModal .form .form-group").removeClass("has-error");
});
});

@ -79,11 +79,11 @@
<div class="navbar-custom-menu">
<ul class="nav navbar-nav">
<li class="dropdown user user-menu">
<a href=";" id="logoutBtn" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
<span class="hidden-xs">${I18n.logout_btn}</span>
</a>
</li>
<li class="dropdown user user-menu">
<a href=";" id="logoutBtn" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
<span class="hidden-xs">${I18n.logout_btn}</span>
</a>
</li>
</ul>
</div>
@ -98,12 +98,15 @@
<section class="sidebar">
<!-- sidebar menu: : style can be found in sidebar.less -->
<ul class="sidebar-menu">
<li class="header">${I18n.system_nav}</li>
<li class="header">${I18n.system_nav}${authority}</li>
<li class="nav-click <#if pageName == "index">active</#if>" ><a href="${request.contextPath}/"><i class="fa fa-circle-o text-aqua"></i><span>${I18n.job_dashboard_name}</span></a></li>
<li class="nav-click <#if pageName == "jobinfo">active</#if>" ><a href="${request.contextPath}/jobinfo"><i class="fa fa-circle-o text-yellow"></i><span>${I18n.jobinfo_name}</span></a></li>
<li class="nav-click <#if pageName == "joblog">active</#if>" ><a href="${request.contextPath}/joblog"><i class="fa fa-circle-o text-green"></i><span>${I18n.joblog_name}</span></a></li>
<li class="nav-click <#if pageName == "jobgroup">active</#if>" ><a href="${request.contextPath}/jobgroup"><i class="fa fa-circle-o text-red"></i><span>${I18n.jobgroup_name}</span></a></li>
<li class="nav-click <#if pageName == "help">active</#if>" ><a href="${request.contextPath}/help"><i class="fa fa-circle-o text-gray"></i><span>${I18n.job_help}</span></a></li>
<#if authority== 1 >
<li class="nav-click <#if pageName == "jobuser">active</#if>" ><a href="${request.contextPath}/jobuser"><i class="fa fa-circle-o text-pyellow"></i><span>${I18n.user_manager}</span></a></li>
</#if>
<li class="nav-click <#if pageName == "help">active</#if>" ><a href="${request.contextPath}/help"><i class="fa fa-circle-o text-gray"></i><span>${I18n.job_help}</span></a></li>
</ul>
</section>
<!-- /.sidebar -->

@ -29,7 +29,9 @@
<div class="box">
<div class="box-header">
<h3 class="box-title">${I18n.jobgroup_list}</h3>&nbsp;&nbsp;
<#if authority== 1 >
<button class="btn btn-info btn-xs pull-left2 add" >${I18n.jobgroup_add}</button>
</#if>
</div>
<div class="box-body">
<table id="joblog_list" class="table table-bordered table-striped display" width="100%" >
@ -68,6 +70,7 @@
</#if>
</td>
<td>
<#if authority== 1 >
<button class="btn btn-warning btn-xs update"
id="${group.id}"
appName="${group.appName}"
@ -76,6 +79,7 @@
addressType="${group.addressType}"
addressList="${group.addressList!}" >${I18n.system_opt_edit}</button>
<button class="btn btn-danger btn-xs remove" id="${group.id}" >${I18n.system_opt_del}</button>
</#if>
</td>
</tr>
</#list>

@ -50,9 +50,11 @@
<div class="col-xs-1">
<button class="btn btn-block btn-info" id="searchBtn">${I18n.system_search}</button>
</div>
<#if authority== 1 >
<div class="col-xs-2">
<button class="btn btn-block btn-success add" type="button">${I18n.jobinfo_field_add}</button>
</div>
</#if>
</div>
<div class="row">
@ -62,6 +64,7 @@
<h3 class="box-title"></h3>
</div>-->
<div class="box-body" >
<input type="hidden" id="authority" value="${authority}" >
<table id="job_list" class="table table-bordered table-striped" width="100%" >
<thead>
<tr>
@ -177,6 +180,7 @@
</div>
</div>
<input type="hidden" name="glueRemark" value="GLUE代码初始化" >
<textarea name="glueSource" style="display:none;" ></textarea>
<textarea class="glueSource_java" style="display:none;" >

@ -70,10 +70,11 @@
<div class="col-xs-1">
<button class="btn btn-block btn-info" id="searchBtn">${I18n.system_search}</button>
</div>
<#if authority== 1 >
<div class="col-xs-1">
<button class="btn btn-block btn-nomal" id="clearLog">${I18n.joblog_clean}</button>
</div>
</#if>
</div>
<div class="row">

@ -0,0 +1,179 @@
<!DOCTYPE html>
<html>
<head>
<#import "../common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle />
<!-- DataTables -->
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/bower_components/datatables.net-bs/css/dataTables.bootstrap.min.css">
<title>${I18n.user_manager}</title>
</head>
<body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && cookieMap["xxljob_adminlte_settings"]?exists && "off" == cookieMap["xxljob_adminlte_settings"].value >sidebar-collapse</#if> ">
<div class="wrapper">
<!-- header -->
<@netCommon.commonHeader />
<!-- left -->
<@netCommon.commonLeft "jobuser" />
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<section class="content-header">
<h1>${I18n.user_manager}</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">${I18n.user_list}</h3>&nbsp;&nbsp;
<button class="btn btn-info btn-xs pull-left2 add" >${I18n.user_add}</button>
</div>
<div class="box-body">
<table id="joblog_list" class="table table-bordered table-striped display" width="100%" >
<thead>
<tr>
<#--<th name="id" >ID</th>-->
<th name="order" >${I18n.user_login_username}</th>
<#--<th name="appName" >${I18n.jobgroup_field_order}</th>-->
<th name="title" >${I18n.user_authority}</th>
<th name="addressType" >${I18n.user_last_login_time}</th>
<#--<th name="registryList" >OnLine ${I18n.jobgroup_field_registryList}</th>-->
<th name="operate" >${I18n.system_opt}</th>
</tr>
</thead>
<tbody>
<#if list?exists && list?size gt 0>
<#list list as user>
<tr>
<#--<td>${group.id}</td>-->
<td>${user.userName}</td>
<#--<td>${group.appName}</td>-->
<td><#if user.authority>${I18n.user_authority_1}<#else>${I18n.user_authority_0}</#if></td>
<td>${user.lastLoginTime}</td>
<#--<td>-->
<#--<#if group.registryList?exists>-->
<#--<#list group.registryList as item>-->
<#--<span class="badge bg-green" title="${item}" >-->
<#--<#if item?length gt 35>-->
<#--${item?substring(0, 35)}...-->
<#--<#else>-->
<#--${item}-->
<#--</#if>-->
<#--</span>-->
<#--<br>-->
<#--</#list>-->
<#--</#if>-->
<#--</td>-->
<td>
<button class="btn btn-warning btn-xs update"
userName="${user.userName}"
authority="${user.authority?string("1","0")}"
password=" "
>${I18n.system_opt_edit}</button>
<button class="btn btn-danger btn-xs remove" username="${user.userName}" >${I18n.system_opt_del}</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" >${I18n.user_add}</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">${I18n.user_login_username}<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="userName" placeholder="${I18n.system_please_input}${I18n.user_login_username}" maxlength="64" ></div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">${I18n.user_login_password}<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="password" placeholder="${I18n.system_please_input}${I18n.user_login_password}" maxlength="12" ></div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">${I18n.user_authority}<font color="red">*</font></label>
<div class="col-sm-10">
<input type="radio" name="authority" value="0" checked />${I18n.user_authority_0}
&nbsp;&nbsp;&nbsp;&nbsp;
<input type="radio" name="authority" value="1" />${I18n.user_authority_1}
</div>
</div>
<hr>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-6">
<button type="submit" class="btn btn-primary" >${I18n.system_save}</button>
<button type="button" class="btn btn-default" data-dismiss="modal">${I18n.system_cancel}</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" >${I18n.jobgroup_edit}</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">${I18n.user_login_username}<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="userName" placeholder="${I18n.system_please_input}${I18n.user_login_username}" maxlength="64" ></div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">${I18n.user_login_password}<font color="red">*</font></label>
<div class="col-sm-10"><input type="text" class="form-control" name="password" placeholder="${I18n.system_please_input}${I18n.user_login_password}" maxlength="12" ></div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">${I18n.user_authority}<font color="red">*</font></label>
<div class="col-sm-10">
<input type="radio" name="authority" value="0" checked />${I18n.user_authority_0}
&nbsp;&nbsp;&nbsp;&nbsp;
<input type="radio" name="authority" value="1" />${I18n.user_authority_1}
</div>
</div>
<hr>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-6">
<button type="submit" class="btn btn-primary" >${I18n.system_save}</button>
<button type="button" class="btn btn-default" data-dismiss="modal">${I18n.system_cancel}</button>
<input type="hidden" name="id" >
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- footer -->
<@netCommon.commonFooter />
</div>
<@netCommon.commonScript />
<!-- DataTables -->
<script src="${request.contextPath}/static/adminlte/bower_components/datatables.net/js/jquery.dataTables.min.js"></script>
<script src="${request.contextPath}/static/adminlte/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js"></script>
<#-- jquery.validate -->
<script src="${request.contextPath}/static/plugins/jquery/jquery.validate.min.js"></script>
<script src="${request.contextPath}/static/js/jobuser.index.1.js"></script>
</body>
</html>
Loading…
Cancel
Save