Commit 753c7678ee5a3da612e0b570c21aecd3447130c3
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<SysUserDO> { |
| 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 | } | ... | ... |