1. 新增业务数据查询,新增 部门 数据权限规则支持
2. 补全子角色排除父角色管理菜单测试用例
This commit is contained in:
@@ -31,6 +31,12 @@
|
|||||||
<artifactId>yudao-spring-boot-starter-biz-data-permission</artifactId>
|
<artifactId>yudao-spring-boot-starter-biz-data-permission</artifactId>
|
||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- Test 测试相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.cloud</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package cn.iocoder.yudao.framework.business.framework;
|
package cn.iocoder.yudao.framework.business.framework;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.datapermission.core.rule.company.CompanyDataPermissionRuleCustomizer;
|
import cn.iocoder.yudao.framework.datapermission.core.rule.company.CompanyDataPermissionRuleCustomizer;
|
||||||
|
import cn.iocoder.yudao.framework.datapermission.core.rule.dept.DeptDataPermissionRuleCustomizer;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
@@ -10,10 +11,18 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
public class BusinessDataPermissionConfiguration {
|
public class BusinessDataPermissionConfiguration {
|
||||||
@Bean
|
@Bean
|
||||||
public CompanyDataPermissionRuleCustomizer sysDeptDataPermissionRuleCustomizer() {
|
public CompanyDataPermissionRuleCustomizer sysCompanyDataPermissionRuleCustomizer() {
|
||||||
return rule -> {
|
return rule -> {
|
||||||
// companyId
|
// companyId
|
||||||
rule.addCompanyColumn("demo_contract", "company_id");
|
rule.addCompanyColumn("demo_contract", "company_id");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public DeptDataPermissionRuleCustomizer sysDeptDataPermissionRuleCustomizer() {
|
||||||
|
return rule -> {
|
||||||
|
// dept
|
||||||
|
rule.addDeptColumn("demo_contract", "dept_id");
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
cn.iocoder.yudao.framework.business.config.YudaoBusinessAutoConfiguration
|
cn.iocoder.yudao.framework.business.config.YudaoBusinessAutoConfiguration
|
||||||
|
cn.iocoder.yudao.framework.business.framework.BusinessDataPermissionConfiguration
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -21,8 +21,8 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
@AutoConfiguration
|
@AutoConfiguration
|
||||||
@ConditionalOnClass(LoginUser.class)
|
@ConditionalOnClass(LoginUser.class)
|
||||||
@ConditionalOnBean(value = {CompanyDataPermissionRuleCustomizer.class})
|
@ConditionalOnBean(value = {CompanyDataPermissionRuleCustomizer.class, DeptDataPermissionRuleCustomizer.class})
|
||||||
public class YudaoCompanyDataPermissionAutoConfiguration {
|
public class YudaoBusinessDataPermissionAutoConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public CompanyDataPermissionRule companyDataPermissionRule(List<CompanyDataPermissionRuleCustomizer> customizers) {
|
public CompanyDataPermissionRule companyDataPermissionRule(List<CompanyDataPermissionRuleCustomizer> customizers) {
|
||||||
@@ -33,4 +33,22 @@ public class YudaoCompanyDataPermissionAutoConfiguration {
|
|||||||
customizers.forEach(customizer -> customizer.customize(rule));
|
customizers.forEach(customizer -> customizer.customize(rule));
|
||||||
return rule;
|
return rule;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public DeptDataPermissionRule deptDataPermissionRule(PermissionCommonApi permissionApi, List<DeptDataPermissionRuleCustomizer> customizers) {
|
||||||
|
// Cloud 专属逻辑:优先使用本地的 PermissionApi 实现类,而不是 Feign 调用
|
||||||
|
// 原因:在创建租户时,租户还没创建好,导致 Feign 调用获取数据权限时,报“租户不存在”的错误
|
||||||
|
try {
|
||||||
|
PermissionCommonApi permissionApiImpl = SpringUtil.getBean("permissionApiImpl", PermissionCommonApi.class);
|
||||||
|
if (permissionApiImpl != null) {
|
||||||
|
permissionApi = permissionApiImpl;
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
|
||||||
|
// 创建 DeptDataPermissionRule 对象
|
||||||
|
DeptDataPermissionRule rule = new DeptDataPermissionRule(permissionApi);
|
||||||
|
// 补全表配置
|
||||||
|
customizers.forEach(customizer -> customizer.customize(rule));
|
||||||
|
return rule;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
cn.iocoder.yudao.framework.datapermission.config.YudaoDataPermissionAutoConfiguration
|
cn.iocoder.yudao.framework.datapermission.config.YudaoDataPermissionAutoConfiguration
|
||||||
cn.iocoder.yudao.framework.datapermission.config.YudaoDeptDataPermissionAutoConfiguration
|
cn.iocoder.yudao.framework.datapermission.config.YudaoDeptDataPermissionAutoConfiguration
|
||||||
cn.iocoder.yudao.framework.datapermission.config.YudaoCompanyDataPermissionAutoConfiguration
|
cn.iocoder.yudao.framework.datapermission.config.YudaoBusinessDataPermissionAutoConfiguration
|
||||||
cn.iocoder.yudao.framework.datapermission.config.YudaoDataPermissionRpcAutoConfiguration
|
cn.iocoder.yudao.framework.datapermission.config.YudaoDataPermissionRpcAutoConfiguration
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public interface ErrorCodeConstants {
|
|||||||
ErrorCode ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE = new ErrorCode(1_002_002_003, "不能操作类型为系统内置的角色");
|
ErrorCode ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE = new ErrorCode(1_002_002_003, "不能操作类型为系统内置的角色");
|
||||||
ErrorCode ROLE_IS_DISABLE = new ErrorCode(1_002_002_004, "名字为【{}】的角色已被禁用");
|
ErrorCode ROLE_IS_DISABLE = new ErrorCode(1_002_002_004, "名字为【{}】的角色已被禁用");
|
||||||
ErrorCode ROLE_ADMIN_CODE_ERROR = new ErrorCode(1_002_002_005, "标识【{}】不能使用");
|
ErrorCode ROLE_ADMIN_CODE_ERROR = new ErrorCode(1_002_002_005, "标识【{}】不能使用");
|
||||||
ErrorCode ROLE_CAN_NOT_UPDATE_NORMAL_TYPE_ROLE = new ErrorCode(1_002_002_006, "不能操作类型为标准的角色,除非是管理员角色");
|
ErrorCode ROLE_CAN_NOT_UPDATE_NORMAL_TYPE_ROLE = new ErrorCode(1_002_002_006, "非管理员,不能操作类型为标准的角色");
|
||||||
ErrorCode ROLE_CAN_NOT_DELETE_HAS_CHILDREN = new ErrorCode(1_002_002_007, " 角色【{}】存在子角色,不允许删除");
|
ErrorCode ROLE_CAN_NOT_DELETE_HAS_CHILDREN = new ErrorCode(1_002_002_007, " 角色【{}】存在子角色,不允许删除");
|
||||||
ErrorCode ROLE_PARENT_IS_CHILD = new ErrorCode(1_002_002_008, "不能设置自己的子角色为父角色");
|
ErrorCode ROLE_PARENT_IS_CHILD = new ErrorCode(1_002_002_008, "不能设置自己的子角色为父角色");
|
||||||
|
|
||||||
|
|||||||
@@ -157,19 +157,26 @@ public class PermissionServiceImpl implements PermissionService {
|
|||||||
allEntries = true) // allEntries 清空所有缓存,主要一次更新涉及到的 menuIds 较多,反倒批量会更快
|
allEntries = true) // allEntries 清空所有缓存,主要一次更新涉及到的 menuIds 较多,反倒批量会更快
|
||||||
})
|
})
|
||||||
public void assignRoleMenu(Long roleId, Set<Long> menuIds) {
|
public void assignRoleMenu(Long roleId, Set<Long> menuIds) {
|
||||||
RoleDO role = roleService.getRole(roleId);
|
|
||||||
Set<Long> userRoleIdListByUserId = permissionService.getUserRoleIdListByUserId(getLoginUserId());
|
|
||||||
// 如果为标准角色,只允许管理员修改菜单权限
|
|
||||||
if (RoleTypeEnum.NORMAL.getType().equals(role.getType()) && !roleService.hasAnySuperAdmin(userRoleIdListByUserId)) {
|
|
||||||
throw exception(ROLE_CAN_NOT_UPDATE_NORMAL_TYPE_ROLE);
|
|
||||||
}
|
|
||||||
// 获得角色拥有菜单编号
|
// 获得角色拥有菜单编号
|
||||||
Set<Long> dbMenuIds = convertSet(roleMenuMapper.selectListByRoleId(roleId), RoleMenuDO::getMenuId);
|
Set<Long> dbMenuIds = convertSet(getRoleMenuListByRoleId(roleId));
|
||||||
|
// 获取父级角色拥有的菜单编号
|
||||||
|
Set<Long> parentRoleIds = roleService.getAllParentAndSelfRoleIds(singleton(roleId));
|
||||||
|
// 移除自身角色编号
|
||||||
|
parentRoleIds.remove(roleId);
|
||||||
|
Set<Long> dbInheritedMenuIds = convertSet(roleMenuMapper.selectListByRoleId(parentRoleIds), RoleMenuDO::getMenuId);
|
||||||
// 计算新增和删除的菜单编号
|
// 计算新增和删除的菜单编号
|
||||||
Set<Long> menuIdList = CollUtil.emptyIfNull(menuIds);
|
Set<Long> menuIdList = CollUtil.emptyIfNull(menuIds);
|
||||||
Collection<Long> createMenuIds = CollUtil.subtract(menuIdList, dbMenuIds);
|
Collection<Long> createMenuIds = CollUtil.subtract(menuIdList, dbMenuIds);
|
||||||
Collection<Long> deleteMenuIds = CollUtil.subtract(dbMenuIds, menuIdList);
|
Collection<Long> deleteMenuIds = CollUtil.subtract(dbMenuIds, menuIdList);
|
||||||
// 执行新增和删除。对于已经授权的菜单,不用做任何处理
|
// 执行新增和删除。对于已经授权的菜单,不用进行新增和删除,处理排除关系即可
|
||||||
|
if (CollUtil.isNotEmpty(createMenuIds)) {
|
||||||
|
Set<Long> inheritedCreateMenuIds = new HashSet<>(dbInheritedMenuIds);
|
||||||
|
inheritedCreateMenuIds.retainAll(createMenuIds);
|
||||||
|
if (CollUtil.isNotEmpty(inheritedCreateMenuIds)) {
|
||||||
|
// 不需要新增,只需要检查是否存在排除关系,如果存在,则标记排除关系失效
|
||||||
|
roleMenuExclusionMapper.deleteListByRoleIdAndMenuIds(roleId, inheritedCreateMenuIds);
|
||||||
|
createMenuIds.removeAll(inheritedCreateMenuIds);
|
||||||
|
}
|
||||||
if (CollUtil.isNotEmpty(createMenuIds)) {
|
if (CollUtil.isNotEmpty(createMenuIds)) {
|
||||||
roleMenuMapper.insertBatch(CollectionUtils.convertList(createMenuIds, menuId -> {
|
roleMenuMapper.insertBatch(CollectionUtils.convertList(createMenuIds, menuId -> {
|
||||||
RoleMenuDO entity = new RoleMenuDO();
|
RoleMenuDO entity = new RoleMenuDO();
|
||||||
@@ -178,10 +185,24 @@ public class PermissionServiceImpl implements PermissionService {
|
|||||||
return entity;
|
return entity;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (CollUtil.isNotEmpty(deleteMenuIds)) {
|
||||||
|
Set<Long> inheritedDeleteMenuIds = new HashSet<>(dbInheritedMenuIds);
|
||||||
|
inheritedDeleteMenuIds.retainAll(deleteMenuIds);
|
||||||
|
if (CollUtil.isNotEmpty(inheritedDeleteMenuIds)) {
|
||||||
|
// 标记排除
|
||||||
|
roleMenuExclusionMapper.insertBatch(CollectionUtils.convertList(inheritedDeleteMenuIds, menuId -> {
|
||||||
|
RoleMenuExclusionDO entity = new RoleMenuExclusionDO();
|
||||||
|
entity.setRoleId(roleId);
|
||||||
|
entity.setMenuId(menuId);
|
||||||
|
return entity;
|
||||||
|
}));
|
||||||
|
}
|
||||||
if (CollUtil.isNotEmpty(deleteMenuIds)) {
|
if (CollUtil.isNotEmpty(deleteMenuIds)) {
|
||||||
roleMenuMapper.deleteListByRoleIdAndMenuIds(roleId, deleteMenuIds);
|
roleMenuMapper.deleteListByRoleIdAndMenuIds(roleId, deleteMenuIds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
@@ -303,7 +324,7 @@ public class PermissionServiceImpl implements PermissionService {
|
|||||||
Set<Long> userRoleIdListByUserId = permissionService.getUserRoleIdListByUserId(getLoginUserId());
|
Set<Long> userRoleIdListByUserId = permissionService.getUserRoleIdListByUserId(getLoginUserId());
|
||||||
// 如果为标准角色,只允许管理员修改数据权限
|
// 如果为标准角色,只允许管理员修改数据权限
|
||||||
if (RoleTypeEnum.NORMAL.getType().equals(role.getType()) && !roleService.hasAnySuperAdmin(userRoleIdListByUserId)) {
|
if (RoleTypeEnum.NORMAL.getType().equals(role.getType()) && !roleService.hasAnySuperAdmin(userRoleIdListByUserId)) {
|
||||||
throw exception(ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE);
|
throw exception(ROLE_CAN_NOT_UPDATE_NORMAL_TYPE_ROLE);
|
||||||
}
|
}
|
||||||
roleService.updateRoleDataScope(roleId, dataScope, dataScopeDeptIds);
|
roleService.updateRoleDataScope(roleId, dataScope, dataScopeDeptIds);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -383,4 +383,29 @@ public class PermissionServiceTest extends BaseDbUnitTest {
|
|||||||
assertTrue(menuIds2.contains(101L));
|
assertTrue(menuIds2.contains(101L));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试子角色排除父角色菜单
|
||||||
|
* 通过 Service 方法排除,确保子角色不继承父角色的菜单
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testExcludeParentRoleMenu() {
|
||||||
|
// mock 父子关系 A->B
|
||||||
|
RoleDO parentRole = randomPojo(RoleDO.class, o -> o.setParentId(0L));
|
||||||
|
roleMapper.insert(parentRole);
|
||||||
|
RoleDO childRole = randomPojo(RoleDO.class, o -> o.setParentId(parentRole.getId()));
|
||||||
|
roleMapper.insert(childRole);
|
||||||
|
// 父角色分配菜单
|
||||||
|
RoleMenuDO parentMenu = randomPojo(RoleMenuDO.class).setRoleId(parentRole.getId()).setMenuId(101L);
|
||||||
|
roleMenuMapper.insert(parentMenu);
|
||||||
|
// 子角色排除父菜单(通过 Service 方法排除)
|
||||||
|
permissionService.assignRoleMenu(childRole.getId(), Collections.emptySet());
|
||||||
|
// 调用:获取子角色菜单(应不包含父菜单)
|
||||||
|
Set<Long> menuIds = permissionService.getRoleMenuListByRoleId(childRole.getId());
|
||||||
|
assertFalse(menuIds.contains(101L));
|
||||||
|
// 新增了子角色的排除菜单记录
|
||||||
|
List<RoleMenuExclusionDO> exclusionDOS = roleMenuExclusionMapper.selectMenuIdListByRoleId(Collections.singleton(childRole.getId()));
|
||||||
|
assertEquals(1, exclusionDOS.size());
|
||||||
|
assertEquals(101L, exclusionDOS.get(0).getMenuId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,26 @@
|
|||||||
<groupId>cn.iocoder.cloud</groupId>
|
<groupId>cn.iocoder.cloud</groupId>
|
||||||
<artifactId>yudao-common</artifactId>
|
<artifactId>yudao-common</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- Web 相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springdoc</groupId> <!-- 接口文档:使用最新版本的 Swagger 模型 -->
|
||||||
|
<artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 参数校验 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- RPC 远程调用相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user