手把手教你搞定菜单权限设计,精确到按钮级别,建议收藏(一)

简介: 在实际的项目开发过程中,菜单权限功能可以说是后端管理系统中必不可少的一个环节,根据业务的复杂度,设计的时候可深可浅,但无论怎么变化,设计的思路基本都是围绕着用户、角色、菜单进行相应的扩展。

一、介绍

在实际的项目开发过程中,菜单权限功能可以说是后端管理系统中必不可少的一个环节,根据业务的复杂度,设计的时候可深可浅,但无论怎么变化,设计的思路基本都是围绕着用户、角色、菜单进行相应的扩展。

今天小编就和大家一起来讨论一下,怎么设计一套可以精确到按钮级别的菜单权限功能,废话不多说,直接开撸!

二、数据库设计

先来看一下,用户、角色、菜单表对应的ER图,如下:

82.jpg

其中,用户和角色是多对多的关系,角色与菜单也是多对多的关系,用户通过角色来关联到菜单,当然也有的业务系统菜单权限模型,是可以直接通过用户关联到菜单,对菜单权限可以直接控制到用户级别,不过这个都不是问题,这个也可以进行扩展。

对于用户、角色表比较简单,下面,我们重点来看看菜单表的设计,如下:

83.jpg

可以看到,整个菜单表就是一个树型结构,关键字段说明:

  • menu_code:菜单编码,用于后端权限控制
  • parent_id:菜单父节点ID,方便递归遍历菜单
  • node_type:节点类型,可以是文件夹、页面或者按钮类型
  • link_url:页面对应的地址,如果是文件夹或者按钮类型,可以为空
  • level:菜单树的层次,以便于查询指定层级的菜单
  • path:树id的路径,主要用于存放从根节点到当前树的父节点的路径,逗号分隔,想要找父节点会特别快

为了后面方便开发,我们先创建一个名为menu_auth_db的数据库,初始脚本如下:

CREATE DATABASE IF NOT EXISTS menu_auth_db default charset utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE menu_auth_db.tb_user (
  id bigint(20) unsigned NOT NULL COMMENT '消息给过来的ID',
  mobile varchar(20) NOT NULL DEFAULT '' COMMENT '手机号',
  name varchar(100) NOT NULL DEFAULT '' COMMENT '姓名',
  password varchar(128) NOT NULL DEFAULT '' COMMENT '密码',
  is_delete tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否删除 1:已删除;0:未删除',
  PRIMARY KEY (id),
  KEY idx_name (name) USING BTREE,
  KEY idx_mobile (mobile) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';
CREATE TABLE menu_auth_db.tb_user_role (
  id bigint(20) unsigned NOT NULL COMMENT '主键',
  user_id bigint(20) NOT NULL COMMENT '用户ID',
  role_id bigint(20) NOT NULL COMMENT '角色ID',
  PRIMARY KEY (id),
  KEY idx_user_id (user_id) USING BTREE,
  KEY idx_role_id (role_id) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户角色表';
CREATE TABLE menu_auth_db.tb_role (
  id bigint(20) unsigned NOT NULL COMMENT '主键',
  code varchar(100) NOT NULL DEFAULT '' COMMENT '编码',
  name varchar(100) NOT NULL DEFAULT '' COMMENT '名称',
  is_delete tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否删除 1:已删除;0:未删除',
  PRIMARY KEY (id),
  KEY idx_code (code) USING BTREE,
  KEY idx_name (name) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='角色表';
CREATE TABLE menu_auth_db.tb_role_menu (
  id bigint(20) unsigned NOT NULL COMMENT '主键',
  role_id bigint(20) NOT NULL COMMENT '角色ID',
  menu_id bigint(20) NOT NULL COMMENT '菜单ID',
  PRIMARY KEY (id),
  KEY idx_role_id (role_id) USING BTREE,
  KEY idx_menu_id (menu_id) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='角色菜单关系表';
CREATE TABLE menu_auth_db.tb_menu (
  id bigint(20) NOT NULL COMMENT '主键',
  name varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '名称',
  menu_code varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '菜单编码',
  parent_id bigint(20) DEFAULT NULL COMMENT '父节点',
  node_type tinyint(4) NOT NULL DEFAULT '1' COMMENT '节点类型,1文件夹,2页面,3按钮',
  icon_url varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '图标地址',
  sort int(11) NOT NULL DEFAULT '1' COMMENT '排序号',
  link_url varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '页面对应的地址',
  level int(11) NOT NULL DEFAULT '0' COMMENT '层次',
  path varchar(2500) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '树id的路径 整个层次上的路径id,逗号分隔,想要找父节点特别快',
  is_delete tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否删除 1:已删除;0:未删除',
  PRIMARY KEY (id) USING BTREE,
  KEY idx_parent_id (parent_id) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='菜单表';

三、后端开发

菜单权限模块的数据库设计,一般5张表就可以搞定,真正有点复杂的地方在于数据的写入和渲染,当然如果老板突然让你来开发一套菜单权限系统,我们也没必要慌张,下面,我们一起来看看后端应该如何开发。

3.1、创建项目

为了方便快捷,小编我采用的是springboot+mybatisPlus组件来快速开发,直接利用mybatisPlus官方提供的快速生成代码的demo,一键生成所需的dao、service、web层的代码,结果如下:

84.jpg

3.2、编写菜单添加服务

@Override
public void addMenu(Menu menu) {
    //如果插入的当前节点为根节点,parentId指定为0
    if(menu.getParentId().longValue() == 0){
        menu.setLevel(1);//根节点层级为1
        menu.setPath(null);//根节点路径为空
    }else{
        Menu parentMenu = baseMapper.selectById(menu.getParentId());
        if(parentMenu == null){
            throw new CommonException("未查询到对应的父节点");
        }
        menu.setLevel(parentMenu.getLevel().intValue() + 1);
        if(StringUtils.isNotEmpty(parentMenu.getPath())){
            menu.setPath(parentMenu.getPath() + "," + parentMenu.getId());
        }else{
            menu.setPath(parentMenu.getId().toString());
        }
    }
    //可以使用雪花算法,生成ID
    menu.setId(System.currentTimeMillis());
    super.save(menu);
}

新增菜单比较简单,直接将数据插入即可,需要注意的地方是parent_id、level、path,这三个字段的写入,如果新建的是根节点,默认parent_id为0,方便后续递归遍历。

3.3、编写菜单后端查询服务

  • 新建一个菜单视图实体类
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class MenuVo implements Serializable {
    private static final long serialVersionUID = -4559267810907997111L;
    /**
     * 主键
     */
    private Long id;
    /**
     * 名称
     */
    private String name;
    /**
     * 菜单编码
     */
    private String menuCode;
    /**
     * 父节点
     */
    private Long parentId;
    /**
     * 节点类型,1文件夹,2页面,3按钮
     */
    private Integer nodeType;
    /**
     * 图标地址
     */
    private String iconUrl;
    /**
     * 排序号
     */
    private Integer sort;
    /**
     * 页面对应的地址
     */
    private String linkUrl;
    /**
     * 层次
     */
    private Integer level;
    /**
     * 树id的路径 整个层次上的路径id,逗号分隔,想要找父节点特别快
     */
    private String path;
    /**
     * 子菜单集合
     */
    List<MenuVo> childMenu;
}
  • 编写菜单查询服务,使用递归重新封装菜单视图
@Override
public List<MenuVo> queryMenuTree() {
    Wrapper queryObj = new QueryWrapper<>().orderByAsc("level","sort");
    List<Menu> allMenu = super.list(queryObj);
 // 0L:表示根节点的父ID
    List<MenuVo> resultList = transferMenuVo(allMenu, 0L);
    return resultList;
}
/**
 * 封装菜单视图
 * @param allMenu
 * @param parentId
 * @return
 */
private List<MenuVo> transferMenuVo(List<Menu> allMenu, Long parentId){
    List<MenuVo> resultList = new ArrayList<>();
    if(!CollectionUtils.isEmpty(allMenu)){
        for (Menu source : allMenu) {
            if(parentId.longValue() == source.getParentId().longValue()){
                MenuVo menuVo = new MenuVo();
                BeanUtils.copyProperties(source, menuVo);
                //递归查询子菜单,并封装信息
                List<MenuVo> childList = transferMenuVo(allMenu, source.getId());
                if(!CollectionUtils.isEmpty(childList)){
                    menuVo.setChildMenu(childList);
                }
                resultList.add(menuVo);
            }
        }
    }
    return resultList;
}
  • 编写一个菜单树查询接口,如下:
@RestController
@RequestMapping("/menu")
public class MenuController {
    @Autowired
    private MenuService menuService;
    @PostMapping(value = "/queryMenuTree")
    public List<MenuVo> queryTreeMenu(){
        return menuService.queryMenuTree();
    }
}

为了便于演示,我们先初始化7条数据,如下图:

85.jpg

其中最后三条是按钮类型,等下会用于后端权限控制,接口查询结果如下:


86.jpg

这个服务是针对后端管理界面查询的,会将所有的菜单全部查询出来以便于进行管理,展示结果类似如下图:

87.jpg


这个图片截图于小编正在开发的一个项目,内容可能不一致,但是数据结构基本都是一致的。

相关文章
|
前端开发 安全 数据安全/隐私保护
管理系统的按钮权限控制
【2月更文挑战第11天】
666 0
|
XML JSON 前端开发
官方Lottie库能力增强实现
背景Lottie提供了播放复杂、酷炫动能力画,在移动端被广泛利用。在我们的应用中也被频繁、大量使用。它使用简单,仅需几行代码就能播放设计师设计的动画,帮助开发节省了时间成本。也正因为使用频繁,在使用过程中我们遇到了一些相关的问题。使用Lottie支持加载本地文件播放,也支持远程下载zip,json文件进行播放。这俩者在我们平时开发中都有使用到。本地播放本地播放比较简单。可以直接在xml实现,也可以
2101 0
官方Lottie库能力增强实现
|
Web App开发 缓存 JavaScript
Python:Python语言下载库或包的常见whl文件集合
Python:Python语言下载库或包的常见whl文件集合
Python:Python语言下载库或包的常见whl文件集合
|
数据安全/隐私保护
经典权限系统设计(五张表)
经典权限系统设计(五张表)
|
存储 Java 文件存储
微服务——SpringBoot使用归纳——Spring Boot使用slf4j进行日志记录—— logback.xml 配置文件解析
本文解析了 `logback.xml` 配置文件的详细内容,包括日志输出格式、存储路径、控制台输出及日志级别等关键配置。通过定义 `LOG_PATTERN` 和 `FILE_PATH`,设置日志格式与存储路径;利用 `&lt;appender&gt;` 节点配置控制台和文件输出,支持日志滚动策略(如文件大小限制和保存时长);最后通过 `&lt;logger&gt;` 和 `&lt;root&gt;` 定义日志级别与输出方式。此配置适用于精细化管理日志输出,满足不同场景需求。
3397 1
|
SQL 关系型数据库 MySQL
阿里云的云数据库RDS简介
阿里云关系型数据库RDS(Relational Database Service)是一种安全稳定、高性价比、可弹性伸缩的在线数据库服务。支持MySQL、SQL Server、PostgreSQL和MariaDB引擎,提供容灾、备份、恢复、监控、迁移等全套解决方案,帮助用户轻松应对数据库运维挑战。RDS具备高可用性、高安全性、轻量运维和弹性伸缩等优势,适用于各类业务场景,助力企业降低成本、提升效率。
|
安全 Unix Linux
VMware Workstation 17.6.3 发布下载,现在完全免费无论个人还是商业用途
VMware Workstation 17.6.3 发布下载,现在完全免费无论个人还是商业用途
163221 65
|
Ubuntu Linux 测试技术
Python 虚拟环境配置
本文总结了 Python 开发中的环境配置、常用操作和常见错误处理。重点介绍了如何使用 `virtualenv` 搭建虚拟环境,解决依赖冲突问题,并保持系统环境的干净。同时,详细说明了依赖库的安装与管理方法,包括使用 `pip install` 安装依赖、生成和使用 `requirements.txt` 文件,以及查看 Python 文档和修改环境变量等实用技巧。
1294 60
|
存储 SQL 前端开发
【若依RuoYi-Vue | 项目实战】帝可得后台管理系统(二)
接着上回的【若依RuoYi-Vue | 项目实战】基于若依的帝可得后台管理系统(一),本次我们继续完成人员管理、设备管理、策略管理模块的开发。
2910 6
【若依RuoYi-Vue | 项目实战】帝可得后台管理系统(二)
|
SQL 存储
数据权限就该这么实现(实践篇),yyds!
数据权限就该这么实现(实践篇),yyds!
1577 0

热门文章

最新文章