1. 增加 yudao-spring-boot-starter-tenant 租户的组件
2. 改造 UserDO,接入多租户
This commit is contained in:
parent
ccb56b3b99
commit
7c8fe2fc50
@ -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>
|
||||
|
@ -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"
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
/**
|
||||
* 验证码的过期时间
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -14,6 +14,13 @@ public interface SysCaptchaService {
|
||||
*/
|
||||
SysCaptchaImageRespVO getCaptchaImage();
|
||||
|
||||
/**
|
||||
* 是否开启图片验证码
|
||||
*
|
||||
* @return 是否
|
||||
*/
|
||||
Boolean isCaptchaEnable();
|
||||
|
||||
/**
|
||||
* 获得 uuid 对应的验证码
|
||||
*
|
||||
|
@ -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);
|
||||
|
@ -166,6 +166,8 @@ logging:
|
||||
|
||||
# 芋道配置项,设置当前项目所有自定义的配置
|
||||
yudao:
|
||||
captcha:
|
||||
enable: false # 本地环境,暂时关闭图片验证码,方便登录等接口的测试
|
||||
security:
|
||||
token-header: Authorization
|
||||
token-secret: abcdefghijklmnopqrstuvwxyz
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
// 准备参数
|
||||
|
@ -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;
|
||||
|
@ -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 '用户信息表';
|
||||
|
||||
|
@ -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,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
|
||||
|
@ -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 -->
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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 配置属性类
|
||||
|
||||
|
@ -10,9 +10,11 @@ import java.util.Date;
|
||||
|
||||
/**
|
||||
* 基础实体对象
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class BaseDO implements Serializable {
|
||||
public abstract class BaseDO implements Serializable {
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
|
@ -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
yudao-framework/yudao-spring-boot-starter-tenant/pom.xml
Normal file
37
yudao-framework/yudao-spring-boot-starter-tenant/pom.xml
Normal file
@ -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>
|
@ -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;
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* 多租户,支持如下层面:
|
||||
* 1. DB:基于 MyBatis Plus 多租户的功能实现
|
||||
* 2. Job:TODO
|
||||
* 3. MQ:TODO
|
||||
* 4. Web:TODO
|
||||
*/
|
||||
package cn.iocoder.yudao.framework.tenant;
|
@ -0,0 +1,3 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
cn.iocoder.yudao.framework.tenant.config.YudaoTenantDatabaseAutoConfiguration,\
|
||||
cn.iocoder.yudao.framework.tenant.config.YudaoTenantWebAutoConfiguration
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
* 修改用户头像
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
})
|
||||
}
|
@ -12,6 +12,7 @@ export const request = (options) => {
|
||||
method: options.method || 'GET',
|
||||
data: options.data || {},
|
||||
header: {
|
||||
...options.header,
|
||||
'Authorization': authToken ? `Bearer ${authToken}` : ''
|
||||
}
|
||||
}).then(res => {
|
||||
|
@ -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() {
|
||||
// 校验信息是否变化
|
||||
|
Loading…
Reference in New Issue
Block a user