SysMenuServiceImpl.java 9.4 KB
Newer Older
H
haoxr 已提交
1
package com.youlai.system.service.impl;
H
hxrui 已提交
2

3
import cn.hutool.core.collection.CollectionUtil;
4
import cn.hutool.core.util.ObjectUtil;
5
import cn.hutool.core.util.StrUtil;
H
hxrui 已提交
6
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
7
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
H
hxrui 已提交
8
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
9
import com.youlai.common.constant.SystemConstants;
R
Ray Hao 已提交
10
import com.youlai.system.enums.MenuTypeEnum;
11 12 13
import com.youlai.common.enums.StatusEnum;
import com.youlai.system.converter.MenuConverter;
import com.youlai.system.mapper.SysMenuMapper;
14 15 16 17 18 19 20
import com.youlai.system.model.bo.RouteBO;
import com.youlai.system.model.entity.SysMenu;
import com.youlai.system.model.form.MenuForm;
import com.youlai.system.model.query.MenuQuery;
import com.youlai.system.model.vo.MenuVO;
import com.youlai.common.web.model.Option;
import com.youlai.system.model.vo.RouteVO;
H
haoxr 已提交
21
import com.youlai.system.service.SysMenuService;
22
import com.youlai.system.service.SysRoleMenuService;
23
import lombok.RequiredArgsConstructor;
24
import org.apache.commons.lang3.StringUtils;
25
import org.springframework.cache.annotation.Cacheable;
H
hxrui 已提交
26 27
import org.springframework.stereotype.Service;

28 29 30
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
31
import java.util.stream.Collectors;
H
hxrui 已提交
32 33

/**
郝先瑞 已提交
34
 * 菜单业务实现类
35
 *
郝先瑞 已提交
36
 * @author haoxr
37
 * @since 2020/11/06
H
hxrui 已提交
38 39
 */
@Service
40
@RequiredArgsConstructor
郝先瑞 已提交
41
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {
42

43
    private final MenuConverter menuConverter;
44
    private final SysRoleMenuService roleMenuService;
45

46
    /**
47 48 49
     * 菜单列表
     *
     * @param queryParams {@link MenuQuery}
50
     */
H
hxrui 已提交
51
    @Override
52
    public List<MenuVO> listMenus(MenuQuery queryParams) {
郝先瑞 已提交
53
        List<SysMenu> menus = this.list(new LambdaQueryWrapper<SysMenu>()
54
                .like(StrUtil.isNotBlank(queryParams.getKeywords()), SysMenu::getName, queryParams.getKeywords())
郝先瑞 已提交
55
                .orderByAsc(SysMenu::getSort)
56
        );
H
hxrui 已提交
57

58 59 60 61 62 63 64
        Set<Long> parentIds = menus.stream()
                .map(SysMenu::getParentId)
                .collect(Collectors.toSet());
        Set<Long> menuIds = menus.stream()
                .map(SysMenu::getId)
                .collect(Collectors.toSet());

郝先瑞 已提交
65 66 67 68
        // 获取根节点ID
        List<Long> rootIds = parentIds.stream()
                .filter(id -> !menuIds.contains(id))
                .toList();
69

郝先瑞 已提交
70
        // 使用递归函数来构建菜单树
71 72

        return rootIds.stream()
郝先瑞 已提交
73 74
                .flatMap(rootId -> buildMenuTree(rootId, menus).stream())
                .collect(Collectors.toList());
75 76
    }

H
hxrui 已提交
77
    /**
郝先瑞 已提交
78 79 80 81 82
     * 递归生成菜单列表
     *
     * @param parentId 父级ID
     * @param menuList 菜单列表
     * @return 菜单列表
83
     */
郝先瑞 已提交
84 85 86 87 88 89 90 91 92 93
    private List<MenuVO> buildMenuTree(Long parentId, List<SysMenu> menuList) {
        return CollectionUtil.emptyIfNull(menuList)
                .stream()
                .filter(menu -> menu.getParentId().equals(parentId))
                .map(entity -> {
                    MenuVO menuVO = menuConverter.entity2Vo(entity);
                    List<MenuVO> children = buildMenuTree(entity.getId(), menuList);
                    menuVO.setChildren(children);
                    return menuVO;
                }).toList();
94 95
    }

96 97 98 99
    /**
     * 菜单下拉数据
     */
    @Override
100
    public List<Option> listMenuOptions() {
101
        List<SysMenu> menuList = this.list(new LambdaQueryWrapper<SysMenu>().orderByAsc(SysMenu::getSort));
郝先瑞 已提交
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
        return buildMenuOptions(SystemConstants.ROOT_NODE_ID, menuList);
    }

    /**
     * 递归生成菜单下拉层级列表
     *
     * @param parentId 父级ID
     * @param menuList 菜单列表
     * @return 菜单下拉列表
     */
    private List<Option> buildMenuOptions(Long parentId, List<SysMenu> menuList) {
        List<Option> menuOptions = new ArrayList<>();

        for (SysMenu menu : menuList) {
            if (menu.getParentId().equals(parentId)) {
                Option option = new Option(menu.getId(), menu.getName());
                List<Option> subMenuOptions = buildMenuOptions(menu.getId(), menuList);
                if (!subMenuOptions.isEmpty()) {
                    option.setChildren(subMenuOptions);
                }
                menuOptions.add(option);
            }
        }

        return menuOptions;
127
    }
128

有来技术 已提交
129
    /**
130
     * 路由列表
有来技术 已提交
131 132
     */
    @Override
133
    @Cacheable(cacheNames = "menu", key = "'routes'")
134
    public List<RouteVO> listRoutes() {
135
        List<RouteBO> menuList = this.baseMapper.listRoutes();
郝先瑞 已提交
136
        return buildRoutes(SystemConstants.ROOT_NODE_ID, menuList);
有来技术 已提交
137 138 139 140 141 142 143
    }

    /**
     * 递归生成菜单路由层级列表
     *
     * @param parentId 父级ID
     * @param menuList 菜单列表
郝先瑞 已提交
144
     * @return 路由层级列表
有来技术 已提交
145
     */
郝先瑞 已提交
146 147 148 149 150 151 152 153
    private List<RouteVO> buildRoutes(Long parentId, List<RouteBO> menuList) {
        List<RouteVO> routeList = new ArrayList<>();

        for (RouteBO menu : menuList) {
            if (menu.getParentId().equals(parentId)) {
                RouteVO routeVO = toRouteVo(menu);
                List<RouteVO> children = buildRoutes(menu.getId(), menuList);
                if (!children.isEmpty()) {
郝先瑞 已提交
154
                    routeVO.setChildren(children);
郝先瑞 已提交
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
                }
                routeList.add(routeVO);
            }
        }

        return routeList;
    }

    /**
     * 根据RouteBO创建RouteVO
     */
    private RouteVO toRouteVo(RouteBO routeBO) {
        RouteVO routeVO = new RouteVO();
        String routeName = StringUtils.capitalize(StrUtil.toCamelCase(routeBO.getPath(), '-'));  // 路由 name 需要驼峰,首字母大写
        routeVO.setName(routeName); // 根据name路由跳转 this.$router.push({name:xxx})
        routeVO.setPath(routeBO.getPath()); // 根据path路由跳转 this.$router.push({path:xxx})
        routeVO.setRedirect(routeBO.getRedirect());
        routeVO.setComponent(routeBO.getComponent());

        RouteVO.Meta meta = new RouteVO.Meta();
        meta.setTitle(routeBO.getName());
        meta.setIcon(routeBO.getIcon());
        meta.setRoles(routeBO.getRoles());
        meta.setHidden(StatusEnum.DISABLE.getValue().equals(routeBO.getVisible()));
        // 【菜单】是否开启页面缓存
        if (MenuTypeEnum.MENU.equals(routeBO.getType())
                && ObjectUtil.equals(routeBO.getKeepAlive(), 1)) {
            meta.setKeepAlive(true);
        }
        // 【目录】只有一个子路由是否始终显示
        if (MenuTypeEnum.CATALOG.equals(routeBO.getType())
                && ObjectUtil.equals(routeBO.getAlwaysShow(), 1)) {
            meta.setAlwaysShow(true);
        }

        routeVO.setMeta(meta);
        return routeVO;
    }

    /**
     * 保存菜单
     */
    @Override
    public boolean saveMenu(MenuForm menuForm) {
        String path = menuForm.getPath();
        MenuTypeEnum menuType = menuForm.getType();

        // 如果是目录
        if (menuType == MenuTypeEnum.CATALOG) {
            if (menuForm.getParentId() == 0 && !path.startsWith("/")) {
                menuForm.setPath("/" + path); // 一级目录需以 / 开头
            }
            menuForm.setComponent("Layout");
        }
        // 如果是外链
        else if (menuType == MenuTypeEnum.EXTLINK) {
            menuForm.setComponent(null);
        }

        SysMenu entity = menuConverter.form2Entity(menuForm);
        String treePath = generateMenuTreePath(menuForm.getParentId());
        entity.setTreePath(treePath);

218 219 220 221 222 223 224 225
        boolean result = this.saveOrUpdate(entity);
        if (result) {
            // 编辑刷新角色权限缓存
            if (menuForm.getId() != null) {
                roleMenuService.refreshRolePermsCache();
            }
        }
        return result;
郝先瑞 已提交
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
    }

    /**
     * 部门路径生成
     *
     * @param parentId 父ID
     * @return 父节点路径以英文逗号(, )分割,eg: 1,2,3
     */
    private String generateMenuTreePath(Long parentId) {
        if (SystemConstants.ROOT_NODE_ID.equals(parentId)) {
            return String.valueOf(parentId);
        } else {
            SysMenu parent = this.getById(parentId);
            return parent != null ? parent.getTreePath() + "," + parent.getId() : null;
        }
有来技术 已提交
241
    }
郝先瑞 已提交
242 243


244 245 246 247 248
    /**
     * 修改菜单显示状态
     *
     * @param menuId  菜单ID
     * @param visible 是否显示(1->显示;2->隐藏)
249
     * @return 是否成功
250 251 252
     */
    @Override
    public boolean updateMenuVisible(Long menuId, Integer visible) {
253
        return this.update(new LambdaUpdateWrapper<SysMenu>()
254 255 256 257 258
                .eq(SysMenu::getId, menuId)
                .set(SysMenu::getVisible, visible)
        );
    }

259 260 261 262
    /**
     * 获取菜单表单数据
     *
     * @param id 菜单ID
263
     * @return {@link MenuForm}
264 265 266 267
     */
    @Override
    public MenuForm getMenuForm(Long id) {
        SysMenu entity = this.getById(id);
268
        return menuConverter.entity2Form(entity);
269 270 271 272 273 274
    }

    /**
     * 删除菜单
     *
     * @param id 菜单ID
275
     * @return 是否成功
276 277 278
     */
    @Override
    public boolean deleteMenu(Long id) {
279 280 281 282 283 284 285 286
        boolean result = this.remove(new LambdaQueryWrapper<SysMenu>()
                .eq(SysMenu::getId, id)
                .or()
                .apply("CONCAT (',',tree_path,',') LIKE CONCAT('%,',{0},',%')", id));

        // 刷新角色权限缓存
        if (result) {
            roleMenuService.refreshRolePermsCache();
287
        }
288
        return result;
289 290
    }

郝先瑞 已提交
291

H
hxrui 已提交
292
}