完成主要在线 session 的功能
parent
ab94fe2d4b
commit
753c7678ee
@ -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,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,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,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,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,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>
|
|
||||||
|
|
||||||
@ -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()));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1 @@
|
|||||||
|
package cn.iocoder.dashboard.modules.system.job;
|
||||||
Loading…
Reference in New Issue