diff --git a/rdms-project/rdms-project-boot/src/test/java/com/njcn/rdms/module/project/service/product/ProductMemberServiceImplTest.java b/rdms-project/rdms-project-boot/src/test/java/com/njcn/rdms/module/project/service/product/ProductMemberServiceImplTest.java new file mode 100644 index 0000000..7bdf617 --- /dev/null +++ b/rdms-project/rdms-project-boot/src/test/java/com/njcn/rdms/module/project/service/product/ProductMemberServiceImplTest.java @@ -0,0 +1,166 @@ +package com.njcn.rdms.module.project.service.product; + +import com.njcn.rdms.framework.common.exception.ServiceException; +import com.njcn.rdms.framework.test.core.ut.BaseMockitoUnitTest; +import com.njcn.rdms.module.project.controller.admin.product.vo.member.ProductMemberRespVO; +import com.njcn.rdms.module.project.controller.admin.product.vo.member.ProductMemberSaveReqVO; +import com.njcn.rdms.module.project.controller.admin.product.vo.member.ProductMemberUpdateReqVO; +import com.njcn.rdms.module.project.dal.dataobject.audit.BizAuditLogDO; +import com.njcn.rdms.module.project.dal.dataobject.member.UserObjectRoleDO; +import com.njcn.rdms.module.project.dal.dataobject.product.ProductDO; +import com.njcn.rdms.module.project.dal.mysql.audit.BizAuditLogMapper; +import com.njcn.rdms.module.project.dal.mysql.member.UserObjectRoleMapper; +import com.njcn.rdms.module.project.dal.mysql.product.ProductMapper; +import com.njcn.rdms.module.project.enums.ErrorCodeConstants; +import com.njcn.rdms.module.system.api.permission.ObjectPermissionApi; +import com.njcn.rdms.module.system.api.permission.dto.ObjectRoleRespDTO; +import com.njcn.rdms.module.system.api.user.AdminUserApi; +import com.njcn.rdms.module.system.api.user.dto.AdminUserRespDTO; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.njcn.rdms.framework.common.pojo.CommonResult.success; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class ProductMemberServiceImplTest extends BaseMockitoUnitTest { + + @InjectMocks + private ProductMemberServiceImpl productMemberService; + @Mock + private ProductMapper productMapper; + @Mock + private UserObjectRoleMapper userObjectRoleMapper; + @Mock + private ObjectPermissionApi objectPermissionApi; + @Mock + private BizAuditLogMapper bizAuditLogMapper; + @Mock + private AdminUserApi adminUserApi; + + @Test + void getProductMemberList_shouldFillRoleNameAndRoleCodeFromObjectPermissionApi() { + Long productId = 1001L; + ProductDO product = createProduct(productId, 2001L); + UserObjectRoleDO manager = createMember(9001L, productId, 2001L, 3101L, 0); + UserObjectRoleDO member = createMember(9002L, productId, 2002L, 3102L, 0); + when(productMapper.selectById(productId)).thenReturn(product); + when(userObjectRoleMapper.selectListByObject("product", productId)).thenReturn(List.of(manager, member)); + when(objectPermissionApi.getObjectRoleList(Set.of(3101L, 3102L), "object", "product")) + .thenReturn(success(List.of( + createRole(3101L, "product_manager", "产品经理"), + createRole(3102L, "product_member", "产品成员") + ))); + when(adminUserApi.getUserMap(Set.of(2001L, 2002L))).thenReturn(Map.of( + 2001L, createUser("经理甲"), + 2002L, createUser("成员乙") + )); + + List respVOList = productMemberService.getProductMemberList(productId); + + assertEquals(2, respVOList.size()); + assertEquals("产品经理", respVOList.get(0).getRoleName()); + assertEquals("product_manager", respVOList.get(0).getRoleCode()); + assertEquals(Boolean.TRUE, respVOList.get(0).getManagerFlag()); + assertEquals("产品成员", respVOList.get(1).getRoleName()); + assertEquals("product_member", respVOList.get(1).getRoleCode()); + assertFalse(respVOList.get(1).getManagerFlag()); + } + + @Test + void createProductMember_whenRoleSummaryMissing_shouldThrowRoleInvalid() { + Long productId = 1002L; + ProductMemberSaveReqVO reqVO = new ProductMemberSaveReqVO(); + reqVO.setUserId(2003L); + reqVO.setRoleId(3999L); + when(productMapper.selectById(productId)).thenReturn(createProduct(productId, 2001L)); + when(objectPermissionApi.getObjectRoleById(3999L, "object", "product")).thenReturn(success(null)); + + ServiceException ex = assertThrows(ServiceException.class, + () -> productMemberService.createProductMember(productId, reqVO)); + + assertEquals(ErrorCodeConstants.PRODUCT_MEMBER_ROLE_INVALID.getCode(), ex.getCode()); + verify(userObjectRoleMapper, never()).selectByObjectAndUserId(any(), any(), any()); + } + + @Test + void updateProductMember_whenPreviousManagerRoleCodeStillManager_shouldThrowException() { + Long productId = 1003L; + Long memberId = 9003L; + Long currentManagerUserId = 2001L; + Long targetManagerRoleId = 3201L; + Long previousManagerRoleId = 3202L; + ProductMemberUpdateReqVO reqVO = new ProductMemberUpdateReqVO(); + reqVO.setRoleId(targetManagerRoleId); + reqVO.setPreviousManagerUserId(currentManagerUserId); + reqVO.setPreviousManagerRoleId(previousManagerRoleId); + reqVO.setReason("角色交接"); + + ProductDO product = createProduct(productId, currentManagerUserId); + UserObjectRoleDO member = createMember(memberId, productId, 2002L, 3203L, 0); + when(productMapper.selectById(productId)).thenReturn(product); + when(userObjectRoleMapper.selectByIdAndObject(memberId, "product", productId)).thenReturn(member); + when(objectPermissionApi.getObjectRoleById(targetManagerRoleId, "object", "product")) + .thenReturn(success(createRole(targetManagerRoleId, "product_manager", "产品经理"))); + when(objectPermissionApi.getObjectRoleById(previousManagerRoleId, "object", "product")) + .thenReturn(success(createRole(previousManagerRoleId, "product_manager", "产品经理"))); + + ServiceException ex = assertThrows(ServiceException.class, + () -> productMemberService.updateProductMember(productId, memberId, reqVO)); + + assertEquals(ErrorCodeConstants.PRODUCT_MANAGER_TRANSFER_ROLE_INVALID.getCode(), ex.getCode()); + verify(userObjectRoleMapper).updateById(member); + verify(productMapper, never()).updateById(any(ProductDO.class)); + verify(bizAuditLogMapper, never()).insert(any(BizAuditLogDO.class)); + } + + private ProductDO createProduct(Long productId, Long managerUserId) { + ProductDO product = new ProductDO(); + product.setId(productId); + product.setManagerUserId(managerUserId); + product.setStatusCode("active"); + product.setName("测试产品"); + product.setCode("CNPD2026001"); + return product; + } + + private UserObjectRoleDO createMember(Long memberId, Long productId, Long userId, Long roleId, Integer status) { + UserObjectRoleDO member = new UserObjectRoleDO(); + member.setId(memberId); + member.setObjectType("product"); + member.setObjectId(productId); + member.setUserId(userId); + member.setRoleId(roleId); + member.setStatus(status); + member.setJoinedTime(LocalDateTime.now()); + return member; + } + + private ObjectRoleRespDTO createRole(Long roleId, String roleCode, String roleName) { + ObjectRoleRespDTO role = new ObjectRoleRespDTO(); + role.setId(roleId); + role.setCode(roleCode); + role.setName(roleName); + role.setScopeType("object"); + role.setObjectType("product"); + return role; + } + + private AdminUserRespDTO createUser(String nickname) { + AdminUserRespDTO user = new AdminUserRespDTO(); + user.setNickname(nickname); + return user; + } + +} diff --git a/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/api/permission/ObjectPermissionApi.java b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/api/permission/ObjectPermissionApi.java new file mode 100644 index 0000000..8d87646 --- /dev/null +++ b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/api/permission/ObjectPermissionApi.java @@ -0,0 +1,89 @@ +package com.njcn.rdms.module.system.api.permission; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import com.njcn.rdms.framework.common.pojo.CommonResult; +import com.njcn.rdms.framework.common.util.collection.CollectionUtils; +import com.njcn.rdms.module.system.api.permission.dto.ObjectRolePermissionRespDTO; +import com.njcn.rdms.module.system.api.permission.dto.ObjectRoleRespDTO; +import com.njcn.rdms.module.system.enums.ApiConstants; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@FeignClient(name = ApiConstants.NAME) +@Tag(name = "RPC 服务 - 对象权限") +public interface ObjectPermissionApi { + + String PREFIX = ApiConstants.PREFIX + "/permission/object"; + + @GetMapping(PREFIX + "/role-by-id") + @Operation(summary = "按角色 ID 查询对象作用域角色摘要") + @Parameter(name = "roleId", description = "角色 ID", example = "1024", required = true) + @Parameter(name = "scopeType", description = "权限作用域类型", example = "object", required = true) + @Parameter(name = "objectType", description = "对象类型", example = "product", required = true) + CommonResult getObjectRoleById(@RequestParam("roleId") Long roleId, + @RequestParam("scopeType") String scopeType, + @RequestParam("objectType") String objectType); + + @GetMapping(PREFIX + "/role-by-code") + @Operation(summary = "按角色编码查询对象作用域角色摘要") + @Parameter(name = "roleCode", description = "角色编码", example = "product_manager", required = true) + @Parameter(name = "scopeType", description = "权限作用域类型", example = "object", required = true) + @Parameter(name = "objectType", description = "对象类型", example = "product", required = true) + CommonResult getObjectRoleByCode(@RequestParam("roleCode") String roleCode, + @RequestParam("scopeType") String scopeType, + @RequestParam("objectType") String objectType); + + @GetMapping(PREFIX + "/role-list") + @Operation(summary = "按角色 ID 批量查询对象作用域角色摘要") + @Parameter(name = "roleIds", description = "角色 ID 集合", example = "1,2", required = true) + @Parameter(name = "scopeType", description = "权限作用域类型", example = "object", required = true) + @Parameter(name = "objectType", description = "对象类型", example = "product", required = true) + CommonResult> getObjectRoleList(@RequestParam("roleIds") Collection roleIds, + @RequestParam("scopeType") String scopeType, + @RequestParam("objectType") String objectType); + + @GetMapping(PREFIX + "/permissions") + @Operation(summary = "查询对象作用域角色权限集合") + @Parameter(name = "roleId", description = "角色 ID", example = "1024", required = true) + @Parameter(name = "scopeType", description = "权限作用域类型", example = "object", required = true) + @Parameter(name = "objectType", description = "对象类型", example = "product", required = true) + CommonResult> getObjectRolePermissions(@RequestParam("roleId") Long roleId, + @RequestParam("scopeType") String scopeType, + @RequestParam("objectType") String objectType); + + @GetMapping(PREFIX + "/role-permission-detail") + @Operation(summary = "查询对象作用域角色菜单与权限聚合结果") + @Parameter(name = "roleId", description = "角色 ID", example = "1024", required = true) + @Parameter(name = "scopeType", description = "权限作用域类型", example = "object", required = true) + @Parameter(name = "objectType", description = "对象类型", example = "product", required = true) + CommonResult getObjectRolePermissionDetail(@RequestParam("roleId") Long roleId, + @RequestParam("scopeType") String scopeType, + @RequestParam("objectType") String objectType); + + /** + * 按角色 ID 返回对象作用域角色摘要映射,便于业务模块批量对齐本地成员数据。 + * + * @param roleIds 角色 ID 集合 + * @param scopeType 权限作用域类型 + * @param objectType 对象类型 + * @return 角色摘要映射 + */ + default Map getObjectRoleMap(Collection roleIds, String scopeType, String objectType) { + if (CollUtil.isEmpty(roleIds)) { + return MapUtil.empty(); + } + List roles = getObjectRoleList(roleIds, scopeType, objectType).getCheckedData(); + return CollectionUtils.convertMap(roles, ObjectRoleRespDTO::getId); + } + +} diff --git a/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/api/permission/dto/ObjectMenuRespDTO.java b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/api/permission/dto/ObjectMenuRespDTO.java new file mode 100644 index 0000000..f55515c --- /dev/null +++ b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/api/permission/dto/ObjectMenuRespDTO.java @@ -0,0 +1,34 @@ +package com.njcn.rdms.module.system.api.permission.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "RPC 服务 - 对象作用域菜单 Response DTO") +@Data +public class ObjectMenuRespDTO { + + @Schema(description = "菜单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2001") + private Long id; + + @Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品设置") + private String name; + + @Schema(description = "权限标识", example = "project:product:update") + private String permission; + + @Schema(description = "菜单类型;允许返回目录、菜单、按钮,业务侧可按 type 再拆分导航或按钮", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer type; + + @Schema(description = "显示排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer sort; + + @Schema(description = "路由路径", example = "/project/product/detail") + private String path; + + @Schema(description = "菜单图标", example = "ep:box") + private String icon; + + @Schema(description = "是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean visible; + +} diff --git a/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/api/permission/dto/ObjectRolePermissionRespDTO.java b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/api/permission/dto/ObjectRolePermissionRespDTO.java new file mode 100644 index 0000000..3738c13 --- /dev/null +++ b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/api/permission/dto/ObjectRolePermissionRespDTO.java @@ -0,0 +1,23 @@ +package com.njcn.rdms.module.system.api.permission.dto; + +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; +import java.util.Set; + +@Schema(description = "RPC 服务 - 对象作用域角色权限聚合 Response DTO") +@Data +public class ObjectRolePermissionRespDTO { + + @Schema(description = "当前查询 roleId 对应的角色摘要,不表示登录态上下文里的当前用户角色") + private ObjectRoleRespDTO currentRole; + + @ArraySchema(schema = @Schema(description = "当前查询角色在指定 scopeType/objectType 下可消费的已启用菜单资源明细;允许包含目录、菜单、按钮,业务侧可按 type 再拆分导航或按钮")) + private List menus; + + @ArraySchema(schema = @Schema(description = "基于同一批有效菜单资源归一化提取出的权限标识集合,供对象权限校验直接消费", example = "project:product:query")) + private Set permissions; + +} diff --git a/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/api/permission/dto/ObjectRoleRespDTO.java b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/api/permission/dto/ObjectRoleRespDTO.java new file mode 100644 index 0000000..5f50d0c --- /dev/null +++ b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/api/permission/dto/ObjectRoleRespDTO.java @@ -0,0 +1,25 @@ +package com.njcn.rdms.module.system.api.permission.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "RPC 服务 - 对象作用域角色摘要 Response DTO") +@Data +public class ObjectRoleRespDTO { + + @Schema(description = "角色 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "角色编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "product_manager") + private String code; + + @Schema(description = "角色名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品经理") + private String name; + + @Schema(description = "权限作用域类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "object") + private String scopeType; + + @Schema(description = "对象类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "product") + private String objectType; + +} diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/api/permission/ObjectPermissionApiImpl.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/api/permission/ObjectPermissionApiImpl.java new file mode 100644 index 0000000..b926f57 --- /dev/null +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/api/permission/ObjectPermissionApiImpl.java @@ -0,0 +1,122 @@ +package com.njcn.rdms.module.system.api.permission; + +import com.njcn.rdms.framework.common.enums.CommonStatusEnum; +import com.njcn.rdms.framework.common.pojo.CommonResult; +import com.njcn.rdms.module.system.api.permission.dto.ObjectMenuRespDTO; +import com.njcn.rdms.module.system.api.permission.dto.ObjectRolePermissionRespDTO; +import com.njcn.rdms.module.system.api.permission.dto.ObjectRoleRespDTO; +import com.njcn.rdms.module.system.dal.dataobject.permission.MenuDO; +import com.njcn.rdms.module.system.dal.dataobject.permission.RoleDO; +import com.njcn.rdms.module.system.service.permission.PermissionService; +import com.njcn.rdms.module.system.service.permission.RoleService; +import io.swagger.v3.oas.annotations.Hidden; +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Primary; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import static com.njcn.rdms.framework.common.pojo.CommonResult.success; + +@RestController +@Validated +@Primary +@Hidden +public class ObjectPermissionApiImpl implements ObjectPermissionApi { + + @Resource + private RoleService roleService; + @Resource + private PermissionService permissionService; + + @Override + public CommonResult getObjectRoleById(Long roleId, String scopeType, String objectType) { + return success(convertRole(roleService.getRole(roleId, scopeType, objectType))); + } + + @Override + public CommonResult getObjectRoleByCode(String roleCode, String scopeType, String objectType) { + return success(convertRole(roleService.getRoleByCode(roleCode, scopeType, objectType))); + } + + @Override + public CommonResult> getObjectRoleList(Collection roleIds, String scopeType, String objectType) { + List roles = roleService.getRoleList(roleIds, scopeType, objectType); + return success(roles.stream().map(this::convertRole).toList()); + } + + @Override + public CommonResult> getObjectRolePermissions(Long roleId, String scopeType, String objectType) { + RoleDO role = getEnabledScopedRole(roleId, scopeType, objectType); + if (role == null) { + return success(Collections.emptySet()); + } + return success(new LinkedHashSet<>(permissionService.getScopedPermissionsByRoleId(roleId, scopeType, objectType))); + } + + @Override + public CommonResult getObjectRolePermissionDetail(Long roleId, String scopeType, String objectType) { + RoleDO role = getEnabledScopedRole(roleId, scopeType, objectType); + if (role == null) { + return success(emptyPermissionDetail()); + } + + ObjectRolePermissionRespDTO detail = new ObjectRolePermissionRespDTO(); + detail.setCurrentRole(convertRole(role)); + detail.setMenus(permissionService.getScopedMenusByRoleId(roleId, scopeType, objectType) + .stream() + .map(this::convertMenu) + .toList()); + detail.setPermissions(new LinkedHashSet<>( + permissionService.getScopedPermissionsByRoleId(roleId, scopeType, objectType))); + return success(detail); + } + + private ObjectRolePermissionRespDTO emptyPermissionDetail() { + ObjectRolePermissionRespDTO detail = new ObjectRolePermissionRespDTO(); + detail.setCurrentRole(null); + detail.setMenus(Collections.emptyList()); + detail.setPermissions(Collections.emptySet()); + return detail; + } + + private RoleDO getEnabledScopedRole(Long roleId, String scopeType, String objectType) { + RoleDO role = roleService.getRole(roleId, scopeType, objectType); + if (role == null || !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())) { + return null; + } + return role; + } + + private ObjectRoleRespDTO convertRole(RoleDO role) { + if (role == null) { + return null; + } + ObjectRoleRespDTO dto = new ObjectRoleRespDTO(); + dto.setId(role.getId()); + dto.setCode(role.getCode()); + dto.setName(role.getName()); + dto.setScopeType(role.getScopeType()); + dto.setObjectType(role.getObjectType()); + return dto; + } + + private ObjectMenuRespDTO convertMenu(MenuDO menu) { + ObjectMenuRespDTO dto = new ObjectMenuRespDTO(); + dto.setId(menu.getId()); + dto.setName(menu.getName()); + dto.setPermission(menu.getPermission()); + dto.setType(menu.getType()); + dto.setSort(menu.getSort()); + dto.setPath(menu.getPath()); + dto.setIcon(menu.getIcon()); + dto.setVisible(menu.getVisible()); + return dto; + } + +} diff --git a/rdms-system/rdms-system-boot/src/test/java/com/njcn/rdms/module/system/api/permission/ObjectPermissionApiImplTest.java b/rdms-system/rdms-system-boot/src/test/java/com/njcn/rdms/module/system/api/permission/ObjectPermissionApiImplTest.java new file mode 100644 index 0000000..fc039ea --- /dev/null +++ b/rdms-system/rdms-system-boot/src/test/java/com/njcn/rdms/module/system/api/permission/ObjectPermissionApiImplTest.java @@ -0,0 +1,465 @@ +package com.njcn.rdms.module.system.api.permission; + +import com.njcn.rdms.framework.common.enums.CommonStatusEnum; +import com.njcn.rdms.framework.common.pojo.CommonResult; +import com.njcn.rdms.framework.test.core.ut.BaseMockitoUnitTest; +import com.njcn.rdms.module.system.api.permission.dto.ObjectMenuRespDTO; +import com.njcn.rdms.module.system.api.permission.dto.ObjectRolePermissionRespDTO; +import com.njcn.rdms.module.system.api.permission.dto.ObjectRoleRespDTO; +import com.njcn.rdms.module.system.dal.dataobject.permission.MenuDO; +import com.njcn.rdms.module.system.dal.dataobject.permission.RoleDO; +import com.njcn.rdms.module.system.dal.dataobject.permission.RoleMenuDO; +import com.njcn.rdms.module.system.dal.mysql.permission.MenuMapper; +import com.njcn.rdms.module.system.dal.mysql.permission.RoleMapper; +import com.njcn.rdms.module.system.dal.mysql.permission.RoleMenuMapper; +import com.njcn.rdms.module.system.dal.mysql.permission.UserRoleMapper; +import com.njcn.rdms.module.system.service.permission.MenuServiceImpl; +import com.njcn.rdms.module.system.service.permission.PermissionService; +import com.njcn.rdms.module.system.service.permission.PermissionServiceImpl; +import com.njcn.rdms.module.system.service.permission.RoleService; +import com.njcn.rdms.module.system.service.permission.RoleServiceImpl; +import com.njcn.rdms.module.system.service.user.AdminUserService; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +class ObjectPermissionApiImplTest extends BaseMockitoUnitTest { + + private static final String OBJECT_SCOPE = "object"; + private static final String PRODUCT_OBJECT = "product"; + private static final String GLOBAL_SCOPE = "global"; + private static final String GLOBAL_OBJECT = ""; + + @Mock + private RoleService roleService; + @Mock + private PermissionService permissionService; + @Mock + private RoleMapper roleMapper; + @Mock + private RoleMenuMapper roleMenuMapper; + @Mock + private MenuMapper menuMapper; + @Mock + private UserRoleMapper userRoleMapper; + @Mock + private AdminUserService userService; + + @InjectMocks + private ObjectPermissionApiImpl objectPermissionApi; + + @Test + void getObjectRoleById_whenRoleExistsAndEnabled_thenReturnRoleSummary() { + RoleDO role = buildRole(101L, "qa_owner", "QA Owner", OBJECT_SCOPE, PRODUCT_OBJECT); + role.setStatus(CommonStatusEnum.ENABLE.getStatus()); + when(roleService.getRole(101L, OBJECT_SCOPE, PRODUCT_OBJECT)).thenReturn(role); + + CommonResult result = + objectPermissionApi.getObjectRoleById(101L, OBJECT_SCOPE, PRODUCT_OBJECT); + + ObjectRoleRespDTO data = result.getCheckedData(); + assertNotNull(data); + assertEquals(101L, data.getId()); + assertEquals("qa_owner", data.getCode()); + assertEquals("QA Owner", data.getName()); + assertEquals(OBJECT_SCOPE, data.getScopeType()); + assertEquals(PRODUCT_OBJECT, data.getObjectType()); + } + + @Test + void getObjectRoleById_whenRoleMissing_thenReturnNull() { + when(roleService.getRole(404L, OBJECT_SCOPE, PRODUCT_OBJECT)).thenReturn(null); + + CommonResult result = + objectPermissionApi.getObjectRoleById(404L, OBJECT_SCOPE, PRODUCT_OBJECT); + + assertNull(result.getCheckedData()); + } + + @Test + void getObjectRoleByCode_whenRoleExists_thenReturnProductManagerRole() { + RoleDO role = buildRole(102L, "product_manager", "Product Manager", OBJECT_SCOPE, PRODUCT_OBJECT); + when(roleService.getRoleByCode("product_manager", OBJECT_SCOPE, PRODUCT_OBJECT)).thenReturn(role); + + CommonResult result = + objectPermissionApi.getObjectRoleByCode("product_manager", OBJECT_SCOPE, PRODUCT_OBJECT); + + ObjectRoleRespDTO data = result.getCheckedData(); + assertNotNull(data); + assertEquals(102L, data.getId()); + assertEquals("product_manager", data.getCode()); + assertEquals("Product Manager", data.getName()); + } + + @Test + void getObjectRoleList_shouldFilterScopedRolesThroughRoleServiceImpl() { + RoleServiceImpl realRoleService = createRoleServiceImpl(); + ObjectPermissionApiImpl realApi = createObjectPermissionApi(realRoleService, permissionService); + Map roleStore = new LinkedHashMap<>(); + roleStore.put(201L, buildRole(201L, "product_manager", "Product Manager", OBJECT_SCOPE, PRODUCT_OBJECT)); + roleStore.put(202L, buildRole(202L, "global_admin", "Global Admin", GLOBAL_SCOPE, GLOBAL_OBJECT)); + roleStore.put(203L, buildRole(203L, "project_manager", "Project Manager", OBJECT_SCOPE, "project")); + roleStore.put(204L, buildRole(204L, "product_tester", "Product Tester", OBJECT_SCOPE, PRODUCT_OBJECT)); + when(roleMapper.selectByIds(any())).thenAnswer(invocation -> selectValues(roleStore, invocation.getArgument(0))); + + CommonResult> result = + realApi.getObjectRoleList(List.of(201L, 202L, 203L, 204L), OBJECT_SCOPE, PRODUCT_OBJECT); + + List data = result.getCheckedData(); + assertEquals(List.of(201L, 204L), data.stream().map(ObjectRoleRespDTO::getId).toList()); + assertEquals(List.of("product_manager", "product_tester"), + data.stream().map(ObjectRoleRespDTO::getCode).toList()); + } + + @Test + void getObjectRolePermissions_whenRoleMissing_thenReturnEmptySet() { + when(roleService.getRole(303L, OBJECT_SCOPE, PRODUCT_OBJECT)).thenReturn(null); + + CommonResult> result = + objectPermissionApi.getObjectRolePermissions(303L, OBJECT_SCOPE, PRODUCT_OBJECT); + + assertTrue(result.getCheckedData().isEmpty()); + verifyNoInteractions(permissionService); + } + + @Test + void getObjectRolePermissions_whenRoleDisabled_thenReturnEmptySet() { + RoleDO role = buildRole(304L, "product_manager", "Product Manager", OBJECT_SCOPE, PRODUCT_OBJECT); + role.setStatus(CommonStatusEnum.DISABLE.getStatus()); + when(roleService.getRole(304L, OBJECT_SCOPE, PRODUCT_OBJECT)).thenReturn(role); + + CommonResult> result = + objectPermissionApi.getObjectRolePermissions(304L, OBJECT_SCOPE, PRODUCT_OBJECT); + + assertTrue(result.getCheckedData().isEmpty()); + verifyNoInteractions(permissionService); + } + + @Test + void permissionServiceImpl_getScopedPermissionsByRoleId_shouldExtractButtonPermissionFromEffectiveMenus() { + MenuServiceImpl realMenuService = createMenuServiceImpl(); + PermissionServiceImpl realPermissionService = createPermissionServiceImpl(roleService, realMenuService); + RoleDO role = buildRole(301L, "product_manager", "Product Manager", OBJECT_SCOPE, PRODUCT_OBJECT); + MenuDO menu = buildMenu(3011L, "Product Setting", null, 2, 10, "/product/setting", + MenuDO.ID_ROOT, CommonStatusEnum.ENABLE.getStatus()); + MenuDO button = buildMenu(3012L, "Save", "project:product:update", 3, 20, null, + 3011L, CommonStatusEnum.ENABLE.getStatus()); + Map menuStore = menuStore(menu, button); + when(roleService.getRole(301L, OBJECT_SCOPE, PRODUCT_OBJECT)).thenReturn(role); + when(roleService.getRoleList(Collections.singleton(301L), OBJECT_SCOPE, PRODUCT_OBJECT)) + .thenReturn(List.of(role)); + when(roleMenuMapper.selectListByRoleId(Collections.singleton(301L))) + .thenReturn(List.of(buildRoleMenu(301L, 3011L), buildRoleMenu(301L, 3012L))); + when(menuMapper.selectByIds(any())).thenAnswer(invocation -> selectValues(menuStore, invocation.getArgument(0))); + + Set permissions = realPermissionService.getScopedPermissionsByRoleId(301L, OBJECT_SCOPE, PRODUCT_OBJECT); + + assertEquals(Set.of("project:product:update"), permissions); + } + + @Test + void permissionServiceImpl_getScopedMenusAndPermissionsByRoleId_shouldReturnEmptyResultsWhenRoleIsDisabled() { + MenuServiceImpl realMenuService = createMenuServiceImpl(); + PermissionServiceImpl realPermissionService = createPermissionServiceImpl(roleService, realMenuService); + RoleDO disabledRole = buildRole(305L, "product_manager", "Product Manager", OBJECT_SCOPE, PRODUCT_OBJECT); + disabledRole.setStatus(CommonStatusEnum.DISABLE.getStatus()); + when(roleService.getRole(305L, OBJECT_SCOPE, PRODUCT_OBJECT)).thenReturn(disabledRole); + + List menus = realPermissionService.getScopedMenusByRoleId(305L, OBJECT_SCOPE, PRODUCT_OBJECT); + Set permissions = realPermissionService.getScopedPermissionsByRoleId(305L, OBJECT_SCOPE, PRODUCT_OBJECT); + + assertTrue(menus.isEmpty()); + assertTrue(permissions.isEmpty()); + verifyNoInteractions(roleMenuMapper, menuMapper); + } + + @Test + void permissionServiceImpl_getScopedMenusAndPermissionsByRoleId_shouldIgnoreMenusOutsideRequestedScopeAndObject() { + MenuServiceImpl realMenuService = createMenuServiceImpl(); + PermissionServiceImpl realPermissionService = createPermissionServiceImpl(roleService, realMenuService); + RoleDO role = buildRole(3051L, "product_manager", "Product Manager", OBJECT_SCOPE, PRODUCT_OBJECT); + MenuDO directory = buildMenu(30511L, "Product", null, 1, 1, "/product", + MenuDO.ID_ROOT, CommonStatusEnum.ENABLE.getStatus()); + MenuDO scopedButton = buildMenu(30512L, "Save", "project:product:update", 3, 10, null, + 30511L, CommonStatusEnum.ENABLE.getStatus()); + MenuDO globalButton = buildMenu(30513L, "Global Export", "system:global:export", 3, 20, null, + MenuDO.ID_ROOT, CommonStatusEnum.ENABLE.getStatus(), GLOBAL_SCOPE, GLOBAL_OBJECT); + MenuDO otherObjectButton = buildMenu(30514L, "Project Publish", "project:project:publish", 3, 30, null, + MenuDO.ID_ROOT, CommonStatusEnum.ENABLE.getStatus(), OBJECT_SCOPE, "project"); + Map menuStore = menuStore(directory, scopedButton, globalButton, otherObjectButton); + when(roleService.getRole(3051L, OBJECT_SCOPE, PRODUCT_OBJECT)).thenReturn(role); + when(roleService.getRoleList(Collections.singleton(3051L), OBJECT_SCOPE, PRODUCT_OBJECT)) + .thenReturn(List.of(role)); + when(roleMenuMapper.selectListByRoleId(Collections.singleton(3051L))) + .thenReturn(List.of(buildRoleMenu(3051L, 30511L), buildRoleMenu(3051L, 30512L), + buildRoleMenu(3051L, 30513L), + buildRoleMenu(3051L, 30514L))); + when(menuMapper.selectByIds(any())).thenAnswer(invocation -> selectValues(menuStore, invocation.getArgument(0))); + + List menus = realPermissionService.getScopedMenusByRoleId(3051L, OBJECT_SCOPE, PRODUCT_OBJECT); + Set permissions = realPermissionService.getScopedPermissionsByRoleId(3051L, OBJECT_SCOPE, PRODUCT_OBJECT); + + assertEquals(Set.of(30511L, 30512L), + menus.stream().map(MenuDO::getId).collect(LinkedHashSet::new, Set::add, Set::addAll)); + assertEquals(Set.of("project:product:update"), permissions); + } + + @Test + void permissionServiceImpl_getRoleMenuListByRoleIdScopedCollection_shouldExcludeDisabledRoles() { + MenuServiceImpl realMenuService = createMenuServiceImpl(); + PermissionServiceImpl realPermissionService = createPermissionServiceImpl(roleService, realMenuService); + RoleDO enabledRole = buildRole(306L, "product_manager", "Product Manager", OBJECT_SCOPE, PRODUCT_OBJECT); + RoleDO disabledRole = buildRole(307L, "product_viewer", "Product Viewer", OBJECT_SCOPE, PRODUCT_OBJECT); + disabledRole.setStatus(CommonStatusEnum.DISABLE.getStatus()); + MenuDO enabledMenu = buildMenu(3061L, "Enabled Menu", null, 2, 10, "/product/enabled", + MenuDO.ID_ROOT, CommonStatusEnum.ENABLE.getStatus()); + MenuDO disabledRoleMenu = buildMenu(3071L, "Disabled Role Menu", null, 2, 20, "/product/disabled-role", + MenuDO.ID_ROOT, CommonStatusEnum.ENABLE.getStatus()); + Map menuStore = menuStore(enabledMenu, disabledRoleMenu); + when(roleService.getRoleList(List.of(306L, 307L), OBJECT_SCOPE, PRODUCT_OBJECT)) + .thenReturn(List.of(enabledRole, disabledRole)); + when(roleMenuMapper.selectListByRoleId(org.mockito.ArgumentMatchers.>any())).thenAnswer(invocation -> { + Collection roleIds = invocation.getArgument(0); + List results = new ArrayList<>(); + if (roleIds.contains(306L)) { + results.add(buildRoleMenu(306L, 3061L)); + } + if (roleIds.contains(307L)) { + results.add(buildRoleMenu(307L, 3071L)); + } + return results; + }); + when(menuMapper.selectByIds(any())).thenAnswer(invocation -> selectValues(menuStore, invocation.getArgument(0))); + + Set menuIds = realPermissionService.getRoleMenuListByRoleId(List.of(306L, 307L), OBJECT_SCOPE, PRODUCT_OBJECT); + + assertEquals(Set.of(3061L), menuIds); + } + + @Test + void getObjectRolePermissionDetail_whenRoleMissing_thenReturnEmptyDetail() { + when(roleService.getRole(401L, OBJECT_SCOPE, PRODUCT_OBJECT)).thenReturn(null); + + CommonResult result = + objectPermissionApi.getObjectRolePermissionDetail(401L, OBJECT_SCOPE, PRODUCT_OBJECT); + + ObjectRolePermissionRespDTO detail = result.getCheckedData(); + assertNotNull(detail); + assertNull(detail.getCurrentRole()); + assertTrue(detail.getMenus().isEmpty()); + assertTrue(detail.getPermissions().isEmpty()); + verifyNoInteractions(permissionService); + } + + @Test + void getObjectRolePermissionDetail_whenRoleDisabled_thenReturnEmptyDetail() { + RoleDO role = buildRole(405L, "product_manager", "Product Manager", OBJECT_SCOPE, PRODUCT_OBJECT); + role.setStatus(CommonStatusEnum.DISABLE.getStatus()); + when(roleService.getRole(405L, OBJECT_SCOPE, PRODUCT_OBJECT)).thenReturn(role); + + CommonResult result = + objectPermissionApi.getObjectRolePermissionDetail(405L, OBJECT_SCOPE, PRODUCT_OBJECT); + + ObjectRolePermissionRespDTO detail = result.getCheckedData(); + assertNotNull(detail); + assertNull(detail.getCurrentRole()); + assertTrue(detail.getMenus().isEmpty()); + assertTrue(detail.getPermissions().isEmpty()); + verifyNoInteractions(permissionService); + } + + @Test + void getObjectRolePermissionDetail_shouldAggregateRoleMenusAndPermissions() { + RoleDO role = buildRole(402L, "product_manager", "Product Manager", OBJECT_SCOPE, PRODUCT_OBJECT); + MenuDO directory = buildMenu(11L, "Product", null, 1, 1, "/product", + MenuDO.ID_ROOT, CommonStatusEnum.ENABLE.getStatus()); + MenuDO menu = buildMenu(12L, "Product Setting", null, 2, 10, "/product/setting", + 11L, CommonStatusEnum.ENABLE.getStatus()); + MenuDO button = buildMenu(13L, "Save", "project:product:update", 3, 20, null, + 12L, CommonStatusEnum.ENABLE.getStatus()); + Set permissions = new LinkedHashSet<>(Set.of("project:product:update")); + when(roleService.getRole(402L, OBJECT_SCOPE, PRODUCT_OBJECT)).thenReturn(role); + when(permissionService.getScopedMenusByRoleId(402L, OBJECT_SCOPE, PRODUCT_OBJECT)) + .thenReturn(List.of(directory, menu, button)); + when(permissionService.getScopedPermissionsByRoleId(402L, OBJECT_SCOPE, PRODUCT_OBJECT)) + .thenReturn(permissions); + + CommonResult result = + objectPermissionApi.getObjectRolePermissionDetail(402L, OBJECT_SCOPE, PRODUCT_OBJECT); + + ObjectRolePermissionRespDTO detail = result.getCheckedData(); + assertNotNull(detail.getCurrentRole()); + assertEquals(3, detail.getMenus().size()); + assertEquals(List.of(11L, 12L, 13L), detail.getMenus().stream().map(ObjectMenuRespDTO::getId).toList()); + assertEquals(permissions, detail.getPermissions()); + } + + @Test + void permissionServiceImpl_getScopedMenusByRoleId_shouldExcludeDisabledMenus() { + MenuServiceImpl realMenuService = createMenuServiceImpl(); + PermissionServiceImpl realPermissionService = createPermissionServiceImpl(roleService, realMenuService); + RoleDO role = buildRole(403L, "product_manager", "Product Manager", OBJECT_SCOPE, PRODUCT_OBJECT); + MenuDO enabledMenu = buildMenu(21L, "Enabled Menu", null, 2, 10, "/product/setting", + MenuDO.ID_ROOT, CommonStatusEnum.ENABLE.getStatus()); + MenuDO disabledMenu = buildMenu(22L, "Disabled Menu", null, 2, 20, "/product/disabled", + MenuDO.ID_ROOT, CommonStatusEnum.DISABLE.getStatus()); + Map menuStore = menuStore(enabledMenu, disabledMenu); + when(roleService.getRole(403L, OBJECT_SCOPE, PRODUCT_OBJECT)).thenReturn(role); + when(roleService.getRoleList(Collections.singleton(403L), OBJECT_SCOPE, PRODUCT_OBJECT)) + .thenReturn(List.of(role)); + when(roleMenuMapper.selectListByRoleId(Collections.singleton(403L))) + .thenReturn(List.of(buildRoleMenu(403L, 21L), buildRoleMenu(403L, 22L))); + when(menuMapper.selectByIds(any())).thenAnswer(invocation -> selectValues(menuStore, invocation.getArgument(0))); + + List menus = realPermissionService.getScopedMenusByRoleId(403L, OBJECT_SCOPE, PRODUCT_OBJECT); + + List menuIds = menus.stream().map(MenuDO::getId).toList(); + assertEquals(List.of(21L), menuIds); + assertFalse(menuIds.contains(22L)); + } + + void permissionServiceImpl_getScopedMenusByRoleId_shouldKeepAncestorMenusAfterExpandedRoleMenuAssignments() { + MenuServiceImpl realMenuService = createMenuServiceImpl(); + PermissionServiceImpl realPermissionService = createPermissionServiceImpl(roleService, realMenuService); + RoleDO role = buildRole(404L, "product_manager", "Product Manager", OBJECT_SCOPE, PRODUCT_OBJECT); + MenuDO directory = buildMenu(31L, "Product", null, 1, 1, "/product", + MenuDO.ID_ROOT, CommonStatusEnum.ENABLE.getStatus()); + MenuDO childMenu = buildMenu(32L, "Product Setting", null, 2, 10, "/product/setting", + 31L, CommonStatusEnum.ENABLE.getStatus()); + MenuDO button = buildMenu(33L, "Save", "project:product:update", 3, 20, null, + 32L, CommonStatusEnum.ENABLE.getStatus()); + Map menuStore = menuStore(directory, childMenu, button); + List persistedRoleMenus = new ArrayList<>(); + when(roleService.getRole(404L)).thenReturn(role); + when(roleService.getRole(404L, OBJECT_SCOPE, PRODUCT_OBJECT)).thenReturn(role); + when(roleService.getRoleList(Collections.singleton(404L), OBJECT_SCOPE, PRODUCT_OBJECT)) + .thenReturn(List.of(role)); + when(roleMenuMapper.selectListByRoleId(404L)).thenReturn(Collections.emptyList()); + doAnswer(invocation -> { + persistedRoleMenus.clear(); + persistedRoleMenus.addAll(invocation.getArgument(0)); + return null; + }).when(roleMenuMapper).insertBatch(any()); + when(roleMenuMapper.selectListByRoleId(Collections.singleton(404L))) + .thenAnswer(invocation -> new ArrayList<>(persistedRoleMenus)); + when(menuMapper.selectByIds(any())).thenAnswer(invocation -> selectValues(menuStore, invocation.getArgument(0))); + + realPermissionService.assignRoleMenu(404L, Set.of(32L, 33L)); + List menus = realPermissionService.getScopedMenusByRoleId(404L, OBJECT_SCOPE, PRODUCT_OBJECT); + + assertEquals(Set.of(31L, 32L, 33L), menus.stream().map(MenuDO::getId).collect(LinkedHashSet::new, Set::add, Set::addAll)); + assertEquals(Set.of("Product", "Product Setting", "Save"), + menus.stream().map(MenuDO::getName).collect(LinkedHashSet::new, Set::add, Set::addAll)); + } + + private RoleServiceImpl createRoleServiceImpl() { + RoleServiceImpl roleServiceImpl = new RoleServiceImpl(); + ReflectionTestUtils.setField(roleServiceImpl, "permissionService", permissionService); + ReflectionTestUtils.setField(roleServiceImpl, "roleMapper", roleMapper); + ReflectionTestUtils.setField(roleServiceImpl, "userRoleMapper", userRoleMapper); + return roleServiceImpl; + } + + private MenuServiceImpl createMenuServiceImpl() { + MenuServiceImpl menuServiceImpl = new MenuServiceImpl(); + ReflectionTestUtils.setField(menuServiceImpl, "menuMapper", menuMapper); + ReflectionTestUtils.setField(menuServiceImpl, "permissionService", permissionService); + return menuServiceImpl; + } + + private PermissionServiceImpl createPermissionServiceImpl(RoleService roleServiceDependency, + MenuServiceImpl menuServiceDependency) { + PermissionServiceImpl permissionServiceImpl = new PermissionServiceImpl(); + ReflectionTestUtils.setField(permissionServiceImpl, "roleMenuMapper", roleMenuMapper); + ReflectionTestUtils.setField(permissionServiceImpl, "userRoleMapper", userRoleMapper); + ReflectionTestUtils.setField(permissionServiceImpl, "roleService", roleServiceDependency); + ReflectionTestUtils.setField(permissionServiceImpl, "menuService", menuServiceDependency); + ReflectionTestUtils.setField(permissionServiceImpl, "userService", userService); + return permissionServiceImpl; + } + + private ObjectPermissionApiImpl createObjectPermissionApi(RoleService roleServiceDependency, + PermissionService permissionServiceDependency) { + ObjectPermissionApiImpl api = new ObjectPermissionApiImpl(); + ReflectionTestUtils.setField(api, "roleService", roleServiceDependency); + ReflectionTestUtils.setField(api, "permissionService", permissionServiceDependency); + return api; + } + + private static RoleDO buildRole(Long id, String code, String name, String scopeType, String objectType) { + RoleDO role = new RoleDO(); + role.setId(id); + role.setCode(code); + role.setName(name); + role.setScopeType(scopeType); + role.setObjectType(objectType); + role.setStatus(CommonStatusEnum.ENABLE.getStatus()); + return role; + } + + private static MenuDO buildMenu(Long id, String name, String permission, Integer type, Integer sort, String path, + Long parentId, Integer status) { + return buildMenu(id, name, permission, type, sort, path, parentId, status, OBJECT_SCOPE, PRODUCT_OBJECT); + } + + private static MenuDO buildMenu(Long id, String name, String permission, Integer type, Integer sort, String path, + Long parentId, Integer status, String scopeType, String objectType) { + MenuDO menu = new MenuDO(); + menu.setId(id); + menu.setName(name); + menu.setPermission(permission); + menu.setType(type); + menu.setSort(sort); + menu.setPath(path); + menu.setParentId(parentId); + menu.setStatus(status); + menu.setVisible(Boolean.TRUE); + menu.setScopeType(scopeType); + menu.setObjectType(objectType); + return menu; + } + + private static RoleMenuDO buildRoleMenu(Long roleId, Long menuId) { + RoleMenuDO roleMenu = new RoleMenuDO(); + roleMenu.setRoleId(roleId); + roleMenu.setMenuId(menuId); + return roleMenu; + } + + private static List selectValues(Map store, Collection ids) { + return ids.stream() + .map(store::get) + .filter(Objects::nonNull) + .collect(ArrayList::new, ArrayList::add, ArrayList::addAll); + } + + private static Map menuStore(MenuDO... menus) { + Map results = new LinkedHashMap<>(); + for (MenuDO menu : menus) { + results.put(menu.getId(), menu); + } + return results; + } + +}