CLIENT_CACHE = new ConcurrentHashMap<>();
- /**
- * 初始化工厂
- */
- public static void init() {
- log.info("初始化OSS工厂");
- RedisUtils.subscribe(OssConstant.DEFAULT_CONFIG_KEY, String.class, configKey -> {
- OssClient client = getClient(configKey);
- // 未初始化不处理
- if (client != null) {
- refresh(configKey);
- log.info("订阅刷新OSS配置 => " + configKey);
- }
- });
- }
-
/**
* 获取默认实例
*/
@@ -55,25 +40,24 @@ public class OssFactory {
* 根据类型获取实例
*/
public static OssClient instance(String configKey) {
- OssClient client = getClient(configKey);
- if (client == null) {
- refresh(configKey);
- return getClient(configKey);
- }
- return client;
- }
-
- private static void refresh(String configKey) {
String json = CacheUtils.get(CacheNames.SYS_OSS_CONFIG, configKey);
if (json == null) {
throw new OssException("系统异常, '" + configKey + "'配置信息不存在!");
}
OssProperties properties = JsonUtils.parseObject(json, OssProperties.class);
- CLIENT_CACHE.put(configKey, new OssClient(configKey, properties));
- }
-
- private static OssClient getClient(String configKey) {
- return CLIENT_CACHE.get(configKey);
+ OssClient client = CLIENT_CACHE.get(configKey);
+ if (client == null) {
+ CLIENT_CACHE.put(configKey, new OssClient(configKey, properties));
+ log.info("创建OSS实例 key => {}", configKey);
+ return CLIENT_CACHE.get(configKey);
+ }
+ // 配置不相同则重新构建
+ if (!client.checkPropertiesSame(properties)) {
+ CLIENT_CACHE.put(configKey, new OssClient(configKey, properties));
+ log.info("重载OSS实例 key => {}", configKey);
+ return CLIENT_CACHE.get(configKey);
+ }
+ return client;
}
}
diff --git a/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/ruoyi/common/ratelimiter/annotation/RateLimiter.java b/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/ruoyi/common/ratelimiter/annotation/RateLimiter.java
index b5bd284f1..9fe9b0aa0 100644
--- a/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/ruoyi/common/ratelimiter/annotation/RateLimiter.java
+++ b/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/ruoyi/common/ratelimiter/annotation/RateLimiter.java
@@ -1,6 +1,6 @@
package com.ruoyi.common.ratelimiter.annotation;
-import com.ruoyi.common.core.constant.CacheConstants;
+import com.ruoyi.common.core.constant.GlobalConstants;
import com.ruoyi.common.ratelimiter.enums.LimitType;
import java.lang.annotation.*;
@@ -17,7 +17,7 @@ public @interface RateLimiter {
/**
* 限流key
*/
- String key() default CacheConstants.RATE_LIMIT_KEY;
+ String key() default GlobalConstants.RATE_LIMIT_KEY;
/**
* 限流时间,单位秒
diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/config/SaTokenConfig.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/config/SaTokenConfig.java
index f4fe28bc2..609e3ec4b 100644
--- a/ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/config/SaTokenConfig.java
+++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/config/SaTokenConfig.java
@@ -1,8 +1,10 @@
package com.ruoyi.common.satoken.config;
+import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpLogic;
+import com.ruoyi.common.satoken.core.dao.PlusSaTokenDao;
import com.ruoyi.common.satoken.core.service.SaPermissionImpl;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
@@ -30,4 +32,12 @@ public class SaTokenConfig implements WebMvcConfigurer {
return new SaPermissionImpl();
}
+ /**
+ * 自定义dao层存储
+ */
+ @Bean
+ public SaTokenDao saTokenDao() {
+ return new PlusSaTokenDao();
+ }
+
}
diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/core/dao/PlusSaTokenDao.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/core/dao/PlusSaTokenDao.java
index 52dd9036e..aafa457cf 100644
--- a/ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/core/dao/PlusSaTokenDao.java
+++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/core/dao/PlusSaTokenDao.java
@@ -3,7 +3,6 @@ package com.ruoyi.common.satoken.core.dao;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.util.SaFoxUtil;
import com.ruoyi.common.redis.utils.RedisUtils;
-import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.ArrayList;
@@ -15,7 +14,6 @@ import java.util.List;
*
* @author Lion Li
*/
-@Component
public class PlusSaTokenDao implements SaTokenDao {
/**
diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/utils/LoginHelper.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/utils/LoginHelper.java
index f7fefa3c5..3bff1f268 100644
--- a/ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/utils/LoginHelper.java
+++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/utils/LoginHelper.java
@@ -1,8 +1,11 @@
package com.ruoyi.common.satoken.utils;
import cn.dev33.satoken.context.SaHolder;
+import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.json.JSONObject;
+import com.ruoyi.common.core.constant.TenantConstants;
import com.ruoyi.common.core.constant.UserConstants;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.enums.DeviceType;
@@ -12,6 +15,8 @@ import com.ruoyi.common.core.utils.StringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
+import java.util.Set;
+
/**
* 登录鉴权助手
*
@@ -37,8 +42,7 @@ public class LoginHelper {
*/
public static void login(LoginUser loginUser) {
SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
- StpUtil.login(loginUser.getLoginId());
- setLoginUser(loginUser);
+ StpUtil.login(loginUser.getLoginId(), new SaLoginModel().setExtra(LOGIN_USER_KEY, loginUser));
}
/**
@@ -49,15 +53,9 @@ public class LoginHelper {
*/
public static void loginByDevice(LoginUser loginUser, DeviceType deviceType) {
SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
- StpUtil.login(loginUser.getLoginId(), deviceType.getDevice());
- setLoginUser(loginUser);
- }
-
- /**
- * 设置用户数据(多级缓存)
- */
- public static void setLoginUser(LoginUser loginUser) {
- StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
+ StpUtil.login(loginUser.getLoginId(), new SaLoginModel()
+ .setDevice(deviceType.getDevice())
+ .setExtra(LOGIN_USER_KEY, loginUser));
}
/**
@@ -68,7 +66,7 @@ public class LoginHelper {
if (loginUser != null) {
return loginUser;
}
- loginUser = (LoginUser) StpUtil.getTokenSession().get(LOGIN_USER_KEY);
+ loginUser = ((JSONObject) StpUtil.getExtra(LOGIN_USER_KEY)).toBean(LoginUser.class);
SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
return loginUser;
}
@@ -96,6 +94,19 @@ public class LoginHelper {
return loginUser.getUserId();
}
+ /**
+ * 获取租户ID
+ */
+ public static String getTenantId() {
+ LoginUser loginUser;
+ try {
+ loginUser = getLoginUser();
+ } catch (Exception e) {
+ return null;
+ }
+ return loginUser.getTenantId();
+ }
+
/**
* 获取部门ID
*/
@@ -119,17 +130,31 @@ public class LoginHelper {
}
/**
- * 是否为管理员
+ * 是否为超级管理员
*
* @param userId 用户ID
* @return 结果
*/
- public static boolean isAdmin(Long userId) {
+ public static boolean isSuperAdmin(Long userId) {
return UserConstants.SUPER_ADMIN_ID.equals(userId);
}
- public static boolean isAdmin() {
- return isAdmin(getUserId());
+ public static boolean isSuperAdmin() {
+ return isSuperAdmin(getUserId());
+ }
+
+ /**
+ * 是否为超级管理员
+ *
+ * @param rolePermission 角色权限标识组
+ * @return 结果
+ */
+ public static boolean isTenantAdmin(Set rolePermission) {
+ return rolePermission.contains(TenantConstants.TENANT_ADMIN_ROLE_KEY);
+ }
+
+ public static boolean isTenantAdmin() {
+ return isTenantAdmin(getLoginUser().getRolePermission());
}
}
diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-satoken/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
index 78e5b7e01..d358acb71 100644
--- a/ruoyi-common/ruoyi-common-satoken/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ b/ruoyi-common/ruoyi-common-satoken/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -1,2 +1 @@
-com.ruoyi.common.satoken.core.dao.PlusSaTokenDao
com.ruoyi.common.satoken.config.SaTokenConfig
diff --git a/ruoyi-common/ruoyi-common-security/pom.xml b/ruoyi-common/ruoyi-common-security/pom.xml
index eaa3258c9..1cc554bbe 100644
--- a/ruoyi-common/ruoyi-common-security/pom.xml
+++ b/ruoyi-common/ruoyi-common-security/pom.xml
@@ -22,12 +22,6 @@
ruoyi-common-satoken
-
-
- cn.dev33
- sa-token-spring-boot3-starter
- ${satoken.version}
-
diff --git a/ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/core/TencentSmsTemplate.java b/ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/core/TencentSmsTemplate.java
index a7be95ab2..8dbd1ad3c 100644
--- a/ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/core/TencentSmsTemplate.java
+++ b/ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/core/TencentSmsTemplate.java
@@ -53,7 +53,7 @@ public class TencentSmsTemplate implements SmsTemplate {
throw new SmsException("模板ID不能为空");
}
SendSmsRequest req = new SendSmsRequest();
- Set set = Arrays.stream(phones.split(",")).map(p -> "+86" + p).collect(Collectors.toSet());
+ Set set = Arrays.stream(phones.split(StringUtils.SEPARATOR)).map(p -> "+86" + p).collect(Collectors.toSet());
req.setPhoneNumberSet(ArrayUtil.toArray(set, String.class));
if (CollUtil.isNotEmpty(param)) {
req.setTemplateParamSet(ArrayUtil.toArray(param.values(), String.class));
diff --git a/ruoyi-common/ruoyi-common-tenant/pom.xml b/ruoyi-common/ruoyi-common-tenant/pom.xml
new file mode 100644
index 000000000..5eeb86a07
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-tenant/pom.xml
@@ -0,0 +1,37 @@
+
+
+
+ com.ruoyi
+ ruoyi-common
+ ${revision}
+ ../pom.xml
+
+ 4.0.0
+
+ ruoyi-common-tenant
+
+
+ ruoyi-common-tenant 租户模块
+
+
+
+
+ com.ruoyi
+ ruoyi-common-mybatis
+
+
+
+ com.ruoyi
+ ruoyi-common-redis
+
+
+
+ com.alibaba
+ transmittable-thread-local
+
+
+
+
+
diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/config/TenantConfig.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/config/TenantConfig.java
new file mode 100644
index 000000000..373e77c64
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/config/TenantConfig.java
@@ -0,0 +1,100 @@
+package com.ruoyi.common.tenant.config;
+
+import cn.dev33.satoken.dao.SaTokenDao;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
+import com.ruoyi.common.core.utils.reflect.ReflectUtils;
+import com.ruoyi.common.mybatis.config.MybatisPlusConfig;
+import com.ruoyi.common.redis.config.RedisConfig;
+import com.ruoyi.common.redis.config.properties.RedissonProperties;
+import com.ruoyi.common.tenant.core.TenantSaTokenDao;
+import com.ruoyi.common.tenant.handle.PlusTenantLineHandler;
+import com.ruoyi.common.tenant.handle.TenantKeyPrefixHandler;
+import com.ruoyi.common.tenant.manager.TenantSpringCacheManager;
+import com.ruoyi.common.tenant.properties.TenantProperties;
+import org.redisson.config.ClusterServersConfig;
+import org.redisson.config.SingleServerConfig;
+import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.cache.CacheManager;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Primary;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 租户配置类
+ *
+ * @author Lion Li
+ */
+@EnableConfigurationProperties(TenantProperties.class)
+@AutoConfiguration(after = {RedisConfig.class, MybatisPlusConfig.class})
+@ConditionalOnProperty(value = "tenant.enable", havingValue = "true")
+public class TenantConfig {
+
+ /**
+ * 初始化租户配置
+ */
+ @Bean
+ public boolean tenantInit(MybatisPlusInterceptor mybatisPlusInterceptor,
+ TenantProperties tenantProperties) {
+ List interceptors = new ArrayList<>();
+ // 多租户插件 必须放到第一位
+ interceptors.add(tenantLineInnerInterceptor(tenantProperties));
+ interceptors.addAll(mybatisPlusInterceptor.getInterceptors());
+ mybatisPlusInterceptor.setInterceptors(interceptors);
+ return true;
+ }
+
+ /**
+ * 多租户插件
+ */
+ public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties tenantProperties) {
+ return new TenantLineInnerInterceptor(new PlusTenantLineHandler(tenantProperties));
+ }
+
+ @Bean
+ public RedissonAutoConfigurationCustomizer tenantRedissonCustomizer(RedissonProperties redissonProperties) {
+ return config -> {
+ TenantKeyPrefixHandler nameMapper = new TenantKeyPrefixHandler(redissonProperties.getKeyPrefix());
+ SingleServerConfig singleServerConfig = ReflectUtils.invokeGetter(config, "singleServerConfig");
+ if (ObjectUtil.isNotNull(singleServerConfig)) {
+ // 使用单机模式
+ // 设置多租户 redis key前缀
+ singleServerConfig.setNameMapper(nameMapper);
+ ReflectUtils.invokeSetter(config, "singleServerConfig", singleServerConfig);
+ }
+ ClusterServersConfig clusterServersConfig = ReflectUtils.invokeGetter(config, "clusterServersConfig");
+ // 集群配置方式 参考下方注释
+ if (ObjectUtil.isNotNull(clusterServersConfig)) {
+ // 设置多租户 redis key前缀
+ clusterServersConfig.setNameMapper(nameMapper);
+ ReflectUtils.invokeSetter(config, "clusterServersConfig", clusterServersConfig);
+ }
+ };
+ }
+
+ /**
+ * 多租户缓存管理器
+ */
+ @Primary
+ @Bean
+ public CacheManager tenantCacheManager() {
+ return new TenantSpringCacheManager();
+ }
+
+ /**
+ * 多租户鉴权dao实现
+ */
+ @Primary
+ @Bean
+ public SaTokenDao tenantSaTokenDao() {
+ return new TenantSaTokenDao();
+ }
+
+}
diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/core/TenantEntity.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/core/TenantEntity.java
new file mode 100644
index 000000000..2864ccd74
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/core/TenantEntity.java
@@ -0,0 +1,21 @@
+package com.ruoyi.common.tenant.core;
+
+import com.ruoyi.common.mybatis.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 租户基类
+ *
+ * @author Michelle.Chung
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class TenantEntity extends BaseEntity {
+
+ /**
+ * 租户编号
+ */
+ private String tenantId;
+
+}
diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/core/TenantSaTokenDao.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/core/TenantSaTokenDao.java
new file mode 100644
index 000000000..db9c0258c
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/core/TenantSaTokenDao.java
@@ -0,0 +1,114 @@
+package com.ruoyi.common.tenant.core;
+
+import com.ruoyi.common.core.constant.GlobalConstants;
+import com.ruoyi.common.satoken.core.dao.PlusSaTokenDao;
+
+import java.util.List;
+
+/**
+ * SaToken 认证数据持久层 适配多租户
+ *
+ * @author Lion Li
+ */
+public class TenantSaTokenDao extends PlusSaTokenDao {
+
+ @Override
+ public String get(String key) {
+ return super.get(GlobalConstants.GLOBAL_REDIS_KEY + key);
+ }
+
+ @Override
+ public void set(String key, String value, long timeout) {
+ super.set(GlobalConstants.GLOBAL_REDIS_KEY + key, value, timeout);
+ }
+
+ /**
+ * 修修改指定key-value键值对 (过期时间不变)
+ */
+ @Override
+ public void update(String key, String value) {
+ super.update(GlobalConstants.GLOBAL_REDIS_KEY + key, value);
+ }
+
+ /**
+ * 删除Value
+ */
+ @Override
+ public void delete(String key) {
+ super.delete(GlobalConstants.GLOBAL_REDIS_KEY + key);
+ }
+
+ /**
+ * 获取Value的剩余存活时间 (单位: 秒)
+ */
+ @Override
+ public long getTimeout(String key) {
+ return super.getTimeout(GlobalConstants.GLOBAL_REDIS_KEY + key);
+ }
+
+ /**
+ * 修改Value的剩余存活时间 (单位: 秒)
+ */
+ @Override
+ public void updateTimeout(String key, long timeout) {
+ super.updateTimeout(GlobalConstants.GLOBAL_REDIS_KEY + key, timeout);
+ }
+
+
+ /**
+ * 获取Object,如无返空
+ */
+ @Override
+ public Object getObject(String key) {
+ return super.getObject(GlobalConstants.GLOBAL_REDIS_KEY + key);
+ }
+
+ /**
+ * 写入Object,并设定存活时间 (单位: 秒)
+ */
+ @Override
+ public void setObject(String key, Object object, long timeout) {
+ super.setObject(GlobalConstants.GLOBAL_REDIS_KEY + key, object, timeout);
+ }
+
+ /**
+ * 更新Object (过期时间不变)
+ */
+ @Override
+ public void updateObject(String key, Object object) {
+ super.updateObject(GlobalConstants.GLOBAL_REDIS_KEY + key, object);
+ }
+
+ /**
+ * 删除Object
+ */
+ @Override
+ public void deleteObject(String key) {
+ super.deleteObject(GlobalConstants.GLOBAL_REDIS_KEY + key);
+ }
+
+ /**
+ * 获取Object的剩余存活时间 (单位: 秒)
+ */
+ @Override
+ public long getObjectTimeout(String key) {
+ return super.getObjectTimeout(GlobalConstants.GLOBAL_REDIS_KEY + key);
+ }
+
+ /**
+ * 修改Object的剩余存活时间 (单位: 秒)
+ */
+ @Override
+ public void updateObjectTimeout(String key, long timeout) {
+ super.updateObjectTimeout(GlobalConstants.GLOBAL_REDIS_KEY + key, timeout);
+ }
+
+
+ /**
+ * 搜索数据
+ */
+ @Override
+ public List searchData(String prefix, String keyword, int start, int size, boolean sortType) {
+ return super.searchData(GlobalConstants.GLOBAL_REDIS_KEY + prefix, keyword, start, size, sortType);
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/exception/TenantException.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/exception/TenantException.java
new file mode 100644
index 000000000..371bc12bc
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/exception/TenantException.java
@@ -0,0 +1,20 @@
+package com.ruoyi.common.tenant.exception;
+
+import com.ruoyi.common.core.exception.base.BaseException;
+
+import java.io.Serial;
+
+/**
+ * 租户异常类
+ *
+ * @author Lion Li
+ */
+public class TenantException extends BaseException {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ public TenantException(String code, Object... args) {
+ super("tenant", code, args, null);
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/handle/PlusTenantLineHandler.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/handle/PlusTenantLineHandler.java
new file mode 100644
index 000000000..0133adbf6
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/handle/PlusTenantLineHandler.java
@@ -0,0 +1,57 @@
+package com.ruoyi.common.tenant.handle;
+
+import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.satoken.utils.LoginHelper;
+import com.ruoyi.common.tenant.helper.TenantHelper;
+import com.ruoyi.common.tenant.properties.TenantProperties;
+import lombok.AllArgsConstructor;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.LongValue;
+import net.sf.jsqlparser.expression.NullValue;
+
+import java.util.List;
+
+/**
+ * 自定义租户处理器
+ *
+ * @author Lion Li
+ */
+@AllArgsConstructor
+public class PlusTenantLineHandler implements TenantLineHandler {
+
+ private final TenantProperties tenantProperties;
+
+ @Override
+ public Expression getTenantId() {
+ String tenantId = LoginHelper.getTenantId();
+ if (StringUtils.isBlank(tenantId)) {
+ return new NullValue();
+ }
+ String dynamicTenantId = TenantHelper.getDynamic();
+ if (StringUtils.isNotBlank(dynamicTenantId)) {
+ // 返回动态租户
+ return new LongValue(dynamicTenantId);
+ }
+ // 返回固定租户
+ return new LongValue(tenantId);
+ }
+
+ @Override
+ public boolean ignoreTable(String tableName) {
+ String tenantId = LoginHelper.getTenantId();
+ // 判断是否有租户
+ if (StringUtils.isNotBlank(tenantId)) {
+ // 不需要过滤租户的表
+ List excludes = tenantProperties.getExcludes();
+ // 非业务表
+ excludes.addAll(List.of(
+ "gen_table",
+ "gen_table_column"
+ ));
+ return excludes.contains(tableName);
+ }
+ return true;
+ }
+
+}
diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/handle/TenantKeyPrefixHandler.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/handle/TenantKeyPrefixHandler.java
new file mode 100644
index 000000000..729702da9
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/handle/TenantKeyPrefixHandler.java
@@ -0,0 +1,58 @@
+package com.ruoyi.common.tenant.handle;
+
+import com.ruoyi.common.core.constant.GlobalConstants;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.redis.handler.KeyPrefixHandler;
+import com.ruoyi.common.tenant.helper.TenantHelper;
+
+/**
+ * 多租户redis缓存key前缀处理
+ *
+ * @author Lion Li
+ */
+public class TenantKeyPrefixHandler extends KeyPrefixHandler {
+
+ public TenantKeyPrefixHandler(String keyPrefix) {
+ super(keyPrefix);
+ }
+
+ /**
+ * 增加前缀
+ */
+ @Override
+ public String map(String name) {
+ if (StringUtils.isBlank(name)) {
+ return null;
+ }
+ if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) {
+ return super.map(name);
+ }
+ String tenantId = TenantHelper.getTenantId();
+ if (StringUtils.startsWith(name, tenantId)) {
+ // 如果存在则直接返回
+ return super.map(name);
+ }
+ return super.map(tenantId + ":" + name);
+ }
+
+ /**
+ * 去除前缀
+ */
+ @Override
+ public String unmap(String name) {
+ String unmap = super.unmap(name);
+ if (StringUtils.isBlank(unmap)) {
+ return null;
+ }
+ if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) {
+ return super.unmap(name);
+ }
+ String tenantId = TenantHelper.getTenantId();
+ if (StringUtils.startsWith(unmap, tenantId)) {
+ // 如果存在则删除
+ return unmap.substring((tenantId + ":").length());
+ }
+ return unmap;
+ }
+
+}
diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/helper/TenantHelper.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/helper/TenantHelper.java
new file mode 100644
index 000000000..f9815be9d
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/helper/TenantHelper.java
@@ -0,0 +1,112 @@
+package com.ruoyi.common.tenant.helper;
+
+import cn.dev33.satoken.context.SaHolder;
+import cn.dev33.satoken.spring.SpringMVCUtil;
+import com.alibaba.ttl.TransmittableThreadLocal;
+import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy;
+import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
+import com.ruoyi.common.core.constant.GlobalConstants;
+import com.ruoyi.common.core.utils.SpringUtils;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.redis.utils.RedisUtils;
+import com.ruoyi.common.satoken.utils.LoginHelper;
+import com.ruoyi.common.tenant.properties.TenantProperties;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 租户助手
+ *
+ * @author Lion Li
+ */
+@Slf4j
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class TenantHelper {
+
+ private static final TenantProperties PROPERTIES = SpringUtils.getBean(TenantProperties.class);
+
+ private static final String DYNAMIC_TENANT_KEY = GlobalConstants.GLOBAL_REDIS_KEY + "dynamicTenant";
+
+ private static final ThreadLocal TEMP_DYNAMIC_TENANT = new TransmittableThreadLocal<>();
+
+ /**
+ * 租户功能是否启用
+ */
+ public static boolean isEnable() {
+ return PROPERTIES.getEnable();
+ }
+
+ /**
+ * 开启忽略租户(开启后需手动调用 {@link #disableIgnore()} 关闭)
+ */
+ public static void enableIgnore() {
+ InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().tenantLine(true).build());
+ }
+
+ /**
+ * 关闭忽略租户
+ */
+ public static void disableIgnore() {
+ InterceptorIgnoreHelper.clearIgnoreStrategy();
+ }
+
+ /**
+ * 设置动态租户(一直有效 需要手动清理)
+ *
+ * 如果为非web环境 那么只在当前线程内生效
+ */
+ public static void setDynamic(String tenantId) {
+ if (!SpringMVCUtil.isWeb()) {
+ TEMP_DYNAMIC_TENANT.set(tenantId);
+ return;
+ }
+ String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getTenantId();
+ RedisUtils.setCacheObject(cacheKey, tenantId);
+ SaHolder.getStorage().set(cacheKey, tenantId);
+ }
+
+ /**
+ * 获取动态租户(一直有效 需要手动清理)
+ *
+ * 如果为非web环境 那么只在当前线程内生效
+ */
+ public static String getDynamic() {
+ if (!SpringMVCUtil.isWeb()) {
+ return TEMP_DYNAMIC_TENANT.get();
+ }
+ String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getTenantId();
+ String tenantId = (String) SaHolder.getStorage().get(cacheKey);
+ if (StringUtils.isNotBlank(tenantId)) {
+ return tenantId;
+ }
+ tenantId = RedisUtils.getCacheObject(cacheKey);
+ SaHolder.getStorage().set(cacheKey, tenantId);
+ return tenantId;
+ }
+
+ /**
+ * 清除动态租户
+ */
+ public static void clearDynamic() {
+ if (!SpringMVCUtil.isWeb()) {
+ TEMP_DYNAMIC_TENANT.remove();
+ return;
+ }
+ String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getTenantId();
+ RedisUtils.deleteObject(cacheKey);
+ SaHolder.getStorage().delete(cacheKey);
+ }
+
+ /**
+ * 获取当前租户id(动态租户优先)
+ */
+ public static String getTenantId() {
+ String tenantId = TenantHelper.getDynamic();
+ if (StringUtils.isBlank(tenantId)) {
+ tenantId = LoginHelper.getTenantId();
+ }
+ return tenantId;
+ }
+
+}
diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/manager/TenantSpringCacheManager.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/manager/TenantSpringCacheManager.java
new file mode 100644
index 000000000..83402b76f
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/manager/TenantSpringCacheManager.java
@@ -0,0 +1,32 @@
+package com.ruoyi.common.tenant.manager;
+
+import com.ruoyi.common.core.constant.GlobalConstants;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.redis.manager.PlusSpringCacheManager;
+import com.ruoyi.common.tenant.helper.TenantHelper;
+import org.springframework.cache.Cache;
+
+/**
+ * 重写 cacheName 处理方法 支持多租户
+ *
+ * @author Lion Li
+ */
+public class TenantSpringCacheManager extends PlusSpringCacheManager {
+
+ public TenantSpringCacheManager() {
+ }
+
+ @Override
+ public Cache getCache(String name) {
+ if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) {
+ return super.getCache(name);
+ }
+ String tenantId = TenantHelper.getTenantId();
+ if (StringUtils.startsWith(name, tenantId)) {
+ // 如果存在则直接返回
+ return super.getCache(name);
+ }
+ return super.getCache(tenantId + ":" + name);
+ }
+
+}
diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/properties/TenantProperties.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/properties/TenantProperties.java
new file mode 100644
index 000000000..473ea77bf
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/properties/TenantProperties.java
@@ -0,0 +1,27 @@
+package com.ruoyi.common.tenant.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.List;
+
+/**
+ * 租户 配置属性
+ *
+ * @author Lion Li
+ */
+@Data
+@ConfigurationProperties(prefix = "tenant")
+public class TenantProperties {
+
+ /**
+ * 是否启用
+ */
+ private Boolean enable;
+
+ /**
+ * 排除表
+ */
+ private List excludes;
+
+}
diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-tenant/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 000000000..8f39d11f1
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-tenant/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1 @@
+com.ruoyi.common.tenant.config.TenantConfig
diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/FilterConfig.java b/ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/FilterConfig.java
index 098ad815f..7444f733b 100644
--- a/ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/FilterConfig.java
+++ b/ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/FilterConfig.java
@@ -30,7 +30,7 @@ public class FilterConfig {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setDispatcherTypes(DispatcherType.REQUEST);
registration.setFilter(new XssFilter());
- registration.addUrlPatterns(StringUtils.split(xssProperties.getUrlPatterns(), ","));
+ registration.addUrlPatterns(StringUtils.split(xssProperties.getUrlPatterns(), StringUtils.SEPARATOR));
registration.setName("xssFilter");
registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
Map initParameters = new HashMap<>();
diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/properties/CaptchaProperties.java b/ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/properties/CaptchaProperties.java
index 9bebbd9fe..e4b8e1589 100644
--- a/ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/properties/CaptchaProperties.java
+++ b/ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/properties/CaptchaProperties.java
@@ -14,6 +14,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "captcha")
public class CaptchaProperties {
+ private Boolean enable;
+
/**
* 验证码类型
*/
diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/filter/XssFilter.java b/ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/filter/XssFilter.java
index a11660f6a..5ae7e7d7b 100644
--- a/ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/filter/XssFilter.java
+++ b/ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/filter/XssFilter.java
@@ -25,7 +25,7 @@ public class XssFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
String tempExcludes = filterConfig.getInitParameter("excludes");
if (StringUtils.isNotEmpty(tempExcludes)) {
- String[] url = tempExcludes.split(",");
+ String[] url = tempExcludes.split(StringUtils.SEPARATOR);
for (int i = 0; url != null && i < url.length; i++) {
excludes.add(url[i]);
}
diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java
index d16fe9d71..3747b61b3 100644
--- a/ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java
+++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java
@@ -45,7 +45,7 @@ import java.util.concurrent.TimeUnit;
@RequestMapping("/demo/demo")
public class TestDemoController extends BaseController {
- private final ITestDemoService iTestDemoService;
+ private final ITestDemoService testDemoService;
/**
* 查询测试单表列表
@@ -53,7 +53,7 @@ public class TestDemoController extends BaseController {
@SaCheckPermission("demo:demo:list")
@GetMapping("/list")
public TableDataInfo list(@Validated(QueryGroup.class) TestDemoBo bo, PageQuery pageQuery) {
- return iTestDemoService.queryPageList(bo, pageQuery);
+ return testDemoService.queryPageList(bo, pageQuery);
}
/**
@@ -62,7 +62,7 @@ public class TestDemoController extends BaseController {
@SaCheckPermission("demo:demo:list")
@GetMapping("/page")
public TableDataInfo page(@Validated(QueryGroup.class) TestDemoBo bo, PageQuery pageQuery) {
- return iTestDemoService.customPageList(bo, pageQuery);
+ return testDemoService.customPageList(bo, pageQuery);
}
/**
@@ -77,7 +77,7 @@ public class TestDemoController extends BaseController {
ExcelResult excelResult = ExcelUtil.importExcel(file.getInputStream(), TestDemoImportVo.class, true);
List volist = excelResult.getList();
List list = BeanUtil.copyToList(volist, TestDemo.class);
- iTestDemoService.saveBatch(list);
+ testDemoService.saveBatch(list);
return R.ok(excelResult.getAnalysis());
}
@@ -88,7 +88,7 @@ public class TestDemoController extends BaseController {
@Log(title = "测试单表", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(@Validated TestDemoBo bo, HttpServletResponse response) {
- List list = iTestDemoService.queryList(bo);
+ List list = testDemoService.queryList(bo);
// 测试雪花id导出
// for (TestDemoVo vo : list) {
// vo.setId(1234567891234567893L);
@@ -105,7 +105,7 @@ public class TestDemoController extends BaseController {
@GetMapping("/{id}")
public R getInfo(@NotNull(message = "主键不能为空")
@PathVariable("id") Long id) {
- return R.ok(iTestDemoService.queryById(id));
+ return R.ok(testDemoService.queryById(id));
}
/**
@@ -119,7 +119,7 @@ public class TestDemoController extends BaseController {
// 使用校验工具对标 @Validated(AddGroup.class) 注解
// 用于在非 Controller 的地方校验对象
ValidatorUtils.validate(bo, AddGroup.class);
- return toAjax(iTestDemoService.insertByBo(bo));
+ return toAjax(testDemoService.insertByBo(bo));
}
/**
@@ -130,7 +130,7 @@ public class TestDemoController extends BaseController {
@RepeatSubmit
@PutMapping()
public R edit(@Validated(EditGroup.class) @RequestBody TestDemoBo bo) {
- return toAjax(iTestDemoService.updateByBo(bo));
+ return toAjax(testDemoService.updateByBo(bo));
}
/**
@@ -143,6 +143,6 @@ public class TestDemoController extends BaseController {
@DeleteMapping("/{ids}")
public R remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
- return toAjax(iTestDemoService.deleteWithValidByIds(Arrays.asList(ids), true));
+ return toAjax(testDemoService.deleteWithValidByIds(Arrays.asList(ids), true));
}
}
diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestTreeController.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestTreeController.java
index ec8821c20..51a680d2b 100644
--- a/ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestTreeController.java
+++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestTreeController.java
@@ -35,7 +35,7 @@ import java.util.List;
@RequestMapping("/demo/tree")
public class TestTreeController extends BaseController {
- private final ITestTreeService iTestTreeService;
+ private final ITestTreeService testTreeService;
/**
* 查询测试树表列表
@@ -43,7 +43,7 @@ public class TestTreeController extends BaseController {
@SaCheckPermission("demo:tree:list")
@GetMapping("/list")
public R> list(@Validated(QueryGroup.class) TestTreeBo bo) {
- List list = iTestTreeService.queryList(bo);
+ List list = testTreeService.queryList(bo);
return R.ok(list);
}
@@ -54,7 +54,7 @@ public class TestTreeController extends BaseController {
@Log(title = "测试树表", businessType = BusinessType.EXPORT)
@GetMapping("/export")
public void export(@Validated TestTreeBo bo, HttpServletResponse response) {
- List list = iTestTreeService.queryList(bo);
+ List list = testTreeService.queryList(bo);
ExcelUtil.exportExcel(list, "测试树表", TestTreeVo.class, response);
}
@@ -67,7 +67,7 @@ public class TestTreeController extends BaseController {
@GetMapping("/{id}")
public R getInfo(@NotNull(message = "主键不能为空")
@PathVariable("id") Long id) {
- return R.ok(iTestTreeService.queryById(id));
+ return R.ok(testTreeService.queryById(id));
}
/**
@@ -78,7 +78,7 @@ public class TestTreeController extends BaseController {
@RepeatSubmit
@PostMapping()
public R add(@Validated(AddGroup.class) @RequestBody TestTreeBo bo) {
- return toAjax(iTestTreeService.insertByBo(bo));
+ return toAjax(testTreeService.insertByBo(bo));
}
/**
@@ -89,7 +89,7 @@ public class TestTreeController extends BaseController {
@RepeatSubmit
@PutMapping()
public R edit(@Validated(EditGroup.class) @RequestBody TestTreeBo bo) {
- return toAjax(iTestTreeService.updateByBo(bo));
+ return toAjax(testTreeService.updateByBo(bo));
}
/**
@@ -102,6 +102,6 @@ public class TestTreeController extends BaseController {
@DeleteMapping("/{ids}")
public R remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
- return toAjax(iTestTreeService.deleteWithValidByIds(Arrays.asList(ids), true));
+ return toAjax(testTreeService.deleteWithValidByIds(Arrays.asList(ids), true));
}
}
diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestTree.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestTree.java
index 056700254..14f135571 100644
--- a/ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestTree.java
+++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestTree.java
@@ -4,7 +4,7 @@ import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.Version;
-import com.ruoyi.common.mybatis.core.domain.TreeEntity;
+import com.ruoyi.common.mybatis.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -19,7 +19,7 @@ import java.io.Serial;
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("test_tree")
-public class TestTree extends TreeEntity {
+public class TestTree extends BaseEntity {
@Serial
private static final long serialVersionUID = 1L;
@@ -30,6 +30,11 @@ public class TestTree extends TreeEntity {
@TableId(value = "id")
private Long id;
+ /**
+ * 父ID
+ */
+ private Long parentId;
+
/**
* 部门id
*/
diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestTreeBo.java b/ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestTreeBo.java
index 7e6daab12..8251b5b64 100644
--- a/ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestTreeBo.java
+++ b/ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestTreeBo.java
@@ -2,12 +2,11 @@ package com.ruoyi.demo.domain.bo;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
-import com.ruoyi.common.mybatis.core.domain.TreeEntity;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-
+import com.ruoyi.common.mybatis.core.domain.BaseEntity;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
/**
* 测试树表业务对象 test_tree
@@ -18,7 +17,7 @@ import jakarta.validation.constraints.NotNull;
@Data
@EqualsAndHashCode(callSuper = true)
-public class TestTreeBo extends TreeEntity {
+public class TestTreeBo extends BaseEntity {
/**
* 主键
@@ -26,6 +25,11 @@ public class TestTreeBo extends TreeEntity {
@NotNull(message = "主键不能为空", groups = {EditGroup.class})
private Long id;
+ /**
+ * 父ID
+ */
+ private Long parentId;
+
/**
* 部门id
*/
diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/constant/GenConstants.java b/ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/constant/GenConstants.java
index f2c59c9e5..eb0d12f09 100644
--- a/ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/constant/GenConstants.java
+++ b/ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/constant/GenConstants.java
@@ -69,35 +69,30 @@ public interface GenConstants {
* BO对象 不需要添加字段
*/
String[] COLUMNNAME_NOT_ADD = {"create_dept", "create_by", "create_time", "del_flag", "update_by",
- "update_time", "version"};
+ "update_time", "version", "tenant_id"};
/**
* BO对象 不需要编辑字段
*/
String[] COLUMNNAME_NOT_EDIT = {"create_dept", "create_by", "create_time", "del_flag", "update_by",
- "update_time", "version"};
+ "update_time", "version", "tenant_id"};
/**
* VO对象 不需要返回字段
*/
String[] COLUMNNAME_NOT_LIST = {"create_dept", "create_by", "create_time", "del_flag", "update_by",
- "update_time", "version"};
+ "update_time", "version", "tenant_id"};
/**
* BO对象 不需要查询字段
*/
String[] COLUMNNAME_NOT_QUERY = {"id", "create_dept", "create_by", "create_time", "del_flag", "update_by",
- "update_time", "remark", "version"};
+ "update_time", "remark", "version", "tenant_id"};
/**
* Entity基类字段
*/
- String[] BASE_ENTITY = {"createDept", "createBy", "createTime", "updateBy", "updateTime"};
-
- /**
- * Tree基类字段
- */
- String[] TREE_ENTITY = {"parentName", "parentId", "children"};
+ String[] BASE_ENTITY = {"createDept", "createBy", "createTime", "updateBy", "updateTime", "tenantId"};
/**
* 文本框
diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java b/ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java
index dec9c8bde..ed1ea1a8f 100644
--- a/ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java
+++ b/ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java
@@ -1,14 +1,16 @@
package com.ruoyi.generator.domain;
-import com.baomidou.mybatisplus.annotation.*;
-import com.ruoyi.generator.constant.GenConstants;
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.mybatis.core.domain.BaseEntity;
+import com.ruoyi.generator.constant.GenConstants;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
-import org.apache.commons.lang3.ArrayUtils;
import java.util.List;
@@ -183,10 +185,6 @@ public class GenTable extends BaseEntity {
}
public static boolean isSuperColumn(String tplCategory, String javaField) {
- if (isTree(tplCategory)) {
- return StringUtils.equalsAnyIgnoreCase(javaField,
- ArrayUtils.addAll(GenConstants.TREE_ENTITY, GenConstants.BASE_ENTITY));
- }
return StringUtils.equalsAnyIgnoreCase(javaField, GenConstants.BASE_ENTITY);
}
}
diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java b/ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java
index 5a4c3cf18..f374bf9f8 100644
--- a/ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java
+++ b/ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java
@@ -212,7 +212,7 @@ public class GenTableColumn extends BaseEntity {
if (StringUtils.isNotEmpty(value)) {
Object startStr = value.subSequence(0, 1);
String endStr = value.substring(1);
- sb.append("").append(startStr).append("=").append(endStr).append(",");
+ sb.append(StringUtils.EMPTY).append(startStr).append("=").append(endStr).append(StringUtils.SEPARATOR);
}
}
return sb.deleteCharAt(sb.length() - 1).toString();
diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java b/ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java
index 77275ef69..5a9286f3b 100644
--- a/ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java
+++ b/ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java
@@ -59,7 +59,7 @@ public class GenUtils {
column.setHtmlType(GenConstants.HTML_INPUT);
// 如果是浮点型 统一用BigDecimal
- String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), ",");
+ String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), StringUtils.SEPARATOR);
if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0) {
column.setJavaType(GenConstants.TYPE_BIGDECIMAL);
}
@@ -168,7 +168,7 @@ public class GenUtils {
boolean autoRemovePre = GenConfig.getAutoRemovePre();
String tablePrefix = GenConfig.getTablePrefix();
if (autoRemovePre && StringUtils.isNotEmpty(tablePrefix)) {
- String[] searchList = StringUtils.split(tablePrefix, ",");
+ String[] searchList = StringUtils.split(tablePrefix, StringUtils.SEPARATOR);
tableName = replaceFirst(tableName, searchList);
}
return StringUtils.convertToCamelCase(tableName);
diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java b/ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java
index 7b3cd018b..b1557fca2 100644
--- a/ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java
+++ b/ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java
@@ -225,7 +225,7 @@ public class VelocityUtils {
*/
public static String getDicts(GenTable genTable) {
List columns = genTable.getColumns();
- Set dicts = new HashSet();
+ Set dicts = new HashSet<>();
addDicts(dicts, columns);
return StringUtils.join(dicts, ", ");
}
diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/bo.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/bo.java.vm
index 1989e06d0..e23520297 100644
--- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/bo.java.vm
+++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/bo.java.vm
@@ -1,21 +1,16 @@
package ${packageName}.domain.bo;
+import com.ruoyi.common.mybatis.core.domain.BaseEntity;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
-
-import java.util.Date;
-
#foreach ($import in $importList)
import ${import};
#end
-#if($table.crud || $table.sub)
-import com.ruoyi.common.mybatis.core.domain.BaseEntity;
-#elseif($table.tree)
-import com.ruoyi.common.mybatis.core.domain.TreeEntity;
-#end
+
+import java.util.Date;
/**
* ${functionName}业务对象 ${tableName}
@@ -23,15 +18,9 @@ import com.ruoyi.common.mybatis.core.domain.TreeEntity;
* @author ${author}
* @date ${datetime}
*/
-#if($table.crud || $table.sub)
-#set($Entity="BaseEntity")
-#elseif($table.tree)
-#set($Entity="TreeEntity<${ClassName}Bo>")
-#end
-
@Data
@EqualsAndHashCode(callSuper = true)
-public class ${ClassName}Bo extends ${Entity} {
+public class ${ClassName}Bo extends BaseEntity {
#foreach ($column in $columns)
#if(!$table.isSuperColumn($column.javaField) && ($column.query || $column.isInsert || $column.isEdit))
diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm
index ee6fdd2e4..761284fb9 100644
--- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm
+++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm
@@ -39,7 +39,7 @@ import com.ruoyi.common.mybatis.core.page.TableDataInfo;
@RequestMapping("/${moduleName}/${businessName}")
public class ${ClassName}Controller extends BaseController {
- private final I${ClassName}Service i${ClassName}Service;
+ private final I${ClassName}Service ${className}Service;
/**
* 查询${functionName}列表
@@ -52,7 +52,7 @@ public class ${ClassName}Controller extends BaseController {
}
#elseif($table.tree)
public R> list(${ClassName}Bo bo) {
- List<${ClassName}Vo> list = i${ClassName}Service.queryList(bo);
+ List<${ClassName}Vo> list = ${className}Service.queryList(bo);
return R.ok(list);
}
#end
@@ -64,7 +64,7 @@ public class ${ClassName}Controller extends BaseController {
@Log(title = "${functionName}", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(${ClassName}Bo bo, HttpServletResponse response) {
- List<${ClassName}Vo> list = i${ClassName}Service.queryList(bo);
+ List<${ClassName}Vo> list = ${className}Service.queryList(bo);
ExcelUtil.exportExcel(list, "${functionName}", ${ClassName}Vo.class, response);
}
@@ -77,7 +77,7 @@ public class ${ClassName}Controller extends BaseController {
@GetMapping("/{${pkColumn.javaField}}")
public R<${ClassName}Vo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable ${pkColumn.javaType} ${pkColumn.javaField}) {
- return R.ok(i${ClassName}Service.queryById(${pkColumn.javaField}));
+ return R.ok(${className}Service.queryById(${pkColumn.javaField}));
}
/**
@@ -88,7 +88,7 @@ public class ${ClassName}Controller extends BaseController {
@RepeatSubmit()
@PostMapping()
public R add(@Validated(AddGroup.class) @RequestBody ${ClassName}Bo bo) {
- return toAjax(i${ClassName}Service.insertByBo(bo));
+ return toAjax(${className}Service.insertByBo(bo));
}
/**
@@ -99,7 +99,7 @@ public class ${ClassName}Controller extends BaseController {
@RepeatSubmit()
@PutMapping()
public R edit(@Validated(EditGroup.class) @RequestBody ${ClassName}Bo bo) {
- return toAjax(i${ClassName}Service.updateByBo(bo));
+ return toAjax(${className}Service.updateByBo(bo));
}
/**
@@ -112,6 +112,6 @@ public class ${ClassName}Controller extends BaseController {
@DeleteMapping("/{${pkColumn.javaField}s}")
public R remove(@NotEmpty(message = "主键不能为空")
@PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) {
- return toAjax(i${ClassName}Service.deleteWithValidByIds(List.of(${pkColumn.javaField}s), true));
+ return toAjax(${className}Service.deleteWithValidByIds(List.of(${pkColumn.javaField}s), true));
}
}
diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/domain.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/domain.java.vm
index 6e0f6a23e..f176ecd5a 100644
--- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/domain.java.vm
+++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/domain.java.vm
@@ -1,21 +1,25 @@
package ${packageName}.domain;
+#foreach ($column in $columns)
+#if($column.javaField=='tenantId')
+#set($IsTenant=1)
+#end
+#end
+#if($IsTenant==1)
+import com.ruoyi.common.tenant.core.TenantEntity;
+#else
+import com.ruoyi.common.mybatis.core.domain.BaseEntity;
+#end
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
-import java.io.Serial;
-import java.io.Serializable;
-import java.util.Date;
-import java.math.BigDecimal;
-
#foreach ($import in $importList)
import ${import};
#end
-#if($table.crud || $table.sub)
-import com.ruoyi.common.mybatis.core.domain.BaseEntity;
-#elseif($table.tree)
-import com.ruoyi.common.mybatis.core.domain.TreeEntity;
-#end
+
+import java.io.Serial;
+import java.util.Date;
+import java.math.BigDecimal;
/**
* ${functionName}对象 ${tableName}
@@ -23,10 +27,10 @@ import com.ruoyi.common.mybatis.core.domain.TreeEntity;
* @author ${author}
* @date ${datetime}
*/
-#if($table.crud || $table.sub)
- #set($Entity="BaseEntity")
-#elseif($table.tree)
- #set($Entity="TreeEntity<${ClassName}>")
+#if($IsTenant==1)
+#set($Entity="TenantEntity")
+#else
+#set($Entity="BaseEntity")
#end
@Data
@EqualsAndHashCode(callSuper = true)
@@ -51,6 +55,7 @@ public class ${ClassName} extends ${Entity} {
@TableId(value = "$column.columnName")
#end
private $column.javaType $column.javaField;
+
#end
#end
diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm
index 2bc699a20..1dcf0b915 100644
--- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm
+++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm
@@ -333,6 +333,7 @@ export default {
// 表单校验
rules: {
#foreach ($column in $columns)
+#if($column.isInsert || $column.isEdit)
#if($column.required)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
@@ -344,6 +345,7 @@ export default {
{ required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select" || $column.htmlType == "radio")"change"#else"blur"#end }
]#if($foreach.count != $columns.size()),#end
#end
+#end
#end
}
};
@@ -404,11 +406,13 @@ export default {
reset() {
this.form = {
#foreach ($column in $columns)
+#if($column.isInsert || $column.isEdit)
#if($column.htmlType == "checkbox")
$column.javaField: []#if($foreach.count != $columns.size()),#end
#else
$column.javaField: null#if($foreach.count != $columns.size()),#end
#end
+#end
#end
};
this.resetForm("form");
diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm
index 9f0131b71..3e7628e29 100644
--- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm
+++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm
@@ -343,6 +343,7 @@ export default {
// 表单校验
rules: {
#foreach ($column in $columns)
+#if($column.isInsert || $column.isEdit)
#if($column.required)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
@@ -354,6 +355,7 @@ export default {
{ required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select" || $column.htmlType == "radio")"change"#else"blur"#end }
]#if($foreach.count != $columns.size()),#end
#end
+#end
#end
}
};
@@ -395,11 +397,13 @@ export default {
reset() {
this.form = {
#foreach ($column in $columns)
+#if($column.isInsert || $column.isEdit)
#if($column.htmlType == "checkbox")
$column.javaField: []#if($foreach.count != $columns.size()),#end
#else
$column.javaField: undefined#if($foreach.count != $columns.size()),#end
#end
+#end
#end
};
this.resetForm("form");
diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm
index 663893a2c..31091a3b1 100644
--- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm
+++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm
@@ -304,6 +304,7 @@ const data = reactive({
},
rules: {
#foreach ($column in $columns)
+#if($column.isInsert || $column.isEdit)
#if($column.required)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
@@ -315,6 +316,7 @@ const data = reactive({
{ required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select" || $column.htmlType == "radio")"change"#else"blur"#end }
]#if($foreach.count != $columns.size()),#end
#end
+#end
#end
}
});
@@ -365,11 +367,13 @@ function cancel() {
function reset() {
form.value = {
#foreach ($column in $columns)
+#if($column.isInsert || $column.isEdit)
#if($column.htmlType == "checkbox")
$column.javaField: []#if($foreach.count != $columns.size()),#end
#else
$column.javaField: null#if($foreach.count != $columns.size()),#end
#end
+#end
#end
};
proxy.resetForm("${businessName}Ref");
diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm
index e623295c1..2a2deb49b 100644
--- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm
+++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm
@@ -315,6 +315,7 @@ const data = reactive({
},
rules: {
#foreach ($column in $columns)
+#if($column.isInsert || $column.isEdit)
#if($column.required)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
@@ -326,6 +327,7 @@ const data = reactive({
{ required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select" || $column.htmlType == "radio")"change"#else"blur"#end }
]#if($foreach.count != $columns.size()),#end
#end
+#end
#end
}
});
@@ -367,11 +369,13 @@ function cancel() {
function reset() {
form.value = {
#foreach ($column in $columns)
+#if($column.isInsert || $column.isEdit)
#if($column.htmlType == "checkbox")
$column.javaField: []#if($foreach.count != $columns.size()),#end
#else
$column.javaField: null#if($foreach.count != $columns.size()),#end
#end
+#end
#end
};
proxy.resetForm("${businessName}Ref");
diff --git a/ruoyi-modules/ruoyi-system/pom.xml b/ruoyi-modules/ruoyi-system/pom.xml
index 7f3572b61..be1c3ceee 100644
--- a/ruoyi-modules/ruoyi-system/pom.xml
+++ b/ruoyi-modules/ruoyi-system/pom.xml
@@ -63,7 +63,7 @@
com.ruoyi
- ruoyi-common-satoken
+ ruoyi-common-tenant
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/CacheController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/CacheController.java
deleted file mode 100644
index 03fc761ad..000000000
--- a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/CacheController.java
+++ /dev/null
@@ -1,168 +0,0 @@
-package com.ruoyi.system.controller.monitor;
-
-import cn.dev33.satoken.annotation.SaCheckPermission;
-import cn.hutool.core.collection.CollUtil;
-import com.ruoyi.common.core.constant.CacheConstants;
-import com.ruoyi.common.core.constant.CacheNames;
-import com.ruoyi.common.core.domain.R;
-import com.ruoyi.common.core.utils.StreamUtils;
-import com.ruoyi.common.core.utils.StringUtils;
-import com.ruoyi.common.json.utils.JsonUtils;
-import com.ruoyi.common.redis.utils.CacheUtils;
-import com.ruoyi.common.redis.utils.RedisUtils;
-import com.ruoyi.system.domain.SysCache;
-import com.ruoyi.system.domain.vo.CacheListInfoVo;
-import lombok.RequiredArgsConstructor;
-import org.redisson.spring.data.connection.RedissonConnectionFactory;
-import org.springframework.data.redis.connection.RedisConnection;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.*;
-
-/**
- * 缓存监控
- *
- * @author Lion Li
- */
-@RequiredArgsConstructor
-@RestController
-@RequestMapping("/monitor/cache")
-public class CacheController {
-
- private final RedissonConnectionFactory connectionFactory;
-
- private final static List CACHES = new ArrayList<>();
-
- static {
- CACHES.add(new SysCache(CacheConstants.LOGIN_TOKEN_KEY, "用户信息"));
- CACHES.add(new SysCache(CacheConstants.ONLINE_TOKEN_KEY, "在线用户"));
- CACHES.add(new SysCache(CacheNames.SYS_CONFIG, "配置信息"));
- CACHES.add(new SysCache(CacheNames.SYS_DICT, "数据字典"));
- CACHES.add(new SysCache(CacheConstants.CAPTCHA_CODE_KEY, "验证码"));
- CACHES.add(new SysCache(CacheConstants.REPEAT_SUBMIT_KEY, "防重提交"));
- CACHES.add(new SysCache(CacheConstants.RATE_LIMIT_KEY, "限流处理"));
- CACHES.add(new SysCache(CacheNames.SYS_OSS_CONFIG, "OSS配置"));
- CACHES.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数"));
- }
-
- /**
- * 获取缓存监控列表
- */
- @SaCheckPermission("monitor:cache:list")
- @GetMapping()
- public R getInfo() throws Exception {
- RedisConnection connection = connectionFactory.getConnection();
- Properties commandStats = connection.commands().info("commandstats");
-
- List