From 7c8fe2fc50dcdc6f625d7420f7c9911f70eaf822 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 4 Dec 2021 21:09:49 +0800 Subject: [PATCH] =?UTF-8?q?1.=20=E5=A2=9E=E5=8A=A0=20yudao-spring-boot-sta?= =?UTF-8?q?rter-tenant=20=E7=A7=9F=E6=88=B7=E7=9A=84=E7=BB=84=E4=BB=B6=202?= =?UTF-8?q?.=20=E6=94=B9=E9=80=A0=20UserDO=EF=BC=8C=E6=8E=A5=E5=85=A5?= =?UTF-8?q?=E5=A4=9A=E7=A7=9F=E6=88=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-admin-server/pom.xml | 5 ++ .../controller/auth/SysAuthController.http | 3 +- .../captcha/config/CaptchaProperties.java | 7 +++ .../service/auth/impl/SysAuthServiceImpl.java | 6 ++- .../service/common/SysCaptchaService.java | 7 +++ .../common/impl/SysCaptchaServiceImpl.java | 5 ++ .../src/main/resources/application-local.yaml | 2 + .../src/main/resources/application.yaml | 2 + .../service/auth/SysAuthServiceImplTest.java | 6 +++ .../service/user/SysUserServiceImplTest.java | 1 + .../src/test/resources/sql/create_tables.sql | 1 + yudao-core-service/pom.xml | 5 ++ .../system/dal/dataobject/user/SysUserDO.java | 4 +- yudao-dependencies/pom.xml | 13 +++++ yudao-framework/pom.xml | 1 + yudao-framework/yudao-common/pom.xml | 11 +++++ .../common/enums/WebFilterOrderEnum.java | 6 ++- .../mybatis/core/dataobject/BaseDO.java | 4 +- .../mybatis/core/util/MyBatisUtils.java | 17 +++++++ .../yudao-spring-boot-starter-tenant/pom.xml | 37 ++++++++++++++ .../tenant/config/TenantProperties.java | 25 ++++++++++ .../YudaoTenantDatabaseAutoConfiguration.java | 43 +++++++++++++++++ .../YudaoTenantWebAutoConfiguration.java | 23 +++++++++ .../core/context/TenantContextHolder.java | 26 ++++++++++ .../tenant/core/db/TenantBaseDO.java | 21 ++++++++ .../core/db/TenantDatabaseInterceptor.java | 33 +++++++++++++ .../tenant/core/web/TenantWebFilter.java | 39 +++++++++++++++ .../yudao/framework/tenant/package-info.java | 8 ++++ .../main/resources/META-INF/spring.factories | 3 ++ .../user/SysUserProfileController.java | 4 +- .../member/service/user/MbrUserService.java | 4 +- .../service/user/impl/MbrUserServiceImpl.java | 6 +-- yudao-vue-ui/api/member/userProfile.js | 14 ++++++ yudao-vue-ui/common/js/request.js | 1 + yudao-vue-ui/pages/set/userInfo.vue | 48 ++++++++++++++++++- 35 files changed, 426 insertions(+), 15 deletions(-) create mode 100644 yudao-framework/yudao-spring-boot-starter-tenant/pom.xml create mode 100644 yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/TenantProperties.java create mode 100644 yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantDatabaseAutoConfiguration.java create mode 100644 yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantWebAutoConfiguration.java create mode 100644 yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/context/TenantContextHolder.java create mode 100644 yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantBaseDO.java create mode 100644 yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantDatabaseInterceptor.java create mode 100644 yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/web/TenantWebFilter.java create mode 100644 yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/package-info.java create mode 100644 yudao-framework/yudao-spring-boot-starter-tenant/src/main/resources/META-INF/spring.factories diff --git a/yudao-admin-server/pom.xml b/yudao-admin-server/pom.xml index 4314e0fed..f3b91ac0f 100644 --- a/yudao-admin-server/pom.xml +++ b/yudao-admin-server/pom.xml @@ -117,6 +117,11 @@ yudao-spring-boot-starter-excel + + cn.iocoder.boot + yudao-spring-boot-starter-tenant + + org.apache.velocity velocity-engine-core diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/SysAuthController.http b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/SysAuthController.http index 125a36846..580b897f8 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/SysAuthController.http +++ b/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" } diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/framework/captcha/config/CaptchaProperties.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/framework/captcha/config/CaptchaProperties.java index 6eadd4d12..73939df75 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/framework/captcha/config/CaptchaProperties.java +++ b/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; /** * 验证码的过期时间 */ diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java index 762649d9d..2804b2813 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java +++ b/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); diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/common/SysCaptchaService.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/common/SysCaptchaService.java index 286a5ef1f..32aff5b06 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/common/SysCaptchaService.java +++ b/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 对应的验证码 * diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/common/impl/SysCaptchaServiceImpl.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/common/impl/SysCaptchaServiceImpl.java index 44d291ffd..daefc007f 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/common/impl/SysCaptchaServiceImpl.java +++ b/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); diff --git a/yudao-admin-server/src/main/resources/application-local.yaml b/yudao-admin-server/src/main/resources/application-local.yaml index d7880547f..79fdb300c 100644 --- a/yudao-admin-server/src/main/resources/application-local.yaml +++ b/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 diff --git a/yudao-admin-server/src/main/resources/application.yaml b/yudao-admin-server/src/main/resources/application.yaml index 8674452cf..f71a66fc6 100644 --- a/yudao-admin-server/src/main/resources/application.yaml +++ b/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 diff --git a/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/SysAuthServiceImplTest.java b/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/SysAuthServiceImplTest.java index 59be97809..492128e6b 100644 --- a/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/SysAuthServiceImplTest.java +++ b/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() { // 准备参数 diff --git a/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/system/service/user/SysUserServiceImplTest.java b/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/system/service/user/SysUserServiceImplTest.java index 70751eeac..cd1faa592 100644 --- a/yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/system/service/user/SysUserServiceImplTest.java +++ b/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; diff --git a/yudao-admin-server/src/test/resources/sql/create_tables.sql b/yudao-admin-server/src/test/resources/sql/create_tables.sql index db67cad23..96157b3d9 100644 --- a/yudao-admin-server/src/test/resources/sql/create_tables.sql +++ b/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 '用户信息表'; diff --git a/yudao-core-service/pom.xml b/yudao-core-service/pom.xml index 4fd85c0aa..4be7e2e30 100644 --- a/yudao-core-service/pom.xml +++ b/yudao-core-service/pom.xml @@ -91,6 +91,11 @@ + + cn.iocoder.boot + yudao-spring-boot-starter-tenant + + com.google.guava guava diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/dataobject/user/SysUserDO.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/dataobject/user/SysUserDO.java index ef33f8d2a..bb5e6854c 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/dataobject/user/SysUserDO.java +++ b/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 diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index cd5efec94..8d94d04c8 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -48,6 +48,7 @@ 2.2 1.0.5 30.1.1-jre + 2.12.2 4.5.25 2.1.0 @@ -350,6 +351,12 @@ ${revision} + + cn.iocoder.boot + yudao-spring-boot-starter-tenant + ${revision} + + org.projectlombok lombok @@ -402,6 +409,12 @@ ${guava.version} + + com.alibaba + transmittable-thread-local + ${transmittable-thread-local.version} + + diff --git a/yudao-framework/pom.xml b/yudao-framework/pom.xml index 136ac3073..63fc2c33c 100644 --- a/yudao-framework/pom.xml +++ b/yudao-framework/pom.xml @@ -32,6 +32,7 @@ yudao-spring-boot-starter-biz-pay yudao-spring-boot-starter-biz-weixin yudao-spring-boot-starter-extension + yudao-spring-boot-starter-tenant yudao-framework diff --git a/yudao-framework/yudao-common/pom.xml b/yudao-framework/yudao-common/pom.xml index ad84d6a24..2681c8914 100644 --- a/yudao-framework/yudao-common/pom.xml +++ b/yudao-framework/yudao-common/pom.xml @@ -122,6 +122,17 @@ jakarta.validation-api provided + + + cn.hutool + hutool-all + + + + com.alibaba + transmittable-thread-local + + diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/WebFilterOrderEnum.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/WebFilterOrderEnum.java index 6ad94a369..1d8cc4034 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/WebFilterOrderEnum.java +++ b/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 配置属性类 diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/dataobject/BaseDO.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/dataobject/BaseDO.java index 15d99a3d8..f63f054de 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/dataobject/BaseDO.java +++ b/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 { /** * 创建时间 diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java index 3c455e893..8789ea574 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java +++ b/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 inners = new ArrayList<>(interceptor.getInterceptors()); + inners.add(0, inner); + interceptor.setInterceptors(inners); + } + } diff --git a/yudao-framework/yudao-spring-boot-starter-tenant/pom.xml b/yudao-framework/yudao-spring-boot-starter-tenant/pom.xml new file mode 100644 index 000000000..854db0bbf --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-tenant/pom.xml @@ -0,0 +1,37 @@ + + + + yudao-framework + cn.iocoder.boot + ${revision} + + 4.0.0 + yudao-spring-boot-starter-tenant + jar + + ${artifactId} + 多租户 + https://github.com/YunaiV/ruoyi-vue-pro + + + + cn.iocoder.boot + yudao-common + + + + + org.springframework.boot + spring-boot-starter-web + + + + + cn.iocoder.boot + yudao-spring-boot-starter-mybatis + + + + diff --git a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/TenantProperties.java b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/TenantProperties.java new file mode 100644 index 000000000..5b97b22f6 --- /dev/null +++ b/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 tables; + +} diff --git a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantDatabaseAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantDatabaseAutoConfiguration.java new file mode 100644 index 000000000..7149e39a2 --- /dev/null +++ b/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; + } + + }; + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantWebAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantWebAutoConfiguration.java new file mode 100644 index 000000000..7b2e1aaf5 --- /dev/null +++ b/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() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new TenantWebFilter()); + registrationBean.setOrder(WebFilterOrderEnum.TENANT_FILTER); + return registrationBean; + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/context/TenantContextHolder.java b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/context/TenantContextHolder.java new file mode 100644 index 000000000..92dbff002 --- /dev/null +++ b/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 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(); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantBaseDO.java b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantBaseDO.java new file mode 100644 index 000000000..f4f0ea50a --- /dev/null +++ b/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; + +} diff --git a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantDatabaseInterceptor.java b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantDatabaseInterceptor.java new file mode 100644 index 000000000..dfcd8a4a4 --- /dev/null +++ b/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); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/web/TenantWebFilter.java b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/web/TenantWebFilter.java new file mode 100644 index 000000000..027656476 --- /dev/null +++ b/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(); + } + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/package-info.java b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/package-info.java new file mode 100644 index 000000000..1f39db41f --- /dev/null +++ b/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; diff --git a/yudao-framework/yudao-spring-boot-starter-tenant/src/main/resources/META-INF/spring.factories b/yudao-framework/yudao-spring-boot-starter-tenant/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..6f0a6f12b --- /dev/null +++ b/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 diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/SysUserProfileController.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/SysUserProfileController.java index 93149e793..c0778184a 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/SysUserProfileController.java +++ b/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 updateNickname(@RequestParam("nickName") String nickName) { - userService.updateNickname(getLoginUserId(), nickName); + public CommonResult updateNickname(@RequestParam("nickname") String nickname) { + userService.updateNickname(getLoginUserId(), nickname); return success(true); } diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/MbrUserService.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/MbrUserService.java index fc5a8564d..e33978bfe 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/MbrUserService.java +++ b/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); /** * 修改用户头像 diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/impl/MbrUserServiceImpl.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/impl/MbrUserServiceImpl.java index 40b7b862b..442da4147 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/impl/MbrUserServiceImpl.java +++ b/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); } diff --git a/yudao-vue-ui/api/member/userProfile.js b/yudao-vue-ui/api/member/userProfile.js index 3c3d52e71..2effa1eb7 100644 --- a/yudao-vue-ui/api/member/userProfile.js +++ b/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 + } + }) } \ No newline at end of file diff --git a/yudao-vue-ui/common/js/request.js b/yudao-vue-ui/common/js/request.js index 7f3d04bae..d78866e4e 100644 --- a/yudao-vue-ui/common/js/request.js +++ b/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 => { diff --git a/yudao-vue-ui/pages/set/userInfo.vue b/yudao-vue-ui/pages/set/userInfo.vue index 07eae7e2f..4d46ed8a8 100644 --- a/yudao-vue-ui/pages/set/userInfo.vue +++ b/yudao-vue-ui/pages/set/userInfo.vue @@ -20,8 +20,20 @@ 昵称 + + + + + + + + + + + + + - @@ -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() { // 校验信息是否变化