十一、后台模块-菜单列表
菜单指的是权限菜单,也就是一堆权限字符串
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){
List<AdminMenuVo> adminMenuVos = menuService.selectAllMenu(menu);
return ResponseResult.okResult(adminMenuVos);
}
}
第三步:把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);
List<AdminMenuVo> 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 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头 |
---|---|---|
Get | system/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头 |
---|---|---|
PUT | system/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头 |
---|---|---|
DELETE | content/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头 |
---|---|---|
GET | system/role/list | 是 |
Query格式请求参数:
pageNum: 页码
pageSize: 每页条数
roleName:角色名称
status:状态
响应格式:
{
"code":200,
"data":{
"rows":[
{
"id":"12",
"roleKey":"link",
"roleName":"友链审核员",
"roleSort":"1",
"status":"0"
}
],
"total":"1"
},
"msg":"操作成功"
}
1.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);
}
}
1.3 测试
2. 改变角色状态
要求能够修改角色的停启用状态
2.1 接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
PUT | system/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 代码实现
第一步:keke-framework的domain/entity Role实体类修改如下,加上mp自填充,并且加上menuIds字段
package com.keke.domain.entity;
import java.util.Date;
import java.io.Serializable;
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;
/**
* 角色信息表(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;
//角色关联的menuIds
List<Long> menuIds;
}
第二步:vo层创建MenuTreeVo
package com.keke.domain.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
//新增角色-获取菜单下拉树列表
public class MenuTreeVo {
private static final long serialVersionUID = 1L;
/** 节点ID */
private Long id;
/** 节点名称 */
private String label;
private Long parentId;
/** 子节点 */
private List<MenuTreeVo> children;
}
第三步:entity层新增RoleMenu实体类
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 {
}
第七步:在keke-framework工程的utils目录新建SystemConverter类,写入如下,是一个获取菜单下拉树工具类
package com.keke.utils;
import com.keke.domain.entity.Menu;
import com.keke.domain.vo.MenuTreeVo;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class SystemConverter {
private SystemConverter() {}
public static List<MenuTreeVo> buildMenuSelectTree(List<Menu> menus) {
List<MenuTreeVo> MenuTreeVos = menus.stream()
.map(m -> new MenuTreeVo(m.getId(), m.getMenuName(), m.getParentId(), null))
.collect(Collectors.toList());
List<MenuTreeVo> options = MenuTreeVos.stream()
.filter(o -> o.getParentId().equals(0L))
.map(o -> o.setChildren(getChildList(MenuTreeVos, o)))
.collect(Collectors.toList());
return options;
}
/**
* 得到子节点列表
*/
private static List<MenuTreeVo> getChildList(List<MenuTreeVo> list, MenuTreeVo option) {
List<MenuTreeVo> options = list.stream()
.filter(o -> Objects.equals(o.getParentId(), option.getId()))
.map(o -> o.setChildren(getChildList(list, o)))
.collect(Collectors.toList());
return options;
}
}
第八步:keke-admin的MenuController中写接口
package com.keke.controller;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Menu;
import com.keke.domain.vo.AdminMenuVo;
import com.keke.domain.vo.MenuTreeVo;
import com.keke.service.MenuService;
import com.keke.utils.BeanCopyUtils;
import com.keke.utils.SystemConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/system/menu")
public class MenuController {
@Autowired
private MenuService menuService;
//查询菜单列表
@GetMapping("/list")
public ResponseResult selectAllMenu(Menu menu){
List<AdminMenuVo> adminMenuVos = menuService.selectAllMenu(menu);
return ResponseResult.okResult(adminMenuVos);
}
//新增菜单
@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);
}
@GetMapping("/treeselect")
public ResponseResult treeSelect(){
//复用之前的selectMenuList方法。方法需要参数,参数可以用来进行条件查询,而这个方法不需要条件,所以直接new Menu()传入
//这样就可以获取所有的菜单了
List<AdminMenuVo> adminMenuVos = menuService.selectAllMenu(new Menu());
List<Menu> menus = BeanCopyUtils.copyBeanList(adminMenuVos, Menu.class);
List<MenuTreeVo> options = SystemConverter.buildMenuSelectTree(menus);
return ResponseResult.okResult(options);
}
}
3.1.3 测试
3.2 新增角色接口
3.2.1 接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
POST | system/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. 修改角色
需要提供修改角色的功能。修改角色时可以修改角色所关联的菜单权限
这里可以分析到修改角色有三个接口,一个点击是修改角色后,角色信息的回显接口,回显中有一个菜单权限树接口,另一个是修改角色的接口
4.1 角色信息回显接口
4.1.1接口分析
请求路径 | 请求方式 | 是否需求token头 |
---|---|---|
system/role/{id} | Get | 是 |
请求参数PathVariable格式:
id: 角色id
响应格式:
{
"code":200,
"data":{
"id":"11",
"remark":"嘎嘎嘎",
"roleKey":"aggag",
"roleName":"嘎嘎嘎",
"roleSort":"5",
"status":"0"
},
"msg":"操作成功"
}
4.1.2 代码实现
第一步:controller层
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);
}
@GetMapping("/{id}")
public ResponseResult getRoleInfo(@PathVariable("id") Long id){
return roleService.getRoleInfo(id);
}
}
第二步:domain/vo层新增
package com.keke.domain.vo;
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;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AdminRoleVo {
//角色ID
private Long id;
//角色名称
private String roleName;
//角色权限字符串
private String roleKey;
//显示顺序
private Integer roleSort;
//角色状态(0正常 1停用)
private String status;
//备注
private String remark;
}
第三步:service层
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);
ResponseResult getRoleInfo(Long id);
}
第四步: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.AdminAddRoleDto;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.entity.Role;
import com.keke.domain.entity.RoleMenu;
import com.keke.domain.vo.AdminRoleVo;
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();
}
@Override
public ResponseResult getRoleInfo(Long id) {
Role role = getById(id);
AdminRoleVo adminRoleVo = BeanCopyUtils.copyBean(role, AdminRoleVo.class);
return ResponseResult.okResult(adminRoleVo);
}
}
4.1.3 测试
回显成功
4.2 对应角色菜单权限树接口
4.2.1 接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
Get | /system/menu/roleMenuTreeselect/{id} | 是 |
请求参数PathVariable格式:
id: 角色id
响应格式:
字段介绍
menus:菜单树
checkedKeys:角色所关联的菜单权限id列表
{
"code":200,
"data":{
"menus":[
{
"children":[],
"id":"2023",
"label":"写博文",
"parentId":"0"
},
{
"children":[
{
"children":[
{
"children":[],
"id":"1001",
"label":"用户查询",
"parentId":"100"
},
{
"children":[],
"id":"1002",
"label":"用户新增",
"parentId":"100"
},
{
"children":[],
"id":"1003",
"label":"用户修改",
"parentId":"100"
},
{
"children":[],
"id":"1004",
"label":"用户删除",
"parentId":"100"
},
{
"children":[],
"id":"1005",
"label":"用户导出",
"parentId":"100"
},
{
"children":[],
"id":"1006",
"label":"用户导入",
"parentId":"100"
},
{
"children":[],
"id":"1007",
"label":"重置密码",
"parentId":"100"
}
],
"id":"100",
"label":"用户管理",
"parentId":"1"
},
{
"children":[
{
"children":[],
"id":"1008",
"label":"角色查询",
"parentId":"101"
},
{
"children":[],
"id":"1009",
"label":"角色新增",
"parentId":"101"
},
{
"children":[],
"id":"1010",
"label":"角色修改",
"parentId":"101"
},
{
"children":[],
"id":"1011",
"label":"角色删除",
"parentId":"101"
},
{
"children":[],
"id":"1012",
"label":"角色导出",
"parentId":"101"
}
],
"id":"101",
"label":"角色管理",
"parentId":"1"
},
{
"children":[
{
"children":[],
"id":"1013",
"label":"菜单查询",
"parentId":"102"
},
{
"children":[],
"id":"1014",
"label":"菜单新增",
"parentId":"102"
},
{
"children":[],
"id":"1015",
"label":"菜单修改",
"parentId":"102"
},
{
"children":[],
"id":"1016",
"label":"菜单删除",
"parentId":"102"
}
],
"id":"102",
"label":"菜单管理",
"parentId":"1"
}
],
"id":"1",
"label":"系统管理",
"parentId":"0"
},
{
"children":[
{
"children":[],
"id":"2019",
"label":"文章管理",
"parentId":"2017"
},
{
"children":[
{
"children":[],
"id":"2028",
"label":"导出分类",
"parentId":"2018"
}
],
"id":"2018",
"label":"分类管理",
"parentId":"2017"
},
{
"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"
}
],
"checkedKeys":[
"1001"
]
},
"msg":"操作成功"
}
4.2.2 代码实现
第一步:vo层新建RoleMenuTreeSelectVo,用于响应返回
package com.keke.domain.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RoleMenuTreeSelectVo {
private List<MenuTreeVo> menus;
private List<Long> checkedKeys;
}
第二步: 把keke-framework工程的MenuService接口修改为如下,增加了 '根据角色id查询对应角色菜单列表树' 的接口
package com.keke.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Menu;
import com.keke.domain.vo.AdminMenuVo;
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);
List<AdminMenuVo> selectAllMenu(Menu menu);
ResponseResult editMenu(Menu menu);
ResponseResult deleteMenu(Long menuId);
List<Long> selectMenuListByRoleId(Long roleId);
}
第三步: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.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 List<AdminMenuVo> 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 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();
}
@Override
public List<Long> selectMenuListByRoleId(Long roleId) {
return getBaseMapper().selectMenuListByRoleId(roleId);
}
/**
* 构建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;
}
}
第四步:MenuMapper新增
package com.keke.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.keke.domain.entity.Menu;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 菜单权限表(Menu)表数据库访问层
*
* @author makejava
* @since 2023-10-18 20:55:48
*/
@Mapper
public interface MenuMapper extends BaseMapper<Menu> {
//Mapper的实现类对应xml映射文件
List<String> selectPermsByUserId(Long userId);
List<Menu> selectAllRoutersMenu();
List<Menu> selectRoutersMenuTreeByUserId(Long userId);
List<Long> selectMenuListByRoleId(Long roleId);
}
第五步: 把keke-framework工程的resources/mapper目录的MenuMapper.xml修改为如下,增加了 '根据角色id查询对应角色菜单列表树' 的具体实现代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.keke.mapper.MenuMapper">
<select id="selectPermsByUserId" resultType="java.lang.String">
-- 这里的逻辑是先用userId连表查询roleId,再用roleId连表查询menuId,再根据menuId
-- 查询对应的用户权限
select
DISTINCT m.`perms`
from `sys_user_role` ur
left join `sys_role_menu` rm on ur.`role_id`=rm.`role_id`
left join `sys_menu` m on m.`id`=rm.`menu_id`
where
ur.`user_id`=#{userId} and
m.`menu_type` in ('C','F') and
m.`status`=0 and
m.`del_flag`=0
</select>
<select id="selectAllRoutersMenu" resultType="com.keke.domain.entity.Menu">
-- 这里与上面的sql差不多,只是menu_type有差别,还有查询的字段个数
SELECT
DISTINCT m.id, m.parent_id, m.menu_name, m.path, m.component, m.visible, m.status,
IFNULL(m.perms,'') AS perms, m.is_frame, m.menu_type, m.icon, m.order_num, m.create_time
FROM
`sys_menu` m
WHERE
-- 查询所有的,所以不需要加userId的条件
m.`menu_type` IN ('C','M') AND
m.`status` = 0 AND
m.`del_flag` = 0
ORDER BY
m.parent_id,m.order_num
</select>
<select id="selectRoutersMenuTreeByUserId" resultType="com.keke.domain.entity.Menu">
-- 这里与上面的sql差不多
SELECT
DISTINCT m.id, m.parent_id, m.menu_name, m.path, m.component, m.visible, m.status,
IFNULL(m.perms,'') AS perms, m.is_frame, m.menu_type, m.icon, m.order_num, m.create_time
FROM
`sys_user_role` ur
LEFT JOIN `sys_role_menu` rm ON ur.`role_id` = rm.`role_id`
LEFT JOIN `sys_menu` m ON m.`id` = rm.`menu_id`
WHERE
ur.`user_id` = #{userId} AND
m.`menu_type` IN ('C','M') AND
m.`status` = 0 AND
m.`del_flag` = 0
ORDER BY
m.parent_id,m.order_num
</select>
<select id="selectMenuListByRoleId" resultType="java.lang.Long">
select m.id
from sys_menu m
left join sys_role_menu rm on m.id = rm.menu_id
where rm.role_id = #{roleId}
order by m.parent_id, m.order_num
</select>
</mapper>
第六步:MenuController
package com.keke.controller;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Menu;
import com.keke.domain.vo.AdminMenuVo;
import com.keke.domain.vo.MenuTreeVo;
import com.keke.domain.vo.RoleMenuTreeSelectVo;
import com.keke.service.MenuService;
import com.keke.utils.BeanCopyUtils;
import com.keke.utils.SystemConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.parameters.P;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/system/menu")
public class MenuController {
@Autowired
private MenuService menuService;
//查询菜单列表
@GetMapping("/list")
public ResponseResult selectAllMenu(Menu menu){
List<AdminMenuVo> adminMenuVos = menuService.selectAllMenu(menu);
return ResponseResult.okResult(adminMenuVos);
}
//新增菜单
@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);
}
@GetMapping("/roleMenuTreeselect/{id}")
public ResponseResult selectMenuListByRoleId(@PathVariable("id") Long roleId){
List<AdminMenuVo> adminMenuVos = menuService.selectAllMenu(new Menu());
List<Menu> menus = BeanCopyUtils.copyBeanList(adminMenuVos, Menu.class);
List<Long> checkedKeys = menuService.selectMenuListByRoleId(roleId);
List<MenuTreeVo> menuTreeVos = SystemConverter.buildMenuSelectTree(menus);
RoleMenuTreeSelectVo roleMenuTreeSelectVo = new RoleMenuTreeSelectVo(menuTreeVos,checkedKeys);
return ResponseResult.okResult(roleMenuTreeSelectVo);
}
@PutMapping
public ResponseResult editMenu(@RequestBody Menu menu){
return menuService.editMenu(menu);
}
@DeleteMapping("/{menuId}")
public ResponseResult deleteMenu(@PathVariable("menuId") Long menuId){
return menuService.deleteMenu(menuId);
}
@GetMapping("/treeselect")
public ResponseResult treeSelect(){
//复用之前的selectMenuList方法。方法需要参数,参数可以用来进行条件查询,而这个方法不需要条件,所以直接new Menu()传入
//这样就可以获取所有的菜单了
List<AdminMenuVo> adminMenuVos = menuService.selectAllMenu(new Menu());
List<Menu> menus = BeanCopyUtils.copyBeanList(adminMenuVos, Menu.class);
List<MenuTreeVo> options = SystemConverter.buildMenuSelectTree(menus);
return ResponseResult.okResult(options);
}
}
4.2.3 测试
可用看到回显出了对应角色的权限信息
4.3 修改角色接口
4.3.1 接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
PUT | system/role | 是 |
请求体:
{
"id":"13",
"remark":"我是角色备注",
"roleKey":"wds",
"roleName":"测试新增角色",
"roleSort":0,
"status":"0",
"menuIds":[
"1",
"100",
"1001"
]
}
响应体
{
"code":200,
"msg":"操作成功"
}
4.3.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 EditRoleDto {
//角色ID
private Long id;
//角色名称
private String roleName;
//角色权限字符串
private String roleKey;
//显示顺序
private Integer roleSort;
//角色状态(0正常 1停用)
private String status;
//备注
private String remark;
//权限id
private List<Long> menuIds;
}
第二步:controller层新增EditRole接口
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.dto.EditRoleDto;
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);
}
@GetMapping("/{id}")
public ResponseResult getRoleInfo(@PathVariable("id") Long id){
return roleService.getRoleInfo(id);
}
@PutMapping
public ResponseResult editRole(@RequestBody EditRoleDto editRoleDto){
return roleService.editRole(editRoleDto);
}
}
第二步:service新增
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.dto.EditRoleDto;
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);
ResponseResult getRoleInfo(Long id);
ResponseResult editRole(EditRoleDto editRoleDto);
}
第三步: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.AdminAddRoleDto;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.dto.EditRoleDto;
import com.keke.domain.entity.Role;
import com.keke.domain.entity.RoleMenu;
import com.keke.domain.vo.AdminRoleVo;
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();
}
@Override
public ResponseResult getRoleInfo(Long id) {
Role role = getById(id);
AdminRoleVo adminRoleVo = BeanCopyUtils.copyBean(role, AdminRoleVo.class);
return ResponseResult.okResult(adminRoleVo);
}
@Override
public ResponseResult editRole(EditRoleDto editRoleDto) {
Role role = BeanCopyUtils.copyBean(editRoleDto, Role.class);
List<Long> menuIds = editRoleDto.getMenuIds();
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());
roleMenuService.saveBatch(roleMenuList);
return ResponseResult.okResult();
}
}
4.3.3 测试
可用新增角色,并且赋予其相应的菜单权限
5. 删除角色
删除固定的某个角色(逻辑删除)
5.1 接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
DELETE | system/role/{id} | 是 |
请求参数PathVariable格式:
id:要删除的角色id
响应格式:
{
"code":200,
"msg":"操作成功"
}
5.2 代码实现
controller层新增接口,逻辑较少直接写
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.dto.EditRoleDto;
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);
}
@GetMapping("/{id}")
public ResponseResult getRoleInfo(@PathVariable("id") Long id){
return roleService.getRoleInfo(id);
}
@PutMapping
public ResponseResult editRole(@RequestBody EditRoleDto editRoleDto){
return roleService.editRole(editRoleDto);
}
@DeleteMapping("/{id}")
public ResponseResult deleteRoleById(@PathVariable("id") Long id){
roleService.removeById(id);
return ResponseResult.okResult();
}
}
5.3 测试
只是逻辑删除
十三、后台模块-用户列表
1. 查询用户
需要用户分页列表接口。
可以根据用户名模糊搜索。
可以进行手机号的搜索。
可以进行状态的查询。
1.1 接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
GET | system/user/list | 是 |
请求参数query格式:
pageNum: 页码
pageSize: 每页条数
userName:用户名
phonenumber:手机号
status:状态
响应格式:
{
"code":200,
"data":{
"rows":[
{
"avatar":"http://r7yxkqloa.bkt.clouddn.com/2022/03/05/75fd15587811443a9a9a771f24da458d.png",
"createTime":"2022-01-05 17:01:56",
"email":"23412332@qq.com",
"id":"1",
"nickName":"sg3334",
"phonenumber":"18888888888",
"sex":"1",
"status":"0",
"updateBy":"1",
"updateTime":"2022-03-13 21:36:22",
"userName":"sg"
}
],
"total":"1"
},
"msg":"操作成功"
}
1.2 代码实现
第一步:keke-admin的controller层新建UserController
package com.keke.controller;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.User;
import com.keke.service.UserService;
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/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/list")
public ResponseResult selectPageUser(User user, Integer pageNum, Integer pageSize){
return userService.selectPageUser(user,pageNum,pageSize);
}
}
第二步:service层
package com.keke.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.keke.domain.ResponseResult;
import com.keke.domain.dto.RegisterUserDto;
import com.keke.domain.dto.UserInfoDto;
import com.keke.domain.entity.User;
import org.springframework.stereotype.Service;
/**
* 用户表(User)表服务接口
*
* @author makejava
* @since 2023-10-13 09:08:38
*/
public interface UserService extends IService<User> {
ResponseResult userInfo();
ResponseResult updateUserInfo(UserInfoDto userInfoDto);
ResponseResult register(RegisterUserDto registerUserDto);
//后台接口分页查询用户
ResponseResult selectPageUser(User user, 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.RegisterUserDto;
import com.keke.domain.dto.UserInfoDto;
import com.keke.domain.entity.User;
import com.keke.domain.vo.PageVo;
import com.keke.domain.vo.UserInfoVo;
import com.keke.enums.AppHttpCodeEnum;
import com.keke.handler.exception.exception.SystemException;
import com.keke.mapper.UserMapper;
import com.keke.service.UserService;
import com.keke.utils.BeanCopyUtils;
import com.keke.utils.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
/**
* 用户表(User)表服务实现类
*
* @author makejava
* @since 2023-10-13 10:12:51
*/
@Service("userService")
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public ResponseResult userInfo() {
Long userId = SecurityUtils.getUserId();
User user = getById(userId);
UserInfoVo userInfoVo = BeanCopyUtils.copyBean(user, UserInfoVo.class);
return ResponseResult.okResult(userInfoVo);
}
@Override
public ResponseResult updateUserInfo(UserInfoDto userInfoDto) {
User user = BeanCopyUtils.copyBean(userInfoDto, User.class);
updateById(user);
return ResponseResult.okResult();
}
@Override
public ResponseResult register(RegisterUserDto registerUserDto) {
//对前端传过来的用户名进行非空判断,例如null、"",就抛出异常
if(!StringUtils.hasText(registerUserDto.getUserName())){
//SystemException是我们写的异常类、AppHttpCodeEnum是我们写的枚举类
throw new SystemException(AppHttpCodeEnum.USERNAME_NOT_NULL);
}
//密码
if(!StringUtils.hasText(registerUserDto.getPassword())){
throw new SystemException(AppHttpCodeEnum.PASSWORD_NOT_NULL);
}
//邮箱
if(!StringUtils.hasText(registerUserDto.getEmail())){
throw new SystemException(AppHttpCodeEnum.EMAIL_NOT_NULL);
}
//昵称
if(!StringUtils.hasText(registerUserDto.getNickName())){
throw new SystemException(AppHttpCodeEnum.NICKNAME_NOT_NULL);
}
//判断用户传给我们的用户名是否在数据库已经存在。userNameExist方法是下面定义的
if(userNameExist(registerUserDto.getUserName())){
//SystemException是我们写的异常类、AppHttpCodeEnum是我们写的枚举类
throw new SystemException(AppHttpCodeEnum.USERNAME_EXIST);
}
//判断用户传给我们的昵称是否在数据库已经存在。NickNameExist方法是下面定义的
if(nickNameExist(registerUserDto.getNickName())){
//SystemException是我们写的异常类、AppHttpCodeEnum是我们写的枚举类
throw new SystemException(AppHttpCodeEnum.NICKNAME_EXIST);
}
//判断用户传给我们的邮箱是否在数据库已经存在。NickNameExist方法是下面定义的
if(emailExist(registerUserDto.getEmail())){
//SystemException是我们写的异常类、AppHttpCodeEnum是我们写的枚举类
throw new SystemException(AppHttpCodeEnum.EMAIL_EXIST);
}
//经过上面的判断,可以确保用户传给我们的用户名和昵称是数据库不存在的,且相关字段都不为空。就可以存入数据库
//注意用户传给我们的密码是明文,对于密码我们要转成密文之后再存入数据库。注意加密要和解密用同一套算法
//keke-blog工程的securityConfig类里面有解密算法,当时我们写了一个passwordEncoder方法,并且注入到了spring容器
//解密
String encodePassword = passwordEncoder.encode(registerUserDto.getPassword());
//封装成user存数据库中
User user = BeanCopyUtils.copyBean(registerUserDto, User.class);
//设置密码为加密的密码
user.setPassword(encodePassword);
//存入数据库中
save(user);
//封装返回
return ResponseResult.okResult();
}
@Override
public ResponseResult selectPageUser(User user, Integer pageNum, Integer pageSize) {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//可以根据用户名模糊搜索
lambdaQueryWrapper.like(StringUtils.hasText(user.getUserName()),User::getUserName,user.getUserName());
//可以进行手机号的搜索
lambdaQueryWrapper.eq(StringUtils.hasText(user.getPhonenumber()),User::getPhonenumber,user.getPhonenumber());
//可以根据状态进行搜索
lambdaQueryWrapper.eq(StringUtils.hasText(user.getStatus()),User::getStatus,user.getStatus());
//mp分页器
Page<User> page = new Page<>(pageNum,pageSize);
page(page,lambdaQueryWrapper);
//封装vo
PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());
//响应返回
return ResponseResult.okResult(pageVo);
}
//'判断用户传给我们的用户名是否在数据库已经存在' 的方法
public boolean userNameExist(String userName){
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//查询数据库中有用户名相同的数据没有
lambdaQueryWrapper.eq(User::getUserName,userName);
//如果查出来有,就说明存在,返回true
int count = count(lambdaQueryWrapper);
return count>0;
}
//'判断用户传给我们的昵称是否在数据库已经存在' 的方法
public boolean nickNameExist(String nickName){
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//查询数据库中有用户名相同的数据没有
lambdaQueryWrapper.eq(User::getNickName,nickName);
//如果查出来有,就说明存在,返回true
int count = count(lambdaQueryWrapper);
return count>0;
}
//'判断用户传给我们的邮箱是否在数据库已经存在' 的方法
public boolean emailExist(String email){
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//查询数据库中有用户名相同的数据没有
lambdaQueryWrapper.eq(User::getEmail,email);
//如果查出来有,就说明存在,返回true
int count = count(lambdaQueryWrapper);
return count>0;
}
}
1.3 测试
2. 新增用户
需要新增用户功能。新增用户时可以直接关联角色。
注意:新增用户时注意密码加密存储。
用户名不能为空,否则提示:必需填写用户名
用户名必须之前未存在,否则提示:用户名已存在
手机号必须之前未存在,否则提示:手机号已存在
邮箱必须之前未存在,否则提示:邮箱已存在
2.1 查询角色列表接口
注意:查询的是所有状态正常的角色
2.1.1 接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
GET | /system/role/listAllRole | 是 |
响应格式:
{
"code":200,
"data":[
{
"createBy":"0",
"createTime":"2021-11-12 18:46:19",
"delFlag":"0",
"id":"1",
"remark":"超级管理员",
"roleKey":"admin",
"roleName":"超级管理员",
"roleSort":"1",
"status":"0",
"updateBy":"0"
},
{
"createBy":"0",
"createTime":"2021-11-12 18:46:19",
"delFlag":"0",
"id":"2",
"remark":"普通角色",
"roleKey":"common",
"roleName":"普通角色",
"roleSort":"2",
"status":"0",
"updateBy":"0",
"updateTime":"2022-01-02 06:32:58"
},
{
"createTime":"2022-01-06 22:07:40",
"delFlag":"0",
"id":"11",
"remark":"嘎嘎嘎",
"roleKey":"aggag",
"roleName":"嘎嘎嘎",
"roleSort":"5",
"status":"0",
"updateBy":"1",
"updateTime":"2022-09-12 10:00:25"
},
{
"createTime":"2022-01-16 14:49:30",
"delFlag":"0",
"id":"12",
"roleKey":"link",
"roleName":"友链审核员",
"roleSort":"1",
"status":"0",
"updateTime":"2022-01-16 16:05:09"
}
],
"msg":"操作成功"
}
2.1.2 代码实现
第一步:在keke-admin的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.dto.EditRoleDto;
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);
}
@GetMapping("/{id}")
public ResponseResult getRoleInfo(@PathVariable("id") Long id){
return roleService.getRoleInfo(id);
}
@PutMapping
public ResponseResult editRole(@RequestBody EditRoleDto editRoleDto){
return roleService.editRole(editRoleDto);
}
@DeleteMapping("/{id}")
public ResponseResult deleteRoleById(@PathVariable("id") Long id){
roleService.removeById(id);
return ResponseResult.okResult();
}
@GetMapping("/listAllRole")
public ResponseResult listAllRole(){
return roleService.listAllRole();
}
}
第二步:service层新增
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.dto.EditRoleDto;
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);
ResponseResult getRoleInfo(Long id);
ResponseResult editRole(EditRoleDto editRoleDto);
ResponseResult listAllRole();
}
第三步: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.constants.SystemConstants;
import com.keke.domain.ResponseResult;
import com.keke.domain.dto.AdminAddRoleDto;
import com.keke.domain.dto.ChangeRoleStatusDto;
import com.keke.domain.dto.EditRoleDto;
import com.keke.domain.entity.Role;
import com.keke.domain.entity.RoleMenu;
import com.keke.domain.vo.AdminRoleVo;
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();
}
@Override
public ResponseResult getRoleInfo(Long id) {
Role role = getById(id);
AdminRoleVo adminRoleVo = BeanCopyUtils.copyBean(role, AdminRoleVo.class);
return ResponseResult.okResult(adminRoleVo);
}
@Override
public ResponseResult editRole(EditRoleDto editRoleDto) {
Role role = BeanCopyUtils.copyBean(editRoleDto, Role.class);
List<Long> menuIds = editRoleDto.getMenuIds();
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());
roleMenuService.saveBatch(roleMenuList);
return ResponseResult.okResult();
}
@Override
public ResponseResult listAllRole() {
LambdaQueryWrapper<Role> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(Role::getStatus, SystemConstants.STATUS_NORMAL);
List<Role> roleList = list(lambdaQueryWrapper);
return ResponseResult.okResult(roleList);
}
}
2.1.3 测试
2.2 新增用户接口
2.2.1 接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
POST | system/user | 是 |
请求体:
{
"userName":"wqeree",
"nickName":"测试新增用户",
"password":"1234343",
"phonenumber":"18889778907",
"email":"233@sq.com",
"sex":"0",
"status":"0",
"roleIds":[
"2"
]
}
响应格式:
{
"code":200,
"msg":"操作成功"
}
2.2.2 代码实现
第一步:keke-framework的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;
/**
* 用户和角色关联表(UserRole)表实体类
*
* @author makejava
* @since 2023-10-23 21:17:14
*/
@SuppressWarnings("serial")
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("sys_user_role")
public class UserRole {
//用户ID
private Long userId;
//角色ID
private Long roleId;
}
第二步:keke-framework的service新增
package com.keke.service;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* 用户和角色关联表(UserRole)表服务接口
*
* @author makejava
* @since 2023-10-23 21:17:32
*/
public interface UserRoleService extends IService<UserRole> {
}
第三步:keke-framework的service/impl新增
package com.keke.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.keke.domain.entity.UserRole;
import com.keke.mapper.UserRoleMapper;
import com.keke.service.UserRoleService;
import org.springframework.stereotype.Service;
/**
* 用户和角色关联表(UserRole)表服务实现类
*
* @author makejava
* @since 2023-10-23 21:17:32
*/
@Service("userRoleService")
public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRole> implements UserRoleService {
}
第四步:keke-framework的mapper新增
package com.keke.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.keke.domain.entity.User;
import org.apache.ibatis.annotations.Mapper;
/**
* 用户表(User)表数据库访问层
*
* @author makejava
* @since 2023-10-11 20:26:26
*/
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
第五步:keke-framework的domain/dto新增
package com.keke.domain.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AdminAddUserDto {
//用户名
private String userName;
//昵称
private String nickName;
//密码
private String password;
//账号状态(0正常 1停用)
private String status;
//邮箱
private String email;
//手机号
private String phonenumber;
//用户性别(0男,1女,2未知)
private String sex;
//roleId
List<Long> roleIds;
}
第六步:keke-admin的controller层UserController新增
package com.keke.controller;
import com.keke.domain.ResponseResult;
import com.keke.domain.dto.AdminAddUserDto;
import com.keke.domain.entity.User;
import com.keke.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/system/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/list")
public ResponseResult selectPageUser(User user, Integer pageNum, Integer pageSize){
return userService.selectPageUser(user,pageNum,pageSize);
}
@PostMapping
public ResponseResult addUser(@RequestBody AdminAddUserDto adminAddUserDto){
return userService.addUser(adminAddUserDto);
}
}
第七步:service层UserService新增
package com.keke.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.keke.domain.ResponseResult;
import com.keke.domain.dto.AdminAddUserDto;
import com.keke.domain.dto.RegisterUserDto;
import com.keke.domain.dto.UserInfoDto;
import com.keke.domain.entity.User;
import org.springframework.stereotype.Service;
/**
* 用户表(User)表服务接口
*
* @author makejava
* @since 2023-10-13 09:08:38
*/
public interface UserService extends IService<User> {
ResponseResult userInfo();
ResponseResult updateUserInfo(UserInfoDto userInfoDto);
ResponseResult register(RegisterUserDto registerUserDto);
//后台接口分页查询用户
ResponseResult selectPageUser(User user, Integer pageNum, Integer pageSize);
ResponseResult addUser(AdminAddUserDto adminAddUserDto);
}
第八步: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.AdminAddUserDto;
import com.keke.domain.dto.RegisterUserDto;
import com.keke.domain.dto.UserInfoDto;
import com.keke.domain.entity.User;
import com.keke.domain.entity.UserRole;
import com.keke.domain.vo.PageVo;
import com.keke.domain.vo.UserInfoVo;
import com.keke.enums.AppHttpCodeEnum;
import com.keke.handler.exception.exception.SystemException;
import com.keke.mapper.UserMapper;
import com.keke.service.UserRoleService;
import com.keke.service.UserService;
import com.keke.utils.BeanCopyUtils;
import com.keke.utils.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 用户表(User)表服务实现类
*
* @author makejava
* @since 2023-10-13 10:12:51
*/
@Service("userService")
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private UserRoleService userRoleService;
@Override
public ResponseResult userInfo() {
Long userId = SecurityUtils.getUserId();
User user = getById(userId);
UserInfoVo userInfoVo = BeanCopyUtils.copyBean(user, UserInfoVo.class);
return ResponseResult.okResult(userInfoVo);
}
@Override
public ResponseResult updateUserInfo(UserInfoDto userInfoDto) {
User user = BeanCopyUtils.copyBean(userInfoDto, User.class);
updateById(user);
return ResponseResult.okResult();
}
@Override
public ResponseResult register(RegisterUserDto registerUserDto) {
//对前端传过来的用户名进行非空判断,例如null、"",就抛出异常
if(!StringUtils.hasText(registerUserDto.getUserName())){
//SystemException是我们写的异常类、AppHttpCodeEnum是我们写的枚举类
throw new SystemException(AppHttpCodeEnum.USERNAME_NOT_NULL);
}
//密码
if(!StringUtils.hasText(registerUserDto.getPassword())){
throw new SystemException(AppHttpCodeEnum.PASSWORD_NOT_NULL);
}
//邮箱
if(!StringUtils.hasText(registerUserDto.getEmail())){
throw new SystemException(AppHttpCodeEnum.EMAIL_NOT_NULL);
}
//昵称
if(!StringUtils.hasText(registerUserDto.getNickName())){
throw new SystemException(AppHttpCodeEnum.NICKNAME_NOT_NULL);
}
//判断用户传给我们的用户名是否在数据库已经存在。userNameExist方法是下面定义的
if(userNameExist(registerUserDto.getUserName())){
//SystemException是我们写的异常类、AppHttpCodeEnum是我们写的枚举类
throw new SystemException(AppHttpCodeEnum.USERNAME_EXIST);
}
//判断用户传给我们的昵称是否在数据库已经存在。NickNameExist方法是下面定义的
if(nickNameExist(registerUserDto.getNickName())){
//SystemException是我们写的异常类、AppHttpCodeEnum是我们写的枚举类
throw new SystemException(AppHttpCodeEnum.NICKNAME_EXIST);
}
//判断用户传给我们的邮箱是否在数据库已经存在。NickNameExist方法是下面定义的
if(emailExist(registerUserDto.getEmail())){
//SystemException是我们写的异常类、AppHttpCodeEnum是我们写的枚举类
throw new SystemException(AppHttpCodeEnum.EMAIL_EXIST);
}
//经过上面的判断,可以确保用户传给我们的用户名和昵称是数据库不存在的,且相关字段都不为空。就可以存入数据库
//注意用户传给我们的密码是明文,对于密码我们要转成密文之后再存入数据库。注意加密要和解密用同一套算法
//keke-blog工程的securityConfig类里面有解密算法,当时我们写了一个passwordEncoder方法,并且注入到了spring容器
//解密
String encodePassword = passwordEncoder.encode(registerUserDto.getPassword());
//封装成user存数据库中
User user = BeanCopyUtils.copyBean(registerUserDto, User.class);
//设置密码为加密的密码
user.setPassword(encodePassword);
//存入数据库中
save(user);
//封装返回
return ResponseResult.okResult();
}
@Override
public ResponseResult selectPageUser(User user, Integer pageNum, Integer pageSize) {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//可以根据用户名模糊搜索
lambdaQueryWrapper.like(StringUtils.hasText(user.getUserName()),User::getUserName,user.getUserName());
//可以进行手机号的搜索
lambdaQueryWrapper.eq(StringUtils.hasText(user.getPhonenumber()),User::getPhonenumber,user.getPhonenumber());
//可以根据状态进行搜索
lambdaQueryWrapper.eq(StringUtils.hasText(user.getStatus()),User::getStatus,user.getStatus());
//mp分页器
Page<User> page = new Page<>(pageNum,pageSize);
page(page,lambdaQueryWrapper);
//封装vo
PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());
//响应返回
return ResponseResult.okResult(pageVo);
}
@Override
public ResponseResult addUser(AdminAddUserDto adminAddUserDto) {
User user = BeanCopyUtils.copyBean(adminAddUserDto, User.class);
List<Long> roleIds = adminAddUserDto.getRoleIds();
List<UserRole> userRoleList = roleIds.stream()
.map(new Function<Long, UserRole>() {
@Override
public UserRole apply(Long roleId) {
UserRole userRole = new UserRole();
userRole.setUserId(user.getId());
userRole.setRoleId(roleId);
return userRole;
}
}).collect(Collectors.toList());
userRoleService.saveBatch(userRoleList);
return ResponseResult.okResult();
}
//'判断用户传给我们的用户名是否在数据库已经存在' 的方法
public boolean userNameExist(String userName){
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//查询数据库中有用户名相同的数据没有
lambdaQueryWrapper.eq(User::getUserName,userName);
//如果查出来有,就说明存在,返回true
int count = count(lambdaQueryWrapper);
return count>0;
}
//'判断用户传给我们的昵称是否在数据库已经存在' 的方法
public boolean nickNameExist(String nickName){
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//查询数据库中有用户名相同的数据没有
lambdaQueryWrapper.eq(User::getNickName,nickName);
//如果查出来有,就说明存在,返回true
int count = count(lambdaQueryWrapper);
return count>0;
}
//'判断用户传给我们的邮箱是否在数据库已经存在' 的方法
public boolean emailExist(String email){
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//查询数据库中有用户名相同的数据没有
lambdaQueryWrapper.eq(User::getEmail,email);
//如果查出来有,就说明存在,返回true
int count = count(lambdaQueryWrapper);
return count>0;
}
}
2.2.3 测试
十四、后台模块-分类列表
1. 查询分类
需要分页查询分类列表。
能根据分类名称进行模糊查询。
能根据状态进行查询。
1.1 接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
GET | content/category/list | 是 |
Query格式请求参数:
pageNum: 页码
pageSize: 每页条数
name:分类名
status: 状态
响应格式:
{
"code":200,
"data":{
"rows":[
{
"description":"wsd",
"id":"1",
"name":"java",
"status":"0"
},
{
"description":"wsd",
"id":"2",
"name":"PHP",
"status":"0"
}
],
"total":"2"
},
"msg":"操作成功"
}
1.2 代码实现
第一步:keke-admin controller层
package com.keke.controller;
import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Category;
import com.keke.domain.vo.ExcelCategoryVo;
import com.keke.enums.AppHttpCodeEnum;
import com.keke.service.CategoryService;
import com.keke.utils.BeanCopyUtils;
import com.keke.utils.WebUtils;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.util.List;
@RestController
@RequestMapping("/content/category")
@Api(tags = "后台标签相关接口")
public class CategoryController {
@Autowired
private CategoryService categoryService;
@GetMapping("/listAllCategory")
public ResponseResult listAllCategory(){
return categoryService.listAllCategory();
}
//权限控制,ps是PermissionService类的bean名称
@PreAuthorize("@ps.hasPermission('content:category:export')")
@GetMapping("/export")
public void export(HttpServletResponse response){
try {
//设置下载文件的请求头
WebUtils.setDownLoadHeader("分类.xlsx",response);
//获取需要导出的数据
List<Category> categoryList = categoryService.list();
List<ExcelCategoryVo> excelCategoryVos = BeanCopyUtils.copyBeanList(categoryList, ExcelCategoryVo.class);
//把数据写入Excel中
EasyExcel.write(response.getOutputStream(), ExcelCategoryVo.class).autoCloseStream(Boolean.FALSE).sheet("分类导出")
.doWrite(excelCategoryVos);
} catch (Exception e) {
//如果出现异常,就返回失败的json数据给前端
ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR);
//WebUtils是我们在keke-framework工程写的类,里面的renderString方法是将json字符串写入到请求体,然后返回给前端
WebUtils.renderString(response, JSON.toJSONString(result));
}
}
@GetMapping("/list")
public ResponseResult selectPageCategory(Category category,Integer pageNum,Integer pageSize){
return categoryService.selectPageCategory(category,pageNum,pageSize);
}
}
第二步:keke-framework service层
package com.keke.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Category;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 分类表(Category)表服务接口
*
* @author makejava
* @since 2023-10-10 20:42:22
*/
public interface CategoryService extends IService<Category> {
ResponseResult getCategoryList();
//后台接口,查询所有文章分类
ResponseResult listAllCategory();
ResponseResult selectPageCategory(Category category, Integer pageNum, Integer pageSize);
}
第三步:keke-framework impl层 实现selectPageCategory
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.constants.SystemConstants;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Article;
import com.keke.domain.entity.Category;
import com.keke.domain.vo.AdminCategoryVo;
import com.keke.domain.vo.CategoryVo;
import com.keke.domain.vo.PageVo;
import com.keke.mapper.CategoryMapper;
import com.keke.service.ArticleService;
import com.keke.service.CategoryService;
import com.keke.utils.BeanCopyUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 分类表(Category)表服务实现类
*
* @author makejava
* @since 2023-10-10 20:42:22
*/
@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {
@Autowired
private ArticleService articleService;
@Override
public ResponseResult getCategoryList() {
//查询文章表,状态已发布的文章,但是在CategoryService下,查询文章表,就要注入ArticleService
LambdaQueryWrapper<Article> articleWrapper = new LambdaQueryWrapper<>();
articleWrapper.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL);
List<Article> articleList = articleService.list(articleWrapper);
//获取文章的分类id,并去重
Set<Long> categoryIds = articleList.stream()
.map(article -> article.getCategoryId())
//toSet可以去除重复的id
.collect(Collectors.toSet());
//查询分类表
List<Category> categories = listByIds(categoryIds);
//分类表中只获取正常状态非禁用的分类,用stream流过滤
categories = categories.stream()
.filter(category -> SystemConstants.STATUS_NORMAL.equals(category.getStatus()))
.collect(Collectors.toList());
//封装Vo
List<CategoryVo> categoryVos = BeanCopyUtils.copyBeanList(categories, CategoryVo.class);
//封装到响应体中,因为有数据,所以要调用有参okResult(),把参数传进去
return ResponseResult.okResult(categoryVos);
}
@Override
public ResponseResult listAllCategory() {
LambdaQueryWrapper<Category> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(Category::getStatus,SystemConstants.STATUS_NORMAL);
List<Category> categoryList = list(lambdaQueryWrapper);
List<AdminCategoryVo> adminCategoryVos = BeanCopyUtils.copyBeanList(categoryList, AdminCategoryVo.class);
return ResponseResult.okResult(adminCategoryVos);
}
@Override
public ResponseResult selectPageCategory(Category category, Integer pageNum, Integer pageSize) {
LambdaQueryWrapper<Category> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//根据分类名进行模糊查询
lambdaQueryWrapper.like(StringUtils.hasText(category.getName()),Category::getName,category.getName());
lambdaQueryWrapper.eq(StringUtils.hasText(category.getStatus()),Category::getStatus,category.getStatus());
//构造分页器
Page<Category> page = new Page<>(pageNum,pageSize);
page(page,lambdaQueryWrapper);
//封装Vo
PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());
return ResponseResult.okResult(pageVo);
}
}
1.3 测试
2. 新增分类
需要新增分类功能
2.1 接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
POST | /content/category | 是 |
请求体:
{
"name":"威威",
"description":"是的",
"status":"0"
}
响应格式:
{
"code":200,
"msg":"操作成功"
}
2.2 代码实现
第一步:新增dto,接受前端传来的参数
package com.keke.domain.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AdminCategoryDto {
//分类名
private String name;
//描述
private String description;
//状态0:正常,1禁用
private String status;
}
第二步:entity层Category修改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 com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 分类表(Category)表实体类
*
* @author makejava
* @since 2023-10-10 20:42:21
*/
@SuppressWarnings("serial")
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("ke_category")
public class Category {
@TableId
private Long id;
//分类名
private String name;
//父分类id,如果没有父分类为-1
private Long pid;
//描述
private String description;
//状态0:正常,1禁用
private String status;
//创建者
@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;
//删除标志(0代表未删除,1代表已删除)
private Integer delFlag;
}
第三步: keke-admin的Controller层
package com.keke.controller;
import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;
import com.keke.domain.ResponseResult;
import com.keke.domain.dto.AdminCategoryDto;
import com.keke.domain.entity.Category;
import com.keke.domain.vo.ExcelCategoryVo;
import com.keke.enums.AppHttpCodeEnum;
import com.keke.service.CategoryService;
import com.keke.utils.BeanCopyUtils;
import com.keke.utils.WebUtils;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.util.List;
@RestController
@RequestMapping("/content/category")
@Api(tags = "后台标签相关接口")
public class CategoryController {
@Autowired
private CategoryService categoryService;
@GetMapping("/listAllCategory")
public ResponseResult listAllCategory(){
return categoryService.listAllCategory();
}
//权限控制,ps是PermissionService类的bean名称
@PreAuthorize("@ps.hasPermission('content:category:export')")
@GetMapping("/export")
public void export(HttpServletResponse response){
try {
//设置下载文件的请求头
WebUtils.setDownLoadHeader("分类.xlsx",response);
//获取需要导出的数据
List<Category> categoryList = categoryService.list();
List<ExcelCategoryVo> excelCategoryVos = BeanCopyUtils.copyBeanList(categoryList, ExcelCategoryVo.class);
//把数据写入Excel中
EasyExcel.write(response.getOutputStream(), ExcelCategoryVo.class).autoCloseStream(Boolean.FALSE).sheet("分类导出")
.doWrite(excelCategoryVos);
} catch (Exception e) {
//如果出现异常,就返回失败的json数据给前端
ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR);
//WebUtils是我们在keke-framework工程写的类,里面的renderString方法是将json字符串写入到请求体,然后返回给前端
WebUtils.renderString(response, JSON.toJSONString(result));
}
}
@GetMapping("/list")
public ResponseResult selectPageCategory(Category category,Integer pageNum,Integer pageSize){
return categoryService.selectPageCategory(category,pageNum,pageSize);
}
@PostMapping
public ResponseResult addCategory(@RequestBody AdminCategoryDto adminCategoryDto){
return categoryService.addCategory(adminCategoryDto);
}
}
第四步:keke-framework的service层
package com.keke.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.keke.domain.ResponseResult;
import com.keke.domain.dto.AdminCategoryDto;
import com.keke.domain.entity.Category;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 分类表(Category)表服务接口
*
* @author makejava
* @since 2023-10-10 20:42:22
*/
public interface CategoryService extends IService<Category> {
ResponseResult getCategoryList();
//后台接口,查询所有文章分类
ResponseResult listAllCategory();
ResponseResult selectPageCategory(Category category, Integer pageNum, Integer pageSize);
ResponseResult addCategory(AdminCategoryDto adminCategoryDto);
}
第五步:keke-framework的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.constants.SystemConstants;
import com.keke.domain.ResponseResult;
import com.keke.domain.dto.AdminCategoryDto;
import com.keke.domain.entity.Article;
import com.keke.domain.entity.Category;
import com.keke.domain.vo.AdminCategoryVo;
import com.keke.domain.vo.CategoryVo;
import com.keke.domain.vo.PageVo;
import com.keke.mapper.CategoryMapper;
import com.keke.service.ArticleService;
import com.keke.service.CategoryService;
import com.keke.utils.BeanCopyUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 分类表(Category)表服务实现类
*
* @author makejava
* @since 2023-10-10 20:42:22
*/
@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {
@Autowired
private ArticleService articleService;
@Override
public ResponseResult getCategoryList() {
//查询文章表,状态已发布的文章,但是在CategoryService下,查询文章表,就要注入ArticleService
LambdaQueryWrapper<Article> articleWrapper = new LambdaQueryWrapper<>();
articleWrapper.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL);
List<Article> articleList = articleService.list(articleWrapper);
//获取文章的分类id,并去重
Set<Long> categoryIds = articleList.stream()
.map(article -> article.getCategoryId())
//toSet可以去除重复的id
.collect(Collectors.toSet());
//查询分类表
List<Category> categories = listByIds(categoryIds);
//分类表中只获取正常状态非禁用的分类,用stream流过滤
categories = categories.stream()
.filter(category -> SystemConstants.STATUS_NORMAL.equals(category.getStatus()))
.collect(Collectors.toList());
//封装Vo
List<CategoryVo> categoryVos = BeanCopyUtils.copyBeanList(categories, CategoryVo.class);
//封装到响应体中,因为有数据,所以要调用有参okResult(),把参数传进去
return ResponseResult.okResult(categoryVos);
}
@Override
public ResponseResult listAllCategory() {
LambdaQueryWrapper<Category> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(Category::getStatus,SystemConstants.STATUS_NORMAL);
List<Category> categoryList = list(lambdaQueryWrapper);
List<AdminCategoryVo> adminCategoryVos = BeanCopyUtils.copyBeanList(categoryList, AdminCategoryVo.class);
return ResponseResult.okResult(adminCategoryVos);
}
@Override
public ResponseResult selectPageCategory(Category category, Integer pageNum, Integer pageSize) {
LambdaQueryWrapper<Category> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//根据分类名进行模糊查询
lambdaQueryWrapper.like(StringUtils.hasText(category.getName()),Category::getName,category.getName());
lambdaQueryWrapper.eq(StringUtils.hasText(category.getStatus()),Category::getStatus,category.getStatus());
//构造分页器
Page<Category> page = new Page<>(pageNum,pageSize);
page(page,lambdaQueryWrapper);
//封装Vo
PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());
return ResponseResult.okResult(pageVo);
}
@Override
public ResponseResult addCategory(AdminCategoryDto adminCategoryDto) {
Category category = BeanCopyUtils.copyBean(adminCategoryDto, Category.class);
save(category);
return ResponseResult.okResult();
}
}
2.3 测试
3. 修改分类
需要提供修改分类的功能
分析:点击修改按钮,需要回显出分类的相关信息
点击确定按钮,修改成功
所以这里需要实现两个接口
3.1 回显分类信息接口
3.1.1 接口分析
根据id查询分类
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
Get | content/category/{id} | 是 |
3.1.2 代码实现
逻辑较少,直接在Controller层写逻辑
package com.keke.controller;
import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;
import com.keke.domain.ResponseResult;
import com.keke.domain.dto.AdminCategoryDto;
import com.keke.domain.entity.Category;
import com.keke.domain.vo.ExcelCategoryVo;
import com.keke.enums.AppHttpCodeEnum;
import com.keke.service.CategoryService;
import com.keke.utils.BeanCopyUtils;
import com.keke.utils.WebUtils;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.util.List;
@RestController
@RequestMapping("/content/category")
@Api(tags = "后台标签相关接口")
public class CategoryController {
@Autowired
private CategoryService categoryService;
@GetMapping("/listAllCategory")
public ResponseResult listAllCategory(){
return categoryService.listAllCategory();
}
//权限控制,ps是PermissionService类的bean名称
@PreAuthorize("@ps.hasPermission('content:category:export')")
@GetMapping("/export")
public void export(HttpServletResponse response){
try {
//设置下载文件的请求头
WebUtils.setDownLoadHeader("分类.xlsx",response);
//获取需要导出的数据
List<Category> categoryList = categoryService.list();
List<ExcelCategoryVo> excelCategoryVos = BeanCopyUtils.copyBeanList(categoryList, ExcelCategoryVo.class);
//把数据写入Excel中
EasyExcel.write(response.getOutputStream(), ExcelCategoryVo.class).autoCloseStream(Boolean.FALSE).sheet("分类导出")
.doWrite(excelCategoryVos);
} catch (Exception e) {
//如果出现异常,就返回失败的json数据给前端
ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR);
//WebUtils是我们在keke-framework工程写的类,里面的renderString方法是将json字符串写入到请求体,然后返回给前端
WebUtils.renderString(response, JSON.toJSONString(result));
}
}
@GetMapping("/list")
public ResponseResult selectPageCategory(Category category,Integer pageNum,Integer pageSize){
return categoryService.selectPageCategory(category,pageNum,pageSize);
}
@PostMapping
public ResponseResult addCategory(@RequestBody AdminCategoryDto adminCategoryDto){
return categoryService.addCategory(adminCategoryDto);
}
@GetMapping("/{id}")
public ResponseResult getInfoById(@PathVariable("id") Long id){
Category category = categoryService.getById(id);
return ResponseResult.okResult(category);
}
}
3.1.3 测试
成功回显出分类信息
3.2 修改分类接口
3.2.1 接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
PUT | /content/category | 是 |
请求体:
{
"description":"是的",
"id":"3",
"name":"威威2",
"status":"0"
}
响应格式:
{
"code":200,
"msg":"操作成功"
}
3.2.2 代码实现
逻辑较少,直接在controller层实现
@PutMapping
public ResponseResult editCategory(@RequestBody Category category){
categoryService.updateById(category);
return ResponseResult.okResult();
}
3.2.3 测试
4. 删除分类
删除某个分类(逻辑删除)
4.1 接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
DELETE | /content/category/{id} | 是 |
请求参数PathVariable格式:
id:要删除的分类id
响应格式:
{
"code":200,
"msg":"操作成功"
}
4.2 代码实现
逻辑较少,直接在controller层实现
@DeleteMapping("/{id}")
public ResponseResult deleteCategory(@PathVariable("/id") Long id){
categoryService.removeById(id);
return ResponseResult.okResult();
}
4.3 测试
十五、后台模块-友链列表
1. 查询友链
需要分页查询友链列表。
能根据友链名称进行模糊查询。
能根据状态进行查询。
1.1 接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
GET | /content/link/list | 是 |
Query格式请求参数:
pageNum: 页码
pageSize: 每页条数
name:友链名
status:状态
响应格式:
{
"code":200,
"data":{
"rows":[
{
"address":"https://www.baidu.com",
"description":"sda",
"id":"1",
"logo":"图片URL",
"name":"sda",
"status":"0"
}
],
"total":"1"
},
"msg":"操作成功"
}
1.2 代码实现
第一步:keke-admin的controller层新建LinkController
package com.keke.controller;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Link;
import com.keke.service.LinkService;
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/link")
public class LinkController {
@Autowired
private LinkService linkService;
@GetMapping("/list")
public ResponseResult selectPageLink(Link link, Integer pageNum, Integer pageSize){
return linkService.selectPageLink(link,pageNum,pageSize);
}
}
第二步:keke-framework的service层新增方法
package com.keke.service;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Link;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* 友链(Link)表服务接口
*
* @author makejava
* @since 2023-10-11 15:46:23
*/
public interface LinkService extends IService<Link> {
ResponseResult getAllLink();
ResponseResult selectPageLink(Link link, Integer pageNum, Integer pageSize);
}
第三步:keke-framework的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.constants.SystemConstants;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Link;
import com.keke.domain.vo.LinkVo;
import com.keke.domain.vo.PageVo;
import com.keke.mapper.LinkMapper;
import com.keke.service.LinkService;
import com.keke.utils.BeanCopyUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.List;
/**
* 友链(Link)表服务实现类
*
* @author makejava
* @since 2023-10-11 16:53:47
*/
@Service("linkService")
public class LinkServiceImpl extends ServiceImpl<LinkMapper, Link> implements LinkService {
@Override
public ResponseResult getAllLink() {
//查询所有审核通过的
LambdaQueryWrapper<Link> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(Link::getStatus, SystemConstants.Link_STATUS_NORMAL);
List<Link> links = list(lambdaQueryWrapper);
//转换Vo
List<LinkVo> linkVos = BeanCopyUtils.copyBeanList(links, LinkVo.class);
//封装响应体
return ResponseResult.okResult(linkVos);
}
@Override
public ResponseResult selectPageLink(Link link, Integer pageNum, Integer pageSize) {
LambdaQueryWrapper<Link> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//根据友链名进行模糊查询
lambdaQueryWrapper.like(StringUtils.hasText(link.getName()),Link::getName,link.getName());
//根据状态进行查询
lambdaQueryWrapper.eq(StringUtils.hasText(link.getStatus()),Link::getStatus,link.getStatus());
//构造分页器
Page<Link> page = new Page<>(pageNum,pageSize);
page(page,lambdaQueryWrapper);
//封装Vo
PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());
//封装响应体返回
return ResponseResult.okResult(pageVo);
}
}
1.3 测试
2. 新增友链
需要新增友链功能
2.1 接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
POST | /content/link | 是 |
请求体:
{
"name":"sda",
"description":"weqw",
"address":"wewe",
"logo":"weqe",
"status":"2"
}
响应格式:
{
"code":200,
"msg":"操作成功"
}
2.2 代码实现
第一步:修改entity层Link实体类,修改四字段为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;
/**
* 友链(Link)表实体类
*
* @author makejava
* @since 2023-10-11 15:45:54
*/
@SuppressWarnings("serial")
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("ke_link")
public class Link {
private Long id;
private String name;
private String logo;
private String description;
//网站地址
private String address;
//审核状态 (0代表审核通过,1代表审核未通过,2代表未审核)
private String status;
//创建者
@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;
//删除标志(0代表未删除,1代表已删除)
private Integer delFlag;
}
第二步:keke-admin的controller层新增添加友链的接口
package com.keke.controller;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Link;
import com.keke.service.LinkService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/content/link")
public class LinkController {
@Autowired
private LinkService linkService;
//分页查询友链
@GetMapping("/list")
public ResponseResult selectPageLink(Link link, Integer pageNum, Integer pageSize){
return linkService.selectPageLink(link,pageNum,pageSize);
}
//新增友链
@PostMapping
public ResponseResult addLink(@RequestBody Link link){
linkService.save(link);
return ResponseResult.okResult();
}
}
2.3 测试
3. 修改友链
需要提供修改友链的功能
3.1 回显友链信息接口
3.1.1 接口分析
根据id查询友链
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
Get | content/link/{id} | 是 |
请求PathVariable格式参数:
id: 友链id
响应格式:
{
"code":200,
"data":{
"address":"wewe",
"description":"weqw",
"id":"4",
"logo":"weqe",
"name":"sda",
"status":"2"
},
"msg":"操作成功"
}
3.1.2 代码实现
逻辑较少,直接在controller层写
//回显友链信息
@GetMapping("/{id}")
public ResponseResult getInfoById(@PathVariable("id") Long id){
Link link = linkService.getById(id);
return ResponseResult.okResult(link);
}
3.1.3 测试
3.2 修改友链接口
3.2.1 接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
PUT | /content/link | 是 |
请求头:
{
"address":"https://www.qq.com",
"description":"dada2",
"id":"2",
"logo":"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fn1.itc.cn%2Fimg8%2Fwb%2Frecom%2F2016%2F05%2F10%2F146286696706220328.PNG&refer=http%3A%2F%2Fn1.itc.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1646205529&t=f942665181eb9b0685db7a6f59d59975",
"name":"sda",
"status":"0"
}
响应体:
{
"code":200,
"msg":"操作成功"
}
3.2.2 代码实现
//修改友链
@PutMapping
public ResponseResult editLink(@RequestBody Link link){
linkService.updateById(link);
return ResponseResult.okResult();
}
3.2.3 测试
我们把刚才新增的友链LOG修改成猴子图标
4. 删除友链
删除某个友链(逻辑删除)
4.1 接口分析
请求方式 | 请求路径 | 是否需求token头 |
---|---|---|
DELETE | /content/link/{id} | 是 |
请求参数PathVariable格式:
id:要删除的友链id
响应格式:
{
"code":200,
"msg":"操作成功"
}
4.2 代码实现
package com.keke.controller;
import com.keke.domain.ResponseResult;
import com.keke.domain.entity.Link;
import com.keke.service.LinkService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/content/link")
public class LinkController {
@Autowired
private LinkService linkService;
//分页查询友链
@GetMapping("/list")
public ResponseResult selectPageLink(Link link, Integer pageNum, Integer pageSize){
return linkService.selectPageLink(link,pageNum,pageSize);
}
//新增友链
@PostMapping
public ResponseResult addLink(@RequestBody Link link){
linkService.save(link);
return ResponseResult.okResult();
}
//回显友链信息
@GetMapping("/{id}")
public ResponseResult getInfoById(@PathVariable("id") Long id){
Link link = linkService.getById(id);
return ResponseResult.okResult(link);
}
//修改友链
@PutMapping
public ResponseResult editLink(@RequestBody Link link){
linkService.updateById(link);
return ResponseResult.okResult();
}
//根据id删除友链
@DeleteMapping("/{id}")
public ResponseResult deleteLink(@PathVariable("id") Long id){
linkService.removeById(id);
return ResponseResult.okResult();
}
}
4.3 测试
至此,KekeBlog的后台模块开发全部结束