1.简介在实际项目开发过程中,菜单权限功能可以说是后台管理不可或缺的一部分系统。根据业务的复杂度,设计时可深可浅,但无论怎么变,设计思路基本上都是围绕着用户、角色、菜单展开相应的展开。今天,小编就来和大家一起探讨一下,如何设计一套可以精确到按钮级别的菜单权限功能。话不多说,直接打开!2、我们先来看看数据库的设计。ER图如下:其中,用户和角色的关系是多对多的,角色和菜单的关系也是多对多的。用户通过角色与菜单相关联。与菜单相关联,可以直接将菜单权限控制到用户级别,但这不是问题,这个也可以扩展。用户和角色表相对简单。接下来重点介绍菜单表的设计,如下:可以看到,整个菜单表是一个树形结构,关键字段解释:menu_code:菜单代码,用于后端权限控制parent_id:菜单父节点的ID,方便递归遍历菜单,可以为空level:菜单树的层级,为了查询指定层级的菜单路径:树id的路径,主要用来存放当前根节点到父节点的路径树,用逗号隔开,查找父节点会很快。Fortheconvenienceoflaterdevelopment,wefirstcreate一个名为menu_auth_db的数据库,初始脚本如下:CREATEDATABASEIFNOTEXISTSmenu_auth_dbdefaultcharsetutf8mb4COLLATEutf8mb4_unicode_ci;CREATETABLEmenu_auth_db.tb_user(idbigint(20)unsignedNOTNULLCOMMENT'消息给过来的ID',mobilevarchar(20)NOTNULLDEFAULT''COMMENT'手机号',namevarchar(100)NOTNULLDEFAULT''COMMENT'name',passwordvarchar(128)NOTNULLDEFAULT''COMMENT'密码',is_deletetinyint(4)NOTNULLDEFAULT'0'COMMENT'被删除1:删除;0:未删除',PRIMARYKEY(id),keyidx_name(name)使用btree,keyidx_mobile(mobile)使用btree)egine=innodbdefeaultchareet=utf8mb4collat??e=utf8mb4_unicode_cicomment='';',role_idbigint(20)NOTNULL评论'角色ID',primarykey(id),keyIdx_user_id(user_id)使用btree,keyidx_role_id(prole_id)使用btree)egine=innodbdefeaultcharse=utf8mb4collat??e=utf8mb4collat??e=utf8mb4_unicode_cicommentcodevarchar(100)NOTNULLDEFAULT''COMMENT'encoding',namevarchar(100)NOTNULLDEFAULT''COMMENT'name',is_deletetinyint(4)NOTNULLDEFAULT'0'COMMENT'被删除1:删除;0:未删除',PRIMARYKEY(id),keyIdx_code(代码)使用btree,keyidx_name(name)使用btree)egine=innodbdefaultcharset=utf8mb4collat??e=utf8mb4b4b4b4b4cicomment='un_uniCode_un_un_'角色(20)使用btree,keyidx_role_id(reo_id)的notNullComment'菜单ID',keyidx_role_id(reo_id),keyidx_menu_id(菜单_id)使用bbtree)egine=innodbdefeault=utf8mb4collat??e=utf8mb4collat??e=utf8mb4_unicode_cicode_cicod_cicig_cicment='NULLCOMMENT'主键',namevarchar(100)COLLATEutf8mb4_unicode_ciNOTNULLDEFAULT''COMMENT'NAME',menu_codevarchar(100)COLLATEutf8mb4_unicode_ciNOTNULLDEFAULT''COMMENT'菜单代码',parent_idbigint(20)DEFAULTNULLCOMMENT'父节点',node_typetinyint(4)NOTNULDEFAULT'1'COMMENT'节点类型,1个文件夹,2页,3buttons',icon_urlvarchar(255)COLLATEutf8mb4_unicode_ciDEFAULT''COMMENT'图标地址',sortint(11)NOTNULLDEFAULT'1'COMMENT'排序编号',link_urlvarchar(500)COLLATEutf8mb4_unicode_ciDEFAULT''COMMENT'页面对应地址',levelint(11)NOTNULLDEFAULT'0'COMMENT'level',pathvarchar(2500)COLLATEutf8mb4_unicode_ciDEFAULT''COMMENT'treeid'spathidontheentirelevelofpath,用逗号分隔,找到父节点非常快',is_deletetinyint(4)NOTNULLDEFAULT'0'COMMENT'删除1:删除;0:未删除',PRIMARYKEY(id)USINGBTREE,KEYidx_parent_id(parent_id)USINGBTREE)ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COLLATE=utf8mb4_unicode_ciCOMMENT='菜单表';3.后台开发菜单权限模块的数据库设计,一般5个表就可以搞定。真正复杂的部分是数据的写入和渲染。当然,如果老板突然让你开发菜单权限系统,我们也不必慌张。接下来,我们来看看后端应该如何开发3.1.为了方便快捷的创建项目,我使用springboot+mybatisPlus组件进行快速开发。我直接使用mybatisPlus提供的demo快速生成代码,一键生成需要的dao、service、web层代码。结果如下:3.2、writemenuaddservice@OverridepublicvoidaddMenu(Menumenu){//如果当前插入的节点是根节点,则指定parentId为0if(menu.getParentId().longValue()==0){menu.setLevel(1);//根节点级别为1menu.setPath(null);//根节点路径为空}else{MenuparentMenu=baseMapper.selectById(menu.getParentId());if(parentMenu==null){thrownewCommonException("没有查询到对应的父节点");}menu.setLevel(parentMenu.getLevel().intValue()+1);if(StringUtils.isNotEmpty(parentMenu.getPath())){menu.setPath(parentMenu.getPath()+","+parentMenu.getId());}else{menu.setPath(parentMenu.getId().toString());}}//可以使用雪花算法生成IDmenu.setId(System.currentTimeMillis());super.save(menu);}添加新菜单比较简单,直接插入数据即可,注意的地方是parent_id,level,path,这三个字段都要写,如果新建的是根节点,默认parent_id为0,方便后续递归遍历。3.3.写一个菜单后端查询服务新建一个菜单视图实体类@Data@EqualsAndHashCode(callSuper=false)@Accessors(chain=true)publicclassMenuVoimplementsSerializable{privatestaticfinallongserialVersionUID=-4559267810907997111L;/***主键*/privateLongid;/***名称*/privateStringname;/***菜单代码*/privateStringmenuCode;/***父节点*/privateLongparentId;/***节点类型,1文件夹,2页面,3按钮*/privateIntegernodeType;/***图标地址*/privateStringiconUrl;/***sortnumber*/privateIntegersort;/***页面对应地址*/privateStringlinkUrl;/***level*/privateIntegerlevel;/***整层树id路径上的路径id,逗号分隔,我想很快找到父节点*/privateStringpath;/***子菜单集合*/ListchildMenu;}写一个菜单查询服务,使用递归重新封装菜单视图@OverridepublicListqueryMenuTree(){WrapperqueryObj=newQueryWrapper<>().orderByAsc("level","sort");List