根据用户是否是VIP给与不同的服务模块

前言

有时我们在浏览网页或者某些APP时,VIP的权限会多一些,那么它本质上,时对当前用户的账户id做了role区分,根据role在页面上动态展示不同的模块,今天我们说说前端使用SlideBar配合SpringMVC+Mybatis实现这个功能

错误认识

直觉上可能觉得三张表就可以,比如

menus
role
user

不过我们想一想,一个菜单项是不是对应多个权限,而一个权限对应多个菜单项,多对多的关系嘛,在中间需要加一个桥表,而user也是一样,user并不一定是非黑及白,比如一个人它既是国家主席同时也是人民群众,所以说一个user对应多个role,而一个role肯定多个user对应,也是多对多的关系,所以也需要添加一个桥表
它们之间的模式👇
在这里插入图片描述


所以我们正确的表结构是这样的👇
在这里插入图片描述

ok我把建表的sql语句写一下,顺便添几条数据,省大家的功夫

create table users(id int primary key auto_increment,uname varchar(32),pwd varchar(32));
insert into users values(1,'zx',md5('abc')),(2,'ls',md5('abc'));
create table roles(id int primary key auto_increment,name varchar(32));
insert into roles
values (1,'SVip'),(2,'Vip'),(3,'普通用户');
create table user_role (id int primary key  auto_increment,userid int,roleid int,foreign key (userid) references users(id),foreign key (roleid) references roles(id));
insert into user_role (id,userid,roleid) values (1,1,1),(2,1,2),(3,1,3);
create table menus(
    id int primary key auto_increment,
    text varchar(50) comment '菜单名字',
    href varchar(100) comment '路由',
    pid int comment '父id',
    sort int comment '排序字段'
);
insert into menus values(1,'书记管理系统','javascript:void(0)',-1,1),
                         (2,'其它系统','javascript:void(1)',-1,2),
                         (3,'书籍模块','javascript:void(0)',1,3),
                         (4,'统计模块','javascript:void(0)',1,4),
                        (5,'统计模块','javascript:void(0)',1,5),
                         (6,'书籍列表','/books',3,6),
                         (7,'出版社信息','/pubs',4,7),
                         (8,'报表','/report',5,8),
                         (9,'权限配置','/pre',2,9);
create table role_menu(id int primary key ,roleid int,menuid int,
foreign key (roleid) references roles(id),
foreign key (menuid) references menus(id)
);
insert into role_menu values(1,1,1),(2,1,2),(3,1,3),(4,1,4),(5,1,5),(6,1,6),(7,3,1),(8,3,2),(9,2,1),(10,2,2),(11,2,3),(12,2,4);

ok我们最重要的表和数据都有了,这里我的意思是roleid为1为超级管理员(VIP) 能看到我们所有的菜单项,而其它roleid只开放部分菜单项

后台

Controller

Spring的知识点我就不多说了

映射
客户端
controller
Service
Mapper
Mapper.xml
datebase

顺着这个流程走,👉controller👇

@Controller
@RequestMapping("/user")
public class UserController {
    @Autowired
    private SetResponse sr;
    @Autowired
    private UserService service;
    @RequestMapping("/queryMenuByUserId")
    @ResponseBody
    public ETResponse queryMenuByUserID(int userID){
        List<Menus> menus =service.queryMenuByUserId(userID);//调用service
        //这个依赖注入是用来组装ETResponse的
        return sr.setResponse(tops);
    }
}

其中sr这个依赖注入是用来组装ETResponse的,我贴一下

@Component
@Data@AllArgsConstructor@NoArgsConstructor
public class SetResponse {
    @Autowired
    private  ETResponse etResponse;
    public ETResponse setResponse(Object obj){
        etResponse.setCode(ETEnums.SUCCESS.getCode());//这里用的枚举
        etResponse.setMsg(ETEnums.SUCCESS.getMsg());
        etResponse.setData(obj);
        return etResponse;
    }
}

接着牵引出ETResonse,也是一个依赖注入,我贴一下

@Component
@Data@NoArgsConstructor@AllArgsConstructor
public class ETResponse {
    private int code;
    private String msg;
    private Object data;
}

这个ETnum的枚举在这里👇

public enum ETEnums {
    SUCCESS(200,"success"),
    ERROR(444,"error"),
    LOGIN_SUCCESS(666,"login_success"),
    LOGIN_ERROR(445,"login_error");
    private int code;
    private String msg;
    private ETEnums(int code,String msg){
        this.code =code;
        this.msg =msg;
    }
    public String getMsg(){
        return msg;
    }
    public int getCode(){
        return code;
    }
}

ok Controller这里都清楚了往下走,去Service

Service+Mapper+.xml

往下就是各种甩锅,Service–>Mapper–>.xml<–>MySql,首先是Service,刚才Controller调用的Service嘛 我们接上

@Service
public class UserService {
    @Autowired
    private UserMapper dao;
    public List<Menus> queryMenuByUserId(int userID) {
        return dao.queryMenuByUserID(userID);//看到了吧 Service调用UserMapper
    }
}

看到了吧 Service调用UserMapper,那么UserMapper👇

public interface UserMapper {
    List<Menus> queryMenuByUserID(int userID);
}

它映射的是一个.xml文件,这是我的UserMapper.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接口的路径-->
<mapper namespace="com.etoak.book.user.mapper.UserMapper">
    <select id="queryMenuByUserID" resultType="menus">
select * from menus m where
        m.id in (select menuid from role_menu rm where rm.roleid in (select ur.roleid from user_role ur where ur.userid=#{userID}))
    </select>
</mapper>

测试

ok 后台基本就完事了,我们用postMan发个请求查一下
在这里插入图片描述
哎,是可以回来数据,但是我们想要的是一个由包含关系的菜单项,不然会给前端造成很大的麻烦,在这里我们得想办法协调一下工作,把菜单的子父关系表现出来,所以我们得改进一下上面的代码

后台(改)

如果在一个菜单项里有子菜单项,那么我们给menus实体类一个属性:List 存放它的子菜单

@Data@AllArgsConstructor@NoArgsConstructor
public class Menus {
    private int id;
    private String text;
    private String href;
    private int pid;
    private int sort;
    private List<Menus> nodes;
}

那么我们需要改进一下Controller的代码

@Controller
@RequestMapping("/user")
public class UserController {
    @Autowired
    private SetResponse sr;
    @Autowired
    private UserService service;
    @RequestMapping("/queryMenuByUserId")
    @ResponseBody
    public ETResponse queryMenuByUserID(int userID){
        List<Menus> menus =service.queryMenuByUserId(userID);
        List<Menus> tops =new ArrayList<>();//找一下顶部菜单
        for (Menus menu : menus) {
            if(menu.getPid()==-1){
                tops.add(menu);
            }
        }
        for (Menus top : tops) {
            SetNodes.setNodes(top,menus);//装载子菜单的方法
        }
        return sr.setResponse(tops);
    }
}

封装了一个小工具类 SetNode

public class SetNodes {
    public static void setNodes(Menus m,List<Menus> menus){
        List<Menus> children =new ArrayList<>();//存子菜单的集合
        for (Menus menu : menus) {
            if(menu.getPid()==m.getId()){
               children.add(menu);//装载
            }
        }
        m.setNodes(children);//设置当前菜单的子菜单属性
        
        if(children.size()>0){//直到没有子菜单
            for (Menus child : children) {
                SetNodes.setNodes(child,menus);//递归
            }
        }
    }
}

这样我们的后台就没毛病了,再测试一下

测试(改)

在这里插入图片描述

哎!完美,是我们要的嵌套效果,这样我们就根据pid和id把菜单串起来了

前台

使用Vue,这里我们用一个基于elementUI的小东西,SlideBar
一个小资源,下载解压拖进views就好
这里我下载解压好了,直接拖到views中,它其实就是一个组件而已
在这里插入图片描述
注册组件并发送请求:

<template>
  <el-container>
    <el-header>
      <div id="logo">
        <img src="../assets/logo.png" /> <span>学生管理系统</span>
      </div>
      <el-dropdown>
        <el-button type="warning">
          更多菜单<i class="el-icon-arrow-down el-icon--right"></i>
        </el-button>
        <el-dropdown-menu slot="dropdown">
          <el-dropdown-item>退出</el-dropdown-item>
        </el-dropdown-menu>
      </el-dropdown>
    </el-header>
    <el-container>
      <el-aside width="200px">
      <!--SlideBar在这里使用 绑定menuList-->
        <side-bar :menuList="menuList">
        </side-bar>
      </el-aside>
      <el-main>
          <router-view></router-view>
      </el-main>
    </el-container>
  </el-container>
</template>
<script>
import SideBar from './book资料/SideBar/SideBar.vue';
export default {
  data(){
    return{
      menuList:[]
    }
  },
  components:{SideBar},
  methods: {
    queryMenu(){
      this.$http
        .post("/user/queryMenuByUserId", this.$qs.stringify({userID:1}))
        .then((response) => {
          this.menuList =response.data.data
        });
    }
  },
  created(){
    this.queryMenu()
  }
};
</script>
<style lang="less" scoped>
.el-container {
  margin: 0;
  padding: 0;
  height: 1000px;
  .el-header {
    background-color: rgb(84, 92, 100);
    color: #333;
    text-align: center;
    line-height: 60px;
    height: 200px;
    .el-dropdown {
      float: right;
    }
    #logo {
      float: left;
      height: 75px;
      width: 250px;
      display: flex;
      align-items: center;
      img {
        width: 30%;
        height: 100%;
      }
      span {
        width: 70%;
        height: 100%;
        color: #d3dce6;
        font-weight: 900;
      }
    }
  }

  .el-aside {
    background-color: #d3dce6;
    color: #333;
    text-align: center;
    line-height: 200px;
    height: 800px;
    background-color: rgb(84, 92, 100);
  }

  .el-main {
    background-color: #e9eef3;
    color: #333;
    text-align: center;
    line-height: 160px;
    height: 800px;
  }
}
</style>


最终效果

效果👇userID为1是Svip,为2是普通vip,为3为普通用户,所以可以看出动态显示模块的效果
在这里插入图片描述

总结

那么根据用户的权限,展示不同的模块,我们就完成了,有需求可以加上登录和定时任务这些需求,好了,今天就说这么多吧,原创不易,喜欢记得点赞+关注哦~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

商朝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值