feat(permission): 新增对象权限API接口及实现

- 定义ObjectPermissionApi接口提供对象作用域权限查询功能
- 实现ObjectPermissionApiImpl提供角色权限查询和转换逻辑
- 添加ObjectMenuRespDTO、ObjectRoleRespDTO和ObjectRolePermissionRespDTO数据传输对象
- 实现按角色ID、角色编码查询对象作用域角色及权限的功能
- 提供获取对象作用域角色菜单与权限聚合结果的方法
- 添加完整单元测试覆盖对象权限API的主要业务场景
This commit is contained in:
2026-04-23 09:23:33 +08:00
parent 156728b1b9
commit 0a6d70f7cf
7 changed files with 924 additions and 0 deletions

View File

@@ -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<ObjectRoleRespDTO> getObjectRoleById(Long roleId, String scopeType, String objectType) {
return success(convertRole(roleService.getRole(roleId, scopeType, objectType)));
}
@Override
public CommonResult<ObjectRoleRespDTO> getObjectRoleByCode(String roleCode, String scopeType, String objectType) {
return success(convertRole(roleService.getRoleByCode(roleCode, scopeType, objectType)));
}
@Override
public CommonResult<List<ObjectRoleRespDTO>> getObjectRoleList(Collection<Long> roleIds, String scopeType, String objectType) {
List<RoleDO> roles = roleService.getRoleList(roleIds, scopeType, objectType);
return success(roles.stream().map(this::convertRole).toList());
}
@Override
public CommonResult<Set<String>> 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<ObjectRolePermissionRespDTO> 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;
}
}

View File

@@ -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<ObjectRoleRespDTO> 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<ObjectRoleRespDTO> 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<ObjectRoleRespDTO> 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<Long, RoleDO> 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<List<ObjectRoleRespDTO>> result =
realApi.getObjectRoleList(List.of(201L, 202L, 203L, 204L), OBJECT_SCOPE, PRODUCT_OBJECT);
List<ObjectRoleRespDTO> 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<Set<String>> 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<Set<String>> 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<Long, MenuDO> 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<String> 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<MenuDO> menus = realPermissionService.getScopedMenusByRoleId(305L, OBJECT_SCOPE, PRODUCT_OBJECT);
Set<String> 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<Long, MenuDO> 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<MenuDO> menus = realPermissionService.getScopedMenusByRoleId(3051L, OBJECT_SCOPE, PRODUCT_OBJECT);
Set<String> 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<Long, MenuDO> 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.<Collection<Long>>any())).thenAnswer(invocation -> {
Collection<Long> roleIds = invocation.getArgument(0);
List<RoleMenuDO> 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<Long> 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<ObjectRolePermissionRespDTO> 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<ObjectRolePermissionRespDTO> 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<String> 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<ObjectRolePermissionRespDTO> 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<Long, MenuDO> 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<MenuDO> menus = realPermissionService.getScopedMenusByRoleId(403L, OBJECT_SCOPE, PRODUCT_OBJECT);
List<Long> 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<Long, MenuDO> menuStore = menuStore(directory, childMenu, button);
List<RoleMenuDO> 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<MenuDO> 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 <T> List<T> selectValues(Map<Long, T> store, Collection<Long> ids) {
return ids.stream()
.map(store::get)
.filter(Objects::nonNull)
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
}
private static Map<Long, MenuDO> menuStore(MenuDO... menus) {
Map<Long, MenuDO> results = new LinkedHashMap<>();
for (MenuDO menu : menus) {
results.put(menu.getId(), menu);
}
return results;
}
}