1. 修改 MenuDO 的字段,menuId 改成 id,menuName 改成 name

2. 添加 Menu 的创建、修改、删除接口
This commit is contained in:
YunaiV
2021-01-08 20:08:20 +08:00
parent dba723b8fc
commit ea4c9e4981
29 changed files with 503 additions and 237 deletions

View File

@@ -12,7 +12,6 @@ import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMe
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO;
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO;
import cn.iocoder.dashboard.modules.system.dal.redis.dao.auth.SysLoginUserRedisDAO;
import cn.iocoder.dashboard.modules.system.enums.permission.MenuIdEnum;
import cn.iocoder.dashboard.modules.system.enums.permission.MenuTypeEnum;
import cn.iocoder.dashboard.modules.system.enums.user.UserStatus;
import cn.iocoder.dashboard.modules.system.service.auth.SysAuthService;
@@ -38,7 +37,10 @@ import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import javax.annotation.Resource;
import java.util.*;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
@@ -243,36 +245,12 @@ public class SysAuthServiceImpl implements SysAuthService {
@Override
public List<SysAuthMenuRespVO> listMenus(Long userId, Set<Long> roleIds) {
// 获得用户拥有的菜单列表
List<SysMenuDO> menuList = permissionService.listRoleMenusFromCache(roleIds,
SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType()),
SetUtils.asSet(CommonStatusEnum.ENABLE.getStatus()));
SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType()), // 只要目录和菜单类型
SetUtils.asSet(CommonStatusEnum.ENABLE.getStatus())); // 只要开启的
// 转换成 Tree 结构返回
return buildRouterTree(menuList);
}
private static List<SysAuthMenuRespVO> buildRouterTree(List<SysMenuDO> menuList) {
// 排序,保证菜单的有序性
menuList.sort(Comparator.comparing(SysMenuDO::getSort));
// 构建菜单树
// 使用 LinkedHashMap 的原因,是为了排序 。实际也可以用 Stream API ,就是太丑了。
Map<Long, SysAuthMenuRespVO> treeNodeMap = new LinkedHashMap<>();
menuList.forEach(menu -> treeNodeMap.put(menu.getMenuId(), SysAuthConvert.INSTANCE.convertTreeNode(menu)));
// 处理父子关系
treeNodeMap.values().stream().filter(node -> !node.getParentId().equals(MenuIdEnum.ROOT.getId())).forEach((childNode) -> {
// 获得父节点
SysAuthMenuRespVO parentNode = treeNodeMap.get(childNode.getParentId());
if (parentNode == null) {
log.error("[buildRouterTree][resource({}) 找不到父资源({})]", childNode.getMenuId(), childNode.getParentId());
return;
}
// 将自己添加到父节点中
if (parentNode.getChildren() == null) {
parentNode.setChildren(new ArrayList<>());
}
parentNode.getChildren().add(childNode);
});
// 获得到所有的根节点
return CollectionUtils.filterList(treeNodeMap.values(), node -> MenuIdEnum.ROOT.getId().equals(node.getParentId()));
return SysAuthConvert.INSTANCE.buildMenuTree(menuList);
}
}

View File

@@ -19,7 +19,7 @@ public class SysDictDataServiceImpl implements SysDictDataService {
private static final Comparator<SysDictDataDO> COMPARATOR_TYPE_AND_SORT = Comparator
.comparing(SysDictDataDO::getDictType)
.thenComparingInt(SysDictDataDO::getDictSort);
.thenComparingInt(SysDictDataDO::getSort);
@Resource
private SysDictDataMapper dictDataMapper;

View File

@@ -1,7 +1,9 @@
package cn.iocoder.dashboard.modules.system.service.permission;
import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuCreateReqVO;
import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuListReqVO;
import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuRespVO;
import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuUpdateReqVO;
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
import java.util.Collection;
@@ -9,6 +11,8 @@ import java.util.List;
/**
* 菜单 Service 接口
*
* @author 芋道源码
*/
public interface SysMenuService {
@@ -49,4 +53,34 @@ public interface SysMenuService {
List<SysMenuDO> listMenusFromCache(Collection<Long> menuIds, Collection<Integer> menuTypes,
Collection<Integer> menusStatuses);
/*
* 创建菜单
*
* @param reqVO 菜单信息
* @return 创建出来的菜单编号
*/
Long createMenu(SysMenuCreateReqVO reqVO);
/**
* 更新菜单
*
* @param reqVO 菜单信息
*/
void updateMenu(SysMenuUpdateReqVO reqVO);
/**
* 删除菜单
*
* @param id 菜单编号
*/
void deleteMenu(Long id);
/**
* 获得菜单
*
* @param id 菜单编号
* @return 菜单
*/
SysMenuDO getMenu(Long id);
}

View File

@@ -48,4 +48,11 @@ public interface SysPermissionService {
*/
Long getDeptRoleId(Long deptId);
/**
* 删除授予给角色的菜单们
*
* @param menuId 菜单编号
*/
void deleteRolesMenuByMenuId(Long menuId);
}

View File

@@ -1,11 +1,17 @@
package cn.iocoder.dashboard.modules.system.service.permission.impl;
import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuCreateReqVO;
import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuListReqVO;
import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuRespVO;
import cn.iocoder.dashboard.modules.system.controller.permission.vo.SysMenuUpdateReqVO;
import cn.iocoder.dashboard.modules.system.convert.permission.SysMenuConvert;
import cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission.SysMenuMapper;
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
import cn.iocoder.dashboard.modules.system.enums.permission.MenuIdEnum;
import cn.iocoder.dashboard.modules.system.enums.permission.MenuTypeEnum;
import cn.iocoder.dashboard.modules.system.service.permission.SysMenuService;
import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService;
import cn.iocoder.dashboard.util.collection.CollectionUtils;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
@@ -21,8 +27,12 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
/**
* 菜单 Service 实现
*
* @author 芋道源码
*/
@Service
@Slf4j
@@ -46,6 +56,8 @@ public class SysMenuServiceImpl implements SysMenuService {
@Resource
private SysMenuMapper menuMapper;
@Resource
private SysPermissionService permissionService;
/**
* 初始化 {@link #menuCache} 和 {@link #permMenuCache} 缓存
@@ -57,7 +69,7 @@ public class SysMenuServiceImpl implements SysMenuService {
ImmutableMap.Builder<Long, SysMenuDO> menuCacheBuilder = ImmutableMap.builder();
ImmutableMultimap.Builder<String, SysMenuDO> permMenuCacheBuilder = ImmutableMultimap.builder();
menuList.forEach(menuDO -> {
menuCacheBuilder.put(menuDO.getMenuId(), menuDO);
menuCacheBuilder.put(menuDO.getId(), menuDO);
permMenuCacheBuilder.put(menuDO.getPermission(), menuDO);
});
menuCache = menuCacheBuilder.build();
@@ -79,7 +91,7 @@ public class SysMenuServiceImpl implements SysMenuService {
return Collections.emptyList();
}
// 创建新数组,避免缓存被修改
return menuCache.values().stream().filter(menu -> menuTypes.contains(menu.getMenuType())
return menuCache.values().stream().filter(menu -> menuTypes.contains(menu.getType())
&& menusStatuses.contains(menu.getStatus()))
.collect(Collectors.toList());
}
@@ -91,10 +103,186 @@ public class SysMenuServiceImpl implements SysMenuService {
if (CollectionUtils.isAnyEmpty(menuIds, menuTypes, menusStatuses)) {
return Collections.emptyList();
}
return menuCache.values().stream().filter(menu -> menuIds.contains(menu.getMenuId())
&& menuTypes.contains(menu.getMenuType())
return menuCache.values().stream().filter(menu -> menuIds.contains(menu.getId())
&& menuTypes.contains(menu.getType())
&& menusStatuses.contains(menu.getStatus()))
.collect(Collectors.toList());
}
@Override
public Long createMenu(SysMenuCreateReqVO reqVO) {
// 校验父菜单存在
checkParentResource(reqVO.getParentId(), null);
// 校验菜单(自己)
checkResource(reqVO.getParentId(), reqVO.getName(), null);
// 插入数据库
SysMenuDO menu = SysMenuConvert.INSTANCE.convert(reqVO);
initMenuProperty(menu);
menuMapper.insert(menu);
// 返回
return menu.getId();
}
@Override
public void updateMenu(SysMenuUpdateReqVO reqVO) {
// 校验更新的菜单是否存在
if (menuMapper.selectById(reqVO.getId()) == null) {
throw ServiceExceptionUtil.exception(MENU_NOT_EXISTS);
}
// 校验父菜单存在
checkParentResource(reqVO.getParentId(), reqVO.getId());
// 校验菜单(自己)
checkResource(reqVO.getParentId(), reqVO.getName(), reqVO.getId());
// 更新到数据库
SysMenuDO updateObject = SysMenuConvert.INSTANCE.convert(reqVO);
initMenuProperty(updateObject);
menuMapper.updateById(updateObject);
}
/**
* 删除菜单
*
* @param menuId 菜单编号
*/
public void deleteMenu(Long menuId) {
// 校验更新的菜单是否存在
if (menuMapper.selectById(menuId) == null) {
throw ServiceExceptionUtil.exception(MENU_NOT_EXISTS);
}
// 校验是否还有子菜单
if (menuMapper.selectCountByParentId(menuId) > 0) {
throw ServiceExceptionUtil.exception(MENU_EXISTS_CHILDREN);
}
// 校验删除的菜单是否存在
if (menuMapper.selectById(menuId) == null) {
throw ServiceExceptionUtil.exception(MENU_NOT_EXISTS);
}
// 标记删除
menuMapper.deleteById(menuId);
// 删除授予给角色的权限
permissionService.deleteRolesMenuByMenuId(menuId);
}
@Override
public SysMenuDO getMenu(Long id) {
return menuMapper.selectById(id);
}
// /**
// * 获得菜单列表
// *
// * @param menuIds 菜单编号列表
// * @return 菜单列表
// */
// public List<ResourceBO> listResources(List<Integer> menuIds) {
// List<ResourceDO> menuDOs = menuMapper.selectBatchIds(menuIds);
// return ResourceConvert.INSTANCE.convertList(menuDOs);
// }
//
// /**
// * 获得菜单全列表
// *
// * @return 菜单全列表
// */
// public List<ResourceBO> listResources() {
// List<ResourceDO> menuDOs = menuMapper.selectList(null);
// return ResourceConvert.INSTANCE.convertList(menuDOs);
// }
//
// /**
// * 获得指定类型的菜单列表
// *
// * @param type 菜单类型,允许空
// * @return 菜单列表
// */
// public List<ResourceBO> listResourcesByType(Integer type) {
// List<ResourceDO> menuDOs = menuMapper.selectListByType(type);
// return ResourceConvert.INSTANCE.convertList(menuDOs);
// }
//
// /**
// * 获得角色拥有的菜单列表
// *
// * @param roleIds 角色编号
// * @param type 菜单类型,允许空
// * @return 菜单列表
// */
// public List<ResourceBO> listRoleResourcesByType(Collection<Integer> roleIds, Integer type) {
// List<RoleResourceDO> roleResourceDOs = roleResourceMapper.selectListByRoleIds(roleIds);
// if (CollectionUtils.isEmpty(roleResourceDOs)) {
// return Collections.emptyList();
// }
// List<ResourceDO> menuDOs = menuMapper.selectListByIdsAndType(
// CollectionUtils.convertSet(roleResourceDOs, RoleResourceDO::getResourceId), type);
// return ResourceConvert.INSTANCE.convertList(menuDOs);
// }
//
/**
* 校验父菜单是否合法
*
* 1. 不能设置自己为父菜单
* 2. 父菜单不存在
* 3. 父菜单必须是 {@link MenuTypeEnum#MENU} 菜单类型
*
* @param parentId 父菜单编号
* @param childId 当前菜单编号
*/
private void checkParentResource(Long parentId, Long childId) {
if (parentId == null || MenuIdEnum.ROOT.getId().equals(parentId)) {
return;
}
// 不能设置自己为父菜单
if (parentId.equals(childId)) {
throw ServiceExceptionUtil.exception(MENU_PARENT_ERROR);
}
SysMenuDO menu = menuMapper.selectById(parentId);
// 父菜单不存在
if (menu == null) {
throw ServiceExceptionUtil.exception(MENU_PARENT_NOT_EXISTS);
}
// 父菜单必须是目录类型
if (!MenuTypeEnum.DIR.getType().equals(menu.getType())) {
throw ServiceExceptionUtil.exception(MENU_PARENT_NOT_MENU);
}
}
/**
* 校验菜单是否合法
*
* 1. 校验相同父菜单编号下,是否存在相同的菜单名
*
* @param name 菜单名字
* @param parentId 父菜单编号
* @param id 菜单编号
*/
private void checkResource(Long parentId, String name, Long id) {
SysMenuDO menu = menuMapper.selectByParentIdAndName(parentId, name);
if (menu == null) {
return;
}
// 如果 menuId 为空,说明不用比较是否为相同 menuId 的菜单
if (id == null) {
throw ServiceExceptionUtil.exception(MENU_NAME_DUPLICATE);
}
if (!menu.getId().equals(id)) {
throw ServiceExceptionUtil.exception(MENU_NAME_DUPLICATE);
}
}
/**
* 初始化菜单的通用属性。
*
* 例如说,只有目录或者菜单类型的菜单,才设置 icon
*
* @param menu 菜单
*/
private void initMenuProperty(SysMenuDO menu) {
// 菜单为按钮类型时,无需 component、icon、path 属性,进行置空
if (MenuTypeEnum.BUTTON.getType().equals(menu.getType())) {
menu.setComponent("");
menu.setIcon("");
menu.setPath("");
}
}
}

View File

@@ -107,4 +107,9 @@ public class SysPermissionServiceImpl implements SysPermissionService {
return roleDept != null ? roleDept.getRoleId() : null;
}
@Override
public void deleteRolesMenuByMenuId(Long menuId) {
// TODO 实现我
}
}