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 8  
9 9 /**
10 10 * swagger 接口
11   - *
  11 + *
12 12 * @author ruoyi
13 13 */
14 14 @Controller
15 15 @RequestMapping("/tool/swagger")
16   -public class SwaggerController extends BaseController
17   -{
  16 +public class SwaggerController extends BaseController {
18 17 @PreAuthorize("@ss.hasPermi('tool:swagger:view')")
19 18 @GetMapping()
20   - public String index()
21   - {
  19 + public String index() {
22 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 57 multi-statement-allow: true
58 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 93 </configuration>
94 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 4 import cn.iocoder.dashboard.common.pojo.PageResult;
5 5 import cn.iocoder.dashboard.modules.system.controller.auth.vo.session.SysUserSessionPageItemRespVO;
6 6 import cn.iocoder.dashboard.modules.system.controller.auth.vo.session.SysUserSessionPageReqVO;
  7 +import cn.iocoder.dashboard.modules.system.convert.auth.SysUserSessionConvert;
7 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 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 15 import io.swagger.annotations.Api;
10 16 import io.swagger.annotations.ApiImplicitParam;
11 17 import io.swagger.annotations.ApiOperation;
... ... @@ -14,26 +20,50 @@ import org.springframework.validation.annotation.Validated;
14 20 import org.springframework.web.bind.annotation.*;
15 21  
16 22 import javax.annotation.Resource;
  23 +import java.util.ArrayList;
  24 +import java.util.List;
  25 +import java.util.Map;
17 26  
18 27 import static cn.iocoder.dashboard.common.pojo.CommonResult.success;
  28 +import static cn.iocoder.dashboard.util.collection.CollectionUtils.convertList;
19 29  
20 30 @Api("用户 Session API")
21 31 @RestController
22   -@RequestMapping("/user-session")
  32 +@RequestMapping("/system/user-session")
23 33 public class SysUserSessionController {
24 34  
25 35 @Resource
26 36 private SysUserSessionService userSessionService;
  37 + @Resource
  38 + private SysUserService userService;
  39 + @Resource
  40 + private SysDeptService deptService;
27 41  
28 42 @ApiOperation("获得 Session 分页列表")
29 43 @PreAuthorize("@ss.hasPermission('system:user-session:page')")
30 44 @GetMapping("/page")
31 45 public CommonResult<PageResult<SysUserSessionPageItemRespVO>> getUserSessionPage(@Validated SysUserSessionPageReqVO reqVO) {
32 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 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 8 import lombok.EqualsAndHashCode;
9 9 import lombok.NoArgsConstructor;
10 10  
  11 +import java.util.Date;
  12 +
11 13 @ApiModel(value = "用户在线 Session Response VO", description = "相比用户基本信息来说,会多部门、用户账号等信息")
12 14 @Data
13 15 @NoArgsConstructor
... ... @@ -25,7 +27,7 @@ public class SysUserSessionPageItemRespVO extends PageParam {
25 27 private String userAgent;
26 28  
27 29 @ApiModelProperty(value = "登陆时间", required = true)
28   - private String createTime;
  30 + private Date createTime;
29 31  
30 32 @ApiModelProperty(value = "用户账号", required = true, example = "yudao")
31 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 14 public class SysUserSessionPageReqVO extends PageParam {
15 15  
16 16 @ApiModelProperty(value = "用户 IP", example = "127.0.0.1", notes = "模糊匹配")
17   - @NotEmpty(message = "用户 IP 不能为空")
18 17 private String userIp;
19 18  
20 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 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 7 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.auth.SysUserSessionDO;
4   -import com.baomidou.mybatisplus.core.mapper.BaseMapper;
5 8 import org.apache.ibatis.annotations.Mapper;
6 9  
  10 +import java.util.Collection;
  11 +
7 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 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 3 import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
4 4 import cn.iocoder.dashboard.framework.security.core.LoginUser;
5 5 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO;
  6 +import com.baomidou.mybatisplus.annotation.IdType;
6 7 import com.baomidou.mybatisplus.annotation.TableId;
7 8 import com.baomidou.mybatisplus.annotation.TableName;
8 9 import lombok.Builder;
... ... @@ -27,7 +28,7 @@ public class SysUserSessionDO extends BaseDO {
27 28 /**
28 29 * 会话编号, 即 sessionId
29 30 */
30   - @TableId
  31 + @TableId(type = IdType.INPUT)
31 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 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 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 1 package cn.iocoder.dashboard.modules.system.service.auth.impl;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.hutool.core.util.IdUtil;
  5 +import cn.hutool.core.util.StrUtil;
4 6 import cn.iocoder.dashboard.common.pojo.PageResult;
5 7 import cn.iocoder.dashboard.framework.security.config.SecurityProperties;
6 8 import cn.iocoder.dashboard.framework.security.core.LoginUser;
7 9 import cn.iocoder.dashboard.modules.system.controller.auth.vo.session.SysUserSessionPageReqVO;
8 10 import cn.iocoder.dashboard.modules.system.dal.mysql.dao.auth.SysUserSessionMapper;
9 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 13 import cn.iocoder.dashboard.modules.system.dal.redis.dao.auth.SysLoginUserRedisDAO;
11 14 import cn.iocoder.dashboard.modules.system.service.auth.SysUserSessionService;
  15 +import cn.iocoder.dashboard.modules.system.service.user.SysUserService;
12 16 import org.springframework.stereotype.Service;
13 17  
14 18 import javax.annotation.Resource;
  19 +import java.util.Collection;
15 20 import java.util.Date;
16 21  
  22 +import static cn.iocoder.dashboard.util.collection.CollectionUtils.convertSet;
  23 +
17 24 /**
18 25 * 在线用户 Session Service 实现类
19 26 *
... ... @@ -27,10 +34,12 @@ public class SysUserSessionServiceImpl implements SysUserSessionService {
27 34  
28 35 @Resource
29 36 private SysLoginUserRedisDAO loginUserRedisDAO;
30   -
31 37 @Resource
32 38 private SysUserSessionMapper userSessionMapper;
33 39  
  40 + @Resource
  41 + private SysUserService userService;
  42 +
34 43 @Override
35 44 public String createUserSession(LoginUser loginUser, String userIp, String userAgent) {
36 45 // 生成 Session 编号
... ... @@ -39,8 +48,8 @@ public class SysUserSessionServiceImpl implements SysUserSessionService {
39 48 loginUser.setUpdateTime(new Date());
40 49 loginUserRedisDAO.set(sessionId, loginUser);
41 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 53 userSessionMapper.insert(userSession);
45 54 // 返回 Session 编号
46 55 return sessionId;
... ... @@ -59,7 +68,10 @@ public class SysUserSessionServiceImpl implements SysUserSessionService {
59 68  
60 69 @Override
61 70 public void deleteUserSession(String sessionId) {
62   -
  71 + // 删除 Redis 缓存
  72 + loginUserRedisDAO.delete(sessionId);
  73 + // 删除 DB 记录
  74 + userSessionMapper.deleteById(sessionId);
63 75 }
64 76  
65 77 @Override
... ... @@ -74,7 +86,15 @@ public class SysUserSessionServiceImpl implements SysUserSessionService {
74 86  
75 87 @Override
76 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 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 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 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 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 30 if (CollUtil.isEmpty(from)) {
31 31 return new ArrayList<>();
32 32 }
33 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 37 if (CollUtil.isEmpty(from)) {
38 38 return new HashSet<>();
39 39 }
40 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 44 if (CollUtil.isEmpty(from)) {
45 45 return new HashMap<>();
46 46 }
47 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 51 if (CollUtil.isEmpty(from)) {
52 52 return new HashMap<>();
53 53 }
54 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 58 if (CollUtil.isEmpty(from)) {
59 59 return new HashMap<>();
60 60 }
... ... @@ -62,7 +62,7 @@ public class CollectionUtils {
62 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 66 if (CollUtil.isEmpty(from)) {
67 67 return new HashMap<>();
68 68 }
... ... @@ -71,7 +71,7 @@ public class CollectionUtils {
71 71 }
72 72  
73 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 75 if (CollUtil.isEmpty(from)) {
76 76 return new HashMap<>();
77 77 }
... ...