Commit 753c7678ee5a3da612e0b570c21aecd3447130c3

Authored by YunaiV
1 parent ab94fe2d

完成主要在线 session 的功能

Showing 34 changed files with 1349 additions and 1260 deletions
ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SwaggerController.java renamed to ruoyi-common/src/main/java/com/ruoyi/common/SwaggerController.java
@@ -8,17 +8,15 @@ import com.ruoyi.common.core.controller.BaseController; @@ -8,17 +8,15 @@ import com.ruoyi.common.core.controller.BaseController;
8 8
9 /** 9 /**
10 * swagger 接口 10 * swagger 接口
11 - * 11 + *
12 * @author ruoyi 12 * @author ruoyi
13 */ 13 */
14 @Controller 14 @Controller
15 @RequestMapping("/tool/swagger") 15 @RequestMapping("/tool/swagger")
16 -public class SwaggerController extends BaseController  
17 -{ 16 +public class SwaggerController extends BaseController {
18 @PreAuthorize("@ss.hasPermi('tool:swagger:view')") 17 @PreAuthorize("@ss.hasPermi('tool:swagger:view')")
19 @GetMapping() 18 @GetMapping()
20 - public String index()  
21 - { 19 + public String index() {
22 return redirect("/swagger-ui.html"); 20 return redirect("/swagger-ui.html");
23 } 21 }
24 } 22 }
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java renamed to ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScopeAspect.java
1 -package com.ruoyi.framework.aspectj;  
2 -  
3 -import java.lang.reflect.Method;  
4 -  
5 -import org.aspectj.lang.JoinPoint;  
6 -import org.aspectj.lang.Signature;  
7 -import org.aspectj.lang.annotation.Aspect;  
8 -import org.aspectj.lang.annotation.Before;  
9 -import org.aspectj.lang.annotation.Pointcut;  
10 -import org.aspectj.lang.reflect.MethodSignature;  
11 -import org.springframework.stereotype.Component;  
12 -import com.ruoyi.common.annotation.DataScope;  
13 -import com.ruoyi.common.core.domain.BaseEntity;  
14 -import com.ruoyi.common.core.domain.entity.SysRole;  
15 -import com.ruoyi.common.core.domain.entity.SysUser;  
16 -import com.ruoyi.common.core.domain.model.LoginUser;  
17 -import com.ruoyi.common.utils.ServletUtils;  
18 -import com.ruoyi.common.utils.StringUtils;  
19 -import com.ruoyi.common.utils.spring.SpringUtils;  
20 -import com.ruoyi.framework.web.service.TokenService;  
21 -  
22 -/**  
23 - * 数据过滤处理  
24 - *  
25 - * @author ruoyi  
26 - */  
27 -@Aspect  
28 -@Component  
29 -public class DataScopeAspect {  
30 - /**  
31 - * 全部数据权限  
32 - */  
33 - public static final String DATA_SCOPE_ALL = "1";  
34 -  
35 - /**  
36 - * 自定数据权限  
37 - */  
38 - public static final String DATA_SCOPE_CUSTOM = "2";  
39 -  
40 - /**  
41 - * 部门数据权限  
42 - */  
43 - public static final String DATA_SCOPE_DEPT = "3";  
44 -  
45 - /**  
46 - * 部门及以下数据权限  
47 - */  
48 - public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";  
49 -  
50 - /**  
51 - * 仅本人数据权限  
52 - */  
53 - public static final String DATA_SCOPE_SELF = "5";  
54 -  
55 - /**  
56 - * 数据权限过滤关键字  
57 - */  
58 - public static final String DATA_SCOPE = "dataScope";  
59 -  
60 - // 配置织入点  
61 - @Pointcut("@annotation(com.ruoyi.common.annotation.DataScope)")  
62 - public void dataScopePointCut() {  
63 - }  
64 -  
65 - @Before("dataScopePointCut()")  
66 - public void doBefore(JoinPoint point) throws Throwable {  
67 - handleDataScope(point);  
68 - }  
69 -  
70 - protected void handleDataScope(final JoinPoint joinPoint) {  
71 - // 获得注解  
72 - DataScope controllerDataScope = getAnnotationLog(joinPoint);  
73 - if (controllerDataScope == null) {  
74 - return;  
75 - }  
76 - // 获取当前的用户  
77 - LoginUser loginUser = SpringUtils.getBean(TokenService.class).getLoginUser(ServletUtils.getRequest());  
78 - if (StringUtils.isNotNull(loginUser)) {  
79 - SysUser currentUser = loginUser.getUser();  
80 - // 如果是超级管理员,则不过滤数据  
81 - if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) {  
82 - dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),  
83 - controllerDataScope.userAlias());  
84 - }  
85 - }  
86 - }  
87 -  
88 - /**  
89 - * 数据范围过滤  
90 - *  
91 - * @param joinPoint 切点  
92 - * @param user 用户  
93 - * @param userAlias 别名  
94 - */  
95 - public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias) {  
96 - StringBuilder sqlString = new StringBuilder();  
97 -  
98 - for (SysRole role : user.getRoles()) {  
99 - String dataScope = role.getDataScope();  
100 - if (DATA_SCOPE_ALL.equals(dataScope)) {  
101 - sqlString = new StringBuilder();  
102 - break;  
103 - } else if (DATA_SCOPE_CUSTOM.equals(dataScope)) {  
104 - sqlString.append(StringUtils.format(  
105 - " OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias,  
106 - role.getRoleId()));  
107 - } else if (DATA_SCOPE_DEPT.equals(dataScope)) {  
108 - sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));  
109 - } else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) {  
110 - sqlString.append(StringUtils.format(  
111 - " OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",  
112 - deptAlias, user.getDeptId(), user.getDeptId()));  
113 - } else if (DATA_SCOPE_SELF.equals(dataScope)) {  
114 - if (StringUtils.isNotBlank(userAlias)) {  
115 - sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));  
116 - } else {  
117 - // 数据权限为仅本人且没有userAlias别名不查询任何数据  
118 - sqlString.append(" OR 1=0 ");  
119 - }  
120 - }  
121 - }  
122 -  
123 - if (StringUtils.isNotBlank(sqlString.toString())) {  
124 - Object params = joinPoint.getArgs()[0];  
125 - if (StringUtils.isNotNull(params) && params instanceof BaseEntity) {  
126 - BaseEntity baseEntity = (BaseEntity) params;  
127 - baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");  
128 - }  
129 - }  
130 - }  
131 -  
132 - /**  
133 - * 是否存在注解,如果存在就获取  
134 - */  
135 - private DataScope getAnnotationLog(JoinPoint joinPoint) {  
136 - Signature signature = joinPoint.getSignature();  
137 - MethodSignature methodSignature = (MethodSignature) signature;  
138 - Method method = methodSignature.getMethod();  
139 -  
140 - if (method != null) {  
141 - return method.getAnnotation(DataScope.class);  
142 - }  
143 - return null;  
144 - }  
145 -} 1 +package com.ruoyi.framework.aspectj;
  2 +
  3 +import java.lang.reflect.Method;
  4 +
  5 +import org.aspectj.lang.JoinPoint;
  6 +import org.aspectj.lang.Signature;
  7 +import org.aspectj.lang.annotation.Aspect;
  8 +import org.aspectj.lang.annotation.Before;
  9 +import org.aspectj.lang.annotation.Pointcut;
  10 +import org.aspectj.lang.reflect.MethodSignature;
  11 +import org.springframework.stereotype.Component;
  12 +import com.ruoyi.common.annotation.DataScope;
  13 +import com.ruoyi.common.core.domain.BaseEntity;
  14 +import com.ruoyi.common.core.domain.entity.SysRole;
  15 +import com.ruoyi.common.core.domain.entity.SysUser;
  16 +import com.ruoyi.common.core.domain.model.LoginUser;
  17 +import com.ruoyi.common.utils.ServletUtils;
  18 +import com.ruoyi.common.utils.StringUtils;
  19 +import com.ruoyi.common.utils.spring.SpringUtils;
  20 +import com.ruoyi.framework.web.service.TokenService;
  21 +
  22 +/**
  23 + * 数据过滤处理
  24 + *
  25 + * @author ruoyi
  26 + */
  27 +@Aspect
  28 +@Component
  29 +public class DataScopeAspect {
  30 + /**
  31 + * 全部数据权限
  32 + */
  33 + public static final String DATA_SCOPE_ALL = "1";
  34 +
  35 + /**
  36 + * 自定数据权限
  37 + */
  38 + public static final String DATA_SCOPE_CUSTOM = "2";
  39 +
  40 + /**
  41 + * 部门数据权限
  42 + */
  43 + public static final String DATA_SCOPE_DEPT = "3";
  44 +
  45 + /**
  46 + * 部门及以下数据权限
  47 + */
  48 + public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";
  49 +
  50 + /**
  51 + * 仅本人数据权限
  52 + */
  53 + public static final String DATA_SCOPE_SELF = "5";
  54 +
  55 + /**
  56 + * 数据权限过滤关键字
  57 + */
  58 + public static final String DATA_SCOPE = "dataScope";
  59 +
  60 + // 配置织入点
  61 + @Pointcut("@annotation(com.ruoyi.common.annotation.DataScope)")
  62 + public void dataScopePointCut() {
  63 + }
  64 +
  65 + @Before("dataScopePointCut()")
  66 + public void doBefore(JoinPoint point) throws Throwable {
  67 + handleDataScope(point);
  68 + }
  69 +
  70 + protected void handleDataScope(final JoinPoint joinPoint) {
  71 + // 获得注解
  72 + DataScope controllerDataScope = getAnnotationLog(joinPoint);
  73 + if (controllerDataScope == null) {
  74 + return;
  75 + }
  76 + // 获取当前的用户
  77 + LoginUser loginUser = SpringUtils.getBean(TokenService.class).getLoginUser(ServletUtils.getRequest());
  78 + if (StringUtils.isNotNull(loginUser)) {
  79 + SysUser currentUser = loginUser.getUser();
  80 + // 如果是超级管理员,则不过滤数据
  81 + if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) {
  82 + dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
  83 + controllerDataScope.userAlias());
  84 + }
  85 + }
  86 + }
  87 +
  88 + /**
  89 + * 数据范围过滤
  90 + *
  91 + * @param joinPoint 切点
  92 + * @param user 用户
  93 + * @param userAlias 别名
  94 + */
  95 + public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias) {
  96 + StringBuilder sqlString = new StringBuilder();
  97 +
  98 + for (SysRole role : user.getRoles()) {
  99 + String dataScope = role.getDataScope();
  100 + if (DATA_SCOPE_ALL.equals(dataScope)) {
  101 + sqlString = new StringBuilder();
  102 + break;
  103 + } else if (DATA_SCOPE_CUSTOM.equals(dataScope)) {
  104 + sqlString.append(StringUtils.format(
  105 + " OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias,
  106 + role.getRoleId()));
  107 + } else if (DATA_SCOPE_DEPT.equals(dataScope)) {
  108 + sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));
  109 + } else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) {
  110 + sqlString.append(StringUtils.format(
  111 + " OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
  112 + deptAlias, user.getDeptId(), user.getDeptId()));
  113 + } else if (DATA_SCOPE_SELF.equals(dataScope)) {
  114 + if (StringUtils.isNotBlank(userAlias)) {
  115 + sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));
  116 + } else {
  117 + // 数据权限为仅本人且没有userAlias别名不查询任何数据
  118 + sqlString.append(" OR 1=0 ");
  119 + }
  120 + }
  121 + }
  122 +
  123 + if (StringUtils.isNotBlank(sqlString.toString())) {
  124 + Object params = joinPoint.getArgs()[0];
  125 + if (StringUtils.isNotNull(params) && params instanceof BaseEntity) {
  126 + BaseEntity baseEntity = (BaseEntity) params;
  127 + baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
  128 + }
  129 + }
  130 + }
  131 +
  132 + /**
  133 + * 是否存在注解,如果存在就获取
  134 + */
  135 + private DataScope getAnnotationLog(JoinPoint joinPoint) {
  136 + Signature signature = joinPoint.getSignature();
  137 + MethodSignature methodSignature = (MethodSignature) signature;
  138 + Method method = methodSignature.getMethod();
  139 +
  140 + if (method != null) {
  141 + return method.getAnnotation(DataScope.class);
  142 + }
  143 + return null;
  144 + }
  145 +}
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java renamed to ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSourceAspect.java
1 -package com.ruoyi.framework.aspectj;  
2 -  
3 -import java.util.Objects;  
4 -  
5 -import org.aspectj.lang.ProceedingJoinPoint;  
6 -import org.aspectj.lang.annotation.Around;  
7 -import org.aspectj.lang.annotation.Aspect;  
8 -import org.aspectj.lang.annotation.Pointcut;  
9 -import org.aspectj.lang.reflect.MethodSignature;  
10 -import org.slf4j.Logger;  
11 -import org.slf4j.LoggerFactory;  
12 -import org.springframework.core.annotation.AnnotationUtils;  
13 -import org.springframework.core.annotation.Order;  
14 -import org.springframework.stereotype.Component;  
15 -import com.ruoyi.common.annotation.DataSource;  
16 -import com.ruoyi.common.utils.StringUtils;  
17 -import com.ruoyi.framework.datasource.DynamicDataSourceContextHolder;  
18 -  
19 -/**  
20 - * 多数据源处理  
21 - *  
22 - * @author ruoyi  
23 - */  
24 -@Aspect  
25 -@Order(1)  
26 -@Component  
27 -public class DataSourceAspect {  
28 - protected Logger logger = LoggerFactory.getLogger(getClass());  
29 -  
30 - @Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)"  
31 - + "|| @within(com.ruoyi.common.annotation.DataSource)")  
32 - public void dsPointCut() {  
33 -  
34 - }  
35 -  
36 - @Around("dsPointCut()")  
37 - public Object around(ProceedingJoinPoint point) throws Throwable {  
38 - DataSource dataSource = getDataSource(point);  
39 -  
40 - if (StringUtils.isNotNull(dataSource)) {  
41 - DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());  
42 - }  
43 -  
44 - try {  
45 - return point.proceed();  
46 - } finally {  
47 - // 销毁数据源 在执行方法之后  
48 - DynamicDataSourceContextHolder.clearDataSourceType();  
49 - }  
50 - }  
51 -  
52 - /**  
53 - * 获取需要切换的数据源  
54 - */  
55 - public DataSource getDataSource(ProceedingJoinPoint point) {  
56 - MethodSignature signature = (MethodSignature) point.getSignature();  
57 - DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);  
58 - if (Objects.nonNull(dataSource)) {  
59 - return dataSource;  
60 - }  
61 -  
62 - return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);  
63 - }  
64 -} 1 +package com.ruoyi.framework.aspectj;
  2 +
  3 +import java.util.Objects;
  4 +
  5 +import org.aspectj.lang.ProceedingJoinPoint;
  6 +import org.aspectj.lang.annotation.Around;
  7 +import org.aspectj.lang.annotation.Aspect;
  8 +import org.aspectj.lang.annotation.Pointcut;
  9 +import org.aspectj.lang.reflect.MethodSignature;
  10 +import org.slf4j.Logger;
  11 +import org.slf4j.LoggerFactory;
  12 +import org.springframework.core.annotation.AnnotationUtils;
  13 +import org.springframework.core.annotation.Order;
  14 +import org.springframework.stereotype.Component;
  15 +import com.ruoyi.common.annotation.DataSource;
  16 +import com.ruoyi.common.utils.StringUtils;
  17 +import com.ruoyi.framework.datasource.DynamicDataSourceContextHolder;
  18 +
  19 +/**
  20 + * 多数据源处理
  21 + *
  22 + * @author ruoyi
  23 + */
  24 +@Aspect
  25 +@Order(1)
  26 +@Component
  27 +public class DataSourceAspect {
  28 + protected Logger logger = LoggerFactory.getLogger(getClass());
  29 +
  30 + @Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)"
  31 + + "|| @within(com.ruoyi.common.annotation.DataSource)")
  32 + public void dsPointCut() {
  33 +
  34 + }
  35 +
  36 + @Around("dsPointCut()")
  37 + public Object around(ProceedingJoinPoint point) throws Throwable {
  38 + DataSource dataSource = getDataSource(point);
  39 +
  40 + if (StringUtils.isNotNull(dataSource)) {
  41 + DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
  42 + }
  43 +
  44 + try {
  45 + return point.proceed();
  46 + } finally {
  47 + // 销毁数据源 在执行方法之后
  48 + DynamicDataSourceContextHolder.clearDataSourceType();
  49 + }
  50 + }
  51 +
  52 + /**
  53 + * 获取需要切换的数据源
  54 + */
  55 + public DataSource getDataSource(ProceedingJoinPoint point) {
  56 + MethodSignature signature = (MethodSignature) point.getSignature();
  57 + DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
  58 + if (Objects.nonNull(dataSource)) {
  59 + return dataSource;
  60 + }
  61 +
  62 + return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
  63 + }
  64 +}
ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java renamed to ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSourceType.java
1 -package com.ruoyi.common.enums;  
2 -  
3 -/**  
4 - * 数据源  
5 - *  
6 - * @author ruoyi  
7 - */  
8 -public enum DataSourceType  
9 -{  
10 - /**  
11 - * 主库  
12 - */  
13 - MASTER,  
14 -  
15 - /**  
16 - * 从库  
17 - */  
18 - SLAVE  
19 -} 1 +package com.ruoyi.common.enums;
  2 +
  3 +/**
  4 + * 数据源
  5 + *
  6 + * @author ruoyi
  7 + */
  8 +public enum DataSourceType
  9 +{
  10 + /**
  11 + * 主库
  12 + */
  13 + MASTER,
  14 +
  15 + /**
  16 + * 从库
  17 + */
  18 + SLAVE
  19 +}
ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java renamed to ruoyi-common/src/main/java/com/ruoyi/common/annotation/DynamicDataSource.java
1 -package com.ruoyi.framework.datasource;  
2 -  
3 -import java.util.Map;  
4 -import javax.sql.DataSource;  
5 -  
6 -import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;  
7 -  
8 -/**  
9 - * 动态数据源  
10 - *  
11 - * @author ruoyi  
12 - */  
13 -public class DynamicDataSource extends AbstractRoutingDataSource {  
14 - public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {  
15 - super.setDefaultTargetDataSource(defaultTargetDataSource);  
16 - super.setTargetDataSources(targetDataSources);  
17 - super.afterPropertiesSet();  
18 - }  
19 -  
20 - @Override  
21 - protected Object determineCurrentLookupKey() {  
22 - return DynamicDataSourceContextHolder.getDataSourceType();  
23 - }  
24 -} 1 +package com.ruoyi.framework.datasource;
  2 +
  3 +import java.util.Map;
  4 +import javax.sql.DataSource;
  5 +
  6 +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
  7 +
  8 +/**
  9 + * 动态数据源
  10 + *
  11 + * @author ruoyi
  12 + */
  13 +public class DynamicDataSource extends AbstractRoutingDataSource {
  14 + public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
  15 + super.setDefaultTargetDataSource(defaultTargetDataSource);
  16 + super.setTargetDataSources(targetDataSources);
  17 + super.afterPropertiesSet();
  18 + }
  19 +
  20 + @Override
  21 + protected Object determineCurrentLookupKey() {
  22 + return DynamicDataSourceContextHolder.getDataSourceType();
  23 + }
  24 +}
ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java renamed to ruoyi-common/src/main/java/com/ruoyi/common/annotation/DynamicDataSourceContextHolder.java
1 -package com.ruoyi.framework.datasource;  
2 -  
3 -import org.slf4j.Logger;  
4 -import org.slf4j.LoggerFactory;  
5 -  
6 -/**  
7 - * 数据源切换处理  
8 - *  
9 - * @author ruoyi  
10 - */  
11 -public class DynamicDataSourceContextHolder {  
12 - public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);  
13 -  
14 - /**  
15 - * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,  
16 - * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。  
17 - */  
18 - private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();  
19 -  
20 - /**  
21 - * 设置数据源的变量  
22 - */  
23 - public static void setDataSourceType(String dsType) {  
24 - log.info("切换到{}数据源", dsType);  
25 - CONTEXT_HOLDER.set(dsType);  
26 - }  
27 -  
28 - /**  
29 - * 获得数据源的变量  
30 - */  
31 - public static String getDataSourceType() {  
32 - return CONTEXT_HOLDER.get();  
33 - }  
34 -  
35 - /**  
36 - * 清空数据源变量  
37 - */  
38 - public static void clearDataSourceType() {  
39 - CONTEXT_HOLDER.remove();  
40 - }  
41 -} 1 +package com.ruoyi.framework.datasource;
  2 +
  3 +import org.slf4j.Logger;
  4 +import org.slf4j.LoggerFactory;
  5 +
  6 +/**
  7 + * 数据源切换处理
  8 + *
  9 + * @author ruoyi
  10 + */
  11 +public class DynamicDataSourceContextHolder {
  12 + public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
  13 +
  14 + /**
  15 + * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
  16 + * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
  17 + */
  18 + private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
  19 +
  20 + /**
  21 + * 设置数据源的变量
  22 + */
  23 + public static void setDataSourceType(String dsType) {
  24 + log.info("切换到{}数据源", dsType);
  25 + CONTEXT_HOLDER.set(dsType);
  26 + }
  27 +
  28 + /**
  29 + * 获得数据源的变量
  30 + */
  31 + public static String getDataSourceType() {
  32 + return CONTEXT_HOLDER.get();
  33 + }
  34 +
  35 + /**
  36 + * 清空数据源变量
  37 + */
  38 + public static void clearDataSourceType() {
  39 + CONTEXT_HOLDER.remove();
  40 + }
  41 +}
ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java renamed to ruoyi-common/src/main/java/com/ruoyi/common/config/DruidConfig.java
1 -package com.ruoyi.framework.config;  
2 -  
3 -import java.io.IOException;  
4 -import java.util.HashMap;  
5 -import java.util.Map;  
6 -import javax.servlet.Filter;  
7 -import javax.servlet.FilterChain;  
8 -import javax.servlet.ServletException;  
9 -import javax.servlet.ServletRequest;  
10 -import javax.servlet.ServletResponse;  
11 -import javax.sql.DataSource;  
12 -  
13 -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;  
14 -import org.springframework.boot.context.properties.ConfigurationProperties;  
15 -import org.springframework.boot.web.servlet.FilterRegistrationBean;  
16 -import org.springframework.context.annotation.Bean;  
17 -import org.springframework.context.annotation.Configuration;  
18 -import org.springframework.context.annotation.Primary;  
19 -import com.alibaba.druid.pool.DruidDataSource;  
20 -import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;  
21 -import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;  
22 -import com.alibaba.druid.util.Utils;  
23 -import com.ruoyi.common.enums.DataSourceType;  
24 -import com.ruoyi.common.utils.spring.SpringUtils;  
25 -import com.ruoyi.framework.config.properties.DruidProperties;  
26 -import com.ruoyi.framework.datasource.DynamicDataSource;  
27 -  
28 -/**  
29 - * druid 配置多数据源  
30 - *  
31 - * @author ruoyi  
32 - */  
33 -@Configuration  
34 -public class DruidConfig {  
35 - @Bean  
36 - @ConfigurationProperties("spring.datasource.druid.master")  
37 - public DataSource masterDataSource(DruidProperties druidProperties) {  
38 - DruidDataSource dataSource = DruidDataSourceBuilder.create().build();  
39 - return druidProperties.dataSource(dataSource);  
40 - }  
41 -  
42 - @Bean  
43 - @ConfigurationProperties("spring.datasource.druid.slave")  
44 - @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")  
45 - public DataSource slaveDataSource(DruidProperties druidProperties) {  
46 - DruidDataSource dataSource = DruidDataSourceBuilder.create().build();  
47 - return druidProperties.dataSource(dataSource);  
48 - }  
49 -  
50 - @Bean(name = "dynamicDataSource")  
51 - @Primary  
52 - public DynamicDataSource dataSource(DataSource masterDataSource) {  
53 - Map<Object, Object> targetDataSources = new HashMap<>();  
54 - targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);  
55 - setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");  
56 - return new DynamicDataSource(masterDataSource, targetDataSources);  
57 - }  
58 -  
59 - /**  
60 - * 设置数据源  
61 - *  
62 - * @param targetDataSources 备选数据源集合  
63 - * @param sourceName 数据源名称  
64 - * @param beanName bean名称  
65 - */  
66 - public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName) {  
67 - try {  
68 - DataSource dataSource = SpringUtils.getBean(beanName);  
69 - targetDataSources.put(sourceName, dataSource);  
70 - } catch (Exception e) {  
71 - }  
72 - }  
73 -  
74 - /**  
75 - * 去除监控页面底部的广告  
76 - */  
77 - @SuppressWarnings({"rawtypes", "unchecked"})  
78 - @Bean  
79 - @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true")  
80 - public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties) {  
81 - // 获取web监控页面的参数  
82 - DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();  
83 - // 提取common.js的配置路径  
84 - String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";  
85 - String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");  
86 - final String filePath = "support/http/resources/js/common.js";  
87 - // 创建filter进行过滤  
88 - Filter filter = new Filter() {  
89 - @Override  
90 - public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {  
91 - }  
92 -  
93 - @Override  
94 - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)  
95 - throws IOException, ServletException {  
96 - chain.doFilter(request, response);  
97 - // 重置缓冲区,响应头不会被重置  
98 - response.resetBuffer();  
99 - // 获取common.js  
100 - String text = Utils.readFromResource(filePath);  
101 - // 正则替换banner, 除去底部的广告信息  
102 - text = text.replaceAll("<a.*?banner\"></a><br/>", "");  
103 - text = text.replaceAll("powered.*?shrek.wang</a>", "");  
104 - response.getWriter().write(text);  
105 - }  
106 -  
107 - @Override  
108 - public void destroy() {  
109 - }  
110 - };  
111 - FilterRegistrationBean registrationBean = new FilterRegistrationBean();  
112 - registrationBean.setFilter(filter);  
113 - registrationBean.addUrlPatterns(commonJsPattern);  
114 - return registrationBean;  
115 - }  
116 -} 1 +package com.ruoyi.framework.config;
  2 +
  3 +import java.io.IOException;
  4 +import java.util.HashMap;
  5 +import java.util.Map;
  6 +import javax.servlet.Filter;
  7 +import javax.servlet.FilterChain;
  8 +import javax.servlet.ServletException;
  9 +import javax.servlet.ServletRequest;
  10 +import javax.servlet.ServletResponse;
  11 +import javax.sql.DataSource;
  12 +
  13 +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  14 +import org.springframework.boot.context.properties.ConfigurationProperties;
  15 +import org.springframework.boot.web.servlet.FilterRegistrationBean;
  16 +import org.springframework.context.annotation.Bean;
  17 +import org.springframework.context.annotation.Configuration;
  18 +import org.springframework.context.annotation.Primary;
  19 +import com.alibaba.druid.pool.DruidDataSource;
  20 +import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
  21 +import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
  22 +import com.alibaba.druid.util.Utils;
  23 +import com.ruoyi.common.enums.DataSourceType;
  24 +import com.ruoyi.common.utils.spring.SpringUtils;
  25 +import com.ruoyi.framework.config.properties.DruidProperties;
  26 +import com.ruoyi.framework.datasource.DynamicDataSource;
  27 +
  28 +/**
  29 + * druid 配置多数据源
  30 + *
  31 + * @author ruoyi
  32 + */
  33 +@Configuration
  34 +public class DruidConfig {
  35 + @Bean
  36 + @ConfigurationProperties("spring.datasource.druid.master")
  37 + public DataSource masterDataSource(DruidProperties druidProperties) {
  38 + DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
  39 + return druidProperties.dataSource(dataSource);
  40 + }
  41 +
  42 + @Bean
  43 + @ConfigurationProperties("spring.datasource.druid.slave")
  44 + @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
  45 + public DataSource slaveDataSource(DruidProperties druidProperties) {
  46 + DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
  47 + return druidProperties.dataSource(dataSource);
  48 + }
  49 +
  50 + @Bean(name = "dynamicDataSource")
  51 + @Primary
  52 + public DynamicDataSource dataSource(DataSource masterDataSource) {
  53 + Map<Object, Object> targetDataSources = new HashMap<>();
  54 + targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
  55 + setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
  56 + return new DynamicDataSource(masterDataSource, targetDataSources);
  57 + }
  58 +
  59 + /**
  60 + * 设置数据源
  61 + *
  62 + * @param targetDataSources 备选数据源集合
  63 + * @param sourceName 数据源名称
  64 + * @param beanName bean名称
  65 + */
  66 + public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName) {
  67 + try {
  68 + DataSource dataSource = SpringUtils.getBean(beanName);
  69 + targetDataSources.put(sourceName, dataSource);
  70 + } catch (Exception e) {
  71 + }
  72 + }
  73 +
  74 + /**
  75 + * 去除监控页面底部的广告
  76 + */
  77 + @SuppressWarnings({"rawtypes", "unchecked"})
  78 + @Bean
  79 + @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true")
  80 + public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties) {
  81 + // 获取web监控页面的参数
  82 + DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
  83 + // 提取common.js的配置路径
  84 + String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";
  85 + String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
  86 + final String filePath = "support/http/resources/js/common.js";
  87 + // 创建filter进行过滤
  88 + Filter filter = new Filter() {
  89 + @Override
  90 + public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
  91 + }
  92 +
  93 + @Override
  94 + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  95 + throws IOException, ServletException {
  96 + chain.doFilter(request, response);
  97 + // 重置缓冲区,响应头不会被重置
  98 + response.resetBuffer();
  99 + // 获取common.js
  100 + String text = Utils.readFromResource(filePath);
  101 + // 正则替换banner, 除去底部的广告信息
  102 + text = text.replaceAll("<a.*?banner\"></a><br/>", "");
  103 + text = text.replaceAll("powered.*?shrek.wang</a>", "");
  104 + response.getWriter().write(text);
  105 + }
  106 +
  107 + @Override
  108 + public void destroy() {
  109 + }
  110 + };
  111 + FilterRegistrationBean registrationBean = new FilterRegistrationBean();
  112 + registrationBean.setFilter(filter);
  113 + registrationBean.addUrlPatterns(commonJsPattern);
  114 + return registrationBean;
  115 + }
  116 +}
ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java renamed to ruoyi-common/src/main/java/com/ruoyi/common/config/FilterConfig.java
1 -package com.ruoyi.framework.config;  
2 -  
3 -import java.util.HashMap;  
4 -import java.util.Map;  
5 -import javax.servlet.DispatcherType;  
6 -  
7 -import org.springframework.beans.factory.annotation.Value;  
8 -import org.springframework.boot.web.servlet.FilterRegistrationBean;  
9 -import org.springframework.context.annotation.Bean;  
10 -import org.springframework.context.annotation.Configuration;  
11 -import com.ruoyi.common.filter.RepeatableFilter;  
12 -import com.ruoyi.common.filter.XssFilter;  
13 -import com.ruoyi.common.utils.StringUtils;  
14 -  
15 -/**  
16 - * Filter配置  
17 - *  
18 - * @author ruoyi  
19 - */  
20 -@Configuration  
21 -public class FilterConfig {  
22 - @Value("${xss.enabled}")  
23 - private String enabled;  
24 -  
25 - @Value("${xss.excludes}")  
26 - private String excludes;  
27 -  
28 - @Value("${xss.urlPatterns}")  
29 - private String urlPatterns;  
30 -  
31 - @SuppressWarnings({"rawtypes", "unchecked"})  
32 - @Bean  
33 - public FilterRegistrationBean xssFilterRegistration() {  
34 - FilterRegistrationBean registration = new FilterRegistrationBean();  
35 - registration.setDispatcherTypes(DispatcherType.REQUEST);  
36 - registration.setFilter(new XssFilter());  
37 - registration.addUrlPatterns(StringUtils.split(urlPatterns, ","));  
38 - registration.setName("xssFilter");  
39 - registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);  
40 - Map<String, String> initParameters = new HashMap<String, String>();  
41 - initParameters.put("excludes", excludes);  
42 - initParameters.put("enabled", enabled);  
43 - registration.setInitParameters(initParameters);  
44 - return registration;  
45 - }  
46 -  
47 - @SuppressWarnings({"rawtypes", "unchecked"})  
48 - @Bean  
49 - public FilterRegistrationBean someFilterRegistration() {  
50 - FilterRegistrationBean registration = new FilterRegistrationBean();  
51 - registration.setFilter(new RepeatableFilter());  
52 - registration.addUrlPatterns("/*");  
53 - registration.setName("repeatableFilter");  
54 - registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);  
55 - return registration;  
56 - }  
57 -  
58 -} 1 +package com.ruoyi.framework.config;
  2 +
  3 +import java.util.HashMap;
  4 +import java.util.Map;
  5 +import javax.servlet.DispatcherType;
  6 +
  7 +import org.springframework.beans.factory.annotation.Value;
  8 +import org.springframework.boot.web.servlet.FilterRegistrationBean;
  9 +import org.springframework.context.annotation.Bean;
  10 +import org.springframework.context.annotation.Configuration;
  11 +import com.ruoyi.common.filter.RepeatableFilter;
  12 +import com.ruoyi.common.filter.XssFilter;
  13 +import com.ruoyi.common.utils.StringUtils;
  14 +
  15 +/**
  16 + * Filter配置
  17 + *
  18 + * @author ruoyi
  19 + */
  20 +@Configuration
  21 +public class FilterConfig {
  22 + @Value("${xss.enabled}")
  23 + private String enabled;
  24 +
  25 + @Value("${xss.excludes}")
  26 + private String excludes;
  27 +
  28 + @Value("${xss.urlPatterns}")
  29 + private String urlPatterns;
  30 +
  31 + @SuppressWarnings({"rawtypes", "unchecked"})
  32 + @Bean
  33 + public FilterRegistrationBean xssFilterRegistration() {
  34 + FilterRegistrationBean registration = new FilterRegistrationBean();
  35 + registration.setDispatcherTypes(DispatcherType.REQUEST);
  36 + registration.setFilter(new XssFilter());
  37 + registration.addUrlPatterns(StringUtils.split(urlPatterns, ","));
  38 + registration.setName("xssFilter");
  39 + registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
  40 + Map<String, String> initParameters = new HashMap<String, String>();
  41 + initParameters.put("excludes", excludes);
  42 + initParameters.put("enabled", enabled);
  43 + registration.setInitParameters(initParameters);
  44 + return registration;
  45 + }
  46 +
  47 + @SuppressWarnings({"rawtypes", "unchecked"})
  48 + @Bean
  49 + public FilterRegistrationBean someFilterRegistration() {
  50 + FilterRegistrationBean registration = new FilterRegistrationBean();
  51 + registration.setFilter(new RepeatableFilter());
  52 + registration.addUrlPatterns("/*");
  53 + registration.setName("repeatableFilter");
  54 + registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);
  55 + return registration;
  56 + }
  57 +
  58 +}
ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java renamed to ruoyi-common/src/main/java/com/ruoyi/common/config/ResourcesConfig.java
1 -package com.ruoyi.framework.config;  
2 -  
3 -import org.springframework.beans.factory.annotation.Autowired;  
4 -import org.springframework.context.annotation.Bean;  
5 -import org.springframework.context.annotation.Configuration;  
6 -import org.springframework.web.cors.CorsConfiguration;  
7 -import org.springframework.web.cors.UrlBasedCorsConfigurationSource;  
8 -import org.springframework.web.filter.CorsFilter;  
9 -import org.springframework.web.servlet.config.annotation.InterceptorRegistry;  
10 -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;  
11 -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;  
12 -import com.ruoyi.common.config.RuoYiConfig;  
13 -import com.ruoyi.common.constant.Constants;  
14 -import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;  
15 -  
16 -/**  
17 - * 通用配置  
18 - *  
19 - * @author ruoyi  
20 - */  
21 -@Configuration  
22 -public class ResourcesConfig implements WebMvcConfigurer {  
23 - @Autowired  
24 - private RepeatSubmitInterceptor repeatSubmitInterceptor;  
25 -  
26 - @Override  
27 - public void addResourceHandlers(ResourceHandlerRegistry registry) {  
28 - /** 本地文件上传路径 */  
29 - registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**").addResourceLocations("file:" + RuoYiConfig.getProfile() + "/");  
30 -  
31 - /** swagger配置 */  
32 - registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");  
33 - registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");  
34 - }  
35 -  
36 - /**  
37 - * 自定义拦截规则  
38 - */  
39 - @Override  
40 - public void addInterceptors(InterceptorRegistry registry) {  
41 - registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");  
42 - }  
43 -  
44 -} 1 +package com.ruoyi.framework.config;
  2 +
  3 +import org.springframework.beans.factory.annotation.Autowired;
  4 +import org.springframework.context.annotation.Bean;
  5 +import org.springframework.context.annotation.Configuration;
  6 +import org.springframework.web.cors.CorsConfiguration;
  7 +import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
  8 +import org.springframework.web.filter.CorsFilter;
  9 +import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  10 +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
  11 +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  12 +import com.ruoyi.common.config.RuoYiConfig;
  13 +import com.ruoyi.common.constant.Constants;
  14 +import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;
  15 +
  16 +/**
  17 + * 通用配置
  18 + *
  19 + * @author ruoyi
  20 + */
  21 +@Configuration
  22 +public class ResourcesConfig implements WebMvcConfigurer {
  23 + @Autowired
  24 + private RepeatSubmitInterceptor repeatSubmitInterceptor;
  25 +
  26 + @Override
  27 + public void addResourceHandlers(ResourceHandlerRegistry registry) {
  28 + /** 本地文件上传路径 */
  29 + registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**").addResourceLocations("file:" + RuoYiConfig.getProfile() + "/");
  30 +
  31 + /** swagger配置 */
  32 + registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
  33 + registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
  34 + }
  35 +
  36 + /**
  37 + * 自定义拦截规则
  38 + */
  39 + @Override
  40 + public void addInterceptors(InterceptorRegistry registry) {
  41 + registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
  42 + }
  43 +
  44 +}
ruoyi-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java renamed to ruoyi-common/src/main/java/com/ruoyi/common/config/ServerConfig.java
1 -package com.ruoyi.framework.config;  
2 -  
3 -import javax.servlet.http.HttpServletRequest;  
4 -  
5 -import org.springframework.stereotype.Component;  
6 -import com.ruoyi.common.utils.ServletUtils;  
7 -  
8 -/**  
9 - * 服务相关配置  
10 - *  
11 - * @author ruoyi  
12 - */  
13 -@Component  
14 -public class ServerConfig {  
15 - /**  
16 - * 获取完整的请求路径,包括:域名,端口,上下文访问路径  
17 - *  
18 - * @return 服务地址  
19 - */  
20 - public String getUrl() {  
21 - HttpServletRequest request = ServletUtils.getRequest();  
22 - return getDomain(request);  
23 - }  
24 -  
25 - public static String getDomain(HttpServletRequest request) {  
26 - StringBuffer url = request.getRequestURL();  
27 - String contextPath = request.getServletContext().getContextPath();  
28 - return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString();  
29 - }  
30 -} 1 +package com.ruoyi.framework.config;
  2 +
  3 +import javax.servlet.http.HttpServletRequest;
  4 +
  5 +import org.springframework.stereotype.Component;
  6 +import com.ruoyi.common.utils.ServletUtils;
  7 +
  8 +/**
  9 + * 服务相关配置
  10 + *
  11 + * @author ruoyi
  12 + */
  13 +@Component
  14 +public class ServerConfig {
  15 + /**
  16 + * 获取完整的请求路径,包括:域名,端口,上下文访问路径
  17 + *
  18 + * @return 服务地址
  19 + */
  20 + public String getUrl() {
  21 + HttpServletRequest request = ServletUtils.getRequest();
  22 + return getDomain(request);
  23 + }
  24 +
  25 + public static String getDomain(HttpServletRequest request) {
  26 + StringBuffer url = request.getRequestURL();
  27 + String contextPath = request.getServletContext().getContextPath();
  28 + return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString();
  29 + }
  30 +}
ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java renamed to ruoyi-common/src/main/java/com/ruoyi/common/config/ThreadPoolConfig.java
1 -package com.ruoyi.framework.config;  
2 -  
3 -import java.util.concurrent.ScheduledExecutorService;  
4 -import java.util.concurrent.ScheduledThreadPoolExecutor;  
5 -import java.util.concurrent.ThreadPoolExecutor;  
6 -  
7 -import org.apache.commons.lang3.concurrent.BasicThreadFactory;  
8 -import org.springframework.context.annotation.Bean;  
9 -import org.springframework.context.annotation.Configuration;  
10 -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;  
11 -import com.ruoyi.common.utils.Threads;  
12 -  
13 -/**  
14 - * 线程池配置  
15 - *  
16 - * @author ruoyi  
17 - **/  
18 -@Configuration  
19 -public class ThreadPoolConfig {  
20 - // 核心线程池大小  
21 - private int corePoolSize = 50;  
22 -  
23 - // 最大可创建的线程数  
24 - private int maxPoolSize = 200;  
25 -  
26 - // 队列最大长度  
27 - private int queueCapacity = 1000;  
28 -  
29 - // 线程池维护线程所允许的空闲时间  
30 - private int keepAliveSeconds = 300;  
31 -  
32 - @Bean(name = "threadPoolTaskExecutor")  
33 - public ThreadPoolTaskExecutor threadPoolTaskExecutor() {  
34 - ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();  
35 - executor.setMaxPoolSize(maxPoolSize);  
36 - executor.setCorePoolSize(corePoolSize);  
37 - executor.setQueueCapacity(queueCapacity);  
38 - executor.setKeepAliveSeconds(keepAliveSeconds);  
39 - // 线程池对拒绝任务(无线程可用)的处理策略  
40 - executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());  
41 - return executor;  
42 - }  
43 -  
44 - /**  
45 - * 执行周期性或定时任务  
46 - */  
47 - @Bean(name = "scheduledExecutorService")  
48 - protected ScheduledExecutorService scheduledExecutorService() {  
49 - return new ScheduledThreadPoolExecutor(corePoolSize,  
50 - new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build()) {  
51 - @Override  
52 - protected void afterExecute(Runnable r, Throwable t) {  
53 - super.afterExecute(r, t);  
54 - Threads.printException(r, t);  
55 - }  
56 - };  
57 - }  
58 -} 1 +package com.ruoyi.framework.config;
  2 +
  3 +import java.util.concurrent.ScheduledExecutorService;
  4 +import java.util.concurrent.ScheduledThreadPoolExecutor;
  5 +import java.util.concurrent.ThreadPoolExecutor;
  6 +
  7 +import org.apache.commons.lang3.concurrent.BasicThreadFactory;
  8 +import org.springframework.context.annotation.Bean;
  9 +import org.springframework.context.annotation.Configuration;
  10 +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
  11 +import com.ruoyi.common.utils.Threads;
  12 +
  13 +/**
  14 + * 线程池配置
  15 + *
  16 + * @author ruoyi
  17 + **/
  18 +@Configuration
  19 +public class ThreadPoolConfig {
  20 + // 核心线程池大小
  21 + private int corePoolSize = 50;
  22 +
  23 + // 最大可创建的线程数
  24 + private int maxPoolSize = 200;
  25 +
  26 + // 队列最大长度
  27 + private int queueCapacity = 1000;
  28 +
  29 + // 线程池维护线程所允许的空闲时间
  30 + private int keepAliveSeconds = 300;
  31 +
  32 + @Bean(name = "threadPoolTaskExecutor")
  33 + public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
  34 + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
  35 + executor.setMaxPoolSize(maxPoolSize);
  36 + executor.setCorePoolSize(corePoolSize);
  37 + executor.setQueueCapacity(queueCapacity);
  38 + executor.setKeepAliveSeconds(keepAliveSeconds);
  39 + // 线程池对拒绝任务(无线程可用)的处理策略
  40 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
  41 + return executor;
  42 + }
  43 +
  44 + /**
  45 + * 执行周期性或定时任务
  46 + */
  47 + @Bean(name = "scheduledExecutorService")
  48 + protected ScheduledExecutorService scheduledExecutorService() {
  49 + return new ScheduledThreadPoolExecutor(corePoolSize,
  50 + new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build()) {
  51 + @Override
  52 + protected void afterExecute(Runnable r, Throwable t) {
  53 + super.afterExecute(r, t);
  54 + Threads.printException(r, t);
  55 + }
  56 + };
  57 + }
  58 +}
ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java renamed to ruoyi-common/src/main/java/com/ruoyi/common/config/properties/DruidProperties.java
1 -package com.ruoyi.framework.config.properties;  
2 -  
3 -import org.springframework.beans.factory.annotation.Value;  
4 -import org.springframework.context.annotation.Configuration;  
5 -import com.alibaba.druid.pool.DruidDataSource;  
6 -  
7 -/**  
8 - * druid 配置属性  
9 - *  
10 - * @author ruoyi  
11 - */  
12 -@Configuration  
13 -public class DruidProperties {  
14 - @Value("${spring.datasource.druid.initialSize}")  
15 - private int initialSize;  
16 -  
17 - @Value("${spring.datasource.druid.minIdle}")  
18 - private int minIdle;  
19 -  
20 - @Value("${spring.datasource.druid.maxActive}")  
21 - private int maxActive;  
22 -  
23 - @Value("${spring.datasource.druid.maxWait}")  
24 - private int maxWait;  
25 -  
26 - @Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}")  
27 - private int timeBetweenEvictionRunsMillis;  
28 -  
29 - @Value("${spring.datasource.druid.minEvictableIdleTimeMillis}")  
30 - private int minEvictableIdleTimeMillis;  
31 -  
32 - @Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}")  
33 - private int maxEvictableIdleTimeMillis;  
34 -  
35 - @Value("${spring.datasource.druid.validationQuery}")  
36 - private String validationQuery;  
37 -  
38 - @Value("${spring.datasource.druid.testWhileIdle}")  
39 - private boolean testWhileIdle;  
40 -  
41 - @Value("${spring.datasource.druid.testOnBorrow}")  
42 - private boolean testOnBorrow;  
43 -  
44 - @Value("${spring.datasource.druid.testOnReturn}")  
45 - private boolean testOnReturn;  
46 -  
47 - public DruidDataSource dataSource(DruidDataSource datasource) {  
48 - /** 配置初始化大小、最小、最大 */  
49 - datasource.setInitialSize(initialSize);  
50 - datasource.setMaxActive(maxActive);  
51 - datasource.setMinIdle(minIdle);  
52 -  
53 - /** 配置获取连接等待超时的时间 */  
54 - datasource.setMaxWait(maxWait);  
55 -  
56 - /** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */  
57 - datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);  
58 -  
59 - /** 配置一个连接在池中最小、最大生存的时间,单位是毫秒 */  
60 - datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);  
61 - datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);  
62 -  
63 - /**  
64 - * 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。  
65 - */  
66 - datasource.setValidationQuery(validationQuery);  
67 - /** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */  
68 - datasource.setTestWhileIdle(testWhileIdle);  
69 - /** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */  
70 - datasource.setTestOnBorrow(testOnBorrow);  
71 - /** 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */  
72 - datasource.setTestOnReturn(testOnReturn);  
73 - return datasource;  
74 - }  
75 -} 1 +package com.ruoyi.framework.config.properties;
  2 +
  3 +import org.springframework.beans.factory.annotation.Value;
  4 +import org.springframework.context.annotation.Configuration;
  5 +import com.alibaba.druid.pool.DruidDataSource;
  6 +
  7 +/**
  8 + * druid 配置属性
  9 + *
  10 + * @author ruoyi
  11 + */
  12 +@Configuration
  13 +public class DruidProperties {
  14 + @Value("${spring.datasource.druid.initialSize}")
  15 + private int initialSize;
  16 +
  17 + @Value("${spring.datasource.druid.minIdle}")
  18 + private int minIdle;
  19 +
  20 + @Value("${spring.datasource.druid.maxActive}")
  21 + private int maxActive;
  22 +
  23 + @Value("${spring.datasource.druid.maxWait}")
  24 + private int maxWait;
  25 +
  26 + @Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}")
  27 + private int timeBetweenEvictionRunsMillis;
  28 +
  29 + @Value("${spring.datasource.druid.minEvictableIdleTimeMillis}")
  30 + private int minEvictableIdleTimeMillis;
  31 +
  32 + @Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}")
  33 + private int maxEvictableIdleTimeMillis;
  34 +
  35 + @Value("${spring.datasource.druid.validationQuery}")
  36 + private String validationQuery;
  37 +
  38 + @Value("${spring.datasource.druid.testWhileIdle}")
  39 + private boolean testWhileIdle;
  40 +
  41 + @Value("${spring.datasource.druid.testOnBorrow}")
  42 + private boolean testOnBorrow;
  43 +
  44 + @Value("${spring.datasource.druid.testOnReturn}")
  45 + private boolean testOnReturn;
  46 +
  47 + public DruidDataSource dataSource(DruidDataSource datasource) {
  48 + /** 配置初始化大小、最小、最大 */
  49 + datasource.setInitialSize(initialSize);
  50 + datasource.setMaxActive(maxActive);
  51 + datasource.setMinIdle(minIdle);
  52 +
  53 + /** 配置获取连接等待超时的时间 */
  54 + datasource.setMaxWait(maxWait);
  55 +
  56 + /** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */
  57 + datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
  58 +
  59 + /** 配置一个连接在池中最小、最大生存的时间,单位是毫秒 */
  60 + datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
  61 + datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);
  62 +
  63 + /**
  64 + * 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
  65 + */
  66 + datasource.setValidationQuery(validationQuery);
  67 + /** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */
  68 + datasource.setTestWhileIdle(testWhileIdle);
  69 + /** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */
  70 + datasource.setTestOnBorrow(testOnBorrow);
  71 + /** 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */
  72 + datasource.setTestOnReturn(testOnReturn);
  73 + return datasource;
  74 + }
  75 +}
ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java renamed to ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatSubmit.java
1 -package com.ruoyi.common.annotation;  
2 -  
3 -import java.lang.annotation.Documented;  
4 -import java.lang.annotation.ElementType;  
5 -import java.lang.annotation.Inherited;  
6 -import java.lang.annotation.Retention;  
7 -import java.lang.annotation.RetentionPolicy;  
8 -import java.lang.annotation.Target;  
9 -  
10 -/**  
11 - * 自定义注解防止表单重复提交  
12 - *  
13 - * @author ruoyi  
14 - */  
15 -@Inherited  
16 -@Target(ElementType.METHOD)  
17 -@Retention(RetentionPolicy.RUNTIME)  
18 -@Documented  
19 -public @interface RepeatSubmit {  
20 -  
21 -} 1 +package com.ruoyi.common.annotation;
  2 +
  3 +import java.lang.annotation.Documented;
  4 +import java.lang.annotation.ElementType;
  5 +import java.lang.annotation.Inherited;
  6 +import java.lang.annotation.Retention;
  7 +import java.lang.annotation.RetentionPolicy;
  8 +import java.lang.annotation.Target;
  9 +
  10 +/**
  11 + * 自定义注解防止表单重复提交
  12 + *
  13 + * @author ruoyi
  14 + */
  15 +@Inherited
  16 +@Target(ElementType.METHOD)
  17 +@Retention(RetentionPolicy.RUNTIME)
  18 +@Documented
  19 +public @interface RepeatSubmit {
  20 +
  21 +}
ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java renamed to ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatSubmitInterceptor.java
1 -package com.ruoyi.framework.interceptor;  
2 -  
3 -import java.lang.reflect.Method;  
4 -import javax.servlet.http.HttpServletRequest;  
5 -import javax.servlet.http.HttpServletResponse;  
6 -  
7 -import org.springframework.stereotype.Component;  
8 -import org.springframework.web.method.HandlerMethod;  
9 -import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;  
10 -import com.alibaba.fastjson.JSONObject;  
11 -import com.ruoyi.common.annotation.RepeatSubmit;  
12 -import com.ruoyi.common.core.domain.AjaxResult;  
13 -import com.ruoyi.common.utils.ServletUtils;  
14 -  
15 -/**  
16 - * 防止重复提交拦截器  
17 - *  
18 - * @author ruoyi  
19 - */  
20 -@Component  
21 -public abstract class RepeatSubmitInterceptor extends HandlerInterceptorAdapter {  
22 - @Override  
23 - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  
24 - if (handler instanceof HandlerMethod) {  
25 - HandlerMethod handlerMethod = (HandlerMethod) handler;  
26 - Method method = handlerMethod.getMethod();  
27 - RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);  
28 - if (annotation != null) {  
29 - if (this.isRepeatSubmit(request)) {  
30 - AjaxResult ajaxResult = AjaxResult.error("不允许重复提交,请稍后再试");  
31 - ServletUtils.renderString(response, JSONObject.toJSONString(ajaxResult));  
32 - return false;  
33 - }  
34 - }  
35 - return true;  
36 - } else {  
37 - return super.preHandle(request, response, handler);  
38 - }  
39 - }  
40 -  
41 - /**  
42 - * 验证是否重复提交由子类实现具体的防重复提交的规则  
43 - *  
44 - * @param request  
45 - * @return  
46 - * @throws Exception  
47 - */  
48 - public abstract boolean isRepeatSubmit(HttpServletRequest request);  
49 -} 1 +package com.ruoyi.framework.interceptor;
  2 +
  3 +import java.lang.reflect.Method;
  4 +import javax.servlet.http.HttpServletRequest;
  5 +import javax.servlet.http.HttpServletResponse;
  6 +
  7 +import org.springframework.stereotype.Component;
  8 +import org.springframework.web.method.HandlerMethod;
  9 +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
  10 +import com.alibaba.fastjson.JSONObject;
  11 +import com.ruoyi.common.annotation.RepeatSubmit;
  12 +import com.ruoyi.common.core.domain.AjaxResult;
  13 +import com.ruoyi.common.utils.ServletUtils;
  14 +
  15 +/**
  16 + * 防止重复提交拦截器
  17 + *
  18 + * @author ruoyi
  19 + */
  20 +@Component
  21 +public abstract class RepeatSubmitInterceptor extends HandlerInterceptorAdapter {
  22 + @Override
  23 + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  24 + if (handler instanceof HandlerMethod) {
  25 + HandlerMethod handlerMethod = (HandlerMethod) handler;
  26 + Method method = handlerMethod.getMethod();
  27 + RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
  28 + if (annotation != null) {
  29 + if (this.isRepeatSubmit(request)) {
  30 + AjaxResult ajaxResult = AjaxResult.error("不允许重复提交,请稍后再试");
  31 + ServletUtils.renderString(response, JSONObject.toJSONString(ajaxResult));
  32 + return false;
  33 + }
  34 + }
  35 + return true;
  36 + } else {
  37 + return super.preHandle(request, response, handler);
  38 + }
  39 + }
  40 +
  41 + /**
  42 + * 验证是否重复提交由子类实现具体的防重复提交的规则
  43 + *
  44 + * @param request
  45 + * @return
  46 + * @throws Exception
  47 + */
  48 + public abstract boolean isRepeatSubmit(HttpServletRequest request);
  49 +}
ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java renamed to ruoyi-common/src/main/java/com/ruoyi/common/filter/SameUrlDataInterceptor.java
1 -package com.ruoyi.framework.interceptor.impl;  
2 -  
3 -import java.util.HashMap;  
4 -import java.util.Map;  
5 -import java.util.concurrent.TimeUnit;  
6 -import javax.servlet.http.HttpServletRequest;  
7 -  
8 -import org.springframework.beans.factory.annotation.Autowired;  
9 -import org.springframework.beans.factory.annotation.Value;  
10 -import org.springframework.stereotype.Component;  
11 -import com.alibaba.fastjson.JSONObject;  
12 -import com.ruoyi.common.constant.Constants;  
13 -import com.ruoyi.common.core.redis.RedisCache;  
14 -import com.ruoyi.common.filter.RepeatedlyRequestWrapper;  
15 -import com.ruoyi.common.utils.StringUtils;  
16 -import com.ruoyi.common.utils.http.HttpHelper;  
17 -import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;  
18 -  
19 -/**  
20 - * 判断请求url和数据是否和上一次相同,  
21 - * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。  
22 - *  
23 - * @author ruoyi  
24 - */  
25 -@Component  
26 -public class SameUrlDataInterceptor extends RepeatSubmitInterceptor {  
27 - public final String REPEAT_PARAMS = "repeatParams";  
28 -  
29 - public final String REPEAT_TIME = "repeatTime";  
30 -  
31 - // 令牌自定义标识  
32 - @Value("${token.header}")  
33 - private String header;  
34 -  
35 - @Autowired  
36 - private RedisCache redisCache;  
37 -  
38 - /**  
39 - * 间隔时间,单位:秒 默认10秒  
40 - * <p>  
41 - * 两次相同参数的请求,如果间隔时间大于该参数,系统不会认定为重复提交的数据  
42 - */  
43 - private int intervalTime = 10;  
44 -  
45 - public void setIntervalTime(int intervalTime) {  
46 - this.intervalTime = intervalTime;  
47 - }  
48 -  
49 - @SuppressWarnings("unchecked")  
50 - @Override  
51 - public boolean isRepeatSubmit(HttpServletRequest request) {  
52 - String nowParams = "";  
53 - if (request instanceof RepeatedlyRequestWrapper) {  
54 - RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request;  
55 - nowParams = HttpHelper.getBodyString(repeatedlyRequest);  
56 - }  
57 -  
58 - // body参数为空,获取Parameter的数据  
59 - if (StringUtils.isEmpty(nowParams)) {  
60 - nowParams = JSONObject.toJSONString(request.getParameterMap());  
61 - }  
62 - Map<String, Object> nowDataMap = new HashMap<String, Object>();  
63 - nowDataMap.put(REPEAT_PARAMS, nowParams);  
64 - nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());  
65 -  
66 - // 请求地址(作为存放cache的key值)  
67 - String url = request.getRequestURI();  
68 -  
69 - // 唯一值(没有消息头则使用请求地址)  
70 - String submitKey = request.getHeader(header);  
71 - if (StringUtils.isEmpty(submitKey)) {  
72 - submitKey = url;  
73 - }  
74 -  
75 - // 唯一标识(指定key + 消息头)  
76 - String cache_repeat_key = Constants.REPEAT_SUBMIT_KEY + submitKey;  
77 -  
78 - Object sessionObj = redisCache.getCacheObject(cache_repeat_key);  
79 - if (sessionObj != null) {  
80 - Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;  
81 - if (sessionMap.containsKey(url)) {  
82 - Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url);  
83 - if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap)) {  
84 - return true;  
85 - }  
86 - }  
87 - }  
88 - Map<String, Object> cacheMap = new HashMap<String, Object>();  
89 - cacheMap.put(url, nowDataMap);  
90 - redisCache.setCacheObject(cache_repeat_key, cacheMap, intervalTime, TimeUnit.SECONDS);  
91 - return false;  
92 - }  
93 -  
94 - /**  
95 - * 判断参数是否相同  
96 - */  
97 - private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap) {  
98 - String nowParams = (String) nowMap.get(REPEAT_PARAMS);  
99 - String preParams = (String) preMap.get(REPEAT_PARAMS);  
100 - return nowParams.equals(preParams);  
101 - }  
102 -  
103 - /**  
104 - * 判断两次间隔时间  
105 - */  
106 - private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap) {  
107 - long time1 = (Long) nowMap.get(REPEAT_TIME);  
108 - long time2 = (Long) preMap.get(REPEAT_TIME);  
109 - if ((time1 - time2) < (this.intervalTime * 1000)) {  
110 - return true;  
111 - }  
112 - return false;  
113 - }  
114 -} 1 +package com.ruoyi.framework.interceptor.impl;
  2 +
  3 +import java.util.HashMap;
  4 +import java.util.Map;
  5 +import java.util.concurrent.TimeUnit;
  6 +import javax.servlet.http.HttpServletRequest;
  7 +
  8 +import org.springframework.beans.factory.annotation.Autowired;
  9 +import org.springframework.beans.factory.annotation.Value;
  10 +import org.springframework.stereotype.Component;
  11 +import com.alibaba.fastjson.JSONObject;
  12 +import com.ruoyi.common.constant.Constants;
  13 +import com.ruoyi.common.core.redis.RedisCache;
  14 +import com.ruoyi.common.filter.RepeatedlyRequestWrapper;
  15 +import com.ruoyi.common.utils.StringUtils;
  16 +import com.ruoyi.common.utils.http.HttpHelper;
  17 +import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;
  18 +
  19 +/**
  20 + * 判断请求url和数据是否和上一次相同,
  21 + * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。
  22 + *
  23 + * @author ruoyi
  24 + */
  25 +@Component
  26 +public class SameUrlDataInterceptor extends RepeatSubmitInterceptor {
  27 + public final String REPEAT_PARAMS = "repeatParams";
  28 +
  29 + public final String REPEAT_TIME = "repeatTime";
  30 +
  31 + // 令牌自定义标识
  32 + @Value("${token.header}")
  33 + private String header;
  34 +
  35 + @Autowired
  36 + private RedisCache redisCache;
  37 +
  38 + /**
  39 + * 间隔时间,单位:秒 默认10秒
  40 + * <p>
  41 + * 两次相同参数的请求,如果间隔时间大于该参数,系统不会认定为重复提交的数据
  42 + */
  43 + private int intervalTime = 10;
  44 +
  45 + public void setIntervalTime(int intervalTime) {
  46 + this.intervalTime = intervalTime;
  47 + }
  48 +
  49 + @SuppressWarnings("unchecked")
  50 + @Override
  51 + public boolean isRepeatSubmit(HttpServletRequest request) {
  52 + String nowParams = "";
  53 + if (request instanceof RepeatedlyRequestWrapper) {
  54 + RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request;
  55 + nowParams = HttpHelper.getBodyString(repeatedlyRequest);
  56 + }
  57 +
  58 + // body参数为空,获取Parameter的数据
  59 + if (StringUtils.isEmpty(nowParams)) {
  60 + nowParams = JSONObject.toJSONString(request.getParameterMap());
  61 + }
  62 + Map<String, Object> nowDataMap = new HashMap<String, Object>();
  63 + nowDataMap.put(REPEAT_PARAMS, nowParams);
  64 + nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());
  65 +
  66 + // 请求地址(作为存放cache的key值)
  67 + String url = request.getRequestURI();
  68 +
  69 + // 唯一值(没有消息头则使用请求地址)
  70 + String submitKey = request.getHeader(header);
  71 + if (StringUtils.isEmpty(submitKey)) {
  72 + submitKey = url;
  73 + }
  74 +
  75 + // 唯一标识(指定key + 消息头)
  76 + String cache_repeat_key = Constants.REPEAT_SUBMIT_KEY + submitKey;
  77 +
  78 + Object sessionObj = redisCache.getCacheObject(cache_repeat_key);
  79 + if (sessionObj != null) {
  80 + Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;
  81 + if (sessionMap.containsKey(url)) {
  82 + Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url);
  83 + if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap)) {
  84 + return true;
  85 + }
  86 + }
  87 + }
  88 + Map<String, Object> cacheMap = new HashMap<String, Object>();
  89 + cacheMap.put(url, nowDataMap);
  90 + redisCache.setCacheObject(cache_repeat_key, cacheMap, intervalTime, TimeUnit.SECONDS);
  91 + return false;
  92 + }
  93 +
  94 + /**
  95 + * 判断参数是否相同
  96 + */
  97 + private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap) {
  98 + String nowParams = (String) nowMap.get(REPEAT_PARAMS);
  99 + String preParams = (String) preMap.get(REPEAT_PARAMS);
  100 + return nowParams.equals(preParams);
  101 + }
  102 +
  103 + /**
  104 + * 判断两次间隔时间
  105 + */
  106 + private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap) {
  107 + long time1 = (Long) nowMap.get(REPEAT_TIME);
  108 + long time2 = (Long) preMap.get(REPEAT_TIME);
  109 + if ((time1 - time2) < (this.intervalTime * 1000)) {
  110 + return true;
  111 + }
  112 + return false;
  113 + }
  114 +}
ruoyi-admin/src/main/resources/application-druid.yml renamed to ruoyi-common/src/main/resources/application-druid.yml
1 -# 数据源配置  
2 -spring:  
3 - datasource:  
4 - type: com.alibaba.druid.pool.DruidDataSource  
5 - driverClassName: com.mysql.cj.jdbc.Driver  
6 - druid:  
7 - # 主库数据源  
8 - master:  
9 - url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8  
10 - username: root  
11 - password: password  
12 - # 从库数据源  
13 - slave:  
14 - # 从数据源开关/默认关闭  
15 - enabled: false  
16 - url:  
17 - username:  
18 - password:  
19 - # 初始连接数  
20 - initialSize: 5  
21 - # 最小连接池数量  
22 - minIdle: 10  
23 - # 最大连接池数量  
24 - maxActive: 20  
25 - # 配置获取连接等待超时的时间  
26 - maxWait: 60000  
27 - # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒  
28 - timeBetweenEvictionRunsMillis: 60000  
29 - # 配置一个连接在池中最小生存的时间,单位是毫秒  
30 - minEvictableIdleTimeMillis: 300000  
31 - # 配置一个连接在池中最大生存的时间,单位是毫秒  
32 - maxEvictableIdleTimeMillis: 900000  
33 - # 配置检测连接是否有效  
34 - validationQuery: SELECT 1 FROM DUAL  
35 - testWhileIdle: true  
36 - testOnBorrow: false  
37 - testOnReturn: false  
38 - webStatFilter:  
39 - enabled: true  
40 - statViewServlet:  
41 - enabled: true  
42 - # 设置白名单,不填则允许所有访问  
43 - allow:  
44 - url-pattern: /druid/*  
45 - # 控制台管理用户名和密码  
46 - login-username:  
47 - login-password:  
48 - filter:  
49 - stat:  
50 - enabled: true  
51 - # 慢SQL记录  
52 - log-slow-sql: true  
53 - slow-sql-millis: 1000  
54 - merge-sql: true  
55 - wall:  
56 - config: 1 +# 数据源配置
  2 +spring:
  3 + datasource:
  4 + type: com.alibaba.druid.pool.DruidDataSource
  5 + driverClassName: com.mysql.cj.jdbc.Driver
  6 + druid:
  7 + # 主库数据源
  8 + master:
  9 + url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
  10 + username: root
  11 + password: password
  12 + # 从库数据源
  13 + slave:
  14 + # 从数据源开关/默认关闭
  15 + enabled: false
  16 + url:
  17 + username:
  18 + password:
  19 + # 初始连接数
  20 + initialSize: 5
  21 + # 最小连接池数量
  22 + minIdle: 10
  23 + # 最大连接池数量
  24 + maxActive: 20
  25 + # 配置获取连接等待超时的时间
  26 + maxWait: 60000
  27 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
  28 + timeBetweenEvictionRunsMillis: 60000
  29 + # 配置一个连接在池中最小生存的时间,单位是毫秒
  30 + minEvictableIdleTimeMillis: 300000
  31 + # 配置一个连接在池中最大生存的时间,单位是毫秒
  32 + maxEvictableIdleTimeMillis: 900000
  33 + # 配置检测连接是否有效
  34 + validationQuery: SELECT 1 FROM DUAL
  35 + testWhileIdle: true
  36 + testOnBorrow: false
  37 + testOnReturn: false
  38 + webStatFilter:
  39 + enabled: true
  40 + statViewServlet:
  41 + enabled: true
  42 + # 设置白名单,不填则允许所有访问
  43 + allow:
  44 + url-pattern: /druid/*
  45 + # 控制台管理用户名和密码
  46 + login-username:
  47 + login-password:
  48 + filter:
  49 + stat:
  50 + enabled: true
  51 + # 慢SQL记录
  52 + log-slow-sql: true
  53 + slow-sql-millis: 1000
  54 + merge-sql: true
  55 + wall:
  56 + config:
57 multi-statement-allow: true 57 multi-statement-allow: true
58 \ No newline at end of file 58 \ No newline at end of file
ruoyi-admin/src/main/resources/application.yml renamed to ruoyi-common/src/main/resources/application.yml
1 -# 项目相关配置  
2 -ruoyi:  
3 - # 名称  
4 - name: RuoYi  
5 - # 版本  
6 - version: 3.3.0  
7 - # 版权年份  
8 - copyrightYear: 2020  
9 - # 实例演示开关  
10 - demoEnabled: true  
11 - # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)  
12 - profile: D:/ruoyi/uploadPath  
13 - # 获取ip地址开关  
14 - addressEnabled: false  
15 -  
16 -# 开发环境配置  
17 -server:  
18 - # 服务器的HTTP端口,默认为8080  
19 - port: 8080  
20 - servlet:  
21 - # 应用的访问路径  
22 - context-path: /  
23 - tomcat:  
24 - # tomcat的URI编码  
25 - uri-encoding: UTF-8  
26 - # tomcat最大线程数,默认为200  
27 - max-threads: 800  
28 - # Tomcat启动初始化的线程数,默认值25  
29 - min-spare-threads: 30  
30 -  
31 -# 日志配置  
32 -logging:  
33 - level:  
34 - com.ruoyi: debug  
35 - org.springframework: warn  
36 -  
37 -# 防止XSS攻击  
38 -xss:  
39 - # 过滤开关  
40 - enabled: true  
41 - # 排除链接(多个用逗号分隔)  
42 - excludes: /system/notice/*  
43 - # 匹配链接  
44 - urlPatterns: /system/*,/monitor/*,/tool/* 1 +# 项目相关配置
  2 +ruoyi:
  3 + # 名称
  4 + name: RuoYi
  5 + # 版本
  6 + version: 3.3.0
  7 + # 版权年份
  8 + copyrightYear: 2020
  9 + # 实例演示开关
  10 + demoEnabled: true
  11 + # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  12 + profile: D:/ruoyi/uploadPath
  13 + # 获取ip地址开关
  14 + addressEnabled: false
  15 +
  16 +# 开发环境配置
  17 +server:
  18 + # 服务器的HTTP端口,默认为8080
  19 + port: 8080
  20 + servlet:
  21 + # 应用的访问路径
  22 + context-path: /
  23 + tomcat:
  24 + # tomcat的URI编码
  25 + uri-encoding: UTF-8
  26 + # tomcat最大线程数,默认为200
  27 + max-threads: 800
  28 + # Tomcat启动初始化的线程数,默认值25
  29 + min-spare-threads: 30
  30 +
  31 +# 日志配置
  32 +logging:
  33 + level:
  34 + com.ruoyi: debug
  35 + org.springframework: warn
  36 +
  37 +# 防止XSS攻击
  38 +xss:
  39 + # 过滤开关
  40 + enabled: true
  41 + # 排除链接(多个用逗号分隔)
  42 + excludes: /system/notice/*
  43 + # 匹配链接
  44 + urlPatterns: /system/*,/monitor/*,/tool/*
ruoyi-admin/src/main/resources/i18n/messages.properties renamed to ruoyi-common/src/main/resources/i18n/messages.properties
1 -#错误消息  
2 -not.null=* 必须填写  
3 -user.jcaptcha.error=验证码错误  
4 -user.jcaptcha.expire=验证码已失效  
5 -user.not.exists=用户不存在/密码错误  
6 -user.password.not.match=用户不存在/密码错误  
7 -user.password.retry.limit.count=密码输入错误{0}次  
8 -user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定10分钟  
9 -user.password.delete=对不起,您的账号已被删除  
10 -user.blocked=用户已封禁,请联系管理员  
11 -role.blocked=角色已封禁,请联系管理员  
12 -user.logout.success=退出成功  
13 -  
14 -length.not.valid=长度必须在{min}到{max}个字符之间  
15 -  
16 -user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头  
17 -user.password.not.valid=* 5-50个字符  
18 -  
19 -user.email.not.valid=邮箱格式错误  
20 -user.mobile.phone.number.not.valid=手机号格式错误  
21 -user.login.success=登录成功  
22 -user.notfound=请重新登录  
23 -user.forcelogout=管理员强制退出,请重新登录  
24 -user.unknown.error=未知错误,请重新登录  
25 -  
26 -##文件上传消息  
27 -upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB!  
28 -upload.filename.exceed.length=上传的文件名最长{0}个字符  
29 -  
30 -##权限  
31 -no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]  
32 -no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]  
33 -no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]  
34 -no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]  
35 -no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]  
36 -no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}] 1 +#错误消息
  2 +not.null=* 必须填写
  3 +user.jcaptcha.error=验证码错误
  4 +user.jcaptcha.expire=验证码已失效
  5 +user.not.exists=用户不存在/密码错误
  6 +user.password.not.match=用户不存在/密码错误
  7 +user.password.retry.limit.count=密码输入错误{0}次
  8 +user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定10分钟
  9 +user.password.delete=对不起,您的账号已被删除
  10 +user.blocked=用户已封禁,请联系管理员
  11 +role.blocked=角色已封禁,请联系管理员
  12 +user.logout.success=退出成功
  13 +
  14 +length.not.valid=长度必须在{min}到{max}个字符之间
  15 +
  16 +user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
  17 +user.password.not.valid=* 5-50个字符
  18 +
  19 +user.email.not.valid=邮箱格式错误
  20 +user.mobile.phone.number.not.valid=手机号格式错误
  21 +user.login.success=登录成功
  22 +user.notfound=请重新登录
  23 +user.forcelogout=管理员强制退出,请重新登录
  24 +user.unknown.error=未知错误,请重新登录
  25 +
  26 +##文件上传消息
  27 +upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB!
  28 +upload.filename.exceed.length=上传的文件名最长{0}个字符
  29 +
  30 +##权限
  31 +no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
  32 +no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
  33 +no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
  34 +no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
  35 +no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
  36 +no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]
ruoyi-admin/src/main/resources/logback.xml renamed to ruoyi-common/src/main/resources/logback.xml
1 -<?xml version="1.0" encoding="UTF-8"?>  
2 -<configuration>  
3 - <!-- 日志存放路径 -->  
4 - <property name="log.path" value="/home/ruoyi/logs" />  
5 - <!-- 日志输出格式 -->  
6 - <property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />  
7 -  
8 - <!-- 控制台输出 -->  
9 - <appender name="console" class="ch.qos.logback.core.ConsoleAppender">  
10 - <encoder>  
11 - <pattern>${log.pattern}</pattern>  
12 - </encoder>  
13 - </appender>  
14 -  
15 - <!-- 系统日志输出 -->  
16 - <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">  
17 - <file>${log.path}/sys-info.log</file>  
18 - <!-- 循环政策:基于时间创建日志文件 -->  
19 - <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  
20 - <!-- 日志文件名格式 -->  
21 - <fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>  
22 - <!-- 日志最大的历史 60天 -->  
23 - <maxHistory>60</maxHistory>  
24 - </rollingPolicy>  
25 - <encoder>  
26 - <pattern>${log.pattern}</pattern>  
27 - </encoder>  
28 - <filter class="ch.qos.logback.classic.filter.LevelFilter">  
29 - <!-- 过滤的级别 -->  
30 - <level>INFO</level>  
31 - <!-- 匹配时的操作:接收(记录) -->  
32 - <onMatch>ACCEPT</onMatch>  
33 - <!-- 不匹配时的操作:拒绝(不记录) -->  
34 - <onMismatch>DENY</onMismatch>  
35 - </filter>  
36 - </appender>  
37 -  
38 - <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">  
39 - <file>${log.path}/sys-error.log</file>  
40 - <!-- 循环政策:基于时间创建日志文件 -->  
41 - <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  
42 - <!-- 日志文件名格式 -->  
43 - <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>  
44 - <!-- 日志最大的历史 60天 -->  
45 - <maxHistory>60</maxHistory>  
46 - </rollingPolicy>  
47 - <encoder>  
48 - <pattern>${log.pattern}</pattern>  
49 - </encoder>  
50 - <filter class="ch.qos.logback.classic.filter.LevelFilter">  
51 - <!-- 过滤的级别 -->  
52 - <level>ERROR</level>  
53 - <!-- 匹配时的操作:接收(记录) -->  
54 - <onMatch>ACCEPT</onMatch>  
55 - <!-- 不匹配时的操作:拒绝(不记录) -->  
56 - <onMismatch>DENY</onMismatch>  
57 - </filter>  
58 - </appender>  
59 -  
60 - <!-- 用户访问日志输出 -->  
61 - <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">  
62 - <file>${log.path}/sys-user.log</file>  
63 - <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  
64 - <!-- 按天回滚 daily -->  
65 - <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>  
66 - <!-- 日志最大的历史 60天 -->  
67 - <maxHistory>60</maxHistory>  
68 - </rollingPolicy>  
69 - <encoder>  
70 - <pattern>${log.pattern}</pattern>  
71 - </encoder>  
72 - </appender>  
73 -  
74 - <!-- 系统模块日志级别控制 -->  
75 - <logger name="com.ruoyi" level="info" />  
76 - <!-- Spring日志级别控制 -->  
77 - <logger name="org.springframework" level="warn" />  
78 -  
79 - <root level="info">  
80 - <appender-ref ref="console" />  
81 - </root>  
82 -  
83 - <!--系统操作日志-->  
84 - <root level="info">  
85 - <appender-ref ref="file_info" />  
86 - <appender-ref ref="file_error" />  
87 - </root>  
88 -  
89 - <!--系统用户操作日志-->  
90 - <logger name="sys-user" level="info">  
91 - <appender-ref ref="sys-user"/>  
92 - </logger> 1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<configuration>
  3 + <!-- 日志存放路径 -->
  4 + <property name="log.path" value="/home/ruoyi/logs" />
  5 + <!-- 日志输出格式 -->
  6 + <property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
  7 +
  8 + <!-- 控制台输出 -->
  9 + <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
  10 + <encoder>
  11 + <pattern>${log.pattern}</pattern>
  12 + </encoder>
  13 + </appender>
  14 +
  15 + <!-- 系统日志输出 -->
  16 + <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
  17 + <file>${log.path}/sys-info.log</file>
  18 + <!-- 循环政策:基于时间创建日志文件 -->
  19 + <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
  20 + <!-- 日志文件名格式 -->
  21 + <fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
  22 + <!-- 日志最大的历史 60天 -->
  23 + <maxHistory>60</maxHistory>
  24 + </rollingPolicy>
  25 + <encoder>
  26 + <pattern>${log.pattern}</pattern>
  27 + </encoder>
  28 + <filter class="ch.qos.logback.classic.filter.LevelFilter">
  29 + <!-- 过滤的级别 -->
  30 + <level>INFO</level>
  31 + <!-- 匹配时的操作:接收(记录) -->
  32 + <onMatch>ACCEPT</onMatch>
  33 + <!-- 不匹配时的操作:拒绝(不记录) -->
  34 + <onMismatch>DENY</onMismatch>
  35 + </filter>
  36 + </appender>
  37 +
  38 + <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
  39 + <file>${log.path}/sys-error.log</file>
  40 + <!-- 循环政策:基于时间创建日志文件 -->
  41 + <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
  42 + <!-- 日志文件名格式 -->
  43 + <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
  44 + <!-- 日志最大的历史 60天 -->
  45 + <maxHistory>60</maxHistory>
  46 + </rollingPolicy>
  47 + <encoder>
  48 + <pattern>${log.pattern}</pattern>
  49 + </encoder>
  50 + <filter class="ch.qos.logback.classic.filter.LevelFilter">
  51 + <!-- 过滤的级别 -->
  52 + <level>ERROR</level>
  53 + <!-- 匹配时的操作:接收(记录) -->
  54 + <onMatch>ACCEPT</onMatch>
  55 + <!-- 不匹配时的操作:拒绝(不记录) -->
  56 + <onMismatch>DENY</onMismatch>
  57 + </filter>
  58 + </appender>
  59 +
  60 + <!-- 用户访问日志输出 -->
  61 + <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
  62 + <file>${log.path}/sys-user.log</file>
  63 + <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
  64 + <!-- 按天回滚 daily -->
  65 + <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
  66 + <!-- 日志最大的历史 60天 -->
  67 + <maxHistory>60</maxHistory>
  68 + </rollingPolicy>
  69 + <encoder>
  70 + <pattern>${log.pattern}</pattern>
  71 + </encoder>
  72 + </appender>
  73 +
  74 + <!-- 系统模块日志级别控制 -->
  75 + <logger name="com.ruoyi" level="info" />
  76 + <!-- Spring日志级别控制 -->
  77 + <logger name="org.springframework" level="warn" />
  78 +
  79 + <root level="info">
  80 + <appender-ref ref="console" />
  81 + </root>
  82 +
  83 + <!--系统操作日志-->
  84 + <root level="info">
  85 + <appender-ref ref="file_info" />
  86 + <appender-ref ref="file_error" />
  87 + </root>
  88 +
  89 + <!--系统用户操作日志-->
  90 + <logger name="sys-user" level="info">
  91 + <appender-ref ref="sys-user"/>
  92 + </logger>
93 </configuration> 93 </configuration>
94 \ No newline at end of file 94 \ No newline at end of file
ruoyi-ui/src/api/monitor/online.js renamed to ruoyi-ui/src/api/system/session.js
1 -import request from '@/utils/request'  
2 -  
3 -// 查询在线用户列表  
4 -export function list(query) {  
5 - return request({  
6 - url: '/monitor/online/list',  
7 - method: 'get',  
8 - params: query  
9 - })  
10 -}  
11 -  
12 -// 强退用户  
13 -export function forceLogout(tokenId) {  
14 - return request({  
15 - url: '/monitor/online/' + tokenId,  
16 - method: 'delete'  
17 - })  
18 -} 1 +import request from '@/utils/request'
  2 +
  3 +// 查询在线用户列表
  4 +export function list(query) {
  5 + return request({
  6 + url: '/system/user-session/page',
  7 + method: 'get',
  8 + params: query
  9 + })
  10 +}
  11 +
  12 +// 强退用户
  13 +export function forceLogout(tokenId) {
  14 + return request({
  15 + url: '/system/user-session/delete?id=' + tokenId,
  16 + method: 'delete'
  17 + })
  18 +}
ruoyi-ui/src/views/monitor/online/index.vue renamed to ruoyi-ui/src/views/system/session/index.vue
1 -<template>  
2 - <div class="app-container">  
3 - <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="68px">  
4 - <el-form-item label="登录地址" prop="ipaddr">  
5 - <el-input  
6 - v-model="queryParams.ipaddr"  
7 - placeholder="请输入登录地址"  
8 - clearable  
9 - size="small"  
10 - @keyup.enter.native="handleQuery"  
11 - />  
12 - </el-form-item>  
13 - <el-form-item label="用户名称" prop="userName">  
14 - <el-input  
15 - v-model="queryParams.userName"  
16 - placeholder="请输入用户名称"  
17 - clearable  
18 - size="small"  
19 - @keyup.enter.native="handleQuery"  
20 - />  
21 - </el-form-item>  
22 - <el-form-item>  
23 - <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>  
24 - <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>  
25 - </el-form-item>  
26 -  
27 - </el-form>  
28 - <el-table  
29 - v-loading="loading"  
30 - :data="list.slice((pageNum-1)*pageSize,pageNum*pageSize)"  
31 - style="width: 100%;"  
32 - >  
33 - <el-table-column label="序号" type="index" align="center">  
34 - <template slot-scope="scope">  
35 - <span>{{(pageNum - 1) * pageSize + scope.$index + 1}}</span>  
36 - </template>  
37 - </el-table-column>  
38 - <el-table-column label="会话编号" align="center" prop="tokenId" :show-overflow-tooltip="true" />  
39 - <el-table-column label="登录名称" align="center" prop="userName" :show-overflow-tooltip="true" />  
40 - <el-table-column label="部门名称" align="center" prop="deptName" />  
41 - <el-table-column label="主机" align="center" prop="ipaddr" :show-overflow-tooltip="true" />  
42 - <el-table-column label="登录地点" align="center" prop="loginLocation" :show-overflow-tooltip="true" />  
43 - <el-table-column label="浏览器" align="center" prop="browser" />  
44 - <el-table-column label="操作系统" align="center" prop="os" />  
45 - <el-table-column label="登录时间" align="center" prop="loginTime" width="180">  
46 - <template slot-scope="scope">  
47 - <span>{{ parseTime(scope.row.loginTime) }}</span>  
48 - </template>  
49 - </el-table-column>  
50 - <el-table-column label="操作" align="center" class-name="small-padding fixed-width">  
51 - <template slot-scope="scope">  
52 - <el-button  
53 - size="mini"  
54 - type="text"  
55 - icon="el-icon-delete"  
56 - @click="handleForceLogout(scope.row)"  
57 - v-hasPermi="['monitor:online:forceLogout']"  
58 - >强退</el-button>  
59 - </template>  
60 - </el-table-column>  
61 - </el-table>  
62 -  
63 - <pagination v-show="total>0" :total="total" :page.sync="pageNum" :limit.sync="pageSize" />  
64 - </div>  
65 -</template>  
66 -  
67 -<script>  
68 -import { list, forceLogout } from "@/api/monitor/online";  
69 -  
70 -export default {  
71 - name: "Online",  
72 - data() {  
73 - return {  
74 - // 遮罩层  
75 - loading: true,  
76 - // 总条数  
77 - total: 0,  
78 - // 表格数据  
79 - list: [],  
80 - pageNum: 1,  
81 - pageSize: 10,  
82 - // 查询参数  
83 - queryParams: {  
84 - ipaddr: undefined,  
85 - userName: undefined  
86 - }  
87 - };  
88 - },  
89 - created() {  
90 - this.getList();  
91 - },  
92 - methods: {  
93 - /** 查询登录日志列表 */  
94 - getList() {  
95 - this.loading = true;  
96 - list(this.queryParams).then(response => {  
97 - this.list = response.rows;  
98 - this.total = response.total;  
99 - this.loading = false;  
100 - });  
101 - },  
102 - /** 搜索按钮操作 */  
103 - handleQuery() {  
104 - this.pageNum = 1;  
105 - this.getList();  
106 - },  
107 - /** 重置按钮操作 */  
108 - resetQuery() {  
109 - this.resetForm("queryForm");  
110 - this.handleQuery();  
111 - },  
112 - /** 强退按钮操作 */  
113 - handleForceLogout(row) {  
114 - this.$confirm('是否确认强退名称为"' + row.userName + '"的数据项?', "警告", {  
115 - confirmButtonText: "确定",  
116 - cancelButtonText: "取消",  
117 - type: "warning"  
118 - }).then(function() {  
119 - return forceLogout(row.tokenId);  
120 - }).then(() => {  
121 - this.getList();  
122 - this.msgSuccess("强退成功");  
123 - })  
124 - }  
125 - }  
126 -};  
127 -</script>  
128 - 1 +<template>
  2 + <div class="app-container">
  3 + <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
  4 + <el-form-item label="登录地址" prop="userIp">
  5 + <el-input
  6 + v-model="queryParams.userIp"
  7 + placeholder="请输入登录地址"
  8 + clearable
  9 + size="small"
  10 + @keyup.enter.native="handleQuery"
  11 + />
  12 + </el-form-item>
  13 + <el-form-item label="用户名称" prop="username">
  14 + <el-input
  15 + v-model="queryParams.username"
  16 + placeholder="请输入用户名称"
  17 + clearable
  18 + size="small"
  19 + @keyup.enter.native="handleQuery"
  20 + />
  21 + </el-form-item>
  22 + <el-form-item>
  23 + <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
  24 + <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
  25 + </el-form-item>
  26 +
  27 + </el-form>
  28 + <el-table
  29 + v-loading="loading"
  30 + :data="list"
  31 + style="width: 100%;"
  32 + >
  33 + <el-table-column label="会话编号" align="center" prop="id" width="300" />
  34 + <el-table-column label="登录名称" align="center" prop="username" width="100" />
  35 + <el-table-column label="部门名称" align="center" prop="deptName" width="100" />
  36 + <el-table-column label="登陆地址" align="center" prop="userIp" width="100" />
  37 + <el-table-column label="userAgent" align="center" prop="userAgent" :show-overflow-tooltip="true" />
  38 + <el-table-column label="登录时间" align="center" prop="createTime" width="180">
  39 + <template slot-scope="scope">
  40 + <span>{{ parseTime(scope.row.createTime) }}</span>
  41 + </template>
  42 + </el-table-column>
  43 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  44 + <template slot-scope="scope">
  45 + <el-button
  46 + size="mini"
  47 + type="text"
  48 + icon="el-icon-delete"
  49 + @click="handleForceLogout(scope.row)"
  50 + v-hasPermi="['system:user-session:delete']"
  51 + >强退</el-button>
  52 + </template>
  53 + </el-table-column>
  54 + </el-table>
  55 +
  56 + <pagination v-show="total>0" :total="total" :page.sync="pageNo" :limit.sync="pageSize" />
  57 + </div>
  58 +</template>
  59 +
  60 +<script>
  61 +import { list, forceLogout } from "@/api/system/session";
  62 +
  63 +export default {
  64 + name: "Online",
  65 + data() {
  66 + return {
  67 + // 遮罩层
  68 + loading: true,
  69 + // 总条数
  70 + total: 0,
  71 + // 表格数据
  72 + list: [],
  73 + // 查询参数
  74 + queryParams: {
  75 + pageNo: 1,
  76 + pageSize: 10,
  77 + userIp: undefined,
  78 + username: undefined
  79 + }
  80 + };
  81 + },
  82 + created() {
  83 + this.getList();
  84 + },
  85 + methods: {
  86 + /** 查询登录日志列表 */
  87 + getList() {
  88 + this.loading = true;
  89 + list(this.queryParams).then(response => {
  90 + this.list = response.data.list;
  91 + this.total = response.data.total;
  92 + this.loading = false;
  93 + });
  94 + },
  95 + /** 搜索按钮操作 */
  96 + handleQuery() {
  97 + this.pageNo = 1;
  98 + this.getList();
  99 + },
  100 + /** 重置按钮操作 */
  101 + resetQuery() {
  102 + this.resetForm("queryForm");
  103 + this.handleQuery();
  104 + },
  105 + /** 强退按钮操作 */
  106 + handleForceLogout(row) {
  107 + this.$confirm('是否确认强退名称为"' + row.username + '"的数据项?', "警告", {
  108 + confirmButtonText: "确定",
  109 + cancelButtonText: "取消",
  110 + type: "warning"
  111 + }).then(function() {
  112 + return forceLogout(row.id);
  113 + }).then(() => {
  114 + this.getList();
  115 + this.msgSuccess("强退成功");
  116 + })
  117 + }
  118 + }
  119 +};
  120 +</script>
  121 +
src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysUserSessionController.java
@@ -4,8 +4,14 @@ import cn.iocoder.dashboard.common.pojo.CommonResult; @@ -4,8 +4,14 @@ import cn.iocoder.dashboard.common.pojo.CommonResult;
4 import cn.iocoder.dashboard.common.pojo.PageResult; 4 import cn.iocoder.dashboard.common.pojo.PageResult;
5 import cn.iocoder.dashboard.modules.system.controller.auth.vo.session.SysUserSessionPageItemRespVO; 5 import cn.iocoder.dashboard.modules.system.controller.auth.vo.session.SysUserSessionPageItemRespVO;
6 import cn.iocoder.dashboard.modules.system.controller.auth.vo.session.SysUserSessionPageReqVO; 6 import cn.iocoder.dashboard.modules.system.controller.auth.vo.session.SysUserSessionPageReqVO;
  7 +import cn.iocoder.dashboard.modules.system.convert.auth.SysUserSessionConvert;
7 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.auth.SysUserSessionDO; 8 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.auth.SysUserSessionDO;
  9 +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.dept.SysDeptDO;
  10 +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO;
8 import cn.iocoder.dashboard.modules.system.service.auth.SysUserSessionService; 11 import cn.iocoder.dashboard.modules.system.service.auth.SysUserSessionService;
  12 +import cn.iocoder.dashboard.modules.system.service.dept.SysDeptService;
  13 +import cn.iocoder.dashboard.modules.system.service.user.SysUserService;
  14 +import cn.iocoder.dashboard.util.collection.MapUtils;
9 import io.swagger.annotations.Api; 15 import io.swagger.annotations.Api;
10 import io.swagger.annotations.ApiImplicitParam; 16 import io.swagger.annotations.ApiImplicitParam;
11 import io.swagger.annotations.ApiOperation; 17 import io.swagger.annotations.ApiOperation;
@@ -14,26 +20,50 @@ import org.springframework.validation.annotation.Validated; @@ -14,26 +20,50 @@ import org.springframework.validation.annotation.Validated;
14 import org.springframework.web.bind.annotation.*; 20 import org.springframework.web.bind.annotation.*;
15 21
16 import javax.annotation.Resource; 22 import javax.annotation.Resource;
  23 +import java.util.ArrayList;
  24 +import java.util.List;
  25 +import java.util.Map;
17 26
18 import static cn.iocoder.dashboard.common.pojo.CommonResult.success; 27 import static cn.iocoder.dashboard.common.pojo.CommonResult.success;
  28 +import static cn.iocoder.dashboard.util.collection.CollectionUtils.convertList;
19 29
20 @Api("用户 Session API") 30 @Api("用户 Session API")
21 @RestController 31 @RestController
22 -@RequestMapping("/user-session") 32 +@RequestMapping("/system/user-session")
23 public class SysUserSessionController { 33 public class SysUserSessionController {
24 34
25 @Resource 35 @Resource
26 private SysUserSessionService userSessionService; 36 private SysUserSessionService userSessionService;
  37 + @Resource
  38 + private SysUserService userService;
  39 + @Resource
  40 + private SysDeptService deptService;
27 41
28 @ApiOperation("获得 Session 分页列表") 42 @ApiOperation("获得 Session 分页列表")
29 @PreAuthorize("@ss.hasPermission('system:user-session:page')") 43 @PreAuthorize("@ss.hasPermission('system:user-session:page')")
30 @GetMapping("/page") 44 @GetMapping("/page")
31 public CommonResult<PageResult<SysUserSessionPageItemRespVO>> getUserSessionPage(@Validated SysUserSessionPageReqVO reqVO) { 45 public CommonResult<PageResult<SysUserSessionPageItemRespVO>> getUserSessionPage(@Validated SysUserSessionPageReqVO reqVO) {
32 // 获得 Session 分页 46 // 获得 Session 分页
33 - PageResult<SysUserSessionDO> sessionPage = userSessionService.getUserSessionPage(reqVO); 47 + PageResult<SysUserSessionDO> pageResult = userSessionService.getUserSessionPage(reqVO);
34 48
35 - //  
36 - return null; 49 + // 获得拼接需要的数据
  50 + Map<Long, SysUserDO> userMap = userService.getUserMap(
  51 + convertList(pageResult.getList(), SysUserSessionDO::getUserId));
  52 + Map<Long, SysDeptDO> deptMap = deptService.getDeptMap(
  53 + convertList(userMap.values(), SysUserDO::getDeptId));
  54 + // 拼接结果返回
  55 + List<SysUserSessionPageItemRespVO> sessionList = new ArrayList<>(pageResult.getList().size());
  56 + pageResult.getList().forEach(session -> {
  57 + SysUserSessionPageItemRespVO respVO = SysUserSessionConvert.INSTANCE.convert(session);
  58 + sessionList.add(respVO);
  59 + // 设置用户账号
  60 + MapUtils.findAndThen(userMap, session.getUserId(), user -> {
  61 + respVO.setUsername(user.getUsername());
  62 + // 设置用户部门
  63 + MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> respVO.setDeptName(dept.getName()));
  64 + });
  65 + });
  66 + return success(new PageResult<>(sessionList, pageResult.getTotal()));
37 } 67 }
38 68
39 @ApiOperation("删除 Session") 69 @ApiOperation("删除 Session")
src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/session/SysUserSessionPageItemRespVO.java
@@ -8,6 +8,8 @@ import lombok.Data; @@ -8,6 +8,8 @@ import lombok.Data;
8 import lombok.EqualsAndHashCode; 8 import lombok.EqualsAndHashCode;
9 import lombok.NoArgsConstructor; 9 import lombok.NoArgsConstructor;
10 10
  11 +import java.util.Date;
  12 +
11 @ApiModel(value = "用户在线 Session Response VO", description = "相比用户基本信息来说,会多部门、用户账号等信息") 13 @ApiModel(value = "用户在线 Session Response VO", description = "相比用户基本信息来说,会多部门、用户账号等信息")
12 @Data 14 @Data
13 @NoArgsConstructor 15 @NoArgsConstructor
@@ -25,7 +27,7 @@ public class SysUserSessionPageItemRespVO extends PageParam { @@ -25,7 +27,7 @@ public class SysUserSessionPageItemRespVO extends PageParam {
25 private String userAgent; 27 private String userAgent;
26 28
27 @ApiModelProperty(value = "登陆时间", required = true) 29 @ApiModelProperty(value = "登陆时间", required = true)
28 - private String createTime; 30 + private Date createTime;
29 31
30 @ApiModelProperty(value = "用户账号", required = true, example = "yudao") 32 @ApiModelProperty(value = "用户账号", required = true, example = "yudao")
31 private String username; 33 private String username;
src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/session/SysUserSessionPageReqVO.java
@@ -14,7 +14,6 @@ import javax.validation.constraints.NotEmpty; @@ -14,7 +14,6 @@ import javax.validation.constraints.NotEmpty;
14 public class SysUserSessionPageReqVO extends PageParam { 14 public class SysUserSessionPageReqVO extends PageParam {
15 15
16 @ApiModelProperty(value = "用户 IP", example = "127.0.0.1", notes = "模糊匹配") 16 @ApiModelProperty(value = "用户 IP", example = "127.0.0.1", notes = "模糊匹配")
17 - @NotEmpty(message = "用户 IP 不能为空")  
18 private String userIp; 17 private String userIp;
19 18
20 @ApiModelProperty(value = "用户账号", example = "yudao", notes = "模糊匹配") 19 @ApiModelProperty(value = "用户账号", example = "yudao", notes = "模糊匹配")
src/main/java/cn/iocoder/dashboard/modules/system/convert/auth/SysUserSessionConvert.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.convert.auth;
  2 +
  3 +import cn.iocoder.dashboard.modules.system.controller.auth.vo.session.SysUserSessionPageItemRespVO;
  4 +import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserPageItemRespVO;
  5 +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.auth.SysUserSessionDO;
  6 +import org.mapstruct.Mapper;
  7 +import org.mapstruct.factory.Mappers;
  8 +
  9 +@Mapper
  10 +public interface SysUserSessionConvert {
  11 +
  12 + SysUserSessionConvert INSTANCE = Mappers.getMapper(SysUserSessionConvert.class);
  13 +
  14 + SysUserSessionPageItemRespVO convert(SysUserSessionDO session);
  15 +
  16 +}
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/auth/SysUserSessionMapper.java
1 package cn.iocoder.dashboard.modules.system.dal.mysql.dao.auth; 1 package cn.iocoder.dashboard.modules.system.dal.mysql.dao.auth;
2 2
  3 +import cn.iocoder.dashboard.common.pojo.PageResult;
  4 +import cn.iocoder.dashboard.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.dashboard.framework.mybatis.core.query.QueryWrapperX;
  6 +import cn.iocoder.dashboard.modules.system.controller.auth.vo.session.SysUserSessionPageReqVO;
3 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.auth.SysUserSessionDO; 7 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.auth.SysUserSessionDO;
4 -import com.baomidou.mybatisplus.core.mapper.BaseMapper;  
5 import org.apache.ibatis.annotations.Mapper; 8 import org.apache.ibatis.annotations.Mapper;
6 9
  10 +import java.util.Collection;
  11 +
7 @Mapper 12 @Mapper
8 -public interface SysUserSessionMapper extends BaseMapper<SysUserSessionDO> { 13 +public interface SysUserSessionMapper extends BaseMapperX<SysUserSessionDO> {
  14 +
  15 + default PageResult<SysUserSessionDO> selectPage(SysUserSessionPageReqVO reqVO, Collection<Long> userIds) {
  16 + return selectPage(reqVO, new QueryWrapperX<SysUserSessionDO>()
  17 + .inIfPresent("user_id", userIds)
  18 + .likeIfPresent("user_ip", reqVO.getUserIp()));
  19 + }
  20 +
9 } 21 }
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/user/SysUserMapper.java
@@ -48,5 +48,9 @@ public interface SysUserMapper extends BaseMapperX&lt;SysUserDO&gt; { @@ -48,5 +48,9 @@ public interface SysUserMapper extends BaseMapperX&lt;SysUserDO&gt; {
48 return selectList(new QueryWrapperX<SysUserDO>().like("nickname", nickname)); 48 return selectList(new QueryWrapperX<SysUserDO>().like("nickname", nickname));
49 } 49 }
50 50
  51 + default List<SysUserDO> selectListByUsername(String username) {
  52 + return selectList(new QueryWrapperX<SysUserDO>().like("username", username));
  53 + }
  54 +
51 } 55 }
52 56
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/auth/SysUserSessionDO.java
@@ -3,6 +3,7 @@ package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.auth; @@ -3,6 +3,7 @@ package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.auth;
3 import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO; 3 import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
4 import cn.iocoder.dashboard.framework.security.core.LoginUser; 4 import cn.iocoder.dashboard.framework.security.core.LoginUser;
5 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO; 5 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO;
  6 +import com.baomidou.mybatisplus.annotation.IdType;
6 import com.baomidou.mybatisplus.annotation.TableId; 7 import com.baomidou.mybatisplus.annotation.TableId;
7 import com.baomidou.mybatisplus.annotation.TableName; 8 import com.baomidou.mybatisplus.annotation.TableName;
8 import lombok.Builder; 9 import lombok.Builder;
@@ -27,7 +28,7 @@ public class SysUserSessionDO extends BaseDO { @@ -27,7 +28,7 @@ public class SysUserSessionDO extends BaseDO {
27 /** 28 /**
28 * 会话编号, 即 sessionId 29 * 会话编号, 即 sessionId
29 */ 30 */
30 - @TableId 31 + @TableId(type = IdType.INPUT)
31 private String id; 32 private String id;
32 /** 33 /**
33 * 用户编号 34 * 用户编号
src/main/java/cn/iocoder/dashboard/modules/system/dal/redis/dao/auth/SysLoginUserRedisDAO.java
@@ -30,8 +30,8 @@ public class SysLoginUserRedisDAO { @@ -30,8 +30,8 @@ public class SysLoginUserRedisDAO {
30 stringRedisTemplate.opsForValue().set(redisKey, JsonUtils.toJsonString(loginUser), LOGIN_USER.getTimeout()); 30 stringRedisTemplate.opsForValue().set(redisKey, JsonUtils.toJsonString(loginUser), LOGIN_USER.getTimeout());
31 } 31 }
32 32
33 - public void delete(String accessToken) {  
34 - String redisKey = formatKey(accessToken); 33 + public void delete(String sessionId) {
  34 + String redisKey = formatKey(sessionId);
35 stringRedisTemplate.delete(redisKey); 35 stringRedisTemplate.delete(redisKey);
36 } 36 }
37 37
src/main/java/cn/iocoder/dashboard/modules/system/job/package-info.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.job;
src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysUserSessionServiceImpl.java
1 package cn.iocoder.dashboard.modules.system.service.auth.impl; 1 package cn.iocoder.dashboard.modules.system.service.auth.impl;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.hutool.core.util.IdUtil; 4 import cn.hutool.core.util.IdUtil;
  5 +import cn.hutool.core.util.StrUtil;
4 import cn.iocoder.dashboard.common.pojo.PageResult; 6 import cn.iocoder.dashboard.common.pojo.PageResult;
5 import cn.iocoder.dashboard.framework.security.config.SecurityProperties; 7 import cn.iocoder.dashboard.framework.security.config.SecurityProperties;
6 import cn.iocoder.dashboard.framework.security.core.LoginUser; 8 import cn.iocoder.dashboard.framework.security.core.LoginUser;
7 import cn.iocoder.dashboard.modules.system.controller.auth.vo.session.SysUserSessionPageReqVO; 9 import cn.iocoder.dashboard.modules.system.controller.auth.vo.session.SysUserSessionPageReqVO;
8 import cn.iocoder.dashboard.modules.system.dal.mysql.dao.auth.SysUserSessionMapper; 10 import cn.iocoder.dashboard.modules.system.dal.mysql.dao.auth.SysUserSessionMapper;
9 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.auth.SysUserSessionDO; 11 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.auth.SysUserSessionDO;
  12 +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO;
10 import cn.iocoder.dashboard.modules.system.dal.redis.dao.auth.SysLoginUserRedisDAO; 13 import cn.iocoder.dashboard.modules.system.dal.redis.dao.auth.SysLoginUserRedisDAO;
11 import cn.iocoder.dashboard.modules.system.service.auth.SysUserSessionService; 14 import cn.iocoder.dashboard.modules.system.service.auth.SysUserSessionService;
  15 +import cn.iocoder.dashboard.modules.system.service.user.SysUserService;
12 import org.springframework.stereotype.Service; 16 import org.springframework.stereotype.Service;
13 17
14 import javax.annotation.Resource; 18 import javax.annotation.Resource;
  19 +import java.util.Collection;
15 import java.util.Date; 20 import java.util.Date;
16 21
  22 +import static cn.iocoder.dashboard.util.collection.CollectionUtils.convertSet;
  23 +
17 /** 24 /**
18 * 在线用户 Session Service 实现类 25 * 在线用户 Session Service 实现类
19 * 26 *
@@ -27,10 +34,12 @@ public class SysUserSessionServiceImpl implements SysUserSessionService { @@ -27,10 +34,12 @@ public class SysUserSessionServiceImpl implements SysUserSessionService {
27 34
28 @Resource 35 @Resource
29 private SysLoginUserRedisDAO loginUserRedisDAO; 36 private SysLoginUserRedisDAO loginUserRedisDAO;
30 -  
31 @Resource 37 @Resource
32 private SysUserSessionMapper userSessionMapper; 38 private SysUserSessionMapper userSessionMapper;
33 39
  40 + @Resource
  41 + private SysUserService userService;
  42 +
34 @Override 43 @Override
35 public String createUserSession(LoginUser loginUser, String userIp, String userAgent) { 44 public String createUserSession(LoginUser loginUser, String userIp, String userAgent) {
36 // 生成 Session 编号 45 // 生成 Session 编号
@@ -39,8 +48,8 @@ public class SysUserSessionServiceImpl implements SysUserSessionService { @@ -39,8 +48,8 @@ public class SysUserSessionServiceImpl implements SysUserSessionService {
39 loginUser.setUpdateTime(new Date()); 48 loginUser.setUpdateTime(new Date());
40 loginUserRedisDAO.set(sessionId, loginUser); 49 loginUserRedisDAO.set(sessionId, loginUser);
41 // 写入 DB 中 50 // 写入 DB 中
42 - SysUserSessionDO userSession = SysUserSessionDO.builder().userId(loginUser.getId())  
43 - .userIp(userIp).userAgent(userAgent).build(); 51 + SysUserSessionDO userSession = SysUserSessionDO.builder().id(sessionId)
  52 + .userId(loginUser.getId()).userIp(userIp).userAgent(userAgent).build();
44 userSessionMapper.insert(userSession); 53 userSessionMapper.insert(userSession);
45 // 返回 Session 编号 54 // 返回 Session 编号
46 return sessionId; 55 return sessionId;
@@ -59,7 +68,10 @@ public class SysUserSessionServiceImpl implements SysUserSessionService { @@ -59,7 +68,10 @@ public class SysUserSessionServiceImpl implements SysUserSessionService {
59 68
60 @Override 69 @Override
61 public void deleteUserSession(String sessionId) { 70 public void deleteUserSession(String sessionId) {
62 - 71 + // 删除 Redis 缓存
  72 + loginUserRedisDAO.delete(sessionId);
  73 + // 删除 DB 记录
  74 + userSessionMapper.deleteById(sessionId);
63 } 75 }
64 76
65 @Override 77 @Override
@@ -74,7 +86,15 @@ public class SysUserSessionServiceImpl implements SysUserSessionService { @@ -74,7 +86,15 @@ public class SysUserSessionServiceImpl implements SysUserSessionService {
74 86
75 @Override 87 @Override
76 public PageResult<SysUserSessionDO> getUserSessionPage(SysUserSessionPageReqVO reqVO) { 88 public PageResult<SysUserSessionDO> getUserSessionPage(SysUserSessionPageReqVO reqVO) {
77 - return null; 89 + // 处理基于用户昵称的查询
  90 + Collection<Long> userIds = null;
  91 + if (StrUtil.isNotEmpty(reqVO.getUsername())) {
  92 + userIds = convertSet(userService.listUsersByUsername(reqVO.getUsername()), SysUserDO::getId);
  93 + if (CollUtil.isEmpty(userIds)) {
  94 + return PageResult.empty();
  95 + }
  96 + }
  97 + return userSessionMapper.selectPage(reqVO, userIds);
78 } 98 }
79 99
80 /** 100 /**
src/main/java/cn/iocoder/dashboard/modules/system/service/user/SysUserService.java
@@ -80,6 +80,14 @@ public interface SysUserService { @@ -80,6 +80,14 @@ public interface SysUserService {
80 List<SysUserDO> listUsersByNickname(String nickname); 80 List<SysUserDO> listUsersByNickname(String nickname);
81 81
82 /** 82 /**
  83 + * 获得用户列表,基于用户账号模糊匹配
  84 + *
  85 + * @param username 用户账号
  86 + * @return 用户列表
  87 + */
  88 + List<SysUserDO> listUsersByUsername(String username);
  89 +
  90 + /**
83 * 创建用户 91 * 创建用户
84 * 92 *
85 * @param reqVO 用户信息 93 * @param reqVO 用户信息
src/main/java/cn/iocoder/dashboard/modules/system/service/user/SysUserServiceImpl.java
@@ -92,6 +92,11 @@ public class SysUserServiceImpl implements SysUserService { @@ -92,6 +92,11 @@ public class SysUserServiceImpl implements SysUserService {
92 return userMapper.selectListByNickname(nickname); 92 return userMapper.selectListByNickname(nickname);
93 } 93 }
94 94
  95 + @Override
  96 + public List<SysUserDO> listUsersByUsername(String username) {
  97 + return userMapper.selectListByUsername(username);
  98 + }
  99 +
95 /** 100 /**
96 * 获得部门条件:查询指定部门的子部门编号们,包括自身 101 * 获得部门条件:查询指定部门的子部门编号们,包括自身
97 * 102 *
src/main/java/cn/iocoder/dashboard/util/collection/CollectionUtils.java
@@ -26,35 +26,35 @@ public class CollectionUtils { @@ -26,35 +26,35 @@ public class CollectionUtils {
26 return from.stream().filter(predicate).collect(Collectors.toList()); 26 return from.stream().filter(predicate).collect(Collectors.toList());
27 } 27 }
28 28
29 - public static <T, U> List<U> convertList(List<T> from, Function<T, U> func) { 29 + public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func) {
30 if (CollUtil.isEmpty(from)) { 30 if (CollUtil.isEmpty(from)) {
31 return new ArrayList<>(); 31 return new ArrayList<>();
32 } 32 }
33 return from.stream().map(func).collect(Collectors.toList()); 33 return from.stream().map(func).collect(Collectors.toList());
34 } 34 }
35 35
36 - public static <T, U> Set<U> convertSet(List<T> from, Function<T, U> func) { 36 + public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) {
37 if (CollUtil.isEmpty(from)) { 37 if (CollUtil.isEmpty(from)) {
38 return new HashSet<>(); 38 return new HashSet<>();
39 } 39 }
40 return from.stream().map(func).collect(Collectors.toSet()); 40 return from.stream().map(func).collect(Collectors.toSet());
41 } 41 }
42 42
43 - public static <T, K> Map<K, T> convertMap(List<T> from, Function<T, K> keyFunc) { 43 + public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc) {
44 if (CollUtil.isEmpty(from)) { 44 if (CollUtil.isEmpty(from)) {
45 return new HashMap<>(); 45 return new HashMap<>();
46 } 46 }
47 return from.stream().collect(Collectors.toMap(keyFunc, item -> item)); 47 return from.stream().collect(Collectors.toMap(keyFunc, item -> item));
48 } 48 }
49 49
50 - public static <T, K, V> Map<K, V> convertMap(List<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) { 50 + public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
51 if (CollUtil.isEmpty(from)) { 51 if (CollUtil.isEmpty(from)) {
52 return new HashMap<>(); 52 return new HashMap<>();
53 } 53 }
54 return from.stream().collect(Collectors.toMap(keyFunc, valueFunc)); 54 return from.stream().collect(Collectors.toMap(keyFunc, valueFunc));
55 } 55 }
56 56
57 - public static <T, K> Map<K, List<T>> convertMultiMap(List<T> from, Function<T, K> keyFunc) { 57 + public static <T, K> Map<K, List<T>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc) {
58 if (CollUtil.isEmpty(from)) { 58 if (CollUtil.isEmpty(from)) {
59 return new HashMap<>(); 59 return new HashMap<>();
60 } 60 }
@@ -62,7 +62,7 @@ public class CollectionUtils { @@ -62,7 +62,7 @@ public class CollectionUtils {
62 Collectors.mapping(t -> t, Collectors.toList()))); 62 Collectors.mapping(t -> t, Collectors.toList())));
63 } 63 }
64 64
65 - public static <T, K, V> Map<K, List<V>> convertMultiMap(List<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) { 65 + public static <T, K, V> Map<K, List<V>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
66 if (CollUtil.isEmpty(from)) { 66 if (CollUtil.isEmpty(from)) {
67 return new HashMap<>(); 67 return new HashMap<>();
68 } 68 }
@@ -71,7 +71,7 @@ public class CollectionUtils { @@ -71,7 +71,7 @@ public class CollectionUtils {
71 } 71 }
72 72
73 // 暂时没想好名字,先以 2 结尾噶 73 // 暂时没想好名字,先以 2 结尾噶
74 - public static <T, K, V> Map<K, Set<V>> convertMultiMap2(List<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) { 74 + public static <T, K, V> Map<K, Set<V>> convertMultiMap2(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
75 if (CollUtil.isEmpty(from)) { 75 if (CollUtil.isEmpty(from)) {
76 return new HashMap<>(); 76 return new HashMap<>();
77 } 77 }