系统支持切换单位来实时过滤业务数据的查询条件
This commit is contained in:
@@ -47,6 +47,10 @@
|
|||||||
<artifactId>yudao-spring-boot-starter-test</artifactId>
|
<artifactId>yudao-spring-boot-starter-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.cloud</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
|
|||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.iocoder.yudao.framework.common.biz.system.permission.PermissionCommonApi;
|
import cn.iocoder.yudao.framework.common.biz.system.permission.PermissionCommonApi;
|
||||||
|
import cn.iocoder.yudao.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO;
|
||||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||||
@@ -12,11 +13,14 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
|||||||
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
|
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
|
||||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||||
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
||||||
import cn.iocoder.yudao.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO;
|
import cn.iocoder.yudao.framework.tenant.core.context.DeptContextHolder;
|
||||||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.sf.jsqlparser.expression.*;
|
import net.sf.jsqlparser.expression.Alias;
|
||||||
|
import net.sf.jsqlparser.expression.Expression;
|
||||||
|
import net.sf.jsqlparser.expression.LongValue;
|
||||||
|
import net.sf.jsqlparser.expression.NullValue;
|
||||||
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
|
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
|
||||||
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
|
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
|
||||||
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
|
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
|
||||||
@@ -112,6 +116,15 @@ public class DeptDataPermissionRule implements DataPermissionRule {
|
|||||||
// 添加到上下文中,避免重复计算
|
// 添加到上下文中,避免重复计算
|
||||||
loginUser.setContext(CONTEXT_KEY, deptDataPermission);
|
loginUser.setContext(CONTEXT_KEY, deptDataPermission);
|
||||||
}
|
}
|
||||||
|
// 如果开启了部门上下文,且缓存的部门编号不等于 DeptContextHolder 的部门编号,则更新缓存
|
||||||
|
if(!DeptContextHolder.isIgnore()) {
|
||||||
|
Set<Long> deptIds = DeptContextHolder.getDeptIdList();
|
||||||
|
if (CollUtil.isNotEmpty(deptIds) && !CollUtil.isEqualList(deptDataPermission.getDeptIds(), deptIds)) {
|
||||||
|
deptDataPermission.setDeptIds(deptIds);
|
||||||
|
// 更新到上下文中
|
||||||
|
loginUser.setContext(CONTEXT_KEY, deptDataPermission);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 情况一,如果是 ALL 可查看全部,则无需拼接条件
|
// 情况一,如果是 ALL 可查看全部,则无需拼接条件
|
||||||
if (deptDataPermission.getAll()) {
|
if (deptDataPermission.getAll()) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package cn.iocoder.yudao.framework.tenant.config;
|
package cn.iocoder.yudao.framework.tenant.config;
|
||||||
|
|
||||||
import cn.hutool.extra.spring.SpringUtil;
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
|
import cn.iocoder.yudao.framework.common.biz.system.tenant.TenantCommonApi;
|
||||||
import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
|
import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
|
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
|
||||||
import cn.iocoder.yudao.framework.redis.config.YudaoCacheProperties;
|
import cn.iocoder.yudao.framework.redis.config.YudaoCacheProperties;
|
||||||
@@ -16,11 +17,11 @@ 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.security.TenantSecurityWebFilter;
|
||||||
import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;
|
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.service.TenantFrameworkServiceImpl;
|
||||||
|
import cn.iocoder.yudao.framework.tenant.core.web.DeptVisitContextInterceptor;
|
||||||
import cn.iocoder.yudao.framework.tenant.core.web.TenantContextWebFilter;
|
import cn.iocoder.yudao.framework.tenant.core.web.TenantContextWebFilter;
|
||||||
import cn.iocoder.yudao.framework.tenant.core.web.TenantVisitContextInterceptor;
|
import cn.iocoder.yudao.framework.tenant.core.web.TenantVisitContextInterceptor;
|
||||||
import cn.iocoder.yudao.framework.web.config.WebProperties;
|
import cn.iocoder.yudao.framework.web.config.WebProperties;
|
||||||
import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
|
import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
|
||||||
import cn.iocoder.yudao.framework.common.biz.system.tenant.TenantCommonApi;
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
|
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
@@ -133,6 +134,10 @@ public class YudaoTenantAutoConfiguration {
|
|||||||
SecurityFrameworkService securityFrameworkService) {
|
SecurityFrameworkService securityFrameworkService) {
|
||||||
return new TenantVisitContextInterceptor(tenantProperties, securityFrameworkService);
|
return new TenantVisitContextInterceptor(tenantProperties, securityFrameworkService);
|
||||||
}
|
}
|
||||||
|
@Bean
|
||||||
|
public DeptVisitContextInterceptor deptVisitContextInterceptor(SecurityFrameworkService securityFrameworkService) {
|
||||||
|
return new DeptVisitContextInterceptor(securityFrameworkService);
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public WebMvcConfigurer tenantWebMvcConfigurer(TenantProperties tenantProperties,
|
public WebMvcConfigurer tenantWebMvcConfigurer(TenantProperties tenantProperties,
|
||||||
@@ -147,6 +152,17 @@ public class YudaoTenantAutoConfiguration {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public WebMvcConfigurer deptWebMvcConfigurer(TenantProperties tenantProperties, DeptVisitContextInterceptor deptVisitContextInterceptor) {
|
||||||
|
return new WebMvcConfigurer() {
|
||||||
|
@Override
|
||||||
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
|
registry.addInterceptor(deptVisitContextInterceptor)
|
||||||
|
.excludePathPatterns(tenantProperties.getIgnoreVisitUrls().toArray(new String[0]));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// ========== Security ==========
|
// ========== Security ==========
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package cn.iocoder.yudao.framework.tenant.core.aop;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 忽略单位切换,标记指定方法不进行租户切换的覆盖
|
||||||
|
* @author chenbowen
|
||||||
|
*/
|
||||||
|
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Inherited
|
||||||
|
public @interface DeptVisitIgnore {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否开启忽略租户,默认为 true 开启
|
||||||
|
*
|
||||||
|
* 支持 Spring EL 表达式,如果返回 true 则满足条件,进行租户的忽略
|
||||||
|
*/
|
||||||
|
String enable() default "true";
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
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 lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.Around;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 忽略单位切换,标记指定方法不进行租户切换的覆盖,基于 {@link DeptVisitIgnore} 注解实现,用于一些全局的逻辑。
|
||||||
|
* 例如说,一个定时任务,读取所有数据,进行处理。
|
||||||
|
* 又例如说,读取所有数据,进行缓存。
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Aspect
|
||||||
|
@Slf4j
|
||||||
|
public class DeptVisitIgnoreAspect {
|
||||||
|
|
||||||
|
@Around("@annotation(deptVisitIgnore)")
|
||||||
|
public Object around(ProceedingJoinPoint joinPoint, DeptVisitIgnore deptVisitIgnore) throws Throwable {
|
||||||
|
Boolean oldIgnore = TenantContextHolder.isIgnore();
|
||||||
|
try {
|
||||||
|
// 计算条件,满足的情况下,才进行忽略
|
||||||
|
Object enable = SpringExpressionUtils.parseExpression(deptVisitIgnore.enable());
|
||||||
|
if (Boolean.TRUE.equals(enable)) {
|
||||||
|
TenantContextHolder.setIgnore(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行逻辑
|
||||||
|
return joinPoint.proceed();
|
||||||
|
} finally {
|
||||||
|
TenantContextHolder.setIgnore(oldIgnore);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
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.web.core.util.WebFrameworkUtils;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
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 {
|
||||||
|
|
||||||
|
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) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
|
||||||
|
if (loginUser == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// if (!securityFrameworkService.hasAnyPermissions(PERMISSION)) {
|
||||||
|
// throw exception0(GlobalErrorCodeConstants.FORBIDDEN.getCode(), "您无权切换部门");
|
||||||
|
// }
|
||||||
|
loginUser.setVisitDeptIds(deptIds);
|
||||||
|
DeptContextHolder.setDeptIdList(deptIds);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
|
||||||
|
// 清理 visitDeptIds
|
||||||
|
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
|
||||||
|
if (loginUser != null) {
|
||||||
|
loginUser.setVisitDeptIds(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package cn.iocoder.yudao.framework.tenant.core.web;
|
package cn.iocoder.yudao.framework.tenant.core.web;
|
||||||
|
|
||||||
import cn.hutool.core.util.ObjUtil;
|
import cn.hutool.core.util.ObjUtil;
|
||||||
|
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||||
import cn.iocoder.yudao.framework.security.core.service.SecurityFrameworkService;
|
import cn.iocoder.yudao.framework.security.core.service.SecurityFrameworkService;
|
||||||
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
||||||
@@ -13,6 +14,11 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.web.servlet.HandlerInterceptor;
|
import org.springframework.web.servlet.HandlerInterceptor;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author chenbowen
|
||||||
|
*/
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class TenantVisitContextInterceptor implements HandlerInterceptor {
|
public class TenantVisitContextInterceptor implements HandlerInterceptor {
|
||||||
@@ -39,10 +45,10 @@ public class TenantVisitContextInterceptor implements HandlerInterceptor {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// // 校验用户是否可切换租户
|
// 校验用户是否可切换租户
|
||||||
// if (!securityFrameworkService.hasAnyPermissions(PERMISSION)) {
|
if (!securityFrameworkService.hasAnyPermissions(PERMISSION)) {
|
||||||
// throw exception0(GlobalErrorCodeConstants.FORBIDDEN.getCode(), "您无权切换租户");
|
throw exception0(GlobalErrorCodeConstants.FORBIDDEN.getCode(), "您无权切换租户");
|
||||||
// }
|
}
|
||||||
|
|
||||||
// 【重点】切换租户编号
|
// 【重点】切换租户编号
|
||||||
loginUser.setVisitTenantId(visitTenantId);
|
loginUser.setVisitTenantId(visitTenantId);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import java.time.LocalDateTime;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登录用户信息
|
* 登录用户信息
|
||||||
@@ -62,6 +63,8 @@ public class LoginUser {
|
|||||||
*/
|
*/
|
||||||
private Long visitTenantId;
|
private Long visitTenantId;
|
||||||
|
|
||||||
|
private Set<Long> visitDeptIds;
|
||||||
|
|
||||||
public void setContext(String key, Object value) {
|
public void setContext(String key, Object value) {
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
context = new HashMap<>();
|
context = new HashMap<>();
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
package cn.iocoder.yudao.framework.web.core.util;
|
package cn.iocoder.yudao.framework.web.core.util;
|
||||||
|
|
||||||
import cn.hutool.core.util.NumberUtil;
|
import cn.hutool.core.util.NumberUtil;
|
||||||
import cn.hutool.extra.servlet.ServletUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.iocoder.yudao.framework.common.enums.RpcConstants;
|
import cn.iocoder.yudao.framework.common.enums.RpcConstants;
|
||||||
import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
|
import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
|
||||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
|
||||||
import cn.iocoder.yudao.framework.web.config.WebProperties;
|
import cn.iocoder.yudao.framework.web.config.WebProperties;
|
||||||
|
import jakarta.servlet.ServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import org.springframework.web.context.request.RequestAttributes;
|
import org.springframework.web.context.request.RequestAttributes;
|
||||||
import org.springframework.web.context.request.RequestContextHolder;
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
import jakarta.servlet.ServletRequest;
|
import java.util.HashSet;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 专属于 web 包的工具类
|
* 专属于 web 包的工具类
|
||||||
@@ -29,6 +30,7 @@ public class WebFrameworkUtils {
|
|||||||
|
|
||||||
public static final String HEADER_TENANT_ID = "tenant-id";
|
public static final String HEADER_TENANT_ID = "tenant-id";
|
||||||
public static final String HEADER_VISIT_TENANT_ID = "visit-tenant-id";
|
public static final String HEADER_VISIT_TENANT_ID = "visit-tenant-id";
|
||||||
|
public static final String HEADER_VISIT_DEPT_IDS = "visit-dept-ids";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 终端的 Header
|
* 终端的 Header
|
||||||
@@ -179,4 +181,27 @@ public class WebFrameworkUtils {
|
|||||||
return className.endsWith("Api");
|
return className.endsWith("Api");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得访问的部门编号列表,从 header 中
|
||||||
|
* @param request 请求
|
||||||
|
* @return 部门编号列表,解析失败或无效时返回 null
|
||||||
|
*/
|
||||||
|
public static Set<Long> getVisitDeptIds(HttpServletRequest request) {
|
||||||
|
String deptIdsHeader = request.getHeader(HEADER_VISIT_DEPT_IDS);
|
||||||
|
if (StrUtil.isBlank(deptIdsHeader)) {
|
||||||
|
return new HashSet<>();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Set<Long> deptIds = java.util.Arrays.stream(deptIdsHeader.split(","))
|
||||||
|
.map(String::trim)
|
||||||
|
.filter(cn.hutool.core.util.StrUtil::isNotBlank)
|
||||||
|
.map(Long::valueOf)
|
||||||
|
.collect(java.util.stream.Collectors.toSet());
|
||||||
|
return deptIds.isEmpty() ? new HashSet<>() : deptIds;
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 解析失败
|
||||||
|
return new HashSet<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.controller.admin.dept;
|
|||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||||
|
import cn.iocoder.yudao.framework.tenant.core.aop.DeptVisitIgnore;
|
||||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptRespVO;
|
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptRespVO;
|
||||||
@@ -71,12 +72,22 @@ public class DeptController {
|
|||||||
|
|
||||||
@GetMapping(value = {"/list-all-simple", "/simple-list"})
|
@GetMapping(value = {"/list-all-simple", "/simple-list"})
|
||||||
@Operation(summary = "获取部门精简信息列表", description = "只包含被开启的部门,主要用于前端的下拉选项")
|
@Operation(summary = "获取部门精简信息列表", description = "只包含被开启的部门,主要用于前端的下拉选项")
|
||||||
|
@DeptVisitIgnore
|
||||||
public CommonResult<List<DeptSimpleRespVO>> getSimpleDeptList() {
|
public CommonResult<List<DeptSimpleRespVO>> getSimpleDeptList() {
|
||||||
List<DeptDO> list = deptService.getDeptList(
|
List<DeptDO> list = deptService.getDeptList(
|
||||||
new DeptListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
new DeptListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
||||||
return success(BeanUtils.toBean(list, DeptSimpleRespVO.class));
|
return success(BeanUtils.toBean(list, DeptSimpleRespVO.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping(value = {"/list-simple", "/simple-dept-list"})
|
||||||
|
@Operation(summary = "获取部门精简信息列表(不包含当前用户归属部门的上级部门)", description = "只包含被开启的部门,主要用于前端的下拉选项")
|
||||||
|
@DeptVisitIgnore
|
||||||
|
public CommonResult<List<DeptSimpleRespVO>> getNoParentSimpleDeptList() {
|
||||||
|
List<DeptDO> list = deptService.getNoParentDeptList(
|
||||||
|
new DeptListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
||||||
|
return success(BeanUtils.toBean(list, DeptSimpleRespVO.class));
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/get")
|
@GetMapping("/get")
|
||||||
@Operation(summary = "获得部门信息")
|
@Operation(summary = "获得部门信息")
|
||||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ public interface DeptService {
|
|||||||
* @return 部门列表
|
* @return 部门列表
|
||||||
*/
|
*/
|
||||||
List<DeptDO> getDeptList(DeptListReqVO reqVO);
|
List<DeptDO> getDeptList(DeptListReqVO reqVO);
|
||||||
|
List<DeptDO> getNoParentDeptList(DeptListReqVO reqVO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得指定编号的部门 Map
|
* 获得指定编号的部门 Map
|
||||||
|
|||||||
@@ -19,9 +19,11 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||||
|
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserDeptId;
|
||||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -169,6 +171,28 @@ public class DeptServiceImpl implements DeptService {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<DeptDO> getNoParentDeptList(DeptListReqVO reqVO){
|
||||||
|
List<DeptDO> list = deptMapper.selectList(reqVO);
|
||||||
|
// 循环过滤掉当前用户归属部门的所有上级部门
|
||||||
|
Long loginUserDeptId = getLoginUserDeptId();
|
||||||
|
Set<Long> parentIds = new HashSet<>();
|
||||||
|
Map<Long, DeptDO> deptMap = list.stream().collect(Collectors.toMap(DeptDO::getId, d -> d));
|
||||||
|
Long currentId = loginUserDeptId;
|
||||||
|
while (currentId != null && !DeptDO.PARENT_ID_ROOT.equals(currentId)) {
|
||||||
|
DeptDO dept = deptMap.get(currentId);
|
||||||
|
if (dept == null) break;
|
||||||
|
Long parentId = dept.getParentId();
|
||||||
|
if (parentId == null || DeptDO.PARENT_ID_ROOT.equals(parentId)) break;
|
||||||
|
parentIds.add(parentId);
|
||||||
|
currentId = parentId;
|
||||||
|
}
|
||||||
|
// 过滤掉所有上级部门
|
||||||
|
return list.stream()
|
||||||
|
.filter(dept -> !parentIds.contains(dept.getId()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<DeptDO> getChildDeptList(Collection<Long> ids) {
|
public List<DeptDO> getChildDeptList(Collection<Long> ids) {
|
||||||
List<DeptDO> children = new LinkedList<>();
|
List<DeptDO> children = new LinkedList<>();
|
||||||
|
|||||||
Reference in New Issue
Block a user