基于springboot+redis+bootstrap+mysql开发一套属于自己的分布式springcloud云权限架构(十二)【权限架构生产者(菜单管理)】

      在第十一章我们完成了对组织架构管理的集成,本章我们将完成对菜单管理的集成开发工作,首先打开我们的rbac-produce工程,接着在com/produce/sys/dao目录底下创建一个UserDao.java接口内容如下:

package com.produce.sys.dao;



import com.base.entity.QueryTree;
import com.base.entity.Tree;
import com.base.entity.User;
import com.produce.common.base.dao.GenericDao;

import java.util.List;

/**
 *@author linzf
 **/
public interface TreeDao extends GenericDao<Tree, QueryTree> {

    /**
     * 功能描述:加载用户的菜单树的数据
     * @param user
     * @return
     */
	List<Tree> loadUserTree(User user);
}

      接着在我们的mapper文件夹底下创建一个mybatis_tree.xml文件,文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.produce.sys.dao.TreeDao">
	<resultMap type="com.base.entity.Tree" id="TreeMap">
		<id property="id" column="id"/>
		<result property="code" column="code"/>
		<result property="icon" column="icon"/>
		<result property="name" column="name"/>
		<result property="pId" column="p_id"/>
		<result property="treeOrder" column="tree_order"/>
		<result property="url" column="url"/>
		<result property="state" column="state"/>
	</resultMap>

	<!-- 加载用户的菜单树的数据 -->
	<select id="loadUserTree" resultMap="TreeMap" parameterType="com.base.entity.User">
		select DISTINCT t.*  from tree t inner join role_associate_tree rat on t.id = rat.tree_id inner join user_role ur on rat.role_id = ur.id where ur.id in
		<foreach collection="roles" index="index" item="role" open="(" separator="," close=")">
			#{role.id}
		</foreach>
	</select>

	<!--根据主键获取对象-->
	<select id="get" parameterType="com.base.entity.Tree" resultMap="TreeMap">
		SELECT id,code,icon,name,p_id,tree_order,url,state FROM tree 
		WHERE id=#{id}
	</select>

	<!--保存-->
	<insert id="save" parameterType="com.base.entity.Tree" useGeneratedKeys="true" keyProperty="id">
		INSERT INTO tree(code,icon,name,p_id,tree_order,url,state)
		VALUES(#{code},#{icon},#{name},#{pId},#{treeOrder},#{url},#{state})
	</insert>

	<!--修改-->
	<update id="update" parameterType="com.base.entity.Tree">
		UPDATE tree SET code=#{code},icon=#{icon},name=#{name},p_id=#{pId},tree_order=#{treeOrder},url=#{url},state=#{state}
		WHERE id=#{id}
	</update>

	<!--删除-->
	<delete id="delete" parameterType="com.base.entity.Tree">
		 DELETE FROM tree WHERE id=#{id}
	</delete>

	<!--分页查询-->
	<select id="findByPage" parameterType="com.base.entity.QueryTree" resultMap="TreeMap">
		SELECT id,code,icon,name,p_id,tree_order,url,state FROM tree
		WHERE 1=1
		<if test="code!=null and code!='' ">
		AND code=#{code}
		</if>
		<if test="icon!=null and icon!='' ">
		AND icon=#{icon}
		</if>
		<if test="name!=null and name!='' ">
		AND name=#{name}
		</if>
		<if test="pId!=null and pId!='' ">
		AND p_id=#{pId}
		</if>
		<if test="treeOrder!=null and treeOrder!='' ">
		AND tree_order=#{treeOrder}
		</if>
		<if test="url!=null and url!='' ">
		AND url=#{url}
		</if>
		<if test="state!=null and state!='' ">
		AND state=#{state}
		</if>
		<if test="sort!= null and sort!='' ">
		order by ${sort} ${order}
		</if>
		limit #{offset},#{limit}
	</select>

	<!--统计-->
	<select id="count" parameterType="com.base.entity.QueryTree" resultType="int">
		SELECT count(*) FROM tree
		WHERE 1=1
		<if test="code!=null and code!='' ">
			AND code=#{code}
		</if>
		<if test="icon!=null and icon!='' ">
			AND icon=#{icon}
		</if>
		<if test="name!=null and name!='' ">
			AND name=#{name}
		</if>
		<if test="pId!=null and pId!='' ">
			AND p_id=#{pId}
		</if>
		<if test="treeOrder!=null and treeOrder!='' ">
			AND tree_order=#{treeOrder}
		</if>
		<if test="url!=null and url!='' ">
			AND url=#{url}
		</if>
		<if test="state!=null and state!='' ">
			AND state=#{state}
		</if>
		<if test="sort!= null and sort!='' ">
			order by ${sort} ${order}
		</if>
	</select>

	<!--查询-->
	<select id="query" parameterType="com.base.entity.QueryTree" resultMap="TreeMap">
		SELECT id,code,icon,name,p_id,tree_order,url,state FROM tree
		WHERE 1=1
		<if test="code!=null and code!='' ">
			AND code=#{code}
		</if>
		<if test="icon!=null and icon!='' ">
			AND icon=#{icon}
		</if>
		<if test="name!=null and name!='' ">
			AND name=#{name}
		</if>
		<if test="pId!=null and pId!='' ">
			AND p_id=#{pId}
		</if>
		<if test="treeOrder!=null and treeOrder!='' ">
			AND tree_order=#{treeOrder}
		</if>
		<if test="url!=null and url!='' ">
			AND url=#{url}
		</if>
		<if test="state!=null and state!='' ">
			AND state=#{state}
		</if>
		<if test="sort!= null and sort!='' ">
			order by ${sort} ${order}
		</if>
	</select>
</mapper>

      由于我们对菜单进行维护的时候,我们在对角色管理的时候会对相应的角色赋予相应的菜单,因此在我们删除某些菜单的时候,我们需要同时删除菜单与角色相关联的数据,因此我们需要在com/produce/sys/dao目录底下创建一个RoleAssociateTreeDao.java接口内容如下:

package com.produce.sys.dao;


import com.base.entity.QueryRoleAssociateTree;
import com.base.entity.RoleAssociateTree;
import com.base.entity.Tree;
import com.base.entity.UserRole;
import com.produce.common.base.dao.GenericDao;

/**
 *@author linzf
 **/
public interface RoleAssociateTreeDao extends GenericDao<RoleAssociateTree, QueryRoleAssociateTree> {

    /**
     * 功能描述:根据菜单ID来删除关联的菜单数据
     * @param tree
     * @return
     */
    int removeTreeByTreeId(Tree tree);

    /**
     * 功能描述:根据角色ID来删除关联的菜单数据
     * @param userRole
     * @return
     */
    int removeTreeByRoleId(UserRole userRole);
	
}

      接着在我们的mapper文件夹底下创建一个mybatis_roleAssociateTree.xml文件,文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.produce.sys.dao.RoleAssociateTreeDao">
	<resultMap type="com.base.entity.RoleAssociateTree" id="RoleAssociateTreeMap">
		<result property="roleId" column="role_id"/>
		<result property="treeId" column="tree_id"/>
	</resultMap>

	<!-- 根据菜单ID来删除关联的菜单数据 -->
	<delete id="removeTreeByTreeId" parameterType="com.base.entity.Tree">
		delete from role_associate_tree where tree_id = #{id}
	</delete>

	<!-- 根据角色ID来删除关联的菜单数据 -->
	<delete id="removeTreeByRoleId" parameterType="com.base.entity.UserRole">
		delete from role_associate_tree where role_id = #{id}
	</delete>

	<!--根据主键获取对象-->
	<select id="get" parameterType="com.base.entity.RoleAssociateTree" resultMap="RoleAssociateTreeMap">
		SELECT role_id,tree_id FROM role_associate_tree 
		WHERE 
	</select>

	<!--保存-->
	<insert id="save" parameterType="com.base.entity.RoleAssociateTree">
		INSERT INTO role_associate_tree(role_id,tree_id)
		VALUES(#{roleId},#{treeId})
	</insert>

	<!--修改-->
	<update id="update" parameterType="com.base.entity.RoleAssociateTree">
		UPDATE role_associate_tree SET role_id=#{roleId},tree_id=#{treeId}
		WHERE 
	</update>

	<!--删除-->
	<delete id="delete" parameterType="com.base.entity.RoleAssociateTree">
		 DELETE FROM role_associate_tree WHERE 
	</delete>

	<!--分页查询-->
	<select id="findByPage" parameterType="com.base.entity.QueryRoleAssociateTree" resultMap="RoleAssociateTreeMap">
		SELECT role_id,tree_id FROM role_associate_tree
		WHERE 1=1
		<if test="roleId!=null and roleId!='' "  >
		AND role_id=#{roleId}
		</if>
		<if test="treeId!=null and treeId!='' "  >
		AND tree_id=#{treeId}
		</if>
		<if test="sort!= null">
		order by ${sort} ${order}
		</if>
		limit #{offset},#{limit}
	</select>

	<!--统计-->
	<select id="count" parameterType="com.base.entity.QueryRoleAssociateTree" resultType="int">
		SELECT count(*) FROM role_associate_tree
		WHERE 1=1
		<if test="roleId!=null and roleId!='' ">
		AND role_id=#{roleId}
		</if>
		<if test="treeId!=null and treeId!='' ">
		AND tree_id=#{treeId}
		</if>
	</select>

	<!--查询-->
	<select id="query" parameterType="com.base.entity.QueryRoleAssociateTree" resultMap="RoleAssociateTreeMap">
		SELECT role_id,tree_id FROM role_associate_tree
		WHERE 1=1
		<if test="roleId!=null and roleId!='' ">
		AND role_id=#{roleId}
		</if>
		<if test="treeId!=null and treeId!='' ">
		AND tree_id=#{treeId}
		</if>
		<if test="sort!= null">
		order by ${sort} ${order}
		</if>
	</select>
</mapper>

      在我们的用户加载菜单的时候,我们需要根据用户的信息来实现加载菜单,因此我们需要在我们的rbac-produce工程中引入redis的依赖以及注入redis和redis的配置,因此我们需要分别修改以下的文件:

1、在主入口类引入如下的配置:


2、在com/produce/common/util底下创建一个node包,同时在该包底下创建NodeUtil.java类内容如下:

package com.produce.common.util.node;







import com.base.entity.Tree;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/*
* 类描述:
* @auther linzf
* @create 2017/9/20 0020 
*/
public class NodeUtil {

    private static List<Tree> returnList = new ArrayList<Tree>();

    /**
     * 根据父节点的ID获取所有子节点
     * @param list 分类表
     * @param typeId 传入的父节点ID
     * @return String
     */
    public static List<Tree> getChildNodes(List<Tree> list, Long typeId) {
        returnList = new ArrayList<Tree>();
        if(list == null && typeId == null) return new ArrayList<Tree>();
        for (Iterator<Tree> iterator = list.iterator(); iterator.hasNext();) {
            Tree node = (Tree) iterator.next();
            // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点
            if (node.getpId()==0 && typeId==node.getId()) {
                recursionFn(list, node);
            }
            // 二、遍历所有的父节点下的所有子节点
            if (node.getpId()==0) {
                recursionFn(list, node);
            }
        }
        // 对顶层菜单按照treeOrder从大到小进行进行排序
        Collections.sort(returnList);
        return returnList;
    }

    private static void recursionFn(List<Tree> list, Tree node) {
        List<Tree> childList = getChildList(list, node);// 得到子节点列表
        if (hasChild(list, node)) {// 判断是否有子节点
            Iterator<Tree> it = childList.iterator();
            while (it.hasNext()) {
                Tree n = (Tree) it.next();
                if(hasChild(list,n)){// 判断子节点是否还有相应的子节点,若有则再次递归遍历
                    recursionFn(list, n);
                }
            }
            node.setChild(childList);
            returnList.add(node);
        }
    }

    // 得到子节点列表
    private static List<Tree> getChildList(List<Tree> list, Tree node) {
        List<Tree> nodeList = new ArrayList<Tree>();
        Iterator<Tree> it = list.iterator();
        while (it.hasNext()) {
            Tree n = (Tree) it.next();
            if (n.getpId() == node.getId()) {
                nodeList.add(n);
            }
        }
        Collections.sort(nodeList);
        return nodeList;
    }

    // 判断是否有子节点
    private static boolean hasChild(List<Tree> list, Tree node) {
        return getChildList(list, node).size() > 0 ? true : false;
    }

}

3、在com/produce/common/util/user包的UserInfo.java类中增加以下的方法:

/**
     * 功能描述:加载菜单节点的数据
     * @return
     */
    public static List<Tree> loadUserTree(TreeService treeService, User user){
        Map<Long,Tree> treeMap = new HashMap<Long,Tree>();
        for(Tree tree:treeService.loadUserTree(user)){
            treeMap.put(tree.getId(),tree);
        }
        List<Tree> treeList = NodeUtil.getChildNodes(new ArrayList<Tree>(treeMap.values()),0l);
        return treeList;
    }

      最后在我们的com/produce/sys/controller底下创建我们的TreeController.java实现类内容如下:

package com.produce.sys.controller;


import com.base.entity.QueryTree;
import com.base.entity.Tree;
import com.base.entity.User;
import com.base.util.redis.RedisCache;
import com.produce.common.base.constant.SystemStaticConst;
import com.produce.common.base.controller.GenericController;
import com.produce.common.base.service.GenericService;
import com.produce.common.util.user.UserInfo;
import com.produce.sys.service.TreeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

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

/*
* 类描述:菜单操作controller
* @auther linzf
* @create 2017/10/10 0010 
*/
@RestController
@RequestMapping("/tree")
public class TreeController extends GenericController<Tree,QueryTree> {

    @Autowired
    private TreeService treeService;

    @Autowired
    private RedisCache redisCache;

    @Override
    protected GenericService<Tree, QueryTree> getService() {
        return treeService;
    }

    /**
     * 功能描述:加载首页菜单节点的数据
     * @return
     */
    @RequestMapping(value="/mainTree",method = RequestMethod.POST,produces = MediaType.APPLICATION_JSON_VALUE)
    public Map<String,Object> mainTree(String token){
        Map<String,Object> result = new HashMap<String, Object>();
        List<Tree> trees = UserInfo.loadUserTree(treeService,(User)redisCache.getObject(token,User.class));
        result.put("data",trees);
        result.put(SystemStaticConst.RESULT, SystemStaticConst.SUCCESS);
        return result;
    }


    /**
     * 功能描述:直接加载整个菜单树的数据(且必须要有管理员权限才可以加载该菜单树的数据)
     * @return
     */
    @RequestMapping(value = "/loadUserTree",method = RequestMethod.POST,produces = MediaType.APPLICATION_JSON_VALUE)
    public Map<String,Object> loadUserTree(){
        Map<String,Object> result = new HashMap<String, Object>();
        List<Tree> treeList = treeService.query(null);
        result.put(SystemStaticConst.RESULT, SystemStaticConst.SUCCESS);
        result.put(SystemStaticConst.MSG,"加载菜单数据成功!");
        result.put("data",treeList);
        return result;
    }


}

      到此处我们完成了对菜单管理的集成,接着启动我们的注册中心,链路中心同时启动我们的rbac-produce,接着直接访问我们的swagger工程地址:http://127.0.0.1:8100/swagger-ui.html#/,那么我们会看到如下的页面则表示我们的菜单管理已经集成成功了。


      到此为止的GitHub项目地址:https://github.com/185594-5-27/spring-cloud-rbac/tree/master-base-produce-tree

上一篇文章地址:基于springboot+redis+bootstrap+mysql开发一套属于自己的分布式springcloud云权限架构(十一)【权限架构生产者(组织架构)】

下一篇文章地址:基于springboot+redis+bootstrap+mysql开发一套属于自己的分布式springcloud云权限架构(十三)【权限架构生产者(改造角色管理)】


QQ交流群:578746866


猜你喜欢

转载自blog.csdn.net/linzhefeng89/article/details/79349466