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