接下来我们写一下如何实现增删改查
菜单接口开发
我们先来开发菜单的接口,因为这3个表:用户表、角色表、菜单表,才有菜单表是不需要通过其他表来获取信息的。比如用户需要关联角色,角色需要关联菜单,而菜单不需要主动关联其他表。因此菜单表的增删改查是最简单的。
再回到我们的前端项目,登录完成之后我们通过JWT获取项目的导航菜单和权限,那么接下来我们就先编写这个接口。
获取菜单导航和权限的链接是/sys/menu/nav,然后我们的菜单导航的json数据应该是这样的:
{
title: '角色管理',
icon: 'el-icon-rank',
path: '/sys/roles',
name: 'SysRoles',
component: 'sys/Role',
children: []
然后返回的权限数据应该是个数组:
["sys:menu:list","sys:menu:save","sys:user:list"...]
注意导航菜单那里有个children,也就是子菜单,是个树形结构,因为我们的菜单可能这样:
系统管理 - 菜单管理 - 添加菜单
可以看到这就已经有3级了菜单了。
所以在打代码时候要注意这个关系的关联。我们的SysMenu实体类中有个parentId,但是没有children,因此我们可以在SysMenu中添加一个children,当然了其实不添加也可以,因为我们也需要一个dto,这样我们才能按照上面json数据格式返回。
我们还是来添加一个children吧:
- com.rao.entity.SysMenu
@Data
@EqualsAndHashCode(callSuper = true)
public class SysMenu extends BaseEntity {
...
@TableField(exist = false)
private List<SysMenu> children = new ArrayList<>();
}
然后我们也先来定义一个SysMenuDto吧,知道要返回什么样的数据,我们就只需要去填充数据就好了
- com.rao.common.dto.SysMenuDto
@Data
public class SysMenuDto implements Serializable {
private Long id;
private String title;
private String icon;
private String path;
private String name;
private String component;
List<SysMenuDto> children = new ArrayList<>();
}
ok,我们来开始我们的编码
- com.rao.controller.SysMenuController#nav
package com.rao.controller;
import cn.hutool.core.map.MapUtil;
import com.rao.common.lang.Result;
import com.rao.entity.SysUser;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.security.Principal;
/**
* <p>
* 前端控制器
* </p>
*
*
* @since 2022-03-09
*/
@RestController
@RequestMapping("/sys/menu")
public class SysMenuController extends BaseController {
@GetMapping("/nav")
public Result nav(Principal principal){
String name = principal.getName();
SysUser sysUser = sysUserService.getByUsername(name);
String[] authoritys = StringUtils.tokenizeToStringArray(sysUserService.getUserAuthoriyInfo(sysUser.getId()), ",");
return Result.succ(
MapUtil.builder()
.put("nav",sysMenuService.getcurrentUserNav())
.put("authoritys",authoritys)
.map()
);
}
}
方法中Principal principal表示注入当前用户的信息,getName就可以获取当当前用户的用户名了。sysUserService.getUserAuthorityInfo方法我们之前已经说过了,就在我们登录完成或者身份认证时候需要返回用户权限时候编写的。然后通过StringUtils.tokenizeToStringArray把字符串通过逗号分开组成数组形式。
重点在与sysMenuService.getcurrentUserNav,获取当前用户的菜单导航,
package com.rao.service.impl;
import com.rao.common.dto.SysMenuDto;
import com.rao.entity.SysMenu;
import com.rao.entity.SysUser;
import com.rao.mapper.SysMenuMapper;
import com.rao.mapper.SysUserMapper;
import com.rao.service.SysMenuService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rao.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* <p>
* 服务实现类
* </p>
*
*
* @since 2022-03-09
*/
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {
@Autowired
SysUserService sysUserService;
@Autowired
SysUserMapper sysUserMapper;
@Override
public List<SysMenuDto> getcurrentUserNav() {
String username = (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
SysUser sysUser = sysUserService.getByUsername(username);
List<Long> navMenuIds = sysUserMapper.getNavMenuIds(sysUser.getId());
List<SysMenu> menus=buildTreeMenu(this.listByIds(navMenuIds));
return conver(menus);
}
private List<SysMenu> buildTreeMenu(List<SysMenu> listByIds) {
List<SysMenu> finalMenus=new ArrayList<>();
for (SysMenu menu:listByIds){
for (SysMenu e:listByIds){
if (e.getParentId()==menu.getId()){
menu.getChildren().add(e);
}
}
if (menu.getParentId()==0){
finalMenus.add(menu);
}
}
return finalMenus;
}
/**
* menu转dto
*/
private List<SysMenuDto> conver(List<SysMenu> menus) {
List<SysMenuDto> menuDtos=new ArrayList<>();
menus.forEach(m->{
SysMenuDto sysMenuDto = new SysMenuDto();
sysMenuDto.setId(m.getId());
sysMenuDto.setName(m.getPerms());
sysMenuDto.setTitle(m.getName());
sysMenuDto.setComponent(m.getComponent());
sysMenuDto.setIcon(m.getIcon());
sysMenuDto.setPath(m.getPath());
if (m.getChildren().size()>0){
sysMenuDto.setChildren(conver(m.getChildren()));
}
menuDtos.add(sysMenuDto);
});
return menuDtos;
}
@Override
public List<SysMenu> tree() {
// 获取所有菜单信息
List<SysMenu> sysMenus = this.list(new QueryWrapper<SysMenu>().orderByAsc("orderNum"));
// 转成树状结构
return buildTreeMenu(sysMenus);
}
}
接口中sysUserMapper.getNavMenuIds我们之前就已经写过的了,通过用户id获取菜单的id,然后后面就是转成树形结构,buildTreeMenu方法的思想很简单,我们现实把菜单循环,让所有菜单先找到各自的子节点,然后我们在把最顶级的菜单获取出来,这样顶级下面有二级,二级也有自己的三级。最后就是convert把menu转成menuDto。这个比较简单,就不说了。
好了,导航菜单已经开发完毕,我们来写菜单管理的增删改查,因为菜单列表也是个树形接口,这次我们就不是获取当前用户的菜单列表的,而是所有菜单然后组成树形结构,一样的思想,数据不一样而已。
- com.rao.controller.SysMenuController
package com.rao.controller;
import cn.hutool.core.map.MapUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.rao.common.lang.Const;
import com.rao.common.lang.Result;
import com.rao.entity.SysMenu;
import com.rao.entity.SysRoleMenu;
import com.rao.entity.SysUser;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.print.attribute.standard.RequestingUserName;
import java.security.Principal;
import java.time.LocalDateTime;
import java.util.List;
/**
* <p>
* 前端控制器
* </p>
*
*
* @since 2022-03-09
*/
@RestController
@RequestMapping("/sys/menu")
public class SysMenuController extends BaseController {
@GetMapping("/nav")
public Result nav(Principal principal){
String name = principal.getName();
SysUser sysUser = sysUserService.getByUsername(name);
String[] authoritys = StringUtils.tokenizeToStringArray(sysUserService.getUserAuthoriyInfo(sysUser.getId()), ",");
return Result.succ(
MapUtil.builder()
.put("nav",sysMenuService.getcurrentUserNav())
.put("authoritys",authoritys)
.map()
);
}
@GetMapping("/info/{id}")
@PreAuthorize("hasAuthority('sys:menu:list')")
public Result info(@PathVariable("id") Long id){
return Result.succ(sysMenuService.getById(id));
}
@GetMapping("/list")
@PreAuthorize("hasAuthority('sys:menu:list')")
public Result list(){
List<SysMenu> menus = sysMenuService.tree();
return Result.succ(menus);
}
@PostMapping("/save")
@PreAuthorize("hasAuthority('sys:menu:save')")
public Result save(@Validated @RequestBody SysMenu sysMenu){
sysMenu.setCreated(LocalDateTime.now());
sysMenu.setStatu(Const.STATUS_ON);
sysMenuService.save(sysMenu);
return Result.succ(sysMenu);
}
@PostMapping("/update")
@PreAuthorize("hasAuthority('sys:menu:update')")
public Result update(@Validated @RequestBody SysMenu sysMenu){
sysMenu.setCreated(LocalDateTime.now());
sysMenuService.updateById(sysMenu);
sysUserService.clearUserAuthorityInfoByMenuId(sysMenu.getId());
return Result.succ(sysMenu);
}
@Transactional
@PostMapping("/delete/{id}")
@PreAuthorize("hasAuthority('sys:menu:delete')")
public Result delete(@PathVariable("id") Long id){
int count = sysMenuService.count(new QueryWrapper<SysMenu>().eq("parent_id", id));
if (count>0){
return Result.fail("请先删除子菜单");
}
//先清除所有与该菜单相关的权限缓存
sysUserService.clearUserAuthorityInfoByMenuId(id);
sysMenuService.removeById(id);
//同步删除
sysRoleMenuService.remove(new QueryWrapper<SysRoleMenu>().eq("menu_id",id));
return Result.succ("");
}
}