从0到1构建MES系统3-权限模块

238 阅读5分钟

1. RBAC

今天我们来说说MES的权限模块,首先我们要了解什么叫做RBAC

RBAC(Role-Based Access Control)是一种通过角色作为权限分配中介的访问控制模型,其核心逻辑为:
用户 → 分配角色 → 角色拥有权限 → 权限控制资源访问

关键特征: - 角色抽象:将权限与具体用户解耦,通过角色实现权限的批量管理(如"仓库角色"自动继承所有仓库相关权限) - 最小特权原则:用户仅获得完成工作所需的最小权限集 - 职责分离(SoD):关键操作需多角色共同授权(如审计员与操作员角色互斥)

graph TD

User -->|1:N| Role
Role -->|M:N| Permission
Permission -->|操作+资源| Resource
Session -->|动态绑定| Context

2. 数据库设计

表ER图如下:

Pasted image 20250526135923.png

说明 用户和组织一对一 用户和角色多对多 角色和菜单多对多

2.1 系统用户表

字段名类型是否必填说明
idBIGINTNOT NULL主键ID
org_idBIGINT部门ID
accountVARCHAR(128)NOT NULL账号
passwordVARCHAR(256)NOT NULL密码
nameVARCHAR(128)NOT NULL姓名
genderVARCHAR(16)性别
emailVARCHAR(128)邮件
phoneVARCHAR(32)电话
user_picVARCHAR(128)用户照片
statusINTNOT NULL状态
create_byBIGINT创建人
create_timeDATETIME创建时间
update_byBIGINT更新人
update_timeDATETIME更新时间

2.2 组织架构

字段名类型是否必填说明
idBIGINTNOT NULL
parent_idBIGINT父节点ID
nameVARCHAR(128)NOT NULL名称
descriptionVARCHAR(128)描述
statusINT状态
create_byBIGINT创建人
create_timeDATETIME
update_byBIGINT
update_timeDATETIME

2.3 系统角色

字段名类型是否必填说明
idBIGINTNOT NULL主键ID
codeVARCHAR(128)NOT NULL角色编码
nameVARCHAR(128)NOT NULL角色名称
descriptionVARCHAR(1024)描述
statusINT状态
create_byBIGINT创建人
create_timeDATETIME创建时间
update_byBIGINT更新人
update_timeDATETIME

2.4 菜单信息

字段名类型是否必填说明
idBIGINTNOT NULL主键ID
parent_idBIGINT父ID
labelVARCHAR(128)标题
nameVARCHAR(128)名称
statusVARCHAR(8)NOT NULL状态
snINT排序
typeINT类型
iconVARCHAR(128)图标
componentVARCHAR(256)路由组件
routeVARCHAR(256)路由地址
permissionVARCHAR(256)权限标识
hiddenCHAR(1)隐藏
new_featureBIT(1)新特性
frame_srcVARCHAR(256)内嵌页面
create_byBIGINT
create_timeDATETIME
update_byBIGINT
update_timeDATETIME

说明

  1. 菜单类型分为: 目录、菜单、按钮
  2. permission字段可以给对应的目录、菜单、按钮设置权限码,系统通过当前用户是否有对应的权限码进行相应的隐藏和现实

2.5 用户角色关联

字段名类型是否必填说明
idBIGINTNOT NULL主键ID
role_idBIGINT角色ID
user_idBIGINT用户ID
create_byBIGINT
create_timeDATETIME
update_byBIGINT
update_timeDATETIME

2.6 角色菜单关联

字段名类型是否必填说明
idBIGINTNOT NULL主键
role_idBIGINTNOT NULL角色ID
menu_idBIGINTNOT NULL菜单ID
create_byBIGINT
create_timeDATETIME
update_byBIGINT
update_timeDATETIME

3 关键代码

3.1 菜单数据树形结构处理

//代码路径:src/main/java/com/hgyc/mom/system/service/impl/SysMenuServiceImpl.java

private List<MenuVO> convertToTree(List<SysMenu> menus) {  
    Map<Long, List<MenuVO>> parentChildrenMap = new HashMap<>();  
    for (SysMenu menu : menus) {  
        Long parentId = menu.getParentId();  
  
        MenuVO menuVO = new MenuVO();  
        BeanUtils.copyProperties(menu, menuVO);  
        // 将当前菜单项添加到父菜单项的子菜单项列表中  
        List<MenuVO> children = parentChildrenMap.getOrDefault(parentId, new ArrayList<>());  
        children.add(menuVO);  
        parentChildrenMap.put(parentId, children);  
  
    }  
    // 将根节点的子菜单项列表转换为树形结构  
    List<MenuVO> rootChildren = parentChildrenMap.getOrDefault(0L, new ArrayList<>());  
    for (MenuVO menu : rootChildren) {  
        convertToTree(menu, parentChildrenMap);  
    }  
    return rootChildren;  
}  
  
private void convertToTree(MenuVO parent, Map<Long, List<MenuVO>> parentChildrenMap) {  
    Long parentId = parent.getId();  
    List<MenuVO> children = parentChildrenMap.getOrDefault(parentId, new ArrayList<>());  
    parent.setChildren(children);  
    for (MenuVO child : children) {  
        convertToTree(child, parentChildrenMap);  
    }  
}

前端菜单树的数据结构为树形parent->children, 我们后端查询出来的是表格的数据结构,所以我们要把列表型的数据结构转换成树形的数据结构。

同理,前端左侧菜单是有当前用户所拥有的菜单权限展示的,我们也需要把菜单权限转换成antdesign-menu的数据结构和react-router路由所需的数据结构; 菜单数据渲染

//代码路径
hgyc-mom-web/src/layouts/dashboard/nav/nav-vertical.tsx


//处理菜单权限数据结构
const permissionRoutes = usePermissionRoutes();

...

const menuList = useMemo(() => {
  const menuRoutes = menuFilter(permissionRoutes);
  return routeToMenuFn(menuRoutes);
}, [routeToMenuFn, permissionRoutes]);

...

<Scrollbar>

<Menu
  mode="inline"
  inlineIndent={16}
  items={menuList}
  theme={sidebarTheme}
  selectedKeys={selectedKeys}
  openKeys={openKeys}
  onOpenChange={handleOpenChange}
  className="!border-none [&.ant-menu-inline-collapsed_.ant-menu-item]:!px-2   [&.ant-menu-inline-collapsed_.ant-menu-submenu-title]:!px-2 [&_.ant-menu-item]:!text-[14px] [&_.ant-menu-submenu-title]:!text-[14px]"
  onClick={onClick}

/>

</Scrollbar>

数据转换主要是: menuFilterrouteToMenuFn 这两个方法。

如需详细代码,请下载源码查看。

3.2 按钮权限

前端自定义hook组件

import useAuth from "@/hooks/system/useAuth";

const { hasPermission } = useAuth();

// 使用示例
<div>
{hasPermission(['mes:md:mditem:add']) && (

<Button type="primary" onClick={handleAdd}>新增</Button>

)}
</div>

4. 功能介绍

4.1 组织管理

Pasted image 20250526151219.png 以树形结构展示部门的上下级关系。

4.2 用户管理

Pasted image 20250526151219.png 维护用户的基础信息,点击编辑,用户基本信息以抽屉的方式在右侧弹出

Pasted image 20250526151323.png

4.3 角色管理

角色基本信息维护

Pasted image 20250526151404.png

角色权限配置,点击列表右侧操作栏中的【权限】按钮,会弹出已有的权限菜单,以树形展示,如果已有菜单权限,勾选框会自动勾选

Pasted image 20250526151440.png

分配角色用户,点击列表右侧操作栏中的【分配用户】按钮,抽屉方式展示该角色已分配的用户信息,点击添加按钮可以分配角色用户。

Pasted image 20250526151636.png

4.4 菜单管理

Pasted image 20250526151813.png

菜单类型分为三种: 目录、菜单、按钮。默认新增的是目录菜单。目录菜单设置的路由为该目录下所有菜单的根地址,路由跳转会自动带上 根路由+菜单路由

Pasted image 20250526152107.png

说明:

  1. 路由:访问菜单浏览器显示的地址
  2. 组件路径:访问该页面代码的页面的路径
  3. 权限标识:配置权限字符,在按钮或接口访问,如果没有权限字符会提示:权限不足

开源项目地址

欢迎在评论区分享你的技术选型经验,或对本文方案的改进建议!