Pre Merge pull request !29 from piyalin/DbSeparate

pull/29/MERGE
piyalin 2023-04-02 04:51:40 +00:00 committed by Gitee
commit 1c0469f617
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
164 changed files with 2428 additions and 3341 deletions

View File

@ -34,7 +34,9 @@
<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>
<servlet.versoin>2.5</servlet.versoin>
<swagger.version>2.2.8</swagger.version>
<mapstruct.version>1.5.3.Final</mapstruct.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

View File

@ -16,7 +16,7 @@
<properties>
<revision>1.7.1-snapshot</revision>
<!-- 统一依赖管理 -->
<spring.boot.version>2.7.8</spring.boot.version>
<spring.boot.version>2.7.9</spring.boot.version>
<spring.cloud.version>2021.0.5</spring.cloud.version>
<spring.cloud.alibaba.version>2021.0.4.0</spring.cloud.alibaba.version>
<!-- Web 相关 -->
@ -25,7 +25,7 @@
<springdoc.version>1.6.14</springdoc.version>
<knife4j.version>4.0.0</knife4j.version>
<!-- 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>
@ -54,8 +54,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>
@ -65,16 +65,16 @@
<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>
<reflections.version>0.10.2</reflections.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>
@ -235,11 +235,10 @@
<version>${knife4j.version}</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId> <!-- 接口文档 UIknife4j【网关专属】 -->
<groupId>com.github.xiaoymin</groupId> <!-- 接口文档 UIknife4j -->
<artifactId>knife4j-gateway-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
<!-- DB 相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>

View File

@ -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();
}
}

View File

@ -38,5 +38,10 @@ public class TenantProperties {
* tenant_id
*/
private Set<String> ignoreTables = Collections.emptySet();
/**
* {@link org.springframework.cache.Cache}
*
* Cache
*/
private Set<String> ignoreCaches = Collections.emptySet();
}

View File

@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
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.TenantJobAspect;
import cn.iocoder.yudao.framework.tenant.core.mq.TenantChannelInterceptor;
import cn.iocoder.yudao.framework.tenant.core.mq.TenantFunctionAroundWrapper;
@ -15,6 +16,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 com.xxl.job.core.executor.XxlJobExecutor;
@ -36,6 +42,7 @@ import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.integration.config.GlobalChannelInterceptor;
import javax.sql.DataSource;
import java.util.Objects;
@AutoConfiguration
@ -66,6 +73,17 @@ public class YudaoTenantAutoConfiguration {
MyBatisUtils.addInterceptor(interceptor, inner, 0);
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 ==========
@ -103,13 +121,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);
}
}

View File

@ -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";
}

View File

@ -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());
}
}

View File

@ -1,6 +1,9 @@
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 +13,32 @@ 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) {
super(cacheWriter, 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();
}
// 继续基于父方法

View File

@ -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);
}
}

View File

@ -1,5 +1,5 @@
package cn.iocoder.yudao.framework.tenant.core.service;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import java.util.List;
/**
@ -22,5 +22,5 @@ public interface TenantFrameworkService {
* @param id
*/
void validTenant(Long id);
DataSourceProperty getDataSourceProperty(Long id);
}

View File

@ -1,8 +1,11 @@
package cn.iocoder.yudao.framework.tenant.core.service;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.infra.api.db.dto.DataSourceConfigRespDTO;
import cn.iocoder.yudao.framework.common.util.cache.CacheUtils;
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;
@ -48,7 +51,27 @@ 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).getData();
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() {
@ -60,5 +83,9 @@ public class TenantFrameworkServiceImpl implements TenantFrameworkService {
public void validTenant(Long id) {
validTenantCache.get(id).checkError();
}
@Override
@SneakyThrows
public DataSourceProperty getDataSourceProperty(Long id) {
return dataSourcePropertyCache.get(id);
}
}

View File

@ -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);
}
}

View File

@ -1,8 +1,7 @@
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;
@ -10,13 +9,13 @@ 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);

View File

@ -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;

View File

@ -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}
}

View File

@ -1 +1 @@
cn.iocoder.yudao.framework.captcha.core.service.RedisCaptchaServiceImpl
cn.iocoder.yudao.framework.captcha.core.RedisCaptchaServiceImpl

View File

@ -1,5 +1,6 @@
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;
@ -14,6 +15,20 @@ public class FlowableUtils {
public static void setAuthenticatedUserId(Long userId) {
Authentication.setAuthenticatedUserId(String.valueOf(userId));
}
/**
*
*
* Flowable
*
* @return
*/
public static String getTenantId() {
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
if (loginUser == null || loginUser.getTenantId() == null) {
return null;
}
return String.valueOf(loginUser.getTenantId());
}
public static void clearAuthenticatedUserId() {
Authentication.setAuthenticatedUserId(null);

View File

@ -1,12 +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 +14,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 +31,8 @@ public class IdempotentRedisDAO {
}
private static String formatKey(String key) {
return String.format(IDEMPOTENT.getKeyTemplate(), key);
return String.format(IDEMPOTENT_KEY_TEMPLATE, key);
}
}

View File

@ -1,8 +1,6 @@
package cn.iocoder.yudao.framework.lock4j.config;
import cn.hutool.core.util.ClassUtil;
import cn.iocoder.yudao.framework.lock4j.core.DefaultLockFailureStrategy;
import cn.iocoder.yudao.framework.lock4j.core.Lock4jRedisKeyConstants;
import com.baomidou.lock.spring.boot.autoconfigure.LockAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
@ -11,13 +9,13 @@ import org.springframework.context.annotation.Bean;
@AutoConfiguration
@AutoConfigureBefore(LockAutoConfiguration.class)
public class YudaoLock4jConfiguration {
/*
static {
// 手动加载 Lock4jRedisKeyConstants 类,因为它不会被使用到
// 如果不加载,会导致 Redis 监控,看到它的 Redis Key 枚举
ClassUtil.loadClass(Lock4jRedisKeyConstants.class.getName());
}
*/
@Bean
public DefaultLockFailureStrategy lockFailureStrategy() {
return new DefaultLockFailureStrategy();

View File

@ -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 数据结构
}

View File

@ -32,10 +32,15 @@
<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>

View File

@ -1,5 +1,6 @@
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 +8,15 @@ 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 +33,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();
@ -46,5 +57,13 @@ 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);
}
}

View File

@ -1,5 +1,7 @@
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;
@ -22,12 +24,16 @@ public class YudaoRedisAutoConfiguration {
// 设置 RedisConnection 工厂。😈 它就是实现多种 Java Redis 客户端接入的秘密工厂。感兴趣的胖友,可以自己去撸下。
template.setConnectionFactory(factory);
// 使用 String 序列化方式,序列化 KEY 。
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;
}
}

View File

@ -110,4 +110,4 @@ public class RedisKeyDefine {
return String.format(keyTemplate, args);
}
}
}

View File

@ -25,4 +25,4 @@ public class RedisKeyRegistry {
return DEFINES.size();
}
}
}

View File

@ -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);
}
}

View File

@ -27,6 +27,11 @@
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<scope>provided</scope> <!-- 设置为 provided主要是 GlobalExceptionHandler 使用 -->
</dependency>
<!-- Web 相关 -->
<dependency>
@ -39,12 +44,11 @@
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<scope>provided</scope> <!-- 设置为 provided主要是 GlobalExceptionHandler 使用 -->
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId> <!-- 接口文档 -->
<artifactId>knife4j-gateway-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId> <!-- 接口文档 -->
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>

View File

@ -21,7 +21,10 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<!-- 业务组件 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
@ -60,7 +63,10 @@
<groupId>com.github.xiaoymin</groupId> <!-- 接口文档 -->
<artifactId>knife4j-gateway-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId> <!-- 接口文档 UIknife4j -->
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
</dependency>
<!-- RPC 远程调用相关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
@ -101,7 +107,7 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.8</version> <!-- 如果 spring.boot.version 版本修改,则这里也要跟着修改 -->
<version>2.7.9</version> <!-- 如果 spring.boot.version 版本修改,则这里也要跟着修改 -->
<configuration>
<fork>true</fork>
</configuration>

View File

@ -54,3 +54,5 @@ knife4j:
# 聚合 Swagger 文档
gateway:
enable: true
debug: true

View File

@ -4,7 +4,8 @@ spring:
profiles:
active: local
main:
web-application-type: reactive
server:
port: 48080
@ -12,3 +13,5 @@ server:
logging:
file:
name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径

View File

@ -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 {

View File

@ -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;
@ -30,6 +31,7 @@ import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.MODEL_DEPLOY_
*/
@Service
@Validated
@TenantDS // 工作流的 Service 必须添加 @TenantDS 注解。原因是Flowable 使用事务,无法切换数据源,需要提使用 @TenantDS 切到它的数据源
public class BpmFormServiceImpl implements BpmFormService {
@Resource

View File

@ -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

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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());

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.bpm.service.oa;
import cn.hutool.core.date.DateUtil;
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;
@ -30,6 +31,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 {
/**

View File

@ -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

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.bpm.service.task;
import cn.iocoder.yudao.framework.tenant.core.db.dynamic.TenantDS;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
@ -59,6 +60,7 @@ import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
@Service
@Validated
@Slf4j
@TenantDS
public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService {
@Resource

View File

@ -7,6 +7,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;
@ -50,6 +51,7 @@ import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
*/
@Slf4j
@Service
@TenantDS // 工作流的 Service 必须添加 @TenantDS 注解。原因是Flowable 使用事务,无法切换数据源,需要提使用 @TenantDS 切到它的数据源
public class BpmTaskServiceImpl implements BpmTaskService {
@Resource

View File

@ -1 +0,0 @@
package cn.iocoder.yudao.module.bpm.service.task;

View File

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.infra.api.db;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.infra.api.db.dto.DataSourceConfigRespDTO;
import cn.iocoder.yudao.module.infra.enums.ApiConstants;
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.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* API
*
* @author
*/
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 数据源配置")
public interface DataSourceConfigServiceApi {
String PREFIX = ApiConstants.PREFIX + "/datasource";
/**
*
*
* @param id
* @return
*/
@GetMapping(PREFIX + "/datasource-config")
@Operation(summary = "获得数据源信息")
@Parameter(name = "id", description = "数据源编号", required = true, example = "1024")
CommonResult<DataSourceConfigRespDTO> getDataSourceConfig(@RequestParam("id")Long id);
}

View File

@ -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;
}

View File

@ -0,0 +1,35 @@
package cn.iocoder.yudao.module.infra.api.db;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
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.apache.dubbo.config.annotation.DubboService;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.module.system.enums.ApiConstants.VERSION;
/**
* API
*
* @author
*/
@RestController // 提供 RESTful API 接口,给 Feign 调用
@DubboService(version = VERSION) // 提供 Dubbo RPC 接口,给 Dubbo Consumer 调用
@Validated
public class DataSourceConfigServiceApiImpl implements DataSourceConfigServiceApi {
@Resource
private DataSourceConfigService dataSourceConfigService;
@Override
public CommonResult<DataSourceConfigRespDTO> getDataSourceConfig(Long id) {
return success(DataSourceConfigConvert.INSTANCE.convert02(dataSourceConfigService.getDataSourceConfig(id)));
}
}

View File

@ -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.*;
@ -69,5 +70,10 @@ public class DataSourceConfigController {
List<DataSourceConfigDO> list = dataSourceConfigService.getDataSourceConfigList();
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));
}
}

View File

@ -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;
}

View File

@ -3,15 +3,13 @@ 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.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.data.redis.connection.RedisServerCommands;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisCallback;
@ -21,10 +19,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.*;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@ -53,11 +48,12 @@ 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")
@Operation(summary = "获得 Redis keys 键名列表")
@Parameter(name = "keyTemplate", description = "Redis Key 定义", example = "true")

View File

@ -1,36 +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 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;
}

View File

@ -2,7 +2,6 @@ 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 io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

View File

@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.infra.convert.db;
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.*;
@ -26,5 +26,8 @@ public interface DataSourceConfigConvert {
DataSourceConfigRespVO convert(DataSourceConfigDO bean);
List<DataSourceConfigRespVO> convertList(List<DataSourceConfigDO> list);
DataSourceConfigRespDTO convert02(DataSourceConfigDO bean);
List<DataSourceConfigSimpleRespVO> convertList02(List<DataSourceConfigDO> list);
}

View File

@ -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);
}

View File

@ -26,7 +26,7 @@
<dependency>
<groupId>io.swagger.core.v3</groupId> <!-- 接口文档:使用最新版本的 Swagger 模型 -->
<artifactId>swagger-annotations</artifactId>
<scope>provided</scope> <!-- 设置为 provided主要是 PageParam 使用到 -->
<scope>provided</scope>
</dependency>
<!-- 参数校验 -->

View File

@ -1,5 +1,5 @@
package cn.iocoder.yudao.module.system.api.tenant;
import cn.iocoder.yudao.module.system.api.tenant.dto.TenantDataSourceConfigRespDTO;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.system.enums.ApiConstants;
import io.swagger.v3.oas.annotations.tags.Tag;
@ -25,5 +25,14 @@ public interface TenantApi {
@Operation(summary = "校验租户是否合法")
@Parameter(name = "id", description = "租户编号", required = true, example = "1024")
CommonResult<Boolean> validTenant(@RequestParam("id") Long id);
/**
*
*
* @param tenantId
* @return
*/
@GetMapping(PREFIX + "/tenant-datasource-config")
@Operation(summary = "获得租户数据源信息")
@Parameter(name = "tenantId", description = "租户编号", required = true, example = "1024")
CommonResult<TenantDataSourceConfigRespDTO> getTenantDataSourceConfig(@RequestParam("tenantId")Long tenantId);
}

View File

@ -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;
}

View File

@ -7,7 +7,7 @@ import lombok.Getter;
*
*
* @author
* @date 2021/2/1 13:39
* @since 2021/2/1 13:39
*/
@Getter
@AllArgsConstructor

View File

@ -7,7 +7,7 @@ import lombok.Getter;
*
*
* @author zzf
* @date 2021/2/1 13:39
* @since 2021/2/1 13:39
*/
@Getter
@AllArgsConstructor

View File

@ -24,7 +24,8 @@ public class PermissionApiImpl implements PermissionApi {
@Override
public CommonResult<Set<Long>> getUserRoleIdListByRoleIds(Collection<Long> roleIds) {
return success(permissionService.getUserRoleIdListByRoleIds(roleIds));
//return success(permissionService.getUserRoleIdListByRoleIds(roleIds));
return success(permissionService.getUserRoleIdListByRoleId(roleIds));
}
@Override

View File

@ -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.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.system.service.tenant.TenantService;
import org.apache.dubbo.config.annotation.DubboService;
@ -20,6 +24,9 @@ public class TenantApiImpl implements TenantApi {
@Resource
private TenantService tenantService;
@Resource
private DataSourceConfigServiceApi dataSourceConfigServiceApi;
@Override
public CommonResult<List<Long>> getTenantIdList() {
return success(tenantService.getTenantIdList());
@ -30,5 +37,15 @@ public class TenantApiImpl implements TenantApi {
tenantService.validTenant(id);
return success(true);
}
@Override
public CommonResult<TenantDataSourceConfigRespDTO> getTenantDataSourceConfig(Long tenantId) {
// 获得租户信息
TenantDO tenant = tenantService.getTenant(tenantId);
if (tenant == null) {
return null;
}
// 获得租户的数据源配置
return success(TenantConvert.INSTANCE.convert(
dataSourceConfigServiceApi.getDataSourceConfig(tenant.getDataSourceConfigId()).getData()));
}
}

View File

@ -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,8 +11,8 @@ 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;
@ -34,6 +33,7 @@ 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;
@ -52,6 +52,8 @@ public class AuthController {
@Resource
private RoleService roleService;
@Resource
private MenuService menuService;
@Resource
private PermissionService permissionService;
@Resource
private SocialUserService socialUserService;
@ -91,33 +93,23 @@ 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));
}
// 1.2 获得角色列表
Set<Long> roleIds = permissionService.getUserRoleIdListByUserId(getLoginUserId());
List<RoleDO> roles = roleService.getRoleList(roleIds);
roles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())); // 移除禁用的角色
@GetMapping("/list-menus")
@Operation(summary = "获得登录用户的菜单列表")
public CommonResult<List<AuthMenuRespVO>> getMenus() {
// 获得角色列表
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.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));
}
// ========== 短信登录相关 ==========

View File

@ -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;
}

View File

@ -7,6 +7,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,8 @@ public class AuthPermissionInfoRespVO {
@Schema(description = "操作权限数组", required = true)
private Set<String> permissions;
@Schema(description = "菜单树", required = true)
private List<MenuVO> menus;
@Schema(description = "用户信息 VO")
@Data
@ -42,5 +45,48 @@ public class AuthPermissionInfoRespVO {
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;
}
}

View File

@ -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")

View File

@ -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",

View File

@ -45,4 +45,8 @@ public class TenantBaseVO {
@NotNull(message = "账号数量不能为空")
private Integer accountCount;
@Schema(description = "数据源配置编号", required = true, example = "4096")
@NotNull(message = "数据源配置编号不能为空")
private Long dataSourceConfigId;
}

View File

@ -64,7 +64,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) {

View File

@ -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,17 +8,19 @@ 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.*;
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;
@Mapper
public interface AuthConvert {
public interface AuthConvert {
AuthConvert INSTANCE = Mappers.getMapper(AuthConvert.class);
@ -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(convertSet(roleList, RoleDO::getCode))
.permissions(convertSet(menuList, MenuDO::getPermission))
.build();
// 用户信息
.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,19 @@ 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());

View File

@ -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;
@ -41,5 +43,5 @@ public interface TenantConvert {
reqVO.setNickname(bean.getContactName()).setMobile(bean.getContactMobile());
return reqVO;
}
TenantDataSourceConfigRespDTO convert(DataSourceConfigRespDTO bean);
}

View File

@ -78,5 +78,12 @@ public class TenantDO extends BaseDO {
*
*/
private Integer accountCount;
/**
*
*
*
*
* DataSourceConfigDO id
*/
private Long dataSourceConfigId;
}

View File

@ -2,13 +2,15 @@ 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) {
@ -24,5 +26,7 @@ public interface DeptMapper extends BaseMapperX<DeptDO> {
default Long selectCountByParentId(Long parentId) {
return selectCount(DeptDO::getParentId, parentId);
}
default List<DeptDO> selectListByParentId(Collection<Long> parentIds) {
return selectList(DeptDO::getParentId, parentIds);
}
}

View File

@ -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) {

View File

@ -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));
}
}

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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.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) {

View File

@ -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) {

View File

@ -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.template.MailTemplatePageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
import com.baomidou.dynamic.datasource.annotation.Master;
import org.apache.ibatis.annotations.Mapper;
@Mapper
@Master
public interface MailTemplateMapper extends BaseMapperX<MailTemplateDO> {
default PageResult<MailTemplateDO> selectPage(MailTemplatePageReqVO pageReqVO){

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -5,11 +5,13 @@ 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.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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,5 +27,7 @@ public interface MenuMapper extends BaseMapperX<MenuDO> {
return selectList(new LambdaQueryWrapperX<MenuDO>().likeIfPresent(MenuDO::getName, reqVO.getName())
.eqIfPresent(MenuDO::getStatus, reqVO.getStatus()));
}
default List<MenuDO> selectListByPermission(String permission) {
return selectList(MenuDO::getPermission, permission);
}
}

View File

@ -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);
}

View File

@ -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,25 @@ 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)

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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> {
/**

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
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 TenantMapper extends BaseMapperX<TenantDO> {
default PageResult<TenantDO> selectPage(TenantPageReqVO reqVO) {

View File

@ -5,6 +5,7 @@ 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.tenant.vo.packages.TenantPackagePageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO;
import com.baomidou.dynamic.datasource.annotation.Master;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@ -15,6 +16,7 @@ import java.util.List;
* @author
*/
@Mapper
@Master
public interface TenantPackageMapper extends BaseMapperX<TenantPackageDO> {
default PageResult<TenantPackageDO> selectPage(TenantPackagePageReqVO reqVO) {

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.dal.mysql.user;
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.user.vo.user.UserExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
@ -12,6 +13,7 @@ import java.util.Collection;
import java.util.List;
@Mapper
@TenantDS
public interface AdminUserMapper extends BaseMapperX<AdminUserDO> {
default AdminUserDO selectByUsername(String username) {

View File

@ -1,11 +1,7 @@
package cn.iocoder.yudao.module.system.dal.redis;
import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;
import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import java.time.Duration;
import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.STRING;
/**
* System Redis Key
@ -14,12 +10,93 @@ import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.S
*/
public interface RedisKeyConstants {
RedisKeyDefine OAUTH2_ACCESS_TOKEN = new RedisKeyDefine("访问令牌的缓存",
"oauth2_access_token:%s", // 参数为访问令牌 token
STRING, OAuth2AccessTokenDO.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC);
/**
*
*
* KEY dept_children_ids:{id}
* VALUE String
*/
String DEPT_CHILDREN_ID_LIST = "dept_children_ids";
RedisKeyDefine SOCIAL_AUTH_STATE = new RedisKeyDefine("社交登陆的 state", // 注意,它是被 JustAuth 的 justauth.type.prefix 使用到
"social_auth_state:%s", // 参数为 state
STRING, String.class, Duration.ofHours(24)); // 值为 state
/**
*
*
* KEY role:{id}
* VALUE String
*/
String ROLE = "role";
/**
*
*
* KEY user_role_ids:{userId}
* VALUE String
*/
String USER_ROLE_ID_LIST = "user_role_ids";
/**
*
*
* KEY user_role_ids:{menuId}
* VALUE String
*/
String MENU_ROLE_ID_LIST = "menu_role_ids";
/**
*
*
* KEY permission_menu_ids:{permission}
* VALUE String
*/
String PERMISSION_MENU_ID_LIST = "permission_menu_ids";
/**
* OAuth2
*
* KEY user:{id}
* VALUE String
*/
String OAUTH_CLIENT = "oauth_client";
/**
* 访
*
* KEY oauth2_access_token:{token}
* VALUE String 访 {@link OAuth2AccessTokenDO}
*
* 使 RedisTemplate
*/
String OAUTH2_ACCESS_TOKEN = "oauth2_access_token:%s";
/**
*
*
* KEY notify_template:{code}
* VALUE String
*/
String NOTIFY_TEMPLATE = "notify_template";
/**
*
*
* KEY sms_template:{id}
* VALUE String
*/
String MAIL_ACCOUNT = "mail_account";
/**
*
*
* KEY mail_template:{code}
* VALUE String
*/
String MAIL_TEMPLATE = "mail_template";
/**
*
*
* KEY sms_template:{id}
* VALUE String
*/
String SMS_TEMPLATE = "sms_template";
}

View File

@ -51,7 +51,7 @@ public class OAuth2AccessTokenRedisDAO {
}
private static String formatKey(String accessToken) {
return String.format(OAUTH2_ACCESS_TOKEN.getKeyTemplate(), accessToken);
return String.format(OAUTH2_ACCESS_TOKEN, accessToken);
}
}

View File

@ -1,10 +1,11 @@
package cn.iocoder.yudao.module.system.framework.rpc.config;
import cn.iocoder.yudao.module.infra.api.db.DataSourceConfigServiceApi;
import cn.iocoder.yudao.module.infra.api.file.FileApi;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@EnableFeignClients(clients = FileApi.class)
@EnableFeignClients(clients = {FileApi.class, DataSourceConfigServiceApi.class})
public class RpcConfiguration {
}

Some files were not shown because too many files have changed in this diff Show More