v 1.0
1. 新增用户与部门,一对多的关系; 2. 新增管理多部门用户,如果有为公司的多个部门可以进行选择登录(选择后,直到下次变更访问公司前,只能访问此次选择公的业务数据,使用 company_id 控制,后续补充此数据权限的实现); 3. sql 转化工具修复,现在可以正确的对 mysql 进行不同数据库实例的转化了; 4. 所有表格主键,修改为分布式 Id 实现; 5. 补全在初始版本中没有被纳入的其他预制功能模块
This commit is contained in:
@@ -17,7 +17,7 @@ import cn.iocoder.yudao.framework.tenant.core.redis.TenantRedisCacheManager;
|
||||
import cn.iocoder.yudao.framework.tenant.core.security.TenantSecurityWebFilter;
|
||||
import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;
|
||||
import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkServiceImpl;
|
||||
import cn.iocoder.yudao.framework.tenant.core.web.DeptVisitContextInterceptor;
|
||||
import cn.iocoder.yudao.framework.tenant.core.web.CompanyVisitContextInterceptor;
|
||||
import cn.iocoder.yudao.framework.tenant.core.web.TenantContextWebFilter;
|
||||
import cn.iocoder.yudao.framework.tenant.core.web.TenantVisitContextInterceptor;
|
||||
import cn.iocoder.yudao.framework.web.config.WebProperties;
|
||||
@@ -135,8 +135,8 @@ public class YudaoTenantAutoConfiguration {
|
||||
return new TenantVisitContextInterceptor(tenantProperties, securityFrameworkService);
|
||||
}
|
||||
@Bean
|
||||
public DeptVisitContextInterceptor deptVisitContextInterceptor(SecurityFrameworkService securityFrameworkService) {
|
||||
return new DeptVisitContextInterceptor(securityFrameworkService);
|
||||
public CompanyVisitContextInterceptor deptVisitContextInterceptor(SecurityFrameworkService securityFrameworkService) {
|
||||
return new CompanyVisitContextInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -153,11 +153,11 @@ public class YudaoTenantAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WebMvcConfigurer deptWebMvcConfigurer(TenantProperties tenantProperties, DeptVisitContextInterceptor deptVisitContextInterceptor) {
|
||||
public WebMvcConfigurer deptWebMvcConfigurer(TenantProperties tenantProperties, CompanyVisitContextInterceptor companyVisitContextInterceptor) {
|
||||
return new WebMvcConfigurer() {
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(deptVisitContextInterceptor)
|
||||
registry.addInterceptor(companyVisitContextInterceptor)
|
||||
.excludePathPatterns(tenantProperties.getIgnoreVisitUrls().toArray(new String[0]));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@ import java.lang.annotation.*;
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
public @interface DeptVisitIgnore {
|
||||
public @interface CompanyVisitIgnore {
|
||||
|
||||
/**
|
||||
* 是否开启忽略租户,默认为 true 开启
|
||||
@@ -1,36 +1,36 @@
|
||||
package cn.iocoder.yudao.framework.tenant.core.aop;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;
|
||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||
import cn.iocoder.yudao.framework.tenant.core.context.CompanyContextHolder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
|
||||
/**
|
||||
* 忽略单位切换,标记指定方法不进行租户切换的覆盖,基于 {@link DeptVisitIgnore} 注解实现,用于一些全局的逻辑。
|
||||
* 忽略单位切换,标记指定方法不进行租户切换的覆盖,基于 {@link CompanyVisitIgnore} 注解实现,用于一些全局的逻辑。
|
||||
* 例如说,一个定时任务,读取所有数据,进行处理。
|
||||
* 又例如说,读取所有数据,进行缓存。
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Aspect
|
||||
@Slf4j
|
||||
public class DeptVisitIgnoreAspect {
|
||||
public class CompanyVisitIgnoreAspect {
|
||||
|
||||
@Around("@annotation(deptVisitIgnore)")
|
||||
public Object around(ProceedingJoinPoint joinPoint, DeptVisitIgnore deptVisitIgnore) throws Throwable {
|
||||
Boolean oldIgnore = TenantContextHolder.isIgnore();
|
||||
@Around("@annotation(companyVisitIgnore)")
|
||||
public Object around(ProceedingJoinPoint joinPoint, CompanyVisitIgnore companyVisitIgnore) throws Throwable {
|
||||
Boolean oldIgnore = CompanyContextHolder.isIgnore();
|
||||
try {
|
||||
// 计算条件,满足的情况下,才进行忽略
|
||||
Object enable = SpringExpressionUtils.parseExpression(deptVisitIgnore.enable());
|
||||
Object enable = SpringExpressionUtils.parseExpression(companyVisitIgnore.enable());
|
||||
if (Boolean.TRUE.equals(enable)) {
|
||||
TenantContextHolder.setIgnore(true);
|
||||
CompanyContextHolder.setIgnore(true);
|
||||
}
|
||||
|
||||
// 执行逻辑
|
||||
return joinPoint.proceed();
|
||||
} finally {
|
||||
TenantContextHolder.setIgnore(oldIgnore);
|
||||
CompanyContextHolder.setIgnore(oldIgnore);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package cn.iocoder.yudao.framework.tenant.core.context;
|
||||
|
||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||
|
||||
/**
|
||||
* 公司上下文 Holder
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class CompanyContextHolder {
|
||||
|
||||
/**
|
||||
* 当前公司编号
|
||||
*/
|
||||
private static final ThreadLocal<Long> COMPANY_ID = new TransmittableThreadLocal<>();
|
||||
|
||||
/**
|
||||
* 是否忽略公司
|
||||
*/
|
||||
private static final ThreadLocal<Boolean> IGNORE = new TransmittableThreadLocal<>();
|
||||
|
||||
/**
|
||||
* 获得公司编号
|
||||
*
|
||||
* @return 公司编号
|
||||
*/
|
||||
public static Long getCompanyId() {
|
||||
return COMPANY_ID.get();
|
||||
}
|
||||
|
||||
public static void setCompanyId(Long companyId) {
|
||||
COMPANY_ID.set(companyId);
|
||||
}
|
||||
|
||||
public static void setIgnore(Boolean ignore) {
|
||||
IGNORE.set(ignore);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前是否忽略公司
|
||||
*
|
||||
* @return 是否忽略
|
||||
*/
|
||||
public static boolean isIgnore() {
|
||||
return Boolean.TRUE.equals(IGNORE.get());
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
COMPANY_ID.remove();
|
||||
IGNORE.remove();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.tenant.core.context;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.DocumentEnum;
|
||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 多部门上下文 Holder
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class DeptContextHolder {
|
||||
|
||||
/**
|
||||
* 当前部门编号列表
|
||||
*/
|
||||
private static final ThreadLocal<Set<Long>> DEPT_ID_LIST = new TransmittableThreadLocal<>();
|
||||
|
||||
/**
|
||||
* 是否忽略部门
|
||||
*/
|
||||
private static final ThreadLocal<Boolean> IGNORE = new TransmittableThreadLocal<>();
|
||||
|
||||
/**
|
||||
* 获得部门编号列表
|
||||
*
|
||||
* @return 部门编号列表
|
||||
*/
|
||||
public static Set<Long> getDeptIdList() {
|
||||
return DEPT_ID_LIST.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得部门编号列表。如果不存在,则抛出 NullPointerException 异常
|
||||
*
|
||||
* @return 部门编号列表
|
||||
*/
|
||||
public static Set<Long> getRequiredDeptIdList() {
|
||||
Set<Long> deptIdList = getDeptIdList();
|
||||
if (deptIdList == null) {
|
||||
throw new NullPointerException("DeptContextHolder 不存在部门编号列表!可参考文档:"
|
||||
+ DocumentEnum.TENANT.getUrl());
|
||||
}
|
||||
return deptIdList;
|
||||
}
|
||||
|
||||
public static void setDeptIdList(Set<Long> deptIdList) {
|
||||
DEPT_ID_LIST.set(deptIdList);
|
||||
}
|
||||
|
||||
public static void setIgnore(Boolean ignore) {
|
||||
IGNORE.set(ignore);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前是否忽略部门
|
||||
*
|
||||
* @return 是否忽略
|
||||
*/
|
||||
public static boolean isIgnore() {
|
||||
return Boolean.TRUE.equals(IGNORE.get());
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
DEPT_ID_LIST.remove();
|
||||
IGNORE.remove();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
package cn.iocoder.yudao.framework.tenant.core.web;
|
||||
|
||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||
import cn.iocoder.yudao.framework.security.core.service.SecurityFrameworkService;
|
||||
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import cn.iocoder.yudao.framework.tenant.core.context.DeptContextHolder;
|
||||
import cn.iocoder.yudao.framework.tenant.core.context.CompanyContextHolder;
|
||||
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
@@ -11,44 +10,49 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author chenbowen
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class DeptVisitContextInterceptor implements HandlerInterceptor {
|
||||
public class CompanyVisitContextInterceptor implements HandlerInterceptor {
|
||||
|
||||
private static final String PERMISSION = "system:dept:visit";
|
||||
|
||||
private final SecurityFrameworkService securityFrameworkService;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
// 解析 header 并设置 visitDeptIds
|
||||
Set<Long> deptIds = WebFrameworkUtils.getVisitDeptIds(request);
|
||||
if (deptIds == null) {
|
||||
// 解析 header 并设置 visitCompanyId
|
||||
Long companyId = WebFrameworkUtils.getCompanyId(request);
|
||||
String companyName = WebFrameworkUtils.getCompanyName(request);
|
||||
if (companyId <= 0L) {
|
||||
// 如果没有设置 companyId,则忽略
|
||||
CompanyContextHolder.setIgnore(true);
|
||||
return true;
|
||||
}
|
||||
Long deptId = WebFrameworkUtils.getDeptId(request);
|
||||
String deptName = WebFrameworkUtils.getDeptName(request);
|
||||
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
|
||||
if (loginUser == null) {
|
||||
return true;
|
||||
}
|
||||
if (deptId > 0L) {
|
||||
loginUser.setVisitDeptId(deptId);
|
||||
loginUser.setVisitDeptName(deptName);
|
||||
}
|
||||
// if (!securityFrameworkService.hasAnyPermissions(PERMISSION)) {
|
||||
// throw exception0(GlobalErrorCodeConstants.FORBIDDEN.getCode(), "您无权切换部门");
|
||||
// }
|
||||
loginUser.setVisitDeptIds(deptIds);
|
||||
DeptContextHolder.setDeptIdList(deptIds);
|
||||
loginUser.setVisitCompanyId(companyId);
|
||||
loginUser.setVisitCompanyName(companyName);
|
||||
CompanyContextHolder.setCompanyId(companyId);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
|
||||
// 清理 visitDeptIds
|
||||
// 清理 visitCompanyId
|
||||
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
|
||||
if (loginUser != null) {
|
||||
loginUser.setVisitDeptIds(null);
|
||||
loginUser.setVisitCompanyId(0L);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user