getActiveSessions()
+ {
+ throw new UnsupportedOperationException("getActiveSessions method not supported");
+ }
+}
diff --git a/src/main/java/com/luckyframe/framework/shiro/web/session/SpringSessionValidationScheduler.java b/src/main/java/com/luckyframe/framework/shiro/web/session/SpringSessionValidationScheduler.java
new file mode 100644
index 0000000..01fe873
--- /dev/null
+++ b/src/main/java/com/luckyframe/framework/shiro/web/session/SpringSessionValidationScheduler.java
@@ -0,0 +1,147 @@
+package com.luckyframe.framework.shiro.web.session;
+
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import org.apache.shiro.session.mgt.DefaultSessionManager;
+import org.apache.shiro.session.mgt.SessionValidationScheduler;
+import org.apache.shiro.session.mgt.ValidatingSessionManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import com.luckyframe.common.utils.Threads;
+
+/**
+ * 自定义任务调度器完成
+ *
+ * @author ruoyi
+ */
+public class SpringSessionValidationScheduler implements SessionValidationScheduler
+{
+ private static final Logger log = LoggerFactory.getLogger(SpringSessionValidationScheduler.class);
+
+ public static final long DEFAULT_SESSION_VALIDATION_INTERVAL = DefaultSessionManager.DEFAULT_SESSION_VALIDATION_INTERVAL;
+
+ /**
+ * 定时器,用于处理超时的挂起请求,也用于连接断开时的重连。
+ */
+ @Autowired
+ @Qualifier("scheduledExecutorService")
+ private ScheduledExecutorService executorService;
+
+ private volatile boolean enabled = false;
+
+ /**
+ * The session manager used to validate sessions.
+ */
+ private ValidatingSessionManager sessionManager;
+
+ /**
+ * The session validation interval in milliseconds.
+ */
+ private long sessionValidationInterval = DEFAULT_SESSION_VALIDATION_INTERVAL;
+
+ /**
+ * Default constructor.
+ */
+ public SpringSessionValidationScheduler()
+ {
+ }
+
+ /**
+ * Constructor that specifies the session manager that should be used for validating sessions.
+ *
+ * @param sessionManager the SessionManager that should be used to validate sessions.
+ */
+ public SpringSessionValidationScheduler(ValidatingSessionManager sessionManager)
+ {
+ this.sessionManager = sessionManager;
+ }
+
+ public void setSessionManager(ValidatingSessionManager sessionManager)
+ {
+ this.sessionManager = sessionManager;
+ }
+
+ @Override
+ public boolean isEnabled()
+ {
+ return this.enabled;
+ }
+
+ /**
+ * Specifies how frequently (in milliseconds) this Scheduler will call the
+ * {@link org.apache.shiro.session.mgt.ValidatingSessionManager#validateSessions()
+ * ValidatingSessionManager#validateSessions()} method.
+ *
+ *
+ * Unless this method is called, the default value is {@link #DEFAULT_SESSION_VALIDATION_INTERVAL}.
+ *
+ * @param sessionValidationInterval
+ */
+ public void setSessionValidationInterval(long sessionValidationInterval)
+ {
+ this.sessionValidationInterval = sessionValidationInterval;
+ }
+
+ /**
+ * Starts session validation by creating a spring PeriodicTrigger.
+ */
+ @Override
+ public void enableSessionValidation()
+ {
+
+ enabled = true;
+
+ if (log.isDebugEnabled())
+ {
+ log.debug("Scheduling session validation job using Spring Scheduler with "
+ + "session validation interval of [" + sessionValidationInterval + "]ms...");
+ }
+
+ try
+ {
+ executorService.scheduleAtFixedRate(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ if (enabled)
+ {
+ sessionManager.validateSessions();
+ }
+ }
+ }, 1000, sessionValidationInterval, TimeUnit.MILLISECONDS);
+
+ this.enabled = true;
+
+ if (log.isDebugEnabled())
+ {
+ log.debug("Session validation job successfully scheduled with Spring Scheduler.");
+ }
+
+ }
+ catch (Exception e)
+ {
+ if (log.isErrorEnabled())
+ {
+ log.error("Error starting the Spring Scheduler session validation job. Session validation may not occur.", e);
+ }
+ }
+ }
+
+ @Override
+ public void disableSessionValidation()
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("Stopping Spring Scheduler session validation job...");
+ }
+
+ if (this.enabled)
+ {
+ Threads.shutdownAndAwaitTermination(executorService);
+ }
+ this.enabled = false;
+ }
+}
diff --git a/src/main/java/com/luckyframe/framework/web/controller/BaseController.java b/src/main/java/com/luckyframe/framework/web/controller/BaseController.java
new file mode 100644
index 0000000..0f7a9f5
--- /dev/null
+++ b/src/main/java/com/luckyframe/framework/web/controller/BaseController.java
@@ -0,0 +1,160 @@
+package com.luckyframe.framework.web.controller;
+
+import java.beans.PropertyEditorSupport;
+import java.util.Date;
+import java.util.List;
+import org.springframework.web.bind.WebDataBinder;
+import org.springframework.web.bind.annotation.InitBinder;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import com.luckyframe.common.utils.DateUtils;
+import com.luckyframe.common.utils.StringUtils;
+import com.luckyframe.common.utils.security.ShiroUtils;
+import com.luckyframe.framework.web.domain.AjaxResult;
+import com.luckyframe.framework.web.page.PageDomain;
+import com.luckyframe.framework.web.page.TableDataInfo;
+import com.luckyframe.framework.web.page.TableSupport;
+import com.luckyframe.project.system.user.domain.User;
+
+/**
+ * web层通用数据处理
+ *
+ * @author ruoyi
+ */
+public class BaseController
+{
+ /**
+ * 将前台传递过来的日期格式的字符串,自动转化为Date类型
+ */
+ @InitBinder
+ public void initBinder(WebDataBinder binder)
+ {
+ // Date 类型转换
+ binder.registerCustomEditor(Date.class, new PropertyEditorSupport()
+ {
+ @Override
+ public void setAsText(String text)
+ {
+ setValue(DateUtils.parseDate(text));
+ }
+ });
+ }
+
+ /**
+ * 设置请求分页数据
+ */
+ protected void startPage()
+ {
+ PageDomain pageDomain = TableSupport.buildPageRequest();
+ Integer pageNum = pageDomain.getPageNum();
+ Integer pageSize = pageDomain.getPageSize();
+ if (StringUtils.isNotNull(pageNum) && StringUtils.isNotNull(pageSize))
+ {
+ String orderBy = pageDomain.getOrderBy();
+ PageHelper.startPage(pageNum, pageSize, orderBy);
+ }
+ }
+
+ /**
+ * 响应请求分页数据
+ */
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ protected TableDataInfo getDataTable(List> list)
+ {
+ TableDataInfo rspData = new TableDataInfo();
+ rspData.setCode(0);
+ rspData.setRows(list);
+ rspData.setTotal(new PageInfo(list).getTotal());
+ return rspData;
+ }
+
+ /**
+ * 响应返回结果
+ *
+ * @param rows 影响行数
+ * @return 操作结果
+ */
+ protected AjaxResult toAjax(int rows)
+ {
+ return rows > 0 ? success() : error();
+ }
+
+ /**
+ * 响应返回结果
+ *
+ * @param result 结果
+ * @return 操作结果
+ */
+ protected AjaxResult toAjax(boolean result)
+ {
+ return result ? success() : error();
+ }
+
+ /**
+ * 返回成功
+ */
+ public AjaxResult success()
+ {
+ return AjaxResult.success();
+ }
+
+ /**
+ * 返回失败消息
+ */
+ public AjaxResult error()
+ {
+ return AjaxResult.error();
+ }
+
+ /**
+ * 返回成功消息
+ */
+ public AjaxResult success(String message)
+ {
+ return AjaxResult.success(message);
+ }
+
+ /**
+ * 返回失败消息
+ */
+ public AjaxResult error(String message)
+ {
+ return AjaxResult.error(message);
+ }
+
+ /**
+ * 返回错误码消息
+ */
+ public AjaxResult error(int code, String message)
+ {
+ return AjaxResult.error(code, message);
+ }
+
+ /**
+ * 页面跳转
+ */
+ public String redirect(String url)
+ {
+ return StringUtils.format("redirect:{}", url);
+ }
+
+ public User getSysUser()
+ {
+ return ShiroUtils.getSysUser();
+ }
+
+ public void setSysUser(User user)
+ {
+ ShiroUtils.setSysUser(user);
+ }
+
+ public Long getUserId()
+ {
+ return getSysUser().getUserId();
+ }
+
+ public String getLoginName()
+ {
+ return getSysUser().getLoginName();
+ }
+}
diff --git a/src/main/java/com/luckyframe/framework/web/domain/AjaxResult.java b/src/main/java/com/luckyframe/framework/web/domain/AjaxResult.java
new file mode 100644
index 0000000..f5b0560
--- /dev/null
+++ b/src/main/java/com/luckyframe/framework/web/domain/AjaxResult.java
@@ -0,0 +1,94 @@
+package com.luckyframe.framework.web.domain;
+
+import java.util.HashMap;
+
+/**
+ * 操作消息提醒
+ *
+ * @author ruoyi
+ */
+public class AjaxResult extends HashMap
+{
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 初始化一个新创建的 Message 对象
+ */
+ public AjaxResult()
+ {
+ }
+
+ /**
+ * 返回错误消息
+ *
+ * @return 错误消息
+ */
+ public static AjaxResult error()
+ {
+ return error(1, "操作失败");
+ }
+
+ /**
+ * 返回错误消息
+ *
+ * @param msg 内容
+ * @return 错误消息
+ */
+ public static AjaxResult error(String msg)
+ {
+ return error(500, msg);
+ }
+
+ /**
+ * 返回错误消息
+ *
+ * @param code 错误码
+ * @param msg 内容
+ * @return 错误消息
+ */
+ public static AjaxResult error(int code, String msg)
+ {
+ AjaxResult json = new AjaxResult();
+ json.put("code", code);
+ json.put("msg", msg);
+ return json;
+ }
+
+ /**
+ * 返回成功消息
+ *
+ * @param msg 内容
+ * @return 成功消息
+ */
+ public static AjaxResult success(String msg)
+ {
+ AjaxResult json = new AjaxResult();
+ json.put("msg", msg);
+ json.put("code", 0);
+ return json;
+ }
+
+ /**
+ * 返回成功消息
+ *
+ * @return 成功消息
+ */
+ public static AjaxResult success()
+ {
+ return AjaxResult.success("操作成功");
+ }
+
+ /**
+ * 返回成功消息
+ *
+ * @param key 键值
+ * @param value 内容
+ * @return 成功消息
+ */
+ @Override
+ public AjaxResult put(String key, Object value)
+ {
+ super.put(key, value);
+ return this;
+ }
+}
diff --git a/src/main/java/com/luckyframe/framework/web/domain/BaseEntity.java b/src/main/java/com/luckyframe/framework/web/domain/BaseEntity.java
new file mode 100644
index 0000000..5c85403
--- /dev/null
+++ b/src/main/java/com/luckyframe/framework/web/domain/BaseEntity.java
@@ -0,0 +1,114 @@
+package com.luckyframe.framework.web.domain;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Map;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.google.common.collect.Maps;
+
+/**
+ * Entity基类
+ *
+ * @author ruoyi
+ */
+public class BaseEntity implements Serializable
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 搜索值 */
+ private String searchValue;
+
+ /** 创建者 */
+ private String createBy;
+
+ /** 创建时间 */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date createTime;
+
+ /** 更新者 */
+ private String updateBy;
+
+ /** 更新时间 */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date updateTime;
+
+ /** 备注 */
+ private String remark;
+
+ /** 请求参数 */
+ private Map params;
+
+ public String getSearchValue()
+ {
+ return searchValue;
+ }
+
+ public void setSearchValue(String searchValue)
+ {
+ this.searchValue = searchValue;
+ }
+
+ public String getCreateBy()
+ {
+ return createBy;
+ }
+
+ public void setCreateBy(String createBy)
+ {
+ this.createBy = createBy;
+ }
+
+ public Date getCreateTime()
+ {
+ return createTime;
+ }
+
+ public void setCreateTime(Date createTime)
+ {
+ this.createTime = createTime;
+ }
+
+ public String getUpdateBy()
+ {
+ return updateBy;
+ }
+
+ public void setUpdateBy(String updateBy)
+ {
+ this.updateBy = updateBy;
+ }
+
+ public Date getUpdateTime()
+ {
+ return updateTime;
+ }
+
+ public void setUpdateTime(Date updateTime)
+ {
+ this.updateTime = updateTime;
+ }
+
+ public String getRemark()
+ {
+ return remark;
+ }
+
+ public void setRemark(String remark)
+ {
+ this.remark = remark;
+ }
+
+ public Map getParams()
+ {
+ if (params == null)
+ {
+ params = Maps.newHashMap();
+ }
+ return params;
+ }
+
+ public void setParams(Map params)
+ {
+ this.params = params;
+ }
+}
diff --git a/src/main/java/com/luckyframe/framework/web/exception/DefaultExceptionHandler.java b/src/main/java/com/luckyframe/framework/web/exception/DefaultExceptionHandler.java
new file mode 100644
index 0000000..16741c7
--- /dev/null
+++ b/src/main/java/com/luckyframe/framework/web/exception/DefaultExceptionHandler.java
@@ -0,0 +1,83 @@
+package com.luckyframe.framework.web.exception;
+
+import org.apache.shiro.authz.AuthorizationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import com.luckyframe.common.exception.BusinessException;
+import com.luckyframe.common.exception.DemoModeException;
+import com.luckyframe.common.utils.security.PermissionUtils;
+import com.luckyframe.framework.web.domain.AjaxResult;
+
+/**
+ * 自定义异常处理器
+ *
+ * @author ruoyi
+ */
+@RestControllerAdvice
+public class DefaultExceptionHandler
+{
+ private static final Logger log = LoggerFactory.getLogger(DefaultExceptionHandler.class);
+
+ /**
+ * 权限校验失败
+ */
+ @ExceptionHandler(AuthorizationException.class)
+ public AjaxResult handleAuthorizationException(AuthorizationException e)
+ {
+ log.error(e.getMessage(), e);
+ return AjaxResult.error(PermissionUtils.getMsg(e.getMessage()));
+ }
+
+ /**
+ * 请求方式不支持
+ */
+ @ExceptionHandler({ HttpRequestMethodNotSupportedException.class })
+ public AjaxResult handleException(HttpRequestMethodNotSupportedException e)
+ {
+ log.error(e.getMessage(), e);
+ return AjaxResult.error("不支持' " + e.getMethod() + "'请求");
+ }
+
+ /**
+ * 拦截未知的运行时异常
+ */
+ @ExceptionHandler(RuntimeException.class)
+ public AjaxResult notFount(RuntimeException e)
+ {
+ log.error("运行时异常:", e);
+ return AjaxResult.error("运行时异常:" + e.getMessage());
+ }
+
+ /**
+ * 系统异常
+ */
+ @ExceptionHandler(Exception.class)
+ public AjaxResult handleException(Exception e)
+ {
+ log.error(e.getMessage(), e);
+ return AjaxResult.error("服务器错误,请联系管理员");
+ }
+
+ /**
+ * 业务异常
+ */
+ @ExceptionHandler(BusinessException.class)
+ public AjaxResult businessException(BusinessException e)
+ {
+ log.error(e.getMessage(), e);
+ return AjaxResult.error(e.getMessage());
+ }
+
+ /**
+ * 演示模式异常
+ */
+ @ExceptionHandler(DemoModeException.class)
+ public AjaxResult demoModeException(DemoModeException e)
+ {
+ return AjaxResult.error("演示模式,不允许操作");
+ }
+
+}
diff --git a/src/main/java/com/luckyframe/framework/web/page/PageDomain.java b/src/main/java/com/luckyframe/framework/web/page/PageDomain.java
new file mode 100644
index 0000000..a81639a
--- /dev/null
+++ b/src/main/java/com/luckyframe/framework/web/page/PageDomain.java
@@ -0,0 +1,70 @@
+package com.luckyframe.framework.web.page;
+
+import com.luckyframe.common.utils.StringUtils;
+
+/**
+ * 分页数据
+ *
+ * @author ruoyi
+ */
+public class PageDomain
+{
+ /** 当前记录起始索引 */
+ private Integer pageNum;
+ /** 每页显示记录数 */
+ private Integer pageSize;
+ /** 排序列 */
+ private String orderByColumn;
+ /** 排序的方向 "desc" 或者 "asc". */
+ private String isAsc;
+
+ public String getOrderBy()
+ {
+ if (StringUtils.isEmpty(orderByColumn))
+ {
+ return "";
+ }
+ return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc;
+ }
+
+ public Integer getPageNum()
+ {
+ return pageNum;
+ }
+
+ public void setPageNum(Integer pageNum)
+ {
+ this.pageNum = pageNum;
+ }
+
+ public Integer getPageSize()
+ {
+ return pageSize;
+ }
+
+ public void setPageSize(Integer pageSize)
+ {
+ this.pageSize = pageSize;
+ }
+
+ public String getOrderByColumn()
+ {
+ return orderByColumn;
+ }
+
+ public void setOrderByColumn(String orderByColumn)
+ {
+ this.orderByColumn = orderByColumn;
+ }
+
+ public String getIsAsc()
+ {
+ return isAsc;
+ }
+
+ public void setIsAsc(String isAsc)
+ {
+ this.isAsc = isAsc;
+ }
+
+}
diff --git a/src/main/java/com/luckyframe/framework/web/page/TableDataInfo.java b/src/main/java/com/luckyframe/framework/web/page/TableDataInfo.java
new file mode 100644
index 0000000..fdd1f1a
--- /dev/null
+++ b/src/main/java/com/luckyframe/framework/web/page/TableDataInfo.java
@@ -0,0 +1,69 @@
+package com.luckyframe.framework.web.page;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 表格分页数据对象
+ *
+ * @author ruoyi
+ */
+public class TableDataInfo implements Serializable
+{
+ private static final long serialVersionUID = 1L;
+ /** 总记录数 */
+ private long total;
+ /** 列表数据 */
+ private List> rows;
+ /** 消息状态码 */
+ private int code;
+
+ /**
+ * 表格数据对象
+ */
+ public TableDataInfo()
+ {
+ }
+
+ /**
+ * 分页
+ *
+ * @param list 列表数据
+ * @param total 总记录数
+ */
+ public TableDataInfo(List> list, int total)
+ {
+ this.rows = list;
+ this.total = total;
+ }
+
+ public long getTotal()
+ {
+ return total;
+ }
+
+ public void setTotal(long total)
+ {
+ this.total = total;
+ }
+
+ public List> getRows()
+ {
+ return rows;
+ }
+
+ public void setRows(List> rows)
+ {
+ this.rows = rows;
+ }
+
+ public int getCode()
+ {
+ return code;
+ }
+
+ public void setCode(int code)
+ {
+ this.code = code;
+ }
+}
diff --git a/src/main/java/com/luckyframe/framework/web/page/TableSupport.java b/src/main/java/com/luckyframe/framework/web/page/TableSupport.java
new file mode 100644
index 0000000..8a013cd
--- /dev/null
+++ b/src/main/java/com/luckyframe/framework/web/page/TableSupport.java
@@ -0,0 +1,31 @@
+package com.luckyframe.framework.web.page;
+
+import com.luckyframe.common.utils.ServletUtils;
+import com.luckyframe.common.constant.Constants;
+
+/**
+ * 表格数据处理
+ *
+ * @author ruoyi
+ */
+public class TableSupport
+{
+ /**
+ * 封装分页对象
+ */
+ public static PageDomain getPageDomain()
+ {
+ PageDomain pageDomain = new PageDomain();
+ pageDomain.setPageNum(ServletUtils.getParameterToInt(Constants.PAGE_NUM));
+ pageDomain.setPageSize(ServletUtils.getParameterToInt(Constants.PAGE_SIZE));
+ pageDomain.setOrderByColumn(ServletUtils.getParameter(Constants.ORDER_BY_COLUMN));
+ pageDomain.setIsAsc(ServletUtils.getParameter(Constants.IS_ASC));
+ return pageDomain;
+ }
+
+ public static PageDomain buildPageRequest()
+ {
+ return getPageDomain();
+ }
+
+}
diff --git a/src/main/java/com/luckyframe/framework/web/service/ConfigService.java b/src/main/java/com/luckyframe/framework/web/service/ConfigService.java
new file mode 100644
index 0000000..a1718af
--- /dev/null
+++ b/src/main/java/com/luckyframe/framework/web/service/ConfigService.java
@@ -0,0 +1,29 @@
+package com.luckyframe.framework.web.service;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.luckyframe.project.system.config.service.IConfigService;
+
+/**
+ * ruoyi首创 html调用 thymeleaf 实现参数管理
+ *
+ * @author ruoyi
+ */
+@Service("config")
+public class ConfigService
+{
+ @Autowired
+ private IConfigService configService;
+
+ /**
+ * 根据键名查询参数配置信息
+ *
+ * @param configName 参数名称
+ * @return 参数键值
+ */
+ public String getKey(String configKey)
+ {
+ return configService.selectConfigByKey(configKey);
+ }
+
+}
diff --git a/src/main/java/com/luckyframe/framework/web/service/DictService.java b/src/main/java/com/luckyframe/framework/web/service/DictService.java
new file mode 100644
index 0000000..97dd8dc
--- /dev/null
+++ b/src/main/java/com/luckyframe/framework/web/service/DictService.java
@@ -0,0 +1,42 @@
+package com.luckyframe.framework.web.service;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.luckyframe.project.system.dict.domain.DictData;
+import com.luckyframe.project.system.dict.service.IDictDataService;
+
+/**
+ * ruoyi首创 html调用 thymeleaf 实现字典读取
+ *
+ * @author ruoyi
+ */
+@Service("dict")
+public class DictService
+{
+ @Autowired
+ private IDictDataService dictDataService;
+
+ /**
+ * 根据字典类型查询字典数据信息
+ *
+ * @param dictType 字典类型
+ * @return 参数键值
+ */
+ public List getType(String dictType)
+ {
+ return dictDataService.selectDictDataByType(dictType);
+ }
+
+ /**
+ * 根据字典类型和字典键值查询字典数据信息
+ *
+ * @param dictType 字典类型
+ * @param dictValue 字典键值
+ * @return 字典标签
+ */
+ public String getLabel(String dictType, String dictValue)
+ {
+ return dictDataService.selectDictLabel(dictType, dictValue);
+ }
+}
diff --git a/src/main/java/com/luckyframe/framework/web/service/PermissionService.java b/src/main/java/com/luckyframe/framework/web/service/PermissionService.java
new file mode 100644
index 0000000..48c3f06
--- /dev/null
+++ b/src/main/java/com/luckyframe/framework/web/service/PermissionService.java
@@ -0,0 +1,24 @@
+package com.luckyframe.framework.web.service;
+
+import org.apache.shiro.SecurityUtils;
+import org.springframework.stereotype.Service;
+
+/**
+ * RuoYi首创 js调用 thymeleaf 实现按钮权限可见性
+ *
+ * @author ruoyi
+ */
+@Service("permission")
+public class PermissionService
+{
+ public String hasPermi(String permission)
+ {
+ return isPermittedOperator(permission) ? "" : "hidden";
+ }
+
+ private boolean isPermittedOperator(String permission)
+ {
+ return SecurityUtils.getSubject().isPermitted(permission);
+ }
+
+}
diff --git a/src/main/java/com/luckyframe/project/common/CommonController.java b/src/main/java/com/luckyframe/project/common/CommonController.java
new file mode 100644
index 0000000..41773cd
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/common/CommonController.java
@@ -0,0 +1,124 @@
+package com.luckyframe.project.common;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.luckyframe.common.utils.file.FileUploadUtils;
+import com.luckyframe.common.utils.file.FileUtils;
+import com.luckyframe.framework.config.LuckyFrameConfig;
+import com.luckyframe.framework.config.ServerConfig;
+import com.luckyframe.framework.web.domain.AjaxResult;
+
+/**
+ * 通用请求处理
+ *
+ * @author ruoyi
+ */
+@Controller
+public class CommonController
+{
+ private static final Logger log = LoggerFactory.getLogger(CommonController.class);
+
+ /**
+ * 文件上传路径
+ */
+ public static final String UPLOAD_PATH = "/profile/upload/";
+
+ @Autowired
+ private ServerConfig serverConfig;
+
+ /**
+ * 通用下载请求
+ *
+ * @param fileName 文件名称
+ * @param delete 是否删除
+ */
+ @GetMapping("common/download")
+ public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request)
+ {
+ String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
+ try
+ {
+ String filePath = LuckyFrameConfig.getDownloadPath() + fileName;
+
+ response.setCharacterEncoding("utf-8");
+ response.setContentType("multipart/form-data");
+ response.setHeader("Content-Disposition",
+ "attachment;fileName=" + setFileDownloadHeader(request, realFileName));
+ FileUtils.writeBytes(filePath, response.getOutputStream());
+ if (delete)
+ {
+ FileUtils.deleteFile(filePath);
+ }
+ }
+ catch (Exception e)
+ {
+ log.error("下载文件失败", e);
+ }
+ }
+
+ /**
+ * 通用上传请求
+ */
+ @PostMapping("/common/upload")
+ @ResponseBody
+ public AjaxResult uploadFile(MultipartFile file) throws Exception
+ {
+ try
+ {
+ // 上传文件路径
+ String filePath = LuckyFrameConfig.getUploadPath();
+ // 上传并返回新文件名称
+ String fileName = FileUploadUtils.upload(filePath, file);
+ String url = serverConfig.getUrl() + UPLOAD_PATH + fileName;
+ AjaxResult ajax = AjaxResult.success();
+ ajax.put("fileName", fileName);
+ ajax.put("url", url);
+ return ajax;
+ }
+ catch (Exception e)
+ {
+ return AjaxResult.error(e.getMessage());
+ }
+ }
+
+ public String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException
+ {
+ final String agent = request.getHeader("USER-AGENT");
+ String filename = fileName;
+ if (agent.contains("MSIE"))
+ {
+ // IE浏览器
+ filename = URLEncoder.encode(filename, "utf-8");
+ filename = filename.replace("+", " ");
+ }
+ else if (agent.contains("Firefox"))
+ {
+ // 火狐浏览器
+ filename = new String(fileName.getBytes(), "ISO8859-1");
+ }
+ else if (agent.contains("Chrome"))
+ {
+ // google浏览器
+ filename = URLEncoder.encode(filename, "utf-8");
+ }
+ else
+ {
+ // 其它浏览器
+ filename = URLEncoder.encode(filename, "utf-8");
+ }
+ return filename;
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/druid/DruidController.java b/src/main/java/com/luckyframe/project/monitor/druid/DruidController.java
new file mode 100644
index 0000000..05dd16c
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/druid/DruidController.java
@@ -0,0 +1,26 @@
+package com.luckyframe.project.monitor.druid;
+
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import com.luckyframe.framework.web.controller.BaseController;
+
+/**
+ * druid 监控
+ *
+ * @author ruoyi
+ */
+@Controller
+@RequestMapping("/monitor/data")
+public class DruidController extends BaseController
+{
+ private String prefix = "/monitor/druid";
+
+ @RequiresPermissions("monitor:data:view")
+ @GetMapping()
+ public String index()
+ {
+ return redirect(prefix + "/index");
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/job/controller/JobController.java b/src/main/java/com/luckyframe/project/monitor/job/controller/JobController.java
new file mode 100644
index 0000000..0afb860
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/job/controller/JobController.java
@@ -0,0 +1,167 @@
+package com.luckyframe.project.monitor.job.controller;
+
+import java.util.List;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import com.luckyframe.common.utils.poi.ExcelUtil;
+import com.luckyframe.framework.aspectj.lang.annotation.Log;
+import com.luckyframe.framework.aspectj.lang.enums.BusinessType;
+import com.luckyframe.framework.web.controller.BaseController;
+import com.luckyframe.framework.web.domain.AjaxResult;
+import com.luckyframe.framework.web.page.TableDataInfo;
+import com.luckyframe.project.monitor.job.domain.Job;
+import com.luckyframe.project.monitor.job.service.IJobService;
+
+/**
+ * 调度任务信息操作处理
+ *
+ * @author ruoyi
+ */
+@Controller
+@RequestMapping("/monitor/job")
+public class JobController extends BaseController
+{
+ private String prefix = "monitor/job";
+
+ @Autowired
+ private IJobService jobService;
+
+ @RequiresPermissions("monitor:job:view")
+ @GetMapping()
+ public String job()
+ {
+ return prefix + "/job";
+ }
+
+ @RequiresPermissions("monitor:job:list")
+ @PostMapping("/list")
+ @ResponseBody
+ public TableDataInfo list(Job job)
+ {
+ startPage();
+ List list = jobService.selectJobList(job);
+ return getDataTable(list);
+ }
+
+ @Log(title = "定时任务", businessType = BusinessType.EXPORT)
+ @RequiresPermissions("monitor:job:export")
+ @PostMapping("/export")
+ @ResponseBody
+ public AjaxResult export(Job job)
+ {
+ List list = jobService.selectJobList(job);
+ ExcelUtil util = new ExcelUtil(Job.class);
+ return util.exportExcel(list, "定时任务");
+ }
+
+ @Log(title = "定时任务", businessType = BusinessType.DELETE)
+ @RequiresPermissions("monitor:job:remove")
+ @PostMapping("/remove")
+ @ResponseBody
+ public AjaxResult remove(String ids)
+ {
+ try
+ {
+ jobService.deleteJobByIds(ids);
+ return success();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ return error(e.getMessage());
+ }
+ }
+
+ @RequiresPermissions("monitor:job:detail")
+ @GetMapping("/detail/{jobId}")
+ public String detail(@PathVariable("jobId") Long jobId, ModelMap mmap)
+ {
+ mmap.put("name", "job");
+ mmap.put("job", jobService.selectJobById(jobId));
+ return prefix + "/detail";
+ }
+
+ /**
+ * 任务调度状态修改
+ */
+ @Log(title = "定时任务", businessType = BusinessType.UPDATE)
+ @RequiresPermissions("monitor:job:changeStatus")
+ @PostMapping("/changeStatus")
+ @ResponseBody
+ public AjaxResult changeStatus(Job job)
+ {
+ return toAjax(jobService.changeStatus(job));
+ }
+
+ /**
+ * 任务调度立即执行一次
+ */
+ @Log(title = "定时任务", businessType = BusinessType.UPDATE)
+ @RequiresPermissions("monitor:job:changeStatus")
+ @PostMapping("/run")
+ @ResponseBody
+ public AjaxResult run(Job job)
+ {
+ return toAjax(jobService.run(job));
+ }
+
+ /**
+ * 新增调度
+ */
+ @GetMapping("/add")
+ public String add()
+ {
+ return prefix + "/add";
+ }
+
+ /**
+ * 新增保存调度
+ */
+ @Log(title = "定时任务", businessType = BusinessType.INSERT)
+ @RequiresPermissions("monitor:job:add")
+ @PostMapping("/add")
+ @ResponseBody
+ public AjaxResult addSave(Job job)
+ {
+ return toAjax(jobService.insertJobCron(job));
+ }
+
+ /**
+ * 修改调度
+ */
+ @GetMapping("/edit/{jobId}")
+ public String edit(@PathVariable("jobId") Long jobId, ModelMap mmap)
+ {
+ mmap.put("job", jobService.selectJobById(jobId));
+ return prefix + "/edit";
+ }
+
+ /**
+ * 修改保存调度
+ */
+ @Log(title = "定时任务", businessType = BusinessType.UPDATE)
+ @RequiresPermissions("monitor:job:edit")
+ @PostMapping("/edit")
+ @ResponseBody
+ public AjaxResult editSave(Job job)
+ {
+ return toAjax(jobService.updateJobCron(job));
+ }
+
+ /**
+ * 校验cron表达式是否有效
+ */
+ @PostMapping("/checkCronExpressionIsValid")
+ @ResponseBody
+ public boolean checkCronExpressionIsValid(Job job)
+ {
+ return jobService.checkCronExpressionIsValid(job.getCronExpression());
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/job/controller/JobLogController.java b/src/main/java/com/luckyframe/project/monitor/job/controller/JobLogController.java
new file mode 100644
index 0000000..f342d19
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/job/controller/JobLogController.java
@@ -0,0 +1,91 @@
+package com.luckyframe.project.monitor.job.controller;
+
+import java.util.List;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import com.luckyframe.common.utils.poi.ExcelUtil;
+import com.luckyframe.framework.aspectj.lang.annotation.Log;
+import com.luckyframe.framework.aspectj.lang.enums.BusinessType;
+import com.luckyframe.framework.web.controller.BaseController;
+import com.luckyframe.framework.web.domain.AjaxResult;
+import com.luckyframe.framework.web.page.TableDataInfo;
+import com.luckyframe.project.monitor.job.domain.JobLog;
+import com.luckyframe.project.monitor.job.service.IJobLogService;
+
+/**
+ * 调度日志操作处理
+ *
+ * @author ruoyi
+ */
+@Controller
+@RequestMapping("/monitor/jobLog")
+public class JobLogController extends BaseController
+{
+ private String prefix = "monitor/job";
+
+ @Autowired
+ private IJobLogService jobLogService;
+
+ @RequiresPermissions("monitor:job:view")
+ @GetMapping()
+ public String jobLog()
+ {
+ return prefix + "/jobLog";
+ }
+
+ @RequiresPermissions("monitor:job:list")
+ @PostMapping("/list")
+ @ResponseBody
+ public TableDataInfo list(JobLog jobLog)
+ {
+ startPage();
+ List list = jobLogService.selectJobLogList(jobLog);
+ return getDataTable(list);
+ }
+
+ @Log(title = "调度日志", businessType = BusinessType.EXPORT)
+ @RequiresPermissions("monitor:job:export")
+ @PostMapping("/export")
+ @ResponseBody
+ public AjaxResult export(JobLog jobLog)
+ {
+ List list = jobLogService.selectJobLogList(jobLog);
+ ExcelUtil util = new ExcelUtil(JobLog.class);
+ return util.exportExcel(list, "调度日志");
+ }
+
+ @Log(title = "调度日志", businessType = BusinessType.DELETE)
+ @RequiresPermissions("monitor:job:remove")
+ @PostMapping("/remove")
+ @ResponseBody
+ public AjaxResult remove(String ids)
+ {
+ return toAjax(jobLogService.deleteJobLogByIds(ids));
+ }
+
+ @RequiresPermissions("monitor:job:detail")
+ @GetMapping("/detail/{jobLogId}")
+ public String detail(@PathVariable("jobLogId") Long jobLogId, ModelMap mmap)
+ {
+ mmap.put("name", "jobLog");
+ mmap.put("jobLog", jobLogService.selectJobLogById(jobLogId));
+ return prefix + "/detail";
+ }
+
+ @Log(title = "调度日志", businessType = BusinessType.CLEAN)
+ @RequiresPermissions("monitor:job:remove")
+ @PostMapping("/clean")
+ @ResponseBody
+ public AjaxResult clean()
+ {
+ jobLogService.cleanJobLog();
+ return success();
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/job/domain/Job.java b/src/main/java/com/luckyframe/project/monitor/job/domain/Job.java
new file mode 100644
index 0000000..e227e1a
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/job/domain/Job.java
@@ -0,0 +1,162 @@
+package com.luckyframe.project.monitor.job.domain;
+
+import java.io.Serializable;
+import java.util.Date;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.luckyframe.common.constant.ScheduleConstants;
+import com.luckyframe.common.utils.StringUtils;
+import com.luckyframe.framework.aspectj.lang.annotation.Excel;
+import com.luckyframe.framework.web.domain.BaseEntity;
+import com.luckyframe.project.monitor.job.util.CronUtils;
+
+/**
+ * 定时任务调度信息 sys_job
+ *
+ * @author ruoyi
+ */
+public class Job extends BaseEntity implements Serializable
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 任务ID */
+ @Excel(name = "任务序号")
+ private Long jobId;
+
+ /** 任务名称 */
+ @Excel(name = "任务名称")
+ private String jobName;
+
+ /** 任务组名 */
+ @Excel(name = "任务组名")
+ private String jobGroup;
+
+ /** 任务方法 */
+ @Excel(name = "任务方法")
+ private String methodName;
+
+ /** 方法参数 */
+ @Excel(name = "方法参数")
+ private String methodParams;
+
+ /** cron执行表达式 */
+ @Excel(name = "执行表达式 ")
+ private String cronExpression;
+
+ /** cron计划策略 */
+ @Excel(name = "计划策略 ")
+ private String misfirePolicy = ScheduleConstants.MISFIRE_DEFAULT;
+
+ /** 任务状态(0正常 1暂停) */
+ @Excel(name = "任务状态", readConverterExp = "0=正常,1=暂停")
+ private String status;
+
+ public Long getJobId()
+ {
+ return jobId;
+ }
+
+ public void setJobId(Long jobId)
+ {
+ this.jobId = jobId;
+ }
+
+ 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 getMethodName()
+ {
+ return methodName;
+ }
+
+ public void setMethodName(String methodName)
+ {
+ this.methodName = methodName;
+ }
+
+ public String getMethodParams()
+ {
+ return methodParams;
+ }
+
+ public void setMethodParams(String methodParams)
+ {
+ this.methodParams = methodParams;
+ }
+
+ 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 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("methodName", getMethodName())
+ .append("methodParams", getMethodParams())
+ .append("cronExpression", getCronExpression())
+ .append("nextValidTime", getNextValidTime())
+ .append("misfirePolicy", getMisfirePolicy())
+ .append("status", getStatus())
+ .append("createBy", getCreateBy())
+ .append("createTime", getCreateTime())
+ .append("updateBy", getUpdateBy())
+ .append("updateTime", getUpdateTime())
+ .append("remark", getRemark())
+ .toString();
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/job/domain/JobLog.java b/src/main/java/com/luckyframe/project/monitor/job/domain/JobLog.java
new file mode 100644
index 0000000..b2e6caf
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/job/domain/JobLog.java
@@ -0,0 +1,143 @@
+package com.luckyframe.project.monitor.job.domain;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.luckyframe.framework.aspectj.lang.annotation.Excel;
+import com.luckyframe.framework.web.domain.BaseEntity;
+
+/**
+ * 定时任务调度日志表 sys_job_log
+ *
+ * @author ruoyi
+ */
+public class JobLog 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 methodName;
+
+ /** 方法参数 */
+ @Excel(name = "方法参数")
+ private String methodParams;
+
+ /** 日志信息 */
+ @Excel(name = "日志信息")
+ private String jobMessage;
+
+ /** 执行状态(0正常 1失败) */
+ @Excel(name = "执行状态", readConverterExp = "0=正常,1=失败")
+ private String status;
+
+ /** 异常信息 */
+ @Excel(name = "异常信息")
+ private String exceptionInfo;
+
+ 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 getMethodName()
+ {
+ return methodName;
+ }
+
+ public void setMethodName(String methodName)
+ {
+ this.methodName = methodName;
+ }
+
+ public String getMethodParams()
+ {
+ return methodParams;
+ }
+
+ public void setMethodParams(String methodParams)
+ {
+ this.methodParams = methodParams;
+ }
+
+ 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;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("jobLogId", getJobLogId())
+ .append("jobName", getJobName())
+ .append("jobGroup", getJobGroup())
+ .append("methodName", getMethodName())
+ .append("methodParams", getMethodParams())
+ .append("jobMessage", getJobMessage())
+ .append("status", getStatus())
+ .append("exceptionInfo", getExceptionInfo())
+ .append("createTime", getCreateTime())
+ .toString();
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/job/mapper/JobLogMapper.java b/src/main/java/com/luckyframe/project/monitor/job/mapper/JobLogMapper.java
new file mode 100644
index 0000000..e90f56e
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/job/mapper/JobLogMapper.java
@@ -0,0 +1,64 @@
+package com.luckyframe.project.monitor.job.mapper;
+
+import java.util.List;
+import com.luckyframe.project.monitor.job.domain.JobLog;
+
+/**
+ * 调度任务日志信息 数据层
+ *
+ * @author ruoyi
+ */
+public interface JobLogMapper
+{
+ /**
+ * 获取quartz调度器日志的计划任务
+ *
+ * @param jobLog 调度日志信息
+ * @return 调度任务日志集合
+ */
+ public List selectJobLogList(JobLog jobLog);
+
+ /**
+ * 查询所有调度任务日志
+ *
+ * @return 调度任务日志列表
+ */
+ public List selectJobLogAll();
+
+ /**
+ * 通过调度任务日志ID查询调度信息
+ *
+ * @param jobLogId 调度任务日志ID
+ * @return 调度任务日志对象信息
+ */
+ public JobLog selectJobLogById(Long jobLogId);
+
+ /**
+ * 新增任务日志
+ *
+ * @param jobLog 调度日志信息
+ * @return 结果
+ */
+ public int insertJobLog(JobLog jobLog);
+
+ /**
+ * 批量删除调度日志信息
+ *
+ * @param ids 需要删除的数据ID
+ * @return 结果
+ */
+ public int deleteJobLogByIds(String[] ids);
+
+ /**
+ * 删除任务日志
+ *
+ * @param jobId 调度日志ID
+ * @return 结果
+ */
+ public int deleteJobLogById(Long jobId);
+
+ /**
+ * 清空任务日志
+ */
+ public void cleanJobLog();
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/job/mapper/JobMapper.java b/src/main/java/com/luckyframe/project/monitor/job/mapper/JobMapper.java
new file mode 100644
index 0000000..f9b14fc
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/job/mapper/JobMapper.java
@@ -0,0 +1,67 @@
+package com.luckyframe.project.monitor.job.mapper;
+
+import java.util.List;
+import com.luckyframe.project.monitor.job.domain.Job;
+
+/**
+ * 调度任务信息 数据层
+ *
+ * @author ruoyi
+ */
+public interface JobMapper
+{
+ /**
+ * 查询调度任务日志集合
+ *
+ * @param job 调度信息
+ * @return 操作日志集合
+ */
+ public List selectJobList(Job job);
+
+ /**
+ * 查询所有调度任务
+ *
+ * @return 调度任务列表
+ */
+ public List selectJobAll();
+
+ /**
+ * 通过调度ID查询调度任务信息
+ *
+ * @param jobId 调度ID
+ * @return 角色对象信息
+ */
+ public Job 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(Job job);
+
+ /**
+ * 新增调度任务信息
+ *
+ * @param job 调度任务信息
+ * @return 结果
+ */
+ public int insertJob(Job job);
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/job/service/IJobLogService.java b/src/main/java/com/luckyframe/project/monitor/job/service/IJobLogService.java
new file mode 100644
index 0000000..3917e8e
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/job/service/IJobLogService.java
@@ -0,0 +1,56 @@
+package com.luckyframe.project.monitor.job.service;
+
+import java.util.List;
+import com.luckyframe.project.monitor.job.domain.JobLog;
+
+/**
+ * 定时任务调度日志信息信息 服务层
+ *
+ * @author ruoyi
+ */
+public interface IJobLogService
+{
+ /**
+ * 获取quartz调度器日志的计划任务
+ *
+ * @param jobLog 调度日志信息
+ * @return 调度任务日志集合
+ */
+ public List selectJobLogList(JobLog jobLog);
+
+ /**
+ * 通过调度任务日志ID查询调度信息
+ *
+ * @param jobLogId 调度任务日志ID
+ * @return 调度任务日志对象信息
+ */
+ public JobLog selectJobLogById(Long jobLogId);
+
+ /**
+ * 新增任务日志
+ *
+ * @param jobLog 调度日志信息
+ */
+ public void addJobLog(JobLog jobLog);
+
+ /**
+ * 批量删除调度日志信息
+ *
+ * @param ids 需要删除的数据ID
+ * @return 结果
+ */
+ public int deleteJobLogByIds(String ids);
+
+ /**
+ * 删除任务日志
+ *
+ * @param jobId 调度日志ID
+ * @return 结果
+ */
+ public int deleteJobLogById(Long jobId);
+
+ /**
+ * 清空任务日志
+ */
+ public void cleanJobLog();
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/job/service/IJobService.java b/src/main/java/com/luckyframe/project/monitor/job/service/IJobService.java
new file mode 100644
index 0000000..ad27ced
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/job/service/IJobService.java
@@ -0,0 +1,100 @@
+package com.luckyframe.project.monitor.job.service;
+
+import java.util.List;
+import com.luckyframe.project.monitor.job.domain.Job;
+
+/**
+ * 定时任务调度信息信息 服务层
+ *
+ * @author ruoyi
+ */
+public interface IJobService
+{
+ /**
+ * 获取quartz调度器的计划任务
+ *
+ * @param job 调度信息
+ * @return 调度任务集合
+ */
+ public List selectJobList(Job job);
+
+ /**
+ * 通过调度任务ID查询调度信息
+ *
+ * @param jobId 调度任务ID
+ * @return 调度任务对象信息
+ */
+ public Job selectJobById(Long jobId);
+
+ /**
+ * 暂停任务
+ *
+ * @param job 调度信息
+ * @return 结果
+ */
+ public int pauseJob(Job job);
+
+ /**
+ * 恢复任务
+ *
+ * @param job 调度信息
+ * @return 结果
+ */
+ public int resumeJob(Job job);
+
+ /**
+ * 删除任务后,所对应的trigger也将被删除
+ *
+ * @param job 调度信息
+ * @return 结果
+ */
+ public int deleteJob(Job job);
+
+ /**
+ * 批量删除调度信息
+ *
+ * @param ids 需要删除的数据ID
+ * @return 结果
+ */
+ public void deleteJobByIds(String ids);
+
+ /**
+ * 任务调度状态修改
+ *
+ * @param job 调度信息
+ * @return 结果
+ */
+ public int changeStatus(Job job);
+
+ /**
+ * 立即运行任务
+ *
+ * @param job 调度信息
+ * @return 结果
+ */
+ public int run(Job job);
+
+ /**
+ * 新增任务表达式
+ *
+ * @param job 调度信息
+ * @return 结果
+ */
+ public int insertJobCron(Job job);
+
+ /**
+ * 更新任务的时间表达式
+ *
+ * @param job 调度信息
+ * @return 结果
+ */
+ public int updateJobCron(Job job);
+
+ /**
+ * 校验cron表达式是否有效
+ *
+ * @param cronExpression 表达式
+ * @return 结果
+ */
+ public boolean checkCronExpressionIsValid(String cronExpression);
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/job/service/JobLogServiceImpl.java b/src/main/java/com/luckyframe/project/monitor/job/service/JobLogServiceImpl.java
new file mode 100644
index 0000000..d8bb0f1
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/job/service/JobLogServiceImpl.java
@@ -0,0 +1,87 @@
+package com.luckyframe.project.monitor.job.service;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.luckyframe.common.support.Convert;
+import com.luckyframe.project.monitor.job.domain.JobLog;
+import com.luckyframe.project.monitor.job.mapper.JobLogMapper;
+
+/**
+ * 定时任务调度日志信息 服务层
+ *
+ * @author ruoyi
+ */
+@Service
+public class JobLogServiceImpl implements IJobLogService
+{
+ @Autowired
+ private JobLogMapper jobLogMapper;
+
+ /**
+ * 获取quartz调度器日志的计划任务
+ *
+ * @param jobLog 调度日志信息
+ * @return 调度任务日志集合
+ */
+ @Override
+ public List selectJobLogList(JobLog jobLog)
+ {
+ return jobLogMapper.selectJobLogList(jobLog);
+ }
+
+ /**
+ * 通过调度任务日志ID查询调度信息
+ *
+ * @param jobId 调度任务日志ID
+ * @return 调度任务日志对象信息
+ */
+ @Override
+ public JobLog selectJobLogById(Long jobLogId)
+ {
+ return jobLogMapper.selectJobLogById(jobLogId);
+ }
+
+ /**
+ * 新增任务日志
+ *
+ * @param jobLog 调度日志信息
+ */
+ @Override
+ public void addJobLog(JobLog jobLog)
+ {
+ jobLogMapper.insertJobLog(jobLog);
+ }
+
+ /**
+ * 批量删除调度日志信息
+ *
+ * @param ids 需要删除的数据ID
+ * @return 结果
+ */
+ @Override
+ public int deleteJobLogByIds(String ids)
+ {
+ return jobLogMapper.deleteJobLogByIds(Convert.toStrArray(ids));
+ }
+
+ /**
+ * 删除任务日志
+ *
+ * @param jobId 调度日志ID
+ */
+ @Override
+ public int deleteJobLogById(Long jobId)
+ {
+ return jobLogMapper.deleteJobLogById(jobId);
+ }
+
+ /**
+ * 清空任务日志
+ */
+ @Override
+ public void cleanJobLog()
+ {
+ jobLogMapper.cleanJobLog();
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/job/service/JobServiceImpl.java b/src/main/java/com/luckyframe/project/monitor/job/service/JobServiceImpl.java
new file mode 100644
index 0000000..b329052
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/job/service/JobServiceImpl.java
@@ -0,0 +1,224 @@
+package com.luckyframe.project.monitor.job.service;
+
+import java.util.List;
+import javax.annotation.PostConstruct;
+import org.quartz.CronTrigger;
+import org.quartz.Scheduler;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.luckyframe.common.constant.ScheduleConstants;
+import com.luckyframe.common.support.Convert;
+import com.luckyframe.common.utils.security.ShiroUtils;
+import com.luckyframe.project.monitor.job.domain.Job;
+import com.luckyframe.project.monitor.job.mapper.JobMapper;
+import com.luckyframe.project.monitor.job.util.CronUtils;
+import com.luckyframe.project.monitor.job.util.ScheduleUtils;
+
+/**
+ * 定时任务调度信息 服务层
+ *
+ * @author ruoyi
+ */
+@Service
+public class JobServiceImpl implements IJobService
+{
+ @Autowired
+ private Scheduler scheduler;
+
+ @Autowired
+ private JobMapper jobMapper;
+
+ /**
+ * 项目启动时,初始化定时器
+ */
+ @PostConstruct
+ public void init()
+ {
+ List jobList = jobMapper.selectJobAll();
+ for (Job job : jobList)
+ {
+ CronTrigger cronTrigger = ScheduleUtils.getCronTrigger(scheduler, job.getJobId());
+ // 如果不存在,则创建
+ if (cronTrigger == null)
+ {
+ ScheduleUtils.createScheduleJob(scheduler, job);
+ }
+ else
+ {
+ ScheduleUtils.updateScheduleJob(scheduler, job);
+ }
+ }
+ }
+
+ /**
+ * 获取quartz调度器的计划任务列表
+ *
+ * @param job 调度信息
+ * @return
+ */
+ @Override
+ public List selectJobList(Job job)
+ {
+ return jobMapper.selectJobList(job);
+ }
+
+ /**
+ * 通过调度任务ID查询调度信息
+ *
+ * @param jobId 调度任务ID
+ * @return 调度任务对象信息
+ */
+ @Override
+ public Job selectJobById(Long jobId)
+ {
+ return jobMapper.selectJobById(jobId);
+ }
+
+ /**
+ * 暂停任务
+ *
+ * @param job 调度信息
+ */
+ @Override
+ public int pauseJob(Job job)
+ {
+ job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
+ job.setUpdateBy(ShiroUtils.getLoginName());
+ int rows = jobMapper.updateJob(job);
+ if (rows > 0)
+ {
+ ScheduleUtils.pauseJob(scheduler, job.getJobId());
+ }
+ return rows;
+ }
+
+ /**
+ * 恢复任务
+ *
+ * @param job 调度信息
+ */
+ @Override
+ public int resumeJob(Job job)
+ {
+ job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
+ job.setUpdateBy(ShiroUtils.getLoginName());
+ int rows = jobMapper.updateJob(job);
+ if (rows > 0)
+ {
+ ScheduleUtils.resumeJob(scheduler, job.getJobId());
+ }
+ return rows;
+ }
+
+ /**
+ * 删除任务后,所对应的trigger也将被删除
+ *
+ * @param job 调度信息
+ */
+ @Override
+ public int deleteJob(Job job)
+ {
+ int rows = jobMapper.deleteJobById(job.getJobId());
+ if (rows > 0)
+ {
+ ScheduleUtils.deleteScheduleJob(scheduler, job.getJobId());
+ }
+ return rows;
+ }
+
+ /**
+ * 批量删除调度信息
+ *
+ * @param ids 需要删除的数据ID
+ * @return 结果
+ */
+ @Override
+ public void deleteJobByIds(String ids)
+ {
+ Long[] jobIds = Convert.toLongArray(ids);
+ for (Long jobId : jobIds)
+ {
+ Job job = jobMapper.selectJobById(jobId);
+ deleteJob(job);
+ }
+ }
+
+ /**
+ * 任务调度状态修改
+ *
+ * @param job 调度信息
+ */
+ @Override
+ public int changeStatus(Job job)
+ {
+ 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
+ public int run(Job job)
+ {
+ return ScheduleUtils.run(scheduler, selectJobById(job.getJobId()));
+ }
+
+ /**
+ * 新增任务
+ *
+ * @param job 调度信息 调度信息
+ */
+ @Override
+ public int insertJobCron(Job job)
+ {
+ job.setCreateBy(ShiroUtils.getLoginName());
+ job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
+ int rows = jobMapper.insertJob(job);
+ if (rows > 0)
+ {
+ ScheduleUtils.createScheduleJob(scheduler, job);
+ }
+ return rows;
+ }
+
+ /**
+ * 更新任务的时间表达式
+ *
+ * @param job 调度信息
+ */
+ @Override
+ public int updateJobCron(Job job)
+ {
+ job.setUpdateBy(ShiroUtils.getLoginName());
+ int rows = jobMapper.updateJob(job);
+ if (rows > 0)
+ {
+ ScheduleUtils.updateScheduleJob(scheduler, job);
+ }
+ return rows;
+ }
+
+ /**
+ * 校验cron表达式是否有效
+ *
+ * @param cronExpression 表达式
+ * @return 结果
+ */
+ @Override
+ public boolean checkCronExpressionIsValid(String cronExpression)
+ {
+ return CronUtils.isValid(cronExpression);
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/job/task/RyTask.java b/src/main/java/com/luckyframe/project/monitor/job/task/RyTask.java
new file mode 100644
index 0000000..c4fe1a1
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/job/task/RyTask.java
@@ -0,0 +1,22 @@
+package com.luckyframe.project.monitor.job.task;
+
+import org.springframework.stereotype.Component;
+
+/**
+ * 定时任务调度测试
+ *
+ * @author ruoyi
+ */
+@Component("ryTask")
+public class RyTask
+{
+ public void ryParams(String params)
+ {
+ System.out.println("执行有参方法:" + params);
+ }
+
+ public void ryNoParams()
+ {
+ System.out.println("执行无参方法");
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/job/util/CronUtils.java b/src/main/java/com/luckyframe/project/monitor/job/util/CronUtils.java
new file mode 100644
index 0000000..fd24a07
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/job/util/CronUtils.java
@@ -0,0 +1,63 @@
+package com.luckyframe.project.monitor.job.util;
+
+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);
+ }
+
+ /**
+ * 返回一个字符串值,表示该消息无效Cron表达式给出有效性
+ *
+ * @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/src/main/java/com/luckyframe/project/monitor/job/util/ScheduleJob.java b/src/main/java/com/luckyframe/project/monitor/job/util/ScheduleJob.java
new file mode 100644
index 0000000..b89a16f
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/job/util/ScheduleJob.java
@@ -0,0 +1,80 @@
+package com.luckyframe.project.monitor.job.util;
+
+import java.util.Date;
+import java.util.concurrent.Future;
+import org.quartz.DisallowConcurrentExecution;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.scheduling.quartz.QuartzJobBean;
+import com.luckyframe.common.constant.Constants;
+import com.luckyframe.common.constant.ScheduleConstants;
+import com.luckyframe.common.utils.StringUtils;
+import com.luckyframe.common.utils.bean.BeanUtils;
+import com.luckyframe.common.utils.spring.SpringUtils;
+import com.luckyframe.project.monitor.job.domain.Job;
+import com.luckyframe.project.monitor.job.domain.JobLog;
+import com.luckyframe.project.monitor.job.service.IJobLogService;
+
+/**
+ * 定时任务
+ *
+ * @author ruoyi
+ *
+ */
+@DisallowConcurrentExecution
+public class ScheduleJob extends QuartzJobBean
+{
+ private static final Logger log = LoggerFactory.getLogger(ScheduleJob.class);
+
+ private ThreadPoolTaskExecutor executor = SpringUtils.getBean("threadPoolTaskExecutor");
+
+ @Override
+ protected void executeInternal(JobExecutionContext context) throws JobExecutionException
+ {
+ Job job = new Job();
+ BeanUtils.copyBeanProp(job, context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES));
+
+ IJobLogService jobLogService = (IJobLogService) SpringUtils.getBean(IJobLogService.class);
+
+ JobLog jobLog = new JobLog();
+ jobLog.setJobName(job.getJobName());
+ jobLog.setJobGroup(job.getJobGroup());
+ jobLog.setMethodName(job.getMethodName());
+ jobLog.setMethodParams(job.getMethodParams());
+ jobLog.setCreateTime(new Date());
+
+ long startTime = System.currentTimeMillis();
+
+ try
+ {
+ // 执行任务
+ log.info("任务开始执行 - 名称:{} 方法:{}", job.getJobName(), job.getMethodName());
+ ScheduleRunnable task = new ScheduleRunnable(job.getJobName(), job.getMethodName(), job.getMethodParams());
+ Future> future = executor.submit(task);
+ future.get();
+ long times = System.currentTimeMillis() - startTime;
+ // 任务状态 0:成功 1:失败
+ jobLog.setStatus(Constants.SUCCESS);
+ jobLog.setJobMessage(job.getJobName() + " 总共耗时:" + times + "毫秒");
+
+ log.info("任务执行结束 - 名称:{} 耗时:{} 毫秒", job.getJobName(), times);
+ }
+ catch (Exception e)
+ {
+ log.info("任务执行失败 - 名称:{} 方法:{}", job.getJobName(), job.getMethodName());
+ log.error("任务执行异常 - :", e);
+ long times = System.currentTimeMillis() - startTime;
+ jobLog.setJobMessage(job.getJobName() + " 总共耗时:" + times + "毫秒");
+ // 任务状态 0:成功 1:失败
+ jobLog.setStatus(Constants.FAIL);
+ jobLog.setExceptionInfo(StringUtils.substring(e.getMessage(), 0, 2000));
+ }
+ finally
+ {
+ jobLogService.addJobLog(jobLog);
+ }
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/job/util/ScheduleRunnable.java b/src/main/java/com/luckyframe/project/monitor/job/util/ScheduleRunnable.java
new file mode 100644
index 0000000..cc2a192
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/job/util/ScheduleRunnable.java
@@ -0,0 +1,56 @@
+package com.luckyframe.project.monitor.job.util;
+
+import java.lang.reflect.Method;
+import org.springframework.util.ReflectionUtils;
+import com.luckyframe.common.utils.StringUtils;
+import com.luckyframe.common.utils.spring.SpringUtils;
+
+/**
+ * 执行定时任务
+ *
+ * @author ruoyi
+ *
+ */
+public class ScheduleRunnable implements Runnable
+{
+ private Object target;
+ private Method method;
+ private String params;
+
+ public ScheduleRunnable(String beanName, String methodName, String params)
+ throws NoSuchMethodException, SecurityException
+ {
+ this.target = SpringUtils.getBean(beanName);
+ this.params = params;
+
+ if (StringUtils.isNotEmpty(params))
+ {
+ this.method = target.getClass().getDeclaredMethod(methodName, String.class);
+ }
+ else
+ {
+ this.method = target.getClass().getDeclaredMethod(methodName);
+ }
+ }
+
+ @Override
+ public void run()
+ {
+ try
+ {
+ ReflectionUtils.makeAccessible(method);
+ if (StringUtils.isNotEmpty(params))
+ {
+ method.invoke(target, params);
+ }
+ else
+ {
+ method.invoke(target);
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/job/util/ScheduleUtils.java b/src/main/java/com/luckyframe/project/monitor/job/util/ScheduleUtils.java
new file mode 100644
index 0000000..8c61a12
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/job/util/ScheduleUtils.java
@@ -0,0 +1,227 @@
+package com.luckyframe.project.monitor.job.util;
+
+import org.quartz.CronScheduleBuilder;
+import org.quartz.CronTrigger;
+import org.quartz.JobBuilder;
+import org.quartz.JobDataMap;
+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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.luckyframe.common.constant.ScheduleConstants;
+import com.luckyframe.common.exception.job.TaskException;
+import com.luckyframe.common.exception.job.TaskException.Code;
+import com.luckyframe.project.monitor.job.domain.Job;
+
+/**
+ * 定时任务工具类
+ *
+ * @author ruoyi
+ *
+ */
+public class ScheduleUtils
+{
+ private static final Logger log = LoggerFactory.getLogger(ScheduleUtils.class);
+
+ /**
+ * 获取触发器key
+ */
+ public static TriggerKey getTriggerKey(Long jobId)
+ {
+ return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME + jobId);
+ }
+
+ /**
+ * 获取jobKey
+ */
+ public static JobKey getJobKey(Long jobId)
+ {
+ return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + jobId);
+ }
+
+ /**
+ * 获取表达式触发器
+ */
+ public static CronTrigger getCronTrigger(Scheduler scheduler, Long jobId)
+ {
+ try
+ {
+ System.out.println(">>>>>>>>scheduler:"+scheduler.getSchedulerName());
+ System.out.println(">>>>>>>>jobId:"+jobId);
+ System.out.println(">>>>>>>>getName:"+getTriggerKey(jobId).getName());
+ return (CronTrigger) scheduler.getTrigger(getTriggerKey(jobId));
+ }
+ catch (SchedulerException e)
+ {
+ log.error("getCronTrigger 异常:", e);
+ }
+ return null;
+ }
+
+ /**
+ * 创建定时任务
+ */
+ public static void createScheduleJob(Scheduler scheduler, Job job)
+ {
+ try
+ {
+ // 构建job信息
+ JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(job.getJobId())).build();
+
+ // 表达式调度构建器
+ CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
+ cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder);
+
+ // 按新的cronExpression表达式构建一个新的trigger
+ CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(job.getJobId())).withSchedule(cronScheduleBuilder).build();
+
+ // 放入参数,运行时的方法可以获取
+ jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job);
+
+ scheduler.scheduleJob(jobDetail, trigger);
+
+ // 暂停任务
+ if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue()))
+ {
+ pauseJob(scheduler, job.getJobId());
+ }
+ }
+ catch (SchedulerException e)
+ {
+ log.error("createScheduleJob 异常:", e);
+ }
+ catch (TaskException e)
+ {
+ log.error("createScheduleJob 异常:", e);
+ }
+ }
+
+ /**
+ * 更新定时任务
+ */
+ public static void updateScheduleJob(Scheduler scheduler, Job job)
+ {
+ try
+ {
+ TriggerKey triggerKey = getTriggerKey(job.getJobId());
+
+ // 表达式调度构建器
+ CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
+ cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder);
+
+ CronTrigger trigger = getCronTrigger(scheduler, job.getJobId());
+
+ // 按新的cronExpression表达式重新构建trigger
+ trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();
+
+ // 参数
+ trigger.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job);
+
+ scheduler.rescheduleJob(triggerKey, trigger);
+
+ // 暂停任务
+ if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue()))
+ {
+ pauseJob(scheduler, job.getJobId());
+ }
+
+ }
+ catch (SchedulerException e)
+ {
+ log.error("SchedulerException 异常:", e);
+ }
+ catch (TaskException e)
+ {
+ log.error("SchedulerException 异常:", e);
+ }
+ }
+
+ /**
+ * 立即执行任务
+ */
+ public static int run(Scheduler scheduler, Job job)
+ {
+ int rows = 0;
+ try
+ {
+ // 参数
+ JobDataMap dataMap = new JobDataMap();
+ dataMap.put(ScheduleConstants.TASK_PROPERTIES, job);
+
+ scheduler.triggerJob(getJobKey(job.getJobId()), dataMap);
+ rows = 1;
+ }
+ catch (SchedulerException e)
+ {
+ log.error("run 异常:", e);
+ }
+ return rows;
+ }
+
+ /**
+ * 暂停任务
+ */
+ public static void pauseJob(Scheduler scheduler, Long jobId)
+ {
+ try
+ {
+ scheduler.pauseJob(getJobKey(jobId));
+ }
+ catch (SchedulerException e)
+ {
+ log.error("pauseJob 异常:", e);
+ }
+ }
+
+ /**
+ * 恢复任务
+ */
+ public static void resumeJob(Scheduler scheduler, Long jobId)
+ {
+ try
+ {
+ scheduler.resumeJob(getJobKey(jobId));
+ }
+ catch (SchedulerException e)
+ {
+ log.error("resumeJob 异常:", e);
+ }
+ }
+
+ /**
+ * 删除定时任务
+ */
+ public static void deleteScheduleJob(Scheduler scheduler, Long jobId)
+ {
+ try
+ {
+ scheduler.deleteJob(getJobKey(jobId));
+ }
+ catch (SchedulerException e)
+ {
+ log.error("deleteScheduleJob 异常:", e);
+ }
+ }
+
+ public static CronScheduleBuilder handleCronScheduleMisfirePolicy(Job 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);
+ }
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/logininfor/controller/LogininforController.java b/src/main/java/com/luckyframe/project/monitor/logininfor/controller/LogininforController.java
new file mode 100644
index 0000000..b2478a6
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/logininfor/controller/LogininforController.java
@@ -0,0 +1,80 @@
+package com.luckyframe.project.monitor.logininfor.controller;
+
+import java.util.List;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import com.luckyframe.common.utils.poi.ExcelUtil;
+import com.luckyframe.framework.aspectj.lang.annotation.Log;
+import com.luckyframe.framework.aspectj.lang.enums.BusinessType;
+import com.luckyframe.framework.web.controller.BaseController;
+import com.luckyframe.framework.web.domain.AjaxResult;
+import com.luckyframe.framework.web.page.TableDataInfo;
+import com.luckyframe.project.monitor.logininfor.domain.Logininfor;
+import com.luckyframe.project.monitor.logininfor.service.ILogininforService;
+
+/**
+ * 系统访问记录
+ *
+ * @author ruoyi
+ */
+@Controller
+@RequestMapping("/monitor/logininfor")
+public class LogininforController extends BaseController
+{
+ private String prefix = "monitor/logininfor";
+
+ @Autowired
+ private ILogininforService logininforService;
+
+ @RequiresPermissions("monitor:logininfor:view")
+ @GetMapping()
+ public String logininfor()
+ {
+ return prefix + "/logininfor";
+ }
+
+ @RequiresPermissions("monitor:logininfor:list")
+ @PostMapping("/list")
+ @ResponseBody
+ public TableDataInfo list(Logininfor logininfor)
+ {
+ startPage();
+ List list = logininforService.selectLogininforList(logininfor);
+ return getDataTable(list);
+ }
+
+ @Log(title = "登陆日志", businessType = BusinessType.EXPORT)
+ @RequiresPermissions("monitor:logininfor:export")
+ @PostMapping("/export")
+ @ResponseBody
+ public AjaxResult export(Logininfor logininfor)
+ {
+ List list = logininforService.selectLogininforList(logininfor);
+ ExcelUtil util = new ExcelUtil(Logininfor.class);
+ return util.exportExcel(list, "登陆日志");
+ }
+
+ @RequiresPermissions("monitor:logininfor:remove")
+ @Log(title = "登陆日志", businessType = BusinessType.DELETE)
+ @PostMapping("/remove")
+ @ResponseBody
+ public AjaxResult remove(String ids)
+ {
+ return toAjax(logininforService.deleteLogininforByIds(ids));
+ }
+
+ @RequiresPermissions("monitor:logininfor:remove")
+ @Log(title = "登陆日志", businessType = BusinessType.CLEAN)
+ @PostMapping("/clean")
+ @ResponseBody
+ public AjaxResult clean()
+ {
+ logininforService.cleanLogininfor();
+ return success();
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/logininfor/domain/Logininfor.java b/src/main/java/com/luckyframe/project/monitor/logininfor/domain/Logininfor.java
new file mode 100644
index 0000000..beac470
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/logininfor/domain/Logininfor.java
@@ -0,0 +1,158 @@
+package com.luckyframe.project.monitor.logininfor.domain;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.luckyframe.framework.aspectj.lang.annotation.Excel;
+import com.luckyframe.framework.web.domain.BaseEntity;
+import java.util.Date;
+
+/**
+ * 系统访问记录表 sys_logininfor
+ *
+ * @author ruoyi
+ */
+public class Logininfor extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+ /** ID */
+ @Excel(name = "序号")
+ private Long infoId;
+
+ /** 用户账号 */
+ @Excel(name = "用户账号")
+ private String loginName;
+
+ /** 登录状态 0成功 1失败 */
+ @Excel(name = "登录状态", readConverterExp = "0=成功,1=失败")
+ private String status;
+
+ /** 登录IP地址 */
+ @Excel(name = "登录地址")
+ private String ipaddr;
+
+ /** 登录地点 */
+ @Excel(name = "登录地点")
+ private String loginLocation;
+
+ /** 浏览器类型 */
+ @Excel(name = "浏览器")
+ private String browser;
+
+ /** 操作系统 */
+ @Excel(name = "操作系统 ")
+ private String os;
+
+ /** 提示消息 */
+ @Excel(name = "提示消息")
+ private String msg;
+
+ /** 访问时间 */
+ @Excel(name = "访问时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+ private Date loginTime;
+
+ public Long getInfoId()
+ {
+ return infoId;
+ }
+
+ public void setInfoId(Long infoId)
+ {
+ this.infoId = infoId;
+ }
+
+ public String getLoginName()
+ {
+ return loginName;
+ }
+
+ public void setLoginName(String loginName)
+ {
+ this.loginName = loginName;
+ }
+
+ public String getStatus()
+ {
+ return status;
+ }
+
+ public void setStatus(String status)
+ {
+ this.status = status;
+ }
+
+ public String getIpaddr()
+ {
+ return ipaddr;
+ }
+
+ public void setIpaddr(String ipaddr)
+ {
+ this.ipaddr = ipaddr;
+ }
+
+ public String getLoginLocation()
+ {
+ return loginLocation;
+ }
+
+ public void setLoginLocation(String loginLocation)
+ {
+ this.loginLocation = loginLocation;
+ }
+
+ public String getBrowser()
+ {
+ return browser;
+ }
+
+ public void setBrowser(String browser)
+ {
+ this.browser = browser;
+ }
+
+ public String getOs()
+ {
+ return os;
+ }
+
+ public void setOs(String os)
+ {
+ this.os = os;
+ }
+
+ public String getMsg()
+ {
+ return msg;
+ }
+
+ public void setMsg(String msg)
+ {
+ this.msg = msg;
+ }
+
+ public Date getLoginTime()
+ {
+ return loginTime;
+ }
+
+ public void setLoginTime(Date loginTime)
+ {
+ this.loginTime = loginTime;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("infoId", getInfoId())
+ .append("loginName", getLoginName())
+ .append("ipaddr", getIpaddr())
+ .append("loginLocation", getLoginLocation())
+ .append("browser", getBrowser())
+ .append("os", getOs())
+ .append("status", getStatus())
+ .append("msg", getMsg())
+ .append("loginTime", getLoginTime())
+ .toString();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/luckyframe/project/monitor/logininfor/mapper/LogininforMapper.java b/src/main/java/com/luckyframe/project/monitor/logininfor/mapper/LogininforMapper.java
new file mode 100644
index 0000000..c5d3632
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/logininfor/mapper/LogininforMapper.java
@@ -0,0 +1,42 @@
+package com.luckyframe.project.monitor.logininfor.mapper;
+
+import java.util.List;
+import com.luckyframe.project.monitor.logininfor.domain.Logininfor;
+
+/**
+ * 系统访问日志情况信息 数据层
+ *
+ * @author ruoyi
+ */
+public interface LogininforMapper
+{
+ /**
+ * 新增系统登录日志
+ *
+ * @param logininfor 访问日志对象
+ */
+ public void insertLogininfor(Logininfor logininfor);
+
+ /**
+ * 查询系统登录日志集合
+ *
+ * @param logininfor 访问日志对象
+ * @return 登录记录集合
+ */
+ public List selectLogininforList(Logininfor logininfor);
+
+ /**
+ * 批量删除系统登录日志
+ *
+ * @param ids 需要删除的数据
+ * @return 结果
+ */
+ public int deleteLogininforByIds(String[] ids);
+
+ /**
+ * 清空系统登录日志
+ *
+ * @return 结果
+ */
+ public int cleanLogininfor();
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/logininfor/service/ILogininforService.java b/src/main/java/com/luckyframe/project/monitor/logininfor/service/ILogininforService.java
new file mode 100644
index 0000000..2ae946e
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/logininfor/service/ILogininforService.java
@@ -0,0 +1,40 @@
+package com.luckyframe.project.monitor.logininfor.service;
+
+import java.util.List;
+import com.luckyframe.project.monitor.logininfor.domain.Logininfor;
+
+/**
+ * 系统访问日志情况信息 服务层
+ *
+ * @author ruoyi
+ */
+public interface ILogininforService
+{
+ /**
+ * 新增系统登录日志
+ *
+ * @param logininfor 访问日志对象
+ */
+ public void insertLogininfor(Logininfor logininfor);
+
+ /**
+ * 查询系统登录日志集合
+ *
+ * @param logininfor 访问日志对象
+ * @return 登录记录集合
+ */
+ public List selectLogininforList(Logininfor logininfor);
+
+ /**
+ * 批量删除系统登录日志
+ *
+ * @param ids 需要删除的数据
+ * @return
+ */
+ public int deleteLogininforByIds(String ids);
+
+ /**
+ * 清空系统登录日志
+ */
+ public void cleanLogininfor();
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/logininfor/service/LogininforServiceImpl.java b/src/main/java/com/luckyframe/project/monitor/logininfor/service/LogininforServiceImpl.java
new file mode 100644
index 0000000..860aaff
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/logininfor/service/LogininforServiceImpl.java
@@ -0,0 +1,64 @@
+package com.luckyframe.project.monitor.logininfor.service;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.luckyframe.common.support.Convert;
+import com.luckyframe.project.monitor.logininfor.domain.Logininfor;
+import com.luckyframe.project.monitor.logininfor.mapper.LogininforMapper;
+
+/**
+ * 系统访问日志情况信息 服务层处理
+ *
+ * @author ruoyi
+ */
+@Service
+public class LogininforServiceImpl implements ILogininforService
+{
+ @Autowired
+ private LogininforMapper logininforMapper;
+
+ /**
+ * 新增系统登录日志
+ *
+ * @param logininfor 访问日志对象
+ */
+ @Override
+ public void insertLogininfor(Logininfor logininfor)
+ {
+ logininforMapper.insertLogininfor(logininfor);
+ }
+
+ /**
+ * 查询系统登录日志集合
+ *
+ * @param logininfor 访问日志对象
+ * @return 登录记录集合
+ */
+ @Override
+ public List selectLogininforList(Logininfor logininfor)
+ {
+ return logininforMapper.selectLogininforList(logininfor);
+ }
+
+ /**
+ * 批量删除系统登录日志
+ *
+ * @param ids 需要删除的数据
+ * @return
+ */
+ @Override
+ public int deleteLogininforByIds(String ids)
+ {
+ return logininforMapper.deleteLogininforByIds(Convert.toStrArray(ids));
+ }
+
+ /**
+ * 清空系统登录日志
+ */
+ @Override
+ public void cleanLogininfor()
+ {
+ logininforMapper.cleanLogininfor();
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/online/controller/UserOnlineController.java b/src/main/java/com/luckyframe/project/monitor/online/controller/UserOnlineController.java
new file mode 100644
index 0000000..bd80d70
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/online/controller/UserOnlineController.java
@@ -0,0 +1,111 @@
+package com.luckyframe.project.monitor.online.controller;
+
+import java.util.List;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import com.luckyframe.common.utils.security.ShiroUtils;
+import com.luckyframe.framework.aspectj.lang.annotation.Log;
+import com.luckyframe.framework.aspectj.lang.enums.BusinessType;
+import com.luckyframe.framework.shiro.session.OnlineSessionDAO;
+import com.luckyframe.framework.web.controller.BaseController;
+import com.luckyframe.framework.web.domain.AjaxResult;
+import com.luckyframe.framework.web.page.TableDataInfo;
+import com.luckyframe.project.monitor.online.domain.OnlineSession;
+import com.luckyframe.project.monitor.online.domain.UserOnline;
+import com.luckyframe.project.monitor.online.service.IUserOnlineService;
+
+/**
+ * 在线用户监控
+ *
+ * @author ruoyi
+ */
+@Controller
+@RequestMapping("/monitor/online")
+public class UserOnlineController extends BaseController
+{
+ private String prefix = "monitor/online";
+
+ @Autowired
+ private IUserOnlineService userOnlineService;
+
+ @Autowired
+ private OnlineSessionDAO onlineSessionDAO;
+
+ @RequiresPermissions("monitor:online:view")
+ @GetMapping()
+ public String online()
+ {
+ return prefix + "/online";
+ }
+
+ @RequiresPermissions("monitor:online:list")
+ @PostMapping("/list")
+ @ResponseBody
+ public TableDataInfo list(UserOnline userOnline)
+ {
+ startPage();
+ List list = userOnlineService.selectUserOnlineList(userOnline);
+ return getDataTable(list);
+ }
+
+ @RequiresPermissions("monitor:online:batchForceLogout")
+ @Log(title = "在线用户", businessType = BusinessType.FORCE)
+ @PostMapping("/batchForceLogout")
+ @ResponseBody
+ public AjaxResult batchForceLogout(@RequestParam("ids[]") String[] ids)
+ {
+ for (String sessionId : ids)
+ {
+ UserOnline online = userOnlineService.selectOnlineById(sessionId);
+ if (online == null)
+ {
+ return error("用户已下线");
+ }
+ OnlineSession onlineSession = (OnlineSession) onlineSessionDAO.readSession(online.getSessionId());
+ if (onlineSession == null)
+ {
+ return error("用户已下线");
+ }
+ if (sessionId.equals(ShiroUtils.getSessionId()))
+ {
+ return error("当前登陆用户无法强退");
+ }
+ onlineSession.setStatus(OnlineSession.OnlineStatus.off_line);
+ online.setStatus(OnlineSession.OnlineStatus.off_line);
+ userOnlineService.saveOnline(online);
+ }
+ return success();
+ }
+
+ @RequiresPermissions("monitor:online:forceLogout")
+ @Log(title = "在线用户", businessType = BusinessType.FORCE)
+ @PostMapping("/forceLogout")
+ @ResponseBody
+ public AjaxResult forceLogout(String sessionId)
+ {
+ UserOnline online = userOnlineService.selectOnlineById(sessionId);
+ if (sessionId.equals(ShiroUtils.getSessionId()))
+ {
+ return error("当前登陆用户无法强退");
+ }
+ if (online == null)
+ {
+ return error("用户已下线");
+ }
+ OnlineSession onlineSession = (OnlineSession) onlineSessionDAO.readSession(online.getSessionId());
+ if (onlineSession == null)
+ {
+ return error("用户已下线");
+ }
+ onlineSession.setStatus(OnlineSession.OnlineStatus.off_line);
+ online.setStatus(OnlineSession.OnlineStatus.off_line);
+ userOnlineService.saveOnline(online);
+ return success();
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/online/domain/OnlineSession.java b/src/main/java/com/luckyframe/project/monitor/online/domain/OnlineSession.java
new file mode 100644
index 0000000..7d96bb2
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/online/domain/OnlineSession.java
@@ -0,0 +1,153 @@
+package com.luckyframe.project.monitor.online.domain;
+
+import org.apache.shiro.session.mgt.SimpleSession;
+
+/**
+ * 在线用户会话属性
+ *
+ * @author ruoyi
+ */
+public class OnlineSession extends SimpleSession
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 用户ID */
+ private Long userId;
+
+ /** 用户名称 */
+ private String loginName;
+
+ /** 部门名称 */
+ private String deptName;
+
+ /** 登录IP地址 */
+ private String host;
+
+ /** 浏览器类型 */
+ private String browser;
+
+ /** 操作系统 */
+ private String os;
+
+ /** 在线状态 */
+ private OnlineStatus status = OnlineStatus.on_line;
+
+ /** 属性是否改变 优化session数据同步 */
+ private transient boolean attributeChanged = false;
+
+ @Override
+ public String getHost()
+ {
+ return host;
+ }
+
+ @Override
+ public void setHost(String host)
+ {
+ this.host = host;
+ }
+
+ public String getBrowser()
+ {
+ return browser;
+ }
+
+ public void setBrowser(String browser)
+ {
+ this.browser = browser;
+ }
+
+ public String getOs()
+ {
+ return os;
+ }
+
+ public void setOs(String os)
+ {
+ this.os = os;
+ }
+
+ public Long getUserId()
+ {
+ return userId;
+ }
+
+ public void setUserId(Long userId)
+ {
+ this.userId = userId;
+ }
+
+ public String getLoginName()
+ {
+ return loginName;
+ }
+
+ public void setLoginName(String loginName)
+ {
+ this.loginName = loginName;
+ }
+
+ public String getDeptName()
+ {
+ return deptName;
+ }
+
+ public void setDeptName(String deptName)
+ {
+ this.deptName = deptName;
+ }
+
+ public OnlineStatus getStatus()
+ {
+ return status;
+ }
+
+ public void setStatus(OnlineStatus status)
+ {
+ this.status = status;
+ }
+
+ public void markAttributeChanged()
+ {
+ this.attributeChanged = true;
+ }
+
+ public void resetAttributeChanged()
+ {
+ this.attributeChanged = false;
+ }
+
+ public boolean isAttributeChanged()
+ {
+ return attributeChanged;
+ }
+
+ @Override
+ public void setAttribute(Object key, Object value)
+ {
+ super.setAttribute(key, value);
+ }
+
+ @Override
+ public Object removeAttribute(Object key)
+ {
+ return super.removeAttribute(key);
+ }
+
+ public static enum OnlineStatus
+ {
+ /** 用户状态 */
+ on_line("在线"), off_line("离线");
+ private final String info;
+
+ private OnlineStatus(String info)
+ {
+ this.info = info;
+ }
+
+ public String getInfo()
+ {
+ return info;
+ }
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/online/domain/UserOnline.java b/src/main/java/com/luckyframe/project/monitor/online/domain/UserOnline.java
new file mode 100644
index 0000000..aa562db
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/online/domain/UserOnline.java
@@ -0,0 +1,190 @@
+package com.luckyframe.project.monitor.online.domain;
+
+import java.util.Date;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.luckyframe.framework.web.domain.BaseEntity;
+import com.luckyframe.project.monitor.online.domain.OnlineSession.OnlineStatus;
+
+/**
+ * 当前在线会话 sys_user_online
+ *
+ * @author ruoyi
+ */
+public class UserOnline extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 用户会话id */
+ private String sessionId;
+
+ /** 部门名称 */
+ private String deptName;
+
+ /** 登录名称 */
+ private String loginName;
+
+ /** 登录IP地址 */
+ private String ipaddr;
+
+ /** 登录地址 */
+ private String loginLocation;
+
+ /** 浏览器类型 */
+ private String browser;
+
+ /** 操作系统 */
+ private String os;
+
+ /** session创建时间 */
+ private Date startTimestamp;
+
+ /** session最后访问时间 */
+ private Date lastAccessTime;
+
+ /** 超时时间,单位为分钟 */
+ private Long expireTime;
+
+ /** 在线状态 */
+ private OnlineStatus status = OnlineStatus.on_line;
+
+ /** 备份的当前用户会话 */
+ private OnlineSession session;
+
+ public String getSessionId()
+ {
+ return sessionId;
+ }
+
+ public void setSessionId(String sessionId)
+ {
+ this.sessionId = sessionId;
+ }
+
+ public String getDeptName()
+ {
+ return deptName;
+ }
+
+ public void setDeptName(String deptName)
+ {
+ this.deptName = deptName;
+ }
+
+ public String getLoginName()
+ {
+ return loginName;
+ }
+
+ public void setLoginName(String loginName)
+ {
+ this.loginName = loginName;
+ }
+
+ public String getIpaddr()
+ {
+ return ipaddr;
+ }
+
+ public void setIpaddr(String ipaddr)
+ {
+ this.ipaddr = ipaddr;
+ }
+
+ public String getLoginLocation()
+ {
+ return loginLocation;
+ }
+
+ public void setLoginLocation(String loginLocation)
+ {
+ this.loginLocation = loginLocation;
+ }
+
+ public String getBrowser()
+ {
+ return browser;
+ }
+
+ public void setBrowser(String browser)
+ {
+ this.browser = browser;
+ }
+
+ public String getOs()
+ {
+ return os;
+ }
+
+ public void setOs(String os)
+ {
+ this.os = os;
+ }
+
+ public Date getStartTimestamp()
+ {
+ return startTimestamp;
+ }
+
+ public void setStartTimestamp(Date startTimestamp)
+ {
+ this.startTimestamp = startTimestamp;
+ }
+
+ public Date getLastAccessTime()
+ {
+ return lastAccessTime;
+ }
+
+ public void setLastAccessTime(Date lastAccessTime)
+ {
+ this.lastAccessTime = lastAccessTime;
+ }
+
+ public Long getExpireTime()
+ {
+ return expireTime;
+ }
+
+ public void setExpireTime(Long expireTime)
+ {
+ this.expireTime = expireTime;
+ }
+
+ public OnlineStatus getStatus()
+ {
+ return status;
+ }
+
+ public void setStatus(OnlineStatus status)
+ {
+ this.status = status;
+ }
+
+ public OnlineSession getSession()
+ {
+ return session;
+ }
+
+ public void setSession(OnlineSession session)
+ {
+ this.session = session;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("sessionId", getSessionId())
+ .append("loginName", getLoginName())
+ .append("deptName", getDeptName())
+ .append("ipaddr", getIpaddr())
+ .append("loginLocation", getLoginLocation())
+ .append("browser", getBrowser())
+ .append("os", getOs())
+ .append("status", getStatus())
+ .append("startTimestamp", getStartTimestamp())
+ .append("lastAccessTime", getLastAccessTime())
+ .append("expireTime", getExpireTime())
+ .toString();
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/online/mapper/UserOnlineMapper.java b/src/main/java/com/luckyframe/project/monitor/online/mapper/UserOnlineMapper.java
new file mode 100644
index 0000000..32120b9
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/online/mapper/UserOnlineMapper.java
@@ -0,0 +1,52 @@
+package com.luckyframe.project.monitor.online.mapper;
+
+import java.util.List;
+import com.luckyframe.project.monitor.online.domain.UserOnline;
+
+/**
+ * 在线用户 数据层
+ *
+ * @author ruoyi
+ */
+public interface UserOnlineMapper
+{
+ /**
+ * 通过会话序号查询信息
+ *
+ * @param sessionId 会话ID
+ * @return 在线用户信息
+ */
+ public UserOnline selectOnlineById(String sessionId);
+
+ /**
+ * 通过会话序号删除信息
+ *
+ * @param sessionId 会话ID
+ * @return 在线用户信息
+ */
+ public int deleteOnlineById(String sessionId);
+
+ /**
+ * 保存会话信息
+ *
+ * @param online 会话信息
+ * @return 结果
+ */
+ public int saveOnline(UserOnline online);
+
+ /**
+ * 查询会话集合
+ *
+ * @param userOnline 会话参数
+ * @return 会话集合
+ */
+ public List selectUserOnlineList(UserOnline userOnline);
+
+ /**
+ * 查询过期会话集合
+ *
+ * @param lastAccessTime 过期时间
+ * @return 会话集合
+ */
+ public List selectOnlineByExpired(String lastAccessTime);
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/online/service/IUserOnlineService.java b/src/main/java/com/luckyframe/project/monitor/online/service/IUserOnlineService.java
new file mode 100644
index 0000000..c2fdcc1
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/online/service/IUserOnlineService.java
@@ -0,0 +1,67 @@
+package com.luckyframe.project.monitor.online.service;
+
+import java.util.Date;
+import java.util.List;
+import com.luckyframe.project.monitor.online.domain.UserOnline;
+
+/**
+ * 在线用户 服务层
+ *
+ * @author ruoyi
+ */
+public interface IUserOnlineService
+{
+ /**
+ * 通过会话序号查询信息
+ *
+ * @param sessionId 会话ID
+ * @return 在线用户信息
+ */
+ public UserOnline selectOnlineById(String sessionId);
+
+ /**
+ * 通过会话序号删除信息
+ *
+ * @param sessionId 会话ID
+ * @return 在线用户信息
+ */
+ public void deleteOnlineById(String sessionId);
+
+ /**
+ * 通过会话序号删除信息
+ *
+ * @param sessions 会话ID集合
+ * @return 在线用户信息
+ */
+ public void batchDeleteOnline(List sessions);
+
+ /**
+ * 保存会话信息
+ *
+ * @param online 会话信息
+ */
+ public void saveOnline(UserOnline online);
+
+ /**
+ * 查询会话集合
+ *
+ * @param userOnline 分页参数
+ * @return 会话集合
+ */
+ public List selectUserOnlineList(UserOnline userOnline);
+
+ /**
+ * 强退用户
+ *
+ * @param sessionId 会话ID
+ */
+ public void forceLogout(String sessionId);
+
+ /**
+ * 查询会话集合
+ *
+ * @param expiredDate 有效期
+ * @return 会话集合
+ */
+ public List selectOnlineByExpired(Date expiredDate);
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/online/service/UserOnlineServiceImpl.java b/src/main/java/com/luckyframe/project/monitor/online/service/UserOnlineServiceImpl.java
new file mode 100644
index 0000000..23f2578
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/online/service/UserOnlineServiceImpl.java
@@ -0,0 +1,125 @@
+package com.luckyframe.project.monitor.online.service;
+
+import java.util.Date;
+import java.util.List;
+import org.apache.shiro.session.Session;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.luckyframe.common.utils.DateUtils;
+import com.luckyframe.common.utils.StringUtils;
+import com.luckyframe.framework.shiro.session.OnlineSessionDAO;
+import com.luckyframe.project.monitor.online.domain.UserOnline;
+import com.luckyframe.project.monitor.online.mapper.UserOnlineMapper;
+
+/**
+ * 在线用户 服务层处理
+ *
+ * @author ruoyi
+ */
+@Service
+public class UserOnlineServiceImpl implements IUserOnlineService
+{
+ @Autowired
+ private UserOnlineMapper userOnlineDao;
+
+ @Autowired
+ private OnlineSessionDAO onlineSessionDAO;
+
+ /**
+ * 通过会话序号查询信息
+ *
+ * @param sessionId 会话ID
+ * @return 在线用户信息
+ */
+ @Override
+ public UserOnline selectOnlineById(String sessionId)
+ {
+ return userOnlineDao.selectOnlineById(sessionId);
+ }
+
+ /**
+ * 通过会话序号删除信息
+ *
+ * @param sessionId 会话ID
+ * @return 在线用户信息
+ */
+ @Override
+ public void deleteOnlineById(String sessionId)
+ {
+ UserOnline userOnline = selectOnlineById(sessionId);
+ if (StringUtils.isNotNull(userOnline))
+ {
+ userOnlineDao.deleteOnlineById(sessionId);
+ }
+ }
+
+ /**
+ * 通过会话序号删除信息
+ *
+ * @param sessions 会话ID集合
+ * @return 在线用户信息
+ */
+ @Override
+ public void batchDeleteOnline(List sessions)
+ {
+ for (String sessionId : sessions)
+ {
+ UserOnline userOnline = selectOnlineById(sessionId);
+ if (StringUtils.isNotNull(userOnline))
+ {
+ userOnlineDao.deleteOnlineById(sessionId);
+ }
+ }
+ }
+
+ /**
+ * 保存会话信息
+ *
+ * @param online 会话信息
+ */
+ @Override
+ public void saveOnline(UserOnline online)
+ {
+ userOnlineDao.saveOnline(online);
+ }
+
+ /**
+ * 查询会话集合
+ *
+ * @param pageUtilEntity 分页参数
+ */
+ @Override
+ public List selectUserOnlineList(UserOnline userOnline)
+ {
+ return userOnlineDao.selectUserOnlineList(userOnline);
+ }
+
+ /**
+ * 强退用户
+ *
+ * @param sessionId 会话ID
+ */
+ @Override
+ public void forceLogout(String sessionId)
+ {
+ Session session = onlineSessionDAO.readSession(sessionId);
+ if (session == null)
+ {
+ return;
+ }
+ session.setTimeout(1000);
+ userOnlineDao.deleteOnlineById(sessionId);
+ }
+
+ /**
+ * 查询会话集合
+ *
+ * @param online 会话信息
+ */
+ @Override
+ public List selectOnlineByExpired(Date expiredDate)
+ {
+ String lastAccessTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, expiredDate);
+ return userOnlineDao.selectOnlineByExpired(lastAccessTime);
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/operlog/controller/OperlogController.java b/src/main/java/com/luckyframe/project/monitor/operlog/controller/OperlogController.java
new file mode 100644
index 0000000..665c31a
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/operlog/controller/OperlogController.java
@@ -0,0 +1,89 @@
+package com.luckyframe.project.monitor.operlog.controller;
+
+import java.util.List;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import com.luckyframe.common.utils.poi.ExcelUtil;
+import com.luckyframe.framework.aspectj.lang.annotation.Log;
+import com.luckyframe.framework.aspectj.lang.enums.BusinessType;
+import com.luckyframe.framework.web.controller.BaseController;
+import com.luckyframe.framework.web.domain.AjaxResult;
+import com.luckyframe.framework.web.page.TableDataInfo;
+import com.luckyframe.project.monitor.operlog.domain.OperLog;
+import com.luckyframe.project.monitor.operlog.service.IOperLogService;
+
+/**
+ * 操作日志记录
+ *
+ * @author ruoyi
+ */
+@Controller
+@RequestMapping("/monitor/operlog")
+public class OperlogController extends BaseController
+{
+ private String prefix = "monitor/operlog";
+
+ @Autowired
+ private IOperLogService operLogService;
+
+ @RequiresPermissions("monitor:operlog:view")
+ @GetMapping()
+ public String operlog()
+ {
+ return prefix + "/operlog";
+ }
+
+ @RequiresPermissions("monitor:operlog:list")
+ @PostMapping("/list")
+ @ResponseBody
+ public TableDataInfo list(OperLog operLog)
+ {
+ startPage();
+ List list = operLogService.selectOperLogList(operLog);
+ return getDataTable(list);
+ }
+
+ @Log(title = "操作日志", businessType = BusinessType.EXPORT)
+ @RequiresPermissions("monitor:operlog:export")
+ @PostMapping("/export")
+ @ResponseBody
+ public AjaxResult export(OperLog operLog)
+ {
+ List list = operLogService.selectOperLogList(operLog);
+ ExcelUtil util = new ExcelUtil(OperLog.class);
+ return util.exportExcel(list, "操作日志");
+ }
+
+ @RequiresPermissions("monitor:operlog:remove")
+ @PostMapping("/remove")
+ @ResponseBody
+ public AjaxResult remove(String ids)
+ {
+ return toAjax(operLogService.deleteOperLogByIds(ids));
+ }
+
+ @RequiresPermissions("monitor:operlog:detail")
+ @GetMapping("/detail/{operId}")
+ public String detail(@PathVariable("operId") Long operId, ModelMap mmap)
+ {
+ mmap.put("operLog", operLogService.selectOperLogById(operId));
+ return prefix + "/detail";
+ }
+
+ @Log(title = "操作日志", businessType = BusinessType.CLEAN)
+ @RequiresPermissions("monitor:operlog:remove")
+ @PostMapping("/clean")
+ @ResponseBody
+ public AjaxResult clean()
+ {
+ operLogService.cleanOperLog();
+ return success();
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/operlog/domain/OperLog.java b/src/main/java/com/luckyframe/project/monitor/operlog/domain/OperLog.java
new file mode 100644
index 0000000..be74b49
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/operlog/domain/OperLog.java
@@ -0,0 +1,233 @@
+package com.luckyframe.project.monitor.operlog.domain;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import java.util.Date;
+import com.luckyframe.framework.aspectj.lang.annotation.Excel;
+import com.luckyframe.framework.web.domain.BaseEntity;
+
+/**
+ * 操作日志记录表 oper_log
+ *
+ * @author ruoyi
+ */
+public class OperLog extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 日志主键 */
+ @Excel(name = "操作序号")
+ private Long operId;
+
+ /** 操作模块 */
+ @Excel(name = "操作模块")
+ private String title;
+
+ /** 操作业务类型 */
+ @Excel(name = "业务类型", readConverterExp = "0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据")
+ private Integer businessType;
+
+ /** 请求方法 */
+ @Excel(name = "请求方法")
+ private String method;
+
+ /** 操作人类别 */
+ @Excel(name = "操作类别", readConverterExp = "0=其它,1=后台用户,2=手机端用户")
+ private Integer operatorType;
+
+ /** 操作人员 */
+ @Excel(name = "操作人员")
+ private String operName;
+
+ /** 部门名称 */
+ @Excel(name = "部门名称")
+ private String deptName;
+
+ /** 请求url */
+ @Excel(name = "请求地址")
+ private String operUrl;
+
+ /** 操作地址 */
+ @Excel(name = "操作地址")
+ private String operIp;
+
+ /** 操作地点 */
+ @Excel(name = "操作地点")
+ private String operLocation;
+
+ /** 请求参数 */
+ @Excel(name = "请求参数")
+ private String operParam;
+
+ /** 状态0正常 1异常 */
+ @Excel(name = "状态", readConverterExp = "0=正常,1=异常")
+ private Integer status;
+
+ /** 错误消息 */
+ @Excel(name = "错误消息")
+ private String errorMsg;
+
+ /** 操作时间 */
+ @Excel(name = "操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+ private Date operTime;
+
+ public Long getOperId()
+ {
+ return operId;
+ }
+
+ public void setOperId(Long operId)
+ {
+ this.operId = operId;
+ }
+
+ public String getTitle()
+ {
+ return title;
+ }
+
+ public void setTitle(String title)
+ {
+ this.title = title;
+ }
+
+ public Integer getBusinessType()
+ {
+ return businessType;
+ }
+
+ public void setBusinessType(Integer businessType)
+ {
+ this.businessType = businessType;
+ }
+
+ public String getMethod()
+ {
+ return method;
+ }
+
+ public void setMethod(String method)
+ {
+ this.method = method;
+ }
+
+ public Integer getOperatorType()
+ {
+ return operatorType;
+ }
+
+ public void setOperatorType(Integer operatorType)
+ {
+ this.operatorType = operatorType;
+ }
+
+ public String getOperName()
+ {
+ return operName;
+ }
+
+ public void setOperName(String operName)
+ {
+ this.operName = operName;
+ }
+
+ public String getDeptName()
+ {
+ return deptName;
+ }
+
+ public void setDeptName(String deptName)
+ {
+ this.deptName = deptName;
+ }
+
+ public String getOperUrl()
+ {
+ return operUrl;
+ }
+
+ public void setOperUrl(String operUrl)
+ {
+ this.operUrl = operUrl;
+ }
+
+ public String getOperIp()
+ {
+ return operIp;
+ }
+
+ public void setOperIp(String operIp)
+ {
+ this.operIp = operIp;
+ }
+
+ public String getOperLocation()
+ {
+ return operLocation;
+ }
+
+ public void setOperLocation(String operLocation)
+ {
+ this.operLocation = operLocation;
+ }
+
+ public String getOperParam()
+ {
+ return operParam;
+ }
+
+ public void setOperParam(String operParam)
+ {
+ this.operParam = operParam;
+ }
+
+ public Integer getStatus()
+ {
+ return status;
+ }
+
+ public void setStatus(Integer status)
+ {
+ this.status = status;
+ }
+
+ public String getErrorMsg()
+ {
+ return errorMsg;
+ }
+
+ public void setErrorMsg(String errorMsg)
+ {
+ this.errorMsg = errorMsg;
+ }
+
+ public Date getOperTime()
+ {
+ return operTime;
+ }
+
+ public void setOperTime(Date operTime)
+ {
+ this.operTime = operTime;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("operId", getOperId())
+ .append("title", getTitle())
+ .append("businessType", getBusinessType())
+ .append("method", getMethod())
+ .append("operatorType", getOperatorType())
+ .append("operName", getOperName())
+ .append("deptName", getDeptName())
+ .append("operUrl", getOperUrl())
+ .append("operIp", getOperIp())
+ .append("operLocation", getOperLocation())
+ .append("operParam", getOperParam())
+ .append("status", getStatus())
+ .append("errorMsg", getErrorMsg())
+ .append("operTime", getOperTime())
+ .toString();
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/operlog/mapper/OperLogMapper.java b/src/main/java/com/luckyframe/project/monitor/operlog/mapper/OperLogMapper.java
new file mode 100644
index 0000000..dd6a938
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/operlog/mapper/OperLogMapper.java
@@ -0,0 +1,48 @@
+package com.luckyframe.project.monitor.operlog.mapper;
+
+import java.util.List;
+import com.luckyframe.project.monitor.operlog.domain.OperLog;
+
+/**
+ * 操作日志 数据层
+ *
+ * @author ruoyi
+ */
+public interface OperLogMapper
+{
+ /**
+ * 新增操作日志
+ *
+ * @param operLog 操作日志对象
+ */
+ public void insertOperlog(OperLog operLog);
+
+ /**
+ * 查询系统操作日志集合
+ *
+ * @param operLog 操作日志对象
+ * @return 操作日志集合
+ */
+ public List selectOperLogList(OperLog operLog);
+
+ /**
+ * 批量删除系统操作日志
+ *
+ * @param ids 需要删除的数据
+ * @return 结果
+ */
+ public int deleteOperLogByIds(String[] ids);
+
+ /**
+ * 查询操作日志详细
+ *
+ * @param operId 操作ID
+ * @return 操作日志对象
+ */
+ public OperLog selectOperLogById(Long operId);
+
+ /**
+ * 清空操作日志
+ */
+ public void cleanOperLog();
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/operlog/service/IOperLogService.java b/src/main/java/com/luckyframe/project/monitor/operlog/service/IOperLogService.java
new file mode 100644
index 0000000..4a731ec
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/operlog/service/IOperLogService.java
@@ -0,0 +1,48 @@
+package com.luckyframe.project.monitor.operlog.service;
+
+import java.util.List;
+import com.luckyframe.project.monitor.operlog.domain.OperLog;
+
+/**
+ * 操作日志 服务层
+ *
+ * @author ruoyi
+ */
+public interface IOperLogService
+{
+ /**
+ * 新增操作日志
+ *
+ * @param operLog 操作日志对象
+ */
+ public void insertOperlog(OperLog operLog);
+
+ /**
+ * 查询系统操作日志集合
+ *
+ * @param operLog 操作日志对象
+ * @return 操作日志集合
+ */
+ public List selectOperLogList(OperLog operLog);
+
+ /**
+ * 批量删除系统操作日志
+ *
+ * @param ids 需要删除的数据
+ * @return 结果
+ */
+ public int deleteOperLogByIds(String ids);
+
+ /**
+ * 查询操作日志详细
+ *
+ * @param operId 操作ID
+ * @return 操作日志对象
+ */
+ public OperLog selectOperLogById(Long operId);
+
+ /**
+ * 清空操作日志
+ */
+ public void cleanOperLog();
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/operlog/service/OperLogServiceImpl.java b/src/main/java/com/luckyframe/project/monitor/operlog/service/OperLogServiceImpl.java
new file mode 100644
index 0000000..e315ffa
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/operlog/service/OperLogServiceImpl.java
@@ -0,0 +1,76 @@
+package com.luckyframe.project.monitor.operlog.service;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.luckyframe.common.support.Convert;
+import com.luckyframe.project.monitor.operlog.domain.OperLog;
+import com.luckyframe.project.monitor.operlog.mapper.OperLogMapper;
+
+/**
+ * 操作日志 服务层处理
+ *
+ * @author ruoyi
+ */
+@Service
+public class OperLogServiceImpl implements IOperLogService
+{
+ @Autowired
+ private OperLogMapper operLogMapper;
+
+ /**
+ * 新增操作日志
+ *
+ * @param operLog 操作日志对象
+ */
+ @Override
+ public void insertOperlog(OperLog operLog)
+ {
+ operLogMapper.insertOperlog(operLog);
+ }
+
+ /**
+ * 查询系统操作日志集合
+ *
+ * @param operLog 操作日志对象
+ * @return 操作日志集合
+ */
+ @Override
+ public List selectOperLogList(OperLog operLog)
+ {
+ return operLogMapper.selectOperLogList(operLog);
+ }
+
+ /**
+ * 批量删除系统操作日志
+ *
+ * @param ids 需要删除的数据
+ * @return
+ */
+ @Override
+ public int deleteOperLogByIds(String ids)
+ {
+ return operLogMapper.deleteOperLogByIds(Convert.toStrArray(ids));
+ }
+
+ /**
+ * 查询操作日志详细
+ *
+ * @param operId 操作ID
+ * @return 操作日志对象
+ */
+ @Override
+ public OperLog selectOperLogById(Long operId)
+ {
+ return operLogMapper.selectOperLogById(operId);
+ }
+
+ /**
+ * 清空操作日志
+ */
+ @Override
+ public void cleanOperLog()
+ {
+ operLogMapper.cleanOperLog();
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/server/controller/ServerController.java b/src/main/java/com/luckyframe/project/monitor/server/controller/ServerController.java
new file mode 100644
index 0000000..7373fd6
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/server/controller/ServerController.java
@@ -0,0 +1,31 @@
+package com.luckyframe.project.monitor.server.controller;
+
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import com.luckyframe.framework.web.controller.BaseController;
+import com.luckyframe.project.monitor.server.domain.Server;
+
+/**
+ * 服务器监控
+ *
+ * @author ruoyi
+ */
+@Controller
+@RequestMapping("/monitor/server")
+public class ServerController extends BaseController
+{
+ private String prefix = "monitor/server";
+
+ @RequiresPermissions("monitor:server:view")
+ @GetMapping()
+ public String server(ModelMap mmap) throws Exception
+ {
+ Server server = new Server();
+ server.copyTo();
+ mmap.put("server", server);
+ return prefix + "/server";
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/server/domain/Cpu.java b/src/main/java/com/luckyframe/project/monitor/server/domain/Cpu.java
new file mode 100644
index 0000000..3c83ad1
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/server/domain/Cpu.java
@@ -0,0 +1,101 @@
+package com.luckyframe.project.monitor.server.domain;
+
+import com.luckyframe.common.utils.Arith;
+
+/**
+ * CPU相关信息
+ *
+ * @author ruoyi
+ */
+public class Cpu
+{
+ /**
+ * 核心数
+ */
+ private int cpuNum;
+
+ /**
+ * CPU总的使用率
+ */
+ private double total;
+
+ /**
+ * CPU系统使用率
+ */
+ private double sys;
+
+ /**
+ * CPU用户使用率
+ */
+ private double used;
+
+ /**
+ * CPU当前等待率
+ */
+ private double wait;
+
+ /**
+ * CPU当前空闲率
+ */
+ private double free;
+
+ public int getCpuNum()
+ {
+ return cpuNum;
+ }
+
+ public void setCpuNum(int cpuNum)
+ {
+ this.cpuNum = cpuNum;
+ }
+
+ public double getTotal()
+ {
+ return Arith.round(Arith.mul(total, 100), 2);
+ }
+
+ public void setTotal(double total)
+ {
+ this.total = total;
+ }
+
+ public double getSys()
+ {
+ return Arith.round(Arith.mul(sys / total, 100), 2);
+ }
+
+ public void setSys(double sys)
+ {
+ this.sys = sys;
+ }
+
+ public double getUsed()
+ {
+ return Arith.round(Arith.mul(used / total, 100), 2);
+ }
+
+ public void setUsed(double used)
+ {
+ this.used = used;
+ }
+
+ public double getWait()
+ {
+ return Arith.round(Arith.mul(wait / total, 100), 2);
+ }
+
+ public void setWait(double wait)
+ {
+ this.wait = wait;
+ }
+
+ public double getFree()
+ {
+ return Arith.round(Arith.mul(free / total, 100), 2);
+ }
+
+ public void setFree(double free)
+ {
+ this.free = free;
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/server/domain/Jvm.java b/src/main/java/com/luckyframe/project/monitor/server/domain/Jvm.java
new file mode 100644
index 0000000..4a903d9
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/server/domain/Jvm.java
@@ -0,0 +1,122 @@
+package com.luckyframe.project.monitor.server.domain;
+
+import java.lang.management.ManagementFactory;
+import com.luckyframe.common.utils.Arith;
+import com.luckyframe.common.utils.DateUtils;
+
+/**
+ * JVM相关信息
+ *
+ * @author ruoyi
+ */
+public class Jvm
+{
+ /**
+ * 当前JVM占用的内存总数(M)
+ */
+ private double total;
+
+ /**
+ * JVM最大可用内存总数(M)
+ */
+ private double max;
+
+ /**
+ * JVM空闲内存(M)
+ */
+ private double free;
+
+ /**
+ * JDK版本
+ */
+ private String version;
+
+ /**
+ * JDK路径
+ */
+ private String home;
+
+ public double getTotal()
+ {
+ return Arith.div(total, (1024 * 1024), 2);
+ }
+
+ public void setTotal(double total)
+ {
+ this.total = total;
+ }
+
+ public double getMax()
+ {
+ return Arith.div(max, (1024 * 1024), 2);
+ }
+
+ public void setMax(double max)
+ {
+ this.max = max;
+ }
+
+ public double getFree()
+ {
+ return Arith.div(free, (1024 * 1024), 2);
+ }
+
+ public void setFree(double free)
+ {
+ this.free = free;
+ }
+
+ public double getUsed()
+ {
+ return Arith.div(total - free, (1024 * 1024), 2);
+ }
+
+ public double getUsage()
+ {
+ return Arith.mul(Arith.div(total - free, total, 4), 100);
+ }
+
+ /**
+ * 获取JDK名称
+ */
+ public String getName()
+ {
+ return ManagementFactory.getRuntimeMXBean().getVmName();
+ }
+
+ public String getVersion()
+ {
+ return version;
+ }
+
+ public void setVersion(String version)
+ {
+ this.version = version;
+ }
+
+ public String getHome()
+ {
+ return home;
+ }
+
+ public void setHome(String home)
+ {
+ this.home = home;
+ }
+
+ /**
+ * JDK启动时间
+ */
+ public String getStartTime()
+ {
+ return DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, DateUtils.getServerStartDate());
+ }
+
+ /**
+ * JDK运行时间
+ */
+ public String getRunTime()
+ {
+ return DateUtils.getDatePoor(DateUtils.getNowDate(), DateUtils.getServerStartDate());
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/server/domain/Mem.java b/src/main/java/com/luckyframe/project/monitor/server/domain/Mem.java
new file mode 100644
index 0000000..e0bc22c
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/server/domain/Mem.java
@@ -0,0 +1,61 @@
+package com.luckyframe.project.monitor.server.domain;
+
+import com.luckyframe.common.utils.Arith;
+
+/**
+ * 內存相关信息
+ *
+ * @author ruoyi
+ */
+public class Mem
+{
+ /**
+ * 内存总量
+ */
+ private double total;
+
+ /**
+ * 已用内存
+ */
+ private double used;
+
+ /**
+ * 剩余内存
+ */
+ private double free;
+
+ public double getTotal()
+ {
+ return Arith.div(total, (1024 * 1024 * 1024), 2);
+ }
+
+ public void setTotal(long total)
+ {
+ this.total = total;
+ }
+
+ public double getUsed()
+ {
+ return Arith.div(used, (1024 * 1024 * 1024), 2);
+ }
+
+ public void setUsed(long used)
+ {
+ this.used = used;
+ }
+
+ public double getFree()
+ {
+ return Arith.div(free, (1024 * 1024 * 1024), 2);
+ }
+
+ public void setFree(long free)
+ {
+ this.free = free;
+ }
+
+ public double getUsage()
+ {
+ return Arith.mul(Arith.div(used, total, 4), 100);
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/server/domain/Server.java b/src/main/java/com/luckyframe/project/monitor/server/domain/Server.java
new file mode 100644
index 0000000..9001631
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/server/domain/Server.java
@@ -0,0 +1,235 @@
+package com.luckyframe.project.monitor.server.domain;
+
+import java.net.UnknownHostException;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Properties;
+import com.luckyframe.common.utils.Arith;
+import com.luckyframe.common.utils.IpUtils;
+import oshi.SystemInfo;
+import oshi.hardware.CentralProcessor;
+import oshi.hardware.CentralProcessor.TickType;
+import oshi.hardware.GlobalMemory;
+import oshi.hardware.HardwareAbstractionLayer;
+import oshi.software.os.FileSystem;
+import oshi.software.os.OSFileStore;
+import oshi.software.os.OperatingSystem;
+import oshi.util.Util;
+
+/**
+ * 服务器相关信息
+ *
+ * @author ruoyi
+ */
+public class Server
+{
+ private static final int OSHI_WAIT_SECOND = 1000;
+
+ /**
+ * CPU相关信息
+ */
+ private Cpu cpu = new Cpu();
+
+ /**
+ * 內存相关信息
+ */
+ private Mem mem = new Mem();
+
+ /**
+ * JVM相关信息
+ */
+ private Jvm jvm = new Jvm();
+
+ /**
+ * 服务器相关信息
+ */
+ private Sys sys = new Sys();
+
+ /**
+ * 磁盘相关信息
+ */
+ private List sysFiles = new LinkedList();
+
+ public Cpu getCpu()
+ {
+ return cpu;
+ }
+
+ public void setCpu(Cpu cpu)
+ {
+ this.cpu = cpu;
+ }
+
+ public Mem getMem()
+ {
+ return mem;
+ }
+
+ public void setMem(Mem mem)
+ {
+ this.mem = mem;
+ }
+
+ public Jvm getJvm()
+ {
+ return jvm;
+ }
+
+ public void setJvm(Jvm jvm)
+ {
+ this.jvm = jvm;
+ }
+
+ public Sys getSys()
+ {
+ return sys;
+ }
+
+ public void setSys(Sys sys)
+ {
+ this.sys = sys;
+ }
+
+ public List getSysFiles()
+ {
+ return sysFiles;
+ }
+
+ public void setSysFiles(List sysFiles)
+ {
+ this.sysFiles = sysFiles;
+ }
+
+ public void copyTo() throws Exception
+ {
+ SystemInfo si = new SystemInfo();
+ HardwareAbstractionLayer hal = si.getHardware();
+
+ setCpuInfo(hal.getProcessor());
+
+ setMemInfo(hal.getMemory());
+
+ setSysInfo();
+
+ setJvmInfo();
+
+ setSysFiles(si.getOperatingSystem());
+ }
+
+ /**
+ * 设置CPU信息
+ */
+ private void setCpuInfo(CentralProcessor processor)
+ {
+ // CPU信息
+ long[] prevTicks = processor.getSystemCpuLoadTicks();
+ Util.sleep(OSHI_WAIT_SECOND);
+ long[] ticks = processor.getSystemCpuLoadTicks();
+ long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()];
+ long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()];
+ long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()];
+ long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()];
+ long cSys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()];
+ long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()];
+ long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()];
+ long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()];
+ long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal;
+ cpu.setCpuNum(processor.getLogicalProcessorCount());
+ cpu.setTotal(totalCpu);
+ cpu.setSys(cSys);
+ cpu.setUsed(user);
+ cpu.setWait(iowait);
+ cpu.setFree(idle);
+ }
+
+ /**
+ * 设置内存信息
+ */
+ private void setMemInfo(GlobalMemory memory)
+ {
+ mem.setTotal(memory.getTotal());
+ mem.setUsed(memory.getTotal() - memory.getAvailable());
+ mem.setFree(memory.getAvailable());
+ }
+
+ /**
+ * 设置服务器信息
+ */
+ private void setSysInfo()
+ {
+ Properties props = System.getProperties();
+ sys.setComputerName(IpUtils.getHostName());
+ sys.setComputerIp(IpUtils.getHostIp());
+ sys.setOsName(props.getProperty("os.name"));
+ sys.setOsArch(props.getProperty("os.arch"));
+ sys.setUserDir(props.getProperty("user.dir"));
+ }
+
+ /**
+ * 设置Java虚拟机
+ */
+ private void setJvmInfo() throws UnknownHostException
+ {
+ Properties props = System.getProperties();
+ jvm.setTotal(Runtime.getRuntime().totalMemory());
+ jvm.setMax(Runtime.getRuntime().maxMemory());
+ jvm.setFree(Runtime.getRuntime().freeMemory());
+ jvm.setVersion(props.getProperty("java.version"));
+ jvm.setHome(props.getProperty("java.home"));
+ }
+
+ /**
+ * 设置磁盘信息
+ */
+ private void setSysFiles(OperatingSystem os)
+ {
+ FileSystem fileSystem = os.getFileSystem();
+ OSFileStore[] fsArray = fileSystem.getFileStores();
+ for (OSFileStore fs : fsArray)
+ {
+ long free = fs.getUsableSpace();
+ long total = fs.getTotalSpace();
+ long used = total - free;
+ SysFile sysFile = new SysFile();
+ sysFile.setDirName(fs.getMount());
+ sysFile.setSysTypeName(fs.getType());
+ sysFile.setTypeName(fs.getName());
+ sysFile.setTotal(convertFileSize(total));
+ sysFile.setFree(convertFileSize(free));
+ sysFile.setUsed(convertFileSize(used));
+ sysFile.setUsage(Arith.mul(Arith.div(used, total, 4), 100));
+ sysFiles.add(sysFile);
+ }
+ }
+
+ /**
+ * 字节转换
+ *
+ * @param size 字节大小
+ * @return 转换后值
+ */
+ public String convertFileSize(long size)
+ {
+ long kb = 1024;
+ long mb = kb * 1024;
+ long gb = mb * 1024;
+ if (size >= gb)
+ {
+ return String.format("%.1f GB", (float) size / gb);
+ }
+ else if (size >= mb)
+ {
+ float f = (float) size / mb;
+ return String.format(f > 100 ? "%.0f MB" : "%.1f MB", f);
+ }
+ else if (size >= kb)
+ {
+ float f = (float) size / kb;
+ return String.format(f > 100 ? "%.0f KB" : "%.1f KB", f);
+ }
+ else
+ {
+ return String.format("%d B", size);
+ }
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/server/domain/Sys.java b/src/main/java/com/luckyframe/project/monitor/server/domain/Sys.java
new file mode 100644
index 0000000..0cae4df
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/server/domain/Sys.java
@@ -0,0 +1,84 @@
+package com.luckyframe.project.monitor.server.domain;
+
+/**
+ * 系统相关信息
+ *
+ * @author ruoyi
+ */
+public class Sys
+{
+ /**
+ * 服务器名称
+ */
+ private String computerName;
+
+ /**
+ * 服务器Ip
+ */
+ private String computerIp;
+
+ /**
+ * 项目路径
+ */
+ private String userDir;
+
+ /**
+ * 操作系统
+ */
+ private String osName;
+
+ /**
+ * 系统架构
+ */
+ private String osArch;
+
+ public String getComputerName()
+ {
+ return computerName;
+ }
+
+ public void setComputerName(String computerName)
+ {
+ this.computerName = computerName;
+ }
+
+ public String getComputerIp()
+ {
+ return computerIp;
+ }
+
+ public void setComputerIp(String computerIp)
+ {
+ this.computerIp = computerIp;
+ }
+
+ public String getUserDir()
+ {
+ return userDir;
+ }
+
+ public void setUserDir(String userDir)
+ {
+ this.userDir = userDir;
+ }
+
+ public String getOsName()
+ {
+ return osName;
+ }
+
+ public void setOsName(String osName)
+ {
+ this.osName = osName;
+ }
+
+ public String getOsArch()
+ {
+ return osArch;
+ }
+
+ public void setOsArch(String osArch)
+ {
+ this.osArch = osArch;
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/monitor/server/domain/SysFile.java b/src/main/java/com/luckyframe/project/monitor/server/domain/SysFile.java
new file mode 100644
index 0000000..98b6719
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/monitor/server/domain/SysFile.java
@@ -0,0 +1,114 @@
+package com.luckyframe.project.monitor.server.domain;
+
+/**
+ * 系统文件相关信息
+ *
+ * @author ruoyi
+ */
+public class SysFile
+{
+ /**
+ * 盘符路径
+ */
+ private String dirName;
+
+ /**
+ * 盘符类型
+ */
+ private String sysTypeName;
+
+ /**
+ * 文件类型
+ */
+ private String typeName;
+
+ /**
+ * 总大小
+ */
+ private String total;
+
+ /**
+ * 剩余大小
+ */
+ private String free;
+
+ /**
+ * 已经使用量
+ */
+ private String used;
+
+ /**
+ * 资源的使用率
+ */
+ private double usage;
+
+ public String getDirName()
+ {
+ return dirName;
+ }
+
+ public void setDirName(String dirName)
+ {
+ this.dirName = dirName;
+ }
+
+ public String getSysTypeName()
+ {
+ return sysTypeName;
+ }
+
+ public void setSysTypeName(String sysTypeName)
+ {
+ this.sysTypeName = sysTypeName;
+ }
+
+ public String getTypeName()
+ {
+ return typeName;
+ }
+
+ public void setTypeName(String typeName)
+ {
+ this.typeName = typeName;
+ }
+
+ public String getTotal()
+ {
+ return total;
+ }
+
+ public void setTotal(String total)
+ {
+ this.total = total;
+ }
+
+ public String getFree()
+ {
+ return free;
+ }
+
+ public void setFree(String free)
+ {
+ this.free = free;
+ }
+
+ public String getUsed()
+ {
+ return used;
+ }
+
+ public void setUsed(String used)
+ {
+ this.used = used;
+ }
+
+ public double getUsage()
+ {
+ return usage;
+ }
+
+ public void setUsage(double usage)
+ {
+ this.usage = usage;
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/system/config/controller/ConfigController.java b/src/main/java/com/luckyframe/project/system/config/controller/ConfigController.java
new file mode 100644
index 0000000..a1ed2ca
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/system/config/controller/ConfigController.java
@@ -0,0 +1,131 @@
+package com.luckyframe.project.system.config.controller;
+
+import java.util.List;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import com.luckyframe.common.utils.poi.ExcelUtil;
+import com.luckyframe.framework.aspectj.lang.annotation.Log;
+import com.luckyframe.framework.aspectj.lang.enums.BusinessType;
+import com.luckyframe.framework.web.controller.BaseController;
+import com.luckyframe.framework.web.domain.AjaxResult;
+import com.luckyframe.framework.web.page.TableDataInfo;
+import com.luckyframe.project.system.config.domain.Config;
+import com.luckyframe.project.system.config.service.IConfigService;
+
+/**
+ * 参数配置 信息操作处理
+ *
+ * @author ruoyi
+ */
+@Controller
+@RequestMapping("/system/config")
+public class ConfigController extends BaseController
+{
+ private String prefix = "system/config";
+
+ @Autowired
+ private IConfigService configService;
+
+ @RequiresPermissions("system:config:view")
+ @GetMapping()
+ public String config()
+ {
+ return prefix + "/config";
+ }
+
+ /**
+ * 查询参数配置列表
+ */
+ @RequiresPermissions("system:config:list")
+ @PostMapping("/list")
+ @ResponseBody
+ public TableDataInfo list(Config config)
+ {
+ startPage();
+ List list = configService.selectConfigList(config);
+ return getDataTable(list);
+ }
+
+ @Log(title = "参数管理", businessType = BusinessType.EXPORT)
+ @RequiresPermissions("system:config:export")
+ @PostMapping("/export")
+ @ResponseBody
+ public AjaxResult export(Config config)
+ {
+ List list = configService.selectConfigList(config);
+ ExcelUtil util = new ExcelUtil(Config.class);
+ return util.exportExcel(list, "参数数据");
+ }
+
+ /**
+ * 新增参数配置
+ */
+ @GetMapping("/add")
+ public String add()
+ {
+ return prefix + "/add";
+ }
+
+ /**
+ * 新增保存参数配置
+ */
+ @RequiresPermissions("system:config:add")
+ @Log(title = "参数管理", businessType = BusinessType.INSERT)
+ @PostMapping("/add")
+ @ResponseBody
+ public AjaxResult addSave(Config config)
+ {
+ return toAjax(configService.insertConfig(config));
+ }
+
+ /**
+ * 修改参数配置
+ */
+ @GetMapping("/edit/{configId}")
+ public String edit(@PathVariable("configId") Long configId, ModelMap mmap)
+ {
+ mmap.put("config", configService.selectConfigById(configId));
+ return prefix + "/edit";
+ }
+
+ /**
+ * 修改保存参数配置
+ */
+ @RequiresPermissions("system:config:edit")
+ @Log(title = "参数管理", businessType = BusinessType.UPDATE)
+ @PostMapping("/edit")
+ @ResponseBody
+ public AjaxResult editSave(Config config)
+ {
+ return toAjax(configService.updateConfig(config));
+ }
+
+ /**
+ * 删除参数配置
+ */
+ @RequiresPermissions("system:config:remove")
+ @Log(title = "参数管理", businessType = BusinessType.DELETE)
+ @PostMapping("/remove")
+ @ResponseBody
+ public AjaxResult remove(String ids)
+ {
+ return toAjax(configService.deleteConfigByIds(ids));
+ }
+
+ /**
+ * 校验参数键名
+ */
+ @PostMapping("/checkConfigKeyUnique")
+ @ResponseBody
+ public String checkConfigKeyUnique(Config config)
+ {
+ return configService.checkConfigKeyUnique(config);
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/system/config/domain/Config.java b/src/main/java/com/luckyframe/project/system/config/domain/Config.java
new file mode 100644
index 0000000..8fda526
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/system/config/domain/Config.java
@@ -0,0 +1,102 @@
+package com.luckyframe.project.system.config.domain;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.luckyframe.framework.aspectj.lang.annotation.Excel;
+import com.luckyframe.framework.web.domain.BaseEntity;
+
+/**
+ * 参数配置表 sys_config
+ *
+ * @author ruoyi
+ */
+public class Config extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 参数主键 */
+ @Excel(name = "参数主键")
+ private Long configId;
+
+ /** 参数名称 */
+ @Excel(name = "参数名称")
+ private String configName;
+
+ /** 参数键名 */
+ @Excel(name = "参数键名")
+ private String configKey;
+
+ /** 参数键值 */
+ @Excel(name = "参数键值")
+ private String configValue;
+
+ /** 系统内置(Y是 N否) */
+ @Excel(name = "系统内置", readConverterExp = "Y=是,N=否")
+ private String configType;
+
+ public Long getConfigId()
+ {
+ return configId;
+ }
+
+ public void setConfigId(Long configId)
+ {
+ this.configId = configId;
+ }
+
+ public String getConfigName()
+ {
+ return configName;
+ }
+
+ public void setConfigName(String configName)
+ {
+ this.configName = configName;
+ }
+
+ public String getConfigKey()
+ {
+ return configKey;
+ }
+
+ public void setConfigKey(String configKey)
+ {
+ this.configKey = configKey;
+ }
+
+ public String getConfigValue()
+ {
+ return configValue;
+ }
+
+ public void setConfigValue(String configValue)
+ {
+ this.configValue = configValue;
+ }
+
+ public String getConfigType()
+ {
+ return configType;
+ }
+
+ public void setConfigType(String configType)
+ {
+ this.configType = configType;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("configId", getConfigId())
+ .append("configName", getConfigName())
+ .append("configKey", getConfigKey())
+ .append("configValue", getConfigValue())
+ .append("configType", getConfigType())
+ .append("createBy", getCreateBy())
+ .append("createTime", getCreateTime())
+ .append("updateBy", getUpdateBy())
+ .append("updateTime", getUpdateTime())
+ .append("remark", getRemark())
+ .toString();
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/system/config/mapper/ConfigMapper.java b/src/main/java/com/luckyframe/project/system/config/mapper/ConfigMapper.java
new file mode 100644
index 0000000..c31d666
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/system/config/mapper/ConfigMapper.java
@@ -0,0 +1,60 @@
+package com.luckyframe.project.system.config.mapper;
+
+import com.luckyframe.project.system.config.domain.Config;
+import java.util.List;
+
+/**
+ * 参数配置 数据层
+ *
+ * @author ruoyi
+ */
+public interface ConfigMapper
+{
+ /**
+ * 查询参数配置信息
+ *
+ * @param config 参数配置信息
+ * @return 参数配置信息
+ */
+ public Config selectConfig(Config config);
+
+ /**
+ * 查询参数配置列表
+ *
+ * @param config 参数配置信息
+ * @return 参数配置集合
+ */
+ public List selectConfigList(Config config);
+
+ /**
+ * 根据键名查询参数配置信息
+ *
+ * @param configKey 参数键名
+ * @return 参数配置信息
+ */
+ public Config checkConfigKeyUnique(String configKey);
+
+ /**
+ * 新增参数配置
+ *
+ * @param config 参数配置信息
+ * @return 结果
+ */
+ public int insertConfig(Config config);
+
+ /**
+ * 修改参数配置
+ *
+ * @param config 参数配置信息
+ * @return 结果
+ */
+ public int updateConfig(Config config);
+
+ /**
+ * 批量删除参数配置
+ *
+ * @param configIds 需要删除的数据ID
+ * @return 结果
+ */
+ public int deleteConfigByIds(String[] configIds);
+}
\ No newline at end of file
diff --git a/src/main/java/com/luckyframe/project/system/config/service/ConfigServiceImpl.java b/src/main/java/com/luckyframe/project/system/config/service/ConfigServiceImpl.java
new file mode 100644
index 0000000..43a6424
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/system/config/service/ConfigServiceImpl.java
@@ -0,0 +1,120 @@
+package com.luckyframe.project.system.config.service;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.luckyframe.common.constant.UserConstants;
+import com.luckyframe.common.support.Convert;
+import com.luckyframe.common.utils.StringUtils;
+import com.luckyframe.common.utils.security.ShiroUtils;
+import com.luckyframe.project.system.config.domain.Config;
+import com.luckyframe.project.system.config.mapper.ConfigMapper;
+
+/**
+ * 参数配置 服务层实现
+ *
+ * @author ruoyi
+ */
+@Service
+public class ConfigServiceImpl implements IConfigService
+{
+ @Autowired
+ private ConfigMapper configMapper;
+
+ /**
+ * 查询参数配置信息
+ *
+ * @param configId 参数配置ID
+ * @return 参数配置信息
+ */
+ @Override
+ public Config selectConfigById(Long configId)
+ {
+ Config config = new Config();
+ config.setConfigId(configId);
+ return configMapper.selectConfig(config);
+ }
+
+ /**
+ * 根据键名查询参数配置信息
+ *
+ * @param configKey 参数名称
+ * @return 参数键值
+ */
+ @Override
+ public String selectConfigByKey(String configKey)
+ {
+ Config config = new Config();
+ config.setConfigKey(configKey);
+ Config retConfig = configMapper.selectConfig(config);
+ return StringUtils.isNotNull(retConfig) ? retConfig.getConfigValue() : "";
+ }
+
+ /**
+ * 查询参数配置列表
+ *
+ * @param config 参数配置信息
+ * @return 参数配置集合
+ */
+ @Override
+ public List selectConfigList(Config config)
+ {
+ return configMapper.selectConfigList(config);
+ }
+
+ /**
+ * 新增参数配置
+ *
+ * @param config 参数配置信息
+ * @return 结果
+ */
+ @Override
+ public int insertConfig(Config config)
+ {
+ config.setCreateBy(ShiroUtils.getLoginName());
+ return configMapper.insertConfig(config);
+ }
+
+ /**
+ * 修改参数配置
+ *
+ * @param config 参数配置信息
+ * @return 结果
+ */
+ @Override
+ public int updateConfig(Config config)
+ {
+ config.setUpdateBy(ShiroUtils.getLoginName());
+ return configMapper.updateConfig(config);
+ }
+
+ /**
+ * 批量删除参数配置对象
+ *
+ * @param ids 需要删除的数据ID
+ * @return 结果
+ */
+ @Override
+ public int deleteConfigByIds(String ids)
+ {
+ return configMapper.deleteConfigByIds(Convert.toStrArray(ids));
+ }
+
+ /**
+ * 校验参数键名是否唯一
+ *
+ * @param config 参数配置信息
+ * @return 结果
+ */
+ @Override
+ public String checkConfigKeyUnique(Config config)
+ {
+ Long configId = StringUtils.isNull(config.getConfigId()) ? -1L : config.getConfigId();
+ Config info = configMapper.checkConfigKeyUnique(config.getConfigKey());
+ if (StringUtils.isNotNull(info) && info.getConfigId().longValue() != configId.longValue())
+ {
+ return UserConstants.CONFIG_KEY_NOT_UNIQUE;
+ }
+ return UserConstants.CONFIG_KEY_UNIQUE;
+ }
+}
diff --git a/src/main/java/com/luckyframe/project/system/config/service/IConfigService.java b/src/main/java/com/luckyframe/project/system/config/service/IConfigService.java
new file mode 100644
index 0000000..6b795cf
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/system/config/service/IConfigService.java
@@ -0,0 +1,68 @@
+package com.luckyframe.project.system.config.service;
+
+import com.luckyframe.project.system.config.domain.Config;
+import java.util.List;
+
+/**
+ * 参数配置 服务层
+ *
+ * @author ruoyi
+ */
+public interface IConfigService
+{
+ /**
+ * 查询参数配置信息
+ *
+ * @param configId 参数配置ID
+ * @return 参数配置信息
+ */
+ public Config selectConfigById(Long configId);
+
+ /**
+ * 根据键名查询参数配置信息
+ *
+ * @param configKey 参数键名
+ * @return 参数键值
+ */
+ public String selectConfigByKey(String configKey);
+
+ /**
+ * 查询参数配置列表
+ *
+ * @param config 参数配置信息
+ * @return 参数配置集合
+ */
+ public List selectConfigList(Config config);
+
+ /**
+ * 新增参数配置
+ *
+ * @param config 参数配置信息
+ * @return 结果
+ */
+ public int insertConfig(Config config);
+
+ /**
+ * 修改参数配置
+ *
+ * @param config 参数配置信息
+ * @return 结果
+ */
+ public int updateConfig(Config config);
+
+ /**
+ * 批量删除参数配置信息
+ *
+ * @param ids 需要删除的数据ID
+ * @return 结果
+ */
+ public int deleteConfigByIds(String ids);
+
+ /**
+ * 校验参数键名是否唯一
+ *
+ * @param config 参数信息
+ * @return 结果
+ */
+ public String checkConfigKeyUnique(Config config);
+}
diff --git a/src/main/java/com/luckyframe/project/system/dept/controller/DeptController.java b/src/main/java/com/luckyframe/project/system/dept/controller/DeptController.java
new file mode 100644
index 0000000..da081df
--- /dev/null
+++ b/src/main/java/com/luckyframe/project/system/dept/controller/DeptController.java
@@ -0,0 +1,163 @@
+package com.luckyframe.project.system.dept.controller;
+
+import java.util.List;
+import java.util.Map;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import com.luckyframe.common.utils.StringUtils;
+import com.luckyframe.framework.aspectj.lang.annotation.Log;
+import com.luckyframe.framework.aspectj.lang.enums.BusinessType;
+import com.luckyframe.framework.web.controller.BaseController;
+import com.luckyframe.framework.web.domain.AjaxResult;
+import com.luckyframe.project.system.dept.domain.Dept;
+import com.luckyframe.project.system.dept.service.IDeptService;
+import com.luckyframe.project.system.role.domain.Role;
+
+/**
+ * 部门信息
+ *
+ * @author ruoyi
+ */
+@Controller
+@RequestMapping("/system/dept")
+public class DeptController extends BaseController
+{
+ private String prefix = "system/dept";
+
+ @Autowired
+ private IDeptService deptService;
+
+ @RequiresPermissions("system:dept:view")
+ @GetMapping()
+ public String dept()
+ {
+ return prefix + "/dept";
+ }
+
+ @RequiresPermissions("system:dept:list")
+ @GetMapping("/list")
+ @ResponseBody
+ public List list(Dept dept)
+ {
+ List deptList = deptService.selectDeptList(dept);
+ return deptList;
+ }
+
+ /**
+ * 新增部门
+ */
+ @GetMapping("/add/{parentId}")
+ public String add(@PathVariable("parentId") Long parentId, ModelMap mmap)
+ {
+ mmap.put("dept", deptService.selectDeptById(parentId));
+ return prefix + "/add";
+ }
+
+ /**
+ * 新增保存部门
+ */
+ @Log(title = "部门管理", businessType = BusinessType.INSERT)
+ @RequiresPermissions("system:dept:add")
+ @PostMapping("/add")
+ @ResponseBody
+ public AjaxResult addSave(Dept dept)
+ {
+ return toAjax(deptService.insertDept(dept));
+ }
+
+ /**
+ * 修改
+ */
+ @GetMapping("/edit/{deptId}")
+ public String edit(@PathVariable("deptId") Long deptId, ModelMap mmap)
+ {
+ Dept dept = deptService.selectDeptById(deptId);
+ if (StringUtils.isNotNull(dept) && 100L == deptId)
+ {
+ dept.setParentName("无");
+ }
+ mmap.put("dept", dept);
+ return prefix + "/edit";
+ }
+
+ /**
+ * 保存
+ */
+ @Log(title = "部门管理", businessType = BusinessType.UPDATE)
+ @RequiresPermissions("system:dept:edit")
+ @PostMapping("/edit")
+ @ResponseBody
+ public AjaxResult editSave(Dept dept)
+ {
+ return toAjax(deptService.updateDept(dept));
+ }
+
+ /**
+ * 删除
+ */
+ @Log(title = "部门管理", businessType = BusinessType.DELETE)
+ @RequiresPermissions("system:dept:remove")
+ @PostMapping("/remove/{deptId}")
+ @ResponseBody
+ public AjaxResult remove(@PathVariable("deptId") Long deptId)
+ {
+ if (deptService.selectDeptCount(deptId) > 0)
+ {
+ return error(1, "存在下级部门,不允许删除");
+ }
+ if (deptService.checkDeptExistUser(deptId))
+ {
+ return error(1, "部门存在用户,不允许删除");
+ }
+ return toAjax(deptService.deleteDeptById(deptId));
+ }
+
+ /**
+ * 校验部门名称
+ */
+ @PostMapping("/checkDeptNameUnique")
+ @ResponseBody
+ public String checkDeptNameUnique(Dept dept)
+ {
+ return deptService.checkDeptNameUnique(dept);
+ }
+
+ /**
+ * 选择部门树
+ */
+ @GetMapping("/selectDeptTree/{deptId}")
+ public String selectDeptTree(@PathVariable("deptId") Long deptId, ModelMap mmap)
+ {
+ mmap.put("dept", deptService.selectDeptById(deptId));
+ return prefix + "/tree";
+ }
+
+ /**
+ * 加载部门列表树
+ */
+ @GetMapping("/treeData")
+ @ResponseBody
+ public List