Compare commits
32 Commits
master
...
feature/de
Author | SHA1 | Date |
---|---|---|
|
998ee168d7 | |
|
b42fe75b88 | |
|
337411ab89 | |
|
7eeefe5b26 | |
|
4828ce080f | |
|
f72155aae7 | |
|
83b3eb487f | |
|
2379b0d544 | |
|
1e4735c1fc | |
|
848e0c897f | |
|
af8f66993e | |
|
68b49c6dd6 | |
|
5dda7a3c05 | |
|
d23d72c8bb | |
|
7ccdf86d3a | |
|
5572bf3fcb | |
|
cae35b636a | |
|
afd3128803 | |
|
152fc70e86 | |
|
be465d758b | |
|
6c9ef97122 | |
|
bcf926f7c7 | |
|
3efcc4bc62 | |
|
1b563fecaa | |
|
9be666e068 | |
|
dfb1bdb9fb | |
|
2db6a4510c | |
|
56c44acd11 | |
|
9a1a80e4b8 | |
|
b371225292 | |
|
2976675331 | |
|
45a5b7d1d4 |
|
@ -39,6 +39,7 @@
|
|||
| 项目名 | 说明 | 传送门 |
|
||||
|--------------------|------------------------|------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `ruoyi-vue-pro` | Spring Boot 多模块 | **[Gitee](https://gitee.com/zhijiantianya/ruoyi-vue-pro)** [Github](https://github.com/YunaiV/ruoyi-vue-pro) |
|
||||
| `yudao-boot-lite` | Spring Boot 多模块 精简版 | **[Gitee](https://gitee.com/yudaocode/yudao-boot-lite)** |
|
||||
| `yudao-cloud` | Spring Cloud 微服务 | **[Gitee](https://gitee.com/zhijiantianya/yudao-cloud)** [Github](https://github.com/YunaiV/yudao-cloud) |
|
||||
| `Spring-Boot-Labs` | Spring Boot & Cloud 入门 | **[Gitee](https://gitee.com/yudaocode/SpringBoot-Labs)** [Github](https://github.com/yudaocode/SpringBoot-Labs) |
|
||||
|
||||
|
@ -226,16 +227,16 @@ ps:核心功能已经实现,正在对接微信小程序中...
|
|||
> * [`yudao-ui-admin-vue3`](https://gitee.com/yudaocode/yudao-ui-admin-vue3):基于 Vue3 + element-plus 实现的管理后台
|
||||
> * `yudao-ui-admin`:基于 Vue2 + element-ui 实现的管理后台
|
||||
> * [`yudao-ui-go-view`](https://gitee.com/yudaocode/yudao-ui-go-view):基于 Vue3 + naive-ui 实现的大屏报表
|
||||
> * `yudao-ui-admin-uniapp`:基于 uni-app + uni-ui 实现的管理后台的小程序
|
||||
> * [`yudao-ui-admin-uniapp`](https://gitee.com/yudaocode/yudao-ui-admin-uniapp):基于 uni-app + uni-ui 实现的管理后台的小程序
|
||||
> * `yudao-ui-app`:基于 uni-app + uview 实现的用户 App
|
||||
|
||||
### 后端
|
||||
|
||||
| 框架 | 说明 | 版本 | 学习指南 |
|
||||
|---------------------------------------------------------------------------------------------|------------------|-------------|----------------------------------------------------------------|
|
||||
| [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 2.7.8 | [文档](https://github.com/YunaiV/SpringBoot-Labs) |
|
||||
| [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 2.7.9 | [文档](https://github.com/YunaiV/SpringBoot-Labs) |
|
||||
| [MySQL](https://www.mysql.com/cn/) | 数据库服务器 | 5.7 / 8.0+ | |
|
||||
| [Druid](https://github.com/alibaba/druid) | JDBC 连接池、监控组件 | 1.2.15 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
|
||||
| [Druid](https://github.com/alibaba/druid) | JDBC 连接池、监控组件 | 1.2.16 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
|
||||
| [MyBatis Plus](https://mp.baomidou.com/) | MyBatis 增强工具包 | 3.5.3.1 | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao) |
|
||||
| [Dynamic Datasource](https://dynamic-datasource.com/) | 动态数据源 | 3.6.1 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
|
||||
| [Redis](https://redis.io/) | key-value 数据库 | 5.0 / 6.0 | |
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -39,7 +39,7 @@
|
|||
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
|
||||
<!-- 看看咋放到 bom 里 -->
|
||||
<lombok.version>1.18.24</lombok.version>
|
||||
<spring.boot.version>2.7.8</spring.boot.version>
|
||||
<spring.boot.version>2.7.9</spring.boot.version>
|
||||
<mapstruct.version>1.5.3.Final</mapstruct.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
|
|
@ -16,13 +16,14 @@
|
|||
<properties>
|
||||
<revision>1.7.0-snapshot</revision>
|
||||
<!-- 统一依赖管理 -->
|
||||
<spring.boot.version>2.7.8</spring.boot.version>
|
||||
<spring.boot.version>2.7.9</spring.boot.version>
|
||||
<!-- Web 相关 -->
|
||||
<servlet.versoin>2.5</servlet.versoin>
|
||||
<swagger.version>2.2.8</swagger.version>
|
||||
<springdoc.version>1.6.14</springdoc.version>
|
||||
<knife4j.version>4.0.0</knife4j.version>
|
||||
<servlet.versoin>2.5</servlet.versoin>
|
||||
<!-- DB 相关 -->
|
||||
<druid.version>1.2.15</druid.version>
|
||||
<druid.version>1.2.16</druid.version>
|
||||
<mybatis-plus.version>3.5.3.1</mybatis-plus.version>
|
||||
<mybatis-plus-generator.version>3.5.3.1</mybatis-plus-generator.version>
|
||||
<dynamic-datasource.version>3.6.1</dynamic-datasource.version>
|
||||
|
@ -45,8 +46,8 @@
|
|||
<jsoup.version>1.15.3</jsoup.version>
|
||||
<lombok.version>1.18.24</lombok.version>
|
||||
<mapstruct.version>1.5.3.Final</mapstruct.version>
|
||||
<hutool.version>5.8.11</hutool.version>
|
||||
<easyexcel.verion>3.2.0</easyexcel.verion>
|
||||
<hutool.version>5.8.12</hutool.version>
|
||||
<easyexcel.verion>3.2.1</easyexcel.verion>
|
||||
<velocity.version>2.3</velocity.version>
|
||||
<screw.version>1.0.5</screw.version>
|
||||
<fastjson.version>1.2.83</fastjson.version>
|
||||
|
@ -56,15 +57,15 @@
|
|||
<commons-net.version>3.8.0</commons-net.version>
|
||||
<jsch.version>0.1.55</jsch.version>
|
||||
<tika-core.version>2.6.0</tika-core.version>
|
||||
<netty-all.version>4.1.86.Final</netty-all.version>
|
||||
<ip2region.version>2.6.6</ip2region.version>
|
||||
<netty-all.version>4.1.89.Final</netty-all.version>
|
||||
<ip2region.version>2.7.0</ip2region.version>
|
||||
<!-- 三方云服务相关 -->
|
||||
<okio.version>3.0.0</okio.version>
|
||||
<okhttp3.version>4.10.0</okhttp3.version>
|
||||
<minio.version>8.5.1</minio.version>
|
||||
<minio.version>8.5.2</minio.version>
|
||||
<aliyun-java-sdk-core.version>4.6.3</aliyun-java-sdk-core.version>
|
||||
<aliyun-java-sdk-dysmsapi.version>2.2.1</aliyun-java-sdk-dysmsapi.version>
|
||||
<tencentcloud-sdk-java.version>3.1.676</tencentcloud-sdk-java.version>
|
||||
<tencentcloud-sdk-java.version>3.1.706</tencentcloud-sdk-java.version>
|
||||
<justauth.version>1.4.0</justauth.version>
|
||||
<jimureport.version>1.5.6</jimureport.version>
|
||||
<xercesImpl.version>2.12.2</xercesImpl.version>
|
||||
|
@ -171,15 +172,40 @@
|
|||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
|
||||
<version>${knife4j.version}</version>
|
||||
<groupId>io.swagger.core.v3</groupId> <!-- 接口文档:使用最新版本的 Swagger 模型 -->
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>${swagger.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<groupId>io.swagger.core.v3</groupId> <!-- 接口文档:使用最新版本的 Swagger 模型 -->
|
||||
<artifactId>swagger-models</artifactId>
|
||||
<version>${swagger.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId> <!-- 接口文档 UI:解决 knife4j 引入的 Spring Doc 版本太老 -->
|
||||
<artifactId>springdoc-openapi-common</artifactId>
|
||||
<version>${springdoc.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId> <!-- 接口文档 UI:解决 knife4j 引入的 Spring Doc 版本太老 -->
|
||||
<artifactId>springdoc-openapi-webmvc-core</artifactId>
|
||||
<version>${springdoc.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId> <!-- 接口文档 UI:解决 knife4j 引入的 Spring Doc 版本太老 -->
|
||||
<artifactId>springdoc-openapi-webflux-core</artifactId>
|
||||
<version>${springdoc.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId> <!-- 接口文档 UI:默认 -->
|
||||
<artifactId>springdoc-openapi-ui</artifactId>
|
||||
<version>${springdoc.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId> <!-- 接口文档 UI:knife4j -->
|
||||
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
|
||||
<version>${knife4j.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- DB 相关 -->
|
||||
<dependency>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<!-- 统一依赖管理 -->
|
||||
<spring.boot.version>2.7.8</spring.boot.version>
|
||||
<spring.boot.version>2.7.9</spring.boot.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
|
@ -52,7 +52,7 @@
|
|||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.11</version>
|
||||
<version>5.8.12</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<!-- 统一依赖管理 -->
|
||||
<spring.boot.version>2.7.8</spring.boot.version>
|
||||
<spring.boot.version>2.7.9</spring.boot.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
|
@ -52,7 +52,7 @@
|
|||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.11</version>
|
||||
<version>5.8.12</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
|
|
@ -59,9 +59,9 @@
|
|||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-ui</artifactId>
|
||||
<scope>provided</scope> <!-- 设置为 provided,主要是 PageParam 使用到 -->
|
||||
<groupId>io.swagger.core.v3</groupId> <!-- 接口文档:使用最新版本的 Swagger 模型 -->
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- 监控相关 -->
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
package cn.iocoder.yudao.framework.common.util.spring;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import org.springframework.aop.framework.AdvisedSupport;
|
||||
import org.springframework.aop.framework.AopProxy;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
|
||||
/**
|
||||
* Spring AOP 工具类
|
||||
*
|
||||
* 参考波克尔 http://www.bubuko.com/infodetail-3471885.html 实现
|
||||
*/
|
||||
public class SpringAopUtils {
|
||||
|
||||
/**
|
||||
* 获取代理的目标对象
|
||||
*
|
||||
* @param proxy 代理对象
|
||||
* @return 目标对象
|
||||
*/
|
||||
public static Object getTarget(Object proxy) throws Exception {
|
||||
// 不是代理对象
|
||||
if (!AopUtils.isAopProxy(proxy)) {
|
||||
return proxy;
|
||||
}
|
||||
// Jdk 代理
|
||||
if (AopUtils.isJdkDynamicProxy(proxy)) {
|
||||
return getJdkDynamicProxyTargetObject(proxy);
|
||||
}
|
||||
// Cglib 代理
|
||||
return getCglibProxyTargetObject(proxy);
|
||||
}
|
||||
|
||||
private static Object getCglibProxyTargetObject(Object proxy) throws Exception {
|
||||
Object dynamicAdvisedInterceptor = BeanUtil.getFieldValue(proxy, "CGLIB$CALLBACK_0");
|
||||
AdvisedSupport advisedSupport = (AdvisedSupport) BeanUtil.getFieldValue(dynamicAdvisedInterceptor, "advised");
|
||||
return advisedSupport.getTargetSource().getTarget();
|
||||
}
|
||||
|
||||
private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {
|
||||
AopProxy aopProxy = (AopProxy) BeanUtil.getFieldValue(proxy, "h");
|
||||
AdvisedSupport advisedSupport = (AdvisedSupport) BeanUtil.getFieldValue(aopProxy, "advised");
|
||||
return advisedSupport.getTargetSource().getTarget();
|
||||
}
|
||||
|
||||
}
|
|
@ -16,7 +16,7 @@ import org.springframework.context.annotation.Primary;
|
|||
* 社交自动装配类
|
||||
*
|
||||
* @author timfruit
|
||||
* @date 2021-10-30
|
||||
* @since 2021-10-30
|
||||
*/
|
||||
@Slf4j
|
||||
@AutoConfiguration
|
||||
|
|
|
@ -18,7 +18,7 @@ import java.lang.reflect.Method;
|
|||
* 为使得拓展配置 {@link AuthConfig} 和默认配置齐平,所以自定义本工厂类
|
||||
*
|
||||
* @author timfruit
|
||||
* @date 2021-10-31
|
||||
* @since 2021-10-31
|
||||
*/
|
||||
public class YudaoAuthRequestFactory extends AuthRequestFactory {
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ import me.zhyd.oauth.utils.UrlBuilder;
|
|||
* 由于 JustAuth 定位是面向 Web 为主的三方登录,所以微信小程序只能自己封装
|
||||
*
|
||||
* @author timfruit
|
||||
* @date 2021-10-29
|
||||
* @since 2021-10-29
|
||||
*/
|
||||
public class AuthWeChatMiniAppRequest extends AuthDefaultRequest {
|
||||
|
||||
|
|
|
@ -39,4 +39,11 @@ public class TenantProperties {
|
|||
*/
|
||||
private Set<String> ignoreTables = Collections.emptySet();
|
||||
|
||||
/**
|
||||
* 需要忽略多租户的 {@link org.springframework.cache.Cache}
|
||||
*
|
||||
* 即默认所有 Cache 都开启多租户的功能
|
||||
*/
|
||||
private Set<String> ignoreCaches = Collections.emptySet();
|
||||
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
|
|||
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
|
||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnoreAspect;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.dynamic.TenantDsProcessor;
|
||||
import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
|
||||
import cn.iocoder.yudao.framework.tenant.core.job.TenantJobHandlerDecorator;
|
||||
import cn.iocoder.yudao.framework.tenant.core.mq.TenantRedisMessageInterceptor;
|
||||
|
@ -17,6 +18,11 @@ import cn.iocoder.yudao.framework.tenant.core.web.TenantContextWebFilter;
|
|||
import cn.iocoder.yudao.framework.web.config.WebProperties;
|
||||
import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
|
||||
import cn.iocoder.yudao.module.system.api.tenant.TenantApi;
|
||||
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
|
||||
import com.baomidou.dynamic.datasource.creator.DataSourceCreator;
|
||||
import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
|
||||
import com.baomidou.dynamic.datasource.processor.DsProcessor;
|
||||
import com.baomidou.dynamic.datasource.processor.DsSpelExpressionProcessor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
|
||||
import org.springframework.beans.BeansException;
|
||||
|
@ -33,6 +39,7 @@ import org.springframework.data.redis.cache.RedisCacheWriter;
|
|||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.Objects;
|
||||
|
||||
@AutoConfiguration
|
||||
|
@ -64,6 +71,18 @@ public class YudaoTenantAutoConfiguration {
|
|||
return inner;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DsProcessor dsProcessor(
|
||||
// TenantFrameworkService tenantFrameworkService,
|
||||
// DataSource dataSource,
|
||||
// DefaultDataSourceCreator dataSourceCreator
|
||||
) {
|
||||
// TenantDsProcessor tenantDsProcessor = new TenantDsProcessor(tenantFrameworkService, dataSourceCreator);
|
||||
TenantDsProcessor tenantDsProcessor = new TenantDsProcessor();
|
||||
tenantDsProcessor.setNextProcessor(new DsSpelExpressionProcessor());
|
||||
return tenantDsProcessor;
|
||||
}
|
||||
|
||||
// ========== WEB ==========
|
||||
|
||||
@Bean
|
||||
|
@ -123,13 +142,14 @@ public class YudaoTenantAutoConfiguration {
|
|||
|
||||
@Bean
|
||||
@Primary // 引入租户时,tenantRedisCacheManager 为主 Bean
|
||||
public RedisCacheManager tenantRedisCacheManager(RedisTemplate<String, Object> redisTemplate,
|
||||
RedisCacheConfiguration redisCacheConfiguration) {
|
||||
public RedisCacheManager redisCacheManager(RedisTemplate<String, Object> redisTemplate,
|
||||
RedisCacheConfiguration redisCacheConfiguration,
|
||||
TenantProperties tenantProperties) {
|
||||
// 创建 RedisCacheWriter 对象
|
||||
RedisConnectionFactory connectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory());
|
||||
RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
|
||||
// 创建 TenantRedisCacheManager 对象
|
||||
return new TenantRedisCacheManager(cacheWriter, redisCacheConfiguration);
|
||||
return new TenantRedisCacheManager(cacheWriter, redisCacheConfiguration, tenantProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package cn.iocoder.yudao.framework.tenant.core.db.dynamic;
|
||||
|
||||
import com.baomidou.dynamic.datasource.annotation.DS;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 使用租户所在的数据源
|
||||
*
|
||||
* 使用方式:当我们希望一个表使用租户所在的数据源,可以在该表的 Mapper 上添加该注解
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@DS(TenantDS.KEY)
|
||||
public @interface TenantDS {
|
||||
|
||||
/**
|
||||
* 租户对应的数据源的占位符
|
||||
*/
|
||||
String KEY = "#context.tenantId";
|
||||
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
package cn.iocoder.yudao.framework.tenant.core.db.dynamic;
|
||||
|
||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||
import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;
|
||||
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
|
||||
import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
|
||||
import com.baomidou.dynamic.datasource.processor.DsProcessor;
|
||||
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
|
||||
import jodd.util.CollectionUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.sql.DataSource;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 基于 {@link TenantDS} 的数据源处理器
|
||||
*
|
||||
* 1. 如果有 @TenantDS 注解,返回该租户的数据源
|
||||
* 2. 如果该租户的数据源未创建,则进行创建
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class TenantDsProcessor extends DsProcessor {
|
||||
|
||||
/**
|
||||
* 用于获取租户数据源配置的 Service
|
||||
*/
|
||||
@Resource
|
||||
@Lazy
|
||||
private TenantFrameworkService tenantFrameworkService;
|
||||
|
||||
/**
|
||||
* 动态数据源
|
||||
*/
|
||||
@Resource
|
||||
@Lazy // 为什么添加 @Lazy 注解?因为它和 DynamicRoutingDataSource 相互依赖,导致无法初始化
|
||||
private DynamicRoutingDataSource dynamicRoutingDataSource;
|
||||
|
||||
/**
|
||||
* 用于创建租户数据源的 Creator
|
||||
*/
|
||||
@Resource
|
||||
@Lazy
|
||||
private DefaultDataSourceCreator dataSourceCreator;
|
||||
|
||||
@Override
|
||||
public boolean matches(String key) {
|
||||
return Objects.equals(key, TenantDS.KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String doDetermineDatasource(MethodInvocation invocation, String key) {
|
||||
// 获得数据源配置
|
||||
Long tenantId = TenantContextHolder.getRequiredTenantId();
|
||||
DataSourceProperty dataSourceProperty = tenantFrameworkService.getDataSourceProperty(tenantId);
|
||||
// 创建 or 创建数据源,并返回数据源名字
|
||||
return createDatasourceIfAbsent(dataSourceProperty);
|
||||
}
|
||||
|
||||
private String createDatasourceIfAbsent(DataSourceProperty dataSourceProperty) {
|
||||
// 1. 重点:如果数据源不存在,则进行创建
|
||||
if (isDataSourceNotExist(dataSourceProperty)) {
|
||||
// 问题一:为什么要加锁?因为,如果多个线程同时执行到这里,会导致多次创建数据源
|
||||
// 问题二:为什么要使用 poolName 加锁?保证多个不同的 poolName 可以并发创建数据源
|
||||
// 问题三:为什么要使用 intern 方法?因为,intern 方法,会返回一个字符串的常量池中的引用
|
||||
// intern 的说明,可见 https://www.cnblogs.com/xrq730/p/6662232.html 文章
|
||||
synchronized (dataSourceProperty.getPoolName().intern()) {
|
||||
if (isDataSourceNotExist(dataSourceProperty)) {
|
||||
DataSource dataSource = dataSourceCreator.createDataSource(dataSourceProperty);
|
||||
dynamicRoutingDataSource.addDataSource(dataSourceProperty.getPoolName(), dataSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2. 返回数据源的名字
|
||||
return dataSourceProperty.getPoolName();
|
||||
}
|
||||
|
||||
private boolean isDataSourceNotExist(DataSourceProperty dataSourceProperty) {
|
||||
return !dynamicRoutingDataSource.getDataSources().containsKey(dataSourceProperty.getPoolName());
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,10 @@
|
|||
package cn.iocoder.yudao.framework.tenant.core.redis;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.redis.core.TimeoutRedisCacheManager;
|
||||
import cn.iocoder.yudao.framework.tenant.config.TenantProperties;
|
||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||
import jodd.io.StreamUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
||||
|
@ -10,24 +14,34 @@ import org.springframework.data.redis.cache.RedisCacheWriter;
|
|||
/**
|
||||
* 多租户的 {@link RedisCacheManager} 实现类
|
||||
*
|
||||
* 操作指定 name 的 {@link Cache} 时,自动拼接租户后缀,格式为 name + ":" + tenantId + 后缀
|
||||
* 操作指定 name 的 {@link Cache} 时,自动拼接租户后缀,格式为 name + "::t" + tenantId + 后缀
|
||||
*
|
||||
* @author airhead
|
||||
*/
|
||||
@Slf4j
|
||||
public class TenantRedisCacheManager extends RedisCacheManager {
|
||||
public class TenantRedisCacheManager extends TimeoutRedisCacheManager {
|
||||
|
||||
/**
|
||||
* 多租户 Redis Key 的前缀,补充在原有 name 的 : 后面
|
||||
*
|
||||
* 原因:如果只补充租户编号,可读性较差
|
||||
*/
|
||||
public static final String PREFIX = "t";
|
||||
|
||||
private final TenantProperties tenantProperties;
|
||||
|
||||
public TenantRedisCacheManager(RedisCacheWriter cacheWriter,
|
||||
RedisCacheConfiguration defaultCacheConfiguration) {
|
||||
RedisCacheConfiguration defaultCacheConfiguration,
|
||||
TenantProperties tenantProperties) {
|
||||
super(cacheWriter, defaultCacheConfiguration);
|
||||
this.tenantProperties = tenantProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cache getCache(String name) {
|
||||
// 如果开启多租户,则 name 拼接租户后缀
|
||||
if (!TenantContextHolder.isIgnore()
|
||||
&& TenantContextHolder.getTenantId() != null) {
|
||||
name = name + ":" + TenantContextHolder.getTenantId();
|
||||
// 如果不忽略多租户的 Cache,则自动拼接租户后缀
|
||||
if (!tenantProperties.getIgnoreCaches().contains(name)) {
|
||||
name = name + StrUtil.COLON + PREFIX + TenantContextHolder.getRequiredTenantId();
|
||||
}
|
||||
|
||||
// 继续基于父方法
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
package cn.iocoder.yudao.framework.tenant.core.redis;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;
|
||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* 多租户拓展的 RedisKeyDefine 实现类
|
||||
*
|
||||
* 由于 Redis 不同于 MySQL 有 column 字段,无法通过类似 WHERE tenant_id = ? 的方式过滤
|
||||
* 所以需要通过在 Redis Key 上增加后缀的方式,进行租户之间的隔离。具体的步骤是:
|
||||
* 1. 假设 Redis Key 是 user:%d,示例是 user:1;对应到多租户的 Redis Key 是 user:%d:%d,
|
||||
* 2. 在 Redis DAO 中,需要使用 {@link #formatKey(Object...)} 方法,进行 Redis Key 的格式化
|
||||
*
|
||||
* 注意,大多数情况下,并不用使用 TenantRedisKeyDefine 实现。主要的使用场景,还是 Redis Key 可能存在冲突的情况。
|
||||
* 例如说,租户 1 和 2 都有一个手机号作为 Key,则他们会存在冲突的问题
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class TenantRedisKeyDefine extends RedisKeyDefine {
|
||||
|
||||
/**
|
||||
* 多租户的 KEY 模板
|
||||
*/
|
||||
private static final String KEY_TEMPLATE_SUFFIX = ":%d";
|
||||
|
||||
public TenantRedisKeyDefine(String memo, String keyTemplate, KeyTypeEnum keyType, Class<?> valueType, Duration timeout) {
|
||||
super(memo, buildKeyTemplate(keyTemplate), keyType, valueType, timeout);
|
||||
}
|
||||
|
||||
public TenantRedisKeyDefine(String memo, String keyTemplate, KeyTypeEnum keyType, Class<?> valueType, TimeoutTypeEnum timeoutType) {
|
||||
super(memo, buildKeyTemplate(keyTemplate), keyType, valueType, timeoutType);
|
||||
}
|
||||
|
||||
private static String buildKeyTemplate(String keyTemplate) {
|
||||
return keyTemplate + KEY_TEMPLATE_SUFFIX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatKey(Object... args) {
|
||||
args = ArrayUtil.append(args, TenantContextHolder.getRequiredTenantId());
|
||||
return super.formatKey(args);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package cn.iocoder.yudao.framework.tenant.core.service;
|
||||
|
||||
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -23,4 +25,12 @@ public interface TenantFrameworkService {
|
|||
*/
|
||||
void validTenant(Long id);
|
||||
|
||||
/**
|
||||
* 获得租户对应的数据源配置
|
||||
*
|
||||
* @param id 租户编号
|
||||
* @return 数据源配置
|
||||
*/
|
||||
DataSourceProperty getDataSourceProperty(Long id);
|
||||
|
||||
}
|
||||
|
|
|
@ -2,7 +2,10 @@ package cn.iocoder.yudao.framework.tenant.core.service;
|
|||
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.framework.common.util.cache.CacheUtils;
|
||||
import cn.iocoder.yudao.module.infra.api.db.dto.DataSourceConfigRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.tenant.TenantApi;
|
||||
import cn.iocoder.yudao.module.system.api.tenant.dto.TenantDataSourceConfigRespDTO;
|
||||
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
@ -56,6 +59,28 @@ public class TenantFrameworkServiceImpl implements TenantFrameworkService {
|
|||
|
||||
});
|
||||
|
||||
/**
|
||||
* 针对 {@link #getDataSourceProperty(Long)} 的缓存
|
||||
*/
|
||||
private final LoadingCache<Long, DataSourceProperty> dataSourcePropertyCache = CacheUtils.buildAsyncReloadingCache(
|
||||
Duration.ofMinutes(1L), // 过期时间 1 分钟
|
||||
new CacheLoader<Long, DataSourceProperty>() {
|
||||
|
||||
@Override
|
||||
public DataSourceProperty load(Long id) {
|
||||
// 获得租户对应的数据源配置
|
||||
TenantDataSourceConfigRespDTO dataSourceConfig = tenantApi.getTenantDataSourceConfig(id);
|
||||
if (dataSourceConfig == null) {
|
||||
return null;
|
||||
}
|
||||
// 转换成 dynamic-datasource 配置
|
||||
return new DataSourceProperty()
|
||||
.setPoolName(dataSourceConfig.getName()).setUrl(dataSourceConfig.getUrl())
|
||||
.setUsername(dataSourceConfig.getUsername()).setPassword(dataSourceConfig.getPassword());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public List<Long> getTenantIds() {
|
||||
|
@ -70,4 +95,10 @@ public class TenantFrameworkServiceImpl implements TenantFrameworkService {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public DataSourceProperty getDataSourceProperty(Long id) {
|
||||
return dataSourcePropertyCache.get(id);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
package cn.iocoder.yudao.framework.tenant.core.redis;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;
|
||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class TenantRedisKeyDefineTest {
|
||||
|
||||
@Test
|
||||
public void testFormatKey() {
|
||||
Long tenantId = 30L;
|
||||
TenantContextHolder.setTenantId(tenantId);
|
||||
// 准备参数
|
||||
TenantRedisKeyDefine define = new TenantRedisKeyDefine("", "user:%d:%d", RedisKeyDefine.KeyTypeEnum.HASH,
|
||||
Object.class, RedisKeyDefine.TimeoutTypeEnum.FIXED);
|
||||
Long userId = 10L;
|
||||
Integer userType = 1;
|
||||
|
||||
// 调用
|
||||
String key = define.formatKey(userId, userType);
|
||||
// 断言
|
||||
assertEquals("user:10:1:30", key);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
package cn.iocoder.yudao.framework.captcha.config;
|
||||
|
||||
import cn.hutool.core.util.ClassUtil;
|
||||
import cn.iocoder.yudao.framework.captcha.core.enums.CaptchaRedisKeyConstants;
|
||||
import cn.iocoder.yudao.framework.captcha.core.service.RedisCaptchaServiceImpl;
|
||||
import cn.iocoder.yudao.framework.captcha.core.RedisCaptchaServiceImpl;
|
||||
import com.xingyuv.captcha.service.CaptchaCacheService;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
@ -11,12 +9,6 @@ import org.springframework.data.redis.core.StringRedisTemplate;
|
|||
@AutoConfiguration
|
||||
public class YudaoCaptchaConfiguration {
|
||||
|
||||
static {
|
||||
// 手动加载 Lock4jRedisKeyConstants 类,因为它不会被使用到
|
||||
// 如果不加载,会导致 Redis 监控,看到它的 Redis Key 枚举
|
||||
ClassUtil.loadClass(CaptchaRedisKeyConstants.class.getName());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CaptchaCacheService captchaCacheService(StringRedisTemplate stringRedisTemplate) {
|
||||
return new RedisCaptchaServiceImpl(stringRedisTemplate);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package cn.iocoder.yudao.framework.captcha.core.service;
|
||||
package cn.iocoder.yudao.framework.captcha.core;
|
||||
|
||||
import com.xingyuv.captcha.service.CaptchaCacheService;
|
||||
import lombok.AllArgsConstructor;
|
|
@ -1,25 +0,0 @@
|
|||
package cn.iocoder.yudao.framework.captcha.core.enums;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;
|
||||
import com.xingyuv.captcha.model.vo.PointVO;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.STRING;
|
||||
|
||||
/**
|
||||
* 验证码 Redis Key 枚举类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface CaptchaRedisKeyConstants {
|
||||
|
||||
RedisKeyDefine AJ_CAPTCHA_REQ_LIMIT = new RedisKeyDefine("验证码的请求限流",
|
||||
"AJ.CAPTCHA.REQ.LIMIT-%s-%s",
|
||||
STRING, Integer.class, Duration.ofSeconds(60)); // 例如说:验证失败 5 次,get 接口锁定
|
||||
|
||||
RedisKeyDefine AJ_CAPTCHA_RUNNING = new RedisKeyDefine("验证码的坐标",
|
||||
"RUNNING:CAPTCHA:%s", // AbstractCaptchaService.REDIS_CAPTCHA_KEY
|
||||
STRING, PointVO.class, Duration.ofSeconds(120)); // {"secretKey":"PP1w2Frr2KEejD2m","x":162,"y":5}
|
||||
|
||||
}
|
|
@ -1 +1 @@
|
|||
cn.iocoder.yudao.framework.captcha.core.service.RedisCaptchaServiceImpl
|
||||
cn.iocoder.yudao.framework.captcha.core.RedisCaptchaServiceImpl
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package cn.iocoder.yudao.framework.flowable.core.util;
|
||||
|
||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import org.flowable.bpmn.converter.BpmnXMLConverter;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.bpmn.model.FlowElement;
|
||||
|
@ -26,6 +28,21 @@ public class FlowableUtils {
|
|||
Authentication.setAuthenticatedUserId(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得租户编号
|
||||
*
|
||||
* 因为 Flowable 的租户编号是字符串,所以封装该方法
|
||||
*
|
||||
* @return 租户编号
|
||||
*/
|
||||
public static String getTenantId() {
|
||||
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
|
||||
if (loginUser == null || loginUser.getTenantId() == null) {
|
||||
return null;
|
||||
}
|
||||
return String.valueOf(loginUser.getTenantId());
|
||||
}
|
||||
|
||||
// ========== BPMN 相关的工具方法 ==========
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
package cn.iocoder.yudao.framework.idempotent.core.redis;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.STRING;
|
||||
|
||||
/**
|
||||
* 幂等 Redis DAO
|
||||
*
|
||||
|
@ -16,9 +13,14 @@ import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.S
|
|||
@AllArgsConstructor
|
||||
public class IdempotentRedisDAO {
|
||||
|
||||
private static final RedisKeyDefine IDEMPOTENT = new RedisKeyDefine("幂等操作",
|
||||
"idempotent:%s", // 参数为 uuid
|
||||
STRING, String.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC);
|
||||
/**
|
||||
* 幂等操作的 Redis Key 模板
|
||||
*
|
||||
* KEY 格式:idempotent:{uuid}
|
||||
* VALUE 格式:空字符串
|
||||
* 过期时间:动态传参
|
||||
*/
|
||||
private static final String IDEMPOTENT_KEY_TEMPLATE = "idempotent:%s";
|
||||
|
||||
private final StringRedisTemplate redisTemplate;
|
||||
|
||||
|
@ -28,7 +30,7 @@ public class IdempotentRedisDAO {
|
|||
}
|
||||
|
||||
private static String formatKey(String key) {
|
||||
return String.format(IDEMPOTENT.getKeyTemplate(), key);
|
||||
return String.format(IDEMPOTENT_KEY_TEMPLATE, key);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,21 +1,13 @@
|
|||
package cn.iocoder.yudao.framework.lock4j.config;
|
||||
|
||||
import cn.hutool.core.util.ClassUtil;
|
||||
import com.baomidou.lock.spring.boot.autoconfigure.LockAutoConfiguration;
|
||||
import cn.iocoder.yudao.framework.lock4j.core.DefaultLockFailureStrategy;
|
||||
import cn.iocoder.yudao.framework.lock4j.core.Lock4jRedisKeyConstants;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
@AutoConfiguration(before = LockAutoConfiguration.class)
|
||||
public class YudaoLock4jConfiguration {
|
||||
|
||||
static {
|
||||
// 手动加载 Lock4jRedisKeyConstants 类,因为它不会被使用到
|
||||
// 如果不加载,会导致 Redis 监控,看到它的 Redis Key 枚举
|
||||
ClassUtil.loadClass(Lock4jRedisKeyConstants.class.getName());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DefaultLockFailureStrategy lockFailureStrategy() {
|
||||
return new DefaultLockFailureStrategy();
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
package cn.iocoder.yudao.framework.lock4j.core;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;
|
||||
import org.redisson.api.RLock;
|
||||
|
||||
import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.HASH;
|
||||
|
||||
/**
|
||||
* Lock4j Redis Key 枚举类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface Lock4jRedisKeyConstants {
|
||||
|
||||
RedisKeyDefine LOCK4J = new RedisKeyDefine("分布式锁",
|
||||
"lock4j:%s", // 参数来自 DefaultLockKeyBuilder 类
|
||||
HASH, RLock.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); // Redisson 的 Lock 锁,使用 Hash 数据结构
|
||||
|
||||
}
|
|
@ -32,11 +32,16 @@
|
|||
<artifactId>spring-boot-starter-cache</artifactId> <!-- 实现对 Caches 的自动化配置 -->
|
||||
</dependency>
|
||||
|
||||
<!-- 工具相关 -->
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package cn.iocoder.yudao.framework.redis.config;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.redis.core.TimeoutRedisCacheManager;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.cache.CacheProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
|
@ -7,9 +9,17 @@ import org.springframework.cache.annotation.EnableCaching;
|
|||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
||||
import org.springframework.data.redis.cache.RedisCacheManager;
|
||||
import org.springframework.data.redis.cache.RedisCacheWriter;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.RedisSerializationContext;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration.buildRedisSerializer;
|
||||
|
||||
/**
|
||||
* Cache 配置类,基于 Redis 实现
|
||||
*/
|
||||
|
@ -26,9 +36,13 @@ public class YudaoCacheAutoConfiguration {
|
|||
@Bean
|
||||
@Primary
|
||||
public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
|
||||
// 设置使用 JSON 序列化方式
|
||||
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
|
||||
config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json()));
|
||||
// 设置使用 : 单冒号,而不是双 :: 冒号,避免 Redis Desktop Manager 多余空格
|
||||
// 详细可见 https://blog.csdn.net/chuixue24/article/details/103928965 博客
|
||||
config = config.computePrefixWith(cacheName -> cacheName + StrUtil.COLON);
|
||||
// 设置使用 JSON 序列化方式
|
||||
config = config.serializeValuesWith(
|
||||
RedisSerializationContext.SerializationPair.fromSerializer(buildRedisSerializer()));
|
||||
|
||||
// 设置 CacheProperties.Redis 的属性
|
||||
CacheProperties.Redis redisProperties = cacheProperties.getRedis();
|
||||
|
@ -47,4 +61,14 @@ public class YudaoCacheAutoConfiguration {
|
|||
return config;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RedisCacheManager redisCacheManager(RedisTemplate<String, Object> redisTemplate,
|
||||
RedisCacheConfiguration redisCacheConfiguration) {
|
||||
// 创建 RedisCacheWriter 对象
|
||||
RedisConnectionFactory connectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory());
|
||||
RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
|
||||
// 创建 TenantRedisCacheManager 对象
|
||||
return new TimeoutRedisCacheManager(cacheWriter, redisCacheConfiguration);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package cn.iocoder.yudao.framework.redis.config;
|
||||
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
|
@ -25,9 +28,17 @@ public class YudaoRedisAutoConfiguration {
|
|||
template.setKeySerializer(RedisSerializer.string());
|
||||
template.setHashKeySerializer(RedisSerializer.string());
|
||||
// 使用 JSON 序列化方式(库是 Jackson ),序列化 VALUE 。
|
||||
template.setValueSerializer(RedisSerializer.json());
|
||||
template.setHashValueSerializer(RedisSerializer.json());
|
||||
template.setValueSerializer(buildRedisSerializer());
|
||||
template.setHashValueSerializer(buildRedisSerializer());
|
||||
return template;
|
||||
}
|
||||
|
||||
public static RedisSerializer<?> buildRedisSerializer() {
|
||||
RedisSerializer<Object> json = RedisSerializer.json();
|
||||
// 解决 LocalDateTime 的序列化
|
||||
ObjectMapper objectMapper = (ObjectMapper) ReflectUtil.getFieldValue(json, "mapper");
|
||||
objectMapper.registerModules(new JavaTimeModule());
|
||||
return json;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,113 +0,0 @@
|
|||
package cn.iocoder.yudao.framework.redis.core;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* Redis Key 定义类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class RedisKeyDefine {
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum KeyTypeEnum {
|
||||
|
||||
STRING("String"),
|
||||
LIST("List"),
|
||||
HASH("Hash"),
|
||||
SET("Set"),
|
||||
ZSET("Sorted Set"),
|
||||
STREAM("Stream"),
|
||||
PUBSUB("Pub/Sub");
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
@JsonValue
|
||||
private final String type;
|
||||
|
||||
}
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum TimeoutTypeEnum {
|
||||
|
||||
FOREVER(1), // 永不超时
|
||||
DYNAMIC(2), // 动态超时
|
||||
FIXED(3); // 固定超时
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
@JsonValue
|
||||
private final Integer type;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Key 模板
|
||||
*/
|
||||
private final String keyTemplate;
|
||||
/**
|
||||
* Key 类型的枚举
|
||||
*/
|
||||
private final KeyTypeEnum keyType;
|
||||
/**
|
||||
* Value 类型
|
||||
*
|
||||
* 如果是使用分布式锁,设置为 {@link java.util.concurrent.locks.Lock} 类型
|
||||
*/
|
||||
private final Class<?> valueType;
|
||||
/**
|
||||
* 超时类型
|
||||
*/
|
||||
private final TimeoutTypeEnum timeoutType;
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
private final Duration timeout;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private final String memo;
|
||||
|
||||
private RedisKeyDefine(String memo, String keyTemplate, KeyTypeEnum keyType, Class<?> valueType,
|
||||
TimeoutTypeEnum timeoutType, Duration timeout) {
|
||||
this.memo = memo;
|
||||
this.keyTemplate = keyTemplate;
|
||||
this.keyType = keyType;
|
||||
this.valueType = valueType;
|
||||
this.timeout = timeout;
|
||||
this.timeoutType = timeoutType;
|
||||
// 添加注册表
|
||||
RedisKeyRegistry.add(this);
|
||||
}
|
||||
|
||||
public RedisKeyDefine(String memo, String keyTemplate, KeyTypeEnum keyType, Class<?> valueType, Duration timeout) {
|
||||
this(memo, keyTemplate, keyType, valueType, TimeoutTypeEnum.FIXED, timeout);
|
||||
}
|
||||
|
||||
public RedisKeyDefine(String memo, String keyTemplate, KeyTypeEnum keyType, Class<?> valueType, TimeoutTypeEnum timeoutType) {
|
||||
this(memo, keyTemplate, keyType, valueType, timeoutType, Duration.ZERO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化 Key
|
||||
*
|
||||
* 注意,内部采用 {@link String#format(String, Object...)} 实现
|
||||
*
|
||||
* @param args 格式化的参数
|
||||
* @return Key
|
||||
*/
|
||||
public String formatKey(Object... args) {
|
||||
return String.format(keyTemplate, args);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
package cn.iocoder.yudao.framework.redis.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@link RedisKeyDefine} 注册表
|
||||
*/
|
||||
public class RedisKeyRegistry {
|
||||
|
||||
/**
|
||||
* Redis RedisKeyDefine 数组
|
||||
*/
|
||||
private static final List<RedisKeyDefine> DEFINES = new ArrayList<>();
|
||||
|
||||
public static void add(RedisKeyDefine define) {
|
||||
DEFINES.add(define);
|
||||
}
|
||||
|
||||
public static List<RedisKeyDefine> list() {
|
||||
return DEFINES;
|
||||
}
|
||||
|
||||
public static int size() {
|
||||
return DEFINES.size();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package cn.iocoder.yudao.framework.redis.core;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import org.springframework.boot.convert.DurationStyle;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.data.redis.cache.RedisCache;
|
||||
import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
||||
import org.springframework.data.redis.cache.RedisCacheManager;
|
||||
import org.springframework.data.redis.cache.RedisCacheWriter;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
/**
|
||||
* 支持自定义过期时间的 {@link RedisCacheManager} 实现类
|
||||
*
|
||||
* 在 {@link Cacheable#cacheNames()} 格式为 "key#ttl" 时,# 后面的 ttl 为过期时间,单位为秒
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class TimeoutRedisCacheManager extends RedisCacheManager {
|
||||
|
||||
private static final String SPLIT = "#";
|
||||
|
||||
public TimeoutRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
|
||||
super(cacheWriter, defaultCacheConfiguration);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {
|
||||
if (StrUtil.isEmpty(name)) {
|
||||
return super.createRedisCache(name, cacheConfig);
|
||||
}
|
||||
// 如果使用 # 分隔,大小不为 2,则说明不使用自定义过期时间
|
||||
String[] names = StrUtil.splitToArray(name, SPLIT);
|
||||
if (names.length != 2) {
|
||||
return super.createRedisCache(name, cacheConfig);
|
||||
}
|
||||
|
||||
// 核心:通过修改 cacheConfig 的过期时间,实现自定义过期时间
|
||||
if (cacheConfig != null) {
|
||||
// 移除 # 后面的 : 以及后面的内容,避免影响解析
|
||||
names[1] = StrUtil.subBefore(names[1], StrUtil.COLON, false);
|
||||
// 解析时间
|
||||
Duration duration = DurationStyle.detectAndParse(names[1], ChronoUnit.SECONDS);
|
||||
cacheConfig = cacheConfig.entryTtl(duration);
|
||||
}
|
||||
return super.createRedisCache(names[0], cacheConfig);
|
||||
}
|
||||
|
||||
}
|
|
@ -21,25 +21,17 @@
|
|||
<artifactId>yudao-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<!-- spring boot 配置所需依赖 -->
|
||||
<!-- Spring Boot 配置所需依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-ui</artifactId>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
@ -48,6 +40,15 @@
|
|||
<scope>provided</scope> <!-- 设置为 provided,主要是 GlobalExceptionHandler 使用 -->
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId> <!-- 接口文档 -->
|
||||
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-ui</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 业务组件 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
|
|
|
@ -17,7 +17,7 @@ import java.util.Set;
|
|||
* 第二步,将【多个】任务候选人们,设置到 DelegateExecution 的 collectionVariable 变量中,以便 BpmUserTaskActivityBehavior 使用它
|
||||
*
|
||||
* @author kemengkai
|
||||
* @date 2022-04-21 16:57
|
||||
* @since 2022-04-21 16:57
|
||||
*/
|
||||
@Slf4j
|
||||
public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehavior {
|
||||
|
|
|
@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.bpm.service.definition;
|
|||
import cn.hutool.core.lang.Assert;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.dynamic.TenantDS;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormCreateReqVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormUpdateReqVO;
|
||||
|
@ -29,6 +30,7 @@ import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
|
|||
*/
|
||||
@Service
|
||||
@Validated
|
||||
@TenantDS // 工作流的 Service 必须添加 @TenantDS 注解。原因是,Flowable 使用事务,无法切换数据源,需要提使用 @TenantDS 切到它的数据源
|
||||
public class BpmFormServiceImpl implements BpmFormService {
|
||||
|
||||
@Resource
|
||||
|
|
|
@ -7,6 +7,8 @@ import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
|||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.PageUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
|
||||
import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.dynamic.TenantDS;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*;
|
||||
import cn.iocoder.yudao.module.bpm.convert.definition.BpmModelConvert;
|
||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
|
||||
|
@ -47,6 +49,7 @@ import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
|
|||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
@TenantDS // 工作流的 Service 必须添加 @TenantDS 注解。原因是,Flowable 使用事务,无法切换数据源,需要提使用 @TenantDS 切到它的数据源
|
||||
public class BpmModelServiceImpl implements BpmModelService {
|
||||
|
||||
@Resource
|
||||
|
@ -71,7 +74,8 @@ public class BpmModelServiceImpl implements BpmModelService {
|
|||
modelQuery.modelCategory(pageVO.getCategory());
|
||||
}
|
||||
// 执行查询
|
||||
List<Model> models = modelQuery.orderByCreateTime().desc()
|
||||
List<Model> models = modelQuery.modelTenantId(FlowableUtils.getTenantId())
|
||||
.orderByCreateTime().desc()
|
||||
.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize());
|
||||
|
||||
// 获得 Form Map
|
||||
|
@ -107,6 +111,7 @@ public class BpmModelServiceImpl implements BpmModelService {
|
|||
// 创建流程定义
|
||||
Model model = repositoryService.newModel();
|
||||
BpmModelConvert.INSTANCE.copy(model, createReqVO);
|
||||
model.setTenantId(FlowableUtils.getTenantId());
|
||||
// 保存流程定义
|
||||
repositoryService.saveModel(model);
|
||||
// 保存 BPMN XML
|
||||
|
|
|
@ -6,6 +6,7 @@ import cn.hutool.core.util.StrUtil;
|
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.PageUtils;
|
||||
import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.dynamic.TenantDS;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionListReqVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageItemRespVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO;
|
||||
|
@ -49,6 +50,7 @@ import static java.util.Collections.emptyList;
|
|||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
@TenantDS // 工作流的 Service 必须添加 @TenantDS 注解。原因是,Flowable 使用事务,无法切换数据源,需要提使用 @TenantDS 切到它的数据源
|
||||
public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionService {
|
||||
|
||||
private static final String BPMN_FILE_SUFFIX = ".bpmn";
|
||||
|
@ -124,6 +126,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
|
|||
Deployment deploy = repositoryService.createDeployment()
|
||||
.key(createReqDTO.getKey()).name(createReqDTO.getName()).category(createReqDTO.getCategory())
|
||||
.addBytes(createReqDTO.getKey() + BPMN_FILE_SUFFIX, createReqDTO.getBpmnBytes())
|
||||
.tenantId(FlowableUtils.getTenantId())
|
||||
.deploy();
|
||||
|
||||
// 设置 ProcessDefinition 的 category 分类
|
||||
|
@ -234,6 +237,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
|
|||
definitionQuery.active();
|
||||
}
|
||||
// 执行查询
|
||||
definitionQuery.processDefinitionTenantId(FlowableUtils.getTenantId());
|
||||
List<ProcessDefinition> processDefinitions = definitionQuery.list();
|
||||
if (CollUtil.isEmpty(processDefinitions)) {
|
||||
return Collections.emptyList();
|
||||
|
|
|
@ -8,6 +8,7 @@ import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
|||
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
||||
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
|
||||
import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.dynamic.TenantDS;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleCreateReqVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleRespVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.rule.BpmTaskAssignRuleUpdateReqVO;
|
||||
|
@ -53,6 +54,7 @@ import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
|
|||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
@TenantDS // 工作流的 Service 必须添加 @TenantDS 注解。原因是,Flowable 使用事务,无法切换数据源,需要提使用 @TenantDS 切到它的数据源
|
||||
public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
|
||||
|
||||
@Resource
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package cn.iocoder.yudao.module.bpm.service.definition;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.dynamic.TenantDS;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupCreateReqVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupPageReqVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupUpdateReqVO;
|
||||
|
@ -30,6 +31,7 @@ import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
|
|||
*/
|
||||
@Service
|
||||
@Validated
|
||||
@TenantDS // 工作流的 Service 必须添加 @TenantDS 注解。原因是,Flowable 使用事务,无法切换数据源,需要提使用 @TenantDS 切到它的数据源
|
||||
public class BpmUserGroupServiceImpl implements BpmUserGroupService {
|
||||
|
||||
@Resource
|
||||
|
|
|
@ -8,7 +8,10 @@ import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcess
|
|||
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.sms.SmsSendApi;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
@ -32,6 +35,7 @@ public class BpmMessageServiceImpl implements BpmMessageService {
|
|||
private WebProperties webProperties;
|
||||
|
||||
@Override
|
||||
@Async // 必须使用异步,因为是在 Flowable 事务内调用,无法切换数据源
|
||||
public void sendMessageWhenProcessInstanceApprove(BpmMessageSendWhenProcessInstanceApproveReqDTO reqDTO) {
|
||||
Map<String, Object> templateParams = new HashMap<>();
|
||||
templateParams.put("processInstanceName", reqDTO.getProcessInstanceName());
|
||||
|
@ -41,6 +45,7 @@ public class BpmMessageServiceImpl implements BpmMessageService {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Async // 必须使用异步,因为是在 Flowable 事务内调用,无法切换数据源
|
||||
public void sendMessageWhenProcessInstanceReject(BpmMessageSendWhenProcessInstanceRejectReqDTO reqDTO) {
|
||||
Map<String, Object> templateParams = new HashMap<>();
|
||||
templateParams.put("processInstanceName", reqDTO.getProcessInstanceName());
|
||||
|
@ -51,6 +56,7 @@ public class BpmMessageServiceImpl implements BpmMessageService {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Async // 必须使用异步,因为是在 Flowable 事务内调用,无法切换数据源
|
||||
public void sendMessageWhenTaskAssigned(BpmMessageSendWhenTaskCreatedReqDTO reqDTO) {
|
||||
Map<String, Object> templateParams = new HashMap<>();
|
||||
templateParams.put("processInstanceName", reqDTO.getProcessInstanceName());
|
||||
|
|
|
@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.bpm.service.oa;
|
|||
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.dynamic.TenantDS;
|
||||
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
|
||||
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.BpmOALeaveCreateReqVO;
|
||||
|
@ -29,6 +30,7 @@ import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.OA_LEAVE_NOT_
|
|||
*/
|
||||
@Service
|
||||
@Validated
|
||||
@TenantDS // 工作流的 Service 必须添加 @TenantDS 注解。原因是,Flowable 使用事务,无法切换数据源,需要提使用 @TenantDS 切到它的数据源
|
||||
public class BpmOALeaveServiceImpl implements BpmOALeaveService {
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package cn.iocoder.yudao.module.bpm.service.task;
|
||||
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.dynamic.TenantDS;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.activity.BpmActivityRespVO;
|
||||
import cn.iocoder.yudao.module.bpm.convert.task.BpmActivityConvert;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -20,6 +21,7 @@ import java.util.List;
|
|||
@Service
|
||||
@Slf4j
|
||||
@Validated
|
||||
@TenantDS // 工作流的 Service 必须添加 @TenantDS 注解。原因是,Flowable 使用事务,无法切换数据源,需要提使用 @TenantDS 切到它的数据源
|
||||
public class BpmActivityServiceImpl implements BpmActivityService {
|
||||
|
||||
@Resource
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -8,6 +8,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|||
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.PageUtils;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.dynamic.TenantDS;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
|
||||
import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
|
||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO;
|
||||
|
@ -51,6 +52,7 @@ import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
|
|||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@TenantDS // 工作流的 Service 必须添加 @TenantDS 注解。原因是,Flowable 使用事务,无法切换数据源,需要提使用 @TenantDS 切到它的数据源
|
||||
public class BpmTaskServiceImpl implements BpmTaskService {
|
||||
|
||||
@Resource
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
package cn.iocoder.yudao.module.bpm.service.task;
|
|
@ -0,0 +1,20 @@
|
|||
package cn.iocoder.yudao.module.infra.api.db;
|
||||
|
||||
import cn.iocoder.yudao.module.infra.api.db.dto.DataSourceConfigRespDTO;
|
||||
|
||||
/**
|
||||
* 数据源配置 API 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface DataSourceConfigServiceApi {
|
||||
|
||||
/**
|
||||
* 获得数据源配置
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 数据源配置
|
||||
*/
|
||||
DataSourceConfigRespDTO getDataSourceConfig(Long id);
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package cn.iocoder.yudao.module.infra.api.db.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 数据源配置 Response DTO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class DataSourceConfigRespDTO {
|
||||
|
||||
/**
|
||||
* 主键编号
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 连接名
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 数据源连接
|
||||
*/
|
||||
private String url;
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private String password;
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package cn.iocoder.yudao.module.infra.api.db;
|
||||
|
||||
import cn.iocoder.yudao.module.infra.api.db.dto.DataSourceConfigRespDTO;
|
||||
import cn.iocoder.yudao.module.infra.convert.db.DataSourceConfigConvert;
|
||||
import cn.iocoder.yudao.module.infra.service.db.DataSourceConfigService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 数据源配置 API 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
public class DataSourceConfigServiceApiImpl implements DataSourceConfigServiceApi {
|
||||
|
||||
@Resource
|
||||
private DataSourceConfigService dataSourceConfigService;
|
||||
|
||||
@Override
|
||||
public DataSourceConfigRespDTO getDataSourceConfig(Long id) {
|
||||
return DataSourceConfigConvert.INSTANCE.convert02(dataSourceConfigService.getDataSourceConfig(id));
|
||||
}
|
||||
|
||||
}
|
|
@ -3,13 +3,14 @@ package cn.iocoder.yudao.module.infra.controller.admin.db;
|
|||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigCreateReqVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigRespVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigSimpleRespVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.infra.convert.db.DataSourceConfigConvert;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;
|
||||
import cn.iocoder.yudao.module.infra.service.db.DataSourceConfigService;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
@ -70,4 +71,11 @@ public class DataSourceConfigController {
|
|||
return success(DataSourceConfigConvert.INSTANCE.convertList(list));
|
||||
}
|
||||
|
||||
@GetMapping("/list-all-simple")
|
||||
@Operation(summary = "获取数据源配置精简信息列表", description = "主要用于前端的下拉选项")
|
||||
public CommonResult<List<DataSourceConfigSimpleRespVO>> getSimpleDataSourceConfigList() {
|
||||
List<DataSourceConfigDO> list = dataSourceConfigService.getDataSourceConfigList();
|
||||
return success(DataSourceConfigConvert.INSTANCE.convertList02(list));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
package cn.iocoder.yudao.module.infra.controller.admin.db.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 数据源配置的精简 Response VO")
|
||||
@Data
|
||||
public class DataSourceConfigSimpleRespVO {
|
||||
|
||||
@Schema(description = "主键编号", required = true, example = "1024")
|
||||
private Integer id;
|
||||
|
||||
@Schema(description = "数据源名称", required = true, example = "test")
|
||||
private String name;
|
||||
|
||||
}
|
|
@ -3,15 +3,12 @@ package cn.iocoder.yudao.module.infra.controller.admin.redis;
|
|||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;
|
||||
import cn.iocoder.yudao.framework.redis.core.RedisKeyRegistry;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisKeyDefineRespVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisKeyValueRespVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisMonitorRespVO;
|
||||
import cn.iocoder.yudao.module.infra.convert.redis.RedisConvert;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.data.redis.connection.RedisServerCommands;
|
||||
import org.springframework.data.redis.core.Cursor;
|
||||
import org.springframework.data.redis.core.RedisCallback;
|
||||
|
@ -50,9 +47,9 @@ public class RedisController {
|
|||
@GetMapping("/get-key-define-list")
|
||||
@Operation(summary = "获得 Redis Key 模板列表")
|
||||
@PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')")
|
||||
public CommonResult<List<RedisKeyDefineRespVO>> getKeyDefineList() {
|
||||
List<RedisKeyDefine> keyDefines = RedisKeyRegistry.list();
|
||||
return success(RedisConvert.INSTANCE.convertList(keyDefines));
|
||||
@Deprecated // 建议使用 https://blog.jetbrains.com/datagrip/2022/11/02/datagrip-2022-3-eap-2-redis-support/ 连接 Redis 查询
|
||||
public CommonResult<List<Object>> getKeyDefineList() {
|
||||
return success(Collections.emptyList());
|
||||
}
|
||||
|
||||
@GetMapping("/get-key-list")
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
package cn.iocoder.yudao.module.infra.controller.admin.redis.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
@Schema(description = "管理后台 - Redis Key 信息 Response VO")
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
public class RedisKeyDefineRespVO {
|
||||
|
||||
@Schema(description = "Key 模板", required = true, example = "login_user:%s")
|
||||
private String keyTemplate;
|
||||
|
||||
@Schema(description = "Key 类型的枚举", required = true, example = "String")
|
||||
private RedisKeyDefine.KeyTypeEnum keyType;
|
||||
|
||||
@Schema(description = "Value 类型", required = true, example = "java.lang.String")
|
||||
private Class<?> valueType;
|
||||
|
||||
@Schema(description = "超时类型", required = true, example = "1")
|
||||
private RedisKeyDefine.TimeoutTypeEnum timeoutType;
|
||||
|
||||
@Schema(description = "过期时间,单位:毫秒", required = true, example = "1024")
|
||||
private Duration timeout;
|
||||
|
||||
@Schema(description = "备注", required = true, example = "啦啦啦啦~")
|
||||
private String memo;
|
||||
|
||||
}
|
|
@ -4,6 +4,7 @@ import java.util.*;
|
|||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
|
||||
import cn.iocoder.yudao.module.infra.api.db.dto.DataSourceConfigRespDTO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.db.vo.*;
|
||||
|
@ -27,4 +28,8 @@ public interface DataSourceConfigConvert {
|
|||
|
||||
List<DataSourceConfigRespVO> convertList(List<DataSourceConfigDO> list);
|
||||
|
||||
DataSourceConfigRespDTO convert02(DataSourceConfigDO bean);
|
||||
|
||||
List<DataSourceConfigSimpleRespVO> convertList02(List<DataSourceConfigDO> list);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
package cn.iocoder.yudao.module.infra.convert.redis;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisKeyDefineRespVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.redis.vo.RedisMonitorRespVO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
@Mapper
|
||||
|
@ -29,6 +26,4 @@ public interface RedisConvert {
|
|||
return respVO;
|
||||
}
|
||||
|
||||
List<RedisKeyDefineRespVO> convertList(List<RedisKeyDefine> list);
|
||||
|
||||
}
|
||||
|
|
|
@ -24,11 +24,7 @@ public class SecurityConfiguration {
|
|||
public void customize(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry) {
|
||||
// Swagger 接口文档
|
||||
registry.antMatchers("/v3/api-docs/**").permitAll()
|
||||
.antMatchers("/swagger-ui.html").permitAll()
|
||||
.antMatchers("/swagger-ui/**").permitAll()
|
||||
.antMatchers("/swagger-resources/**").anonymous()
|
||||
.antMatchers("/webjars/**").anonymous()
|
||||
.antMatchers("/*/api-docs").anonymous();
|
||||
.antMatchers("/swagger-ui.html").permitAll();
|
||||
// 积木报表
|
||||
registry.antMatchers("/jmreport/**").permitAll();
|
||||
// Spring Boot Actuator 的安全配置
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package cn.iocoder.yudao.module.pay.dal.redis;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;
|
||||
import org.redisson.api.RLock;
|
||||
|
||||
/**
|
||||
|
@ -10,8 +9,13 @@ import org.redisson.api.RLock;
|
|||
*/
|
||||
public interface RedisKeyConstants {
|
||||
|
||||
RedisKeyDefine PAY_NOTIFY_LOCK = new RedisKeyDefine("通知任务的分布式锁",
|
||||
"pay_notify:lock:", // 参数来自 DefaultLockKeyBuilder 类
|
||||
RedisKeyDefine.KeyTypeEnum.HASH, RLock.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); // Redisson 的 Lock 锁,使用 Hash 数据结构
|
||||
/**
|
||||
* 通知任务的分布式锁
|
||||
*
|
||||
* KEY 格式:pay_notify:{id}
|
||||
* VALUE 数据类型:HASH {@link RLock}
|
||||
* 过期时间:动态传参
|
||||
*/
|
||||
String PAY_NOTIFY_LOCK = "pay_notify:lock:%s";
|
||||
|
||||
}
|
||||
|
|
|
@ -20,6 +20,13 @@ public class PayNotifyLockRedisDAO {
|
|||
@Resource
|
||||
private RedissonClient redissonClient;
|
||||
|
||||
/**
|
||||
* 获得分布式锁,执行逻辑
|
||||
*
|
||||
* @param id 通知任务编号
|
||||
* @param timeoutMillis 过期时间
|
||||
* @param runnable 逻辑
|
||||
*/
|
||||
public void lock(Long id, Long timeoutMillis, Runnable runnable) {
|
||||
String lockKey = formatKey(id);
|
||||
RLock lock = redissonClient.getLock(lockKey);
|
||||
|
@ -33,7 +40,7 @@ public class PayNotifyLockRedisDAO {
|
|||
}
|
||||
|
||||
private static String formatKey(Long id) {
|
||||
return String.format(PAY_NOTIFY_LOCK.getKeyTemplate(), id);
|
||||
return String.format(PAY_NOTIFY_LOCK, id);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package cn.iocoder.yudao.module.system.api.tenant;
|
||||
|
||||
import cn.iocoder.yudao.module.system.api.tenant.dto.TenantDataSourceConfigRespDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -23,4 +25,12 @@ public interface TenantApi {
|
|||
*/
|
||||
void validateTenant(Long id);
|
||||
|
||||
/**
|
||||
* 获得租户的数据源配置
|
||||
*
|
||||
* @param tenantId 租户编号
|
||||
* @return 数据源配置
|
||||
*/
|
||||
TenantDataSourceConfigRespDTO getTenantDataSourceConfig(Long tenantId);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package cn.iocoder.yudao.module.system.api.tenant.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 多租户的数据源配置 Response DTO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class TenantDataSourceConfigRespDTO {
|
||||
|
||||
/**
|
||||
* 连接名
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 数据源连接
|
||||
*/
|
||||
private String url;
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private String password;
|
||||
|
||||
}
|
|
@ -7,7 +7,7 @@ import lombok.Getter;
|
|||
* 短信的接收状态枚举
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @date 2021/2/1 13:39
|
||||
* @since 2021/2/1 13:39
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
|
|
|
@ -7,7 +7,7 @@ import lombok.Getter;
|
|||
* 短信的发送状态枚举
|
||||
*
|
||||
* @author zzf
|
||||
* @date 2021/2/1 13:39
|
||||
* @since 2021/2/1 13:39
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
|
|
|
@ -21,7 +21,7 @@ public class PermissionApiImpl implements PermissionApi {
|
|||
|
||||
@Override
|
||||
public Set<Long> getUserRoleIdListByRoleIds(Collection<Long> roleIds) {
|
||||
return permissionService.getUserRoleIdListByRoleIds(roleIds);
|
||||
return permissionService.getUserRoleIdListByRoleId(roleIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package cn.iocoder.yudao.module.system.api.tenant;
|
||||
|
||||
import cn.iocoder.yudao.module.infra.api.db.DataSourceConfigServiceApi;
|
||||
import cn.iocoder.yudao.module.system.api.tenant.dto.TenantDataSourceConfigRespDTO;
|
||||
import cn.iocoder.yudao.module.system.convert.tenant.TenantConvert;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
|
||||
import cn.iocoder.yudao.module.system.service.tenant.TenantService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
@ -17,6 +21,9 @@ public class TenantApiImpl implements TenantApi {
|
|||
@Resource
|
||||
private TenantService tenantService;
|
||||
|
||||
@Resource
|
||||
private DataSourceConfigServiceApi dataSourceConfigServiceApi;
|
||||
|
||||
@Override
|
||||
public List<Long> getTenantIdList() {
|
||||
return tenantService.getTenantIdList();
|
||||
|
@ -27,4 +34,16 @@ public class TenantApiImpl implements TenantApi {
|
|||
tenantService.validTenant(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TenantDataSourceConfigRespDTO getTenantDataSourceConfig(Long tenantId) {
|
||||
// 获得租户信息
|
||||
TenantDO tenant = tenantService.getTenant(tenantId);
|
||||
if (tenant == null) {
|
||||
return null;
|
||||
}
|
||||
// 获得租户的数据源配置
|
||||
return TenantConvert.INSTANCE.convert(
|
||||
dataSourceConfigServiceApi.getDataSourceConfig(tenant.getDataSourceConfigId()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,9 +24,3 @@ tenant-id: {{adminTenentId}}
|
|||
GET {{baseUrl}}/system/auth/get-permission-info
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
||||
|
||||
### 请求 /list-menus 接口 => 成功
|
||||
GET {{baseUrl}}/system/list-menus
|
||||
Authorization: Bearer {{token}}
|
||||
#Authorization: Bearer a6aa7714a2e44c95aaa8a2c5adc2a67a
|
||||
tenant-id: {{adminTenentId}}
|
||||
|
|
|
@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.system.controller.admin.auth;
|
|||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
|
||||
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
|
||||
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*;
|
||||
|
@ -12,16 +11,16 @@ import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
|
|||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
||||
import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
|
||||
import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum;
|
||||
import cn.iocoder.yudao.module.system.service.auth.AdminAuthService;
|
||||
import cn.iocoder.yudao.module.system.service.permission.MenuService;
|
||||
import cn.iocoder.yudao.module.system.service.permission.PermissionService;
|
||||
import cn.iocoder.yudao.module.system.service.permission.RoleService;
|
||||
import cn.iocoder.yudao.module.system.service.social.SocialUserService;
|
||||
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
@ -34,9 +33,9 @@ import java.util.List;
|
|||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.obtainAuthorization;
|
||||
import static java.util.Collections.singleton;
|
||||
|
||||
@Tag(name = "管理后台 - 认证")
|
||||
@RestController
|
||||
|
@ -52,6 +51,8 @@ public class AuthController {
|
|||
@Resource
|
||||
private RoleService roleService;
|
||||
@Resource
|
||||
private MenuService menuService;
|
||||
@Resource
|
||||
private PermissionService permissionService;
|
||||
@Resource
|
||||
private SocialUserService socialUserService;
|
||||
|
@ -90,33 +91,24 @@ public class AuthController {
|
|||
@GetMapping("/get-permission-info")
|
||||
@Operation(summary = "获取登录用户的权限信息")
|
||||
public CommonResult<AuthPermissionInfoRespVO> getPermissionInfo() {
|
||||
// 获得用户信息
|
||||
// 1.1 获得用户信息
|
||||
AdminUserDO user = userService.getUser(getLoginUserId());
|
||||
if (user == null) {
|
||||
return null;
|
||||
}
|
||||
// 获得角色列表
|
||||
Set<Long> roleIds = permissionService.getUserRoleIdsFromCache(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus()));
|
||||
List<RoleDO> roleList = roleService.getRoleListFromCache(roleIds);
|
||||
// 获得菜单列表
|
||||
List<MenuDO> menuList = permissionService.getRoleMenuListFromCache(roleIds,
|
||||
SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType(), MenuTypeEnum.BUTTON.getType()),
|
||||
singleton(CommonStatusEnum.ENABLE.getStatus())); // 只要开启的
|
||||
// 拼接结果返回
|
||||
return success(AuthConvert.INSTANCE.convert(user, roleList, menuList));
|
||||
}
|
||||
|
||||
@GetMapping("/list-menus")
|
||||
@Operation(summary = "获得登录用户的菜单列表")
|
||||
public CommonResult<List<AuthMenuRespVO>> getMenuList() {
|
||||
// 获得角色列表
|
||||
Set<Long> roleIds = permissionService.getUserRoleIdsFromCache(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus()));
|
||||
// 获得用户拥有的菜单列表
|
||||
List<MenuDO> menuList = permissionService.getRoleMenuListFromCache(roleIds,
|
||||
SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType()), // 只要目录和菜单类型
|
||||
singleton(CommonStatusEnum.ENABLE.getStatus())); // 只要开启的
|
||||
// 转换成 Tree 结构返回
|
||||
return success(AuthConvert.INSTANCE.buildMenuTree(menuList));
|
||||
// 1.2 获得角色列表
|
||||
Set<Long> roleIds = permissionService.getUserRoleIdListByUserId(getLoginUserId());
|
||||
List<RoleDO> roles = roleService.getRoleList(roleIds);
|
||||
roles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())); // 移除禁用的角色
|
||||
|
||||
// 1.3 获得菜单列表
|
||||
Set<Long> menuIds = permissionService.getRoleMenuListByRoleId(convertSet(roles, RoleDO::getId));
|
||||
List<MenuDO> menuList = menuService.getMenuList(menuIds);
|
||||
menuList.removeIf(menu -> !CommonStatusEnum.ENABLE.getStatus().equals(menu.getStatus())); // 移除禁用的菜单
|
||||
|
||||
// 2. 拼接结果返回
|
||||
return success(AuthConvert.INSTANCE.convert(user, roles, menuList));
|
||||
}
|
||||
|
||||
// ========== 短信登录相关 ==========
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
package cn.iocoder.yudao.module.system.controller.admin.auth.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 登录用户的菜单信息 Response VO")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class AuthMenuRespVO {
|
||||
|
||||
@Schema(description = "菜单名称", required = true, example = "芋道")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "父菜单 ID", required = true, example = "1024")
|
||||
private Long parentId;
|
||||
|
||||
@Schema(description = "菜单名称", required = true, example = "芋道")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "路由地址,仅菜单类型为菜单或者目录时,才需要传", example = "post")
|
||||
private String path;
|
||||
|
||||
@Schema(description = "组件路径,仅菜单类型为菜单时,才需要传", example = "system/post/index")
|
||||
private String component;
|
||||
|
||||
@Schema(description = "组件名", example = "SystemUser")
|
||||
private String componentName;
|
||||
|
||||
@Schema(description = "菜单图标,仅菜单类型为菜单或者目录时,才需要传", example = "/menu/list")
|
||||
private String icon;
|
||||
|
||||
@Schema(description = "是否可见", required = true, example = "false")
|
||||
private Boolean visible;
|
||||
|
||||
@Schema(description = "是否缓存", required = true, example = "false")
|
||||
private Boolean keepAlive;
|
||||
|
||||
@Schema(description = "是否总是显示", example = "false")
|
||||
private Boolean alwaysShow;
|
||||
|
||||
/**
|
||||
* 子路由
|
||||
*/
|
||||
private List<AuthMenuRespVO> children;
|
||||
|
||||
}
|
|
@ -6,6 +6,7 @@ import lombok.Builder;
|
|||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Schema(description = "管理后台 - 登录用户的权限信息 Response VO,额外包括用户信息和角色列表")
|
||||
|
@ -24,6 +25,9 @@ public class AuthPermissionInfoRespVO {
|
|||
@Schema(description = "操作权限数组", required = true)
|
||||
private Set<String> permissions;
|
||||
|
||||
@Schema(description = "菜单树", required = true)
|
||||
private List<MenuVO> menus;
|
||||
|
||||
@Schema(description = "用户信息 VO")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
|
@ -37,9 +41,53 @@ public class AuthPermissionInfoRespVO {
|
|||
@Schema(description = "用户昵称", required = true, example = "芋道源码")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "用户头像", required = true, example = "http://www.iocoder.cn/xx.jpg")
|
||||
@Schema(description = "用户头像", required = true, example = "https://www.iocoder.cn/xx.jpg")
|
||||
private String avatar;
|
||||
|
||||
}
|
||||
|
||||
@Schema(description = "管理后台 - 登录用户的菜单信息 Response VO")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public static class MenuVO {
|
||||
|
||||
@Schema(description = "菜单名称", required = true, example = "芋道")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "父菜单 ID", required = true, example = "1024")
|
||||
private Long parentId;
|
||||
|
||||
@Schema(description = "菜单名称", required = true, example = "芋道")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "路由地址,仅菜单类型为菜单或者目录时,才需要传", example = "post")
|
||||
private String path;
|
||||
|
||||
@Schema(description = "组件路径,仅菜单类型为菜单时,才需要传", example = "system/post/index")
|
||||
private String component;
|
||||
|
||||
@Schema(description = "组件名", example = "SystemUser")
|
||||
private String componentName;
|
||||
|
||||
@Schema(description = "菜单图标,仅菜单类型为菜单或者目录时,才需要传", example = "/menu/list")
|
||||
private String icon;
|
||||
|
||||
@Schema(description = "是否可见", required = true, example = "false")
|
||||
private Boolean visible;
|
||||
|
||||
@Schema(description = "是否缓存", required = true, example = "false")
|
||||
private Boolean keepAlive;
|
||||
|
||||
@Schema(description = "是否总是显示", example = "false")
|
||||
private Boolean alwaysShow;
|
||||
|
||||
/**
|
||||
* 子路由
|
||||
*/
|
||||
private List<MenuVO> children;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -37,10 +37,10 @@ public class PermissionController {
|
|||
|
||||
@Operation(summary = "获得角色拥有的菜单编号")
|
||||
@Parameter(name = "roleId", description = "角色编号", required = true)
|
||||
@GetMapping("/list-role-resources")
|
||||
@GetMapping("/list-role-menus")
|
||||
@PreAuthorize("@ss.hasPermission('system:permission:assign-role-menu')")
|
||||
public CommonResult<Set<Long>> listRoleMenus(Long roleId) {
|
||||
return success(permissionService.getRoleMenuIds(roleId));
|
||||
public CommonResult<Set<Long>> getRoleMenuList(Long roleId) {
|
||||
return success(permissionService.getRoleMenuListByRoleId(roleId));
|
||||
}
|
||||
|
||||
@PostMapping("/assign-role-menu")
|
||||
|
|
|
@ -13,7 +13,7 @@ tenant-id: {{adminTenentId}}
|
|||
"contactMobile": "15601691300",
|
||||
"status": 0,
|
||||
"domain": "https://www.iocoder.cn",
|
||||
"packageId": 110,
|
||||
"packageId": 111,
|
||||
"expireTime": 1699545600000,
|
||||
"accountCount": 20,
|
||||
"username": "admin",
|
||||
|
|
|
@ -43,4 +43,8 @@ public class TenantBaseVO {
|
|||
@NotNull(message = "账号数量不能为空")
|
||||
private Integer accountCount;
|
||||
|
||||
@Schema(description = "数据源配置编号", required = true, example = "4096")
|
||||
@NotNull(message = "数据源配置编号不能为空")
|
||||
private Long dataSourceConfigId;
|
||||
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ public class UserProfileController {
|
|||
AdminUserDO user = userService.getUser(getLoginUserId());
|
||||
UserProfileRespVO resp = UserConvert.INSTANCE.convert03(user);
|
||||
// 获得用户角色
|
||||
List<RoleDO> userRoles = roleService.getRoleListFromCache(permissionService.getUserRoleIdListByUserId(user.getId()));
|
||||
List<RoleDO> userRoles = roleService.getRoleList(permissionService.getUserRoleIdListByUserId(user.getId()));
|
||||
resp.setRoles(UserConvert.INSTANCE.convertList(userRoles));
|
||||
// 获得部门信息
|
||||
if (user.getDeptId() != null) {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package cn.iocoder.yudao.module.system.convert.auth;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
|
||||
|
@ -9,12 +8,14 @@ import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
|
|||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
||||
import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
|
||||
import static cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO.ID_ROOT;
|
||||
|
||||
|
@ -27,13 +28,19 @@ public interface AuthConvert {
|
|||
|
||||
default AuthPermissionInfoRespVO convert(AdminUserDO user, List<RoleDO> roleList, List<MenuDO> menuList) {
|
||||
return AuthPermissionInfoRespVO.builder()
|
||||
.user(AuthPermissionInfoRespVO.UserVO.builder().id(user.getId()).nickname(user.getNickname()).avatar(user.getAvatar()).build())
|
||||
.roles(CollectionUtils.convertSet(roleList, RoleDO::getCode))
|
||||
.permissions(CollectionUtils.convertSet(menuList, MenuDO::getPermission))
|
||||
// 用户信息
|
||||
.user(AuthPermissionInfoRespVO.UserVO.builder().id(user.getId())
|
||||
.nickname(user.getNickname()).avatar(user.getAvatar()).build())
|
||||
// 角色信息
|
||||
.roles(convertSet(roleList, RoleDO::getCode))
|
||||
// 权限标识信息
|
||||
.permissions(convertSet(menuList, MenuDO::getPermission))
|
||||
// 菜单树
|
||||
.menus(buildMenuTree(menuList))
|
||||
.build();
|
||||
}
|
||||
|
||||
AuthMenuRespVO convertTreeNode(MenuDO menu);
|
||||
AuthPermissionInfoRespVO.MenuVO convertTreeNode(MenuDO menu);
|
||||
|
||||
/**
|
||||
* 将菜单列表,构建成菜单树
|
||||
|
@ -41,17 +48,20 @@ public interface AuthConvert {
|
|||
* @param menuList 菜单列表
|
||||
* @return 菜单树
|
||||
*/
|
||||
default List<AuthMenuRespVO> buildMenuTree(List<MenuDO> menuList) {
|
||||
default List<AuthPermissionInfoRespVO.MenuVO> buildMenuTree(List<MenuDO> menuList) {
|
||||
// 移除按钮
|
||||
menuList.removeIf(menu -> menu.getType().equals(MenuTypeEnum.BUTTON.getType()));
|
||||
// 排序,保证菜单的有序性
|
||||
menuList.sort(Comparator.comparing(MenuDO::getSort));
|
||||
|
||||
// 构建菜单树
|
||||
// 使用 LinkedHashMap 的原因,是为了排序 。实际也可以用 Stream API ,就是太丑了。
|
||||
Map<Long, AuthMenuRespVO> treeNodeMap = new LinkedHashMap<>();
|
||||
Map<Long, AuthPermissionInfoRespVO.MenuVO> treeNodeMap = new LinkedHashMap<>();
|
||||
menuList.forEach(menu -> treeNodeMap.put(menu.getId(), AuthConvert.INSTANCE.convertTreeNode(menu)));
|
||||
// 处理父子关系
|
||||
treeNodeMap.values().stream().filter(node -> !node.getParentId().equals(ID_ROOT)).forEach(childNode -> {
|
||||
// 获得父节点
|
||||
AuthMenuRespVO parentNode = treeNodeMap.get(childNode.getParentId());
|
||||
AuthPermissionInfoRespVO.MenuVO parentNode = treeNodeMap.get(childNode.getParentId());
|
||||
if (parentNode == null) {
|
||||
LoggerFactory.getLogger(getClass()).error("[buildRouterTree][resource({}) 找不到父资源({})]",
|
||||
childNode.getId(), childNode.getParentId());
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package cn.iocoder.yudao.module.system.convert.tenant;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.infra.api.db.dto.DataSourceConfigRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.tenant.dto.TenantDataSourceConfigRespDTO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantCreateReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantExcelVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantRespVO;
|
||||
|
@ -42,4 +44,6 @@ public interface TenantConvert {
|
|||
return reqVO;
|
||||
}
|
||||
|
||||
TenantDataSourceConfigRespDTO convert(DataSourceConfigRespDTO bean);
|
||||
|
||||
}
|
||||
|
|
|
@ -79,4 +79,13 @@ public class TenantDO extends BaseDO {
|
|||
*/
|
||||
private Integer accountCount;
|
||||
|
||||
/**
|
||||
* 数据源配置编号
|
||||
*
|
||||
* 多租户采用“分库”方案时,通过该字段配置所在数据源
|
||||
*
|
||||
* 关联 DataSourceConfigDO 的 id 字段
|
||||
*/
|
||||
private Long dataSourceConfigId;
|
||||
|
||||
}
|
||||
|
|
|
@ -2,13 +2,16 @@ package cn.iocoder.yudao.module.system.dal.mysql.dept;
|
|||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.dynamic.TenantDS;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
@TenantDS
|
||||
public interface DeptMapper extends BaseMapperX<DeptDO> {
|
||||
|
||||
default List<DeptDO> selectList(DeptListReqVO reqVO) {
|
||||
|
@ -25,4 +28,8 @@ public interface DeptMapper extends BaseMapperX<DeptDO> {
|
|||
return selectCount(DeptDO::getParentId, parentId);
|
||||
}
|
||||
|
||||
default List<DeptDO> selectListByParentId(Collection<Long> parentIds) {
|
||||
return selectList(DeptDO::getParentId, parentIds);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.dal.mysql.dept;
|
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.dynamic.TenantDS;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostExportReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO;
|
||||
|
@ -12,6 +13,7 @@ import java.util.Collection;
|
|||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
@TenantDS
|
||||
public interface PostMapper extends BaseMapperX<PostDO> {
|
||||
|
||||
default List<PostDO> selectList(Collection<Long> ids, Collection<Integer> statuses) {
|
||||
|
|
|
@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.dal.mysql.dept;
|
|||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.dynamic.TenantDS;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.dept.UserPostDO;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
@ -10,6 +11,7 @@ import java.util.Collection;
|
|||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
@TenantDS
|
||||
public interface UserPostMapper extends BaseMapperX<UserPostDO> {
|
||||
|
||||
default List<UserPostDO> selectListByUserId(Long userId) {
|
||||
|
@ -27,6 +29,6 @@ public interface UserPostMapper extends BaseMapperX<UserPostDO> {
|
|||
}
|
||||
|
||||
default void deleteByUserId(Long userId) {
|
||||
delete(Wrappers.lambdaUpdate(UserPostDO.class).eq(UserPostDO::getUserId, userId));
|
||||
delete(new LambdaQueryWrapperX<UserPostDO>().eq(UserPostDO::getUserId, userId));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
|||
import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataExportReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO;
|
||||
import com.baomidou.dynamic.datasource.annotation.Master;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
|
@ -14,6 +15,7 @@ import java.util.Collection;
|
|||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
@Master
|
||||
public interface DictDataMapper extends BaseMapperX<DictDataDO> {
|
||||
|
||||
default DictDataDO selectByDictTypeAndValue(String dictType, String value) {
|
||||
|
|
|
@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
|||
import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypeExportReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypePageReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO;
|
||||
import com.baomidou.dynamic.datasource.annotation.Master;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
|
@ -14,6 +15,7 @@ import java.time.LocalDateTime;
|
|||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
@Master
|
||||
public interface DictTypeMapper extends BaseMapperX<DictTypeDO> {
|
||||
|
||||
default PageResult<DictTypeDO> selectPage(DictTypePageReqVO reqVO) {
|
||||
|
|
|
@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
|||
import cn.iocoder.yudao.module.system.controller.admin.errorcode.vo.ErrorCodeExportReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.errorcode.vo.ErrorCodePageReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.errorcode.ErrorCodeDO;
|
||||
import com.baomidou.dynamic.datasource.annotation.Master;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
@ -13,6 +14,7 @@ import java.util.Collection;
|
|||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
@Master
|
||||
public interface ErrorCodeMapper extends BaseMapperX<ErrorCodeDO> {
|
||||
|
||||
default PageResult<ErrorCodeDO> selectPage(ErrorCodePageReqVO reqVO) {
|
||||
|
|
|
@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.dal.mysql.logger;
|
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.dynamic.TenantDS;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.logger.vo.loginlog.LoginLogExportReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.logger.vo.loginlog.LoginLogPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.logger.LoginLogDO;
|
||||
|
@ -12,6 +13,7 @@ import org.apache.ibatis.annotations.Mapper;
|
|||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
@TenantDS
|
||||
public interface LoginLogMapper extends BaseMapperX<LoginLogDO> {
|
||||
|
||||
default PageResult<LoginLogDO> selectPage(LoginLogPageReqVO reqVO) {
|
||||
|
|
|
@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstant
|
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.dynamic.TenantDS;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogExportReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO;
|
||||
|
@ -13,6 +14,7 @@ import java.util.Collection;
|
|||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
@TenantDS
|
||||
public interface OperateLogMapper extends BaseMapperX<OperateLogDO> {
|
||||
|
||||
default PageResult<OperateLogDO> selectPage(OperateLogPageReqVO reqVO, Collection<Long> userIds) {
|
||||
|
|
|
@ -6,9 +6,11 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
|||
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
|
||||
import com.baomidou.dynamic.datasource.annotation.Master;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
@Master
|
||||
public interface MailAccountMapper extends BaseMapperX<MailAccountDO> {
|
||||
|
||||
default PageResult<MailAccountDO> selectPage(MailAccountPageReqVO pageReqVO) {
|
||||
|
|
|
@ -5,9 +5,11 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
|||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO;
|
||||
import com.baomidou.dynamic.datasource.annotation.Master;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
@Master
|
||||
public interface MailLogMapper extends BaseMapperX<MailLogDO> {
|
||||
|
||||
default PageResult<MailLogDO> selectPage(MailLogPageReqVO reqVO) {
|
||||
|
|
|
@ -7,12 +7,14 @@ import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
|
|||
import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
|
||||
import com.baomidou.dynamic.datasource.annotation.Master;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Mapper
|
||||
@Master
|
||||
public interface MailTemplateMapper extends BaseMapperX<MailTemplateDO> {
|
||||
|
||||
default PageResult<MailTemplateDO> selectPage(MailTemplatePageReqVO pageReqVO){
|
||||
|
|
|
@ -3,11 +3,13 @@ package cn.iocoder.yudao.module.system.dal.mysql.notice;
|
|||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.dynamic.TenantDS;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticePageReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.notice.NoticeDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
@TenantDS
|
||||
public interface NoticeMapper extends BaseMapperX<NoticeDO> {
|
||||
|
||||
default PageResult<NoticeDO> selectPage(NoticePageReqVO reqVO) {
|
||||
|
|
|
@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.dynamic.TenantDS;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageMyPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessagePageReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO;
|
||||
|
@ -14,6 +15,7 @@ import java.util.Collection;
|
|||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
@TenantDS
|
||||
public interface NotifyMessageMapper extends BaseMapperX<NotifyMessageDO> {
|
||||
|
||||
default PageResult<NotifyMessageDO> selectPage(NotifyMessagePageReqVO reqVO) {
|
||||
|
|
|
@ -5,9 +5,11 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
|||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplatePageReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
|
||||
import com.baomidou.dynamic.datasource.annotation.Master;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
@Master
|
||||
public interface NotifyTemplateMapper extends BaseMapperX<NotifyTemplateDO> {
|
||||
|
||||
default NotifyTemplateDO selectByCode(String code) {
|
||||
|
|
|
@ -4,11 +4,13 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
|||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
|
||||
import com.baomidou.dynamic.datasource.annotation.Master;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
@Master
|
||||
public interface MenuMapper extends BaseMapperX<MenuDO> {
|
||||
|
||||
default MenuDO selectByParentIdAndName(Long parentId, String name) {
|
||||
|
@ -25,4 +27,7 @@ public interface MenuMapper extends BaseMapperX<MenuDO> {
|
|||
.eqIfPresent(MenuDO::getStatus, reqVO.getStatus()));
|
||||
}
|
||||
|
||||
default List<MenuDO> selectListByPermission(String permission) {
|
||||
return selectList(MenuDO::getPermission, permission);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,16 +4,20 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.dynamic.TenantDS;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleExportReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
|
||||
import com.baomidou.dynamic.datasource.annotation.Master;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
@TenantDS
|
||||
public interface RoleMapper extends BaseMapperX<RoleDO> {
|
||||
|
||||
default PageResult<RoleDO> selectPage(RolePageReqVO reqVO) {
|
||||
|
@ -41,7 +45,7 @@ public interface RoleMapper extends BaseMapperX<RoleDO> {
|
|||
return selectOne(RoleDO::getCode, code);
|
||||
}
|
||||
|
||||
default List<RoleDO> selectListByStatus(@Nullable Collection<Integer> statuses) {
|
||||
default List<RoleDO> selectListByStatus(Collection<Integer> statuses) {
|
||||
return selectList(RoleDO::getStatus, statuses);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package cn.iocoder.yudao.module.system.dal.mysql.permission;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.dynamic.TenantDS;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
|
@ -9,18 +10,24 @@ import org.springframework.stereotype.Repository;
|
|||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Mapper
|
||||
@TenantDS
|
||||
public interface RoleMenuMapper extends BaseMapperX<RoleMenuDO> {
|
||||
|
||||
@Repository
|
||||
class BatchInsertMapper extends ServiceImpl<RoleMenuMapper, RoleMenuDO> {
|
||||
}
|
||||
|
||||
default List<RoleMenuDO> selectListByRoleId(Long roleId) {
|
||||
return selectList(RoleMenuDO::getRoleId, roleId);
|
||||
}
|
||||
|
||||
default List<RoleMenuDO> selectListByRoleId(Collection<Long> roleIds) {
|
||||
return selectList(RoleMenuDO::getRoleId, roleIds);
|
||||
}
|
||||
|
||||
default List<RoleMenuDO> selectListByMenuId(Long menuId) {
|
||||
return selectList(RoleMenuDO::getMenuId, menuId);
|
||||
}
|
||||
|
||||
default void deleteListByRoleIdAndMenuIds(Long roleId, Collection<Long> menuIds) {
|
||||
delete(new LambdaQueryWrapper<RoleMenuDO>()
|
||||
.eq(RoleMenuDO::getRoleId, roleId)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package cn.iocoder.yudao.module.system.dal.mysql.permission;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.dynamic.TenantDS;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
@ -9,6 +10,7 @@ import java.util.Collection;
|
|||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
@TenantDS
|
||||
public interface UserRoleMapper extends BaseMapperX<UserRoleDO> {
|
||||
|
||||
default List<UserRoleDO> selectListByUserId(Long userId) {
|
||||
|
|
|
@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
|||
import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordExportReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword.SensitiveWordDO;
|
||||
import com.baomidou.dynamic.datasource.annotation.Master;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -16,6 +17,7 @@ import java.util.List;
|
|||
* @author 永不言败
|
||||
*/
|
||||
@Mapper
|
||||
@Master
|
||||
public interface SensitiveWordMapper extends BaseMapperX<SensitiveWordDO> {
|
||||
|
||||
default PageResult<SensitiveWordDO> selectPage(SensitiveWordPageReqVO reqVO) {
|
||||
|
|
|
@ -5,9 +5,11 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
|||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
|
||||
import com.baomidou.dynamic.datasource.annotation.Master;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
@Master
|
||||
public interface SmsChannelMapper extends BaseMapperX<SmsChannelDO> {
|
||||
|
||||
default PageResult<SmsChannelDO> selectPage(SmsChannelPageReqVO reqVO) {
|
||||
|
|
|
@ -3,9 +3,11 @@ package cn.iocoder.yudao.module.system.dal.mysql.sms;
|
|||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsCodeDO;
|
||||
import com.baomidou.dynamic.datasource.annotation.Master;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
@Master
|
||||
public interface SmsCodeMapper extends BaseMapperX<SmsCodeDO> {
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,11 +6,13 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
|||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogExportReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO;
|
||||
import com.baomidou.dynamic.datasource.annotation.Master;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
@Master
|
||||
public interface SmsLogMapper extends BaseMapperX<SmsLogDO> {
|
||||
|
||||
default PageResult<SmsLogDO> selectPage(SmsLogPageReqVO reqVO) {
|
||||
|
|
|
@ -6,11 +6,13 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
|||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateExportReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
|
||||
import com.baomidou.dynamic.datasource.annotation.Master;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
@Master
|
||||
public interface SmsTemplateMapper extends BaseMapperX<SmsTemplateDO> {
|
||||
|
||||
default SmsTemplateDO selectByCode(String code) {
|
||||
|
|
|
@ -2,12 +2,14 @@ package cn.iocoder.yudao.module.system.dal.mysql.social;
|
|||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.dynamic.TenantDS;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
@TenantDS
|
||||
public interface SocialUserBindMapper extends BaseMapperX<SocialUserBindDO> {
|
||||
|
||||
default void deleteByUserTypeAndUserIdAndSocialType(Integer userType, Long userId, Integer socialType) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package cn.iocoder.yudao.module.system.dal.mysql.social;
|
||||
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.dynamic.TenantDS;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
|
@ -10,6 +11,7 @@ import java.util.Collection;
|
|||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
@TenantDS
|
||||
public interface SocialUserMapper extends BaseMapperX<SocialUserDO> {
|
||||
|
||||
default SocialUserDO selectByTypeAndCodeAnState(Integer type, String code, String state) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue