博客后台模块续更(五)

news2024/10/5 20:26:35

十一、后台模块-菜单列表

菜单指的是权限菜单,也就是一堆权限字符串

1. 查询菜单

1.1 接口分析

需要展示菜单列表,不需要分页。可以针对菜单名进行模糊查询。也可以针对菜单的状态进行查询。菜单要按照父菜单id和orderNum进行排序

请求方式

请求路径

是否需求token头

GET

system/menu/list

请求参数是query格式的: 

{
status  : 状态

menuName: 菜单名
}

响应格式:

{
	"code":200,
	"data":[
		{
			"component":"组件路径",
			"icon":"build",
			"id":"2023",
			"isFrame":1,
			"menuName":"菜单名称",
			"menuType":"C",
			"orderNum":0,
			"parentId":"0",
			"path":"write",
			"perms":"权限字符串",
			"remark":"备注信息",
			"status":"0",
			"visible":"0"
		},
		{
			"icon":"system",
			"id":"1",
			"isFrame":1,
			"menuName":"菜单名称",
			"menuType":"M",
			"orderNum":1,
			"parentId":"0",
			"path":"system",
			"perms":"权限字符串",
			"remark":"备注信息",
			"status":"0",
			"visible":"0"
		}
	],
	"msg":"操作成功"
}

2.2 代码实现

第一步: 在keke-framework工程的Vo目录新建MenuVo类,写入如下,用于把指定字段返回给前端

package com.keke.domain.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class AdminMenuVo {
     //菜单ID
     private Long id;
     //菜单名称
     private String menuName;
     //父菜单ID
     private Long parentId;
     //显示顺序
     private Integer orderNum;
     //路由地址
     private String path;
     //组件路径
     private String component;
     //是否为外链(0是 1否)
     private Integer isFrame;
     //菜单类型(M目录 C菜单 F按钮)
     private String menuType;
     //菜单状态(0显示 1隐藏)
     private String visible;
     //菜单状态(0正常 1停用)
     private String status;
     //权限标识
     private String perms;
     //菜单图标
     private String icon;
     //备注
     private String remark;
}

第二步: 在keke-admin工程的controller目录新建MenuController类,写入如下,是查询菜单列表的访问接口

package com.keke.controller;


import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Menu;
import com.keke.service.MenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/system/menu")
public class MenuController {

     @Autowired
     private MenuService menuService;


     //查询菜单列表
     @GetMapping("/list")
     public ResponseResult selectAllMenu(Menu menu){
          return menuService.selectAllMenu(menu);
     }
}

第三步:把keke-framework工程的MenuService接口修改为如下,增加了查询菜单列表的接口

package com.keke.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Menu;

import java.util.List;


/**
 * 菜单权限表(Menu)表服务接口
 *
 * @author makejava
 * @since 2023-10-18 20:55:48
 */
public interface MenuService extends IService<Menu> {


     //查询用户权限信息
     List<String> selectPermsByUserId(Long userId);

     //查询用户的路由信息,也就是权限菜单
     List<Menu> selectRouterMenuTreeByUserId(Long userId);

     ResponseResult selectAllMenu(Menu menu);

}

第四步: 把keke-framework工程的MenuServiceImpl类修改为如下,增加了查询菜单列表的具体代码实现

package com.keke.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.keke.constants.SystemConstants;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Menu;
import com.keke.domain.vo.AdminMenuVo;
import com.keke.mapper.MenuMapper;
import com.keke.service.MenuService;
import com.keke.utils.BeanCopyUtils;
import com.keke.utils.SecurityUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 菜单权限表(Menu)表服务实现类
 *
 * @author makejava
 * @since 2023-10-18 20:55:48
 */
@Service("menuService")
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {



     //根据用户id查询权限关键字
     @Override
     public List<String> selectPermsByUserId(Long userId) {
          //如果用户id为1代表管理员,roles 中只需要有admin,
          // permissions中需要有所有菜单类型为C(菜单)或者F(按钮)的,状态为正常的,未被删除的权限
          if(SecurityUtils.isAdmin()) {
               LambdaQueryWrapper<Menu> lambdaQueryWrapper = new LambdaQueryWrapper<>();
               lambdaQueryWrapper.in(Menu::getMenuType, SystemConstants.MENU, SystemConstants.BUTTON);
               lambdaQueryWrapper.eq(Menu::getStatus, SystemConstants.STATUS_NORMAL);
               //由于我们的逻辑删除字段已经配置了,所以无需封装lambdaQueryWrapper
               List<Menu> menuList = list(lambdaQueryWrapper);
               //我们需要的是String类型的集合,这里我们要进行数据的处理,这里采用流的方式
               List<String> permissions = menuList.stream()
                       .map(new Function<Menu, String>() {
                            @Override
                            public String apply(Menu menu) {
                                 String perms = menu.getPerms();
                                 return perms;
                            }
                       })
                       .collect(Collectors.toList());
               return permissions;
          }
          //否则返回这个用户所具有的权限
          //这里我们需要进行连表查询,因为我们的用户先和角色关联,然后角色才跟权限关联
          MenuMapper menuMapper = getBaseMapper();
          //我们期望menuMapper中有一个方法可以直接帮我们去实现这个复杂的逻辑,这里直接返回
          return menuMapper.selectPermsByUserId(userId);
     }

     @Override
     public List<Menu> selectRouterMenuTreeByUserId(Long userId) {
          MenuMapper menuMapper = getBaseMapper();
          List<Menu> menus = null;
          //如果是管理员,返回所有
          if(SecurityUtils.isAdmin()){
               menus = menuMapper.selectAllRoutersMenu();
          }else {
               //如果不是管理员,返回对应用户的菜单
               menus = menuMapper.selectRoutersMenuTreeByUserId(userId);
          }
          //因为上面的查询都是从数据库进行查询,所以无法封装children,这里构建Tree

          List<Menu> menuTree = buildMenuTree(menus,0L);
          return menuTree;
     }

     @Override
     public ResponseResult selectAllMenu(Menu menu) {
          //可以针对菜单名进行模糊查询。也可以针对菜单的状态进行查询
          LambdaQueryWrapper<Menu> lambdaQueryWrapper = new LambdaQueryWrapper<>();
          lambdaQueryWrapper.like(StringUtils.hasText(menu.getMenuName()),Menu::getMenuName,menu.getMenuName());
          lambdaQueryWrapper.eq(StringUtils.hasText(menu.getStatus()),Menu::getStatus,menu.getStatus());
          //排序 parent_id和order_num
          lambdaQueryWrapper.orderByAsc(Menu::getParentId,Menu::getOrderNum);
          List<Menu> menus = list(lambdaQueryWrapper);
          List<AdminMenuVo> adminMenuVos = BeanCopyUtils.copyBeanList(menus, AdminMenuVo.class);
          return ResponseResult.okResult(adminMenuVos);
     }






     /**
      * 构建MenuTree
      * 思路先找第一层级的菜单,就是找到id于parentId的对应关系,然后把parentId设置为Id的children
      * @param menus
      * @return
      */
     private List<Menu> buildMenuTree(List<Menu> menus,Long parentId) {
          //转化流处理
          List<Menu> menuTree = menus.stream()
                  //过滤掉除一级菜单之外的菜单
                  .filter(menu -> menu.getParentId().equals(parentId))
                  //然后将获取其子菜单设置到children字段,并返回
                  .map(m -> m.setChildren(gerChildren(m, menus)))
                  .collect(Collectors.toList());
          return menuTree;
     }

     //获取当前菜单的子菜单
     private List<Menu> gerChildren(Menu menu, List<Menu> menus) {
          //流处理,遍历每一个流对象,筛选出流对象的parentId=menu的id,即过滤
          List<Menu> children = menus.stream()
                  .filter(m -> m.getParentId().equals(menu.getId()))
                  //这里其实不必要写,这一步的逻辑是如果有三级,
                  //可以把流对象中再过筛选出子菜单设置给对应的children并返回
                  .map(m -> m.setChildren(gerChildren(m,menus)))
                  .collect(Collectors.toList());
          return children;
     }

}

2.3 测试

运行前端工程,打开redis,打开菜单管理

2. 新增菜单

2.1 接口分析

新增权限菜单

请求方式

请求路径

是否需求token头

POST

system/menu

请求体参数:

Menu类对应的json格式

响应格式:

{
	"code":200,
	"msg":"操作成功"
}

2.2 代码实现

第一步: 把keke-framework工程的Menu类修改为如下,注意有四个字段使用了mybatisplus的字段自增

package com.keke.domain.entity;

import java.util.Date;
import java.util.List;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.experimental.Accessors;

/**
 * 菜单权限表(Menu)表实体类
 *
 * @author makejava
 * @since 2023-10-18 20:55:24
 */
@SuppressWarnings("serial")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@TableName("sys_menu")
public class Menu {
    //菜单ID
    private Long id;
    //菜单名称
    private String menuName;
    //父菜单ID
    private Long parentId;
    //显示顺序
    private Integer orderNum;
    //路由地址
    private String path;
    //组件路径
    private String component;
    //是否为外链(0是 1否)
    private Integer isFrame;
    //菜单类型(M目录 C菜单 F按钮)
    private String menuType;
    //菜单状态(0显示 1隐藏)
    private String visible;
    //菜单状态(0正常 1停用)
    private String status;
    //权限标识
    private String perms;
    //菜单图标
    private String icon;
    //创建者
    @TableField(fill = FieldFill.INSERT)
    private Long createBy;
    //创建时间
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    //更新者
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateBy;
    //更新时间
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    //备注
    private String remark;
    
    private String delFlag;

    //由于数据库没有children字段,所以我们要添加@TableField(exist = false)注解
    // 让mybatis在查表时不查询这个字段
    @TableField(exist = false)
    private List<Menu> children;
}

第二步: 把huanf-framework工程的MenuController类修改为如下,增加了新增菜单的具体代码实现

package com.keke.controller;


import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Menu;
import com.keke.service.MenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/system/menu")
public class MenuController {

     @Autowired
     private MenuService menuService;


     //查询菜单列表
     @GetMapping("/list")
     public ResponseResult selectAllMenu(Menu menu){
          return menuService.selectAllMenu(menu);
     }

     //新增菜单
     @PostMapping
     public ResponseResult add(@RequestBody Menu menu) {
          menuService.save(menu);
          return ResponseResult.okResult();
     }

}

2.3 测试

启动工程,打开前端工程,redis

测试在 '系统管理' 页面,点击 '新增',能否可以添加"测试目录"类型的菜单

3. 修改菜单

能够修改菜单,但是修改的时候不能把父菜单设置为当前菜单,如果设置了需要给出相应的提示。并且修改失败。

修改菜单包含两个接口,一个是点击修改回显出菜单的详情信息,一个是点击确定后菜单修改成功

3.1 菜单详情

3.1.1菜单详情接口分析
请求方式请求路径是否需求token头
Getsystem/menu/{id}

请求参数PathVariable格式:

id: 菜单id

响应格式:

{
	"code":200,
	"data":{
		"icon":"table",
		"id":"2017",
		"menuName":"内容管理",
		"menuType":"M",
		"orderNum":"4",
		"parentId":"0",
		"path":"content",
		"remark":"",
		"status":"0",
		"visible":"0"
	},
	"msg":"操作成功"
}
3.1.2 代码实现

controller层新增

package com.keke.controller;


import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Menu;
import com.keke.service.MenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/system/menu")
public class MenuController {

     @Autowired
     private MenuService menuService;


     //查询菜单列表
     @GetMapping("/list")
     public ResponseResult selectAllMenu(Menu menu){
          return menuService.selectAllMenu(menu);
     }

     //新增菜单
     @PostMapping
     public ResponseResult add(@RequestBody Menu menu) {
          menuService.save(menu);
          return ResponseResult.okResult();
     }

     @GetMapping("/{menuId}")
     public ResponseResult getInfoById(@PathVariable("menuId") Long menuId){
          Menu menu = menuService.getById(menuId);
          return ResponseResult.okResult(menu);
     }

}
3.1.3 测试

点击修改按钮后,回显出菜单的详情信息

3.2 修改菜单

3.2.1 修改菜单接口分析
请求方式请求路径是否需求token头
PUTsystem/menu

请求体参数:

Menu类对应的json格式

响应格式:

{
	"code":200,
	"msg":"操作成功"
}

如果把父菜单设置为当前菜单:

{
	"code":500,
	"msg":"修改菜单'写博文'失败,上级菜单不能选择自己"
}
3.2.2 代码实现

第一步:Controller层新增

package com.keke.controller;


import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Menu;
import com.keke.service.MenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/system/menu")
public class MenuController {

     @Autowired
     private MenuService menuService;


     //查询菜单列表
     @GetMapping("/list")
     public ResponseResult selectAllMenu(Menu menu){
          return menuService.selectAllMenu(menu);
     }

     //新增菜单
     @PostMapping
     public ResponseResult add(@RequestBody Menu menu) {
          menuService.save(menu);
          return ResponseResult.okResult();
     }

     @GetMapping("/{menuId}")
     public ResponseResult getInfoById(@PathVariable("menuId") Long menuId){
          Menu menu = menuService.getById(menuId);
          return ResponseResult.okResult(menu);
     }

     @PutMapping
     public ResponseResult editMenu(@RequestBody Menu menu){
          return menuService.editMenu(menu);
     }

}

第二步:domain/entity的Menu实体类修改四个字段,设置为mp自动填充

package com.keke.domain.entity;

import java.util.Date;
import java.util.List;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.experimental.Accessors;

/**
 * 菜单权限表(Menu)表实体类
 *
 * @author makejava
 * @since 2023-10-18 20:55:24
 */
@SuppressWarnings("serial")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@TableName("sys_menu")
public class Menu {
    //菜单ID
    private Long id;
    //菜单名称
    private String menuName;
    //父菜单ID
    private Long parentId;
    //显示顺序
    private Integer orderNum;
    //路由地址
    private String path;
    //组件路径
    private String component;
    //是否为外链(0是 1否)
    private Integer isFrame;
    //菜单类型(M目录 C菜单 F按钮)
    private String menuType;
    //菜单状态(0显示 1隐藏)
    private String visible;
    //菜单状态(0正常 1停用)
    private String status;
    //权限标识
    private String perms;
    //菜单图标
    private String icon;
    //创建者
    @TableField(fill = FieldFill.INSERT)
    private Long createBy;
    //创建时间
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    //更新者
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateBy;
    //更新时间
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    //备注
    private String remark;
    
    private String delFlag;

    //由于数据库没有children字段,所以我们要添加@TableField(exist = false)注解
    // 让mybatis在查表时不查询这个字段
    @TableField(exist = false)
    private List<Menu> children;
}

第三步:service层新增

package com.keke.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Menu;

import java.util.List;


/**
 * 菜单权限表(Menu)表服务接口
 *
 * @author makejava
 * @since 2023-10-18 20:55:48
 */
public interface MenuService extends IService<Menu> {


     //查询用户权限信息
     List<String> selectPermsByUserId(Long userId);

     //查询用户的路由信息,也就是权限菜单
     List<Menu> selectRouterMenuTreeByUserId(Long userId);

     ResponseResult selectAllMenu(Menu menu);

     ResponseResult editMenu(Menu menu);

}

第四步:impl层新增

package com.keke.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.keke.constants.SystemConstants;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Menu;
import com.keke.domain.vo.AdminMenuVo;
import com.keke.mapper.MenuMapper;
import com.keke.service.MenuService;
import com.keke.utils.BeanCopyUtils;
import com.keke.utils.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 菜单权限表(Menu)表服务实现类
 *
 * @author makejava
 * @since 2023-10-18 20:55:48
 */
@Service("menuService")
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {

     @Autowired
     private MenuService menuService;

     //根据用户id查询权限关键字
     @Override
     public List<String> selectPermsByUserId(Long userId) {
          //如果用户id为1代表管理员,roles 中只需要有admin,
          // permissions中需要有所有菜单类型为C(菜单)或者F(按钮)的,状态为正常的,未被删除的权限
          if(SecurityUtils.isAdmin()) {
               LambdaQueryWrapper<Menu> lambdaQueryWrapper = new LambdaQueryWrapper<>();
               lambdaQueryWrapper.in(Menu::getMenuType, SystemConstants.MENU, SystemConstants.BUTTON);
               lambdaQueryWrapper.eq(Menu::getStatus, SystemConstants.STATUS_NORMAL);
               //由于我们的逻辑删除字段已经配置了,所以无需封装lambdaQueryWrapper
               List<Menu> menuList = list(lambdaQueryWrapper);
               //我们需要的是String类型的集合,这里我们要进行数据的处理,这里采用流的方式
               List<String> permissions = menuList.stream()
                       .map(new Function<Menu, String>() {
                            @Override
                            public String apply(Menu menu) {
                                 String perms = menu.getPerms();
                                 return perms;
                            }
                       })
                       .collect(Collectors.toList());
               return permissions;
          }
          //否则返回这个用户所具有的权限
          //这里我们需要进行连表查询,因为我们的用户先和角色关联,然后角色才跟权限关联
          MenuMapper menuMapper = getBaseMapper();
          //我们期望menuMapper中有一个方法可以直接帮我们去实现这个复杂的逻辑,这里直接返回
          return menuMapper.selectPermsByUserId(userId);
     }

     @Override
     public List<Menu> selectRouterMenuTreeByUserId(Long userId) {
          MenuMapper menuMapper = getBaseMapper();
          List<Menu> menus = null;
          //如果是管理员,返回所有
          if(SecurityUtils.isAdmin()){
               menus = menuMapper.selectAllRoutersMenu();
          }else {
               //如果不是管理员,返回对应用户的菜单
               menus = menuMapper.selectRoutersMenuTreeByUserId(userId);
          }
          //因为上面的查询都是从数据库进行查询,所以无法封装children,这里构建Tree

          List<Menu> menuTree = buildMenuTree(menus,0L);
          return menuTree;
     }

     @Override
     public ResponseResult selectAllMenu(Menu menu) {
          //可以针对菜单名进行模糊查询。也可以针对菜单的状态进行查询
          LambdaQueryWrapper<Menu> lambdaQueryWrapper = new LambdaQueryWrapper<>();
          lambdaQueryWrapper.like(StringUtils.hasText(menu.getMenuName()),Menu::getMenuName,menu.getMenuName());
          lambdaQueryWrapper.eq(StringUtils.hasText(menu.getStatus()),Menu::getStatus,menu.getStatus());
          //排序 parent_id和order_num
          lambdaQueryWrapper.orderByAsc(Menu::getParentId,Menu::getOrderNum);
          List<Menu> menus = list(lambdaQueryWrapper);
          List<AdminMenuVo> adminMenuVos = BeanCopyUtils.copyBeanList(menus, AdminMenuVo.class);
          return ResponseResult.okResult(adminMenuVos);
     }

     @Override
     public ResponseResult editMenu(Menu menu) {
          if(menu.getParentId().equals(menu.getId())){
               return ResponseResult.okResult(500,"修改菜单" + "'" + menu.getMenuName() + "'" + "失败,上级菜单不能选择自己");
          }
          updateById(menu);
          return ResponseResult.okResult();
     }


     /**
      * 构建MenuTree
      * 思路先找第一层级的菜单,就是找到id于parentId的对应关系,然后把parentId设置为Id的children
      * @param menus
      * @return
      */
     private List<Menu> buildMenuTree(List<Menu> menus,Long parentId) {
          //转化流处理
          List<Menu> menuTree = menus.stream()
                  //过滤掉除一级菜单之外的菜单
                  .filter(menu -> menu.getParentId().equals(parentId))
                  //然后将获取其子菜单设置到children字段,并返回
                  .map(m -> m.setChildren(gerChildren(m, menus)))
                  .collect(Collectors.toList());
          return menuTree;
     }

     //获取当前菜单的子菜单
     private List<Menu> gerChildren(Menu menu, List<Menu> menus) {
          //流处理,遍历每一个流对象,筛选出流对象的parentId=menu的id,即过滤
          List<Menu> children = menus.stream()
                  .filter(m -> m.getParentId().equals(menu.getId()))
                  //这里其实不必要写,这一步的逻辑是如果有三级,
                  //可以把流对象中再过筛选出子菜单设置给对应的children并返回
                  .map(m -> m.setChildren(gerChildren(m,menus)))
                  .collect(Collectors.toList());
          return children;
     }

}
3.2.3 测试

4. 删除菜单

能够删除菜单,但是如果要删除的菜单有子菜单则提示:存在子菜单不允许删除 并且删除失败

4.1 接口分析

请求方式请求路径是否需求token头
DELETEcontent/article/{menuId}

请求参数PathVariable参数:

menuId:要删除菜单的id

响应格式: 

{
	"code":200,
	"msg":"操作成功"
}

如果有子菜单

{
	"code":500,
	"msg":"存在子菜单不允许删除"
}

 4.2 代码实现

第一步:controller层新增

package com.keke.controller;


import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Menu;
import com.keke.service.MenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/system/menu")
public class MenuController {

     @Autowired
     private MenuService menuService;


     //查询菜单列表
     @GetMapping("/list")
     public ResponseResult selectAllMenu(Menu menu){
          return menuService.selectAllMenu(menu);
     }

     //新增菜单
     @PostMapping
     public ResponseResult add(@RequestBody Menu menu) {
          menuService.save(menu);
          return ResponseResult.okResult();
     }

     @GetMapping("/{menuId}")
     public ResponseResult getInfoById(@PathVariable("menuId") Long menuId){
          Menu menu = menuService.getById(menuId);
          return ResponseResult.okResult(menu);
     }

     @PutMapping
     public ResponseResult editMenu(@RequestBody Menu menu){
          return menuService.editMenu(menu);
     }

     @DeleteMapping("/{menuId}")
     public ResponseResult deleteMenu(@PathVariable("menuId") Long menuId){
          return menuService.deleteMenu(menuId);
     }

}

第二步:service层新增

package com.keke.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Menu;

import java.util.List;


/**
 * 菜单权限表(Menu)表服务接口
 *
 * @author makejava
 * @since 2023-10-18 20:55:48
 */
public interface MenuService extends IService<Menu> {


     //查询用户权限信息
     List<String> selectPermsByUserId(Long userId);

     //查询用户的路由信息,也就是权限菜单
     List<Menu> selectRouterMenuTreeByUserId(Long userId);

     ResponseResult selectAllMenu(Menu menu);

     ResponseResult editMenu(Menu menu);

     ResponseResult deleteMenu(Long menuId);
}

第三步:impl层新增

package com.keke.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.keke.constants.SystemConstants;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Menu;
import com.keke.domain.vo.AdminMenuVo;
import com.keke.mapper.MenuMapper;
import com.keke.service.MenuService;
import com.keke.utils.BeanCopyUtils;
import com.keke.utils.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 菜单权限表(Menu)表服务实现类
 *
 * @author makejava
 * @since 2023-10-18 20:55:48
 */
@Service("menuService")
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {

     @Autowired
     private MenuService menuService;

     //根据用户id查询权限关键字
     @Override
     public List<String> selectPermsByUserId(Long userId) {
          //如果用户id为1代表管理员,roles 中只需要有admin,
          // permissions中需要有所有菜单类型为C(菜单)或者F(按钮)的,状态为正常的,未被删除的权限
          if(SecurityUtils.isAdmin()) {
               LambdaQueryWrapper<Menu> lambdaQueryWrapper = new LambdaQueryWrapper<>();
               lambdaQueryWrapper.in(Menu::getMenuType, SystemConstants.MENU, SystemConstants.BUTTON);
               lambdaQueryWrapper.eq(Menu::getStatus, SystemConstants.STATUS_NORMAL);
               //由于我们的逻辑删除字段已经配置了,所以无需封装lambdaQueryWrapper
               List<Menu> menuList = list(lambdaQueryWrapper);
               //我们需要的是String类型的集合,这里我们要进行数据的处理,这里采用流的方式
               List<String> permissions = menuList.stream()
                       .map(new Function<Menu, String>() {
                            @Override
                            public String apply(Menu menu) {
                                 String perms = menu.getPerms();
                                 return perms;
                            }
                       })
                       .collect(Collectors.toList());
               return permissions;
          }
          //否则返回这个用户所具有的权限
          //这里我们需要进行连表查询,因为我们的用户先和角色关联,然后角色才跟权限关联
          MenuMapper menuMapper = getBaseMapper();
          //我们期望menuMapper中有一个方法可以直接帮我们去实现这个复杂的逻辑,这里直接返回
          return menuMapper.selectPermsByUserId(userId);
     }

     @Override
     public List<Menu> selectRouterMenuTreeByUserId(Long userId) {
          MenuMapper menuMapper = getBaseMapper();
          List<Menu> menus = null;
          //如果是管理员,返回所有
          if(SecurityUtils.isAdmin()){
               menus = menuMapper.selectAllRoutersMenu();
          }else {
               //如果不是管理员,返回对应用户的菜单
               menus = menuMapper.selectRoutersMenuTreeByUserId(userId);
          }
          //因为上面的查询都是从数据库进行查询,所以无法封装children,这里构建Tree

          List<Menu> menuTree = buildMenuTree(menus,0L);
          return menuTree;
     }

     @Override
     public ResponseResult selectAllMenu(Menu menu) {
          //可以针对菜单名进行模糊查询。也可以针对菜单的状态进行查询
          LambdaQueryWrapper<Menu> lambdaQueryWrapper = new LambdaQueryWrapper<>();
          lambdaQueryWrapper.like(StringUtils.hasText(menu.getMenuName()),Menu::getMenuName,menu.getMenuName());
          lambdaQueryWrapper.eq(StringUtils.hasText(menu.getStatus()),Menu::getStatus,menu.getStatus());
          //排序 parent_id和order_num
          lambdaQueryWrapper.orderByAsc(Menu::getParentId,Menu::getOrderNum);
          List<Menu> menus = list(lambdaQueryWrapper);
          List<AdminMenuVo> adminMenuVos = BeanCopyUtils.copyBeanList(menus, AdminMenuVo.class);
          return ResponseResult.okResult(adminMenuVos);
     }

     @Override
     public ResponseResult editMenu(Menu menu) {
          if(menu.getParentId().equals(menu.getId())){
               return ResponseResult.okResult(500,"修改菜单" + "'" + menu.getMenuName() + "'" + "失败,上级菜单不能选择自己");
          }
          updateById(menu);
          return ResponseResult.okResult();
     }

     @Override
     public ResponseResult deleteMenu(Long menuId) {
          Menu menu = menuService.getById(menuId);
          //如果该菜单有子菜单,那么就提示不能删除,逻辑就是查询菜单表中是否有父菜单id是当前菜单id
          LambdaQueryWrapper<Menu> lambdaQueryWrapper = new LambdaQueryWrapper<>();
          lambdaQueryWrapper.eq(Menu::getParentId,menuId);
          List<Menu> menus = list(lambdaQueryWrapper);
          if(!menus.isEmpty()) {
               return ResponseResult.okResult(500, "存在子菜单不允许删除");
          }
          removeById(menuId);
          return ResponseResult.okResult();
     }


     /**
      * 构建MenuTree
      * 思路先找第一层级的菜单,就是找到id于parentId的对应关系,然后把parentId设置为Id的children
      * @param menus
      * @return
      */
     private List<Menu> buildMenuTree(List<Menu> menus,Long parentId) {
          //转化流处理
          List<Menu> menuTree = menus.stream()
                  //过滤掉除一级菜单之外的菜单
                  .filter(menu -> menu.getParentId().equals(parentId))
                  //然后将获取其子菜单设置到children字段,并返回
                  .map(m -> m.setChildren(gerChildren(m, menus)))
                  .collect(Collectors.toList());
          return menuTree;
     }

     //获取当前菜单的子菜单
     private List<Menu> gerChildren(Menu menu, List<Menu> menus) {
          //流处理,遍历每一个流对象,筛选出流对象的parentId=menu的id,即过滤
          List<Menu> children = menus.stream()
                  .filter(m -> m.getParentId().equals(menu.getId()))
                  //这里其实不必要写,这一步的逻辑是如果有三级,
                  //可以把流对象中再过筛选出子菜单设置给对应的children并返回
                  .map(m -> m.setChildren(gerChildren(m,menus)))
                  .collect(Collectors.toList());
          return children;
     }
}

4.3 测试

 

 

 

 

十二、后台模块-角色列表

1. 查询角色

需要有角色列表分页查询的功能。

要求能够针对角色名称进行模糊查询。

要求能够针对状态进行查询。

要求按照role_sort进行升序排列。

1.1 接口分析

请求方式请求路径是否需求token头
GETsystem/role/list

Query格式请求参数:

pageNum: 页码

pageSize: 每页条数

roleName:角色名称

status:状态

响应格式:

{
	"code":200,
	"data":{
		"rows":[
			{
				"id":"12",
				"roleKey":"link",
				"roleName":"友链审核员",
				"roleSort":"1",
				"status":"0"
			}
		],
		"total":"1"
	},
	"msg":"操作成功"
}

2.2 代码实现

第一步:新建RoleController新增

package com.keke.controller;

import com.keke.domain.ResponseResult;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.entity.Role;
import com.keke.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/system/role")
public class RoleController {

     @Autowired
     private RoleService roleService;

     @GetMapping("/list")
     public ResponseResult selectPageRole(Role role,Integer pageNum,Integer pageSize){
          return roleService.selectPageRole(role,pageNum,pageSize);
     }
}

第二步:service层新增

package com.keke.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.keke.domain.ResponseResult;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.entity.Role;

import java.util.List;


/**
 * 角色信息表(Role)表服务接口
 *
 * @author makejava
 * @since 2023-10-18 21:04:06
 */
public interface RoleService extends IService<Role> {

     List<String> selectRoleKeyByUserId(Long userId);


     ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize);
}

第三步:impl层新增

package com.keke.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.keke.domain.ResponseResult;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.entity.Role;
import com.keke.domain.vo.PageVo;
import com.keke.mapper.RoleMapper;
import com.keke.service.RoleService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * 角色信息表(Role)表服务实现类
 *
 * @author makejava
 * @since 2023-10-18 21:04:06
 */
@Service("roleService")
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {


     //根据用户id查询角色信息
     @Override
     public List<String> selectRoleKeyByUserId(Long userId) {
          //如果userId为1,那么角色权限字符串就只需要返回一个admin
          if(userId==1L){
               List<String> roles = new ArrayList<>();
               roles.add("admin");
               return roles;
          }
          //如果用户id不为1,那么需要根据userId连表查询对应的roleId,然后再去角色表中去查询
          //对应的角色权限字符串
          //这里我们期望RoleMapper中封装一个方法去帮我们实现这个复杂的操作
          RoleMapper roleMapper = getBaseMapper();
          return roleMapper.selectRoleKeyByUserId(userId);
     }

     @Override
     public ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize) {
          LambdaQueryWrapper<Role> lambdaQueryWrapper = new LambdaQueryWrapper<>();
          lambdaQueryWrapper.like(StringUtils.hasText(role.getRoleName()),Role::getRoleName,role.getRoleName());
          lambdaQueryWrapper.eq(StringUtils.hasText(role.getStatus()),Role::getStatus,role.getStatus());
          lambdaQueryWrapper.orderByAsc(Role::getRoleSort);
          Page<Role> page = new Page<>(pageNum,pageSize);
          page(page,lambdaQueryWrapper);
          PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());
          return ResponseResult.okResult(pageVo);
     }
}

3.3 测试

2. 改变角色状态

要求能够修改角色的停启用状态

2.1 接口分析

请求方式请求路径是否需求token头
PUTsystem/role/changeStatus

请求体:

{
    "roleId":"11",
    "status":"1"
}

响应格式:

{
	"code":200,
	"msg":"操作成功"
}

2.2 代码实现

第一步:controller层新增

package com.keke.controller;

import com.keke.domain.ResponseResult;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.entity.Role;
import com.keke.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/system/role")
public class RoleController {

     @Autowired
     private RoleService roleService;

     @GetMapping("/list")
     public ResponseResult selectPageRole(Role role,Integer pageNum,Integer pageSize){
          return roleService.selectPageRole(role,pageNum,pageSize);
     }

     @PutMapping("/changeStatus")
     public ResponseResult changeRoleStatus(@RequestBody ChangeRoleStatusDto changeRoleStatusDto){
          return roleService.changeRoleStatus(changeRoleStatusDto);
     }
}

第二步:domain/dto新增

package com.keke.domain.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ChangeRoleStatusDto {

     private Long roleId;
     //角色状态(0正常 1停用)
     private String status;

}

第三步:service层新增

package com.keke.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.keke.domain.ResponseResult;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.entity.Role;

import java.util.List;


/**
 * 角色信息表(Role)表服务接口
 *
 * @author makejava
 * @since 2023-10-18 21:04:06
 */
public interface RoleService extends IService<Role> {

     List<String> selectRoleKeyByUserId(Long userId);


     ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize);

     ResponseResult changeRoleStatus(ChangeRoleStatusDto changeRoleStatusDto);
}

第四步:impl层新增

package com.keke.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.keke.domain.ResponseResult;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.entity.Role;
import com.keke.domain.vo.PageVo;
import com.keke.mapper.RoleMapper;
import com.keke.service.RoleService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * 角色信息表(Role)表服务实现类
 *
 * @author makejava
 * @since 2023-10-18 21:04:06
 */
@Service("roleService")
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {


     //根据用户id查询角色信息
     @Override
     public List<String> selectRoleKeyByUserId(Long userId) {
          //如果userId为1,那么角色权限字符串就只需要返回一个admin
          if(userId==1L){
               List<String> roles = new ArrayList<>();
               roles.add("admin");
               return roles;
          }
          //如果用户id不为1,那么需要根据userId连表查询对应的roleId,然后再去角色表中去查询
          //对应的角色权限字符串
          //这里我们期望RoleMapper中封装一个方法去帮我们实现这个复杂的操作
          RoleMapper roleMapper = getBaseMapper();
          return roleMapper.selectRoleKeyByUserId(userId);
     }

     @Override
     public ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize) {
          LambdaQueryWrapper<Role> lambdaQueryWrapper = new LambdaQueryWrapper<>();
          lambdaQueryWrapper.like(StringUtils.hasText(role.getRoleName()),Role::getRoleName,role.getRoleName());
          lambdaQueryWrapper.eq(StringUtils.hasText(role.getStatus()),Role::getStatus,role.getStatus());
          lambdaQueryWrapper.orderByAsc(Role::getRoleSort);
          Page<Role> page = new Page<>(pageNum,pageSize);
          page(page,lambdaQueryWrapper);
          PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());
          return ResponseResult.okResult(pageVo);
     }

     @Override
     public ResponseResult changeRoleStatus(ChangeRoleStatusDto changeRoleStatusDto) {
          Long roleId = changeRoleStatusDto.getRoleId();
          Role role = getById(roleId);
          role.setStatus(changeRoleStatusDto.getStatus());
          updateById(role);
          return ResponseResult.okResult();
     }
}

2.3 测试

把该用户启用

 

数据库中状态正常

3. 新增角色

需要提供新增角色的功能。新增角色时能够直接设置角色所关联的菜单权限

首先应该获取权限菜单数,勾选,然后才能新增

分析下来,有两个接口需要实现

3.1 获取菜单权限树接口

3.1.1 接口分析
请求方式请求路径是否需求token头
GET/system/menu/treeselect

无需请求参数

响应格式:

{
	"code":200,
	"data":[
		{
			"children":[],
			"id":"2023",
			"label":"写博文",
			"parentId":"0"
		},
		{
			"children":[
				{
					"children":[
						{
							"children":[],
							"id":"1001",
							"label":"用户查询",
							"parentId":"100"
						},
						{
							"children":[],
							"id":"1007",
							"label":"重置密码",
							"parentId":"100"
						}
					],
					"id":"100",
					"label":"用户管理",
					"parentId":"1"
				},
				{
					"children":[
						{
							"children":[],
							"id":"2024",
							"label":"友链新增",
							"parentId":"2022"
						},
						{
							"children":[],
							"id":"2025",
							"label":"友链修改",
							"parentId":"2022"
						},
						{
							"children":[],
							"id":"2026",
							"label":"友链删除",
							"parentId":"2022"
						},
						{
							"children":[],
							"id":"2027",
							"label":"友链查询",
							"parentId":"2022"
						}
					],
					"id":"2022",
					"label":"友链管理",
					"parentId":"2017"
				},
				{
					"children":[],
					"id":"2021",
					"label":"标签管理",
					"parentId":"2017"
				}
			],
			"id":"2017",
			"label":"内容管理",
			"parentId":"0"
		}
	],
	"msg":"操作成功"
}
3.1.2 代码实现

3.1.3 测试

3.2 新增角色接口

3.2.1 接口分析
请求方式请求路径是否需求token头
POSTsystem/role

请求体:

{
    "roleName":"测试新增角色",
    "roleKey":"wds",
    "roleSort":0,
    "status":"0",
    "menuIds":[
        "1",
        "100"
    ],
    "remark":"我是角色备注"
}

响应格式:

{
	"code":200,
	"msg":"操作成功"
}
3.2.2 代码实现

第一步:domain/dto层新增

package com.keke.domain.dto;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;
import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class AdminAddRoleDto {
     //角色名称
     private String roleName;
     //角色权限字符串
     private String roleKey;
     //显示顺序
     private Integer roleSort;
     //角色状态(0正常 1停用)
     private String status;
     //备注
     private String remark;
     //关联的menuId
     List<Long> menuIds;
}

第二步:domain/entity层新增

package com.keke.domain.entity;

import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import com.baomidou.mybatisplus.annotation.TableName;

/**
 * 角色和菜单关联表(RoleMenu)表实体类
 *
 * @author makejava
 * @since 2023-10-23 17:00:27
 */
@SuppressWarnings("serial")
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("sys_role_menu")
public class RoleMenu {
    //角色ID
    private Long roleId;
    //菜单ID
    private Long menuId;

}

第三步:mapper层新增RoleMenuMapper

package com.keke.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.keke.domain.entity.RoleMenu;


/**
 * 角色和菜单关联表(RoleMenu)表数据库访问层
 *
 * @author makejava
 * @since 2023-10-23 17:01:09
 */
public interface RoleMenuMapper extends BaseMapper<RoleMenu> {

}

第四步:service新增RoleMenuService

package com.keke.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.keke.domain.entity.RoleMenu;


/**
 * 角色和菜单关联表(RoleMenu)表服务接口
 *
 * @author makejava
 * @since 2023-10-23 17:01:09
 */
public interface RoleMenuService extends IService<RoleMenu> {

}

第五步:impl层新增RoleMenuServiceImpl

package com.keke.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.keke.domain.entity.RoleMenu;
import com.keke.mapper.RoleMenuMapper;
import com.keke.service.RoleMenuService;
import org.springframework.stereotype.Service;

/**
 * 角色和菜单关联表(RoleMenu)表服务实现类
 *
 * @author makejava
 * @since 2023-10-23 17:01:09
 */
@Service("roleMenuService")
public class RoleMenuServiceImpl extends ServiceImpl<RoleMenuMapper, RoleMenu> implements RoleMenuService {

}

第六步:controller的RoleController新增

package com.keke.controller;

import com.keke.domain.ResponseResult;
import com.keke.domain.dto.AdminAddRoleDto;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.entity.Role;
import com.keke.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/system/role")
public class RoleController {

     @Autowired
     private RoleService roleService;

     @GetMapping("/list")
     public ResponseResult selectPageRole(Role role,Integer pageNum,Integer pageSize){
          return roleService.selectPageRole(role,pageNum,pageSize);
     }

     @PutMapping("/changeStatus")
     public ResponseResult changeRoleStatus(@RequestBody ChangeRoleStatusDto changeRoleStatusDto){
          return roleService.changeRoleStatus(changeRoleStatusDto);
     }

     @PostMapping
     public ResponseResult addRole(@RequestBody AdminAddRoleDto adminAddRoleDto){
          return roleService.addRole(adminAddRoleDto);
     }

}

第七步:service层的RoleService新增

package com.keke.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.keke.domain.ResponseResult;
import com.keke.domain.dto.AdminAddRoleDto;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.entity.Role;

import java.util.List;


/**
 * 角色信息表(Role)表服务接口
 *
 * @author makejava
 * @since 2023-10-18 21:04:06
 */
public interface RoleService extends IService<Role> {

     List<String> selectRoleKeyByUserId(Long userId);


     ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize);

     ResponseResult changeRoleStatus(ChangeRoleStatusDto changeRoleStatusDto);

     ResponseResult addRole(AdminAddRoleDto adminAddRoleDto);

}

第八步:impl层的RoleServiceImpl新增,记得方法加上@Transactional注解

package com.keke.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.keke.domain.ResponseResult;
import com.keke.domain.dto.AdminAddRoleDto;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.entity.Role;
import com.keke.domain.entity.RoleMenu;
import com.keke.domain.vo.PageVo;
import com.keke.mapper.RoleMapper;
import com.keke.service.RoleMenuService;
import com.keke.service.RoleService;
import com.keke.utils.BeanCopyUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 角色信息表(Role)表服务实现类
 *
 * @author makejava
 * @since 2023-10-18 21:04:06
 */
@Service("roleService")
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {

     @Autowired
     private RoleMenuService roleMenuService;

     //根据用户id查询角色信息
     @Override
     public List<String> selectRoleKeyByUserId(Long userId) {
          //如果userId为1,那么角色权限字符串就只需要返回一个admin
          if(userId==1L){
               List<String> roles = new ArrayList<>();
               roles.add("admin");
               return roles;
          }
          //如果用户id不为1,那么需要根据userId连表查询对应的roleId,然后再去角色表中去查询
          //对应的角色权限字符串
          //这里我们期望RoleMapper中封装一个方法去帮我们实现这个复杂的操作
          RoleMapper roleMapper = getBaseMapper();
          return roleMapper.selectRoleKeyByUserId(userId);
     }

     @Override
     public ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize) {
          LambdaQueryWrapper<Role> lambdaQueryWrapper = new LambdaQueryWrapper<>();
          lambdaQueryWrapper.like(StringUtils.hasText(role.getRoleName()),Role::getRoleName,role.getRoleName());
          lambdaQueryWrapper.eq(StringUtils.hasText(role.getStatus()),Role::getStatus,role.getStatus());
          lambdaQueryWrapper.orderByAsc(Role::getRoleSort);
          Page<Role> page = new Page<>(pageNum,pageSize);
          page(page,lambdaQueryWrapper);
          PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());
          return ResponseResult.okResult(pageVo);
     }

     @Override
     public ResponseResult changeRoleStatus(ChangeRoleStatusDto changeRoleStatusDto) {
          Long roleId = changeRoleStatusDto.getRoleId();
          Role role = getById(roleId);
          role.setStatus(changeRoleStatusDto.getStatus());
          updateById(role);
          return ResponseResult.okResult();
     }

     @Transactional
     @Override
     public ResponseResult addRole(AdminAddRoleDto adminAddRoleDto) {
          //Bean拷贝
          Role role = BeanCopyUtils.copyBean(adminAddRoleDto, Role.class);
          //拿到菜单权限id集合
          List<Long> menuIds = adminAddRoleDto.getMenuIds();
          //流式处理转化,把每一个menuId都设置到该roleId下
          List<RoleMenu> roleMenuList = menuIds.stream()
                  .map(new Function<Long, RoleMenu>() {
                       @Override
                       public RoleMenu apply(Long menuId) {
                            RoleMenu roleMenu = new RoleMenu();
                            roleMenu.setRoleId(role.getId());
                            roleMenu.setMenuId(menuId);
                            return roleMenu;
                       }
                  }).collect(Collectors.toList());
          //mp批量保存到数据库中
          roleMenuService.saveBatch(roleMenuList);
          //封装返回
          return ResponseResult.okResult();
     }
}

第九步:domain/entity的Role实体类修改四个字段为mp自动填充

package com.keke.domain.entity;

import java.util.Date;
import java.io.Serializable;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import com.baomidou.mybatisplus.annotation.TableName;

/**
 * 角色信息表(Role)表实体类
 *
 * @author makejava
 * @since 2023-10-18 21:03:52
 */
@SuppressWarnings("serial")
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("sys_role")
public class Role {
    //角色ID
    private Long id;
    //角色名称
    private String roleName;
    //角色权限字符串
    private String roleKey;
    //显示顺序
    private Integer roleSort;
    //角色状态(0正常 1停用)
    private String status;
    //删除标志(0代表存在 1代表删除)
    private String delFlag;
    //创建者
    @TableField(fill = FieldFill.INSERT)
    private Long createBy;
    //创建时间
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    //更新者
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateBy;
    //更新时间
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    //备注
    private String remark;
}
3.2.3 测试

4. 修改角色

需要提供修改角色的功能。修改角色时可以修改角色所关联的菜单权限

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1124461.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从0开始学云计算之服务器:服务的定义,特点,应用场景,分类

服务器定义 服务器是计算机的一种。它比普通计算机运行速度更快、负载更高且价格更高。 服务器的英文名称为“Server”&#xff0c;是指在网络上提供各种服务的高性能计算机。作为网络的节点&#xff0c;存储、处理网络上80%的数据、信息&#xff0c;因此也被称为x络的灵魂。 …

三、机器学习基础知识:Python常用机器学习库(Pandas)

文章目录 1、Pandas2、Series数据结构2.1 Series的创建2.2 Series的索引与访问2.3 Series的常见操作 3、DataFrame对象3.1 DataFrame的创建与索引3.2 DataFrame的访问3.3 DataFrame的常见操作 4、数据对齐5、缺失数据处理6、Pandas存取文件 1、Pandas Pandas 库是以Numpy库为基…

互联网医院牌照|互联网医院牌照资质申请流程和所需材料

那么不同的企业基于不同的业务板块&#xff0c;所需要搭建的互联网医院也存在差异&#xff0c;平衡好自身业务及互联网医疗建设二者间的关系&#xff0c;是企业建设互医的基础需求&#xff0c;那么互联网医院牌照办理流程和材料都包含哪些呢&#xff1f;接下来就给大家介绍下。…

短视频矩阵系统软件源码

短视频矩阵系统软件源码 视频成为获得免费流量最便宜的渠道&#xff0c;平台给所有视频最基础的保底流量。如果按照一个视频最低500流量计算&#xff0c;5个账户就是2500的流量&#xff0c;200个视频就是50W流量&#xff0c;如果从其他渠道获得50W流量是个很困难的事情。短视频…

第四章 文件管理 三、文件目录

目录 一、文件控制块 1、定义&#xff1a; 2、要进行哪些操作 &#xff08;1&#xff09;搜索: &#xff08;2&#xff09;创建文件: &#xff08;3&#xff09;删除文件: &#xff08;4&#xff09;显示目录: &#xff08;5&#xff09;修改目录: 二、目录结构 1、单…

QSOP24封装的语音芯片优势列举

1. 封装紧凑&#xff1a;QSOP24封装采用了更小的封装尺寸&#xff0c;相比于其他大型封装&#xff0c;它的体积更小、尺寸更紧凑。这使得它在空间受限的应用中更加适用&#xff0c;可以方便地集成到小型或薄型设备中&#xff0c;提供更高的设计灵活性。 2. 强大的功能&#xf…

Go之流程控制大全: 细节、示例与最佳实践

引言 在计算机编程中&#xff0c;流程控制是核心的组成部分&#xff0c;它决定了程序应该如何根据给定的情况执行或决策。以下是Go语言所支持的流程控制结构的简要概览&#xff1a; 流程控制类型代码if-else条件分支if condition { } else { }for循环for initialization; con…

el-table相同的值合并单元格+多级表头

<el-table border :data"costList" :span-method"objectSpanMethod" ><el-table-column label"类别" prop"类别" width"120"> </el-table-column><el-table-column label"费用科目" prop&qu…

如何给照片添加水印?请看下面3个简单教程

如何给照片添加水印&#xff1f;随着智能手机的普及和不断提升的拍摄技术&#xff0c;如今人们可以轻松使用手机进行高质量的照片拍摄。从老人到小孩&#xff0c;每个人都可以在日常生活中捕捉到美好瞬间&#xff0c;并将其记录下来。作为一种表达自己的方式&#xff0c;现在手…

重拾潜行初心,用艾尔莎EA B760M-E D5玩转《刺客信条:幻景》

不知不觉&#xff0c;《刺客信条》系列游戏已经推出了有近20部作品。为了体现游戏的差异化&#xff0c;育碧经常会在新作上进行不同程度的玩法修改&#xff0c;然而在多部作品的变化与改动以后&#xff0c;许多玩家已经开始出现了审美疲劳&#xff0c;并怀念起了初代经典而纯粹…

2018年亚太杯APMCM数学建模大赛A题老年人平衡能力的实时训练模型求解全过程文档及程序

2018年亚太杯APMCM数学建模大赛 A题 老年人平衡能力的实时训练模型 原题再现 跌倒在老年人中很常见。跌倒可能会导致老年人出现许多并发症&#xff0c;因为他们的康复能力通常较差&#xff0c;因此副作用可能会使人衰弱&#xff0c;从而加速身体衰竭。此外&#xff0c;对跌倒…

深度分析微软未来十年在AI领域的投资机会

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 总结&#xff1a; &#xff08;1&#xff09;人工智能不仅仅是一个概念&#xff0c;还是一场旨在提高生产率的技术革命&#xff0c;并且有可能在未来十年让全球GDP增长10%。 &#xff08;2&#xff09;由于庞大的用户规模&…

数字化转型“黑话”知多少?一文让你不仅听得懂、还会落地执行

何为OEE&#xff1f;如何提升OEE&#xff1f; 从上图可知&#xff0c;客户是机加工行业的&#xff0c;而机加工行业属于重资产行业&#xff0c;主要以设备生产为主&#xff0c;管好设备、提升设备的综合效率&#xff08;OEE&#xff09;。效率高了&#xff0c;毛利率自然也会增…

java--三元运算符、运算符的优先级

1.三元运算符介绍 1.格式&#xff1a;条件表达式&#xff1f;值1&#xff1a;值2&#xff1b; 2.执行流程&#xff1a;首先计算关系表达式的值&#xff0c;如果值为true&#xff0c;返回值1&#xff0c;如果为false&#xff0c;返回值2 2.运算符优先级 1.在表达式中&#xf…

2016年408真题复盘

紫色标记是认为有一定的思维难度或重点总结 红色标记是这次刷真题做错的 记录自己对题目的一些想法与联系&#xff0c;可能并不太关注题目本身。 分数用时 选择部分10/23 78/8044min大题部分10/2361/7076min总分139120min 摘自知乎老哥&#xff1a;“我做历年真题时&#xff0c…

Unity中Shader的Fallback

文章目录 前言一、解决 Pass 复用的方案方案一&#xff1a;使用之前的UsePass方案&#xff0c;把ShadowCaster的Pass提出来到一个单独的Shader中&#xff0c;在使用的时候直接使用 UsePass方案二&#xff1a;使用Fallback功能 前言 Unity中Shader的Fallback&#xff0c;我们在…

g++ 重要编译参数

一. -g 产生带调试信息的可执行文件test 产生带调试信息的可执行文件test&#xff0c;如果不加-g&#xff0c;输出的test是不包含调试信息的。加了-g 才可以被GDB所调试。 g -g test.cpp -o test二. -o 优化源代码 优化指省略掉代码中从未使用过的变量&#xff0c;直接将常量…

成集云 | 云椒集成用友T | 解决方案

源系统成集云目标系统 方案介绍 云椒SAAS管理系统&#xff0c;帮助餐饮企业实现精细化管理、批量化生产、标准化出品、连锁化经营、降本增效、食安控制的全流程解决方案。该系统为团餐企业的转型和发展持续赋能&#xff0c;打造全产业链生命周期的可持续发展服务平台&#xf…

纺织工厂数字孪生3D可视化管理平台,推动纺织产业数字化转型

近年来&#xff0c;我国加快数字化发展战略部署&#xff0c;全面推进制造业数字化转型&#xff0c;促进数字经济与实体经济深度融合。以数字孪生、物联网、云计算、人工智能为代表的数字技术发挥重要作用。聚焦数字孪生智能工厂可视化平台&#xff0c;推动纺织制造业数字化转型…

程序设计(2023年10月)

函数传参有三种方式&#xff1a;值传递&#xff0c;地址传递&#xff0c;引用传递 排序算法&#xff1a;见下文八大排序 数据结构—八大排序_数据结构排序-CSDN博客