deptIds;
+
+}
diff --git a/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/entity/RoleMenuDataScope.java b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/entity/RoleMenuDataScope.java
new file mode 100644
index 00000000..6f1cf5b0
--- /dev/null
+++ b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/entity/RoleMenuDataScope.java
@@ -0,0 +1,68 @@
+/**
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * 1. This software is for development use only under a valid license
+ * from BladeX.
+ *
+ * 2. Redistribution of this software's source code to any third party
+ * without a commercial license is strictly prohibited.
+ *
+ * 3. Licensees may copyright their own code but cannot use segments
+ * from this software for such purposes. Copyright of this software
+ * remains with BladeX.
+ *
+ * Using this software signifies agreement to this License, and the software
+ * must not be used for illegal purposes.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
+ * not liable for any claims arising from secondary or illegal development.
+ *
+ * Author: Chill Zhuang (bladejava@qq.com)
+ */
+package org.springblade.system.pojo.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.core.tenant.mp.TenantEntity;
+
+import java.io.Serial;
+
+/**
+ * 角色菜单数据权限
+ *
+ * @author Chill
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("blade_role_menu_data_scope")
+@Schema(description = "RoleMenuDataScope对象")
+public class RoleMenuDataScope extends TenantEntity {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 角色主键
+ */
+ @Schema(description = "角色主键")
+ private Long roleId;
+
+ /**
+ * 菜单主键
+ */
+ @Schema(description = "菜单主键")
+ private Long menuId;
+
+ /**
+ * 数据权限类型:1所在部门及下级 2全部组织 3本人创建 4自定义组织
+ */
+ @Schema(description = "数据权限类型")
+ private Integer scopeType;
+
+}
diff --git a/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/entity/RoleMenuDataScopeDept.java b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/entity/RoleMenuDataScopeDept.java
new file mode 100644
index 00000000..5b655fb3
--- /dev/null
+++ b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/entity/RoleMenuDataScopeDept.java
@@ -0,0 +1,65 @@
+/**
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * 1. This software is for development use only under a valid license
+ * from BladeX.
+ *
+ * 2. Redistribution of this software's source code to any third party
+ * without a commercial license is strictly prohibited.
+ *
+ * 3. Licensees may copyright their own code but cannot use segments
+ * from this software for such purposes. Copyright of this software
+ * remains with BladeX.
+ *
+ * Using this software signifies agreement to this License, and the software
+ * must not be used for illegal purposes.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
+ * not liable for any claims arising from secondary or illegal development.
+ *
+ * Author: Chill Zhuang (bladejava@qq.com)
+ */
+package org.springblade.system.pojo.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 角色菜单数据权限自定义组织
+ *
+ * @author Chill
+ */
+@Data
+@TableName("blade_role_menu_data_scope_dept")
+@Schema(description = "RoleMenuDataScopeDept对象")
+public class RoleMenuDataScopeDept implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ @JsonSerialize(using = ToStringSerializer.class)
+ @Schema(description = "主键")
+ @TableId(value = "id", type = IdType.ASSIGN_ID)
+ private Long id;
+
+ @JsonSerialize(using = ToStringSerializer.class)
+ @Schema(description = "角色菜单数据权限主键")
+ private Long scopeId;
+
+ @JsonSerialize(using = ToStringSerializer.class)
+ @Schema(description = "组织主键")
+ private Long deptId;
+
+}
diff --git a/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/enums/RoleDataScopeType.java b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/enums/RoleDataScopeType.java
new file mode 100644
index 00000000..c8e08123
--- /dev/null
+++ b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/enums/RoleDataScopeType.java
@@ -0,0 +1,84 @@
+/**
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * 1. This software is for development use only under a valid license
+ * from BladeX.
+ *
+ * 2. Redistribution of this software's source code to any third party
+ * without a commercial license is strictly prohibited.
+ *
+ * 3. Licensees may copyright their own code but cannot use segments
+ * from this software for such purposes. Copyright of this software
+ * remains with BladeX.
+ *
+ * Using this software signifies agreement to this License, and the software
+ * must not be used for illegal purposes.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
+ * not liable for any claims arising from secondary or illegal development.
+ *
+ * Author: Chill Zhuang (bladejava@qq.com)
+ */
+package org.springblade.system.pojo.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 角色菜单数据权限类型
+ *
+ * @author Chill
+ */
+@Getter
+@AllArgsConstructor
+public enum RoleDataScopeType {
+
+ /**
+ * 所在部门及下级
+ */
+ DEPT_AND_CHILD(1, "所在部门及下级"),
+ /**
+ * 全部组织(默认)
+ */
+ ALL(2, "全部组织"),
+ /**
+ * 本人创建
+ */
+ OWN_CREATE(3, "本人创建"),
+ /**
+ * 自定义组织
+ */
+ CUSTOM(4, "自定义组织");
+
+ private final int code;
+ private final String name;
+
+ /**
+ * 默认权限类型:全部组织
+ */
+ public static final int DEFAULT_CODE = ALL.code;
+
+ public static RoleDataScopeType of(Integer code) {
+ if (code == null) {
+ return ALL;
+ }
+ for (RoleDataScopeType type : values()) {
+ if (type.code == code) {
+ return type;
+ }
+ }
+ return ALL;
+ }
+
+ public static String nameOf(Integer code) {
+ return of(code).getName();
+ }
+
+ public boolean matches(Integer code) {
+ return code != null && this.code == code;
+ }
+}
diff --git a/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/vo/RoleDataScopeVO.java b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/vo/RoleDataScopeVO.java
new file mode 100644
index 00000000..1656f7ae
--- /dev/null
+++ b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/vo/RoleDataScopeVO.java
@@ -0,0 +1,87 @@
+/**
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * 1. This software is for development use only under a valid license
+ * from BladeX.
+ *
+ * 2. Redistribution of this software's source code to any third party
+ * without a commercial license is strictly prohibited.
+ *
+ * 3. Licensees may copyright their own code but cannot use segments
+ * from this software for such purposes. Copyright of this software
+ * remains with BladeX.
+ *
+ * Using this software signifies agreement to this License, and the software
+ * must not be used for illegal purposes.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
+ * not liable for any claims arising from secondary or illegal development.
+ *
+ * Author: Chill Zhuang (bladejava@qq.com)
+ */
+package org.springblade.system.pojo.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springblade.system.pojo.enums.RoleDataScopeType;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 当前用户解析后的角色数据权限
+ *
+ * @author Chill
+ */
+@Data
+@Schema(description = "角色数据权限解析结果")
+public class RoleDataScopeVO implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "数据权限类型")
+ private Integer scopeType;
+
+ @Schema(description = "是否全部组织")
+ private boolean allDept;
+
+ @Schema(description = "是否仅本人创建")
+ private boolean ownCreate;
+
+ @Schema(description = "允许的组织ID集合(已展开)")
+ private List deptIds;
+
+ public static RoleDataScopeVO all() {
+ RoleDataScopeVO scope = new RoleDataScopeVO();
+ scope.setScopeType(RoleDataScopeType.ALL.getCode());
+ scope.setAllDept(true);
+ scope.setOwnCreate(false);
+ scope.setDeptIds(Collections.emptyList());
+ return scope;
+ }
+
+ public static RoleDataScopeVO ownCreate() {
+ RoleDataScopeVO scope = new RoleDataScopeVO();
+ scope.setScopeType(RoleDataScopeType.OWN_CREATE.getCode());
+ scope.setAllDept(false);
+ scope.setOwnCreate(true);
+ scope.setDeptIds(Collections.emptyList());
+ return scope;
+ }
+
+ public static RoleDataScopeVO ofDepts(Integer scopeType, List deptIds) {
+ RoleDataScopeVO scope = new RoleDataScopeVO();
+ scope.setScopeType(scopeType == null ? RoleDataScopeType.CUSTOM.getCode() : scopeType);
+ scope.setAllDept(false);
+ scope.setOwnCreate(false);
+ scope.setDeptIds(deptIds == null ? Collections.emptyList() : List.copyOf(deptIds));
+ return scope;
+ }
+}
diff --git a/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/vo/RoleMenuDataScopeTreeVO.java b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/vo/RoleMenuDataScopeTreeVO.java
new file mode 100644
index 00000000..ac2c155d
--- /dev/null
+++ b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/vo/RoleMenuDataScopeTreeVO.java
@@ -0,0 +1,58 @@
+/**
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * 1. This software is for development use only under a valid license
+ * from BladeX.
+ *
+ * 2. Redistribution of this software's source code to any third party
+ * without a commercial license is strictly prohibited.
+ *
+ * 3. Licensees may copyright their own code but cannot use segments
+ * from this software for such purposes. Copyright of this software
+ * remains with BladeX.
+ *
+ * Using this software signifies agreement to this License, and the software
+ * must not be used for illegal purposes.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
+ * not liable for any claims arising from secondary or illegal development.
+ *
+ * Author: Chill Zhuang (bladejava@qq.com)
+ */
+package org.springblade.system.pojo.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springblade.core.tool.node.TreeNode;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 角色数据权限配置树
+ *
+ * @author Chill
+ */
+@Data
+@Schema(description = "角色数据权限配置树")
+public class RoleMenuDataScopeTreeVO implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "角色名称")
+ private String roleName;
+
+ @Schema(description = "菜单树")
+ private List menuTree = new ArrayList<>();
+
+ @Schema(description = "已配置的菜单数据权限")
+ private List scopes = new ArrayList<>();
+
+}
diff --git a/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/vo/RoleMenuDataScopeVO.java b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/vo/RoleMenuDataScopeVO.java
new file mode 100644
index 00000000..f7081124
--- /dev/null
+++ b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/vo/RoleMenuDataScopeVO.java
@@ -0,0 +1,67 @@
+/**
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * 1. This software is for development use only under a valid license
+ * from BladeX.
+ *
+ * 2. Redistribution of this software's source code to any third party
+ * without a commercial license is strictly prohibited.
+ *
+ * 3. Licensees may copyright their own code but cannot use segments
+ * from this software for such purposes. Copyright of this software
+ * remains with BladeX.
+ *
+ * Using this software signifies agreement to this License, and the software
+ * must not be used for illegal purposes.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
+ * not liable for any claims arising from secondary or illegal development.
+ *
+ * Author: Chill Zhuang (bladejava@qq.com)
+ */
+package org.springblade.system.pojo.vo;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 角色菜单数据权限配置项
+ *
+ * @author Chill
+ */
+@Data
+@Schema(description = "角色菜单数据权限配置项")
+public class RoleMenuDataScopeVO implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ @JsonSerialize(using = ToStringSerializer.class)
+ @Schema(description = "菜单主键")
+ private Long menuId;
+
+ @Schema(description = "菜单编码")
+ private String menuCode;
+
+ @Schema(description = "数据权限类型")
+ private Integer scopeType;
+
+ @Schema(description = "数据权限类型名称")
+ private String scopeTypeName;
+
+ @JsonSerialize(contentUsing = ToStringSerializer.class)
+ @Schema(description = "自定义组织ID集合")
+ private List deptIds = new ArrayList<>();
+
+}
diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/CommonAddressVO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/CommonAddressVO.java
index e0938b4e..6368fae5 100644
--- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/CommonAddressVO.java
+++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/CommonAddressVO.java
@@ -29,6 +29,7 @@ import lombok.EqualsAndHashCode;
import org.springblade.transport.pojo.entity.CommonAddress;
import java.io.Serial;
+import java.util.List;
/**
* 常用地址视图实体类
@@ -63,4 +64,12 @@ public class CommonAddressVO extends CommonAddress {
@Schema(description = "是否查看全部组织")
private Integer allDept;
+ @TableField(exist = false)
+ @Schema(description = "数据权限组织ID集合")
+ private List deptIds;
+
+ @TableField(exist = false)
+ @Schema(description = "仅本人创建时的创建人ID")
+ private Long scopeCreateUser;
+
}
diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/controller/RoleMenuDataScopeController.java b/blade-service/blade-system/src/main/java/org/springblade/system/controller/RoleMenuDataScopeController.java
new file mode 100644
index 00000000..9e6e011f
--- /dev/null
+++ b/blade-service/blade-system/src/main/java/org/springblade/system/controller/RoleMenuDataScopeController.java
@@ -0,0 +1,83 @@
+/**
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * 1. This software is for development use only under a valid license
+ * from BladeX.
+ *
+ * 2. Redistribution of this software's source code to any third party
+ * without a commercial license is strictly prohibited.
+ *
+ * 3. Licensees may copyright their own code but cannot use segments
+ * from this software for such purposes. Copyright of this software
+ * remains with BladeX.
+ *
+ * Using this software signifies agreement to this License, and the software
+ * must not be used for illegal purposes.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
+ * not liable for any claims arising from secondary or illegal development.
+ *
+ * Author: Chill Zhuang (bladejava@qq.com)
+ */
+package org.springblade.system.controller;
+
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import lombok.AllArgsConstructor;
+import org.springblade.core.boot.ctrl.BladeController;
+import org.springblade.core.secure.annotation.IsAdmin;
+import org.springblade.core.tenant.annotation.NonDS;
+import org.springblade.core.tool.api.R;
+import org.springblade.system.pojo.dto.RoleMenuDataScopeDTO;
+import org.springblade.system.pojo.vo.RoleMenuDataScopeTreeVO;
+import org.springblade.system.service.IRoleMenuDataScopeService;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 角色菜单数据权限配置
+ *
+ * @author Chill
+ */
+@NonDS
+@RestController
+@AllArgsConstructor
+@IsAdmin
+@RequestMapping("/role-menu-data-scope")
+@Tag(name = "角色菜单数据权限", description = "角色菜单数据权限")
+public class RoleMenuDataScopeController extends BladeController {
+
+ private final IRoleMenuDataScopeService roleMenuDataScopeService;
+
+ /**
+ * 角色数据权限配置树
+ */
+ @GetMapping("/tree")
+ @ApiOperationSupport(order = 1)
+ @Operation(summary = "角色数据权限配置树", description = "传入roleId")
+ public R tree(@Parameter(description = "角色主键", required = true) @RequestParam Long roleId) {
+ return R.data(roleMenuDataScopeService.tree(roleId));
+ }
+
+ /**
+ * 批量保存角色菜单数据权限
+ */
+ @PostMapping("/submit")
+ @ApiOperationSupport(order = 2)
+ @Operation(summary = "批量保存角色菜单数据权限", description = "传入RoleMenuDataScopeDTO")
+ public R submit(@Valid @RequestBody RoleMenuDataScopeDTO dto) {
+ return R.status(roleMenuDataScopeService.submit(dto));
+ }
+
+}
diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/feign/RoleDataScopeClient.java b/blade-service/blade-system/src/main/java/org/springblade/system/feign/RoleDataScopeClient.java
new file mode 100644
index 00000000..6c91700c
--- /dev/null
+++ b/blade-service/blade-system/src/main/java/org/springblade/system/feign/RoleDataScopeClient.java
@@ -0,0 +1,63 @@
+/**
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * 1. This software is for development use only under a valid license
+ * from BladeX.
+ *
+ * 2. Redistribution of this software's source code to any third party
+ * without a commercial license is strictly prohibited.
+ *
+ * 3. Licensees may copyright their own code but cannot use segments
+ * from this software for such purposes. Copyright of this software
+ * remains with BladeX.
+ *
+ * Using this software signifies agreement to this License, and the software
+ * must not be used for illegal purposes.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
+ * not liable for any claims arising from secondary or illegal development.
+ *
+ * Author: Chill Zhuang (bladejava@qq.com)
+ */
+package org.springblade.system.feign;
+
+import io.swagger.v3.oas.annotations.Hidden;
+import lombok.AllArgsConstructor;
+import org.springblade.core.tenant.annotation.NonDS;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.system.pojo.vo.RoleDataScopeVO;
+import org.springblade.system.service.IRoleMenuDataScopeService;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 角色数据权限 Feign 实现
+ *
+ * @author Chill
+ */
+@NonDS
+@Hidden
+@RestController
+@AllArgsConstructor
+public class RoleDataScopeClient implements IRoleDataScopeClient {
+
+ private final IRoleMenuDataScopeService roleMenuDataScopeService;
+
+ @Override
+ @GetMapping(RESOLVE_BY_MENU_CODE)
+ public R resolveByMenuCode(String roleIds, String menuCode, String deptIds) {
+ return R.data(roleMenuDataScopeService.resolveByMenuCode(Func.toLongList(roleIds), menuCode, Func.toLongList(deptIds)));
+ }
+
+ @Override
+ @GetMapping(RESOLVE_WIDEST)
+ public R resolveWidest(String roleIds, String deptIds) {
+ return R.data(roleMenuDataScopeService.resolveWidest(Func.toLongList(roleIds), Func.toLongList(deptIds)));
+ }
+
+}
diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/mapper/RoleMenuDataScopeDeptMapper.java b/blade-service/blade-system/src/main/java/org/springblade/system/mapper/RoleMenuDataScopeDeptMapper.java
new file mode 100644
index 00000000..d54d06e7
--- /dev/null
+++ b/blade-service/blade-system/src/main/java/org/springblade/system/mapper/RoleMenuDataScopeDeptMapper.java
@@ -0,0 +1,37 @@
+/**
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * 1. This software is for development use only under a valid license
+ * from BladeX.
+ *
+ * 2. Redistribution of this software's source code to any third party
+ * without a commercial license is strictly prohibited.
+ *
+ * 3. Licensees may copyright their own code but cannot use segments
+ * from this software for such purposes. Copyright of this software
+ * remains with BladeX.
+ *
+ * Using this software signifies agreement to this License, and the software
+ * must not be used for illegal purposes.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
+ * not liable for any claims arising from secondary or illegal development.
+ *
+ * Author: Chill Zhuang (bladejava@qq.com)
+ */
+package org.springblade.system.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.springblade.system.pojo.entity.RoleMenuDataScopeDept;
+
+/**
+ * 角色菜单数据权限自定义组织 Mapper
+ *
+ * @author Chill
+ */
+public interface RoleMenuDataScopeDeptMapper extends BaseMapper {
+}
diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/mapper/RoleMenuDataScopeMapper.java b/blade-service/blade-system/src/main/java/org/springblade/system/mapper/RoleMenuDataScopeMapper.java
new file mode 100644
index 00000000..06849aa6
--- /dev/null
+++ b/blade-service/blade-system/src/main/java/org/springblade/system/mapper/RoleMenuDataScopeMapper.java
@@ -0,0 +1,37 @@
+/**
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * 1. This software is for development use only under a valid license
+ * from BladeX.
+ *
+ * 2. Redistribution of this software's source code to any third party
+ * without a commercial license is strictly prohibited.
+ *
+ * 3. Licensees may copyright their own code but cannot use segments
+ * from this software for such purposes. Copyright of this software
+ * remains with BladeX.
+ *
+ * Using this software signifies agreement to this License, and the software
+ * must not be used for illegal purposes.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
+ * not liable for any claims arising from secondary or illegal development.
+ *
+ * Author: Chill Zhuang (bladejava@qq.com)
+ */
+package org.springblade.system.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.springblade.system.pojo.entity.RoleMenuDataScope;
+
+/**
+ * 角色菜单数据权限 Mapper
+ *
+ * @author Chill
+ */
+public interface RoleMenuDataScopeMapper extends BaseMapper {
+}
diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/IRoleMenuDataScopeService.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/IRoleMenuDataScopeService.java
new file mode 100644
index 00000000..93cb33d2
--- /dev/null
+++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/IRoleMenuDataScopeService.java
@@ -0,0 +1,78 @@
+/**
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * 1. This software is for development use only under a valid license
+ * from BladeX.
+ *
+ * 2. Redistribution of this software's source code to any third party
+ * without a commercial license is strictly prohibited.
+ *
+ * 3. Licensees may copyright their own code but cannot use segments
+ * from this software for such purposes. Copyright of this software
+ * remains with BladeX.
+ *
+ * Using this software signifies agreement to this License, and the software
+ * must not be used for illegal purposes.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
+ * not liable for any claims arising from secondary or illegal development.
+ *
+ * Author: Chill Zhuang (bladejava@qq.com)
+ */
+package org.springblade.system.service;
+
+import org.springblade.core.mp.base.BaseService;
+import org.springblade.system.pojo.dto.RoleMenuDataScopeDTO;
+import org.springblade.system.pojo.entity.RoleMenuDataScope;
+import org.springblade.system.pojo.vo.RoleDataScopeVO;
+import org.springblade.system.pojo.vo.RoleMenuDataScopeTreeVO;
+
+import java.util.List;
+
+/**
+ * 角色菜单数据权限服务
+ *
+ * @author Chill
+ */
+public interface IRoleMenuDataScopeService extends BaseService {
+
+ /**
+ * 查询角色数据权限配置树
+ *
+ * @param roleId 角色主键
+ * @return 菜单树与已配置权限
+ */
+ RoleMenuDataScopeTreeVO tree(Long roleId);
+
+ /**
+ * 批量保存角色菜单数据权限
+ *
+ * @param dto 保存参数
+ * @return 是否成功
+ */
+ boolean submit(RoleMenuDataScopeDTO dto);
+
+ /**
+ * 按菜单编码合并解析角色数据权限
+ *
+ * @param roleIds 角色ID集合
+ * @param menuCode 菜单编码
+ * @param userDeptIds 用户所属组织
+ * @return 解析结果
+ */
+ RoleDataScopeVO resolveByMenuCode(List roleIds, String menuCode, List userDeptIds);
+
+ /**
+ * 合并用户全部角色菜单配置为最宽数据权限
+ *
+ * @param roleIds 角色ID集合
+ * @param userDeptIds 用户所属组织
+ * @return 解析结果
+ */
+ RoleDataScopeVO resolveWidest(List roleIds, List userDeptIds);
+
+}
diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DeptServiceImpl.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DeptServiceImpl.java
index 551918ed..a211a169 100644
--- a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DeptServiceImpl.java
+++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/DeptServiceImpl.java
@@ -46,15 +46,15 @@ import org.springblade.core.tool.node.ForestNodeMerger;
import org.springblade.core.tool.utils.*;
import org.springblade.system.cache.SysCache;
import org.springblade.system.mapper.DeptMapper;
-import org.springblade.system.mapper.UserDataScopeMapper;
import org.springblade.system.pojo.entity.Dept;
import org.springblade.system.pojo.entity.User;
-import org.springblade.system.pojo.entity.UserDataScope;
import org.springblade.system.pojo.enums.DeptCategory;
import org.springblade.system.pojo.vo.DeptVO;
+import org.springblade.system.pojo.vo.RoleDataScopeVO;
import org.springblade.system.pojo.vo.UserVO;
import org.springblade.system.props.IamSyncProperties;
import org.springblade.system.service.IDeptService;
+import org.springblade.system.service.IRoleMenuDataScopeService;
import org.springblade.system.service.IUserService;
import org.springblade.system.wrapper.DeptWrapper;
import org.springblade.system.wrapper.UserWrapper;
@@ -94,19 +94,9 @@ public class DeptServiceImpl extends ServiceImpl implements ID
private static final String IAM_SYNC_TENANT_ID = "000000";
private static final Long IAM_SYNC_PARENT_ID = 1123598813738675201L;
private static final HttpClient IAM_HTTP_CLIENT = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();
- /** 数据权限范围:仅所属组织 */
- private static final int DATA_SCOPE_OWN = 1;
- /** 数据权限范围:全部组织 */
- private static final int DATA_SCOPE_ALL = 2;
- /** 数据权限范围:自定义 */
- private static final int DATA_SCOPE_CUSTOM = 3;
- /** 数据层级范围:包含下级 */
- private static final int DATA_LEVEL_INCLUDE_CHILDREN = 1;
- /** 数据层级范围:仅本级 */
- private static final int DATA_LEVEL_CURRENT_ONLY = 2;
private final IUserService userService;
- private final UserDataScopeMapper userDataScopeMapper;
+ private final IRoleMenuDataScopeService roleMenuDataScopeService;
private final IamSyncProperties iamSyncProperties;
private final ObjectMapper objectMapper;
@@ -237,7 +227,8 @@ public class DeptServiceImpl extends ServiceImpl implements ID
/**
* 解析当前登录用户的组织树数据权限。
*
- * 超管或「全部组织」返回 allDept=true;其余按所属组织/自定义组织,并结合「包含下级/仅本级」展开。
+ * 超管或角色菜单数据权限最宽合并为「全部组织」时不过滤;
+ * 其余按角色配置的组织并集过滤。仅「本人创建」时按用户所属组织及下级展示组织树。
*
* @return 过滤上下文;无法识别用户时按空权限处理
*/
@@ -246,55 +237,39 @@ public class DeptServiceImpl extends ServiceImpl implements ID
return DeptDataScopeFilter.all();
}
Long userId = AuthUtil.getUserId();
- if (Func.isEmpty(userId) || userId <= 0) {
+ if (Func.isEmpty(userId) || userId <= 0 || AuthUtil.getUser() == null) {
return DeptDataScopeFilter.empty();
}
+ List roleIds = Func.toLongList(AuthUtil.getUser().getRoleId());
+ List userDeptIds = Func.toLongList(AuthUtil.getDeptId());
User user = userService.getById(userId);
- if (Func.isEmpty(user)) {
- return DeptDataScopeFilter.empty();
+ if (Func.isNotEmpty(user) && Func.isNotEmpty(user.getDeptId())) {
+ userDeptIds = Func.toLongList(user.getDeptId());
}
- Integer dataScopeRange = user.getDataScopeRange() == null ? DATA_SCOPE_OWN : user.getDataScopeRange();
- Integer dataLevelRange = user.getDataLevelRange() == null ? DATA_LEVEL_INCLUDE_CHILDREN : user.getDataLevelRange();
- if (Objects.equals(dataScopeRange, DATA_SCOPE_ALL)) {
+ RoleDataScopeVO scope = roleMenuDataScopeService.resolveWidest(roleIds, userDeptIds);
+ if (scope == null || scope.isAllDept()) {
return DeptDataScopeFilter.all();
}
- List baseDeptIds;
- if (Objects.equals(dataScopeRange, DATA_SCOPE_CUSTOM)) {
- baseDeptIds = userDataScopeMapper.selectList(Wrappers.lambdaQuery()
- .eq(UserDataScope::getUserId, userId))
- .stream()
- .map(UserDataScope::getDeptId)
- .filter(Objects::nonNull)
- .distinct()
- .toList();
- } else {
- // 仅所属组织:优先用户档案中的所属组织,其次当前会话组织。
- baseDeptIds = Func.toLongList(user.getDeptId());
- if (Func.isEmpty(baseDeptIds)) {
- baseDeptIds = Func.toLongList(AuthUtil.getDeptId());
- }
- }
- if (Func.isEmpty(baseDeptIds)) {
- return DeptDataScopeFilter.empty();
- }
- boolean includeChildren = !Objects.equals(dataLevelRange, DATA_LEVEL_CURRENT_ONLY);
LinkedHashSet allowedDeptIds = new LinkedHashSet<>();
- for (Long deptId : baseDeptIds) {
- if (Func.isEmpty(deptId) || deptId <= 0) {
- continue;
- }
- if (includeChildren) {
+ if (scope.isOwnCreate()) {
+ for (Long deptId : userDeptIds) {
+ if (Func.isEmpty(deptId) || deptId <= 0) {
+ continue;
+ }
List childIds = SysCache.getDeptChildIds(deptId);
if (Func.isNotEmpty(childIds)) {
allowedDeptIds.addAll(childIds);
} else {
allowedDeptIds.add(deptId);
}
- } else {
- allowedDeptIds.add(deptId);
}
+ } else if (Func.isNotEmpty(scope.getDeptIds())) {
+ allowedDeptIds.addAll(scope.getDeptIds());
}
- return new DeptDataScopeFilter(false, includeChildren, allowedDeptIds);
+ if (Func.isEmpty(allowedDeptIds)) {
+ return DeptDataScopeFilter.empty();
+ }
+ return new DeptDataScopeFilter(false, true, allowedDeptIds);
}
/**
diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/RoleMenuDataScopeServiceImpl.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/RoleMenuDataScopeServiceImpl.java
new file mode 100644
index 00000000..0314afc9
--- /dev/null
+++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/RoleMenuDataScopeServiceImpl.java
@@ -0,0 +1,288 @@
+/**
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * 1. This software is for development use only under a valid license
+ * from BladeX.
+ *
+ * 2. Redistribution of this software's source code to any third party
+ * without a commercial license is strictly prohibited.
+ *
+ * 3. Licensees may copyright their own code but cannot use segments
+ * from this software for such purposes. Copyright of this software
+ * remains with BladeX.
+ *
+ * Using this software signifies agreement to this License, and the software
+ * must not be used for illegal purposes.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
+ * not liable for any claims arising from secondary or illegal development.
+ *
+ * Author: Chill Zhuang (bladejava@qq.com)
+ */
+package org.springblade.system.service.impl;
+
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import org.springblade.core.cache.utils.CacheUtil;
+import org.springblade.core.log.exception.ServiceException;
+import org.springblade.core.mp.base.BaseServiceImpl;
+import org.springblade.core.secure.utils.AuthUtil;
+import org.springblade.core.tool.node.ForestNodeMerger;
+import org.springblade.core.tool.node.TreeNode;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.system.cache.SysCache;
+import org.springblade.system.mapper.MenuMapper;
+import org.springblade.system.mapper.RoleMenuDataScopeDeptMapper;
+import org.springblade.system.mapper.RoleMenuDataScopeMapper;
+import org.springblade.system.pojo.dto.RoleMenuDataScopeDTO;
+import org.springblade.system.pojo.entity.Menu;
+import org.springblade.system.pojo.entity.Role;
+import org.springblade.system.pojo.entity.RoleMenuDataScope;
+import org.springblade.system.pojo.entity.RoleMenuDataScopeDept;
+import org.springblade.system.pojo.enums.RoleDataScopeType;
+import org.springblade.system.pojo.vo.RoleDataScopeVO;
+import org.springblade.system.pojo.vo.RoleMenuDataScopeTreeVO;
+import org.springblade.system.pojo.vo.RoleMenuDataScopeVO;
+import org.springblade.system.service.IRoleMenuDataScopeService;
+import org.springblade.system.service.IRoleService;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static org.springblade.core.cache.constant.CacheConstant.SYS_CACHE;
+
+/**
+ * 角色菜单数据权限服务实现
+ *
+ * @author Chill
+ */
+@Service
+@RequiredArgsConstructor
+public class RoleMenuDataScopeServiceImpl extends BaseServiceImpl
+ implements IRoleMenuDataScopeService {
+
+ private final RoleMenuDataScopeDeptMapper roleMenuDataScopeDeptMapper;
+ private final MenuMapper menuMapper;
+ private final IRoleService roleService;
+
+ @Override
+ public RoleMenuDataScopeTreeVO tree(Long roleId) {
+ if (Func.isEmpty(roleId) || roleId <= 0) {
+ throw new ServiceException("角色ID不能为空");
+ }
+ Role role = roleService.getById(roleId);
+ if (Func.isEmpty(role)) {
+ throw new ServiceException("角色不存在");
+ }
+ RoleMenuDataScopeTreeVO treeVO = new RoleMenuDataScopeTreeVO();
+ treeVO.setRoleName(role.getRoleName());
+ List menuNodes = menuMapper.grantTree();
+ treeVO.setMenuTree(ForestNodeMerger.merge(menuNodes));
+
+ List scopeList = list(Wrappers.lambdaQuery()
+ .eq(RoleMenuDataScope::getRoleId, roleId)
+ .eq(RoleMenuDataScope::getIsDeleted, 0));
+ if (Func.isEmpty(scopeList)) {
+ treeVO.setScopes(Collections.emptyList());
+ return treeVO;
+ }
+ List scopeIds = scopeList.stream().map(RoleMenuDataScope::getId).toList();
+ Map> deptMap = roleMenuDataScopeDeptMapper.selectList(Wrappers.lambdaQuery()
+ .in(RoleMenuDataScopeDept::getScopeId, scopeIds))
+ .stream()
+ .collect(Collectors.groupingBy(RoleMenuDataScopeDept::getScopeId,
+ Collectors.mapping(RoleMenuDataScopeDept::getDeptId, Collectors.toList())));
+
+ List menuIds = scopeList.stream().map(RoleMenuDataScope::getMenuId).distinct().toList();
+ Map menuCodeMap = menuMapper.selectBatchIds(menuIds).stream()
+ .collect(Collectors.toMap(Menu::getId, Menu::getCode, (left, right) -> left));
+
+ List scopeVOList = new ArrayList<>();
+ for (RoleMenuDataScope scope : scopeList) {
+ RoleMenuDataScopeVO scopeVO = new RoleMenuDataScopeVO();
+ scopeVO.setMenuId(scope.getMenuId());
+ scopeVO.setMenuCode(menuCodeMap.get(scope.getMenuId()));
+ scopeVO.setScopeType(scope.getScopeType() == null ? RoleDataScopeType.DEFAULT_CODE : scope.getScopeType());
+ scopeVO.setScopeTypeName(RoleDataScopeType.nameOf(scopeVO.getScopeType()));
+ scopeVO.setDeptIds(deptMap.getOrDefault(scope.getId(), Collections.emptyList()));
+ scopeVOList.add(scopeVO);
+ }
+ treeVO.setScopes(scopeVOList);
+ return treeVO;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public boolean submit(RoleMenuDataScopeDTO dto) {
+ if (dto == null || Func.isEmpty(dto.getRoleId()) || dto.getRoleId() <= 0) {
+ throw new ServiceException("角色ID不能为空");
+ }
+ if (Func.isEmpty(dto.getMenuIds())) {
+ throw new ServiceException("请选择要设置的菜单");
+ }
+ RoleDataScopeType scopeType = RoleDataScopeType.of(dto.getScopeType());
+ if (scopeType == RoleDataScopeType.CUSTOM && Func.isEmpty(dto.getDeptIds())) {
+ throw new ServiceException("自定义组织时请勾选组织");
+ }
+ Role role = roleService.getById(dto.getRoleId());
+ if (Func.isEmpty(role)) {
+ throw new ServiceException("角色不存在");
+ }
+ String tenantId = Func.toStrWithEmpty(role.getTenantId(), AuthUtil.getTenantId());
+ List menuIds = dto.getMenuIds().stream().filter(Objects::nonNull).distinct().toList();
+ List deptIds = scopeType == RoleDataScopeType.CUSTOM
+ ? dto.getDeptIds().stream().filter(Objects::nonNull).distinct().toList()
+ : Collections.emptyList();
+
+ for (Long menuId : menuIds) {
+ RoleMenuDataScope exists = getOne(Wrappers.lambdaQuery()
+ .eq(RoleMenuDataScope::getRoleId, dto.getRoleId())
+ .eq(RoleMenuDataScope::getMenuId, menuId)
+ .eq(RoleMenuDataScope::getIsDeleted, 0)
+ .last("limit 1"));
+ RoleMenuDataScope scope;
+ if (Func.isEmpty(exists)) {
+ scope = new RoleMenuDataScope();
+ scope.setTenantId(tenantId);
+ scope.setRoleId(dto.getRoleId());
+ scope.setMenuId(menuId);
+ scope.setScopeType(scopeType.getCode());
+ save(scope);
+ } else {
+ scope = exists;
+ scope.setScopeType(scopeType.getCode());
+ updateById(scope);
+ roleMenuDataScopeDeptMapper.delete(Wrappers.lambdaQuery()
+ .eq(RoleMenuDataScopeDept::getScopeId, scope.getId()));
+ }
+ if (scopeType == RoleDataScopeType.CUSTOM) {
+ for (Long deptId : deptIds) {
+ RoleMenuDataScopeDept scopeDept = new RoleMenuDataScopeDept();
+ scopeDept.setScopeId(scope.getId());
+ scopeDept.setDeptId(deptId);
+ roleMenuDataScopeDeptMapper.insert(scopeDept);
+ }
+ }
+ }
+ CacheUtil.clear(SYS_CACHE, Boolean.FALSE);
+ return true;
+ }
+
+ @Override
+ public RoleDataScopeVO resolveByMenuCode(List roleIds, String menuCode, List userDeptIds) {
+ if (Func.isEmpty(roleIds) || Func.isBlank(menuCode)) {
+ return RoleDataScopeVO.all();
+ }
+ Menu menu = menuMapper.selectOne(Wrappers.