diff --git a/README.md b/README.md index 48490c8f75..0c8e56e8ab 100644 --- a/README.md +++ b/README.md @@ -35,12 +35,12 @@ | | 部门管理 | 配置系统组织机构(公司、部门、小组),树结构展现支持数据权限 | | | 岗位管理 | 配置系统用户所属担任职务 | | | 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 | -| | 通知公告 | 系统通知公告信息发布维护 | +| 🚀 | 短信管理 | 短信渠道、短息模板、短信日志,对接阿里云、云片等主流短信平台 | | 🚀 | 操作日志 | 系统正常操作日志记录和查询,集成 Swagger 生成日志内容 | | | 登录日志 | 系统登录日志记录查询包含登录异常 | +| | 通知公告 | 系统通知公告信息发布维护 | 计划新增功能: -* 短信 * 邮件 * 钉钉、飞书等通知 @@ -87,14 +87,15 @@ ## 技术栈 -**后端** +### 后端 | 框架 | 说明 | 版本 | 学习指南 | | --- | --- | --- | --- | | [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 2.4.2 | [文档](https://github.com/YunaiV/SpringBoot-Labs) | | [MySQL](https://www.mysql.com/cn/) | 数据库服务器 | 5.7 | | | [Druid](https://github.com/alibaba/druid) | JDBC 连接池、监控组件 | 1.2.4 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) | -| [MyBatis-Plus](https://mp.baomidou.com/) | MyBatis 增强工具包 | 3.4.1 | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao) | +| [MyBatis Plus](https://mp.baomidou.com/) | MyBatis 增强工具包 | 3.4.1 | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao) | +| [Dynamic Datasource](https://dynamic-datasource.com/) | 动态数据源 | 3.3.2 | [文档](hhttp://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) | | [Redis](https://redis.io/) | key-value 数据库 | 5.0 | | | [Redisson](https://github.com/redisson/redisson) | Redis 客户端 | 3.1.46 | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?yudao) | | [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC 框架 | 5.4.2 | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao) | @@ -111,7 +112,7 @@ | [JUnit](https://junit.org/junit5/) | Java 单元测试框架 | 5.7.0 | - | | [Mockito](https://github.com/mockito/mockito) | Java Mock 框架 | 3.6.28 | - | -**前端** +### 前端 | 框架 | 说明 | 版本 | | --- | --- | --- | diff --git a/pom.xml b/pom.xml index 532f7e2003..8bc8c53d59 100644 --- a/pom.xml +++ b/pom.xml @@ -30,6 +30,7 @@ 5.1.46 1.2.4 3.4.2 + 3.3.2 3.15.1 1.7.0 @@ -139,6 +140,11 @@ mybatis-plus-boot-starter ${mybatis-plus.version} + + com.baomidou + dynamic-datasource-spring-boot-starter + ${dynamic-datasource.version} + org.redisson diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java deleted file mode 100644 index 26d664f6e8..0000000000 --- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.ruoyi.common.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 数据权限过滤注解 - * - * @author ruoyi - */ -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface DataScope { - /** - * 部门表的别名 - */ - public String deptAlias() default ""; - - /** - * 用户表的别名 - */ - public String userAlias() default ""; -} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScopeAspect.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScopeAspect.java deleted file mode 100644 index cc01b9422a..0000000000 --- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScopeAspect.java +++ /dev/null @@ -1,145 +0,0 @@ -package com.ruoyi.framework.aspectj; - -import java.lang.reflect.Method; - -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.Signature; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Before; -import org.aspectj.lang.annotation.Pointcut; -import org.aspectj.lang.reflect.MethodSignature; -import org.springframework.stereotype.Component; -import com.ruoyi.common.annotation.DataScope; -import com.ruoyi.common.core.domain.BaseEntity; -import com.ruoyi.common.core.domain.entity.SysRole; -import com.ruoyi.common.core.domain.entity.SysUser; -import com.ruoyi.common.core.domain.model.LoginUser; -import com.ruoyi.common.utils.ServletUtils; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.spring.SpringUtils; -import com.ruoyi.framework.web.service.TokenService; - -/** - * 数据过滤处理 - * - * @author ruoyi - */ -@Aspect -@Component -public class DataScopeAspect { - /** - * 全部数据权限 - */ - public static final String DATA_SCOPE_ALL = "1"; - - /** - * 自定数据权限 - */ - public static final String DATA_SCOPE_CUSTOM = "2"; - - /** - * 部门数据权限 - */ - public static final String DATA_SCOPE_DEPT = "3"; - - /** - * 部门及以下数据权限 - */ - public static final String DATA_SCOPE_DEPT_AND_CHILD = "4"; - - /** - * 仅本人数据权限 - */ - public static final String DATA_SCOPE_SELF = "5"; - - /** - * 数据权限过滤关键字 - */ - public static final String DATA_SCOPE = "dataScope"; - - // 配置织入点 - @Pointcut("@annotation(com.ruoyi.common.annotation.DataScope)") - public void dataScopePointCut() { - } - - @Before("dataScopePointCut()") - public void doBefore(JoinPoint point) throws Throwable { - handleDataScope(point); - } - - protected void handleDataScope(final JoinPoint joinPoint) { - // 获得注解 - DataScope controllerDataScope = getAnnotationLog(joinPoint); - if (controllerDataScope == null) { - return; - } - // 获取当前的用户 - LoginUser loginUser = SpringUtils.getBean(TokenService.class).getLoginUser(ServletUtils.getRequest()); - if (StringUtils.isNotNull(loginUser)) { - SysUser currentUser = loginUser.getUser(); - // 如果是超级管理员,则不过滤数据 - if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) { - dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), - controllerDataScope.userAlias()); - } - } - } - - /** - * 数据范围过滤 - * - * @param joinPoint 切点 - * @param user 用户 - * @param userAlias 别名 - */ - public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias) { - StringBuilder sqlString = new StringBuilder(); - - for (SysRole role : user.getRoles()) { - String dataScope = role.getDataScope(); - if (DATA_SCOPE_ALL.equals(dataScope)) { - sqlString = new StringBuilder(); - break; - } else if (DATA_SCOPE_CUSTOM.equals(dataScope)) { - sqlString.append(StringUtils.format( - " OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, - role.getRoleId())); - } else if (DATA_SCOPE_DEPT.equals(dataScope)) { - sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId())); - } else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) { - sqlString.append(StringUtils.format( - " OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", - deptAlias, user.getDeptId(), user.getDeptId())); - } else if (DATA_SCOPE_SELF.equals(dataScope)) { - if (StringUtils.isNotBlank(userAlias)) { - sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId())); - } else { - // 数据权限为仅本人且没有userAlias别名不查询任何数据 - sqlString.append(" OR 1=0 "); - } - } - } - - if (StringUtils.isNotBlank(sqlString.toString())) { - Object params = joinPoint.getArgs()[0]; - if (StringUtils.isNotNull(params) && params instanceof BaseEntity) { - BaseEntity baseEntity = (BaseEntity) params; - baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")"); - } - } - } - - /** - * 是否存在注解,如果存在就获取 - */ - private DataScope getAnnotationLog(JoinPoint joinPoint) { - Signature signature = joinPoint.getSignature(); - MethodSignature methodSignature = (MethodSignature) signature; - Method method = methodSignature.getMethod(); - - if (method != null) { - return method.getAnnotation(DataScope.class); - } - return null; - } -} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java deleted file mode 100644 index 11b0d579c8..0000000000 --- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.ruoyi.common.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import com.ruoyi.common.enums.DataSourceType; - -/** - * 自定义多数据源切换注解 - *

- * 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准 - * - * @author ruoyi - */ -@Target({ElementType.METHOD, ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@Inherited -public @interface DataSource { - /** - * 切换数据源名称 - */ - public DataSourceType value() default DataSourceType.MASTER; -} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSourceAspect.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSourceAspect.java deleted file mode 100644 index 020493d2db..0000000000 --- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSourceAspect.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.ruoyi.framework.aspectj; - -import java.util.Objects; - -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Pointcut; -import org.aspectj.lang.reflect.MethodSignature; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.annotation.AnnotationUtils; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Component; -import com.ruoyi.common.annotation.DataSource; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.framework.datasource.DynamicDataSourceContextHolder; - -/** - * 多数据源处理 - * - * @author ruoyi - */ -@Aspect -@Order(1) -@Component -public class DataSourceAspect { - protected Logger logger = LoggerFactory.getLogger(getClass()); - - @Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)" - + "|| @within(com.ruoyi.common.annotation.DataSource)") - public void dsPointCut() { - - } - - @Around("dsPointCut()") - public Object around(ProceedingJoinPoint point) throws Throwable { - DataSource dataSource = getDataSource(point); - - if (StringUtils.isNotNull(dataSource)) { - DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name()); - } - - try { - return point.proceed(); - } finally { - // 销毁数据源 在执行方法之后 - DynamicDataSourceContextHolder.clearDataSourceType(); - } - } - - /** - * 获取需要切换的数据源 - */ - public DataSource getDataSource(ProceedingJoinPoint point) { - MethodSignature signature = (MethodSignature) point.getSignature(); - DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class); - if (Objects.nonNull(dataSource)) { - return dataSource; - } - - return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class); - } -} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSourceType.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSourceType.java deleted file mode 100644 index 0d945be549..0000000000 --- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSourceType.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.ruoyi.common.enums; - -/** - * 数据源 - * - * @author ruoyi - */ -public enum DataSourceType -{ - /** - * 主库 - */ - MASTER, - - /** - * 从库 - */ - SLAVE -} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DynamicDataSource.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DynamicDataSource.java deleted file mode 100644 index f60b35cb65..0000000000 --- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DynamicDataSource.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.ruoyi.framework.datasource; - -import java.util.Map; -import javax.sql.DataSource; - -import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; - -/** - * 动态数据源 - * - * @author ruoyi - */ -public class DynamicDataSource extends AbstractRoutingDataSource { - public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources) { - super.setDefaultTargetDataSource(defaultTargetDataSource); - super.setTargetDataSources(targetDataSources); - super.afterPropertiesSet(); - } - - @Override - protected Object determineCurrentLookupKey() { - return DynamicDataSourceContextHolder.getDataSourceType(); - } -} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DynamicDataSourceContextHolder.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DynamicDataSourceContextHolder.java deleted file mode 100644 index 37a5f8e580..0000000000 --- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DynamicDataSourceContextHolder.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.ruoyi.framework.datasource; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * 数据源切换处理 - * - * @author ruoyi - */ -public class DynamicDataSourceContextHolder { - public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class); - - /** - * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本, - * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。 - */ - private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>(); - - /** - * 设置数据源的变量 - */ - public static void setDataSourceType(String dsType) { - log.info("切换到{}数据源", dsType); - CONTEXT_HOLDER.set(dsType); - } - - /** - * 获得数据源的变量 - */ - public static String getDataSourceType() { - return CONTEXT_HOLDER.get(); - } - - /** - * 清空数据源变量 - */ - public static void clearDataSourceType() { - CONTEXT_HOLDER.remove(); - } -} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/config/DruidConfig.java b/ruoyi-common/src/main/java/com/ruoyi/common/config/DruidConfig.java deleted file mode 100644 index 7e9f4b03fd..0000000000 --- a/ruoyi-common/src/main/java/com/ruoyi/common/config/DruidConfig.java +++ /dev/null @@ -1,116 +0,0 @@ -package com.ruoyi.framework.config; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.sql.DataSource; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; -import com.alibaba.druid.pool.DruidDataSource; -import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; -import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; -import com.alibaba.druid.util.Utils; -import com.ruoyi.common.enums.DataSourceType; -import com.ruoyi.common.utils.spring.SpringUtils; -import com.ruoyi.framework.config.properties.DruidProperties; -import com.ruoyi.framework.datasource.DynamicDataSource; - -/** - * druid 配置多数据源 - * - * @author ruoyi - */ -@Configuration -public class DruidConfig { - @Bean - @ConfigurationProperties("spring.datasource.druid.master") - public DataSource masterDataSource(DruidProperties druidProperties) { - DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); - return druidProperties.dataSource(dataSource); - } - - @Bean - @ConfigurationProperties("spring.datasource.druid.slave") - @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true") - public DataSource slaveDataSource(DruidProperties druidProperties) { - DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); - return druidProperties.dataSource(dataSource); - } - - @Bean(name = "dynamicDataSource") - @Primary - public DynamicDataSource dataSource(DataSource masterDataSource) { - Map targetDataSources = new HashMap<>(); - targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource); - setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource"); - return new DynamicDataSource(masterDataSource, targetDataSources); - } - - /** - * 设置数据源 - * - * @param targetDataSources 备选数据源集合 - * @param sourceName 数据源名称 - * @param beanName bean名称 - */ - public void setDataSource(Map targetDataSources, String sourceName, String beanName) { - try { - DataSource dataSource = SpringUtils.getBean(beanName); - targetDataSources.put(sourceName, dataSource); - } catch (Exception e) { - } - } - - /** - * 去除监控页面底部的广告 - */ - @SuppressWarnings({"rawtypes", "unchecked"}) - @Bean - @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true") - public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties) { - // 获取web监控页面的参数 - DruidStatProperties.StatViewServlet config = properties.getStatViewServlet(); - // 提取common.js的配置路径 - String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*"; - String commonJsPattern = pattern.replaceAll("\\*", "js/common.js"); - final String filePath = "support/http/resources/js/common.js"; - // 创建filter进行过滤 - Filter filter = new Filter() { - @Override - public void init(javax.servlet.FilterConfig filterConfig) throws ServletException { - } - - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) - throws IOException, ServletException { - chain.doFilter(request, response); - // 重置缓冲区,响应头不会被重置 - response.resetBuffer(); - // 获取common.js - String text = Utils.readFromResource(filePath); - // 正则替换banner, 除去底部的广告信息 - text = text.replaceAll("
", ""); - text = text.replaceAll("powered.*?shrek.wang", ""); - response.getWriter().write(text); - } - - @Override - public void destroy() { - } - }; - FilterRegistrationBean registrationBean = new FilterRegistrationBean(); - registrationBean.setFilter(filter); - registrationBean.addUrlPatterns(commonJsPattern); - return registrationBean; - } -} diff --git a/ruoyi-common/src/main/resources/application-druid.yml b/ruoyi-common/src/main/resources/application-druid.yml deleted file mode 100644 index 6057ce24f5..0000000000 --- a/ruoyi-common/src/main/resources/application-druid.yml +++ /dev/null @@ -1,37 +0,0 @@ -# 数据源配置 -spring: - datasource: - type: com.alibaba.druid.pool.DruidDataSource - driverClassName: com.mysql.cj.jdbc.Driver - druid: - # 主库数据源 - master: - url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 - username: root - password: password - # 从库数据源 - slave: - # 从数据源开关/默认关闭 - enabled: false - url: - username: - password: - # 初始连接数 - initialSize: 5 - # 最小连接池数量 - minIdle: 10 - # 最大连接池数量 - maxActive: 20 - # 配置获取连接等待超时的时间 - maxWait: 60000 - # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 - timeBetweenEvictionRunsMillis: 60000 - # 配置一个连接在池中最小生存的时间,单位是毫秒 - minEvictableIdleTimeMillis: 300000 - # 配置一个连接在池中最大生存的时间,单位是毫秒 - maxEvictableIdleTimeMillis: 900000 - # 配置检测连接是否有效 - validationQuery: SELECT 1 FROM DUAL - testWhileIdle: true - testOnBorrow: false - testOnReturn: false diff --git a/src/main/java/cn/iocoder/dashboard/framework/datasource/config/DataSourceConfiguration.java b/src/main/java/cn/iocoder/dashboard/framework/datasource/config/DataSourceConfiguration.java index 44654f3284..203230a52b 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/datasource/config/DataSourceConfiguration.java +++ b/src/main/java/cn/iocoder/dashboard/framework/datasource/config/DataSourceConfiguration.java @@ -1,12 +1,38 @@ package cn.iocoder.dashboard.framework.datasource.config; +import cn.iocoder.dashboard.framework.datasource.core.filter.DruidAdRemoveFilter; +import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.annotation.EnableTransactionManagement; /** - * 数据库匹配类 + * 数据库配置类 + * + * @author 芋道源码 */ @Configuration @EnableTransactionManagement(proxyTargetClass = true) // 启动事务管理 public class DataSourceConfiguration { + + /** + * 创建 DruidAdRemoveFilter 过滤器,过滤 common.js 的广告 + */ + @Bean + @ConditionalOnProperty(name = "spring.datasource.druid.web-stat-filter.enabled", havingValue = "true") + public FilterRegistrationBean druidAdRemoveFilterFilter(DruidStatProperties properties) { + // 获取 druid web 监控页面的参数 + DruidStatProperties.StatViewServlet config = properties.getStatViewServlet(); + // 提取 common.js 的配置路径 + String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*"; + String commonJsPattern = pattern.replaceAll("\\*", "js/common.js"); + // 创建 DruidAdRemoveFilter Bean + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new DruidAdRemoveFilter()); + registrationBean.addUrlPatterns(commonJsPattern); + return registrationBean; + } + } diff --git a/src/main/java/cn/iocoder/dashboard/framework/datasource/core/enums/DataSourceEnum.java b/src/main/java/cn/iocoder/dashboard/framework/datasource/core/enums/DataSourceEnum.java new file mode 100644 index 0000000000..5013ef7b7c --- /dev/null +++ b/src/main/java/cn/iocoder/dashboard/framework/datasource/core/enums/DataSourceEnum.java @@ -0,0 +1,22 @@ +package cn.iocoder.dashboard.framework.datasource.core.enums; + +/** + * 对应于多数据源中不同数据源配置 + * + * 通过在方法上,使用 {@link com.baomidou.dynamic.datasource.annotation.DS} 注解,设置使用的数据源。 + * 注意,默认是 {@link #MASTER} 数据源 + * + * 对应官方文档为 http://dynamic-datasource.com/guide/customize/Annotation.html + */ +public interface DataSourceEnum { + + /** + * 主库,推荐使用 {@link com.baomidou.dynamic.datasource.annotation.Master} 注解 + */ + String MASTER = "master"; + /** + * 从库,推荐使用 {@link com.baomidou.dynamic.datasource.annotation.Slave} 注解 + */ + String SLAVE = "slave"; + +} diff --git a/src/main/java/cn/iocoder/dashboard/framework/datasource/core/filter/DruidAdRemoveFilter.java b/src/main/java/cn/iocoder/dashboard/framework/datasource/core/filter/DruidAdRemoveFilter.java new file mode 100644 index 0000000000..7bc9a98b5d --- /dev/null +++ b/src/main/java/cn/iocoder/dashboard/framework/datasource/core/filter/DruidAdRemoveFilter.java @@ -0,0 +1,38 @@ +package cn.iocoder.dashboard.framework.datasource.core.filter; + +import com.alibaba.druid.util.Utils; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Druid 底部广告过滤器 + * + * @author 芋道源码 + */ +public class DruidAdRemoveFilter extends OncePerRequestFilter { + + /** + * common.js 的路径 + */ + private static final String COMMON_JS_ILE_PATH = "support/http/resources/js/common.js"; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws ServletException, IOException { + chain.doFilter(request, response); + // 重置缓冲区,响应头不会被重置 + response.resetBuffer(); + // 获取 common.js + String text = Utils.readFromResource(COMMON_JS_ILE_PATH); + // 正则替换 banner, 除去底部的广告信息 + text = text.replaceAll("
", ""); + text = text.replaceAll("powered.*?shrek.wang", ""); + response.getWriter().write(text); + } + +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/tool/dal/mysql/codegen/ToolSchemaTableMapper.java b/src/main/java/cn/iocoder/dashboard/modules/tool/dal/mysql/codegen/ToolSchemaTableMapper.java index 622257c1c5..fcc625c0dc 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/tool/dal/mysql/codegen/ToolSchemaTableMapper.java +++ b/src/main/java/cn/iocoder/dashboard/modules/tool/dal/mysql/codegen/ToolSchemaTableMapper.java @@ -18,11 +18,7 @@ public interface ToolSchemaTableMapper extends BaseMapperX { .likeIfPresent("table_comment", tableComment)); } - default List selectListByTableSchema(String tableSchema) { - return selectList(new QueryWrapper().eq("table_schema", tableSchema)); - } - - default ToolSchemaTableDO selectByTableName1(String tableSchema, String tableName) { + default ToolSchemaTableDO selectByTableSchemaAndTableName(String tableSchema, String tableName) { return selectOne(new QueryWrapper().eq("table_schema",tableSchema) .eq("table_name", tableName)); } diff --git a/src/main/java/cn/iocoder/dashboard/modules/tool/service/codegen/impl/ToolCodegenServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/tool/service/codegen/impl/ToolCodegenServiceImpl.java index adb9fbfec2..0b35e0579d 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/tool/service/codegen/impl/ToolCodegenServiceImpl.java +++ b/src/main/java/cn/iocoder/dashboard/modules/tool/service/codegen/impl/ToolCodegenServiceImpl.java @@ -101,10 +101,10 @@ public class ToolCodegenServiceImpl implements ToolCodegenService { @Override public Long createCodegen(String tableName) { - //获取当前schema + // 获取当前schema String tableSchema = codegenProperties.getDbSchemas().iterator().next(); // 从数据库中,获得数据库表结构 - ToolSchemaTableDO schemaTable = schemaTableMapper.selectByTableName1(tableSchema, tableName); + ToolSchemaTableDO schemaTable = schemaTableMapper.selectByTableSchemaAndTableName(tableSchema, tableName); List schemaColumns = schemaColumnMapper.selectListByTableName(tableSchema, tableName); // 导入 return this.createCodegen0(ToolCodegenImportTypeEnum.DB, schemaTable, schemaColumns); diff --git a/src/main/resources/application-dev.yaml b/src/main/resources/application-dev.yaml index 39ae7e8513..1ecf43d6a8 100644 --- a/src/main/resources/application-dev.yaml +++ b/src/main/resources/application-dev.yaml @@ -4,34 +4,56 @@ server: --- #################### 数据库相关配置 #################### spring: - # 数据源配置项 TODO 多数据源;TODO 监控配置 + # 数据源配置项 + autoconfigure: + exclude: + - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源 datasource: - name: ruoyi-vue-pro - url: jdbc:mysql://400-infra.server.iocoder.cn:3306/${spring.datasource.name}?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT - driver-class-name: com.mysql.jdbc.Driver - username: root - password: 3WLiVUBEwTbvAfsh - druid: + druid: # Druid 【监控】相关的全局配置 web-stat-filter: enabled: true stat-view-servlet: enabled: true - # 设置白名单,不填则允许所有访问 - allow: + allow: # 设置白名单,不填则允许所有访问 url-pattern: /druid/* - # 控制台管理用户名和密码 - login-username: + login-username: # 控制台管理用户名和密码 login-password: filter: stat: enabled: true - # 慢 SQL 记录 - log-slow-sql: true + log-slow-sql: true # 慢 SQL 记录 slow-sql-millis: 100 merge-sql: true wall: config: multi-statement-allow: true + dynamic: # 多数据源配置 + druid: # Druid 【连接池】相关的全局配置 + initial-size: 5 # 初始连接数 + min-idle: 10 # 最小连接池数量 + max-active: 20 # 最大连接池数量 + max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒 + time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 + min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 + max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 + validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效 + test-while-idle: true + test-on-borrow: false + test-on-return: false + primary: master + datasource: + master: + name: ruoyi-vue-pro + url: jdbc:mysql://400-infra.server.iocoder.cn:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT + driver-class-name: com.mysql.jdbc.Driver + username: root + password: 3WLiVUBEwTbvAfsh + slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改 + name: ruoyi-vue-pro + url: jdbc:mysql://400-infra.server.iocoder.cn:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT + driver-class-name: com.mysql.jdbc.Driver + username: root + password: 3WLiVUBEwTbvAfsh # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 redis: @@ -77,9 +99,9 @@ apollo: eagerLoad: enabled: true # 设置 Apollo 在日志初始化前生效,可以实现日志的动态级别配置 jdbc: # 自定义的 JDBC 配置项,用于数据库的地址 - url: ${spring.datasource.url} - username: ${spring.datasource.username} - password: ${spring.datasource.password} + url: ${spring.datasource.dynamic.datasource.master.url} + username: ${spring.datasource.dynamic.datasource.master.username} + password: ${spring.datasource.dynamic.datasource.master.password} --- #################### 服务保障相关配置 #################### @@ -155,7 +177,7 @@ yudao: base-path: http://127.0.0.1:${server.port}${yudao.web.api-prefix}/system/file/get/ codegen: base-package: ${yudao.info.base-package} - db-schemas: ${spring.datasource.name} + db-schemas: ${spring.datasource.dynamic.datasource.master.name} xss: enable: false exclude-urls: # 如下两个 url,仅仅是为了演示,去掉配置也没关系 diff --git a/src/main/resources/application-local.yaml b/src/main/resources/application-local.yaml index 5736736c56..f99655ebef 100644 --- a/src/main/resources/application-local.yaml +++ b/src/main/resources/application-local.yaml @@ -4,34 +4,56 @@ server: --- #################### 数据库相关配置 #################### spring: - # 数据源配置项 TODO 多数据源;TODO 监控配置 + # 数据源配置项 + autoconfigure: + exclude: + - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源 datasource: - name: ruoyi-vue-pro - url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.name}?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT - driver-class-name: com.mysql.jdbc.Driver - username: root - password: 123456 - druid: + druid: # Druid 【监控】相关的全局配置 web-stat-filter: enabled: true stat-view-servlet: enabled: true - # 设置白名单,不填则允许所有访问 - allow: + allow: # 设置白名单,不填则允许所有访问 url-pattern: /druid/* - # 控制台管理用户名和密码 - login-username: + login-username: # 控制台管理用户名和密码 login-password: filter: stat: enabled: true - # 慢 SQL 记录 - log-slow-sql: true + log-slow-sql: true # 慢 SQL 记录 slow-sql-millis: 100 merge-sql: true wall: config: multi-statement-allow: true + dynamic: # 多数据源配置 + druid: # Druid 【连接池】相关的全局配置 + initial-size: 5 # 初始连接数 + min-idle: 10 # 最小连接池数量 + max-active: 20 # 最大连接池数量 + max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒 + time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 + min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 + max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 + validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效 + test-while-idle: true + test-on-borrow: false + test-on-return: false + primary: master + datasource: + master: + name: ruoyi-vue-pro + url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT + driver-class-name: com.mysql.jdbc.Driver + username: root + password: 123456 + slave: # 模拟从库,可根据自己需要修改 + name: ruoyi-vue-pro + url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT + driver-class-name: com.mysql.jdbc.Driver + username: root + password: 123456 # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 redis: @@ -77,9 +99,9 @@ apollo: eagerLoad: enabled: true # 设置 Apollo 在日志初始化前生效,可以实现日志的动态级别配置 jdbc: # 自定义的 JDBC 配置项,用于数据库的地址 - url: ${spring.datasource.url} - username: ${spring.datasource.username} - password: ${spring.datasource.password} + url: ${spring.datasource.dynamic.datasource.master.url} + username: ${spring.datasource.dynamic.datasource.master.username} + password: ${spring.datasource.dynamic.datasource.master.password} --- #################### 服务保障相关配置 #################### @@ -155,7 +177,7 @@ yudao: base-path: http://127.0.0.1:${server.port}${yudao.web.api-prefix}/system/file/get/ codegen: base-package: ${yudao.info.base-package} - db-schemas: ${spring.datasource.name} + db-schemas: ${spring.datasource.dynamic.datasource.master.name} xss: enable: false exclude-urls: # 如下两个 url,仅仅是为了演示,去掉配置也没关系 diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 33f84c5c8c..a834ddd265 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -12,7 +12,6 @@ spring: max-file-size: 16MB # 单个文件大小 max-request-size: 32MB # 设置总上传的文件大小 - # Jackson 配置项 jackson: serialization: write-dates-as-timestamps: true # 设置 Date 的格式,使用时间戳 @@ -32,3 +31,6 @@ mybatis-plus: logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) mapper-locations: classpath*:mapper/*.xml type-aliases-package: ${yudao.info.base-package}.modules.*.dal.dataobject + +--- #################### 芋道相关配置 #################### + diff --git a/src/test-integration/java/cn/iocoder/dashboard/BaseDbAndRedisIntegrationTest.java b/src/test-integration/java/cn/iocoder/dashboard/BaseDbAndRedisIntegrationTest.java index 98e1721c28..bb0b4a219a 100644 --- a/src/test-integration/java/cn/iocoder/dashboard/BaseDbAndRedisIntegrationTest.java +++ b/src/test-integration/java/cn/iocoder/dashboard/BaseDbAndRedisIntegrationTest.java @@ -3,7 +3,7 @@ package cn.iocoder.dashboard; import cn.iocoder.dashboard.framework.datasource.config.DataSourceConfiguration; import cn.iocoder.dashboard.framework.mybatis.config.MybatisConfiguration; import cn.iocoder.dashboard.framework.redis.config.RedisConfig; -import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure; +import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration; import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; import org.redisson.spring.starter.RedissonAutoConfiguration; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; @@ -19,13 +19,14 @@ public class BaseDbAndRedisIntegrationTest { @Import({ // DB 配置类 + DynamicDataSourceAutoConfiguration.class, // Dynamic Datasource 配置类 DataSourceConfiguration.class, // 自己的 DB 配置类 DataSourceAutoConfiguration.class, // Spring DB 自动配置类 DataSourceTransactionManagerAutoConfiguration.class, // Spring 事务自动配置类 - DruidDataSourceAutoConfigure.class, // Druid 自动配置类 // MyBatis 配置类 MybatisConfiguration.class, // 自己的 MyBatis 配置类 MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类 + // Redis 配置类 RedisAutoConfiguration.class, // Spring Redis 自动配置类 RedisConfig.class, // 自己的 Redis 配置类 diff --git a/src/test-integration/java/cn/iocoder/dashboard/modules/tool/dal/mysql/codegen/ToolInformationSchemaTableMapperTest.java b/src/test-integration/java/cn/iocoder/dashboard/modules/tool/dal/mysql/codegen/ToolInformationSchemaTableMapperTest.java index 67e8f7066e..a0004f3d4d 100644 --- a/src/test-integration/java/cn/iocoder/dashboard/modules/tool/dal/mysql/codegen/ToolInformationSchemaTableMapperTest.java +++ b/src/test-integration/java/cn/iocoder/dashboard/modules/tool/dal/mysql/codegen/ToolInformationSchemaTableMapperTest.java @@ -1,24 +1,12 @@ package cn.iocoder.dashboard.modules.tool.dal.mysql.codegen; import cn.iocoder.dashboard.BaseDbUnitTest; -import cn.iocoder.dashboard.modules.tool.dal.dataobject.codegen.ToolSchemaTableDO; -import org.junit.jupiter.api.Test; import javax.annotation.Resource; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertTrue; class ToolInformationSchemaTableMapperTest extends BaseDbUnitTest { @Resource private ToolSchemaTableMapper toolInformationSchemaTableMapper; - @Test - public void tstSelectListByTableSchema() { - List tables = toolInformationSchemaTableMapper - .selectListByTableSchema("ruoyi-vue-pro"); - assertTrue(tables.size() > 0); - } - } diff --git a/src/test-integration/resources/application-integration-test.yaml b/src/test-integration/resources/application-integration-test.yaml index 43a846ee2f..a248134d2b 100644 --- a/src/test-integration/resources/application-integration-test.yaml +++ b/src/test-integration/resources/application-integration-test.yaml @@ -7,12 +7,55 @@ spring: spring: # 数据源配置项 + autoconfigure: + exclude: + - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源 datasource: - name: ruoyi-vue-pro - url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.name}?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT - driver-class-name: com.mysql.jdbc.Driver - username: root - password: 123456 + druid: # Druid 【监控】相关的全局配置 + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白名单,不填则允许所有访问 + url-pattern: /druid/* + login-username: # 控制台管理用户名和密码 + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # 慢 SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # 多数据源配置 + druid: # Druid 【连接池】相关的全局配置 + initial-size: 5 # 初始连接数 + min-idle: 10 # 最小连接池数量 + max-active: 20 # 最大连接池数量 + max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒 + time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 + min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 + max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 + validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效 + test-while-idle: true + test-on-borrow: false + test-on-return: false + primary: master + datasource: + master: + name: ruoyi-vue-pro + url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT + driver-class-name: com.mysql.jdbc.Driver + username: root + password: 123456 + slave: # 模拟从库,可根据自己需要修改 + name: ruoyi-vue-pro + url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT + driver-class-name: com.mysql.jdbc.Driver + username: root + password: 123456 # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 redis: @@ -70,7 +113,7 @@ yudao: base-path: http://127.0.0.1:${server.port}/${yudao.web.api-prefix}/file/get/ codegen: base-package: ${yudao.info.base-package}.modules - db-schemas: ${spring.datasource.name} + db-schemas: ${spring.datasource.dynamic.datasource.master.name} xss: enable: false exclude-urls: # 如下两个 url,仅仅是为了演示,去掉配置也没关系