package com.oying.modules.system.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.oying.modules.system.domain.Menu; import com.oying.modules.system.domain.Role; import com.oying.modules.system.domain.User; import com.oying.modules.system.domain.dto.MenuMetaVo; import com.oying.modules.system.domain.dto.MenuQueryCriteria; import com.oying.modules.system.domain.dto.MenuVo; import com.oying.modules.system.mapper.MenuMapper; import com.oying.modules.system.mapper.RoleMenuMapper; import com.oying.modules.system.mapper.UserMapper; import com.oying.modules.system.service.MenuService; import com.oying.modules.system.service.RoleService; import lombok.RequiredArgsConstructor; import com.oying.utils.CacheKey; import com.oying.utils.FileUtil; import com.oying.utils.RedisUtils; import com.oying.utils.StringUtils; import com.oying.exception.BadRequestException; import com.oying.exception.EntityExistException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.lang.reflect.Field; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** * @author Z */ @Service @RequiredArgsConstructor public class MenuServiceImpl extends ServiceImpl implements MenuService { private final MenuMapper menuMapper; private final RoleMenuMapper roleMenuMapper; private final UserMapper userMapper; private final RoleService roleService; private final RedisUtils redisUtils; private static final String HTTP_PRE = "http://"; private static final String HTTPS_PRE = "https://"; private static final String YES_STR = "是"; private static final String NO_STR = "否"; private static final String BAD_REQUEST = "外链必须以http://或者https://开头"; @Override public List queryAll(MenuQueryCriteria criteria, Boolean isQuery) throws Exception { if(Boolean.TRUE.equals(isQuery)){ criteria.setPidIsNull(true); List fields = StringUtils.getAllFields(criteria.getClass(), new ArrayList<>()); for (Field field : fields) { //设置对象的访问权限,保证对private的属性的访问 field.setAccessible(true); Object val = field.get(criteria); if("pidIsNull".equals(field.getName())){ continue; } // 如果有查询条件,则不指定pidIsNull if (ObjectUtil.isNotNull(val)) { criteria.setPidIsNull(null); break; } } } return menuMapper.findAll(criteria); } @Override public Menu findById(long id) { String key = CacheKey.MENU_ID + id; Menu menu = redisUtils.get(key, Menu.class); if(menu == null){ menu = getById(id); redisUtils.set(key, menu, 1, TimeUnit.DAYS); } return menu; } /** * 用户角色改变时需清理缓存 * @param currentUserId / * @return / */ @Override public List findByUser(Long currentUserId) { String key = CacheKey.MENU_USER + currentUserId; List menus = redisUtils.getList(key, Menu.class); if (CollUtil.isEmpty(menus)){ List roles = roleService.findByUsersId(currentUserId); Set roleIds = roles.stream().map(Role::getId).collect(Collectors.toSet()); menus = new ArrayList<>(menuMapper.findByRoleIdsAndTypeNot(roleIds, 2)); redisUtils.set(key, menus, 1, TimeUnit.DAYS); } return menus; } @Override @Transactional(rollbackFor = Exception.class) public void create(Menu resources) { if(menuMapper.findByTitle(resources.getTitle()) != null){ throw new EntityExistException(Menu.class,"title",resources.getTitle()); } if(StringUtils.isNotBlank(resources.getComponentName())){ if(menuMapper.findByComponentName(resources.getComponentName()) != null){ throw new EntityExistException(Menu.class,"componentName",resources.getComponentName()); } } if (Long.valueOf(0L).equals(resources.getPid())) { resources.setPid(null); } if(resources.getIFrame()){ if (!(resources.getPath().toLowerCase().startsWith(HTTP_PRE)||resources.getPath().toLowerCase().startsWith(HTTPS_PRE))) { throw new BadRequestException(BAD_REQUEST); } } save(resources); // 计算子节点数目 resources.setSubCount(0); // 更新父节点菜单数目 updateSubCnt(resources.getPid()); } @Override @Transactional(rollbackFor = Exception.class) public void update(Menu resources) { if(resources.getId().equals(resources.getPid())) { throw new BadRequestException("上级不能为自己"); } Menu menu = getById(resources.getId()); if(resources.getIFrame()){ if (!(resources.getPath().toLowerCase().startsWith(HTTP_PRE)||resources.getPath().toLowerCase().startsWith(HTTPS_PRE))) { throw new BadRequestException(BAD_REQUEST); } } Menu menu1 = menuMapper.findByTitle(resources.getTitle()); if(menu1 != null && !menu1.getId().equals(menu.getId())){ throw new EntityExistException(Menu.class,"title",resources.getTitle()); } if(resources.getPid().equals(0L)){ resources.setPid(null); } // 记录的父节点ID Long oldPid = menu.getPid(); Long newPid = resources.getPid(); if(StringUtils.isNotBlank(resources.getComponentName())){ menu1 = menuMapper.findByComponentName(resources.getComponentName()); if(menu1 != null && !menu1.getId().equals(menu.getId())){ throw new EntityExistException(Menu.class,"componentName",resources.getComponentName()); } } menu.setTitle(resources.getTitle()); menu.setComponent(resources.getComponent()); menu.setPath(resources.getPath()); menu.setIcon(resources.getIcon()); menu.setIFrame(resources.getIFrame()); menu.setPid(resources.getPid()); menu.setMenuSort(resources.getMenuSort()); menu.setCache(resources.getCache()); menu.setHidden(resources.getHidden()); menu.setComponentName(resources.getComponentName()); menu.setPermission(resources.getPermission()); menu.setType(resources.getType()); saveOrUpdate(menu); // 计算父级菜单节点数目 updateSubCnt(oldPid); updateSubCnt(newPid); // 清理缓存 delCaches(resources.getId()); } @Override public Set getChildMenus(List menuList, Set menuSet) { for (Menu menu : menuList) { menuSet.add(menu); List menus = menuMapper.findByPidOrderByMenuSort(menu.getId()); if(CollUtil.isNotEmpty(menus)){ getChildMenus(menus, menuSet); } } return menuSet; } @Override @Transactional(rollbackFor = Exception.class) public void delete(Set menuSet) { for (Menu menu : menuSet) { // 清理缓存 delCaches(menu.getId()); roleMenuMapper.deleteByMenuId(menu.getId()); menuMapper.deleteById(menu.getId()); updateSubCnt(menu.getPid()); } } @Override public List getMenus(Long pid) { List menus; if(pid != null && !pid.equals(0L)){ menus = menuMapper.findByPidOrderByMenuSort(pid); } else { menus = menuMapper.findByPidIsNullOrderByMenuSort(); } return menus; } @Override public List getSuperior(Menu menu, List menus) { if(menu.getPid() == null){ menus.addAll(menuMapper.findByPidIsNullOrderByMenuSort()); return menus; } menus.addAll(menuMapper.findByPidOrderByMenuSort(menu.getPid())); return getSuperior(findById(menu.getPid()), menus); } @Override public List buildTree(List menus) { List trees = new ArrayList<>(); Set ids = new HashSet<>(); for (Menu menu : menus) { if (menu.getPid() == null) { trees.add(menu); } for (Menu it : menus) { if (menu.getId().equals(it.getPid())) { if (menu.getChildren() == null) { menu.setChildren(new ArrayList<>()); } menu.getChildren().add(it); ids.add(it.getId()); } } } if(CollUtil.isNotEmpty(trees)){ trees = menus.stream().filter(s -> !ids.contains(s.getId())).collect(Collectors.toList()); } return trees; } @Override public List buildMenus(List menus) { List list = new LinkedList<>(); menus.forEach(menu -> { if (menu!=null){ List menuList = menu.getChildren(); MenuVo menuVo = new MenuVo(); menuVo.setName(ObjectUtil.isNotEmpty(menu.getComponentName()) ? menu.getComponentName() : menu.getTitle()); // 一级目录需要加斜杠,不然会报警告 menuVo.setPath(menu.getPid() == null ? "/" + menu.getPath() :menu.getPath()); menuVo.setHidden(menu.getHidden()); // 如果不是外链 if(!menu.getIFrame()){ if(menu.getPid() == null){ menuVo.setComponent(StringUtils.isEmpty(menu.getComponent())?"Layout":menu.getComponent()); // 如果不是一级菜单,并且菜单类型为目录,则代表是多级菜单 }else if(menu.getType() == 0){ menuVo.setComponent(StringUtils.isEmpty(menu.getComponent())?"ParentView":menu.getComponent()); }else if(StringUtils.isNoneBlank(menu.getComponent())){ menuVo.setComponent(menu.getComponent()); } } menuVo.setMeta(new MenuMetaVo(menu.getTitle(),menu.getIcon(),!menu.getCache())); if(CollectionUtil.isNotEmpty(menuList)){ menuVo.setAlwaysShow(true); menuVo.setRedirect("noredirect"); menuVo.setChildren(buildMenus(menuList)); // 处理是一级菜单并且没有子菜单的情况 } else if(menu.getPid() == null){ MenuVo menuVo1 = getMenuVo(menu, menuVo); menuVo.setName(null); menuVo.setMeta(null); menuVo.setComponent("Layout"); List list1 = new ArrayList<>(); list1.add(menuVo1); menuVo.setChildren(list1); } list.add(menuVo); } } ); return list; } @Override public void download(List menus, HttpServletResponse response) throws IOException { List> list = new ArrayList<>(); for (Menu menu : menus) { Map map = new LinkedHashMap<>(); map.put("菜单标题", menu.getTitle()); map.put("菜单类型", menu.getType() == null ? "目录" : menu.getType() == 1 ? "菜单" : "按钮"); map.put("权限标识", menu.getPermission()); map.put("外链菜单", menu.getIFrame() ? YES_STR : NO_STR); map.put("菜单可见", menu.getHidden() ? NO_STR : YES_STR); map.put("是否缓存", menu.getCache() ? YES_STR : NO_STR); map.put("创建日期", menu.getCreateTime()); list.add(map); } FileUtil.downloadExcel(list, response); } private void updateSubCnt(Long menuId){ if(menuId != null){ int count = menuMapper.countByPid(menuId); menuMapper.updateSubCntById(count, menuId); } } /** * 清理缓存 * @param id 菜单ID */ public void delCaches(Long id){ List users = userMapper.findByMenuId(id); redisUtils.del(CacheKey.MENU_ID + id); redisUtils.delByKeys(CacheKey.MENU_USER, users.stream().map(User::getId).collect(Collectors.toSet())); // 清除 Role 缓存 List roles = roleService.findByMenuId(id); redisUtils.delByKeys(CacheKey.ROLE_ID, roles.stream().map(Role::getId).collect(Collectors.toSet())); } /** * 获取 MenuVo * @param menu / * @param menuVo / * @return / */ private static MenuVo getMenuVo(Menu menu, MenuVo menuVo) { MenuVo menuVo1 = new MenuVo(); menuVo1.setMeta(menuVo.getMeta()); // 非外链 if(!menu.getIFrame()){ menuVo1.setPath("index"); menuVo1.setName(menuVo.getName()); menuVo1.setComponent(menuVo.getComponent()); } else { menuVo1.setPath(menu.getPath()); } return menuVo1; } }