Browse Source

1. 增加 yudao-spring-boot-starter-tenant 租户的组件
2. 改造 UserDO,接入多租户

YunaiV 3 years ago
parent
commit
7c8fe2fc50
35 changed files with 426 additions and 15 deletions
  1. 5 0
      yudao-admin-server/pom.xml
  2. 2 1
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/SysAuthController.http
  3. 7 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/framework/captcha/config/CaptchaProperties.java
  4. 5 1
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java
  5. 7 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/common/SysCaptchaService.java
  6. 5 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/common/impl/SysCaptchaServiceImpl.java
  7. 2 0
      yudao-admin-server/src/main/resources/application-local.yaml
  8. 2 0
      yudao-admin-server/src/main/resources/application.yaml
  9. 6 0
      yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/SysAuthServiceImplTest.java
  10. 1 0
      yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/system/service/user/SysUserServiceImplTest.java
  11. 1 0
      yudao-admin-server/src/test/resources/sql/create_tables.sql
  12. 5 0
      yudao-core-service/pom.xml
  13. 2 2
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/dataobject/user/SysUserDO.java
  14. 13 0
      yudao-dependencies/pom.xml
  15. 1 0
      yudao-framework/pom.xml
  16. 11 0
      yudao-framework/yudao-common/pom.xml
  17. 4 2
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/WebFilterOrderEnum.java
  18. 3 1
      yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/dataobject/BaseDO.java
  19. 17 0
      yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java
  20. 37 0
      yudao-framework/yudao-spring-boot-starter-tenant/pom.xml
  21. 25 0
      yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/TenantProperties.java
  22. 43 0
      yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantDatabaseAutoConfiguration.java
  23. 23 0
      yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantWebAutoConfiguration.java
  24. 26 0
      yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/context/TenantContextHolder.java
  25. 21 0
      yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantBaseDO.java
  26. 33 0
      yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantDatabaseInterceptor.java
  27. 39 0
      yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/web/TenantWebFilter.java
  28. 8 0
      yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/package-info.java
  29. 3 0
      yudao-framework/yudao-spring-boot-starter-tenant/src/main/resources/META-INF/spring.factories
  30. 2 2
      yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/SysUserProfileController.java
  31. 2 2
      yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/MbrUserService.java
  32. 3 3
      yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/impl/MbrUserServiceImpl.java
  33. 14 0
      yudao-vue-ui/api/member/userProfile.js
  34. 1 0
      yudao-vue-ui/common/js/request.js
  35. 47 1
      yudao-vue-ui/pages/set/userInfo.vue

+ 5 - 0
yudao-admin-server/pom.xml

@@ -117,6 +117,11 @@
             <artifactId>yudao-spring-boot-starter-excel</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-tenant</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>org.apache.velocity</groupId>
             <artifactId>velocity-engine-core</artifactId>

+ 2 - 1
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/SysAuthController.http

@@ -1,11 +1,12 @@
 ### 请求 /login 接口 => 成功
 POST {{baseUrl}}/login
 Content-Type: application/json
+tenant-id: 0
 
 {
   "username": "admin",
   "password": "admin123",
-  "uuid": "9b2ffbc1-7425-4155-9894-9d5c08541d62",
+  "uuid": "3acd87a09a4f48fb9118333780e94883",
   "code": "1024"
 }
 

+ 7 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/framework/captcha/config/CaptchaProperties.java

@@ -12,6 +12,13 @@ import java.time.Duration;
 @Data
 public class CaptchaProperties {
 
+    private static final Boolean ENABLE_DEFAULT = true;
+
+    /**
+     * 是否开启
+     * 注意,这里仅仅是后端 Server 是否校验,暂时不控制前端的逻辑
+     */
+    private Boolean enable = ENABLE_DEFAULT;
     /**
      * 验证码的过期时间
      */

+ 5 - 1
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java

@@ -133,9 +133,13 @@ public class SysAuthServiceImpl implements SysAuthService {
     }
 
     private void verifyCaptcha(String username, String captchaUUID, String captchaCode) {
+        // 如果验证码关闭,则不进行校验
+        if (!captchaService.isCaptchaEnable()) {
+            return;
+        }
+        // 验证码不存在
         final SysLoginLogTypeEnum logTypeEnum = SysLoginLogTypeEnum.LOGIN_USERNAME;
         String code = captchaService.getCaptchaCode(captchaUUID);
-        // 验证码不存在
         if (code == null) {
             // 创建登录失败日志(验证码不存在)
             this.createLoginLog(username, logTypeEnum, SysLoginResultEnum.CAPTCHA_NOT_FOUND);

+ 7 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/common/SysCaptchaService.java

@@ -14,6 +14,13 @@ public interface SysCaptchaService {
      */
     SysCaptchaImageRespVO getCaptchaImage();
 
+    /**
+     * 是否开启图片验证码
+     *
+     * @return 是否
+     */
+    Boolean isCaptchaEnable();
+
     /**
      * 获得 uuid 对应的验证码
      *

+ 5 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/common/impl/SysCaptchaServiceImpl.java

@@ -35,6 +35,11 @@ public class SysCaptchaServiceImpl implements SysCaptchaService {
         return SysCaptchaConvert.INSTANCE.convert(uuid, captcha);
     }
 
+    @Override
+    public Boolean isCaptchaEnable() {
+        return captchaProperties.getEnable();
+    }
+
     @Override
     public String getCaptchaCode(String uuid) {
         return captchaRedisDAO.get(uuid);

+ 2 - 0
yudao-admin-server/src/main/resources/application-local.yaml

@@ -166,6 +166,8 @@ logging:
 
 # 芋道配置项,设置当前项目所有自定义的配置
 yudao:
+  captcha:
+    enable: false # 本地环境,暂时关闭图片验证码,方便登录等接口的测试
   security:
     token-header: Authorization
     token-secret: abcdefghijklmnopqrstuvwxyz

+ 2 - 0
yudao-admin-server/src/main/resources/application.yaml

@@ -73,5 +73,7 @@ yudao:
     constants-class-list:
       - cn.iocoder.yudao.adminserver.modules.infra.enums.InfErrorCodeConstants
       - cn.iocoder.yudao.adminserver.modules.system.enums.SysErrorCodeConstants
+  tenant:
+    tables: sys_user
 
 debug: false

+ 6 - 0
yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/SysAuthServiceImplTest.java

@@ -17,6 +17,7 @@ import cn.iocoder.yudao.coreservice.modules.system.service.user.SysUserCoreServi
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.security.core.LoginUser;
 import cn.iocoder.yudao.framework.test.core.util.AssertUtils;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.springframework.boot.test.mock.mockito.MockBean;
 import org.springframework.context.annotation.Import;
@@ -71,6 +72,11 @@ public class SysAuthServiceImplTest extends BaseDbUnitTest {
     @MockBean
     private SysPostService postService;
 
+    @BeforeEach
+    public void setUp() {
+        when(captchaService.isCaptchaEnable()).thenReturn(true);
+    }
+
     @Test
     public void testLoadUserByUsername_success() {
         // 准备参数

+ 1 - 0
yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/system/service/user/SysUserServiceImplTest.java

@@ -21,6 +21,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
+import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
 import org.junit.jupiter.api.Test;
 import org.mockito.stubbing.Answer;
 import org.springframework.boot.test.mock.mockito.MockBean;

+ 1 - 0
yudao-admin-server/src/test/resources/sql/create_tables.sql

@@ -287,6 +287,7 @@ CREATE TABLE IF NOT EXISTS "sys_user" (
     "updater" varchar(64) default '',
     "update_time" timestamp not null default current_timestamp,
     "deleted" bit not null default false,
+    "tenant_id" bigint not null default  '0',
     primary key ("id")
 ) comment '用户信息表';
 

+ 5 - 0
yudao-core-service/pom.xml

@@ -91,6 +91,11 @@
         </dependency>
 
         <!-- 工具类相关 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-tenant</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>

+ 2 - 2
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/dataobject/user/SysUserDO.java

@@ -2,8 +2,8 @@ package cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user;
 
 import cn.iocoder.yudao.coreservice.modules.system.enums.common.SysSexEnum;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
 import cn.iocoder.yudao.framework.mybatis.core.type.JsonLongSetTypeHandler;
+import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
@@ -24,7 +24,7 @@ import java.util.Set;
 @Builder
 @NoArgsConstructor
 @AllArgsConstructor
-public class SysUserDO extends BaseDO {
+public class SysUserDO extends TenantBaseDO {
 
     /**
      * 用户ID

+ 13 - 0
yudao-dependencies/pom.xml

@@ -48,6 +48,7 @@
         <velocity.version>2.2</velocity.version>
         <screw.version>1.0.5</screw.version>
         <guava.version>30.1.1-jre</guava.version>
+        <transmittable-thread-local.version>2.12.2</transmittable-thread-local.version>
         <!-- 三方云服务相关 -->
         <aliyun-java-sdk-core.version>4.5.25</aliyun-java-sdk-core.version>
         <aliyun-java-sdk-dysmsapi.version>2.1.0</aliyun-java-sdk-dysmsapi.version>
@@ -350,6 +351,12 @@
                 <version>${revision}</version>
             </dependency>
 
+            <dependency>
+                <groupId>cn.iocoder.boot</groupId>
+                <artifactId>yudao-spring-boot-starter-tenant</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
             <dependency>
                 <groupId>org.projectlombok</groupId>
                 <artifactId>lombok</artifactId>
@@ -402,6 +409,12 @@
                 <version>${guava.version}</version>
             </dependency>
 
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>transmittable-thread-local</artifactId> <!-- 解决 ThreadLocal 父子线程的传值问题 -->
+                <version>${transmittable-thread-local.version}</version>
+            </dependency>
+
             <!-- 三方云服务相关 -->
 
             <!-- SMS SDK begin -->

+ 1 - 0
yudao-framework/pom.xml

@@ -32,6 +32,7 @@
         <module>yudao-spring-boot-starter-biz-pay</module>
         <module>yudao-spring-boot-starter-biz-weixin</module>
         <module>yudao-spring-boot-starter-extension</module>
+        <module>yudao-spring-boot-starter-tenant</module>
     </modules>
 
     <artifactId>yudao-framework</artifactId>

+ 11 - 0
yudao-framework/yudao-common/pom.xml

@@ -122,6 +122,17 @@
             <artifactId>jakarta.validation-api</artifactId>
             <scope>provided</scope> <!-- 设置为 provided,主要是 PageParam 使用到 -->
         </dependency>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>transmittable-thread-local</artifactId>
+        </dependency>
+
     </dependencies>
 
 </project>

+ 4 - 2
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/WebFilterOrderEnum.java

@@ -17,9 +17,11 @@ public interface WebFilterOrderEnum {
 
     // OrderedRequestContextFilter 默认为 -105,用于国际化上下文等等
 
-    int API_ACCESS_LOG_FILTER = -104; // 需要保证在 RequestBodyCacheFilter 后
+    int TENANT_FILTER = - 100; // 需要保证在 ApiAccessLogFilter 前
 
-    int XSS_FILTER = -103;  // 需要保证在 RequestBodyCacheFilter 后面
+    int API_ACCESS_LOG_FILTER = -90; // 需要保证在 RequestBodyCacheFilter 后面
+
+    int XSS_FILTER = -80;  // 需要保证在 RequestBodyCacheFilter 后面
 
     // Spring Security Filter 默认为 -100,可见 SecurityProperties 配置属性类
 

+ 3 - 1
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/dataobject/BaseDO.java

@@ -10,9 +10,11 @@ import java.util.Date;
 
 /**
  * 基础实体对象
+ *
+ * @author 芋道源码
  */
 @Data
-public class BaseDO implements Serializable {
+public abstract class BaseDO implements Serializable {
 
     /**
      * 创建时间

+ 17 - 0
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java

@@ -4,9 +4,13 @@ import cn.hutool.core.collection.CollectionUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.pojo.SortingField;
 import com.baomidou.mybatisplus.core.metadata.OrderItem;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 import java.util.stream.Collectors;
 
 /**
@@ -30,4 +34,17 @@ public class MyBatisUtils {
         return page;
     }
 
+    /**
+     * 将拦截器添加到链中
+     * 由于 MybatisPlusInterceptor 不支持添加拦截器,所以只能全量设置
+     *
+     * @param interceptor 链
+     * @param inner 拦截器
+     */
+    public static void addInterceptor(MybatisPlusInterceptor interceptor, InnerInterceptor inner) {
+        List<InnerInterceptor> inners = new ArrayList<>(interceptor.getInterceptors());
+        inners.add(0, inner);
+        interceptor.setInterceptors(inners);
+    }
+
 }

+ 37 - 0
yudao-framework/yudao-spring-boot-starter-tenant/pom.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>yudao-framework</artifactId>
+        <groupId>cn.iocoder.boot</groupId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>yudao-spring-boot-starter-tenant</artifactId>
+    <packaging>jar</packaging>
+
+    <name>${artifactId}</name>
+    <description>多租户</description>
+    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
+
+    <dependencies>
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-common</artifactId>
+        </dependency>
+
+        <!-- Web 相关 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <!-- DB 相关 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-mybatis</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 25 - 0
yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/TenantProperties.java

@@ -0,0 +1,25 @@
+package cn.iocoder.yudao.framework.tenant.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.Set;
+
+/**
+ * 多租户配置
+ *
+ * @author 芋道源码
+ */
+@ConfigurationProperties(prefix = "yudao.tenant")
+@Data
+public class TenantProperties {
+
+    /**
+     * 需要多租户的表
+     *
+     * 由于多租户并不作为 yudao 项目的重点功能,更多是扩展性的功能,所以采用正向配置需要多租户的表。
+     * 如果需要,你可以改成 ignoreTables 来取消部分不需要的表
+     */
+    private Set<String> tables;
+
+}

+ 43 - 0
yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantDatabaseAutoConfiguration.java

@@ -0,0 +1,43 @@
+package cn.iocoder.yudao.framework.tenant.config;
+
+import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
+import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+
+/**
+ * 多租户针对 DB 的自动配置
+ *
+ * @author 芋道源码
+ */
+@EnableConfigurationProperties(TenantProperties.class)
+public class YudaoTenantDatabaseAutoConfiguration {
+
+    @Bean
+    public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties properties) {
+        return new TenantLineInnerInterceptor(new TenantDatabaseInterceptor(properties));
+    }
+
+    @Bean
+    public BeanPostProcessor mybatisPlusInterceptorBeanPostProcessor(TenantLineInnerInterceptor tenantLineInnerInterceptor) {
+        return new BeanPostProcessor() {
+
+            @Override
+            public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+                if (!(bean instanceof MybatisPlusInterceptor)) {
+                    return bean;
+                }
+                // 将 TenantDatabaseInterceptor 添加到最前面
+                MybatisPlusInterceptor interceptor = (MybatisPlusInterceptor) bean;
+                MyBatisUtils.addInterceptor(interceptor, tenantLineInnerInterceptor);
+                return bean;
+            }
+
+        };
+    }
+
+}

+ 23 - 0
yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantWebAutoConfiguration.java

@@ -0,0 +1,23 @@
+package cn.iocoder.yudao.framework.tenant.config;
+
+import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
+import cn.iocoder.yudao.framework.tenant.core.web.TenantWebFilter;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+
+/**
+ * 多租户针对 Web 的自动配置
+ *
+ * @author 芋道源码
+ */
+public class YudaoTenantWebAutoConfiguration {
+
+    @Bean
+    public FilterRegistrationBean<TenantWebFilter> tenantWebFilter() {
+        FilterRegistrationBean<TenantWebFilter> registrationBean = new FilterRegistrationBean<>();
+        registrationBean.setFilter(new TenantWebFilter());
+        registrationBean.setOrder(WebFilterOrderEnum.TENANT_FILTER);
+        return registrationBean;
+    }
+
+}

+ 26 - 0
yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/context/TenantContextHolder.java

@@ -0,0 +1,26 @@
+package cn.iocoder.yudao.framework.tenant.core.context;
+
+import com.alibaba.ttl.TransmittableThreadLocal;
+
+/**
+ * 多租户上下文 Holder
+ *
+ * @author 芋道源码
+ */
+public class TenantContextHolder {
+
+    private static final ThreadLocal<Long> TENANT_ID = new TransmittableThreadLocal<>();
+
+    public static Long getTenantId() {
+        return TENANT_ID.get();
+    }
+
+    public static void setTenantId(Long tenantId) {
+        TENANT_ID.set(tenantId);
+    }
+
+    public static void clear() {
+        TENANT_ID.remove();
+    }
+
+}

+ 21 - 0
yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantBaseDO.java

@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.framework.tenant.core.db;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 拓展多租户的 BaseDO 基类
+ *
+ * @author 芋道源码
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public abstract class TenantBaseDO extends BaseDO {
+
+    /**
+     * 多租户编号
+     */
+    private Long tenantId;
+
+}

+ 33 - 0
yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantDatabaseInterceptor.java

@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.framework.tenant.core.db;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.tenant.config.TenantProperties;
+import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
+import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
+import lombok.AllArgsConstructor;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.StringValue;
+
+/**
+ * 基于 MyBatis Plus 多租户的功能,实现 DB 层面的多租户的功能
+ *
+ * @author 芋道源码
+ */
+@AllArgsConstructor
+public class TenantDatabaseInterceptor implements TenantLineHandler {
+
+    private final TenantProperties properties;
+
+    @Override
+    public Expression getTenantId() {
+        // TODO 芋艿:暂时不考虑获取不到的情况。此时,会存在 NPE 的报错
+        return new StringValue(TenantContextHolder.getTenantId().toString());
+    }
+
+    @Override
+    public boolean ignoreTable(String tableName) {
+        // 不包含,说明要过滤
+        return !CollUtil.contains(properties.getTables(), tableName);
+    }
+
+}

+ 39 - 0
yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/web/TenantWebFilter.java

@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.framework.tenant.core.web;
+
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * 多租户 Web 过滤器
+ * 将请求 Header 中的 tenant-id 解析出来,添加到 {@link TenantContextHolder} 中,这样后续的 DB 等操作,可以获得到租户编号
+ *
+ * @author 芋道源码
+ */
+public class TenantWebFilter extends OncePerRequestFilter {
+
+    private static final String HEADER_TENANT_ID = "tenant-id";
+
+    @Override
+    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
+            throws ServletException, IOException {
+        // 设置
+        String tenantId = request.getHeader(HEADER_TENANT_ID);
+        if (StrUtil.isNotEmpty(tenantId)) {
+            TenantContextHolder.setTenantId(Long.valueOf(tenantId));
+        }
+        try {
+            chain.doFilter(request, response);
+        } finally {
+            // 清理
+            TenantContextHolder.clear();
+        }
+    }
+
+}

+ 8 - 0
yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/package-info.java

@@ -0,0 +1,8 @@
+/**
+ * 多租户,支持如下层面:
+ * 1. DB:基于 MyBatis Plus 多租户的功能实现
+ * 2. Job:TODO
+ * 3. MQ:TODO
+ * 4. Web:TODO
+ */
+package cn.iocoder.yudao.framework.tenant;

+ 3 - 0
yudao-framework/yudao-spring-boot-starter-tenant/src/main/resources/META-INF/spring.factories

@@ -0,0 +1,3 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+  cn.iocoder.yudao.framework.tenant.config.YudaoTenantDatabaseAutoConfiguration,\
+  cn.iocoder.yudao.framework.tenant.config.YudaoTenantWebAutoConfiguration

+ 2 - 2
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/SysUserProfileController.java

@@ -41,8 +41,8 @@ public class SysUserProfileController {
     @PutMapping("/update-nickname")
     @ApiOperation("修改用户昵称")
     @PreAuthenticated
-    public CommonResult<Boolean> updateNickname(@RequestParam("nickName") String nickName) {
-        userService.updateNickname(getLoginUserId(), nickName);
+    public CommonResult<Boolean> updateNickname(@RequestParam("nickname") String nickname) {
+        userService.updateNickname(getLoginUserId(), nickname);
         return success(true);
     }
 

+ 2 - 2
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/MbrUserService.java

@@ -51,9 +51,9 @@ public interface MbrUserService {
     /**
      * 修改用户昵称
      * @param userId 用户id
-     * @param nickName 用户新昵称
+     * @param nickname 用户新昵称
      */
-    void updateNickname(Long userId, String nickName);
+    void updateNickname(Long userId, String nickname);
 
     /**
      * 修改用户头像

+ 3 - 3
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/impl/MbrUserServiceImpl.java

@@ -86,15 +86,15 @@ public class MbrUserServiceImpl implements MbrUserService {
     }
 
     @Override
-    public void updateNickname(Long userId, String nickName) {
+    public void updateNickname(Long userId, String nickname) {
         MbrUserDO user = this.checkUserExists(userId);
         // 仅当新昵称不等于旧昵称时进行修改
-        if (nickName.equals(user.getNickname())){
+        if (nickname.equals(user.getNickname())){
             return;
         }
         MbrUserDO userDO = new MbrUserDO();
         userDO.setId(user.getId());
-        userDO.setNickname(nickName);
+        userDO.setNickname(nickname);
         userMapper.updateById(userDO);
     }
 

+ 14 - 0
yudao-vue-ui/api/member/userProfile.js

@@ -6,4 +6,18 @@ export function getUserInfo() {
     url: 'member/user/profile/get',
     method: 'get'
   })
+}
+
+// 修改
+export function updateNickname(nickname) {
+	return request({
+		url: 'member/user/profile/update-nickname',
+		method: 'post',
+		header: {
+			"Content-Type": "application/x-www-form-urlencoded"
+		},
+		data: {
+			nickname
+		}
+	})
 }

+ 1 - 0
yudao-vue-ui/common/js/request.js

@@ -12,6 +12,7 @@ export const request = (options) => {
 			method: options.method || 'GET',
 			data: options.data || {},
 			header: {
+				...options.header,
 				'Authorization': authToken ? `Bearer ${authToken}` : ''
 			}
 		}).then(res => {

+ 47 - 1
yudao-vue-ui/pages/set/userInfo.vue

@@ -20,8 +20,20 @@
 			<text class="tit fill">昵称</text>
 			<input class="input" v-model="userInfo.nickname" type="text" maxlength="8" placeholder="请输入昵称" placeholder-class="placeholder">
 		</view>
+		<u-cell-group>
+			<u-cell title="昵称" :value="userInfo.nickname" isLink @click="nicknameClick()"></u-cell>
+		</u-cell-group>
+		
+		<u-modal :show="nicknameOpen" title="修改昵称" showCancelButton @confirm="nicknameSubmit" @cancel="nicknameCancel">
+			<view class="slot-content">
+				<u--form labelPosition="left" :model="nicknameForm" :rules="nicknameRules" ref="nicknameForm" errorType="toast">
+					<u-form-item prop="nickname">
+						<u--input v-model="nicknameForm.nickname" placeholder="请输入昵称" border="none"></u--input>
+					</u-form-item>
+				</u--form>
+			</view>
+		</u-modal>
 		
-		<mix-button ref="confirmBtn" text="保存资料" marginTop="80rpx" @onConfirm="confirm"></mix-button>
 	</view>
 </template>
 
@@ -32,6 +44,16 @@
 				uploadProgress: 100, //头像上传进度
 				tempAvatar: '', 
 				userInfo: {},
+				nicknameOpen: false,
+				nicknameForm: {
+					nickname: ''
+				},
+				nicknameRules: {
+					nickname: [{
+						required: true,
+						message: '请输入昵称'
+					}]
+				}
 			}
 		},
 		computed: {
@@ -50,6 +72,30 @@
 			this.userInfo = {avatar, nickname, gender};
 		},
 		methods: {
+			nicknameClick() {
+				this.nicknameOpen = true;
+				this.nicknameForm.nickname = this.userInfo.nickname;
+			},
+			nicknameCancel() {
+				this.nicknameOpen = false;
+			},
+			nicknameSubmit() {
+				this.$refs.nicknameForm.validate().then(() => {
+					this.loading = true;
+					// 执行登陆
+					const { mobile, code, password} = this.form;
+					const loginPromise = this.loginType == 'password' ? login(mobile, password) :
+						smsLogin(mobile, code);
+					loginPromise.then(data => {
+						// 登陆成功
+						this.loginSuccessCallBack(data);
+					}).catch(errors => {
+					}).finally(() => {
+						this.loading = false;
+					})
+				}).catch(errors => {
+				});
+			},
 			// 提交修改
 			async confirm() {
 				// 校验信息是否变化