Commit 79311ecc71f0c6beabe0e5f84e1423ce745a5f09

Authored by YunaiV
1 parent 27c30279

* 【新增】后端 `yudao.tenant.enable` 配置项,前端 `VUE_APP_TENANT_ENABLE` 配置项,用于开关租户功能

* 【优化】调整默认所有表开启多租户的特性,可通过 `yudao.tenant.ignore-tables` 配置项进行忽略,替代原本默认不开启的策略
* 【新增】通过 `yudao.tenant.ignore-urls` 配置忽略多租户的请求,例如说 ,例如说短信回调、支付回调等 Open API
Showing 36 changed files with 220 additions and 122 deletions
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/WebFilterOrderEnum.java
... ... @@ -3,7 +3,7 @@ package cn.iocoder.yudao.framework.common.enums;
3 3 /**
4 4 * Web 过滤器顺序的枚举类,保证过滤器按照符合我们的预期
5 5 *
6   - * 考虑到每个 starter 都需要用到该工具类,所以放到 common 模块下的 util 包下
  6 + * 考虑到每个 starter 都需要用到该工具类,所以放到 common 模块下的 util 包下
7 7 *
8 8 * @author 芋道源码
9 9 */
... ...
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/TenantProperties.java
... ... @@ -14,22 +14,28 @@ import java.util.Set;
14 14 @Data
15 15 public class TenantProperties {
16 16  
17   -// /**
18   -// * 租户是否开启
19   -// */
20   -// private static final Boolean ENABLE_DEFAULT = true;
21   -//
22   -// /**
23   -// * 是否开启
24   -// */
25   -// private Boolean enable = ENABLE_DEFAULT;
  17 + /**
  18 + * 租户是否开启
  19 + */
  20 + private static final Boolean ENABLE_DEFAULT = true;
  21 +
  22 + /**
  23 + * 是否开启
  24 + */
  25 + private Boolean enable = ENABLE_DEFAULT;
  26 +
  27 + /**
  28 + * 需要忽略多租户的请求
  29 + *
  30 + * 默认情况下,每个请求需要带上 tenant-id 的请求头。但是,部分请求是无需带上的,例如说短信回调、支付回调等 Open API!
  31 + */
  32 + private Set<String> ignoreUrls;
26 33  
27 34 /**
28   - * 需要多租户的表
  35 + * 需要忽略多租户的表
29 36 *
30   - * 由于多租户并不作为 yudao 项目的重点功能,更多是扩展性的功能,所以采用正向配置需要多租户的表。
31   - * 如果需要,你可以改成 ignoreTables 来取消部分不需要的表
  37 + * 即默认所有表都开启多租户的功能,所以记得添加对应的 tenant_id 字段哟
32 38 */
33   - private Set<String> tables;
  39 + private Set<String> ignoreTables;
34 40  
35 41 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantDatabaseAutoConfiguration.java
... ... @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
4 4 import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor;
5 5 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
6 6 import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
  7 +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
7 8 import org.springframework.boot.context.properties.EnableConfigurationProperties;
8 9 import org.springframework.context.annotation.Bean;
9 10 import org.springframework.context.annotation.Configuration;
... ... @@ -14,6 +15,8 @@ import org.springframework.context.annotation.Configuration;
14 15 * @author 芋道源码
15 16 */
16 17 @Configuration
  18 +// 允许使用 yudao.tenant.enable=false 禁用多租户
  19 +@ConditionalOnProperty(prefix = "yudao.tenant", value = "enable", matchIfMissing = true)
17 20 @EnableConfigurationProperties(TenantProperties.class)
18 21 public class YudaoTenantDatabaseAutoConfiguration {
19 22  
... ...
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantJobAutoConfiguration.java
... ... @@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.tenant.core.job.TenantJobHandlerDecorator;
7 7 import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;
8 8 import org.springframework.beans.BeansException;
9 9 import org.springframework.beans.factory.config.BeanPostProcessor;
  10 +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
10 11 import org.springframework.context.annotation.Bean;
11 12 import org.springframework.context.annotation.Configuration;
12 13  
... ... @@ -16,6 +17,8 @@ import org.springframework.context.annotation.Configuration;
16 17 * @author 芋道源码
17 18 */
18 19 @Configuration
  20 +// 允许使用 yudao.tenant.enable=false 禁用多租户
  21 +@ConditionalOnProperty(prefix = "yudao.tenant", value = "enable", matchIfMissing = true)
19 22 public class YudaoTenantJobAutoConfiguration {
20 23  
21 24 @Bean
... ...
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantMQAutoConfiguration.java
1 1 package cn.iocoder.yudao.framework.tenant.config;
2 2  
3 3 import cn.iocoder.yudao.framework.tenant.core.mq.TenantRedisMessageInterceptor;
  4 +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
4 5 import org.springframework.context.annotation.Bean;
5 6 import org.springframework.context.annotation.Configuration;
6 7  
... ... @@ -10,6 +11,8 @@ import org.springframework.context.annotation.Configuration;
10 11 * @author 芋道源码
11 12 */
12 13 @Configuration
  14 +// 允许使用 yudao.tenant.enable=false 禁用多租户
  15 +@ConditionalOnProperty(prefix = "yudao.tenant", value = "enable", matchIfMissing = true)
13 16 public class YudaoTenantMQAutoConfiguration {
14 17  
15 18 @Bean
... ...
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantSecurityAutoConfiguration.java
... ... @@ -2,6 +2,9 @@ package cn.iocoder.yudao.framework.tenant.config;
2 2  
3 3 import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
4 4 import cn.iocoder.yudao.framework.tenant.core.security.TenantSecurityWebFilter;
  5 +import cn.iocoder.yudao.framework.web.config.WebProperties;
  6 +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  7 +import org.springframework.boot.context.properties.EnableConfigurationProperties;
5 8 import org.springframework.boot.web.servlet.FilterRegistrationBean;
6 9 import org.springframework.context.annotation.Bean;
7 10 import org.springframework.context.annotation.Configuration;
... ... @@ -12,12 +15,16 @@ import org.springframework.context.annotation.Configuration;
12 15 * @author 芋道源码
13 16 */
14 17 @Configuration
  18 +// 允许使用 yudao.tenant.enable=false 禁用多租户
  19 +@ConditionalOnProperty(prefix = "yudao.tenant", value = "enable", matchIfMissing = true)
  20 +@EnableConfigurationProperties(TenantProperties.class)
15 21 public class YudaoTenantSecurityAutoConfiguration {
16 22  
17 23 @Bean
18   - public FilterRegistrationBean<TenantSecurityWebFilter> tenantSecurityWebFilter() {
  24 + public FilterRegistrationBean<TenantSecurityWebFilter> tenantSecurityWebFilter(TenantProperties tenantProperties,
  25 + WebProperties webProperties) {
19 26 FilterRegistrationBean<TenantSecurityWebFilter> registrationBean = new FilterRegistrationBean<>();
20   - registrationBean.setFilter(new TenantSecurityWebFilter());
  27 + registrationBean.setFilter(new TenantSecurityWebFilter(tenantProperties, webProperties));
21 28 registrationBean.setOrder(WebFilterOrderEnum.TENANT_SECURITY_FILTER);
22 29 return registrationBean;
23 30 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantWebAutoConfiguration.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.tenant.config;
2 2  
3 3 import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
4 4 import cn.iocoder.yudao.framework.tenant.core.web.TenantContextWebFilter;
  5 +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
5 6 import org.springframework.boot.web.servlet.FilterRegistrationBean;
6 7 import org.springframework.context.annotation.Bean;
7 8 import org.springframework.context.annotation.Configuration;
... ... @@ -12,6 +13,8 @@ import org.springframework.context.annotation.Configuration;
12 13 * @author 芋道源码
13 14 */
14 15 @Configuration
  16 +// 允许使用 yudao.tenant.enable=false 禁用多租户
  17 +@ConditionalOnProperty(prefix = "yudao.tenant", value = "enable", matchIfMissing = true)
15 18 public class YudaoTenantWebAutoConfiguration {
16 19  
17 20 @Bean
... ...
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/context/TenantContextHolder.java
... ... @@ -33,7 +33,7 @@ public class TenantContextHolder {
33 33 public static Long getRequiredTenantId() {
34 34 Long tenantId = getTenantId();
35 35 if (tenantId == null) {
36   - throw new NullPointerException("TenantContextHolder 不存在租户编号");
  36 + throw new NullPointerException("TenantContextHolder 不存在租户编号"); // TODO 芋艿:增加文档链接
37 37 }
38 38 return tenantId;
39 39 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantDatabaseInterceptor.java
... ... @@ -3,8 +3,6 @@ package cn.iocoder.yudao.framework.tenant.core.db;
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.iocoder.yudao.framework.tenant.config.TenantProperties;
5 5 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
6   -import com.baomidou.mybatisplus.core.metadata.TableInfo;
7   -import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
8 6 import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
9 7 import lombok.AllArgsConstructor;
10 8 import net.sf.jsqlparser.expression.Expression;
... ... @@ -27,13 +25,7 @@ public class TenantDatabaseInterceptor implements TenantLineHandler {
27 25  
28 26 @Override
29 27 public boolean ignoreTable(String tableName) {
30   - // 如果实体类继承 TenantBaseDO 类,则是多租户表,不进行忽略
31   - TableInfo tableInfo = TableInfoHelper.getTableInfo(tableName);
32   - if (tableInfo != null && TenantBaseDO.class.isAssignableFrom(tableInfo.getEntityType())) {
33   - return false;
34   - }
35   - // 不包含,说明要过滤
36   - return !CollUtil.contains(properties.getTables(), tableName);
  28 + return CollUtil.contains(properties.getIgnoreTables(), tableName);
37 29 }
38 30  
39 31 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/security/TenantSecurityWebFilter.java
1 1 package cn.iocoder.yudao.framework.tenant.core.security;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
4 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 6 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
6 7 import cn.iocoder.yudao.framework.security.core.LoginUser;
7 8 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
  9 +import cn.iocoder.yudao.framework.tenant.config.TenantProperties;
8 10 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
  11 +import cn.iocoder.yudao.framework.web.config.WebProperties;
  12 +import cn.iocoder.yudao.framework.web.core.filter.ApiRequestFilter;
9 13 import lombok.extern.slf4j.Slf4j;
10   -import org.springframework.web.filter.OncePerRequestFilter;
  14 +import org.springframework.util.AntPathMatcher;
11 15  
12 16 import javax.servlet.FilterChain;
13 17 import javax.servlet.ServletException;
... ... @@ -18,34 +22,72 @@ import java.util.Objects;
18 22  
19 23 /**
20 24 * 多租户 Security Web 过滤器
21   - * 校验用户访问的租户,是否是其所在的租户,避免越权问题
  25 + * 1. 如果是登陆的用户,校验是否有权限访问该租户,避免越权问题。
  26 + * 2. 如果请求未带租户的编号,检查是否是忽略的 URL,否则也不允许访问。
  27 + *
  28 + * 校验用户访问的租户,是否是其所在的租户,
22 29 *
23 30 * @author 芋道源码
24 31 */
25 32 @Slf4j
26   -public class TenantSecurityWebFilter extends OncePerRequestFilter {
  33 +public class TenantSecurityWebFilter extends ApiRequestFilter {
  34 +
  35 + private final TenantProperties tenantProperties;
  36 + private final AntPathMatcher pathMatcher;
  37 +
  38 + public TenantSecurityWebFilter(TenantProperties tenantProperties,
  39 + WebProperties webProperties) {
  40 + super(webProperties);
  41 + this.tenantProperties = tenantProperties;
  42 + this.pathMatcher = new AntPathMatcher();
  43 + }
27 44  
28 45 @Override
29 46 protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
30 47 throws ServletException, IOException {
  48 + Long tenantId = TenantContextHolder.getTenantId();
  49 + // 1. 登陆的用户,校验是否有权限访问该租户,避免越权问题。
31 50 LoginUser user = SecurityFrameworkUtils.getLoginUser();
32   - assert user != null; // shouldNotFilter 已经校验
33   - // 校验租户是否匹配。
34   - if (!Objects.equals(user.getTenantId(), TenantContextHolder.getTenantId())) {
35   - log.error("[doFilterInternal][租户({}) User({}/{}) 越权访问租户({}) URL({}/{})]",
36   - user.getTenantId(), user.getId(), user.getUserType(),
37   - TenantContextHolder.getTenantId(), request.getRequestURI(), request.getMethod());
38   - ServletUtils.writeJSON(response, CommonResult.error(GlobalErrorCodeConstants.FORBIDDEN.getCode(),
39   - "您无权访问该租户的数据"));
  51 + if (user != null) {
  52 + // 如果获取不到租户编号,则尝试使用登陆用户的租户编号
  53 + if (tenantId == null) {
  54 + tenantId = user.getTenantId();
  55 + TenantContextHolder.setTenantId(tenantId);
  56 + // 如果传递了租户编号,则进行比对租户编号,避免越权问题
  57 + } else if (!Objects.equals(user.getTenantId(), TenantContextHolder.getTenantId())) {
  58 + log.error("[doFilterInternal][租户({}) User({}/{}) 越权访问租户({}) URL({}/{})]",
  59 + user.getTenantId(), user.getId(), user.getUserType(),
  60 + TenantContextHolder.getTenantId(), request.getRequestURI(), request.getMethod());
  61 + ServletUtils.writeJSON(response, CommonResult.error(GlobalErrorCodeConstants.FORBIDDEN.getCode(),
  62 + "您无权访问该租户的数据"));
  63 + return;
  64 + }
  65 + }
  66 +
  67 + // 2. 如果请求未带租户的编号,检查是否是忽略的 URL,否则也不允许访问。
  68 + if (tenantId == null && !isIgnoreUrl(request)) {
  69 + log.error("[doFilterInternal][URL({}/{}) 未传递租户编号]", request.getRequestURI(), request.getMethod());
  70 + ServletUtils.writeJSON(response, CommonResult.error(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
  71 + "租户的请求未传递,请进行排查"));
40 72 return;
41 73 }
  74 +
42 75 // 继续过滤
43 76 chain.doFilter(request, response);
44 77 }
45 78  
46   - @Override
47   - protected boolean shouldNotFilter(HttpServletRequest request) {
48   - return SecurityFrameworkUtils.getLoginUser() == null;
  79 + private boolean isIgnoreUrl(HttpServletRequest request) {
  80 + // 快速匹配,保证性能
  81 + if (CollUtil.contains(tenantProperties.getIgnoreUrls(), request.getRequestURI())) {
  82 + return true;
  83 + }
  84 + // 逐个 Ant 路径匹配
  85 + for (String url : tenantProperties.getIgnoreUrls()) {
  86 + if (pathMatcher.match(url, request.getRequestURI())) {
  87 + return true;
  88 + }
  89 + }
  90 + return false;
49 91 }
50 92  
51 93 }
... ...
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java
... ... @@ -8,15 +8,9 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
8 8 import org.springframework.context.annotation.Bean;
9 9 import org.springframework.context.annotation.Configuration;
10 10 import org.springframework.http.HttpHeaders;
11   -import springfox.documentation.RequestHandler;
12 11 import springfox.documentation.builders.ApiInfoBuilder;
13 12 import springfox.documentation.builders.PathSelectors;
14   -import springfox.documentation.service.ApiInfo;
15   -import springfox.documentation.service.ApiKey;
16   -import springfox.documentation.service.AuthorizationScope;
17   -import springfox.documentation.service.Contact;
18   -import springfox.documentation.service.SecurityReference;
19   -import springfox.documentation.service.SecurityScheme;
  13 +import springfox.documentation.service.*;
20 14 import springfox.documentation.spi.DocumentationType;
21 15 import springfox.documentation.spi.service.contexts.SecurityContext;
22 16 import springfox.documentation.spring.web.plugins.Docket;
... ... @@ -24,7 +18,6 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2;
24 18  
25 19 import java.util.Collections;
26 20 import java.util.List;
27   -import java.util.function.Predicate;
28 21  
29 22 import static springfox.documentation.builders.RequestHandlerSelectors.basePackage;
30 23  
... ... @@ -37,8 +30,8 @@ import static springfox.documentation.builders.RequestHandlerSelectors.basePacka
37 30 @EnableSwagger2
38 31 @EnableKnife4j
39 32 @ConditionalOnClass({Docket.class, ApiInfoBuilder.class})
40   -@ConditionalOnProperty(prefix = "yudao.swagger", value = "enable", matchIfMissing = true)
41 33 // 允许使用 swagger.enable=false 禁用 Swagger
  34 +@ConditionalOnProperty(prefix = "yudao.swagger", value = "enable", matchIfMissing = true)
42 35 @EnableConfigurationProperties(SwaggerProperties.class)
43 36 public class YudaoSwaggerAutoConfiguration {
44 37  
... ...
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/filter/ApiRequestFilter.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.web.core.filter;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.framework.web.config.WebProperties;
  5 +import lombok.RequiredArgsConstructor;
  6 +import org.springframework.web.filter.OncePerRequestFilter;
  7 +
  8 +import javax.servlet.http.HttpServletRequest;
  9 +
  10 +/**
  11 + * 过滤 /admin-api、/app-api 等 API 请求的过滤器
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@RequiredArgsConstructor
  16 +public abstract class ApiRequestFilter extends OncePerRequestFilter {
  17 +
  18 + protected final WebProperties webProperties;
  19 +
  20 + @Override
  21 + protected boolean shouldNotFilter(HttpServletRequest request) {
  22 + // 只过滤 API 请求的地址
  23 + return !StrUtil.startWithAny(request.getRequestURI(), webProperties.getAdminApi().getPrefix(),
  24 + webProperties.getAppApi().getPrefix());
  25 + }
  26 +
  27 +}
... ...
yudao-module-infra/yudao-module-infra-impl/pom.xml
... ... @@ -39,10 +39,6 @@
39 39 <groupId>cn.iocoder.boot</groupId>
40 40 <artifactId>yudao-spring-boot-starter-biz-operatelog</artifactId>
41 41 </dependency>
42   - <dependency>
43   - <groupId>cn.iocoder.boot</groupId>
44   - <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
45   - </dependency>
46 42  
47 43 <!-- Web 相关 -->
48 44 <dependency>
... ... @@ -67,6 +63,12 @@
67 63 <artifactId>yudao-spring-boot-starter-config</artifactId>
68 64 </dependency>
69 65  
  66 + <!-- Job 定时任务相关 -->
  67 + <dependency>
  68 + <groupId>cn.iocoder.boot</groupId>
  69 + <artifactId>yudao-spring-boot-starter-job</artifactId>
  70 + </dependency>
  71 +
70 72 <!-- 消息队列相关 -->
71 73 <dependency>
72 74 <groupId>cn.iocoder.boot</groupId>
... ...
yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java
... ... @@ -4,7 +4,6 @@ import cn.hutool.core.io.IoUtil;
4 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 6 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
7   -import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
8 7 import cn.iocoder.yudao.module.infra.controller.admin.file.vo.FilePageReqVO;
9 8 import cn.iocoder.yudao.module.infra.controller.admin.file.vo.FileRespVO;
10 9 import cn.iocoder.yudao.module.infra.convert.file.FileConvert;
... ... @@ -62,7 +61,6 @@ public class FileController {
62 61 @ApiOperation("下载文件")
63 62 @ApiImplicitParam(name = "path", value = "文件附件", required = true, dataTypeClass = MultipartFile.class)
64 63 public void getFile(HttpServletResponse response, @PathVariable("path") String path) throws IOException {
65   - TenantContextHolder.setNullTenantId();
66 64 FileDO file = fileService.getFile(path);
67 65 if (file == null) {
68 66 log.warn("[getFile][path({}) 文件不存在]", path);
... ...
yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileDO.java
1 1 package cn.iocoder.yudao.module.infra.dal.dataobject.file;
2 2  
3   -import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 4 import com.baomidou.mybatisplus.annotation.IdType;
5 5 import com.baomidou.mybatisplus.annotation.TableField;
6 6 import com.baomidou.mybatisplus.annotation.TableId;
... ... @@ -21,7 +21,7 @@ import java.io.InputStream;
21 21 @Builder
22 22 @NoArgsConstructor
23 23 @AllArgsConstructor
24   -public class FileDO extends TenantBaseDO {
  24 +public class FileDO extends BaseDO {
25 25  
26 26 /**
27 27 * 文件路径
... ...
yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiAccessLogDO.java
... ... @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.infra.dal.dataobject.logger;
2 2  
3 3 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
4 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5   -import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
  5 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
6 6 import com.baomidou.mybatisplus.annotation.TableId;
7 7 import com.baomidou.mybatisplus.annotation.TableName;
8 8 import lombok.*;
... ... @@ -21,7 +21,7 @@ import java.util.Date;
21 21 @Builder
22 22 @NoArgsConstructor
23 23 @AllArgsConstructor
24   -public class ApiAccessLogDO extends TenantBaseDO {
  24 +public class ApiAccessLogDO extends BaseDO {
25 25  
26 26 /**
27 27 * 编号
... ...
yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiErrorLogDO.java
1 1 package cn.iocoder.yudao.module.infra.dal.dataobject.logger;
2 2  
3 3 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
4   -import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
  4 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
5 5 import cn.iocoder.yudao.module.infra.enums.logger.ApiErrorLogProcessStatusEnum;
6 6 import com.baomidou.mybatisplus.annotation.TableId;
7 7 import com.baomidou.mybatisplus.annotation.TableName;
... ... @@ -21,7 +21,7 @@ import java.util.Date;
21 21 @Builder
22 22 @NoArgsConstructor
23 23 @AllArgsConstructor
24   -public class ApiErrorLogDO extends TenantBaseDO {
  24 +public class ApiErrorLogDO extends BaseDO {
25 25  
26 26 /**
27 27 * 编号
... ...
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/LoginLogCreateReqDTO.java
... ... @@ -54,8 +54,9 @@ public class LoginLogCreateReqDTO {
54 54 private String userIp;
55 55 /**
56 56 * 浏览器 UserAgent
  57 + *
  58 + * 允许空,原因:Job 过期登出时,是无法传递 UserAgent 的
57 59 */
58   - @NotEmpty(message = "浏览器 UserAgent 不能为空")
59 60 private String userAgent;
60 61  
61 62 }
... ...
yudao-module-system/yudao-module-system-impl/pom.xml
... ... @@ -53,11 +53,11 @@
53 53 </dependency>
54 54 <dependency>
55 55 <groupId>cn.iocoder.boot</groupId>
56   - <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
  56 + <artifactId>yudao-spring-boot-starter-biz-social</artifactId>
57 57 </dependency>
58 58 <dependency>
59 59 <groupId>cn.iocoder.boot</groupId>
60   - <artifactId>yudao-spring-boot-starter-biz-social</artifactId>
  60 + <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
61 61 </dependency>
62 62  
63 63 <!-- Web 相关 -->
... ... @@ -77,6 +77,12 @@
77 77 <artifactId>yudao-spring-boot-starter-redis</artifactId>
78 78 </dependency>
79 79  
  80 + <!-- Job 定时任务相关 -->
  81 + <dependency>
  82 + <groupId>cn.iocoder.boot</groupId>
  83 + <artifactId>yudao-spring-boot-starter-job</artifactId>
  84 + </dependency>
  85 +
80 86 <!-- 消息队列相关 -->
81 87 <dependency>
82 88 <groupId>cn.iocoder.boot</groupId>
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/UserSessionDO.java
1 1 package cn.iocoder.yudao.module.system.dal.dataobject.auth;
2 2  
3 3 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
  4 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 5 import cn.iocoder.yudao.framework.security.core.LoginUser;
5   -import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
6 6 import com.baomidou.mybatisplus.annotation.IdType;
7 7 import com.baomidou.mybatisplus.annotation.TableId;
8 8 import com.baomidou.mybatisplus.annotation.TableName;
... ... @@ -25,7 +25,7 @@ import java.util.Date;
25 25 @Data
26 26 @Builder
27 27 @EqualsAndHashCode(callSuper = true)
28   -public class UserSessionDO extends TenantBaseDO {
  28 +public class UserSessionDO extends BaseDO {
29 29  
30 30 /**
31 31 * 会话编号, 即 sessionId
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/DeptDO.java
1 1 package cn.iocoder.yudao.module.system.dal.dataobject.dept;
2 2  
3   -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
4 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5   -import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
  4 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
6 6 import com.baomidou.mybatisplus.annotation.TableId;
7 7 import com.baomidou.mybatisplus.annotation.TableName;
8 8 import lombok.Data;
... ... @@ -17,7 +17,7 @@ import lombok.EqualsAndHashCode;
17 17 @TableName("system_dept")
18 18 @Data
19 19 @EqualsAndHashCode(callSuper = true)
20   -public class DeptDO extends TenantBaseDO {
  20 +public class DeptDO extends BaseDO {
21 21  
22 22 /**
23 23 * 部门ID
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/PostDO.java
1 1 package cn.iocoder.yudao.module.system.dal.dataobject.dept;
2 2  
3 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4   -import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
  4 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
5 5 import com.baomidou.mybatisplus.annotation.TableId;
6 6 import com.baomidou.mybatisplus.annotation.TableName;
7 7 import lombok.Data;
... ... @@ -15,7 +15,7 @@ import lombok.EqualsAndHashCode;
15 15 @TableName("system_post")
16 16 @Data
17 17 @EqualsAndHashCode(callSuper = true)
18   -public class PostDO extends TenantBaseDO {
  18 +public class PostDO extends BaseDO {
19 19  
20 20 /**
21 21 * 岗位序号
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/OperateLogDO.java
... ... @@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.system.dal.dataobject.logger;
2 2  
3 3 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
4 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
5 6 import cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum;
6   -import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
7 7 import com.baomidou.mybatisplus.annotation.TableField;
8 8 import com.baomidou.mybatisplus.annotation.TableId;
9 9 import com.baomidou.mybatisplus.annotation.TableName;
... ... @@ -22,7 +22,7 @@ import java.util.Map;
22 22 @TableName(value = "system_operate_log", autoResultMap = true)
23 23 @Data
24 24 @EqualsAndHashCode(callSuper = true)
25   -public class OperateLogDO extends TenantBaseDO {
  25 +public class OperateLogDO extends BaseDO {
26 26  
27 27 /**
28 28 * {@link #javaMethodArgs} 的最大长度
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/notice/NoticeDO.java
1 1 package cn.iocoder.yudao.module.system.dal.dataobject.notice;
2 2  
3   -import cn.iocoder.yudao.module.system.enums.notice.NoticeTypeEnum;
4 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5   -import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
  4 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.yudao.module.system.enums.notice.NoticeTypeEnum;
6 6 import com.baomidou.mybatisplus.annotation.TableField;
7 7 import com.baomidou.mybatisplus.annotation.TableName;
8 8 import lombok.Data;
... ... @@ -16,7 +16,7 @@ import lombok.EqualsAndHashCode;
16 16 @TableName("system_notice")
17 17 @Data
18 18 @EqualsAndHashCode(callSuper = true)
19   -public class NoticeDO extends TenantBaseDO {
  19 +public class NoticeDO extends BaseDO {
20 20  
21 21 /**
22 22 * 公告ID
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/user/AdminUserDO.java
1 1 package cn.iocoder.yudao.module.system.dal.dataobject.user;
2 2  
3   -import cn.iocoder.yudao.module.system.enums.common.SexEnum;
4 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 4 import cn.iocoder.yudao.framework.mybatis.core.type.JsonLongSetTypeHandler;
6 5 import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
  6 +import cn.iocoder.yudao.module.system.enums.common.SexEnum;
7 7 import com.baomidou.mybatisplus.annotation.TableField;
8 8 import com.baomidou.mybatisplus.annotation.TableId;
9 9 import com.baomidou.mybatisplus.annotation.TableName;
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/job/auth/UserSessionTimeoutJob.java
... ... @@ -19,12 +19,12 @@ import javax.annotation.Resource;
19 19 public class UserSessionTimeoutJob implements JobHandler {
20 20  
21 21 @Resource
22   - private UserSessionService sysUserSessionService;
  22 + private UserSessionService userSessionService;
23 23  
24 24 @Override
25 25 public String execute(String param) throws Exception {
26 26 // 执行过期
27   - Long timeoutCount = sysUserSessionService.clearSessionTimeout();
  27 + Long timeoutCount = userSessionService.clearSessionTimeout();
28 28 // 返回结果,记录每次的超时数量
29 29 return String.format("移除在线会话数量为 %s 个", timeoutCount);
30 30 }
... ...
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java
... ... @@ -146,7 +146,7 @@ public class PermissionServiceImpl implements PermissionService {
146 146 public List<MenuDO> getRoleMenusFromCache(Collection<Long> roleIds, Collection<Integer> menuTypes,
147 147 Collection<Integer> menusStatuses) {
148 148 // 任一一个参数为空时,不返回任何菜单
149   - if (CollectionUtils.isAnyEmpty(roleIds, menusStatuses, menusStatuses)) {
  149 + if (CollectionUtils.isAnyEmpty(roleIds, menuTypes, menusStatuses)) {
150 150 return Collections.emptyList();
151 151 }
152 152 // 判断角色是否包含管理员
... ...
yudao-module-tool/yudao-module-tool-impl/pom.xml
... ... @@ -38,10 +38,6 @@
38 38 <groupId>cn.iocoder.boot</groupId>
39 39 <artifactId>yudao-spring-boot-starter-biz-dict</artifactId>
40 40 </dependency>
41   - <dependency>
42   - <groupId>cn.iocoder.boot</groupId>
43   - <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
44   - </dependency>
45 41  
46 42 <!-- Web 相关 -->
47 43 <dependency>
... ...
yudao-module-tool/yudao-module-tool-impl/src/main/java/cn/iocoder/yudao/module/tool/service/codegen/inner/CodegenBuilder.java
... ... @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.tool.service.codegen.inner;
3 3 import cn.hutool.core.map.MapUtil;
4 4 import cn.hutool.core.util.ReflectUtil;
5 5 import cn.hutool.core.util.StrUtil;
6   -import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
  6 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
7 7 import cn.iocoder.yudao.module.tool.convert.codegen.CodegenConvert;
8 8 import cn.iocoder.yudao.module.tool.dal.dataobject.codegen.CodegenColumnDO;
9 9 import cn.iocoder.yudao.module.tool.dal.dataobject.codegen.CodegenTableDO;
... ... @@ -60,7 +60,7 @@ public class CodegenBuilder {
60 60 */
61 61 public static final String TENANT_ID_FIELD = "tenant_id";
62 62 /**
63   - * {@link TenantBaseDO} 的字段
  63 + * {@link cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO} 的字段
64 64 */
65 65 public static final Set<String> BASE_DO_FIELDS = new HashSet<>();
66 66 /**
... ... @@ -96,7 +96,8 @@ public class CodegenBuilder {
96 96 .build();
97 97  
98 98 static {
99   - Arrays.stream(ReflectUtil.getFields(TenantBaseDO.class)).forEach(field -> BASE_DO_FIELDS.add(field.getName()));
  99 + Arrays.stream(ReflectUtil.getFields(BaseDO.class)).forEach(field -> BASE_DO_FIELDS.add(field.getName()));
  100 + BASE_DO_FIELDS.add(TENANT_ID_FIELD);
100 101 // 处理 OPERATION 相关的字段
101 102 CREATE_OPERATION_EXCLUDE_COLUMN.addAll(BASE_DO_FIELDS);
102 103 UPDATE_OPERATION_EXCLUDE_COLUMN.addAll(BASE_DO_FIELDS);
... ...
yudao-module-tool/yudao-module-tool-impl/src/main/java/cn/iocoder/yudao/module/tool/service/codegen/inner/CodegenEngine.java
1 1 package cn.iocoder.yudao.module.tool.service.codegen.inner;
2 2  
3   -import cn.hutool.core.collection.ListUtil;
4 3 import cn.hutool.core.map.MapUtil;
5   -import cn.hutool.core.util.ArrayUtil;
6 4 import cn.hutool.core.util.StrUtil;
7 5 import cn.hutool.extra.template.TemplateConfig;
8 6 import cn.hutool.extra.template.TemplateEngine;
... ... @@ -11,23 +9,21 @@ import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
11 9 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
12 10 import cn.iocoder.yudao.framework.common.pojo.PageParam;
13 11 import cn.iocoder.yudao.framework.common.pojo.PageResult;
14   -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
15   -import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
16   -import cn.iocoder.yudao.module.tool.enums.codegen.CodegenSceneEnum;
17   -import cn.iocoder.yudao.module.tool.framework.codegen.config.CodegenProperties;
  12 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  13 +import cn.iocoder.yudao.framework.common.util.date.DateUtils;
18 14 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
19 15 import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
20 16 import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
21 17 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
22   -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
23   -import cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum;
24 18 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
25 19 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
26   -import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
  20 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  21 +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
  22 +import cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum;
27 23 import cn.iocoder.yudao.module.tool.dal.dataobject.codegen.CodegenColumnDO;
28 24 import cn.iocoder.yudao.module.tool.dal.dataobject.codegen.CodegenTableDO;
29   -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
30   -import cn.iocoder.yudao.framework.common.util.date.DateUtils;
  25 +import cn.iocoder.yudao.module.tool.enums.codegen.CodegenSceneEnum;
  26 +import cn.iocoder.yudao.module.tool.framework.codegen.config.CodegenProperties;
31 27 import com.google.common.collect.Maps;
32 28 import org.springframework.stereotype.Component;
33 29  
... ... @@ -115,7 +111,8 @@ public class CodegenEngine {
115 111 private void initGlobalBindingMap() {
116 112 // 全局配置
117 113 globalBindingMap.put("basePackage", codegenProperties.getBasePackage());
118   - globalBindingMap.put("baseFrameworkPackage", codegenProperties.getBasePackage() + '.' + "framework"); // 用于后续获取测试类的 package 地址
  114 + globalBindingMap.put("baseFrameworkPackage", codegenProperties.getBasePackage()
  115 + + '.' + "framework"); // 用于后续获取测试类的 package 地址
119 116 // 全局 Java Bean
120 117 globalBindingMap.put("CommonResultClassName", CommonResult.class.getName());
121 118 globalBindingMap.put("PageResultClassName", PageResult.class.getName());
... ... @@ -123,6 +120,7 @@ public class CodegenEngine {
123 120 globalBindingMap.put("PageParamClassName", PageParam.class.getName());
124 121 globalBindingMap.put("DictFormatClassName", DictFormat.class.getName());
125 122 // DO 类,独有字段
  123 + globalBindingMap.put("BaseDOClassName", BaseDO.class.getName());
126 124 globalBindingMap.put("baseDOFields", CodegenBuilder.BASE_DO_FIELDS);
127 125 globalBindingMap.put("QueryWrapperClassName", LambdaQueryWrapperX.class.getName());
128 126 globalBindingMap.put("BaseMapperClassName", BaseMapperX.class.getName());
... ... @@ -156,15 +154,6 @@ public class CodegenEngine {
156 154 // permission 前缀
157 155 bindingMap.put("permissionPrefix", table.getModuleName() + ":" + simpleClassNameStrikeCase);
158 156  
159   - // 如果多租户,则进行覆盖 DB 独有字段
160   - if (CollectionUtils.findFirst(columns, column -> column.getColumnName().equals(CodegenBuilder.TENANT_ID_FIELD)) != null) {
161   - bindingMap.put("BaseDOClassName", TenantBaseDO.class.getName());
162   - bindingMap.put("BaseDOClassName_simple", TenantBaseDO.class.getSimpleName());
163   - } else {
164   - bindingMap.put("BaseDOClassName", BaseDO.class.getName());
165   - bindingMap.put("BaseDOClassName_simple", BaseDO.class.getSimpleName());
166   - }
167   -
168 157 // 执行生成
169 158 final Map<String, String> result = Maps.newLinkedHashMapWithExpectedSize(TEMPLATES.size()); // 有序
170 159 TEMPLATES.forEach((vmPath, filePath) -> {
... ...
yudao-module-tool/yudao-module-tool-impl/src/main/resources/codegen/java/dal/do.vm
... ... @@ -17,7 +17,7 @@ import ${BaseDOClassName};
17 17 @Builder
18 18 @NoArgsConstructor
19 19 @AllArgsConstructor
20   -public class ${table.className}DO extends ${BaseDOClassName_simple} {
  20 +public class ${table.className}DO extends BaseDO {
21 21  
22 22 #foreach ($column in $columns)
23 23 #if (!${baseDOFields.contains(${column.javaField})})##排除 BaseDO 的字段
... ...
yudao-server/src/main/resources/application.yaml
... ... @@ -78,7 +78,9 @@ yudao:
78 78 - cn.iocoder.yudao.module.system.enums.ErrorCodeConstants
79 79 - cn.iocoder.yudao.module.tool.enums.ErrorCodeConstants
80 80 tenant: # 多租户相关配置项
81   - tables: # 配置需要开启多租户的表;如果实体已经继承 TenantBaseDO 类,则无需重复配置
  81 + enable: true
  82 + ignore-urls: /admin-api/system/captcha/get-image, /admin-api/infra/file/get/*
  83 + ignore-tables: infra_config, infra_file, infra_job, infra_job_log, infra_job_log, system_tenant, system_dict_data, system_dict_type, system_error_code, system_menu, system_role, system_role_menu, system_sms_channel, tool_codegen_column, tool_codegen_table, tool_test_demo
82 84 sms-code: # 短信验证码相关的配置项
83 85 expire-times: 10m
84 86 send-frequency: 1m
... ...
yudao-ui-admin/.env.development
... ... @@ -10,3 +10,6 @@ VUE_APP_BASE_API = &#39;/dev-api&#39;
10 10  
11 11 # 路由懒加载
12 12 VUE_CLI_BABEL_TRANSPILE_MODULES = true
  13 +
  14 +# 多租户的开关
  15 +VUE_APP_TENANT_ENABLE = true
... ...
yudao-ui-admin/src/utils/request.js
... ... @@ -4,6 +4,7 @@ import store from &#39;@/store&#39;
4 4 import { getToken } from '@/utils/auth'
5 5 import errorCode from '@/utils/errorCode'
6 6 import Cookies from "js-cookie";
  7 +import {getTenantEnable} from "@/utils/ruoyi";
7 8  
8 9 // 是否显示重新登录
9 10 let isReloginShow;
... ... @@ -24,9 +25,11 @@ service.interceptors.request.use(config =&gt; {
24 25 config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
25 26 }
26 27 // 设置租户
27   - const tenantId = Cookies.get('tenantId');
28   - if (tenantId) {
29   - config.headers['tenant-id'] = tenantId;
  28 + if (getTenantEnable()) {
  29 + const tenantId = Cookies.get('tenantId');
  30 + if (tenantId) {
  31 + config.headers['tenant-id'] = tenantId;
  32 + }
30 33 }
31 34 // get请求映射params参数
32 35 if (config.method === 'get' && config.params) {
... ...
yudao-ui-admin/src/utils/ruoyi.js
... ... @@ -170,3 +170,17 @@ export function getNowDateTime(timeStr) {
170 170 let seconds = now.getSeconds().toString().padStart(2, "0") // 得到秒;
171 171 return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
172 172 }
  173 +
  174 +/**
  175 + * 获得租户功能是否开启
  176 + */
  177 +export function getTenantEnable() {
  178 + console.log("enable: " + process.env.VUE_APP_TENANT_ENABLE)
  179 + if (process.env.VUE_APP_TENANT_ENABLE === "true") {
  180 + return true;
  181 + }
  182 + if (process.env.VUE_APP_TENANT_ENABLE === "false") {
  183 + return false;
  184 + }
  185 + return process.env.VUE_APP_TENANT_ENABLE || true;
  186 +}
... ...
yudao-ui-admin/src/views/login.vue
... ... @@ -2,7 +2,7 @@
2 2 <div class="login">
3 3 <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form">
4 4 <h3 class="title">芋道后台管理系统</h3>
5   - <el-form-item prop="tenantName">
  5 + <el-form-item prop="tenantName" v-if="tenantEnable">
6 6 <el-input v-model="loginForm.tenantName" type="text" auto-complete="off" placeholder='租户'>
7 7 <svg-icon slot="prefix" icon-class="tree" class="el-input__icon input-icon" />
8 8 </el-input>
... ... @@ -54,7 +54,8 @@ import { getCodeImg,socialAuthRedirect } from &quot;@/api/login&quot;;
54 54 import { getTenantIdByName } from "@/api/system/tenant";
55 55 import Cookies from "js-cookie";
56 56 import { encrypt, decrypt } from '@/utils/jsencrypt'
57   -import {InfraApiErrorLogProcessStatusEnum, SystemUserSocialTypeEnum} from "@/utils/constants";
  57 +import {SystemUserSocialTypeEnum} from "@/utils/constants";
  58 +import { getTenantEnable } from "@/utils/ruoyi";
58 59  
59 60 export default {
60 61 name: "Login",
... ... @@ -62,6 +63,7 @@ export default {
62 63 return {
63 64 codeUrl: "",
64 65 captchaEnable: true,
  66 + tenantEnable: true,
65 67 loginForm: {
66 68 username: "admin",
67 69 password: "admin123",
... ... @@ -71,6 +73,13 @@ export default {
71 73 tenantName: "芋道源码",
72 74 },
73 75 loginRules: {
  76 + username: [
  77 + { required: true, trigger: "blur", message: "用户名不能为空" }
  78 + ],
  79 + password: [
  80 + { required: true, trigger: "blur", message: "密码不能为空" }
  81 + ],
  82 + code: [{ required: true, trigger: "change", message: "验证码不能为空" }],
74 83 tenantName: [
75 84 { required: true, trigger: "blur", message: "租户不能为空" },
76 85 {
... ... @@ -90,13 +99,6 @@ export default {
90 99 trigger: 'blur'
91 100 }
92 101 ],
93   - username: [
94   - { required: true, trigger: "blur", message: "用户名不能为空" }
95   - ],
96   - password: [
97   - { required: true, trigger: "blur", message: "密码不能为空" }
98   - ],
99   - code: [{ required: true, trigger: "change", message: "验证码不能为空" }]
100 102 },
101 103 loading: false,
102 104 redirect: undefined,
... ... @@ -113,6 +115,8 @@ export default {
113 115 // }
114 116 // },
115 117 created() {
  118 + // 租户开关
  119 + this.tenantEnable = getTenantEnable();
116 120 // 重定向地址
117 121 this.redirect = this.$route.query.redirect;
118 122 this.getCode();
... ...