天天看点

SaaS-HRM(5)系统用户权限设计(角色管理、权限和资源管理)1、角色管理2、权限和资源管理3、分配角色4、分配权限

在上一篇中,我们已经完成了用户的增删改查操作,同时也创建了角色和权限相关的数据库表,而且也创建好了相关实体类。这次我们来完成角色和权限以及资源的相关操作。

1、角色管理

角色管理的操作和用户管理差不多,所以我们首先要完成后端的CRUD操作。

1.1 后端代码

1.1.1 持久化dao

在模块ihrm_system中的dao包下,创建RoleDao接口:

package com.zdw.ihrm.system.dao;

import com.zdw.ihrm.domain.system.Role;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

public interface RoleDao extends JpaRepository<Role,String>, JpaSpecificationExecutor<Role> {
}
           

1.1.2 业务逻辑层service

在模块ihrm_system中的service包下,创建RoleService类:

package com.zdw.ihrm.system.service;

import com.ihrm.common.service.BaseService;
import com.ihrm.common.utils.IdWorker;
import com.zdw.ihrm.domain.system.Role;
import com.zdw.ihrm.system.dao.RoleDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class RoleService extends BaseService {
    @Autowired
    private RoleDao roleDao;
    @Autowired
    private IdWorker idWorker;

    //保存角色
    public void save(Role role){
        role.setId(idWorker.nextId()+"");//设置id
        roleDao.save(role);
    }
    //修改角色
    public void update(Role role){
        Role target = roleDao.getOne(role.getId());//根据角色id查询角色信息
        target.setDescription(role.getDescription());
        target.setName(role.getName());
        roleDao.save(target);
    }

    //根据id查询角色
    public Role findById(String id){
        return roleDao.findById(id).get();
    }

    //查询所有角色列表
    public List<Role> findAll(String companyId){
        return roleDao.findAll(getSpec(companyId));//调用父类的方法构造根据企业id查询的条件,然后查询角色信息
    }
    //根据id删除角色
    public void deleteById(String id){
        roleDao.deleteById(id);
    }

    //查询角色列表(根据条件分页查询)
    public Page<Role> findByPage(String companyId, int page, int size) {
        return roleDao.findAll(getSpec(companyId), PageRequest.of(page-1, size));
    }

}
           

1.1.3 业务逻辑层controller

在模块ihrm_system中的controller包下,创建RoleController类:

package com.zdw.ihrm.system.controller;

import com.ihrm.common.controller.BaseController;
import com.ihrm.common.entity.PageResult;
import com.ihrm.common.entity.Result;
import com.ihrm.common.entity.ResultCode;
import com.zdw.ihrm.domain.system.Role;
import com.zdw.ihrm.domain.system.response.RoleResult;
import com.zdw.ihrm.system.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@CrossOrigin//解决跨域问题
@RestController
@RequestMapping("sys")
public class RoleController extends BaseController {
    @Autowired
    private RoleService roleService;

    //保存角色
    @RequestMapping(value = "/role",method = RequestMethod.POST)
    public Result save(@RequestBody Role role){
        role.setCompanyId(parseCompanyId());//设置角色的公司id
        roleService.save(role);
        return new Result(ResultCode.SUCCESS);
    }
    //修改角色
    @RequestMapping(value = "/role/{id}", method = RequestMethod.PUT)
    public Result update(@PathVariable("id") String id,@RequestBody Role role){
        roleService.update(role);
        return Result.SUCCESS();
    }

    //根据id查询角色
    @RequestMapping(value = "/role/{id}", method = RequestMethod.GET)
    public Result findById(@PathVariable("id") String id){
        Role role = roleService.findById(id);
        RoleResult roleResult = new RoleResult(role);
        return new Result(ResultCode.SUCCESS,role);
    }

    //查询所有角色
    @RequestMapping(value="/role/list" ,method=RequestMethod.GET)
    public Result findAll(){
        List<Role> roleList = roleService.findAll(parseCompanyId());//根据公司id查询所有角色
        return new Result(ResultCode.SUCCESS,roleList);
    }

    //根据id删除角色
    @RequestMapping(value = "/role/{id}", method = RequestMethod.DELETE)
    public Result deleteById(@PathVariable("id") String id){
        roleService.deleteById(id);
        return Result.SUCCESS();
    }

    //分页查询角色列表
    @RequestMapping(value = "/role", method = RequestMethod.GET)
    public Result findByPage(int page,int pagesize,Role role){
        Page<Role> searchPage = roleService.findByPage(parseCompanyId(), page, pagesize);//根据公司id分页查询
        PageResult<Role> pr = new PageResult(searchPage.getTotalElements(),searchPage.getContent());//构造分页结果对象
        return new Result(ResultCode.SUCCESS,pr);
    }
}
           

1.2 前端代码

1.2.1 创建用户管理模块

在前端工程的src目录下创建模块:module-settings ,并创建相关目录和文件

SaaS-HRM(5)系统用户权限设计(角色管理、权限和资源管理)1、角色管理2、权限和资源管理3、分配角色4、分配权限

1.2.2 注册模块

在前端工程的main.js中,将刚才新建的模块注册:

import settings from '@/module-settings/' // 把角色管理模块注册到项目中
Vue.use(settings, store)  // 把角色管理模块注册到项目中
           

1.2.3 API方法

在src/base下面创建role.js文件,里面编写相关API,请求后台方法:

import {createAPI} from '@/utils/request'
import { get } from 'https';

export const list = data => createAPI('/sys/role', 'get', data)
export const simple = data => createAPI('/sys/role/simple', 'get', data)
export const add = data => createAPI('/sys/role', 'post', data)
export const update = data => createAPI(`/sys/role/${data.id}`, 'put', data)
export const remove = data => createAPI(`/sys/role/${data.id}`, 'delete', data)
export const detail = data => createAPI(`/sys/role/${data.id}`, 'get', data)
export const assignPrem = data => createAPI(`/sys/role/assignPrem`, 'put', data)
export const findAll = data => createAPI(`/sys/role/list`,'get',data)
           

1.2.4 配置路由

module-setttings模块的router/index.js文件中,配置路由:

import Layout from '@/module-dashboard/pages/layout'
const _import = require('@/router/import_' + process.env.NODE_ENV)

export default [
  {
    root: true,
    path: '/settings',
    component: Layout,
    redirect: 'noredirect',
    name: 'settings',
    meta: {
      title: '公司设置管理',
      icon: 'set'
    },
    children: [
      {
        path: 'index',
        component: _import('settings/pages/index'),
        name: 'settings-index',
        meta: {title: '公司设置', icon: 'set', noCache: true}
      }
    ]
  }
]
           

1.2.5 注册路由

在module-settings模块根目录的index.js下面,注册路由:

// vue-router
import {asyncRouterMap} from '@/router'
import routerMaps from './router'
// vuex
import app from './store/app'

export default {
  install(module, store) {
    // 注册路由
    asyncRouterMap.push(routerMaps[0])
    // 注册状态管理
    if (store !== undefined) {
      // store.registerModule('app', app)
    }
  }
}
           

1.2.6 index.vue视图

pages下面的index.vue,是整个角色相关的列表:

<template>
  <div class="settingBox">
    <div class="settingTop">
      <div class="setTop">
        <el-tabs v-model="activeName" class="topLab">
            <el-tab-pane name="role" class="rInfo">
              <span slot="label">角色管理</span>
              <component v-bind:is="roleList"></component>
            </el-tab-pane>
            <el-tab-pane name="companyInfo" class="rInfo">
              <span slot="label">公司信息</span>
              <component v-bind:is="roleList"></component>
            </el-tab-pane>
        </el-tabs>
      </div>
    </div>
  </div>
</template>

<script>
import roleList from './../components/role-list'
export default {
  name: 'settings-table-index',
  components: { roleList },
  data() {
    return {
      roleList: 'roleList',
      activeName: 'role'
    }
  },
  methods: {
 
  }
}
</script>
<style>
.disabled .el-upload--picture-card {
  display: none !important;
}
</style>
<style rel="stylesheet/scss"  scoped>
@import './../../styles/variables.scss';
.settingBox {
  padding: 20px;
  background: #fff;
  border-radius: 3px;
  margin: 15px;
  border: 1px solid #ebeef5;
  .setTop {
    color: #666;
    //border-bottom: solid 1px #ccc;
    line-height: 40px;
    span {
      display: inline-block;
      padding: 0 25px;
      font-size: 18px;
      cursor: pointer;
    }
    .act {
      color: $green;
      border-bottom: solid 2px $green;
    }
  }
  .settingCont {
    background: #fff;
    .tips {
      background: #f4f4f4;
      color: #666;
      margin: 10px 0;
      padding: 0 10px;
      line-height: 30px;
      i {
        margin-right: 5px;
        position: relative;
        top: 1px;
        color: $orange;
      }
    }
    .formList {
    }
    .jurisdictionSet {
      position: relative;
      .addAdmin {
        text-align: right;
        line-height: 40px;
        position: relative;
        top: -5px;
        i {
          border: solid 1px #666;
          color: #666;
          display: inline-block;
          margin-right: 5px;
          border-radius: 3px;
          padding: 0 3px 2px 3px;
          line-height: 12px;
        }
      }
      .jurTabLab {
        line-height: 40px;
        span {
          display: inline-block;
          margin-right: 20px;
          cursor: pointer;
        }
        .act {
          color: $green;
        }
      }
      .jurContent {
        .name-wrapper {
          span {
            display: inline-block;
            border: solid 1px #ccc;
            margin-right: 10px;
            padding: 0 10px;
            border-radius: 3px;
            background: #f4f4f4;
          }
        }
      }
    }
  }
  .inputWt {
    width: 400px;
  }
}
</style>
           

1.2.7 角色列表组件

在index.vue中,用到了组件:

<span slot="label">角色管理</span>
<component v-bind:is="roleList"></component>

import roleList from './../components/role-list'
           

该组件位于components目录下的role-list.vue,具体如下:

<template>
  <div class="boxInfo">
    <!-- 表单内容 -->
    <div class="formInfo">
      <div>
        <!-- 头部信息  -->
        <div class="userInfo">
          <el-button type="primary" size="mini" icon="el-icon-plus" @click="handlerAdd">新增角色</el-button>
          <el-table :data="dataList" border fit highlight-current-row style="width:100%; margin-top:10px;">
                <el-table-column type="index" :index="1" label="序号" width="150"> </el-table-column>
                <el-table-column sortable prop="name" label="角色名" width="150"></el-table-column>
                <el-table-column sortable prop="description" label="描述"></el-table-column>
                <el-table-column fixed="right" label="操作" align="center" width="250">
                  <template slot-scope="scope">
                    <el-button @click="handlerPerm(scope.row)" type="text" size="small">分配权限</el-button>
                    <el-button @click="handleUpdate(scope.row)" type="text" size="small">修改</el-button>
                    <el-button @click="handleDelete(scope.row)" type="text" size="small">删除</el-button>
                  </template>
                </el-table-column>
          </el-table>
          <div class="pagination">
            <PageTool :paginationPage="requestParameters.page" :paginationPagesize="requestParameters.pagesize" :total="counts" @pageChange="handleCurrentChange" @pageSizeChange="handleSizeChange">
            </PageTool>
          </div>
        </div>
      </div>
    </div>
    <el-dialog title="编辑角色" :visible.sync="dialogFormVisible" style="hight:100px;line-height:1px">
      <el-form :model="formData" label-width="90px" style="margin-top:20px">
        <el-form-item label="角色名称">
          <el-input v-model="formData.name" autocomplete="off" style="width:90%"></el-input>
        </el-form-item>
        <el-form-item label="角色描述">
          <el-input v-model="formData.description" autocomplete="off" style="width:90%"></el-input>
        </el-form-item>    
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="saveOrUpdate">确 定</el-button>
      </div>
    </el-dialog>
    <el-dialog :title="'为【'+formData.name+'】分配权限'" :visible.sync="permFormVisible" style="hight:100px;line-height:1px">
      <el-tree
        :data="treeData"
        default-expand-all	
        show-checkbox
        node-key="id"
        ref="tree"
        :check-strictly="true"
        :default-checked-keys="checkNodes"
        :props="{label:'name'}">
      </el-tree>
      <div slot="footer" class="dialog-footer">
        <el-button @click="permFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="assignPrem">确 定</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import {list,add,update,remove,detail,assignPrem} from "@/api/base/role"
import * as permApi from "@/api/base/permissions"
import commonApi from "@/utils/common"
import PageTool from './../../components/page/page-tool'
var _this = null
export default {
  name: 'roleList',
  components: {PageTool},
  props: ['objId'],
  data() {
    return {
      formData:{},
      treeData:[],
      checkNodes:[],
      dialogFormVisible: false,
      permFormVisible:false,
      dataList:[],
      counts:0,
      requestParameters:{
        page: 1,
        pagesize: 10
      }    
    }
  },
  methods: {
    assignPrem() {
      assignPrem({id:this.formData.id,permIds:this.$refs.tree.getCheckedKeys()}).then(res => {
         this.$message({message:res.data.message,type:res.data.success?"success":"error"});
         this.permFormVisible=false
      })
    },
    handlerPerm(obj) {
       detail({id:obj.id}).then(res=>{
         this.formData = res.data.data
         this.checkNodes = res.data.data.permIds
          permApi.list({type:0,pid:null,enVisible:1}).then(res => {
            this.treeData = commonApi.transformTozTreeFormat(res.data.data)
            this.permFormVisible=true
          })
       })
    },
    handlerAdd() {
      this.formData={}
      this.dialogFormVisible = true
    },
    handleDelete(obj) {
      this.$confirm(
        `本次操作将删除${obj.name},删除后角色将不可恢复,您确认删除吗?`
      ).then(() => {
          remove({id: obj.id}).then(res => {
              this.$message({message:res.data.message,type:res.data.success?"success":"error"});
              this.doQuery()
          })
      })
    },
    handleUpdate(obj) {
      detail({id:obj.id}).then(res=>{
        this.formData = res.data.data;
        this.formData.id = obj.id;
        this.dialogFormVisible = true
      })
    },
    saveOrUpdate() {
      if(this.formData.id == null || this.formData.id == undefined) {
          this.save()
      }else{
          this.update();
      }
    },
    update(){
      update(this.formData).then(res=>{
        this.$message({message:res.data.message,type:res.data.success?"success":"error"});
        if(res.data.success){
          this.formData={};
          this.dialogFormVisible=false;
          this.doQuery();
        }
      })
    },
    save() {
      add(this.formData).then(res=>{
        this.$message({message:res.data.message,type:res.data.success?"success":"error"});
        if(res.data.success){
          this.formData={};
          this.dialogFormVisible=false;
          this.doQuery();
        }
      })
    },
    // 获取详情
    doQuery() {
      list(this.requestParameters).then(res => {
          this.dataList = res.data.data.rows
          this.counts = res.data.data.total
        })
    },
    // 每页显示信息条数
    handleSizeChange(pageSize) {
      this.requestParameters.pagesize = pageSize
      if (this.requestParameters.page === 1) {
        _this.doQuery(this.requestParameters)
      }
    },
    // 进入某一页
    handleCurrentChange(val) {
      this.requestParameters.page = val
      _this.doQuery()
    },
  },
  // 挂载结束
  mounted: function() {},
  // 创建完毕状态
  created: function() {
    _this = this
    this.doQuery()
  },
  // 组件更新
  updated: function() {}
}
</script>

<style rel="stylesheet/scss" >
.el-collapse-item__arrow {
  float: left;
}

.el-collapse-item {
  position: relative;
  // width: 80%;
  // .el-collapse-item__header{width: 80%;}
  .infoR {
    position: absolute;
    background: #fff;
    display: inline-block;
    width: 100px;
    height: 35px;
    line-height: 35px;
    text-align: right;
    right: -100px;
    top: 0px;
  }
}
// .el-input--medium {
//   width: 80%;
// }
.linkage {
  display: inline-block;
}
.textBotm {
  vertical-align: text-bottom;
}
.navInfo {
  height: auto;
  font-size: 30px;
  color: #333;
  background-color: #e4e4e4;
  text-align: center;
  border-bottom: 1px solid #333;
}
.step {
  position: fixed;
  left: 220px;
  top: 50%;
  margin-top: -150px;
  background: #fff;
  z-index: 9;
}
</style>

<style rel="stylesheet/scss"  scoped>
</style>
           

注意://import * as permApi from "@/api/base/permissions"  这一行注释掉,因为这里要用到权限管理模块,而我们还没创建该模块,如果不注释这行会报错。等完善了权限管理之后再放开。

1.3 前后端联合测试

上面都完成之后,我们可以启动后端的企业和权限微服务,启动前端项目,然后访问,看是否都正常,然后可以进行角色的增上改查操作了。

2、权限和资源管理

2.1 需求分析

完成权限(菜单,按钮(权限点),API接口)的基本操作

SaaS-HRM(5)系统用户权限设计(角色管理、权限和资源管理)1、角色管理2、权限和资源管理3、分配角色4、分配权限

      权限与菜单,菜单与按钮,菜单与API接口都是一对一关系。为了方便操作,在SAAS-HRM系统的表设计中,采用基于共享主键的形式实现一对一关系维护,并且数据库约束,一切的关系维护需要程序员在代码中实现。

2.2 后端实现

相关的实体类,我们在之前就已经实现啦,数据库表的创建也已经实现。

2.2.1 持久层Dao

这里需要在ihrm_system模块的dao包下,创建权限和资源相关的四个dao,分别如下:

package com.zdw.ihrm.system.dao;

import com.zdw.ihrm.domain.system.Permission;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

public interface PermissionDao extends JpaRepository<Permission,String>, JpaSpecificationExecutor<Permission> {
}
           
package com.zdw.ihrm.system.dao;

import com.zdw.ihrm.domain.system.PermissionMenu;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

public interface PermissionMenuDao extends JpaRepository<PermissionMenu,String>, JpaSpecificationExecutor<PermissionMenu> {
}
           
package com.zdw.ihrm.system.dao;

import com.zdw.ihrm.domain.system.PermissionPoint;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

public interface PermissionPointDao extends JpaRepository<PermissionPoint,String>, JpaSpecificationExecutor<PermissionPoint> {
}
           
package com.zdw.ihrm.system.dao;

import com.zdw.ihrm.domain.system.PermissionApi;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

public interface PermissionApiDao extends JpaRepository<PermissionApi,String>, JpaSpecificationExecutor<PermissionApi> {
}
           

2.2.2 业务逻辑service

在ihrm_system模块的service包下,创建权限和资源相关的PermissionService:

首先在模块ihrm_common中的util包下,添加两个工具类,用来把map转成bean,把bean转成map:

package com.ihrm.common.utils;

import org.springframework.cglib.beans.BeanMap;

import java.util.HashMap;
import java.util.Map;

public class BeanMapUtils {

    /**
     * 将对象属性转化为map结合
     */
    public static <T> Map<String, Object> beanToMap(T bean) {
        Map<String, Object> map = new HashMap<>();
        if (bean != null) {
            BeanMap beanMap = BeanMap.create(bean);
            for (Object key : beanMap.keySet()) {
                map.put(key+"", beanMap.get(key));
            }
        }
        return map;
    }

    /**
     * 将map集合中的数据转化为指定对象的同名属性中
     */
    public static <T> T mapToBean(Map<String, Object> map,Class<T> clazz) throws Exception {
        T bean = clazz.newInstance();
        BeanMap beanMap = BeanMap.create(bean);
        beanMap.putAll(map);
        return bean;
    }
}
           
package com.ihrm.common.utils;

public class PermissionConstants {
    /**
     * 权限类型 1为菜单 2为功能 3为API
     */
    public static final int PY_MENU = 1;
    public static final int PY_POINT = 2;
    public static final int PY_API = 3;
}
           

PermissionService内容如下:

package com.zdw.ihrm.system.service;

import com.ihrm.common.entity.ResultCode;
import com.ihrm.common.exception.CommonException;
import com.ihrm.common.utils.BeanMapUtils;
import com.ihrm.common.utils.IdWorker;
import com.ihrm.common.utils.PermissionConstants;
import com.zdw.ihrm.domain.system.Permission;
import com.zdw.ihrm.domain.system.PermissionApi;
import com.zdw.ihrm.domain.system.PermissionMenu;
import com.zdw.ihrm.domain.system.PermissionPoint;
import com.zdw.ihrm.system.dao.PermissionApiDao;
import com.zdw.ihrm.system.dao.PermissionDao;
import com.zdw.ihrm.system.dao.PermissionMenuDao;
import com.zdw.ihrm.system.dao.PermissionPointDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Service
@Transactional  //涉及到多表操作,添加事务注解
public class PermissionService {
    @Autowired
    private PermissionDao permissionDao;
    @Autowired
    private PermissionMenuDao permissionMenuDao;
    @Autowired
    private PermissionPointDao permissionPointDao;
    @Autowired
    private PermissionApiDao permissionApiDao;
    @Autowired
    private IdWorker idWorker;

    /**
     * 保存权限及其资源
     * @param map  map里面有权限及其对应的资源信息
     */
    public void save(Map<String,Object> map) throws Exception {
        //设置主键的值
        String id = idWorker.nextId()+"";
        //调用工具类代码,把map中的值封装到Permission对象中
        Permission permission = BeanMapUtils.mapToBean(map, Permission.class);
        permission.setId(id);
        Integer type = permission.getType();
        //根据权限类型判断,把map封装到对应的资源对象中,并保存
        switch (type){
            case PermissionConstants.PY_MENU :
                PermissionMenu menu = BeanMapUtils.mapToBean(map, PermissionMenu.class);
                menu.setId(id);
                permissionMenuDao.save(menu);
                break ;
            case PermissionConstants.PY_POINT :
                PermissionPoint point = BeanMapUtils.mapToBean(map, PermissionPoint.class);
                point.setId(id);
                permissionPointDao.save(point);
                break ;
            case PermissionConstants.PY_API :
                PermissionApi api = BeanMapUtils.mapToBean(map, PermissionApi.class);
                api.setId(id);
                permissionApiDao.save(api);
                break ;
            default:
                throw new CommonException(ResultCode.FAIL);
        }
        //保存权限信息
        permissionDao.save(permission);
    }

    //修改权限
    public void update(Map<String,Object> map) throws Exception {
        //调用工具类代码,把map中的值封装到Permission对象中
        Permission perm = BeanMapUtils.mapToBean(map, Permission.class);
        //根据id查询权限信息
        Optional<Permission> optional = permissionDao.findById(perm.getId());
        if(optional.isPresent()){
            Permission permission = optional.get();
            permission.setName(perm.getName());
            permission.setCode(perm.getCode());
            permission.setDescription(perm.getDescription());
            permission.setEnVisible(perm.getEnVisible());

            Integer type = permission.getType();
            //根据权限类型判断,把map封装到对应的资源对象中,并保存
            switch (type){
                case PermissionConstants.PY_MENU :
                    PermissionMenu menu = BeanMapUtils.mapToBean(map, PermissionMenu.class);
                    menu.setId(permission.getId());
                    permissionMenuDao.save(menu);
                    break ;
                case PermissionConstants.PY_POINT :
                    PermissionPoint point = BeanMapUtils.mapToBean(map, PermissionPoint.class);
                    point.setId(permission.getId());
                    permissionPointDao.save(point);
                    break ;
                case PermissionConstants.PY_API :
                    PermissionApi api = BeanMapUtils.mapToBean(map, PermissionApi.class);
                    api.setId(permission.getId());
                    permissionApiDao.save(api);
                    break ;
                default:
                    throw new CommonException(ResultCode.FAIL);
            }
            //保存权限信息
            permissionDao.save(permission);
        }else{
            throw new CommonException((ResultCode.FAIL));
        }
    }

    //根据id查询
    public Map<String, Object> findById(String id) throws Exception {
        Optional<Permission> optional = permissionDao.findById(id);
        if(optional.isPresent()){
            Permission permission = optional.get();
            Integer type = permission.getType();
            Object object = null;
            if(type==PermissionConstants.PY_MENU){
                object = permissionMenuDao.findById(id).get();
            }else if(type==PermissionConstants.PY_POINT){
                object = permissionPointDao.findById(id).get();
            }else if(type==PermissionConstants.PY_API){
                object = permissionApiDao.findById(id).get();
            }else {
                throw new CommonException(ResultCode.FAIL);
            }
            Map<String, Object> map = BeanMapUtils.beanToMap(object);//把资源对象转成map
            //把权限相关的属性也放到map中
            map.put("name",permission.getName());
            map.put("type",permission.getType());
            map.put("code",permission.getCode());
            map.put("description",permission.getDescription());
            map.put("pid",permission.getPid());
            map.put("enVisible",permission.getEnVisible());
            return map;
        }else{
            throw new CommonException(ResultCode.FAIL);
        }
    }
    //根据id删除
    public void deleteById(String id) throws CommonException {
        //1.通过传递的权限id查询权限
        Optional<Permission> optional = permissionDao.findById(id);
        if(optional.isPresent()){
            Permission permission = optional.get();
            //删除权限信息
            permissionDao.delete(permission);
            //根据权限类型,查询对应的权限资源信息
            Integer type = permission.getType();
            if(type==PermissionConstants.PY_MENU){
                permissionMenuDao.deleteById(id);
            }else if(type==PermissionConstants.PY_POINT){
                permissionPointDao.deleteById(id);
            }else if(type==PermissionConstants.PY_API){
                permissionApiDao.deleteById(id);
            }else {
                throw new CommonException(ResultCode.FAIL);
            }
        }else{
            throw new CommonException(ResultCode.FAIL);
        }
    }

     /**
       * 4.根据条件查询全部,这些条件都封装到了参数map中
       * type      : 查询全部权限列表type:0:菜单 + 按钮(权限点) 1:菜单2:按钮(权限点)3:API接口
       * enVisible : 0:查询所有saas平台的最高权限,1:查询企业的权限
       * pid       :父id
       */
     public List<Permission> findAll(Map<String,Object> map){
        //创建条件查询对象
         Specification<Permission> specification = new Specification<Permission>() {
             //动态拼装查询条件
             @Override
             public Predicate toPredicate(Root<Permission> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                 List<Predicate> list = new ArrayList<>();
                 //根据父id查询
                 String pid = (String)map.get("pid");
                 if(!StringUtils.isEmpty(pid)){
                     list.add(criteriaBuilder.equal(root.get("pid").as(String.class),pid));
                 }
                 //根据enVisible查询
                 String enVisible = (String)map.get("enVisible");
                 if(!StringUtils.isEmpty(enVisible)){
                     list.add(criteriaBuilder.equal(root.get("enVisible").as(String.class),enVisible));
                 }
                 //根据type查询
                 String type = (String)map.get("type");
                 if(!StringUtils.isEmpty(type)){
                     CriteriaBuilder.In<Object> in = criteriaBuilder.in(root.get("type"));//创建根据type的in对象
                     if("0".equals(type)){//type=0,查询0:菜单 + 按钮(权限点),菜单是1,按钮是2
                         in.value(1).value(2);
                     }else{
                         in.value(Integer.valueOf(type));
                     }
                 }
                 return criteriaBuilder.and(list.toArray(new Predicate[list.size()]));
             }
         };
         return permissionDao.findAll(specification);
     }
}
           

2.2.3 控制器controller

在ihrm_system的controller包下,创建PermissionController:

package com.zdw.ihrm.system.controller;

import com.ihrm.common.entity.Result;
import com.ihrm.common.entity.ResultCode;
import com.ihrm.common.exception.CommonException;
import com.zdw.ihrm.domain.system.Permission;
import com.zdw.ihrm.system.service.PermissionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

@CrossOrigin
@RestController
@RequestMapping("sys")
public class PermissionController {
    @Autowired
    private PermissionService permissionService;

    //保存
    @RequestMapping(value = "/permission", method = RequestMethod.POST)
    public Result save(@RequestBody Map<String,Object> map) throws Exception {
        permissionService.save(map);
        return Result.SUCCESS();
    }

    //修改
    @RequestMapping(value = "/permission/{id}", method = RequestMethod.PUT)
    public Result update(@PathVariable("id") String id,@RequestBody Map<String,Object> map) throws Exception {
        map.put("id",id);
        permissionService.update(map);
        return Result.SUCCESS();
    }
    //根据id查询
    @RequestMapping(value = "/permission/{id}", method = RequestMethod.GET)
    public Result findById(@PathVariable("id") String id) throws Exception {
        Map<String, Object> map = permissionService.findById(id);
        return new Result(ResultCode.SUCCESS,map);
    }

    //根据id删除
    @RequestMapping(value = "/permission/{id}", method = RequestMethod.DELETE)
    public Result deleteById(@PathVariable("id") String id) throws CommonException {
        permissionService.deleteById(id);
        return Result.SUCCESS();
    }

    //根据条件查询所有
    @RequestMapping(value = "/permission", method = RequestMethod.GET)
    public Result findAll(@RequestParam Map<String,Object> map){
        List<Permission> list = permissionService.findAll(map);
        return new Result(ResultCode.SUCCESS,list);
    }
}
           

2.3 前端实现

2.3.1 创建权限相关模块

在前端代码的src下面创建模块:module-permissions

SaaS-HRM(5)系统用户权限设计(角色管理、权限和资源管理)1、角色管理2、权限和资源管理3、分配角色4、分配权限

2.3.2 注册模块

在前端工程的main.js里面注册模块:

import permissions from '@/module-permissions/' // 把权限管理模块注册到项目中

Vue.use(permissions, store)  // 把权限管理模块注册到项目中
           

注意:把module-settings模块的 role-list.vue中的注释掉的这行代码放开:

//import * as permApi from "@/api/base/permissions"
           

2.3.3 API方法

在src/base目录下新建permissions.js,里面封装请求后台的方法:

import {createAPI} from '@/utils/request'

const api = "/sys/permission"
export const list = data => createAPI(`${api}`, 'get', data)
export const add = data => createAPI(`${api}`, 'post', data)
export const update = data => createAPI(`${api}/${data.id}`, 'put', data)
export const remove = data => createAPI(`${api}/${data.id}`, 'delete', data)
export const detail = data => createAPI(`${api}/${data.id}`, 'get', data)
export const saveOrUpdate = data => {return data.id?update(data):add(data)}
           

2.3.4 路由配置

在module-permissions模块的router的index.js中配置路由:

import Layout from '@/module-dashboard/pages/layout'
const _import = require('@/router/import_' + process.env.NODE_ENV)

export default [
  {
    root: true,
    path: '/permissions',
    component: Layout,
    redirect: 'noredirect',
    name: 'permissions',
    meta: {
      title: '权限设置',
      icon: 'set'
    },
    children: [
      {
        path: 'index',
        component: _import('permissions/pages/index'),
        name: 'permissions-index',
        meta: {title: '权限设置', icon: 'set', noCache: true}
      }
    ]
  }
]
           

2.3.5 注册路由

在module-permissions模块根目录的index.js中注册路由:

// vue-router
import {asyncRouterMap} from '@/router'
import routerMaps from './router'
// vuex
import app from './store/app'

export default {
  install(module, store) {
    // 注册路由
    asyncRouterMap.push(routerMaps[0])
    // 注册状态管理
    if (store !== undefined) {
      // store.registerModule('app', app)
    }
  }
}
           

2.3.6 index.vue

<template>
  <div class="dashboard-container">
    <div class="app-container">
      <el-card shadow="never">
        <el-button class="filter-item fr" size="small" style="margin-left: 10px;" @click="handleCreate(null,1);setPid(1,'0')" type="primary" icon="el-icon-edit">添加菜单</el-button>
            <el-table :data="dataList" fit style="width: 100%;" highlight-current-row>
                <el-table-column fixed prop="name" label="菜单名称" width="200px">
                    <template slot-scope="scope">
                        <i :class="scope.row.type==1?'ivu-icon fa fa-folder-open-o fa-fw':'ivu-icon  el-icon-view'" 
                            :style="scope.row.type==1?'margin-left: 0px':'margin-left: 20px'"></i>
                        <span @click="show(scope.$index,scope.row.id)">{{scope.row.name}}</span>
                    </template>
                </el-table-column>
                <el-table-column fixed prop="code" label="权限标识" width="200"></el-table-column>
                <el-table-column fixed prop="description" label="描述" width="200"></el-table-column>        
                <el-table-column fixed="right" label="操作">
                    <template slot-scope="scope">
                        <el-button v-if="scope.row.type==1" @click="handleCreate(null,2);setPid(2,scope.row.id)" type="text" size="small">添加权限点</el-button>
                        <el-button @click="handlerApiList(scope.row.id)" type="text" size="small">查看api权限</el-button>
                        <el-button @click="handleCreate(scope.row.id,scope.row.type);setPid(scope.row.type,scope.row.pid)" type="text" size="small">查看</el-button>
                        <el-button @click="handleDelete(scope.row.id)" type="text" size="small">删除</el-button>
                    </template>
                </el-table-column>
            </el-table>
        </el-card>
      </div>

      <el-dialog title="编辑权限" :visible.sync="dialogFormVisible" style="hight:100px;line-height:1px">
          <el-form :model="formData" label-width="90px" style="margin-top:20px">
            <el-form-item label="权限名称">
              <el-input v-model="formData.name" autocomplete="off" style="width:90%"></el-input>
            </el-form-item>
            <el-form-item label="权限标识">
              <el-input v-model="formData.code" autocomplete="off" style="width:90%"></el-input>
            </el-form-item>
            <el-form-item label="权限描述">
              <el-input v-model="formData.description" autocomplete="off" style="width:90%"></el-input>
            </el-form-item>
            <el-form-item label="企业可见">
              <el-switch
                v-model="formData.enVisible"
                active-value="1"
                inactive-value="0"
                active-text="可见"
                inactive-text="不可见">
              </el-switch>
            </el-form-item>  
            <div v-if="type==1">
              <el-form-item label="菜单顺序">
                <el-input v-model="formData.menuOrder" autocomplete="off" style="width:90%"></el-input>
              </el-form-item> 
              <el-form-item label="菜单icon">
                <el-input v-model="formData.menuIcon" autocomplete="off" style="width:90%"></el-input>
              </el-form-item> 
            </div>
            <div v-else-if="type==2">
              <el-form-item label="按钮样式">
                <el-input v-model="formData.pointClass" autocomplete="off" style="width:90%"></el-input>
              </el-form-item> 
              <el-form-item label="按钮icon">
                <el-input v-model="formData.pointIcon" autocomplete="off" style="width:90%"></el-input>
              </el-form-item>  
              <el-form-item label="按钮状态">
                <el-input v-model="formData.pointStatus" autocomplete="off" style="width:90%"></el-input>
              </el-form-item> 
            </div>
            <div v-else-if="type==3">
              <el-form-item label="api请求地址">
                <el-input v-model="formData.apiUrl" autocomplete="off" style="width:90%"></el-input>
              </el-form-item> 
              <el-form-item label="api请求方式">
                <el-input v-model="formData.apiMethod" autocomplete="off" style="width:90%"></el-input>
              </el-form-item>  
              <el-form-item label="api类型">
                <el-input v-model="formData.apiLevel" autocomplete="off" style="width:90%"></el-input>
              </el-form-item>               
            </div>

          </el-form>
          <div slot="footer" class="dialog-footer">
            <el-button @click="dialogFormVisible = false">取 消</el-button>
            <el-button type="primary" @click="saveOrUpdate">确 定</el-button>
          </div>
      </el-dialog>

      <el-dialog  title="API权限列表" :visible.sync="apiDialogVisible" style="hight:400px;line-height:1px">
            <el-button class="filter-item fr" size="small" style="margin-left: 10px;" @click="handleCreate(null,1);setPid(3,pid)" type="primary" icon="el-icon-edit">添加api权限</el-button>
            <el-table :data="apiList" fit style="width: 100%;" max-height="250" >
                <el-table-column fixed prop="name" label="菜单名称" width="120px"></el-table-column>
                <el-table-column fixed prop="code" label="权限标识" width="200"></el-table-column>
                <el-table-column fixed prop="description" label="描述" width="200"></el-table-column>        
                <el-table-column fixed="right" label="操作" width="200">
                    <template slot-scope="scope">
                        <el-button @click="handleCreate(scope.row.id,scope.row.type);setPid(scope.row.type,scope.row.pid)" type="text" size="small">查看</el-button>
                        <el-button @click="handleDelete(scope.row.id);handlerApiList(pid)" type="text" size="small">删除</el-button>
                    </template>
                </el-table-column>
            </el-table>        
      </el-dialog>
  </div>
</template>

<script>
import {saveOrUpdate,list,detail,remove} from "@/api/base/permissions"
export default {
  name: 'permissions-table-index',
  data() {
    return {
      MenuList: 'menuList',
      type:0,
      pid:"",
      dialogFormVisible:false,
      apiDialogVisible:false,
      formData:{},
      dataList:[],
      apiList:[],
      pointEnable:{}
    }
  },
  methods: {
    setPid(type,pid){
      this.pid = pid;
      this.type = type
    },
    handleCreate(id) {
      if(id && id !=undefined) {
        detail({id}).then(res => {
          this.formData = res.data.data
          this.dialogFormVisible=true
        })
      }else{
        this.formData = {}
        this.dialogFormVisible=true
      }
    },
    saveOrUpdate() {
      this.formData.type = this.type
      this.formData.pid = this.pid
      saveOrUpdate(this.formData).then(res => {
        this.$message({message:res.data.message,type:res.data.success?"success":"error"});
        if(res.data.success){
          this.formData={};
          this.dialogFormVisible=false;
        }
        if(this.type ==3){
          this.handlerApiList(this.pid);
        }else{
          this.getList();
          this.pointEnable = {}
        }
      })
    },
    handleDelete(id) {
      remove({id}).then(res=> {
        this.$message({message:res.data.message,type:res.data.success?"success":"error"});
       if(res.data.success){
          this.getList();
        }
      })
    },
    getList() {
      list({type:1,pid:0}).then(res=> {
          this.dataList = res.data.data
      })
    },
    show(index,id) {
        if(!this.pointEnable[id] == null || this.pointEnable[id]==undefined){
            list({type:2,pid:id}).then(res=> {
                if(res.data.data.length <=0) {
                  this.$message.error("无子权限")
                }else{
                  for(var i = 0 ; i <res.data.data.length;i++) {
                      this.dataList.splice(index+1,0,res.data.data[i]);
                  }
                  this.pointEnable[id] = res.data.data.length;
                }
            })
        }else{
            this.dataList.splice(index+1,this.pointEnable[id])
            this.pointEnable[id] = null;
        }
    },
    handlerApiList(id) {
      this.pid = id;
      list({type:3,pid:id}).then(res=> {
          this.apiList = res.data.data
          this.apiDialogVisible = true;
      })
    }
  },
  created () {
    this.getList();
  }
}
</script>
<style rel="stylesheet/scss"  scoped>
.alert {
  margin: 10px 0px;
}
.pagination {
  margin-top: 10px;
  // text-align: right;
}
</style>

<style>
.el-table th {
  background-color: #fafafa;
}
.el-table th.is-leaf {
  border-bottom: 2px solid #e8e8e8;
}
.el-table__row i{ font-style:normal}
</style>
           

2.3.7 组件menu-list.vue

<template>
    <el-table :data="dataList" fit style="width: 100%;" highlight-current-row>
        <el-table-column fixed prop="name" label="菜单名称" width="200px">
            <template slot-scope="scope">
                <i :class="scope.row.type==1?'ivu-icon fa fa-folder-open-o fa-fw':'ivu-icon  el-icon-view'" 
                    :style="scope.row.type==1?'margin-left: 0px':'margin-left: 20px'"></i>
                <span @click="show(scope.$index,scope.row.id)">{{scope.row.name}}</span>
            </template>
        </el-table-column>
        <el-table-column fixed prop="code" label="权限标识" width="200"></el-table-column>
        <el-table-column fixed prop="description" label="描述" width="200"></el-table-column>        
        <el-table-column fixed="right" label="操作">
            <template slot-scope="scope">
                <el-button @click="handleClick(scope.row)" type="text" size="small">查看</el-button>
                <el-button type="text" size="small">编辑</el-button>
            </template>
        </el-table-column>
    </el-table>
</template>

<script>
import {list} from "@/api/base/permissions"
import commonApi from "@/utils/common"
export default {
    data () {
        return {
            dataList:[],
            pointEnable:{}
        }
    },
    created () {
        this.getList();
    },
    methods: {
        getList() {
            list({type:1,pid:0}).then(res=> {
                this.dataList = res.data.data
            })
        },
        show(index,id) {
            if(!this.pointEnable[id] == null || this.pointEnable[id]==undefined){
                list({type:2,pid:id}).then(res=> {
                    for(var i = 0 ; i <res.data.data.length;i++) {
                        this.dataList.splice(index+1,0,res.data.data[i]);
                    }
                    this.pointEnable[id] = res.data.data.length;
                })
            }else{
                this.dataList.splice(index+1,this.pointEnable[id])
                this.pointEnable[id] = null;
            }
        }
    }
}
</script>
           

3、分配角色

在用户管理列表中,操作列有分配角色按钮,我们可以点击进行角色的分配,但是该功能还没有完善。

SaaS-HRM(5)系统用户权限设计(角色管理、权限和资源管理)1、角色管理2、权限和资源管理3、分配角色4、分配权限

3.1 前端代码实现

3.1.1 添加分配角色组件

在components目录下新增addRole.vue组件:

<template>
  <div class="add-form">
    <el-dialog title="分配角色" :visible.sync="roleFormVisible" style="height:300px">
      <el-form  :model="formBase"  label-position="left" label-width="120px" style='margin-left:120px; width:500px;'>
          <el-checkbox-group 
            v-model="checkedRoles">
            <el-checkbox v-for="(item,index) in roles" :label="item.id" :key="index">{{item.name}}</el-checkbox>
          </el-checkbox-group>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="createData">提交</el-button>
        <el-button @click="roleFormVisible=false">取消</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
import {findAll} from "@/api/base/role"
import {assignRoles,detail} from "@/api/base/users"
export default {
    data () {
        return {
            roleFormVisible:false,
            formBase:{},
            checkedRoles:[],
            data:[],
            roles:[],
            id:null
        }
    },
    methods: {
        toAssignPrem(id) {
            detail({id:id}).then(res1 => {
                this.checkedRoles = res1.data.data.roleIds;
                console.log(this.checkedRoles)
                findAll().then(res => {
                    this.id = id;
                    this.roles = res.data.data
                    console.log(this.roles)
                    this.roleFormVisible=true
                })
            })
        },
        createData() {
            assignRoles({id:this.id,roleIds:this.checkedRoles}).then(res => {
                this.$message({message:res.data.message,type:res.data.success?"success":"error"});
                this.roleFormVisible=false
            })
        }
    }
}
</script>
           

在index.vue中要引用该组件:

SaaS-HRM(5)系统用户权限设计(角色管理、权限和资源管理)1、角色管理2、权限和资源管理3、分配角色4、分配权限
SaaS-HRM(5)系统用户权限设计(角色管理、权限和资源管理)1、角色管理2、权限和资源管理3、分配角色4、分配权限

上面两个图中,红框标记的都是新增的内容。

当我们再次点击分配角色时,打开浏览器的F12,发现如下:

SaaS-HRM(5)系统用户权限设计(角色管理、权限和资源管理)1、角色管理2、权限和资源管理3、分配角色4、分配权限

究竟是怎么回事呢?仔细查看组件中的代码,发现:

SaaS-HRM(5)系统用户权限设计(角色管理、权限和资源管理)1、角色管理2、权限和资源管理3、分配角色4、分配权限

这个checkedRoles表示的是我们选中的角色id,但是当我们查询用户相关信息的时候, 我们之前的后台并没有查询用户相关的角色,所以在下面这行代码中,导致了this.checkedRoles的值是undefined,从而报错:

SaaS-HRM(5)系统用户权限设计(角色管理、权限和资源管理)1、角色管理2、权限和资源管理3、分配角色4、分配权限

解决上面的问题,就需要我们在查询用户信息的时候,就应该查询出该用户对应的角色id的集合,修改UserController的findById方法:

//根据id查询用户
    @RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
    public Result findById(@PathVariable("id") String id){
        User user = userService.findtById(id);
        // 添加 roleIds (用户已经具有的角色id数组)
        UserResult userResult = new UserResult(user);
        return new Result(ResultCode.SUCCESS,userResult);
    }
           

 UserResult实体类定义在ihrm_common_model的包system.response下面,里面通过User对象,封装了角色id的集合:

package com.zdw.ihrm.domain.system.response;

import com.zdw.ihrm.domain.system.Role;
import com.zdw.ihrm.domain.system.User;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.BeanUtils;

import javax.persistence.Id;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Getter
@Setter
public class UserResult implements Serializable {

    private static final long serialVersionUID = -1578313786206948978L;
    @Id
    private String id;//ID

    private String mobile;//手机号码

    private String username;//用户名称

    private String password;//密码

    private Integer enableState;//启用状态 0为禁用 1为启用

    private Date createTime;//创建时间

    private String companyId;//企业id

    private String companyName;//企业名称

    private String departmentId;//部门id

    private Date timeOfEntry;//入职时间

    private Integer formOfEmployment;//聘用形式

    private String workNumber;//工号

    private String formOfManagement;//管理形式

    private String workingCity;//工作城市

    private Date correctionTime;//转正时间

    private Integer inServiceStatus;//在职状态 1.在职  2.离职

    private String departmentName;//部门名称

    private List<String> roleIds = new ArrayList<>();//用户对应的角色ID的集合

    public UserResult(User user) {//通过传入用户,得到返回对象UserResult,
        BeanUtils.copyProperties(user,this);
        for (Role role : user.getRoles()) {//循环用户角色信息,把用户的角色对应的id添加到集合roleIds中
            this.roleIds.add(role.getId());
        }
    }
}
           

重启后台,我们访问页面,点击分配角色就可以看到如下:

SaaS-HRM(5)系统用户权限设计(角色管理、权限和资源管理)1、角色管理2、权限和资源管理3、分配角色4、分配权限

3.2 分配角色服务端代码实现

在UserController中,添加分配角色的控制器方法实现:

//分配角色
    @RequestMapping(value = "/user/assignRoles", method = RequestMethod.PUT)
    public Result save(@RequestBody Map<String,Object> map) {
        //1.获取被分配的用户id
        String userId = (String) map.get("id");
        //2.获取到角色的id列表
        List<String> roleIds = (List<String>) map.get("roleIds");
        //3.调用service完成角色分配
        userService.assignRoles(userId,roleIds);
        return new Result(ResultCode.SUCCESS);
    }
           

在UserService中,添加分配角色assignRoles方法:

@Autowired
    private RoleDao roleDao;

    /**
     * 分配角色
     */
    public void assignRoles(String userId,List<String> roleIds) {
        //1.根据id查询用户
        User user = userDao.findById(userId).get();
        //2.设置用户的角色集合
        Set<Role> roles = new HashSet<>();
        for (String roleId : roleIds) {
            Role role = roleDao.findById(roleId).get();
            roles.add(role);
        }
        //设置用户和角色集合的关系
        user.setRoles(roles);
        //3.更新用户
        userDao.save(user);
    }
           

4、分配权限

在公司设置菜单,我们可以看到列表的操作列中,有分配权限的按钮:

SaaS-HRM(5)系统用户权限设计(角色管理、权限和资源管理)1、角色管理2、权限和资源管理3、分配角色4、分配权限

4.1 前端实现

前端代码还是在之前的role-list.vue中已经实现啦。

吸取刚才分配角色的教训,我们可以查看module-settings模块的components目录下的role-list.vue中,有这样的代码:

SaaS-HRM(5)系统用户权限设计(角色管理、权限和资源管理)1、角色管理2、权限和资源管理3、分配角色4、分配权限

这里就是点击分配权限展开的树型权限对话框,二且在点击分配权限会触发的方法如下:

SaaS-HRM(5)系统用户权限设计(角色管理、权限和资源管理)1、角色管理2、权限和资源管理3、分配角色4、分配权限

能看出来,this.checkNodes就是我们的角色所拥有的权限的id集合,所以当在后台查询角色信息的时候,我们也需要查询权限id的集合信息,所以修改后台RoleController的findById方法:

//根据id查询角色
    @RequestMapping(value = "/role/{id}", method = RequestMethod.GET)
    public Result findById(@PathVariable("id") String id){
        Role role = roleService.findById(id);
        RoleResult roleResult = new RoleResult(role);
        return new Result(ResultCode.SUCCESS,roleResult);
    }
           

RoleResult类如下:

package com.zdw.ihrm.domain.system.response;

import com.zdw.ihrm.domain.system.Permission;
import com.zdw.ihrm.domain.system.Role;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.BeanUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

@Getter
@Setter
public class RoleResult implements Serializable {
    private static final long serialVersionUID = 152410808770300424L;
    private String id;//ID
    private String name;//角色名
    private String description;//说明
    private String companyId;//企业id

    private List<String> permIds = new ArrayList<>();//权限的集合

    public RoleResult(Role role) {//构造方法,传入角色对象,构造RoleResult对象,得到角色的权限,然后添加到权限集合permIds中
        BeanUtils.copyProperties(role,this);
        for (Permission perm : role.getPermissions()) {
            this.permIds.add(perm.getId());
        }
    }
}
           

4.2 后台实现

在RoleController新增分配权限的方法:

/**
       * 分配权限
       */
    @RequestMapping(value = "/role/assignPrem", method = RequestMethod.PUT)
    public Result assignPrem(@RequestBody Map<String,Object> map) {
       //1.获取被分配的角色的id
       String roleId = (String) map.get("id");
       //2.获取到权限的id列表
       List<String> permIds = (List<String>) map.get("permIds");
       //3.调用service完成权限分配
       roleService.assignPerms(roleId,permIds);
       return new Result(ResultCode.SUCCESS);
    }
           

在RoleService新增分配权限的方法:

@Autowired
    private PermissionDao permissionDao;

    public void assignPerms(String roleId,List<String> permIds) {
        //1.获取分配的角色对象
        Role role = roleDao.findById(roleId).get();
        //2.构造角色的权限集合
        Set<Permission> perms = new HashSet<>();
        for (String permId : permIds) {
            Permission permission = permissionDao.findById(permId).get();
            //需要根据父id和类型查询API权限列表
            List<Permission> apiList = permissionDao.findByTypeAndPid(PermissionConstants.PY_API, permission.getId());
            perms.addAll(apiList);//自定赋予API权限
            perms.add(permission);//当前菜单或按钮的权
            System.out.println(perms.size());
        }
        //3.设置角色和权限的关系
        role.setPermissions(perms);
        //4.更新角色
        roleDao.save(role);
    }
           

因为上面:permissionDao.findByTypeAndPid,所以需要在PermissionDao中添加该方法:

public interface PermissionDao extends JpaRepository<Permission,String>, JpaSpecificationExecutor<Permission> {
    List<Permission> findByTypeAndPid(Integer type,String pid);
}
           

接下来就可以启动前后台进行测试啦。