diff --git a/yudao-framework/yudao-spring-boot-starter-biz-data-permission/pom.xml b/yudao-framework/yudao-spring-boot-starter-biz-data-permission/pom.xml index a6db4949..9f0c712f 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-data-permission/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-biz-data-permission/pom.xml @@ -47,6 +47,10 @@ yudao-spring-boot-starter-test test + + cn.iocoder.cloud + yudao-spring-boot-starter-biz-tenant + diff --git a/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java b/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java index 7ec7a4a5..a2e82f46 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java @@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; 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.dto.DeptDataPermissionRespDTO; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 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.security.core.LoginUser; 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 lombok.AllArgsConstructor; 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.relational.EqualsTo; import net.sf.jsqlparser.expression.operators.relational.ExpressionList; @@ -112,6 +116,15 @@ public class DeptDataPermissionRule implements DataPermissionRule { // 添加到上下文中,避免重复计算 loginUser.setContext(CONTEXT_KEY, deptDataPermission); } + // 如果开启了部门上下文,且缓存的部门编号不等于 DeptContextHolder 的部门编号,则更新缓存 + if(!DeptContextHolder.isIgnore()) { + Set deptIds = DeptContextHolder.getDeptIdList(); + if (CollUtil.isNotEmpty(deptIds) && !CollUtil.isEqualList(deptDataPermission.getDeptIds(), deptIds)) { + deptDataPermission.setDeptIds(deptIds); + // 更新到上下文中 + loginUser.setContext(CONTEXT_KEY, deptDataPermission); + } + } // 情况一,如果是 ALL 可查看全部,则无需拼接条件 if (deptDataPermission.getAll()) { diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java index 8fe59c3a..49055ded 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.framework.tenant.config; 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.mybatis.core.util.MyBatisUtils; 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.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.TenantContextWebFilter; import cn.iocoder.yudao.framework.tenant.core.web.TenantVisitContextInterceptor; import cn.iocoder.yudao.framework.web.config.WebProperties; 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.inner.TenantLineInnerInterceptor; import jakarta.annotation.Resource; @@ -133,6 +134,10 @@ public class YudaoTenantAutoConfiguration { SecurityFrameworkService securityFrameworkService) { return new TenantVisitContextInterceptor(tenantProperties, securityFrameworkService); } + @Bean + public DeptVisitContextInterceptor deptVisitContextInterceptor(SecurityFrameworkService securityFrameworkService) { + return new DeptVisitContextInterceptor(securityFrameworkService); + } @Bean 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 ========== @Bean diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/aop/DeptVisitIgnore.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/aop/DeptVisitIgnore.java new file mode 100644 index 00000000..b30160f5 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/aop/DeptVisitIgnore.java @@ -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"; + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/aop/DeptVisitIgnoreAspect.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/aop/DeptVisitIgnoreAspect.java new file mode 100644 index 00000000..cc3b0aa2 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/aop/DeptVisitIgnoreAspect.java @@ -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); + } + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/context/DeptContextHolder.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/context/DeptContextHolder.java new file mode 100644 index 00000000..e4c5d7ce --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/context/DeptContextHolder.java @@ -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> DEPT_ID_LIST = new TransmittableThreadLocal<>(); + + /** + * 是否忽略部门 + */ + private static final ThreadLocal IGNORE = new TransmittableThreadLocal<>(); + + /** + * 获得部门编号列表 + * + * @return 部门编号列表 + */ + public static Set getDeptIdList() { + return DEPT_ID_LIST.get(); + } + + /** + * 获得部门编号列表。如果不存在,则抛出 NullPointerException 异常 + * + * @return 部门编号列表 + */ + public static Set getRequiredDeptIdList() { + Set deptIdList = getDeptIdList(); + if (deptIdList == null) { + throw new NullPointerException("DeptContextHolder 不存在部门编号列表!可参考文档:" + + DocumentEnum.TENANT.getUrl()); + } + return deptIdList; + } + + public static void setDeptIdList(Set 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(); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/web/DeptVisitContextInterceptor.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/web/DeptVisitContextInterceptor.java new file mode 100644 index 00000000..7c1370da --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/web/DeptVisitContextInterceptor.java @@ -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 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); + } + } +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/web/TenantVisitContextInterceptor.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/web/TenantVisitContextInterceptor.java index 12d78774..4ab9eb6a 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/web/TenantVisitContextInterceptor.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/web/TenantVisitContextInterceptor.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.framework.tenant.core.web; 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.service.SecurityFrameworkService; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; @@ -13,6 +14,11 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.servlet.HandlerInterceptor; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0; + +/** + * @author chenbowen + */ @RequiredArgsConstructor @Slf4j public class TenantVisitContextInterceptor implements HandlerInterceptor { @@ -39,10 +45,10 @@ public class TenantVisitContextInterceptor implements HandlerInterceptor { return true; } -// // 校验用户是否可切换租户 -// if (!securityFrameworkService.hasAnyPermissions(PERMISSION)) { -// throw exception0(GlobalErrorCodeConstants.FORBIDDEN.getCode(), "您无权切换租户"); -// } + // 校验用户是否可切换租户 + if (!securityFrameworkService.hasAnyPermissions(PERMISSION)) { + throw exception0(GlobalErrorCodeConstants.FORBIDDEN.getCode(), "您无权切换租户"); + } // 【重点】切换租户编号 loginUser.setVisitTenantId(visitTenantId); diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/LoginUser.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/LoginUser.java index ec53d203..91dd320e 100644 --- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/LoginUser.java +++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/LoginUser.java @@ -9,6 +9,7 @@ import java.time.LocalDateTime; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; /** * 登录用户信息 @@ -62,6 +63,8 @@ public class LoginUser { */ private Long visitTenantId; + private Set visitDeptIds; + public void setContext(String key, Object value) { if (context == null) { context = new HashMap<>(); diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java index 86b5c3e4..013a2523 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java @@ -1,19 +1,20 @@ package cn.iocoder.yudao.framework.web.core.util; 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.TerminalEnum; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; 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 jakarta.servlet.ServletRequest; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; -import jakarta.servlet.ServletRequest; -import jakarta.servlet.http.HttpServletRequest; +import java.util.HashSet; +import java.util.Set; /** * 专属于 web 包的工具类 @@ -29,6 +30,7 @@ public class WebFrameworkUtils { 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_DEPT_IDS = "visit-dept-ids"; /** * 终端的 Header @@ -179,4 +181,27 @@ public class WebFrameworkUtils { return className.endsWith("Api"); } + /** + * 获得访问的部门编号列表,从 header 中 + * @param request 请求 + * @return 部门编号列表,解析失败或无效时返回 null + */ + public static Set getVisitDeptIds(HttpServletRequest request) { + String deptIdsHeader = request.getHeader(HEADER_VISIT_DEPT_IDS); + if (StrUtil.isBlank(deptIdsHeader)) { + return new HashSet<>(); + } + try { + Set 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<>(); + } + } + } diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java index 2f43da82..144b55a6 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java @@ -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.pojo.CommonResult; 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.module.system.controller.admin.dept.vo.dept.DeptListReqVO; 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"}) @Operation(summary = "获取部门精简信息列表", description = "只包含被开启的部门,主要用于前端的下拉选项") + @DeptVisitIgnore public CommonResult> getSimpleDeptList() { List list = deptService.getDeptList( new DeptListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus())); return success(BeanUtils.toBean(list, DeptSimpleRespVO.class)); } + @GetMapping(value = {"/list-simple", "/simple-dept-list"}) + @Operation(summary = "获取部门精简信息列表(不包含当前用户归属部门的上级部门)", description = "只包含被开启的部门,主要用于前端的下拉选项") + @DeptVisitIgnore + public CommonResult> getNoParentSimpleDeptList() { + List list = deptService.getNoParentDeptList( + new DeptListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus())); + return success(BeanUtils.toBean(list, DeptSimpleRespVO.class)); + } + @GetMapping("/get") @Operation(summary = "获得部门信息") @Parameter(name = "id", description = "编号", required = true, example = "1024") diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java index a0b765e5..285063f7 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java @@ -59,6 +59,7 @@ public interface DeptService { * @return 部门列表 */ List getDeptList(DeptListReqVO reqVO); + List getNoParentDeptList(DeptListReqVO reqVO); /** * 获得指定编号的部门 Map diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java index 89d1f64e..cc63ff95 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java @@ -19,9 +19,11 @@ import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; 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.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserDeptId; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; /** @@ -169,6 +171,28 @@ public class DeptServiceImpl implements DeptService { return list; } + @Override + public List getNoParentDeptList(DeptListReqVO reqVO){ + List list = deptMapper.selectList(reqVO); + // 循环过滤掉当前用户归属部门的所有上级部门 + Long loginUserDeptId = getLoginUserDeptId(); + Set parentIds = new HashSet<>(); + Map 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 public List getChildDeptList(Collection ids) { List children = new LinkedList<>();