CRM -权限管理

1. 学习目标

(img-gcKQyfta-1652103301810)(E:\program\安装资料\笔记工具\Markdown\Pictures\java中级\11 Crm项目\03 权限管理\主要内容.png)]

2. 权限管理RBAC基本概念

RBAC是基于角色的访问控制( Role-Based Access Control )在RBAC中,权限与角色相关联,用

户通过扮演适当的角色从而得到这些角色的权限。这样管理都是层级相互依赖的,权限赋予给角色,角

色又赋予用户,这样的权限设计很清楚,管理起来很方便。

RBAC授权实际上是 Who 、 What 、 How 三元组之间的关系,也就是 Who 对 What 进行 How 的操

作,简单说明就是谁对什么资源做了怎样的操作。

3. RBAC表结构设计

4. 用户管理功能实现

4.1. 用户查询操作

4.1.1. 页面效果
4.1.2. 后端代码实现
4.1.2.1. 查询条件

UserQuery.java 查询类定义

package com.xxxx.crm.query;

import com.xxxx.crm.base.BaseQuery;

public class UserQuery extends BaseQuery {

    private String userName;
    private String email;
    private String phone;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }
}
4.1.2.2. 设置SQL

UserMapper.xml

<!--  多条件分页查询-->
  <select id="queryUserBYParams" parameterType="com.xxxx.crm.query.UserQuery" resultType="user">
    select
    *
    from
    t_user
    <where>
      is_valid = 1
      <if test=" null != userName and '' != userName">
        and user_name like concat("%",#{userName},"%")
      </if>
      <if test=" null != email and '' != email">
        and email = #{email}
      </if>
      <if test=" null != phone and '' != phone">
        and phone = #{phone}
      </if>
    </where>
  </select>
4.1.2.3. Service

UserService.java

 //多条件分页查询
    public Map<String,Object> queryUserBYParams(UserQuery query){
        Map<String, Object> map = new HashMap<>();
        //开启分页
        PageHelper.startPage(query.getPage(),query.getLimit());
        List<User> users = userMapper.queryUserBYParams(query);
        //按照分页条件,格式化数据
        PageInfo<User> userPageInfo = new PageInfo<>(users);

        map.put("code",0);
        map.put("msg","");
        map.put("count",userPageInfo.getTotal());
        map.put("data",userPageInfo.getList());
        return map;
    }
4.1.2.4. Controller

UserController.java

列表查询方法添加

//多条件分页查询
    @RequestMapping("list")
    @ResponseBody
    public Map<String,Object> queryUserBYParams(UserQuery query){
        return userService.queryUserBYParams(query);
    }
4.1.3. 前端核心代码
4.1.3.1. 页面模板

user.ftl

views/user 目录下添加 user.ftl 视图模板

<!DOCTYPE html>
<html>
    <head>
        <title>用户管理</title>
        <#include "../common.ftl">
    </head>
    <body class="childrenBody">
        <form class="layui-form" >
            <blockquote class="layui-elem-quote quoteBox">
                <form class="layui-form">
                    <div class="layui-inline">
                        <div class="layui-input-inline">
                            <input type="text" name="userName" class="layui-input searchVal" placeholder="用户名" />
                        </div>
                        <div class="layui-input-inline">
                            <input type="text" name="email" class="layui-input searchVal" placeholder="邮箱" />
                        </div>
                        <div class="layui-input-inline">
                            <input type="text" name="phone" class="layui-input searchVal" placeholder="手机号" />
                        </div>
                        <a class="layui-btn search_btn" data-type="reload" id="btnSearch">
                            <i class="layui-icon">&#xe615;</i>
                            搜索
                        </a>
                    </div>
                </form>
            </blockquote>

            <#--数据表格-->
            <table id="userList" class="layui-table"  lay-filter="users"></table>

            <#--头部工具栏-->
            <script type="text/html" id="toolbarDemo">
                <div class="layui-btn-container">
                    <a class="layui-btn layui-btn-normal addNews_btn" lay-event="add">
                        <i class="layui-icon">&#xe608;</i>
                        添加用户
                    </a>
                    <a class="layui-btn layui-btn-normal delNews_btn" lay-event="del">
                        <i class="layui-icon">&#xe608;</i>
                        删除用户
                    </a>
                </div>
            </script>

            <!--操作-->
            <#--行工具栏-->
            <script id="userListBar" type="text/html">
                <a class="layui-btn layui-btn-xs" id="edit" lay-event="edit">编辑</a>
                <a class="layui-btn layui-btn-xs layui-btn-danger" lay-event="del">删除</a>
            </script>
        </form>

    <script type="text/javascript" src="${ctx}/js/user/user.js"></script>
    </body>
</html>
4.1.3.2. 页面入口
<dd>
  <a href="javascript:;" class="layui-menu-tips" data-type="tabAdd" data-tab-mpi="m-p-i-11" data-tab="user/index" target="_self"><i class="fa fa-user"></i><span class="layui-left-nav"> 用户管理</span></a>
 </dd>

UserController 后台设置对应的接口

 /**
     *  跳转用户模块首页
     */
    @RequestMapping("index")
    public String index(){
        return "user/user";
    }

4.1.3.3. 核心 JS

user.js

js/user 目录下添加 user.js 文件

layui.use(['table','layer'],function(){
    var layer = parent.layer === undefined ? layui.layer : top.layer,
        $ = layui.jquery,
        table = layui.table;
    /**
     * 用户列表展示
     */
    var  tableIns = table.render({
        elem: '#userList',
        url : ctx + '/user/list',
        cellMinWidth : 95,
        page : true,
        height : "full-125",
        limits : [10,15,20,25],
        limit : 10,
        toolbar: "#toolbarDemo",
        id : "userListTable",
        cols : [[
            {type: "checkbox", fixed:"left", width:50},
            {field: "id", title:'编号',fixed:"true", width:80},
            {field: 'userName', title: '用户名', minWidth:50, align:"center"},
            {field: 'email', title: '用户邮箱', minWidth:100, align:'center'},
            {field: 'phone', title: '用户电话', minWidth:100, align:'center'},
            {field: 'trueName', title: '真实姓名', align:'center'},
            {field: 'createDate', title: '创建时间', align:'center',minWidth:150},
            {field: 'updateDate', title: '更新时间', align:'center',minWidth:150},
            {title: '操作', minWidth:150, templet:'#userListBar',fixed:"right",align:"center"}
        ]]
    });
});
4.1.3.4. 多条件查询

添加多条件查询点击事件

user.ftl

 <a class="layui-btn search_btn" data-type="reload" id="btnSearch">
     <i class="layui-icon">&#xe615;</i>
             搜索
 </a>

user.js 添加搜索点击事件

 //数据表格重载
    $("#btnSearch").click(function (){
        tableIns.reload({
            where: { //设定异步数据接口的额外参数,任意设
                userName:$('[name="userName"]').val(),
                email:$('[name="email"]').val(),
                phone:$('[name="phone"]').val()
            }
            ,page: {
                curr: 1 //重新从第 1 页开始
            }
        });

    });

4.2. 用户添加与更新

4.2.1. 页面效果
4.2.2. 用户记录添加
4.2.2.1. 实现思路
/**
     * 添加用户
     *  1.校验参数
     *      用户名  非空 | 唯一
     *      邮箱    非空
     *      手机号  非空 | 格式正确
     *  2.设置默认值
     *      is_valid
     *      update_date
     *      create_date
     *      user_password  设置用户默认密码 123456(加密MD5)
     *  3.执行添加操作
     *
     */
4.2.2.2. 核心代码

UserService.java

 public void saveUser(User user){
        AssertUtil.isTrue(StringUtils.isBlank(user.getUserName()),"用户名称不能为空");
        AssertUtil.isTrue(null != userMapper.queryUserByName(user.getUserName()),"用户名已存在");
        // 校验参数
        checkUserParams(user.getEmail(),user.getPhone());
        //设置默认值
        user.setUpdateDate(new Date());
        user.setIsValid(1);
        user.setCreateDate(new Date());
        //设置用户默认密码 123456(加密MD5)
        user.setUserPwd(Md5Util.encode("123456"));
        //执行添加操作
        AssertUtil.isTrue(userMapper.insertSelective(user) < 1,"用户添加失败");
    }

    /**
     * 校验用户添加和修改的数据
     用户名  非空 | 唯一
     *      邮箱    非空
     *      手机号  非空 | 格式正确
     * @param email
     * @param phone
     */
    private void checkUserParams( String email, String phone) {

        AssertUtil.isTrue(StringUtils.isBlank(email),"邮箱不能为空");
        AssertUtil.isTrue(StringUtils.isBlank(phone),"手机号不能为空");
        AssertUtil.isTrue(!PhoneUtil.isMobile(phone),"手机号格式错误");
    }

UserController.java

@PostMapping("save")
    @ResponseBody
    public ResultInfo saveUser(User user){
        userService.saveUser(user);
        return success("用户添加成功");
    }
4.2.3. 用户记录更新
4.2.3.1. 实现思路
 /**
     * 修改用户
     *  1.校验参数
     *      id     非空|存在
     *      用户名  非空 | 唯一
     *      邮箱    非空
     *      手机号  非空 | 格式正确
     *  2.设置默认值
     *      update_date
     *  3.执行修改操作
     *
     */
4.2.3.2. 核心代码

UserService.java

 public void updateUser(User user){
        //id     非空|存在
        AssertUtil.isTrue(null == user.getId() || null == userMapper.selectByPrimaryKey(user.getId()),"数据异常请重试");
        //用户名  非空 | 唯一
        AssertUtil.isTrue(user.getUserName() == null,"用户名不能为空");
        // 名称唯一
        User dbUser = userMapper.queryUserByName(user.getUserName());
        AssertUtil.isTrue(dbUser != null && user.getId() != dbUser.getId(),"用户名已存在");
        //校验邮箱和手机号
        checkUserParams(user.getEmail(),user.getPhone());
        //设置默认值
        user.setUpdateDate(new Date());
        //执行修改操作
        AssertUtil.isTrue(userMapper.updateByPrimaryKeySelective(user) < 1,"用户修改失败");
    }

UserController.java

 @PostMapping("updateUser")
    @ResponseBody
    public ResultInfo updateUser(User user){
        userService.updateUser(user);
        return success("用户修改成功");
    }
4.2.4. 工具栏与行监听事件

user.js

    /**
     * 监听表格的头部工具栏
     */
    table.on('toolbar(users)', function(obj){
        var checkStatus = table.checkStatus(obj.config.id);
        console.log(checkStatus.data);
        switch(obj.event){
            case 'add':
                openAddOrUpdateDialog();//打开添加修改的窗口页面
                break;
            case 'del':
                deleteBatch(checkStatus.data);
                break;
        };
    });

    /**
     * 监听表格的行工具栏
     */
    table.on('tool(users)', function(obj){
        //修改操作
        if(obj.event == "edit"){
            openAddOrUpdateDialog(obj.data.id);
        }else if(obj.event == "del"){
            // 询问是否确认删除
            layer.confirm("确定要删除这条记录吗?", {icon: 3, title:"营销机会数据管理"}, function (index) {
                // 关闭窗口
                layer.close(index);
                // 发送ajax请求,删除记录
                $.ajax({
                    type:"post",
                    url: ctx + "/user/deleteBatch",
                    data:{
                        ids:obj.data.id
                    },
                    dataType:"json",
                    success:function (result) {
                        if (result.code == 200) {
                            // 加载表格
                            tableIns.reload();
                        } else {
                            layer.msg(result.msg, {icon: 5});
                        }
                    }
                });
            });
        }
    });
    
     //打开对应添加修改的窗口
    function openAddOrUpdateDialog(id){
        var title = "<h2>用户管理-用户添加</h2>";
        var url = ctx + "/user/toUpdateAddPage";

        //通过参数id判断目前是修改还是添加操作
        if(id){
            title = "<h2>用户管理-用户修改</h2>";
            url += "?id="+id;
        }

        //打开修改添加页面
        layer.open({
            type:2,   //ifame
            title:title,
            content: url,   //页面的内容
            area:["650px","400px"], //设置宽高
            maxmin:true //可以伸缩页面大小
        });
    }
4.2.5. 视图转发方法

UserController.java

UserController 添加视图转发方法

//打开添加/修改页面
    @RequestMapping("toUpdateAddPage")
    public String toUpdateAddPage(Integer id,HttpServletRequest request){
        //如果是修改操作,那么需要将数据显示在页面中
        if(id != null){
            User user = userService.selectByPrimaryKey(id);
            AssertUtil.isTrue(user == null,"数据异常请重试");
            request.setAttribute("user",user);
        }
        return "user/add_update";
    }
4.2.6. 页面模板

add_update.ftl

views/user 目录下添加add_update.ftl 模板文件.

<!DOCTYPE html>
<html>
<head>
    <#include "../common.ftl">
</head>
<body class="childrenBody">
<form class="layui-form" style="width:80%;">
    <input type="hidden" name="id" id="hidId" value="${(saleChance.id)!}">
    <input type="hidden" id="assignId" value="${(saleChance.assignMan)!}"><#--获取当前数据的指派人id-->
    <div class="layui-form-item layui-row layui-col-xs12">
        <label class="layui-form-label">客户名称</label>
        <div class="layui-input-block">
            <input type="text" class="layui-input" lay-verify="required"
                   name="customerName" id="customerName"  value="${(saleChance.customerName)!}" placeholder="请输入客户名称">
        </div>
    </div>
    <div class="layui-form-item layui-row layui-col-xs12">
        <label class="layui-form-label">机会来源</label>
        <div class="layui-input-block">
            <input type="text" class="layui-input"  name="chanceSource"
                   id="chanceSource" value="${(saleChance.chanceSource)!}" placeholder="请输入机会来源">
        </div>
    </div>
    <div class="layui-form-item layui-row layui-col-xs12">
        <label class="layui-form-label">联系人</label>
        <div class="layui-input-block">
            <input type="text" class="layui-input" name="linkMan"
                   lay-verify="required"  value="${(saleChance.linkMan)!}" placeholder="请输入联系人">
        </div>
    </div>

    <div class="layui-form-item layui-row layui-col-xs12">
        <label class="layui-form-label">联系电话</label>
        <div class="layui-input-block">
            <input type="text" class="layui-input" lay-verify="phone"
                   name="linkPhone" value="${(saleChance.linkPhone)!}" id="phone" placeholder="请输入联系电话">
        </div>
    </div>
    <div class="layui-form-item layui-row layui-col-xs12">
        <label class="layui-form-label">概要</label>
        <div class="layui-input-block">
            <input type="text" class="layui-input"
                   name="overview" value="${(saleChance.overview)!}" id="phone" placeholder="请输入概要">
        </div>
    </div>
    <div class="layui-form-item layui-row layui-col-xs12">
        <label class="layui-form-label">成功几率(%)</label>
        <div class="layui-input-block">
            <input type="text" class="layui-input" name="cgjl" value="${(saleChance.cgjl)!}"
                   placeholder="请输入成功几率">
        </div>
    </div>
    <div class="layui-form-item layui-row layui-col-xs12">
        <label class="layui-form-label">机会描述</label>
        <div class="layui-input-block">
                    <textarea placeholder="请输入机会描述信息" name="description" class="layui-textarea">
                  	    ${(saleChance.description)!}
                    </textarea>
        </div>
    </div>
    <div class="layui-form-item layui-row layui-col-xs12">
        <label class="layui-form-label">指派给</label>
        <div class="layui-input-block">
            <select name="assignMan" id="assignMan">
                <option value="">请选择</option>
            </select>
        </div>
    </div>
    <br/>
    <div class="layui-form-item layui-row layui-col-xs12">
        <div class="layui-input-block">
            <button class="layui-btn layui-btn-lg" lay-submit="" lay-filter="addOrUpdateSaleChance">
                确认
            </button>
            <button class="layui-btn layui-btn-lg layui-btn-normal">取消</button>
        </div>
    </div>
</form>
<script type="text/javascript" src="${ctx}/js/saleChance/add.update.js"></script>
</body>
</html>
4.2.7. JS 文件

add.update.js

js/user 目录下添加 add.update.js 文件,实现表单数据提交操作

layui.use(['form', 'layer','formSelects'], function () {
    var form = layui.form,
        layer = parent.layer === undefined ? layui.layer : top.layer,
        $ = layui.jquery;
    formSelects = layui.formSelects;

    /**
     * 监听表单的提交
     */
    form.on("submit(addOrUpdateUser)",function (data){
        // 提交数据时的加载层 (https://layer.layui.com/)
        var index = layer.msg("数据提交中,请稍后...",{
            icon:16, // 图标
            time:false, // 不关闭
            shade:0.8 // 设置遮罩的透明度
        });

        var url = ctx+"/user/save";
        var id = $('[name="id"]').val();
        //如果进入判断则是修改
        if(id){
            url = ctx+"/user/updateUser";
        }

        //发送请求
        $.post(url,data.field,function (data){
            if(data.code == 200){
                //关闭弹出框
                layer.close(index);
                //关闭iframe层
                layer.closeAll("iframe");
                //刷新父页面,将添加的新数据展示
                parent.location.reload();
            }else{
                layer.msg(data.msg,{icon:5})
            }
        });

        return false;
    });
});

4.3. 用户删除操作

4.3.1. 后端代码实现
4.3.1.1. 设置SQL

UserMapper.java

//批量删除
    public Integer deleteUsers(Integer[] ids);

用户数据支持数据批量删除操作,后端接收前端数组参数借助 mybatis 动态标签 foreach 实现记录批量

删除。

UserMapper.xml

<!--批量删除-->
  <update id="deleteUsers">
    update
    t_user
    set
    is_valid = 0
    where
    id in
    <foreach collection="array" open="(" close=")" item="id" separator=",">
      #{id}
    </foreach>
  </update>
4.3.1.2. Service

UserService.java

 //批量删除
    public void deleteUsers(Integer[] ids){
        AssertUtil.isTrue(ids == null || ids.length < 1,"未选中删除数据");
        AssertUtil.isTrue(userMapper.deleteUsers(ids) != ids.length,"用户删除失败");
    }
4.3.1.3. Controller

UserController.java

//批量删除
    @PostMapping("deleteBatch")
    @ResponseBody
    public ResultInfo deleteUsers(Integer[] ids){
        userService.deleteUsers(ids);
        return success();
    }
4.3.2. 前端核心代码

修改 user.js 文件,添加删除按钮事件

layui.use(['table','layer'],function(){
    var layer = parent.layer === undefined ? layui.layer : top.layer,
        $ = layui.jquery,
        table = layui.table;
    /**
     * 用户列表展示
     */
    var  tableIns = table.render({
        elem: '#userList',
        url : ctx + '/user/list',
        cellMinWidth : 95,
        page : true,
        height : "full-125",
        limits : [10,15,20,25],
        limit : 10,
        toolbar: "#toolbarDemo",
        id : "userListTable",
        cols : [[
            {type: "checkbox", fixed:"left", width:50},
            {field: "id", title:'编号',fixed:"true", width:80},
            {field: 'userName', title: '用户名', minWidth:50, align:"center"},
            {field: 'email', title: '用户邮箱', minWidth:100, align:'center'},
            {field: 'phone', title: '用户电话', minWidth:100, align:'center'},
            {field: 'trueName', title: '真实姓名', align:'center'},
            {field: 'createDate', title: '创建时间', align:'center',minWidth:150},
            {field: 'updateDate', title: '更新时间', align:'center',minWidth:150},
            {title: '操作', minWidth:150, templet:'#userListBar',fixed:"right",align:"center"}
        ]]
    });


    //数据表格重载
    $("#btnSearch").click(function (){
        tableIns.reload({
            where: { //设定异步数据接口的额外参数,任意设
                userName:$('[name="userName"]').val(),
                email:$('[name="email"]').val(),
                phone:$('[name="phone"]').val()
            }
            ,page: {
                curr: 1 //重新从第 1 页开始
            }
        });

    });


    /**
     * 监听表格的头部工具栏
     */
    table.on('toolbar(users)', function(obj){
        var checkStatus = table.checkStatus(obj.config.id);
        console.log(checkStatus.data);
        switch(obj.event){
            case 'add':
                openAddOrUpdateDialog();//打开添加修改的窗口页面
                break;
            case 'del':
                deleteBatch(checkStatus.data);
                break;
        };
    });

    /**
     * 监听表格的行工具栏
     */
    table.on('tool(users)', function(obj){
        //修改操作
        if(obj.event == "edit"){
            openAddOrUpdateDialog(obj.data.id);
        }else if(obj.event == "del"){
            // 询问是否确认删除
            layer.confirm("确定要删除这条记录吗?", {icon: 3, title:"营销机会数据管理"}, function (index) {
                // 关闭窗口
                layer.close(index);
                // 发送ajax请求,删除记录
                $.ajax({
                    type:"post",
                    url: ctx + "/user/deleteBatch",
                    data:{
                        ids:obj.data.id
                    },
                    dataType:"json",
                    success:function (result) {
                        if (result.code == 200) {
                            // 加载表格
                            tableIns.reload();
                        } else {
                            layer.msg(result.msg, {icon: 5});
                        }
                    }
                });
            });
        }
    });

    //批量删除用户
    function deleteBatch(data){
        //判断是否选中数据
        if(data.length == 0){
            layer.msg("请至少选中一条数据");
            return;
        }
        //向用户确认删除行为
        layer.confirm("您确定要删除选中的记录吗?",{
            btn:["确认","取消"],
        },function (index) {
            //关闭弹出框
            layer.close(index);

            //拼接后台需要的id数组  ids=1&ids=2
            var str = "ids=";
            for(var i = 0; i < data.length; i++){
                //判断是否是倒数第二个
                if(i < data.length - 1){
                    str += data[i].id + "&ids=";
                }else{
                    str += data[i].id;
                }
            }
            console.log(str);

            $.ajax({
                type:"post",
                url: ctx+"/user/deleteBatch",
                data:str,
                dataType:"json",
                success:function(data){
                    if(data.code == 200){
                        //刷新数据表格
                        tableIns.reload();
                    }else{
                        layer.msg(data.msg,{icon:5})
                    }
                }
            });


        })
    }


    //打开对应添加修改的窗口
    function openAddOrUpdateDialog(id){
        var title = "<h2>用户管理-用户添加</h2>";
        var url = ctx + "/user/toUpdateAddPage";

        //通过参数id判断目前是修改还是添加操作
        if(id){
            title = "<h2>用户管理-用户修改</h2>";
            url += "?id="+id;
        }

        //打开修改添加页面
        layer.open({
            type:2,   //ifame
            title:title,
            content: url,   //页面的内容
            area:["650px","400px"], //设置宽高
            maxmin:true //可以伸缩页面大小
        });
    }
});

4.4. 用户角色关联

基于角色的权限管理在实现用户基本信息维护的同时,这是还需考虑用户在系统中扮演的角色有哪

些,然后通过角色来进一步控制用户能够操作的资源,这里为用户管理角色使用到前端多选下拉框插件

formSelects 详见官网介绍。

4.4.1. 用户角色查询
<!--权限管理用户角色-->
        <table tableName="t_role" domainObjectName="Role"
               enableCountByExample="false" enableUpdateByExample="false"
               enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false">
        </table>
4.4.1.1. 页面效果
4.4.1.2. 后端代码实现

RoleMapper.xml

添加查询所有角色记录标签

<!--查询对应的角色名称和id反馈给前台使用-->
  <select id="queryAllRoles" resultType="map">
    select
        id,role_name rname
    from
        t_role
    where
        is_valid = 1
  </select>

RoleMapper.java

定义查询角色信息的接口

// 查询对应的角色名称和id反馈给前台使用
    public List<Map<String,Object>> queryAllRoles();

RoleService.java

package com.xxxx.crm.service;

import com.xxxx.crm.base.BaseService;
import com.xxxx.crm.dao.RoleMapper;
import com.xxxx.crm.vo.Role;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

@Service
public class RoleService extends BaseService<Role,Integer> {

    @Resource
    private RoleMapper roleMapper;

    // 查询对应的角色名称和id反馈给前台使用
    public List<Map<String,Object>> queryAllRoles(){
        return roleMapper.queryAllRoles();
    }
}

RoleController.java

提供方法返回所有角色记录

package com.xxxx.crm.controller;

import com.xxxx.crm.base.BaseController;
import com.xxxx.crm.service.RoleService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

@Controller
@RequestMapping("role")
public class RoleController extends BaseController {

    @Resource
    private RoleService roleService;

    // 查询对应的角色名称和id反馈给前台使用
    @RequestMapping("queryAllRoles")
    @ResponseBody
    public List<Map<String,Object>> queryAllRoles(){
        return roleService.queryAllRoles();
    }
}
4.4.1.3. 前端核心代码

user/add_update.ftl

在用户的添加和修改页面,设置选择用户角色的下拉框。

 <div class="magb15 layui-col-md4 layui-col-xs12">
                <label class="layui-form-label">角色</label>
                <div class="layui-input-block">
                    <select name="roleIds"  xm-select="selectId">
                    </select>
                </div>
            </div>

user/add.update.js

这里要引入 formSelects 模块实现下拉框数据填充操作。

 /**
     * 加载下拉框角色数据
     */
    formSelects.config('selectId',{
        type:"post",
        searchUrl:ctx + "/role/queryAllRoles",
        //自定义返回数据中name的key, 默认 name
        keyName: 'rname',
        //自定义返回数据中value的key, 默认 value
        keyVal: 'id'
    },true);
4.4.2. 用户角色关联

UserService.java

 /**
     * 添加用户
     *  1.校验参数
     *      用户名  非空 | 唯一
     *      邮箱    非空
     *      手机号  非空 | 格式正确
     *  2.设置默认值
     *      is_valid
     *      update_date
     *      create_date
     *      user_password  设置用户默认密码 123456(加密MD5)
     *  3.执行添加操作
     *
     */
    public void saveUser(User user){
        AssertUtil.isTrue(StringUtils.isBlank(user.getUserName()),"用户名称不能为空");
        AssertUtil.isTrue(null != userMapper.queryUserByName(user.getUserName()),"用户名已存在");
        // 校验参数
        checkUserParams(user.getEmail(),user.getPhone());
        //设置默认值
        user.setUpdateDate(new Date());
        user.setIsValid(1);
        user.setCreateDate(new Date());
        //设置用户默认密码 123456(加密MD5)
        user.setUserPwd(Md5Util.encode("123456"));
        //执行添加操作
        //AssertUtil.isTrue(userMapper.insertSelective(user) < 1,"用户添加失败");
        //执行添加操作,设置对应sql属性,主键返回到user对象中
        AssertUtil.isTrue(userMapper.insertHasKey(user) < 1,"用户添加失败");
        //绑定角色给用户
        relationUserRole(user.getId(),user.getRoleIds());
    }

    /**
     * 给用户绑定角色
     * @param id  用户id
     * @param roleIds 1,2,3,4  角色id
     */
    private void relationUserRole(Integer id, String roleIds) {
        //修改角色操作:查询是否原来就有角色,如果有那么直接删除再绑定新角色
        Integer count = userRoleMapper.countUserRole(id);
        if(count > 0){
            AssertUtil.isTrue(userRoleMapper.deleteUserRoleByUid(id) != count,"原有角色删除失败");
        }
        //准备一个容器接收遍历出来的新对象/新数据
        List<UserRole> urs = new ArrayList<>();
        //切割获取到每个id
        String[] splits = roleIds.split(",");
        for(String idStr:splits ){
            UserRole userRole = new UserRole();
            userRole.setUserId(id);
            userRole.setRoleId(Integer.parseInt(idStr));
            userRole.setUpdateDate(new Date());
            userRole.setCreateDate(new Date());

            //将数据添加到集合中
            urs.add(userRole);
        }
        //执行批量添加操作
        AssertUtil.isTrue(userRoleMapper.insertBatch(urs) != splits.length,"角色绑定失败");

    }

    /**
     * 校验用户添加和修改的数据
     用户名  非空 | 唯一
     *      邮箱    非空
     *      手机号  非空 | 格式正确
     * @param email
     * @param phone
     */
    private void checkUserParams( String email, String phone) {

        AssertUtil.isTrue(StringUtils.isBlank(email),"邮箱不能为空");
        AssertUtil.isTrue(StringUtils.isBlank(phone),"手机号不能为空");
        AssertUtil.isTrue(!PhoneUtil.isMobile(phone),"手机号格式错误");
    }

    /**
     * 修改用户
     *  1.校验参数
     *      id     非空|存在
     *      用户名  非空 | 唯一
     *      邮箱    非空
     *      手机号  非空 | 格式正确
     *  2.设置默认值
     *      update_date
     *  3.执行修改操作
     *
     */
    public void updateUser(User user){
        //id     非空|存在
        AssertUtil.isTrue(null == user.getId() || null == userMapper.selectByPrimaryKey(user.getId()),"数据异常请重试");
        //用户名  非空 | 唯一
        AssertUtil.isTrue(user.getUserName() == null,"用户名不能为空");
        // 名称唯一
        User dbUser = userMapper.queryUserByName(user.getUserName());
        AssertUtil.isTrue(dbUser != null && user.getId() != dbUser.getId(),"用户名已存在");
        //校验邮箱和手机号
        checkUserParams(user.getEmail(),user.getPhone());
        //设置默认值
        user.setUpdateDate(new Date());
        //执行修改操作
        AssertUtil.isTrue(userMapper.updateByPrimaryKeySelective(user) < 1,"用户修改失败");
        //修改绑定的角色
        relationUserRole(user.getId(),user.getRoleIds());
    }
    //批量删除
    public void deleteUsers(Integer[] ids){
        AssertUtil.isTrue(ids == null || ids.length < 1,"未选中删除数据");
        AssertUtil.isTrue(userMapper.deleteUsers(ids) != ids.length,"用户删除失败");
    }
4.4.3. 更新SQL配置

当更新用户信息时,如果用户有设置过角色,需要默认选中。根据当前点击用户id 查询查询所有角色并

标记用户已分配角色。

RoleMapper.xml

<!--查询对应的角色名称和id反馈给前台使用-->
  <select id="queryAllRoles" resultType="map" parameterType="int">
    SELECT
	r.role_name rname,r.id,temp.selected
    from
        t_role r
        left JOIN(
            SELECT
                r.role_name name,r.id,'selected' selected
            from
                t_user_role ur
            LEFT join
                t_role r
            on r.id = ur.role_id
            WHERE
                ur.user_id  = #{id}
        ) temp
        on temp.id = r.id
    where
        r.is_valid = 1
  </select>
4.4.4. 修改 JS 文件

add_update.js

查询角色记录时传入用户id

/**
     * 加载下拉框角色数据
     */
    formSelects.config('selectId',{
        type:"post",
        searchUrl:ctx + "/role/queryAllRoles?id="+$('[name="id"]').val(),
        //自定义返回数据中name的key, 默认 name
        keyName: 'rname',
        //自定义返回数据中value的key, 默认 value
        keyVal: 'id'
    },true);

5. 角色管理功能实现

5.1. 角色记录查询

5.1.1. 角色录查询主页面效果
5.1.2. 角色记录查询后端实现

RoleMapper.xml

<!--多条件分页查询角色-->
  <select id="selectByParams" parameterType="com.xxxx.crm.query.RoleQuery" resultType="com.xxxx.crm.vo.Role">
    select <include refid="Base_Column_List"/>
    from t_role
    <where>
      is_valid=1
      <if test="null !=roleName and roleName !=''">
        and role_name =#{roleName}
      </if>
    </where>
  </select>

RoleQuery.java 查询类定义

package com.xxxx.crm.query;

import com.xxxx.crm.base.BaseQuery;

public class RoleQuery extends BaseQuery {
    // 角色名
    private String roleName;
   
}

RoleController.java

添加角色关注主页面视图转发 &列表查询 方法

    /**
     * 打开角色页面
     * @return
     */
    @RequestMapping("index")
    public String index(){
        return "role/role";
    }

    /**
     * 多条件分页查询角色数据
     * @param roleQuery
     * @return
     */
    @RequestMapping("list")
    @ResponseBody
    public Map<String,Object> userList(RoleQuery roleQuery){
        return roleService.queryByParamsForTable(roleQuery);
    }
5.1.3. 角色管理主页面视图添加 & 核心js

角色管理主页面管理模板

views/role 目录下创建role.ftl 文件

<!DOCTYPE html>
<html>
<head>
    <title>角色管理</title>
    <#include "../common.ftl">
</head>
<body class="childrenBody">

<form class="layui-form">
    <blockquote class="layui-elem-quote quoteBox">
        <form class="layui-form">
            <div class="layui-inline">
                <div class="layui-input-inline">
                    <input type="text" name="roleName"
                           class="layui-input
               searchVal" placeholder="角色名"/>
                </div>
                <a class="layui-btn search_btn" data-type="reload"><i
                            class="layui-icon">&#xe615;</i> 搜索</a>
            </div>
        </form>
    </blockquote>
    <table id="roleList" class="layui-table" lay-filter="roles"></table>

    <script type="text/html" id="toolbarDemo">
        <div class="layui-btn-container">
            <a class="layui-btn layui-btn-normal addNews_btn" lay-event="add">
                <i class="layui-icon">&#xe608;</i>
                添加角色
            </a>
            <a class="layui-btn layui-btn-normal delNews_btn" lay-event="grant">
                <i class="layui-icon">&#xe672;</i>
                授权
            </a>
        </div>
    </script>
    <!--操作-->
    <script id="roleListBar" type="text/html">
        <a class="layui-btn layui-btn-xs" id="edit" lay-event="edit">编辑</a>
        <a class="layui-btn layui-btn-xs layui-btn-danger" lay-event="del">删除</a>
    </script>
</form>
<script type="text/javascript" src="${ctx}/js/role/role.js"></script>
</body>
</html>

role.js

js/role 目录下添加role.js 初始化角色表格数据

layui.use(['table','layer'],function(){
    var layer = parent.layer === undefined ? layui.layer : top.layer,
        $ = layui.jquery,
        table = layui.table;
    //角色列表展示
    var  tableIns = table.render({
        elem: '#roleList',
        url : ctx+'/role/list',
        cellMinWidth : 95,
        page : true,
        height : "full-125",
        limits : [10,15,20,25],
        limit : 10,
        toolbar: "#toolbarDemo",
        id : "roleListTable",
        cols : [[
            {type: "checkbox", fixed:"left", width:50},
            {field: "id", title:'编号',fixed:"true", width:80},
            {field: 'roleName', title: '角色名', minWidth:50, align:"center"},
            {field: 'roleRemark', title: '角色备注', minWidth:100, align:'center'},
            {field: 'createDate', title: '创建时间', align:'center',minWidth:150},
            {field: 'updateDate', title: '更新时间', align:'center',minWidth:150},
            {title: '操作', minWidth:150, templet:'#roleListBar',fixed:"right",align:"center"}
        ]]
    });

    // 多条件搜索
    $(".search_btn").on("click",function(){
        table.reload("roleListTable",{
            page: {
                curr: 1 //重新从第 1 页开始
            },
            where: {
                roleName: $("input[name='roleName']").val()
            }
        })
    });


    //头工具栏事件
    table.on('toolbar(roles)', function(obj){
        var checkStatus = table.checkStatus(obj.config.id);
        switch(obj.event){
            case "add":
                openAddOrUpdateRoleDialog();
                break;
            case "grant":
                openGrantRoleDialog(checkStatus);
                break;
        };
    });


    //打开授权页面
    function openGrantRoleDialog(data){
        if(data.data.length == 0){
            layer.msg("未选中授权数据");
            return;
        }
        if(data.data.length > 1){
            layer.msg("不能给多条数据授权");
            return;
        }
        var url  =  ctx+"/role/toAddGrantPage?rId="+data.data[0].id;
        var title="角色管理-角色授权";
        layui.layer.open({
            title : title,
            type : 2,
            area:["400px","480px"],
            maxmin:true,
            content : url
        });
    }


    /**
     * 行监听
     */
    table.on("tool(roles)", function(obj){
        var layEvent = obj.event;
        if(layEvent === "edit") {
            openAddOrUpdateRoleDialog(obj.data.id);
        }
    });

    // 打开添加页面
    function openAddOrUpdateRoleDialog(uid){
        var url  =  ctx+"/role/addOrUpdateRolePage";
        var title="角色管理-角色添加";
        if(uid){
            url = url+"?id="+uid;
            title="角色管理-角色更新";
        }
        layui.layer.open({
            title : title,
            type : 2,
            area:["600px","280px"],
            maxmin:true,
            content : url
        });
    }



    /**
     * 行监听
     */
    table.on("tool(roles)", function(obj){
        var layEvent = obj.event;
        if(layEvent === "edit") {
            openAddOrUpdateRoleDialog(obj.data.id);
        }else if(layEvent === "del") {
            layer.confirm('确定删除当前角色?', {icon: 3, title: "角色管理"}, function (index) {
                $.post(ctx+"/role/delete",{id:obj.data.id},function (data) {
                    if(data.code==200){
                        layer.msg("操作成功!");
                        tableIns.reload();
                    }else{
                        layer.msg(data.msg, {icon: 5});
                    }
                });
            })
        }
    });

});

5.2. 角色添加与更新

5.2.1. 后端代码实现

RoleService.java

/**
     * 添加角色
     * @param role
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void saveRole(Role role){
        AssertUtil.isTrue(StringUtils.isBlank(role.getRoleName()),"请输入角色名!");
        Role temp = roleMapper.queryRoleByRoleName(role.getRoleName());
        AssertUtil.isTrue(null !=temp,"该角色已存在!");
        role.setIsValid(1);
        role.setCreateDate(new Date());
        role.setUpdateDate(new Date());
        AssertUtil.isTrue(insertSelective(role)<1,"角色记录添加失败!");
    }

    /**
     * 修改角色
     * @param role
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void  updateRole(Role role){
        AssertUtil.isTrue(null==role.getId()||null==selectByPrimaryKey(role.getId()),"待修改的记录不存在!");
        AssertUtil.isTrue(StringUtils.isBlank(role.getRoleName()),"请输入角色名!");
        Role temp = roleMapper.queryRoleByRoleName(role.getRoleName());
        AssertUtil.isTrue(null !=temp && !(temp.getId().equals(role.getId())),"该角色已存在!");
        role.setUpdateDate(new Date());
        AssertUtil.isTrue(updateByPrimaryKeySelective(role)<1,"角色记录更新失败!");
    }

RoleController.java

/**
     * 打开添加/修改页面
     * @param id
     * @param model
     * @return
     */
    @RequestMapping("addOrUpdateRolePage")
    public String addUserPage(Integer id, Model model){
        if(null !=id){
            model.addAttribute("role",roleService.selectByPrimaryKey(id));
        }
        return "role/add_update";
    }

    /**
     * 添加角色数据
     * @param role
     * @return
     */
    @RequestMapping("save")
    @ResponseBody
    public ResultInfo saveRole(Role role){
        roleService.saveRole(role);
        return success("角色记录添加成功");
    }

    /**
     * 修改角色数据
     * @param role
     * @return
     */
    @RequestMapping("update")
    @ResponseBody
    public ResultInfo updateRole(Role role){
        roleService.updateRole(role);
        return success("角色记录更新成功");
    }
5.2.2. 前端核心代码
5.2.2.1. 添加与编辑事件

role.js

//头工具栏事件
    table.on('toolbar(roles)', function(obj){
        var checkStatus = table.checkStatus(obj.config.id);
        switch(obj.event){
            case "add":
                openAddOrUpdateRoleDialog();
                break;
        };
    });
    
    
    /**
     * 行监听
     */
    table.on("tool(roles)", function(obj){
        var layEvent = obj.event;
        if(layEvent === "edit") {
            openAddOrUpdateRoleDialog(obj.data.id);
        }
    });

    // 打开添加页面
    function openAddOrUpdateRoleDialog(uid){
        var url  =  ctx+"/role/addOrUpdateRolePage";
        var title="角色管理-角色添加";
        if(uid){
            url = url+"?id="+uid;
            title="角色管理-角色更新";
        }
        layui.layer.open({
            title : title,
            type : 2,
            area:["600px","280px"],
            maxmin:true,
            content : url
        });
    }
5.2.2.2. 模板文件

views/role 目录下添加 add_update.ftl 文件

<!DOCTYPE html>
<html>
<head>
    <#include "../common.ftl">
</head>
<body class="childrenBody">
<form class="layui-form" style="width:80%;">
    <input name="id" type="hidden" value="${(role.id)!}"/>
    <div class="layui-form-item layui-row layui-col-xs12">
        <label class="layui-form-label">角色名</label>
        <div class="layui-input-block">
            <input type="text" class="layui-input userName"
                   lay-verify="required" name="roleName" id="roleName"  value="${(role.roleName)!}" placeholder="角色名">
        </div>
    </div>
    <div class="layui-form-item layui-row layui-col-xs12">
        <label class="layui-form-label">角色备注</label>
        <div class="layui-input-block">
            <input type="text" class="layui-input userName"
                   lay-verify="required" name="roleRemark" id="roleRemark" value="${(role.roleRemark)!}" placeholder="请输入角色备注">
        </div>
    </div>

    <br/>
    <div class="layui-form-item layui-row layui-col-xs12">
        <div class="layui-input-block">
            <button class="layui-btn layui-btn-lg" lay-submit=""
                    lay-filter="addOrUpdateRole">确认
            </button>
            <button class="layui-btn layui-btn-lg layui-btn-normal">取消</button>
        </div>
    </div>
</form>
<script type="text/javascript" src="${ctx}/js/role/add.update.js"></script>
</body>
</html>
5.2.2.3. add.update.js

js/role 目录下添加 add_update.js 文件

layui.use(['form', 'layer'], function () {
    var form = layui.form,
        layer = parent.layer === undefined ? layui.layer : top.layer,
        $ = layui.jquery;
    form.on("submit(addOrUpdateRole)", function (data) {
        var index = top.layer.msg('数据提交中,请稍候', {icon: 16, time: false, shade: 0.8});
        //弹出loading
        var url=ctx + "/role/save";
        if($("input[name='id']").val()){
            url=ctx + "/role/update";
        }
        $.post(url, data.field, function (res) {
            if (res.code == 200) {
                setTimeout(function () {
                    top.layer.close(index);
                    top.layer.msg("操作成功!");
                    layer.closeAll("iframe");
                    //刷新父页面
                    parent.location.reload();
                }, 500);
            } else {
                layer.msg(
                    res.msg, {
                        icon: 5
                    }
                );
            }
        });
        return false;
    });
});

5.3. 角色删除

5.3.1. 后端代码实现

RoleService.java

/**
     * 删除角色
     * @param roleId
     */
    public void deleteRole(Integer roleId){
        Role temp =selectByPrimaryKey(roleId);
        AssertUtil.isTrue(null==roleId||null==temp,"待删除的记录不存在!");
        temp.setIsValid(0);
        AssertUtil.isTrue(updateByPrimaryKeySelective(temp)<1,"角色记录删除失败!");
    }

RoleController.java

    /**
     * 删除角色
     * @param id
     * @return
     */
    @RequestMapping("delete")
    @ResponseBody
    public ResultInfo deleteRole(Integer id){
        roleService.deleteRole(id);
        return success("角色记录删除成功");
    }
5.3.2. 前端代码实现

修改role.js 添加删除事件实现角色记录删除操作

 /**
     * 行监听
     */
    table.on("tool(roles)", function(obj){
        var layEvent = obj.event;
        if(layEvent === "edit") {
            openAddOrUpdateRoleDialog(obj.data.id);
        }else if(layEvent === "del") {
            layer.confirm('确定删除当前角色?', {icon: 3, title: "角色管理"}, function (index) {
                $.post(ctx+"/role/delete",{id:obj.data.id},function (data) {
                    if(data.code==200){
                        layer.msg("操作成功!");
                        tableIns.reload();
                    }else{
                        layer.msg(data.msg, {icon: 5});
                    }
                });
            })
        }
    });

5.4. 角色授权

树形数据展示

完成角色记录基本crud功能之后,接下来实现角色授权功能,这里实现角色授权首先完成待授权资源

显示功能。对于资源的显示,这里使用开源的tree插件 ztree。

<table tableName="t_module" domainObjectName="Module"
               enableCountByExample="false" enableUpdateByExample="false"
               enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false">
        </table>

5.4.1. 资源数据查询后端实现

前端ztree显示的资源数据格式参考官网。

ModuleMapper.xml

 <!--  //查询所有资源-->
  <select id="queryAllModules" resultType="com.xxxx.crm.model.TreeModel">
    SELECT id,module_name name,parent_id pId from t_module WHERE is_valid = 1
  </select>

ModuleService.java

Service
public class ModuleService extends BaseService<Module,Integer> {

    @Resource
    private ModuleMapper moduleMapper;
   
    //查询所有资源
   public List<TreeModel> queryAllModules(){
       return moduleMapper.queryAllModules();
   }
   
}

ModuleController.java

@Controller
@RequestMapping("module")
public class ModuleController extends BaseController {

    @Resource
    private ModuleService moduleService;

    //查询所有资源
    @RequestMapping("queryAllModules")
    @ResponseBody
    public List<TreeModel> queryAllModules(){
        return moduleService.queryAllModules();
    }
}

5.4.2. 资源数据ztree显示

role.js 添加授权点击事件

 //头工具栏事件
    table.on('toolbar(roles)', function(obj){
        var checkStatus = table.checkStatus(obj.config.id);
        switch(obj.event){
            case "add":
                openAddOrUpdateRoleDialog();
                break;
            case "grant":
                openGrantRoleDialog(checkStatus);
                break;
        };
    });


    //打开授权页面
    function openGrantRoleDialog(data){
        if(data.data.length == 0){
            layer.msg("未选中授权数据");
            return;
        }
        if(data.data.length > 1){
            layer.msg("不能给多条数据授权");
            return;
        }
        var url  =  ctx+"/role/toAddGrantPage?rId="+data.data[0].id;
        var title="角色管理-角色授权";
        layui.layer.open({
            title : title,
            type : 2,
            area:["400px","480px"],
            maxmin:true,
            content : url
        });
    }

RoleController.java 添加视图转发方法

**
     * 跳转到授权页面
     * @return
     */
    @RequestMapping("toAddGrantPage")
    public String toAddGrantPage(Integer rId, HttpServletRequest request){
        AssertUtil.isTrue(rId == null,"角色不存在");
        request.setAttribute("roleId",rId);
        return "role/grant";
    }

准备显示资源数据模板

views/role目录下添加grant.ftl 模板文件()

<html>
<head>
	<link rel="stylesheet" href="${ctx}/js/zTree_v3/css/zTreeStyle/zTreeStyle.css" type="text/css">
	<script type="text/javascript" src="${ctx}/js/zTree_v3/js/jquery-3.5.1.min.js"></script>
	<script type="text/javascript" src="${ctx}/js/zTree_v3/js/jquery.ztree.core.js"></script>
	<script type="text/javascript" src="${ctx}/js/zTree_v3/js/jquery.ztree.excheck.js"></script>
	<script type="text/javascript">
		var ctx="${ctx}";
	</script>
</head>
<body>
<input type="hidden" name="roleId" value="${roleId!}"/>

<div id="test1" class="ztree"></div>

<script type="text/javascript" src="${ctx}/js/role/grant.js"></script>
</body>
</html>

添加grant.js

var zTreeObj;
//加载权限树形结构
function loadModuleInfo(){
    $.ajax({
        type:'get',
        url:ctx+'/module/queryAllModules?rId='+$('[name="roleId"]').val(),
        dataType:'json',
        success:function (data){

            // zTree 的参数配置,深入使用请参考 API 文档(setting 配置详解)
            var setting = {
                check: {
                    enable: true  //复选框
                },
                data: {
                    simpleData: {
                        enable: true  //支持简单json数据的展示
                    }
                },
                callback: {
                    onCheck: zTreeOnCheck //用于捕获 checkbox / radio 被勾选 或 取消勾选的事件回调函数
                }
            };

            $(document).ready(function(){
                zTreeObj = $.fn.zTree.init($("#test1"), setting, data);
            });
        }
    });
}
5.4.3. 角色授权
<table tableName="t_permission" domainObjectName="Permission"       enableCountByExample="false" enableUpdateByExample="false"       enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"></table>
5.4.3.1 Permission

PermissionMapper.xml

 <!--批量添加-->
  <insert id="insertBatch" parameterType="permission">
    insert into t_permission
        (role_id, module_id, acl_value, create_date, update_date)
    values
        <foreach collection="list" item="per" separator=",">
          (#{per.roleId},#{per.moduleId},#{per.aclValue},#{per.createDate},#{per.updateDate})
        </foreach>
  </insert>

<!--  //判断当前角色原来是否有资源-->
  <select id="countPermission" parameterType="int" resultType="int">
    select count(1) from t_permission where role_id = #{roleId}
  </select>


<!--  将原有的资源全部删除-->
  <delete id="deletePermissionByRoleId" parameterType="int">
    delete from t_permission where role_id = #{roleId}
  </delete>

PermissionMapper

public interface PermissionMapper extends BaseMapper<Permission,Integer> {

    //判断当前角色原来是否有资源
    Integer countPermission(Integer roleId);

    //将原有的资源全部删除
    Integer deletePermissionByRoleId(Integer roleId);

    // 查询当前角色拥有的权限
    List<Integer> selectPermissionByRid(Integer rId);
}
5.4.3.2. 权限记录添加后端实现

RoleService.java

 /**
     * 添加权限
     * @param roleId
     * @param mIds
     */
    public void addGrant(Integer roleId, Integer[] mIds) {
        //判断角色是否存在
        Role role = roleMapper.selectByPrimaryKey(roleId);
        AssertUtil.isTrue(role == null,"角色不存在");
        //判断需要绑定的权限是否传输过来
        // AssertUtil.isTrue(mIds == null || mIds.length < 1,"需要绑定的模块不存在");
        //判断当前角色原来是否有资源
        Integer count = permissionMapper.countPermission(roleId);
        if(count > 0){
            //将原有的资源全部删除
            AssertUtil.isTrue(permissionMapper.deletePermissionByRoleId(roleId) != count,"数据异常请重试");
        }
        //给角色绑定权限
        List<Permission> permissions = new ArrayList<>();
        for(Integer mid:mIds){
            Permission permission = new Permission();

            permission.setRoleId(roleId);
            permission.setModuleId(mid);
            permission.setAclValue(moduleMapper.selectByPrimaryKey(mid).getOptValue());//设置权限码  需要去module表中查询得到
            permission.setCreateDate(new Date());
            permission.setUpdateDate(new Date());

            permissions.add(permission);
        }

        //执行批量添加操作,绑定多个权限
        AssertUtil.isTrue(permissionMapper.insertBatch(permissions) != permissions.size(),"权限绑定失败");

    }

RoleController.java

 /**
     * 绑定权限
     * @param roleId
     * @param mIds
     * @return
     */
    @PostMapping("addGrant")
    @ResponseBody
    public ResultInfo addGrant(Integer roleId,Integer[] mIds){
        roleService.addGrant(roleId,mIds);
        return success();
    }
5.4.3.3. 权限记录添加前端核心js

修改grant.js文件 添加ztree 复选框点击回调onCheck事件

var zTreeObj;
//加载权限树形结构
function loadModuleInfo(){
    $.ajax({
        type:'get',
        url:ctx+'/module/queryAllModules?rId=',
        dataType:'json',
        success:function (data){

            // zTree 的参数配置,深入使用请参考 API 文档(setting 配置详解)
            var setting = {
                check: {
                    enable: true  //复选框
                },
                data: {
                    simpleData: {
                        enable: true  //支持简单json数据的展示
                    }
                },
                callback: {
                    onCheck: zTreeOnCheck //用于捕获 checkbox / radio 被勾选 或 取消勾选的事件回调函数
                }
            };

            $(document).ready(function(){
                zTreeObj = $.fn.zTree.init($("#test1"), setting, data);
            });
        }
    });
}

//监听多选框单选框的选中状态
function zTreeOnCheck(){
    var nodes = zTreeObj.getCheckedNodes(true); //获取所有被选中的节点数据
    //循环拼接mIds
    var str = "mIds="
    for(var i = 0; i < nodes.length; i++){
        //判断是否是倒数第二个
        if(i < nodes.length - 1){
            str += nodes[i].id + "&mIds=";
        }else{
            str += nodes[i].id;
        }
    }
    console.log(str);

    //发送请求
    $.ajax({
        type:'post',
        url:ctx+"/role/addGrant?roleId="+$('[name="roleId"]').val(),
        data:str,             //模块id
        dataType: 'json',
        success:function (data){
            if(data.code == 200){

            }else{
              // layer.msg(data.msg,{icon:5})
            }
        }
    });
5.4.3.4. 角色已添加权限记录回显

这里要实现已添加的角色记录权限再次查看或授权时显示原始权限的功能,ztree复选框是否选择属性

配置参考这里。

5.4.3.4.1. 资源查询后端方法实现

ModuleService.java

@Service
public class ModuleService extends BaseService<Module,Integer> {

    @Resource
    private ModuleMapper moduleMapper;

    @Resource
    private RoleMapper roleMapper;

    @Resource
    private PermissionMapper permissionMapper;
    //查询所有资源
   /*public List<TreeModel> queryAllModules(){
       return moduleMapper.queryAllModules();
   }*/
    public List<TreeModel> queryAllModules(Integer rId){
        //角色非空且存在
        AssertUtil.isTrue(rId == null || roleMapper.selectByPrimaryKey(rId) == null,"角色不存在");
        //查询当前角色拥有的权限
        List<Integer> mIds = permissionMapper.selectPermissionByRid(rId);
        //查询所有的模块
        List<TreeModel> treeModels = moduleMapper.queryAllModules();
        //遍历需要返回到前台的所有资源
        for(TreeModel treeModel:treeModels){
            //获取当前遍历对象的模块id
            Integer id = treeModel.getId();
            //判断当前角色拥有的权限中是否包含了 遍历对象的模块id
            if(mIds.contains(id)){  //当前方法判断某个数据是否存在于这个集合中
                treeModel.setChecked(true);
                treeModel.setOpen(true);
            }
        }
        return treeModels;
    }
}

角色拥有权限sql查询PermissionMapper.xml

<!--  // 查询当前角色拥有的权限-->
  <select id="selectPermissionByRid" parameterType="int" resultType="int">
    select module_id  from t_permission where role_id = #{rId}
  </select>

ModuleController.java

@Controller
@RequestMapping("module")
public class ModuleController extends BaseController {

    @Resource
    private ModuleService moduleService;

    //查询所有资源
    @RequestMapping("queryAllModules")
    @ResponseBody
    public List<TreeModel> queryAllModules(Integer rId){
        return moduleService.queryAllModules(rId);
    }
}
5.4.1.3.2. 权限回显前端js

这里修改grant.js 查询资源时传入当前选择角色id

var zTreeObj;
//加载权限树形结构
function loadModuleInfo(){
    $.ajax({
        type:'get',
        url:ctx+'/module/queryAllModules?rId='+$('[name="roleId"]').val(),
        dataType:'json',
        success:function (data){

            // zTree 的参数配置,深入使用请参考 API 文档(setting 配置详解)
            var setting = {
                check: {
                    enable: true  //复选框
                },
                data: {
                    simpleData: {
                        enable: true  //支持简单json数据的展示
                    }
                },
                callback: {
                    onCheck: zTreeOnCheck //用于捕获 checkbox / radio 被勾选 或 取消勾选的事件回调函数
                }
            };

            $(document).ready(function(){
                zTreeObj = $.fn.zTree.init($("#test1"), setting, data);
            });
        }
    });
}

5.5. 角色权限认证

当完成角色权限添加功能后,下一步就是对角色操作的资源进行认证操作,这里对于认证包含两块:

  1. 菜单级别显示控制

  2. 后端方法访问控制

5.5.1. 菜单级别访问控制实现

系统根据登录用户扮演的不同角色来对登录用户操作的菜单进行动态控制显示操作,这里显示的控制

使用freemarker指令+内建函数实现,指令与内建函数操作参考序列内建函数 - FreeMarker 中文官方参考手册 (foofun.cn)

5.5.1.1. 登录用户角色拥有权限查询实现

IndexController.java

 /**
     * 后端管理主页面
     * @return
     */
    @RequestMapping("main")
  /*  public String main() {
        return "main";
    }*/
    public String main(HttpServletRequest request){
        //将用户数据查询出来,设置到作用域中
        int id = LoginUserUtil.releaseUserIdFromCookie(request);
        User user = userService.selectByPrimaryKey(id);
        request.setAttribute("user",user);
        //当用户登录后进去主页面之前将当前用户具备所有的权限码查询出来,放在session作用域中
        List<String> permission = permissionService.selectAclvalueByUserId(id);
        request.getSession().setAttribute("permission",permission);//权限码
        return "main";
    }

PermissionService.java

@Service
public class PermissionService extends BaseService<Permission,Integer> {

    @Resource
    private PermissionMapper permissionMapper;

    public List<String> selectAclvalueByUserId(int id) {
        return permissionMapper.selectAclvalueByUserId(id);
    }
}

PermissionMapper.java & PermissionMapper.xml

public interface PermissionMapper extends BaseMapper<Permission,Integer> {

    //判断当前角色原来是否有资源
    Integer countPermission(Integer roleId);

    //将原有的资源全部删除
    Integer deletePermissionByRoleId(Integer roleId);

    // 查询当前角色拥有的权限
    List<Integer> selectPermissionByRid(Integer rId);

    //根据登录用户的id查询对应的权限
    List<String> selectAclvalueByUserId(int id);

}
<!--  //根据登录用户的id查询对应的权限码-->
  <select id="selectAclvalueByUserId" parameterType="int" resultType="int">
    SELECT
        DISTINCT acl_value
    FROM
        t_permission p
        LEFT JOIN t_user_role AS ur ON p.role_id = ur.role_id
    WHERE
        ur.user_id = #{id}
  </select>
5.5.1.2. 系统主页面菜单显示指令控制

这里仅显示部分菜单控制。

main.ftl

<#if permission??>
                <ul class="layui-nav layui-nav-tree layui-left-nav-tree layui-this" id="currency">
                    <#if permission?seq_contains(10)>
                        <li class="layui-nav-item">
                            <a href="javascript:;" class="layui-menu-tips"><i class="fa fa-street-view"></i><span class="layui-left-nav"> 营销管理</span> <span class="layui-nav-more"></span></a>
                            <dl class="layui-nav-child">
                                    <dd>
                                        <a href="javascript:;" class="layui-menu-tips" data-type="tabAdd" data-tab-mpi="m-p-i-1" data-tab="sale_chance/index" target="_self"><i class="fa fa-tty"></i><span class="layui-left-nav"> 营销机会管理</span></a>
                                    </dd>
                                    <dd>
                                        <a href="javascript:;" class="layui-menu-tips" data-type="tabAdd" data-tab-mpi="m-p-i-2" data-tab="cus_dev_plan/index" target="_self"><i class="fa fa-ellipsis-h"></i><span class="layui-left-nav"> 客户开发计划</span></a>
                                    </dd>
                            </dl>
                        </li>
                    </#if>

sale_chance.ftl

 <#--头部工具栏-->
    <script type="text/html" id="toolbarDemo">
        <div class="layui-btn-container">
            <#if permission?seq_contains(101002)>
                <a class="layui-btn layui-btn-normal addNews_btn" lay-event="add">
                    <i class="layui-icon">&#xe608;</i>
                    添加
                </a>
            </#if>
            <#if permission?seq_contains(101003)>
                <a class="layui-btn layui-btn-normal delNews_btn" lay-event="del">
                    <i class="layui-icon">&#xe608;</i>
                    删除
                </a>
            </#if>
        </div>
    </script>

5.5.2. 后端方法级别访问控制

实现了菜单级别显示控制,但最终客户端有可能会通过浏览器来输入资源地址从而越过ui界面来访问后

端资源,所以接下来加入控制方法级别资源的访问控制操作,这里使用aop+自定义注解实现

5.5.2.1. 自定义注解@RequirePermission

package com.xxxx.crm.annotation;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequirePermission {
   /* int code() ;*/
    String code() default "";
}
5.5.2.2. 方法级别使用注解

SaleChanceController

/**
     * 添加数据
     * @return
     */
    @RequestMapping("save")
    @ResponseBody
    @RequirePermission(code = "101002")
    public ResultInfo save(HttpServletRequest request, SaleChance saleChance){
        //获取创建人
        String userName = CookieUtil.getCookieValue(request, "userName");
        saleChance.setCreateMan(userName);
        saleChanceService.addSlaChance(saleChance);
        return success();
    }

5.5.2.3. 定义aop切面类 拦截指定注解标注的方法

package com.xxxx.crm.annotation;

@Component
@Aspect
public class PermissionProxy {

    //@Autowired
    @Resource
    private HttpSession session;

    //环绕通知(需要放行才能够到达目标方法),拦截有这个注解的所有方法
    @Around(value = "@annotation(com.xxxx.crm.annotation.RequirePermission)")
    public  Object around(ProceedingJoinPoint pjp) throws Throwable {
        //设置当前登录用户的所有权限
        List<Integer> permissions = (List<Integer>) session.getAttribute("permissions");
        //如果没有权限,直接跳转到登录页面,相当于不通过前台页面访问过来,而是直接输入地址进来
        if(null == permissions || permissions.size()==0){
            throw  new NoLoginException();
        }
        Object result =null;
        //获取目标方法
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        //获取目标方法注解的权限值
        RequirePermission requirePermission = methodSignature.getMethod().getDeclaredAnnotation(RequirePermission.class);
        if(!(permissions.contains(requirePermission.code()))){
            throw  new NoLoginException();
        }
        result= pjp.proceed();//放行
        return result;
    }
}

6. 资源管理功能实现

6.1. 资源记录查询

6.1.1. 页面效果
6.1.2. 后端代码实现

菜单资源展示这里使用layui treeTable组件显示,treetable使用参考这里。

ModuleMapper.xml

<select id="queryModules" resultType="com.xxxx.crm.vo.Module">
    select * from t_module where is_valid = 1
  </select>

ModuleMapper

 //查询所有资源 资源管理使用
    public List<Module> queryModules();

ModuleService.java

 //查询所有的模块资源
    public Map<String,Object> queryModules(){
        Map<String,Object> map = new HashMap<>();
        List<Module> modules = moduleMapper.queryModules();
        AssertUtil.isTrue(modules == null || modules.size()<1,"数据显示异常");
        //准备前台需要的数据接口
        map.put("code",0);
        map.put("msg","");
        map.put("count",modules.size());
        map.put("data",modules);
        return map;
    }

ModuleController.java

 //查询所有资源 资源管理使用
    @RequestMapping("list")
    @ResponseBody
    public Map<String,Object> queryModules(){
        return moduleService.queryModules();
    }

    //跳转到模块页面
    @RequestMapping("index")
    public String toIndex(){
        return "module/module";
    }
6.1.3. 前端代码实现

module.ftl 模板文件添加

views/module 目录下添加module.ftl 模板文件

<!DOCTYPE html>
<html>
<head>
    <title>资源管理</title>
    <#include "../common.ftl">
</head>
<body class="childrenBody">
    <table id="munu-table" class="layui-table" lay-filter="munu-table"></table>

    <!-- 操作列 行工具栏-->
    <script type="text/html" id="auth-state">
        <a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="add">添加子项</a>
        <a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="edit">修改</a>
        <a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
    </script>

<#--    //头部工具栏-->
    <script type="text/html" id="toolbarDemo">
        <div class="layui-btn-container">
            <a class="layui-btn layui-btn-normal addNews_btn" lay-event="expand">
                <i class="layui-icon">&#xe608;</i>
                全部展开
            </a>
            <a class="layui-btn layui-btn-normal addNews_btn" lay-event="fold">
                <i class="layui-icon">&#xe608;</i>
                全部折叠
            </a>
            <a class="layui-btn layui-btn-normal addNews_btn" lay-event="add">
                <i class="layui-icon">&#xe608;</i>
                添加目录
            </a>
        </div>
    </script>

    <script type="text/javascript" src="${ctx}/js/module/module.js"></script>
</body>
</html>

module.js 文件添加

js/module 目录下添加module.js,初始化表格数据

layui.use(['table', 'treetable'], function () {
    var $ = layui.jquery;
    var table = layui.table;
    var treeTable = layui.treetable;

    // 渲染表格
    treeTable.render({
        treeColIndex: 1,
        treeSpid: -1,
        treeIdName: 'id',
        treePidName: 'parentId',
        elem: '#munu-table',
        url: ctx+'/module/list',
        toolbar: "#toolbarDemo",
        treeDefaultClose:true,
        page: true,
        cols: [[
            {type: 'numbers'},
            {field: 'moduleName', minWidth: 100, title: '菜单名称'},
            {field: 'optValue', title: '权限码'},
            {field: 'url', title: '菜单url'},
            {field: 'createDate', title: '创建时间'},
            {field: 'updateDate', title: '更新时间'},
            {
                field: 'grade', width: 80, align: 'center', templet: function (d) {
                    if (d.grade == 0) {
                        return '<span class="layui-badge layui-bg-blue">目录</span>';
                    }
                    if(d.grade==1){
                        return '<span class="layui-badge-rim">菜单</span>';
                    }
                    if (d.grade == 2) {
                        return '<span class="layui-badge layui-bg-gray">按钮</span>';
                    }
                }, title: '类型'
            },
            {templet: '#auth-state', width: 260, align: 'center', title: '操作'}
        ]],
        done: function () {
            layer.closeAll('loading');
        }
    });


    /**
     * 监听头部工具栏
     */
    table.on("toolbar(munu-table)",function (data) {
        if(data.event == "expand"){
            //全部展开
            treeTable.expandAll('#munu-table');
        }else if(data.event == "fold"){
            //全部折叠
            treeTable.foldAll('#munu-table');
        }else if(data.event == "add"){
            openAddModuleDialog(0,-1);
        }
    });


    /**
     * 监听行部工具栏
     */
    table.on("tool(munu-table)",function (data) {
        if(data.event == "add"){
            if(data.data.grade == 2){
                layer.msg("未设置四级模块")
                return;
            }
            //打开添加窗口
            openAddModuleDialog(data.data.grade+1,data.data.id);
        }else if(data.event == "edit"){
            openupdateModuleDialog(data.data.id);

        }else if(data.event == "del"){
            console.log(data);

            layer.confirm('确定删除当前资源?', {icon: 3, title: "资源管理"}, function (index) {
                $.post(ctx+"/module/delete",{mId:data.data.id},function (data) {
                    if(data.code==200){
                        layer.msg("操作成功!",{icon:6});
                        parent.location.reload();  /*刷新页面*/
                        // data.del();
                    }else{
                        layer.msg(data.msg, {icon: 5});
                    }
                });
            })
        }
    });
});


//修改窗口
function openupdateModuleDialog(id){
    var url  =  ctx+"/module/updateModulePage?id="+id;
    var title="菜单更新";
    layui.layer.open({
        title : title,
        type : 2,
        area:["700px","450px"],
        maxmin:true,
        content : url
    });
}


//打开添加窗口
function openAddModuleDialog(grade,parentId){
    var title = "菜单管理-添加模块";
    var url = ctx + "/module/toAdd?grade="+grade+"&parentId="+parentId;
    layer.open({
        title:title,
        type:2,
        content:url,
        maxmin:true,
        area:["700px","450px"]
    });
}

6.2. 资源记录添加 & 更新

ModuleMapper

public interface ModuleMapper extends BaseMapper<Module,Integer> {

    //查询所有资源 授权使用
    public List<TreeModel> queryAllModules();
    //查询所有资源 资源管理使用
    public List<Module> queryModules();

    //同级下名称唯一
    Module queryModulByGradeAName(@Param("grade") Integer grade,@Param("moduleName") String moduleName);

    Module queryModulByGradeAUrl(Integer grade, String url);

    Module queryModulById(Integer parentId);

    Module queryModulByOptValue(String optValue);

}

ModuleMapper.xml

 <!-- 查询所有资源填充树形结构-->
  <select id="queryAllModules" resultType="com.xxxx.crm.model.TreeModel">
    SELECT id,module_name name,parent_id pId from t_module WHERE is_valid = 1
  </select>

  <select id="queryModules" resultType="com.xxxx.crm.vo.Module">
    select * from t_module where is_valid = 1
  </select>

  <select id="queryModulByGradeAName" resultType="com.xxxx.crm.vo.Module">
    select * from t_module where is_valid = 1 and grade = #{grade} and module_name = #{moduleName}
  </select>

  <select id="queryModulByGradeAUrl" resultType="com.xxxx.crm.vo.Module">
    select * from t_module where is_valid = 1 and grade = #{grade} and url = #{url}
  </select>

  <select id="queryModulById" parameterType="Integer" resultType="com.xxxx.crm.vo.Module">
    select * from t_module where is_valid = 1 and id = #{parentId}
  </select>

  <select id="queryModulByOptValue" parameterType="String" resultType="com.xxxx.crm.vo.Module">
    select * from t_module where is_valid = 1 and opt_value = #{optValue}
  </select>

6.2.1. 资源记录添加
6.2.1.1. 实现思路
 /**
     * 模块添加
     *   1.数据校验
     模块名称
     非空,同级唯一
     地址 URL
     二级菜单:非空,同级唯一
     父级菜单 parentId
     一级:null | -1
     二级|三级:非空 | 必须存在
     层级 grade
     非空  值必须为 0|1|2
     权限码
     非空  唯一
     2.默认值
     is_valid
     updateDate
     createDate
     3.执行添加操作  判断受影响行数
     *
     * @param module
     */
6.2.1.2. 后端代码实现
@Transactional
    public void moduleAdd(Module module) {
        //层级 grade  非空  值必须为 0|1|2
        AssertUtil.isTrue(module.getGrade() == null,"层级不能为空");
        AssertUtil.isTrue(!(module.getGrade() == 0 || module.getGrade() == 1 || module.getGrade() == 2),"层级有误");

        //模块名称 非空  同级唯一
        AssertUtil.isTrue(StringUtils.isBlank(module.getModuleName()),"模块名称不能为空");
        Module dbModule = moduleMapper.queryModulByGradeAName(module.getGrade(),module.getModuleName());
        AssertUtil.isTrue(dbModule != null,"模块名称已存在");

        // 二级菜单URL:非空,同级唯一
        if(module.getGrade() == 1){
            AssertUtil.isTrue(StringUtils.isBlank(module.getUrl()),"模块地址不能为空");
            dbModule = moduleMapper.queryModulByGradeAUrl(module.getGrade(),module.getUrl());
            AssertUtil.isTrue(dbModule != null,"地址已存在,请重新输入");
        }

        //父级菜单  二级|三级:非空 | 必须存在
        if(module.getGrade() == 1 || module.getGrade() == 2){
            AssertUtil.isTrue(module.getParentId() == null,"父ID不能为空");
            dbModule = moduleMapper.queryModulById(module.getParentId());
            AssertUtil.isTrue(dbModule == null,"父ID不存在");
        }

        //权限码  非空  唯一
        AssertUtil.isTrue(StringUtils.isBlank(module.getOptValue()),"权限码不能为空");
        dbModule = moduleMapper.queryModulByOptValue(module.getOptValue());
        AssertUtil.isTrue(dbModule != null,"权限码已存在");

        //默认值
        module.setIsValid((byte) 1);
        module.setCreateDate(new Date());
        module.setUpdateDate(new Date());

        //执行添加操作  判断受影响行数
        AssertUtil.isTrue(moduleMapper.insertSelective(module) < 1,"模块添加失败");
    }
6.2.2. 资源记录更新
6.2.2.1. 实现思路
/**
     * 修改模块
     1.数据校验
     id
     非空,并且资源存在
     模块名称
     非空,同级唯一
     地址 URL
     二级菜单:非空,同级唯一
     父级菜单 parentId
     一级:null | -1
     二级|三级:非空 | 必须存在
     层级 grade
     非空  值必须为 0|1|2
     权限码
     非空  唯一
     2.默认值
     is_valid
     updateDate
     createDate
     3.执行修改操作  判断受影响行数
     * @param module
     */
6.2.2.2. 后端核心代码
@Transactional
    public void moduleUpdate(Module module) {
        // id  非空,并且资源存在
        AssertUtil.isTrue(module.getId() == null,"待删除的资源不存在");
        Module dbModule = moduleMapper.selectByPrimaryKey(module.getId());
        AssertUtil.isTrue(dbModule == null,"系统异常");

        //层级 grade  非空  值必须为 0|1|2
        AssertUtil.isTrue(module.getGrade() == null,"层级不能为空");
        AssertUtil.isTrue(!(module.getGrade() == 0 || module.getGrade() == 1 || module.getGrade() == 2),"层级有误");

        //模块名称 非空  同级唯一
        AssertUtil.isTrue(StringUtils.isBlank(module.getModuleName()),"模块名称不能为空");
        dbModule = moduleMapper.queryModulByGradeAName(module.getGrade(),module.getModuleName());
        AssertUtil.isTrue(dbModule != null && !(module.getId().equals(dbModule.getId())),"模块名称已存在");

        // 二级菜单URL:非空,同级唯一
        if(module.getGrade() == 1){
            AssertUtil.isTrue(StringUtils.isBlank(module.getUrl()),"模块地址不能为空");
            dbModule = moduleMapper.queryModulByGradeAUrl(module.getGrade(),module.getUrl());
            AssertUtil.isTrue(dbModule != null && !(module.getId().equals(dbModule.getId())),"地址已存在,请重新输入");
        }

        //父级菜单  二级|三级:非空 | 必须存在
        if(module.getGrade() == 1 || module.getGrade() == 2){
            AssertUtil.isTrue(module.getParentId() == null,"父ID不能为空");
            dbModule = moduleMapper.queryModulById(module.getParentId());
            AssertUtil.isTrue(dbModule == null && !(module.getId().equals(dbModule.getId())),"父ID不存在");
        }

        //权限码  非空  唯一
        AssertUtil.isTrue(StringUtils.isBlank(module.getOptValue()),"权限码不能为空");
        dbModule = moduleMapper.queryModulByOptValue(module.getOptValue());
        AssertUtil.isTrue(dbModule != null && !(module.getId().equals(dbModule.getId())),"权限码已存在");

        //默认值
        module.setUpdateDate(new Date());

        //执行修改操作
        AssertUtil.isTrue(moduleMapper.updateByPrimaryKeySelective(module) < 1,"资源修改失败");
    }
6.2.3. 控制层核心代码
@Controller
@RequestMapping("module")
public class ModuleController extends BaseController {

    @Resource
    private ModuleService moduleService;

    //查询所有资源
    @RequestMapping("queryAllModules")
    @ResponseBody
    public List<TreeModel> queryAllModules(Integer rId){
        return moduleService.queryAllModules(rId);
    }

    //查询所有资源 资源管理使用
    @RequestMapping("list")
    @ResponseBody
    public Map<String,Object> queryModules(){
        return moduleService.queryModules();
    }

    //跳转到模块页面
    @RequestMapping("index")
    public String toIndex(){
        return "module/module";
    }

    @RequestMapping("toAdd")
    public String toAdd(Integer grade, Integer parentId, HttpServletRequest request){
        request.setAttribute("grade",grade);
        request.setAttribute("parentId",parentId);
        return "module/add";
    }

    @RequestMapping("updateModulePage")
    public String updateModulePage(Integer id,HttpServletRequest request){
        Module module = moduleService.selectByPrimaryKey(id);
        request.setAttribute("module",module);
        return "module/update";
    }

    // 资源记录添加
    @RequestMapping("add")
    @ResponseBody
    public ResultInfo moduleAdd(Module module){
        moduleService.moduleAdd(module);
        return success("资源添加成功");
    }

    @RequestMapping("update")
    @ResponseBody
    public ResultInfo moduleUpdate(Module module){
        moduleService.moduleUpdate(module);
        return success("资源修改成功");
    }


}
6.2.4. 前端核心代码
6.2.4.1. 添加与编辑监听事件
  /**
     * 监听头部工具栏
     */
    table.on("toolbar(munu-table)",function (data) {
        if(data.event == "expand"){
            //全部展开
            treeTable.expandAll('#munu-table');
        }else if(data.event == "fold"){
            //全部折叠
            treeTable.foldAll('#munu-table');
        }else if(data.event == "add"){
            openAddModuleDialog(0,-1);
        }
    });


    /**
     * 监听行部工具栏
     */
    table.on("tool(munu-table)",function (data) {
        if(data.event == "add"){
            if(data.data.grade == 2){
                layer.msg("未设置四级模块")
                return;
            }
            //打开添加窗口
            openAddModuleDialog(data.data.grade+1,data.data.id);
        }else if(data.event == "edit"){
            openupdateModuleDialog(data.data.id);

        }else if(data.event == "del"){
            console.log(data);

            layer.confirm('确定删除当前资源?', {icon: 3, title: "资源管理"}, function (index) {
                $.post(ctx+"/module/delete",{mId:data.data.id},function (data) {
                    if(data.code==200){
                        layer.msg("操作成功!",{icon:6});
                        parent.location.reload();  /*刷新页面*/
                        // data.del();
                    }else{
                        layer.msg(data.msg, {icon: 5});
                    }
                });
            })
        }
    });
});


//修改窗口
function openupdateModuleDialog(id){
    var url  =  ctx+"/module/updateModulePage?id="+id;
    var title="菜单更新";
    layui.layer.open({
        title : title,
        type : 2,
        area:["700px","450px"],
        maxmin:true,
        content : url
    });
}


//打开添加窗口
function openAddModuleDialog(grade,parentId){
    var title = "菜单管理-添加模块";
    var url = ctx + "/module/toAdd?grade="+grade+"&parentId="+parentId;
    layer.open({
        title:title,
        type:2,
        content:url,
        maxmin:true,
        area:["700px","450px"]
    });
}
6.2.4.2. 视图添加

views/module 目录下分别添加add.ftl update.ftl 文件

add.ftl 资源添加模板文件

<!DOCTYPE html>
<html>
<head>
    <#include "../common.ftl">
</head>
<body class="childrenBody">
<form class="layui-form" style="width:80%;">
    <div class="layui-form-item layui-row layui-col-xs12">
        <label class="layui-form-label">菜单名</label>
        <div class="layui-input-block">
            <input type="text" class="layui-input userName"
                   lay-verify="required" name="moduleName" id="moduleName"   placeholder="请输入菜单名">
        </div>
    </div>
    <div class="layui-form-item layui-row layui-col-xs12">
        <label class="layui-form-label">菜单样式</label>
        <div class="layui-input-block">
            <input type="text" class="layui-input userName"
                   name="moduleStyle" id="moduleStyle" placeholder="请输入菜单样式">
        </div>
    </div>
    <div class="layui-form-item layui-row layui-col-xs12">
        <label class="layui-form-label">排序</label>
        <div class="layui-input-block">
            <input type="text" class="layui-input userName"
                    name="orders" id="orders" placeholder="请输入排序值">
        </div>
    </div>
    <div class="layui-form-item layui-row layui-col-xs12">
        <label class="layui-form-label">权限码</label>
        <div class="layui-input-block">
            <input type="text" class="layui-input userName"
                   lay-verify="required" name="optValue" id="optValue" placeholder="请输入菜单权限码">
        </div>
    </div>
    <div class="layui-form-item layui-row layui-col-xs12">
        <label class="layui-form-label">菜单级别</label>
        <div class="layui-input-block">
            <#if grade??>
                <select name="grade" readonly="readonly">
                    <#if grade==0><option value="0" selected="selected">一级菜单</option></#if>
                    <#if grade==1><option value="1" selected="selected">二级菜单</option></#if>
                    <#if grade==2><option value="2" selected="selected">三级菜单</option></#if>
                </select>
            </#if>
        </div>
    </div>

    <#if grade==1>
        <div class="layui-form-item layui-row layui-col-xs12">
            <label class="layui-form-label">菜单url</label>
            <div class="layui-input-block">
                <input type="text" class="layui-input userName"
                       lay-verify="required" name="url" id="url" placeholder="请输入菜单url">
            </div>
        </div>
    </#if>

    <!--
       添加根级菜单
    -->
    <input name="parentId" type="hidden" value="${parentId}"/>
    <br/>
    <div class="layui-form-item layui-row layui-col-xs12">
        <div class="layui-input-block">
            <button class="layui-btn layui-btn-lg" lay-submit=""
                    lay-filter="addModule">确认
            </button>
            <button class="layui-btn layui-btn-lg layui-btn-normal">取消</button>
        </div>
    </div>
</form>
<script type="text/javascript" src="${ctx}/js/module/add.js"></script>
</body>
</html>

add.js 文件 实现表单数据提交操作

js/module 目录下添加add.js 实现资源添加表单数据提交

layui.use(['form', 'layer'], function () {
    var form = layui.form,
        layer = parent.layer === undefined ? layui.layer : top.layer,
        $ = layui.jquery;

    //表单提交监听
    form.on("submit(addModule)",function (data) {
        // 提交数据时的加载层 (https://layer.layui.com/)
        var index = layer.msg("数据提交中,请稍后...",{
            icon:16, // 图标
            time:false, // 不关闭
            shade:0.8 // 设置遮罩的透明度
        });

        //请求地址
        var url = ctx + "/module/add";

        //发送请求
        $.post(url,data.field,function(data){
            console.log(data);
            if(data.code == 200){
                //提示用户添加成功
                layer.msg("模块添加成功",{icon:6});
                //关闭加载层
                layer.close(index);
                //关闭添加窗口
                layer.closeAll("iframe");

                //刷新页面的营销记录
                parent.location.reload();
            }else{
                layer.msg(data.msg,{icon:5});
            }
        });


        //阻止表单提交
        return false;
    })
});

update.ftl 资源更新模板文件

<!DOCTYPE html>
<html>
<head>
    <#include "../common.ftl">
</head>
<body class="childrenBody">
<form class="layui-form" style="width:80%;">
    <div class="layui-form-item layui-row layui-col-xs12">
        <label class="layui-form-label">菜单名</label>
        <div class="layui-input-block">
            <input type="text" class="layui-input userName"
                   lay-verify="required" name="moduleName" id="moduleName" value="${(module.moduleName)!""}"   placeholder="请输入菜单名">
        </div>
    </div>
    <div class="layui-form-item layui-row layui-col-xs12">
        <label class="layui-form-label">菜单样式</label>
        <div class="layui-input-block">
            <input type="text" class="layui-input userName"
                   name="moduleStyle" id="moduleStyle" value="${(module.moduleStyle)!""}" placeholder="请输入菜单样式">
        </div>
    </div>
    <div class="layui-form-item layui-row layui-col-xs12">
        <label class="layui-form-label">排序</label>
        <div class="layui-input-block">
            <input type="text" class="layui-input userName"
                    name="orders" id="orders" placeholder="请输入排序值" value="${(module.orders)!""}">
        </div>
    </div>
    <div class="layui-form-item layui-row layui-col-xs12">
        <label class="layui-form-label">权限码</label>
        <div class="layui-input-block">
            <input type="text" class="layui-input userName"
                   lay-verify="required" name="optValue" id="optValue" placeholder="请输入菜单权限码" value="${(module.optValue)!}">
        </div>
    </div>
    <div class="layui-form-item layui-row layui-col-xs12">
        <label class="layui-form-label">菜单级别</label>
        <div class="layui-input-block">
                <select name="grade" >
                    <option value="0" <#if module.grade==0>selected="selected"</#if> >一级菜单</option>
                    <option value="1" <#if module.grade==1>selected="selected"</#if>>二级菜单</option>
                    <option value="2" <#if module.grade==2>selected="selected"</#if>>三级菜单</option>
                </select>
        </div>
    </div>

    <#if module.grade==1>
        <div class="layui-form-item layui-row layui-col-xs12">
            <label class="layui-form-label">菜单url</label>
            <div class="layui-input-block">
                <input type="text" class="layui-input userName"
                       lay-verify="required" name="url" id="url" placeholder="请输入菜单url" value="${(module.url)!""}">
            </div>
        </div>
    </#if>


    <!--
       添加根级菜单
    -->
    <input name="parentId" type="hidden" value="${module.parentId}"/>
    <input name="id" type="hidden" value="${module.id}"/>
    <br/>
    <div class="layui-form-item layui-row layui-col-xs12">
        <div class="layui-input-block">
            <button class="layui-btn layui-btn-lg" lay-submit=""
                    lay-filter="updateModule">确认
            </button>
            <button class="layui-btn layui-btn-lg layui-btn-normal">取消</button>
        </div>
    </div>
</form>
<script type="text/javascript" src="${ctx}/js/module/update.js"></script>
</body>
</html>

update.js 文件 实现表单数据提交操作

js/module 目录下添加update.js 实现资源添加表单数据提交

layui.use(['form', 'layer'], function () {
    var form = layui.form,
        layer = parent.layer === undefined ? layui.layer : top.layer,
        $ = layui.jquery;

    //表单提交监听
    form.on("submit(updateModule)",function (data) {
        // 提交数据时的加载层 (https://layer.layui.com/)
        var index = layer.msg("数据提交中,请稍后...",{
            icon:16, // 图标
            time:false, // 不关闭
            shade:0.8 // 设置遮罩的透明度
        });

        //请求地址
        var url = ctx + "/module/update";

        //发送请求
        $.post(url,data.field,function(data){
            console.log(data);
            if(data.code == 200){
                //提示用户添加成功
                layer.msg("模块添加成功",{icon:6});
                //关闭加载层
                layer.close(index);
                //关闭添加窗口
                layer.closeAll("iframe");

                //刷新页面的营销记录
                parent.location.reload();
            }else{
                layer.msg(data.msg,{icon:5});
            }
        });

        //阻止表单提交
        return false;
    })
});

6.3. 资源记录删除

6.3.1. 资源记录删除后端实现

ModuleMapper

//查询某个模块下是否存在子模块
    Integer queryCountModuleByParentId(Integer mId);

    //根据模块id 删除对应模块
    Integer deleteModuleByMid(Integer mId);

ModuleMapper.xml

 <select id="queryCountModuleByParentId" parameterType="int" resultType="int">
    select count(1) from t_module where is_valid = 1 and parent_id = #{mId}
  </select>

  <update id="deleteModuleByMid" parameterType="int">
    update t_module set is_valid = 0 where id = #{mId}
  </update>

ModuleService.java

 /**
     * 逻辑删除资源
     *      逻辑判断
     *          1.参数id 非空,删除的数据必须存在
     *          2.查询当前id下是否有子模块,如果有不能删除
     *          3.查询权限表中(角色和资源)是否包含当前模块的数据,有则删除
     *          4.删除资源
     * @param mId
     */
    @Transactional
    public void moduledelete(Integer mId) {
        //参数id 非空,删除的数据必须存在
        AssertUtil.isTrue(mId == null,"系统异常,请重试");
        AssertUtil.isTrue(selectByPrimaryKey(mId) == null,"待删除数据不存在");

        //查询当前id下是否有子模块,如果有不能删除
        Integer count = moduleMapper.queryCountModuleByParentId(mId);
        AssertUtil.isTrue(count > 0,"该模块下存在子模块,不能删除");

        //查询权限表中(角色和资源)是否包含当前模块的数据,有则删除
        count = permissionMapper.queryCountByMoudleId(mId);
        if(count > 0){
            AssertUtil.isTrue(permissionMapper.deletePermissionByMoudleId(mId) != count ,"权限删除失败");
        }

        //删除资源
        AssertUtil.isTrue(moduleMapper.deleteModuleByMid(mId) < 1,"资源删除失败");
    }

ModuleController.java

@RequestMapping("delete")
    @ResponseBody
    public ResultInfo moduledelete(Integer mId){
        moduleService.moduledelete(mId);
        return success("资源删除成功");
    }
6.3.2. 资源记录删除前端实现

修改module.js 添加删除监听事件实现资源记录删除操作。

    /**
     * 监听行部工具栏
     */
    table.on("tool(munu-table)",function (data) {
        if(data.event == "add"){
            if(data.data.grade == 2){
                layer.msg("未设置四级模块")
                return;
            }
            //打开添加窗口
            openAddModuleDialog(data.data.grade+1,data.data.id);
        }else if(data.event == "edit"){
            openupdateModuleDialog(data.data.id);

        }else if(data.event == "del"){
            console.log(data);

            layer.confirm('确定删除当前资源?', {icon: 3, title: "资源管理"}, function (index) {
                $.post(ctx+"/module/delete",{mId:data.data.id},function (data) {
                    if(data.code==200){
                        layer.msg("操作成功!",{icon:6});
                        parent.location.reload();  /*刷新页面*/
                        // data.del();
                    }else{
                        layer.msg(data.msg, {icon: 5});
                    }
                });
            })
        }
    });
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值