• KekeBlog项目实战后台模块(二)(已完结)


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

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

    1. 查询菜单

    1.1 接口分析

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

    请求方式

    请求路径

    是否需求token头

    GET

    system/menu/list

    请求参数是query格式的: 

    1. {
    2. status : 状态
    3. menuName: 菜单名
    4. }

    响应格式:

    1. {
    2. "code":200,
    3. "data":[
    4. {
    5. "component":"组件路径",
    6. "icon":"build",
    7. "id":"2023",
    8. "isFrame":1,
    9. "menuName":"菜单名称",
    10. "menuType":"C",
    11. "orderNum":0,
    12. "parentId":"0",
    13. "path":"write",
    14. "perms":"权限字符串",
    15. "remark":"备注信息",
    16. "status":"0",
    17. "visible":"0"
    18. },
    19. {
    20. "icon":"system",
    21. "id":"1",
    22. "isFrame":1,
    23. "menuName":"菜单名称",
    24. "menuType":"M",
    25. "orderNum":1,
    26. "parentId":"0",
    27. "path":"system",
    28. "perms":"权限字符串",
    29. "remark":"备注信息",
    30. "status":"0",
    31. "visible":"0"
    32. }
    33. ],
    34. "msg":"操作成功"
    35. }

    2.2 代码实现

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

    1. package com.keke.domain.vo;
    2. import lombok.AllArgsConstructor;
    3. import lombok.Data;
    4. import lombok.NoArgsConstructor;
    5. @Data
    6. @NoArgsConstructor
    7. @AllArgsConstructor
    8. public class AdminMenuVo {
    9. //菜单ID
    10. private Long id;
    11. //菜单名称
    12. private String menuName;
    13. //父菜单ID
    14. private Long parentId;
    15. //显示顺序
    16. private Integer orderNum;
    17. //路由地址
    18. private String path;
    19. //组件路径
    20. private String component;
    21. //是否为外链(0是 1否)
    22. private Integer isFrame;
    23. //菜单类型(M目录 C菜单 F按钮)
    24. private String menuType;
    25. //菜单状态(0显示 1隐藏)
    26. private String visible;
    27. //菜单状态(0正常 1停用)
    28. private String status;
    29. //权限标识
    30. private String perms;
    31. //菜单图标
    32. private String icon;
    33. //备注
    34. private String remark;
    35. }

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

    1. package com.keke.controller;
    2. import com.keke.domain.ResponseResult;
    3. import com.keke.domain.entity.Menu;
    4. import com.keke.service.MenuService;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.web.bind.annotation.GetMapping;
    7. import org.springframework.web.bind.annotation.RequestMapping;
    8. import org.springframework.web.bind.annotation.RestController;
    9. @RestController
    10. @RequestMapping("/system/menu")
    11. public class MenuController {
    12. @Autowired
    13. private MenuService menuService;
    14. //查询菜单列表
    15. @GetMapping("/list")
    16. public ResponseResult selectAllMenu(Menu menu){
    17. List adminMenuVos = menuService.selectAllMenu(menu);
    18. return ResponseResult.okResult(adminMenuVos);
    19. }
    20. }

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

    1. package com.keke.service;
    2. import com.baomidou.mybatisplus.extension.service.IService;
    3. import com.keke.domain.ResponseResult;
    4. import com.keke.domain.entity.Menu;
    5. import java.util.List;
    6. /**
    7. * 菜单权限表(Menu)表服务接口
    8. *
    9. * @author makejava
    10. * @since 2023-10-18 20:55:48
    11. */
    12. public interface MenuService extends IService {
    13. //查询用户权限信息
    14. List selectPermsByUserId(Long userId);
    15. //查询用户的路由信息,也就是权限菜单
    16. List selectRouterMenuTreeByUserId(Long userId);
    17. List selectAllMenu(Menu menu);
    18. }

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

    1. package com.keke.service.impl;
    2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    3. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    4. import com.keke.constants.SystemConstants;
    5. import com.keke.domain.ResponseResult;
    6. import com.keke.domain.entity.Menu;
    7. import com.keke.domain.vo.AdminMenuVo;
    8. import com.keke.mapper.MenuMapper;
    9. import com.keke.service.MenuService;
    10. import com.keke.utils.BeanCopyUtils;
    11. import com.keke.utils.SecurityUtils;
    12. import org.springframework.stereotype.Service;
    13. import org.springframework.util.StringUtils;
    14. import java.util.List;
    15. import java.util.function.Function;
    16. import java.util.stream.Collectors;
    17. /**
    18. * 菜单权限表(Menu)表服务实现类
    19. *
    20. * @author makejava
    21. * @since 2023-10-18 20:55:48
    22. */
    23. @Service("menuService")
    24. public class MenuServiceImpl extends ServiceImpl implements MenuService {
    25. //根据用户id查询权限关键字
    26. @Override
    27. public List selectPermsByUserId(Long userId) {
    28. //如果用户id为1代表管理员,roles 中只需要有admin,
    29. // permissions中需要有所有菜单类型为C(菜单)或者F(按钮)的,状态为正常的,未被删除的权限
    30. if(SecurityUtils.isAdmin()) {
    31. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    32. lambdaQueryWrapper.in(Menu::getMenuType, SystemConstants.MENU, SystemConstants.BUTTON);
    33. lambdaQueryWrapper.eq(Menu::getStatus, SystemConstants.STATUS_NORMAL);
    34. //由于我们的逻辑删除字段已经配置了,所以无需封装lambdaQueryWrapper
    35. List menuList = list(lambdaQueryWrapper);
    36. //我们需要的是String类型的集合,这里我们要进行数据的处理,这里采用流的方式
    37. List permissions = menuList.stream()
    38. .map(new Function() {
    39. @Override
    40. public String apply(Menu menu) {
    41. String perms = menu.getPerms();
    42. return perms;
    43. }
    44. })
    45. .collect(Collectors.toList());
    46. return permissions;
    47. }
    48. //否则返回这个用户所具有的权限
    49. //这里我们需要进行连表查询,因为我们的用户先和角色关联,然后角色才跟权限关联
    50. MenuMapper menuMapper = getBaseMapper();
    51. //我们期望menuMapper中有一个方法可以直接帮我们去实现这个复杂的逻辑,这里直接返回
    52. return menuMapper.selectPermsByUserId(userId);
    53. }
    54. @Override
    55. public List selectRouterMenuTreeByUserId(Long userId) {
    56. MenuMapper menuMapper = getBaseMapper();
    57. List menus = null;
    58. //如果是管理员,返回所有
    59. if(SecurityUtils.isAdmin()){
    60. menus = menuMapper.selectAllRoutersMenu();
    61. }else {
    62. //如果不是管理员,返回对应用户的菜单
    63. menus = menuMapper.selectRoutersMenuTreeByUserId(userId);
    64. }
    65. //因为上面的查询都是从数据库进行查询,所以无法封装children,这里构建Tree
    66. List menuTree = buildMenuTree(menus,0L);
    67. return menuTree;
    68. }
    69. @Override
    70. public ResponseResult selectAllMenu(Menu menu) {
    71. //可以针对菜单名进行模糊查询。也可以针对菜单的状态进行查询
    72. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    73. lambdaQueryWrapper.like(StringUtils.hasText(menu.getMenuName()),Menu::getMenuName,menu.getMenuName());
    74. lambdaQueryWrapper.eq(StringUtils.hasText(menu.getStatus()),Menu::getStatus,menu.getStatus());
    75. //排序 parent_id和order_num
    76. lambdaQueryWrapper.orderByAsc(Menu::getParentId,Menu::getOrderNum);
    77. List menus = list(lambdaQueryWrapper);
    78. List adminMenuVos = BeanCopyUtils.copyBeanList(menus, AdminMenuVo.class);
    79. return adminMenuVos;
    80. }
    81. /**
    82. * 构建MenuTree
    83. * 思路先找第一层级的菜单,就是找到id于parentId的对应关系,然后把parentId设置为Id的children
    84. * @param menus
    85. * @return
    86. */
    87. private List buildMenuTree(List menus,Long parentId) {
    88. //转化流处理
    89. List menuTree = menus.stream()
    90. //过滤掉除一级菜单之外的菜单
    91. .filter(menu -> menu.getParentId().equals(parentId))
    92. //然后将获取其子菜单设置到children字段,并返回
    93. .map(m -> m.setChildren(gerChildren(m, menus)))
    94. .collect(Collectors.toList());
    95. return menuTree;
    96. }
    97. //获取当前菜单的子菜单
    98. private List gerChildren(Menu menu, List menus) {
    99. //流处理,遍历每一个流对象,筛选出流对象的parentId=menu的id,即过滤
    100. List children = menus.stream()
    101. .filter(m -> m.getParentId().equals(menu.getId()))
    102. //这里其实不必要写,这一步的逻辑是如果有三级,
    103. //可以把流对象中再过筛选出子菜单设置给对应的children并返回
    104. .map(m -> m.setChildren(gerChildren(m,menus)))
    105. .collect(Collectors.toList());
    106. return children;
    107. }
    108. }

    2.3 测试

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

    2. 新增菜单

    2.1 接口分析

    新增权限菜单

    请求方式

    请求路径

    是否需求token头

    POST

    system/menu

    请求体参数:

    Menu类对应的json格式

    响应格式:

    1. {
    2. "code":200,
    3. "msg":"操作成功"
    4. }

    2.2 代码实现

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

    1. package com.keke.domain.entity;
    2. import java.util.Date;
    3. import java.util.List;
    4. import com.baomidou.mybatisplus.annotation.FieldFill;
    5. import com.baomidou.mybatisplus.annotation.TableField;
    6. import lombok.AllArgsConstructor;
    7. import lombok.Data;
    8. import lombok.NoArgsConstructor;
    9. import com.baomidou.mybatisplus.annotation.TableName;
    10. import lombok.experimental.Accessors;
    11. /**
    12. * 菜单权限表(Menu)表实体类
    13. *
    14. * @author makejava
    15. * @since 2023-10-18 20:55:24
    16. */
    17. @SuppressWarnings("serial")
    18. @Data
    19. @NoArgsConstructor
    20. @AllArgsConstructor
    21. @Accessors(chain = true)
    22. @TableName("sys_menu")
    23. public class Menu {
    24. //菜单ID
    25. private Long id;
    26. //菜单名称
    27. private String menuName;
    28. //父菜单ID
    29. private Long parentId;
    30. //显示顺序
    31. private Integer orderNum;
    32. //路由地址
    33. private String path;
    34. //组件路径
    35. private String component;
    36. //是否为外链(0是 1否)
    37. private Integer isFrame;
    38. //菜单类型(M目录 C菜单 F按钮)
    39. private String menuType;
    40. //菜单状态(0显示 1隐藏)
    41. private String visible;
    42. //菜单状态(0正常 1停用)
    43. private String status;
    44. //权限标识
    45. private String perms;
    46. //菜单图标
    47. private String icon;
    48. //创建者
    49. @TableField(fill = FieldFill.INSERT)
    50. private Long createBy;
    51. //创建时间
    52. @TableField(fill = FieldFill.INSERT)
    53. private Date createTime;
    54. //更新者
    55. @TableField(fill = FieldFill.INSERT_UPDATE)
    56. private Long updateBy;
    57. //更新时间
    58. @TableField(fill = FieldFill.INSERT_UPDATE)
    59. private Date updateTime;
    60. //备注
    61. private String remark;
    62. private String delFlag;
    63. //由于数据库没有children字段,所以我们要添加@TableField(exist = false)注解
    64. // 让mybatis在查表时不查询这个字段
    65. @TableField(exist = false)
    66. private List children;
    67. }

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

    1. package com.keke.controller;
    2. import com.keke.domain.ResponseResult;
    3. import com.keke.domain.entity.Menu;
    4. import com.keke.service.MenuService;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.web.bind.annotation.*;
    7. @RestController
    8. @RequestMapping("/system/menu")
    9. public class MenuController {
    10. @Autowired
    11. private MenuService menuService;
    12. //查询菜单列表
    13. @GetMapping("/list")
    14. public ResponseResult selectAllMenu(Menu menu){
    15. return menuService.selectAllMenu(menu);
    16. }
    17. //新增菜单
    18. @PostMapping
    19. public ResponseResult add(@RequestBody Menu menu) {
    20. menuService.save(menu);
    21. return ResponseResult.okResult();
    22. }
    23. }

    2.3 测试

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

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

    3. 修改菜单

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

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

    3.1 菜单详情

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

    请求参数PathVariable格式:

    id: 菜单id

    响应格式:

    1. {
    2. "code":200,
    3. "data":{
    4. "icon":"table",
    5. "id":"2017",
    6. "menuName":"内容管理",
    7. "menuType":"M",
    8. "orderNum":"4",
    9. "parentId":"0",
    10. "path":"content",
    11. "remark":"",
    12. "status":"0",
    13. "visible":"0"
    14. },
    15. "msg":"操作成功"
    16. }
    3.1.2 代码实现

    controller层新增

    1. package com.keke.controller;
    2. import com.keke.domain.ResponseResult;
    3. import com.keke.domain.entity.Menu;
    4. import com.keke.service.MenuService;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.web.bind.annotation.*;
    7. @RestController
    8. @RequestMapping("/system/menu")
    9. public class MenuController {
    10. @Autowired
    11. private MenuService menuService;
    12. //查询菜单列表
    13. @GetMapping("/list")
    14. public ResponseResult selectAllMenu(Menu menu){
    15. return menuService.selectAllMenu(menu);
    16. }
    17. //新增菜单
    18. @PostMapping
    19. public ResponseResult add(@RequestBody Menu menu) {
    20. menuService.save(menu);
    21. return ResponseResult.okResult();
    22. }
    23. @GetMapping("/{menuId}")
    24. public ResponseResult getInfoById(@PathVariable("menuId") Long menuId){
    25. Menu menu = menuService.getById(menuId);
    26. return ResponseResult.okResult(menu);
    27. }
    28. }
    3.1.3 测试

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

    3.2 修改菜单

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

    请求体参数:

    Menu类对应的json格式

    响应格式:

    1. {
    2. "code":200,
    3. "msg":"操作成功"
    4. }

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

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

    第一步:Controller层新增

    1. package com.keke.controller;
    2. import com.keke.domain.ResponseResult;
    3. import com.keke.domain.entity.Menu;
    4. import com.keke.service.MenuService;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.web.bind.annotation.*;
    7. @RestController
    8. @RequestMapping("/system/menu")
    9. public class MenuController {
    10. @Autowired
    11. private MenuService menuService;
    12. //查询菜单列表
    13. @GetMapping("/list")
    14. public ResponseResult selectAllMenu(Menu menu){
    15. return menuService.selectAllMenu(menu);
    16. }
    17. //新增菜单
    18. @PostMapping
    19. public ResponseResult add(@RequestBody Menu menu) {
    20. menuService.save(menu);
    21. return ResponseResult.okResult();
    22. }
    23. @GetMapping("/{menuId}")
    24. public ResponseResult getInfoById(@PathVariable("menuId") Long menuId){
    25. Menu menu = menuService.getById(menuId);
    26. return ResponseResult.okResult(menu);
    27. }
    28. @PutMapping
    29. public ResponseResult editMenu(@RequestBody Menu menu){
    30. return menuService.editMenu(menu);
    31. }
    32. }

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

    1. package com.keke.domain.entity;
    2. import java.util.Date;
    3. import java.util.List;
    4. import com.baomidou.mybatisplus.annotation.FieldFill;
    5. import com.baomidou.mybatisplus.annotation.TableField;
    6. import lombok.AllArgsConstructor;
    7. import lombok.Data;
    8. import lombok.NoArgsConstructor;
    9. import com.baomidou.mybatisplus.annotation.TableName;
    10. import lombok.experimental.Accessors;
    11. /**
    12. * 菜单权限表(Menu)表实体类
    13. *
    14. * @author makejava
    15. * @since 2023-10-18 20:55:24
    16. */
    17. @SuppressWarnings("serial")
    18. @Data
    19. @NoArgsConstructor
    20. @AllArgsConstructor
    21. @Accessors(chain = true)
    22. @TableName("sys_menu")
    23. public class Menu {
    24. //菜单ID
    25. private Long id;
    26. //菜单名称
    27. private String menuName;
    28. //父菜单ID
    29. private Long parentId;
    30. //显示顺序
    31. private Integer orderNum;
    32. //路由地址
    33. private String path;
    34. //组件路径
    35. private String component;
    36. //是否为外链(0是 1否)
    37. private Integer isFrame;
    38. //菜单类型(M目录 C菜单 F按钮)
    39. private String menuType;
    40. //菜单状态(0显示 1隐藏)
    41. private String visible;
    42. //菜单状态(0正常 1停用)
    43. private String status;
    44. //权限标识
    45. private String perms;
    46. //菜单图标
    47. private String icon;
    48. //创建者
    49. @TableField(fill = FieldFill.INSERT)
    50. private Long createBy;
    51. //创建时间
    52. @TableField(fill = FieldFill.INSERT)
    53. private Date createTime;
    54. //更新者
    55. @TableField(fill = FieldFill.INSERT_UPDATE)
    56. private Long updateBy;
    57. //更新时间
    58. @TableField(fill = FieldFill.INSERT_UPDATE)
    59. private Date updateTime;
    60. //备注
    61. private String remark;
    62. private String delFlag;
    63. //由于数据库没有children字段,所以我们要添加@TableField(exist = false)注解
    64. // 让mybatis在查表时不查询这个字段
    65. @TableField(exist = false)
    66. private List children;
    67. }

    第三步:service层新增

    1. package com.keke.service;
    2. import com.baomidou.mybatisplus.extension.service.IService;
    3. import com.keke.domain.ResponseResult;
    4. import com.keke.domain.entity.Menu;
    5. import java.util.List;
    6. /**
    7. * 菜单权限表(Menu)表服务接口
    8. *
    9. * @author makejava
    10. * @since 2023-10-18 20:55:48
    11. */
    12. public interface MenuService extends IService {
    13. //查询用户权限信息
    14. List selectPermsByUserId(Long userId);
    15. //查询用户的路由信息,也就是权限菜单
    16. List selectRouterMenuTreeByUserId(Long userId);
    17. ResponseResult selectAllMenu(Menu menu);
    18. ResponseResult editMenu(Menu menu);
    19. }

    第四步:impl层新增

    1. package com.keke.service.impl;
    2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    3. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    4. import com.keke.constants.SystemConstants;
    5. import com.keke.domain.ResponseResult;
    6. import com.keke.domain.entity.Menu;
    7. import com.keke.domain.vo.AdminMenuVo;
    8. import com.keke.mapper.MenuMapper;
    9. import com.keke.service.MenuService;
    10. import com.keke.utils.BeanCopyUtils;
    11. import com.keke.utils.SecurityUtils;
    12. import org.springframework.beans.factory.annotation.Autowired;
    13. import org.springframework.stereotype.Service;
    14. import org.springframework.util.StringUtils;
    15. import java.util.List;
    16. import java.util.function.Function;
    17. import java.util.stream.Collectors;
    18. /**
    19. * 菜单权限表(Menu)表服务实现类
    20. *
    21. * @author makejava
    22. * @since 2023-10-18 20:55:48
    23. */
    24. @Service("menuService")
    25. public class MenuServiceImpl extends ServiceImpl implements MenuService {
    26. @Autowired
    27. private MenuService menuService;
    28. //根据用户id查询权限关键字
    29. @Override
    30. public List selectPermsByUserId(Long userId) {
    31. //如果用户id为1代表管理员,roles 中只需要有admin,
    32. // permissions中需要有所有菜单类型为C(菜单)或者F(按钮)的,状态为正常的,未被删除的权限
    33. if(SecurityUtils.isAdmin()) {
    34. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    35. lambdaQueryWrapper.in(Menu::getMenuType, SystemConstants.MENU, SystemConstants.BUTTON);
    36. lambdaQueryWrapper.eq(Menu::getStatus, SystemConstants.STATUS_NORMAL);
    37. //由于我们的逻辑删除字段已经配置了,所以无需封装lambdaQueryWrapper
    38. List menuList = list(lambdaQueryWrapper);
    39. //我们需要的是String类型的集合,这里我们要进行数据的处理,这里采用流的方式
    40. List permissions = menuList.stream()
    41. .map(new Function() {
    42. @Override
    43. public String apply(Menu menu) {
    44. String perms = menu.getPerms();
    45. return perms;
    46. }
    47. })
    48. .collect(Collectors.toList());
    49. return permissions;
    50. }
    51. //否则返回这个用户所具有的权限
    52. //这里我们需要进行连表查询,因为我们的用户先和角色关联,然后角色才跟权限关联
    53. MenuMapper menuMapper = getBaseMapper();
    54. //我们期望menuMapper中有一个方法可以直接帮我们去实现这个复杂的逻辑,这里直接返回
    55. return menuMapper.selectPermsByUserId(userId);
    56. }
    57. @Override
    58. public List selectRouterMenuTreeByUserId(Long userId) {
    59. MenuMapper menuMapper = getBaseMapper();
    60. List menus = null;
    61. //如果是管理员,返回所有
    62. if(SecurityUtils.isAdmin()){
    63. menus = menuMapper.selectAllRoutersMenu();
    64. }else {
    65. //如果不是管理员,返回对应用户的菜单
    66. menus = menuMapper.selectRoutersMenuTreeByUserId(userId);
    67. }
    68. //因为上面的查询都是从数据库进行查询,所以无法封装children,这里构建Tree
    69. List menuTree = buildMenuTree(menus,0L);
    70. return menuTree;
    71. }
    72. @Override
    73. public ResponseResult selectAllMenu(Menu menu) {
    74. //可以针对菜单名进行模糊查询。也可以针对菜单的状态进行查询
    75. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    76. lambdaQueryWrapper.like(StringUtils.hasText(menu.getMenuName()),Menu::getMenuName,menu.getMenuName());
    77. lambdaQueryWrapper.eq(StringUtils.hasText(menu.getStatus()),Menu::getStatus,menu.getStatus());
    78. //排序 parent_id和order_num
    79. lambdaQueryWrapper.orderByAsc(Menu::getParentId,Menu::getOrderNum);
    80. List menus = list(lambdaQueryWrapper);
    81. List adminMenuVos = BeanCopyUtils.copyBeanList(menus, AdminMenuVo.class);
    82. return ResponseResult.okResult(adminMenuVos);
    83. }
    84. @Override
    85. public ResponseResult editMenu(Menu menu) {
    86. if(menu.getParentId().equals(menu.getId())){
    87. return ResponseResult.okResult(500,"修改菜单" + "'" + menu.getMenuName() + "'" + "失败,上级菜单不能选择自己");
    88. }
    89. updateById(menu);
    90. return ResponseResult.okResult();
    91. }
    92. /**
    93. * 构建MenuTree
    94. * 思路先找第一层级的菜单,就是找到id于parentId的对应关系,然后把parentId设置为Id的children
    95. * @param menus
    96. * @return
    97. */
    98. private List buildMenuTree(List menus,Long parentId) {
    99. //转化流处理
    100. List menuTree = menus.stream()
    101. //过滤掉除一级菜单之外的菜单
    102. .filter(menu -> menu.getParentId().equals(parentId))
    103. //然后将获取其子菜单设置到children字段,并返回
    104. .map(m -> m.setChildren(gerChildren(m, menus)))
    105. .collect(Collectors.toList());
    106. return menuTree;
    107. }
    108. //获取当前菜单的子菜单
    109. private List gerChildren(Menu menu, List menus) {
    110. //流处理,遍历每一个流对象,筛选出流对象的parentId=menu的id,即过滤
    111. List children = menus.stream()
    112. .filter(m -> m.getParentId().equals(menu.getId()))
    113. //这里其实不必要写,这一步的逻辑是如果有三级,
    114. //可以把流对象中再过筛选出子菜单设置给对应的children并返回
    115. .map(m -> m.setChildren(gerChildren(m,menus)))
    116. .collect(Collectors.toList());
    117. return children;
    118. }
    119. }
    3.2.3 测试

    4. 删除菜单

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

    4.1 接口分析

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

    请求参数PathVariable参数:

    menuId:要删除菜单的id

    响应格式: 

    1. {
    2. "code":200,
    3. "msg":"操作成功"
    4. }

    如果有子菜单

    1. {
    2. "code":500,
    3. "msg":"存在子菜单不允许删除"
    4. }

     4.2 代码实现

    第一步:controller层新增

    1. package com.keke.controller;
    2. import com.keke.domain.ResponseResult;
    3. import com.keke.domain.entity.Menu;
    4. import com.keke.service.MenuService;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.web.bind.annotation.*;
    7. @RestController
    8. @RequestMapping("/system/menu")
    9. public class MenuController {
    10. @Autowired
    11. private MenuService menuService;
    12. //查询菜单列表
    13. @GetMapping("/list")
    14. public ResponseResult selectAllMenu(Menu menu){
    15. return menuService.selectAllMenu(menu);
    16. }
    17. //新增菜单
    18. @PostMapping
    19. public ResponseResult add(@RequestBody Menu menu) {
    20. menuService.save(menu);
    21. return ResponseResult.okResult();
    22. }
    23. @GetMapping("/{menuId}")
    24. public ResponseResult getInfoById(@PathVariable("menuId") Long menuId){
    25. Menu menu = menuService.getById(menuId);
    26. return ResponseResult.okResult(menu);
    27. }
    28. @PutMapping
    29. public ResponseResult editMenu(@RequestBody Menu menu){
    30. return menuService.editMenu(menu);
    31. }
    32. @DeleteMapping("/{menuId}")
    33. public ResponseResult deleteMenu(@PathVariable("menuId") Long menuId){
    34. return menuService.deleteMenu(menuId);
    35. }
    36. }

    第二步:service层新增

    1. package com.keke.service;
    2. import com.baomidou.mybatisplus.extension.service.IService;
    3. import com.keke.domain.ResponseResult;
    4. import com.keke.domain.entity.Menu;
    5. import java.util.List;
    6. /**
    7. * 菜单权限表(Menu)表服务接口
    8. *
    9. * @author makejava
    10. * @since 2023-10-18 20:55:48
    11. */
    12. public interface MenuService extends IService {
    13. //查询用户权限信息
    14. List selectPermsByUserId(Long userId);
    15. //查询用户的路由信息,也就是权限菜单
    16. List selectRouterMenuTreeByUserId(Long userId);
    17. ResponseResult selectAllMenu(Menu menu);
    18. ResponseResult editMenu(Menu menu);
    19. ResponseResult deleteMenu(Long menuId);
    20. }

    第三步:impl层新增

    1. package com.keke.service.impl;
    2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    3. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    4. import com.keke.constants.SystemConstants;
    5. import com.keke.domain.ResponseResult;
    6. import com.keke.domain.entity.Menu;
    7. import com.keke.domain.vo.AdminMenuVo;
    8. import com.keke.mapper.MenuMapper;
    9. import com.keke.service.MenuService;
    10. import com.keke.utils.BeanCopyUtils;
    11. import com.keke.utils.SecurityUtils;
    12. import org.springframework.beans.factory.annotation.Autowired;
    13. import org.springframework.stereotype.Service;
    14. import org.springframework.util.StringUtils;
    15. import java.util.List;
    16. import java.util.function.Function;
    17. import java.util.stream.Collectors;
    18. /**
    19. * 菜单权限表(Menu)表服务实现类
    20. *
    21. * @author makejava
    22. * @since 2023-10-18 20:55:48
    23. */
    24. @Service("menuService")
    25. public class MenuServiceImpl extends ServiceImpl implements MenuService {
    26. @Autowired
    27. private MenuService menuService;
    28. //根据用户id查询权限关键字
    29. @Override
    30. public List selectPermsByUserId(Long userId) {
    31. //如果用户id为1代表管理员,roles 中只需要有admin,
    32. // permissions中需要有所有菜单类型为C(菜单)或者F(按钮)的,状态为正常的,未被删除的权限
    33. if(SecurityUtils.isAdmin()) {
    34. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    35. lambdaQueryWrapper.in(Menu::getMenuType, SystemConstants.MENU, SystemConstants.BUTTON);
    36. lambdaQueryWrapper.eq(Menu::getStatus, SystemConstants.STATUS_NORMAL);
    37. //由于我们的逻辑删除字段已经配置了,所以无需封装lambdaQueryWrapper
    38. List menuList = list(lambdaQueryWrapper);
    39. //我们需要的是String类型的集合,这里我们要进行数据的处理,这里采用流的方式
    40. List permissions = menuList.stream()
    41. .map(new Function() {
    42. @Override
    43. public String apply(Menu menu) {
    44. String perms = menu.getPerms();
    45. return perms;
    46. }
    47. })
    48. .collect(Collectors.toList());
    49. return permissions;
    50. }
    51. //否则返回这个用户所具有的权限
    52. //这里我们需要进行连表查询,因为我们的用户先和角色关联,然后角色才跟权限关联
    53. MenuMapper menuMapper = getBaseMapper();
    54. //我们期望menuMapper中有一个方法可以直接帮我们去实现这个复杂的逻辑,这里直接返回
    55. return menuMapper.selectPermsByUserId(userId);
    56. }
    57. @Override
    58. public List selectRouterMenuTreeByUserId(Long userId) {
    59. MenuMapper menuMapper = getBaseMapper();
    60. List menus = null;
    61. //如果是管理员,返回所有
    62. if(SecurityUtils.isAdmin()){
    63. menus = menuMapper.selectAllRoutersMenu();
    64. }else {
    65. //如果不是管理员,返回对应用户的菜单
    66. menus = menuMapper.selectRoutersMenuTreeByUserId(userId);
    67. }
    68. //因为上面的查询都是从数据库进行查询,所以无法封装children,这里构建Tree
    69. List menuTree = buildMenuTree(menus,0L);
    70. return menuTree;
    71. }
    72. @Override
    73. public ResponseResult selectAllMenu(Menu menu) {
    74. //可以针对菜单名进行模糊查询。也可以针对菜单的状态进行查询
    75. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    76. lambdaQueryWrapper.like(StringUtils.hasText(menu.getMenuName()),Menu::getMenuName,menu.getMenuName());
    77. lambdaQueryWrapper.eq(StringUtils.hasText(menu.getStatus()),Menu::getStatus,menu.getStatus());
    78. //排序 parent_id和order_num
    79. lambdaQueryWrapper.orderByAsc(Menu::getParentId,Menu::getOrderNum);
    80. List menus = list(lambdaQueryWrapper);
    81. List adminMenuVos = BeanCopyUtils.copyBeanList(menus, AdminMenuVo.class);
    82. return ResponseResult.okResult(adminMenuVos);
    83. }
    84. @Override
    85. public ResponseResult editMenu(Menu menu) {
    86. if(menu.getParentId().equals(menu.getId())){
    87. return ResponseResult.okResult(500,"修改菜单" + "'" + menu.getMenuName() + "'" + "失败,上级菜单不能选择自己");
    88. }
    89. updateById(menu);
    90. return ResponseResult.okResult();
    91. }
    92. @Override
    93. public ResponseResult deleteMenu(Long menuId) {
    94. Menu menu = menuService.getById(menuId);
    95. //如果该菜单有子菜单,那么就提示不能删除,逻辑就是查询菜单表中是否有父菜单id是当前菜单id
    96. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    97. lambdaQueryWrapper.eq(Menu::getParentId,menuId);
    98. List menus = list(lambdaQueryWrapper);
    99. if(!menus.isEmpty()) {
    100. return ResponseResult.okResult(500, "存在子菜单不允许删除");
    101. }
    102. removeById(menuId);
    103. return ResponseResult.okResult();
    104. }
    105. /**
    106. * 构建MenuTree
    107. * 思路先找第一层级的菜单,就是找到id于parentId的对应关系,然后把parentId设置为Id的children
    108. * @param menus
    109. * @return
    110. */
    111. private List buildMenuTree(List menus,Long parentId) {
    112. //转化流处理
    113. List menuTree = menus.stream()
    114. //过滤掉除一级菜单之外的菜单
    115. .filter(menu -> menu.getParentId().equals(parentId))
    116. //然后将获取其子菜单设置到children字段,并返回
    117. .map(m -> m.setChildren(gerChildren(m, menus)))
    118. .collect(Collectors.toList());
    119. return menuTree;
    120. }
    121. //获取当前菜单的子菜单
    122. private List gerChildren(Menu menu, List menus) {
    123. //流处理,遍历每一个流对象,筛选出流对象的parentId=menu的id,即过滤
    124. List children = menus.stream()
    125. .filter(m -> m.getParentId().equals(menu.getId()))
    126. //这里其实不必要写,这一步的逻辑是如果有三级,
    127. //可以把流对象中再过筛选出子菜单设置给对应的children并返回
    128. .map(m -> m.setChildren(gerChildren(m,menus)))
    129. .collect(Collectors.toList());
    130. return children;
    131. }
    132. }

    4.3 测试

     

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

    1. 查询角色

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

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

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

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

    1.1 接口分析

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

    Query格式请求参数:

    1. pageNum: 页码
    2. pageSize: 每页条数
    3. roleName:角色名称
    4. status:状态

    响应格式:

    1. {
    2. "code":200,
    3. "data":{
    4. "rows":[
    5. {
    6. "id":"12",
    7. "roleKey":"link",
    8. "roleName":"友链审核员",
    9. "roleSort":"1",
    10. "status":"0"
    11. }
    12. ],
    13. "total":"1"
    14. },
    15. "msg":"操作成功"
    16. }

    1.2 代码实现

    第一步:新建RoleController新增

    1. package com.keke.controller;
    2. import com.keke.domain.ResponseResult;
    3. import com.keke.domain.dto.ChangeRoleStatusDto;
    4. import com.keke.domain.entity.Role;
    5. import com.keke.service.RoleService;
    6. import org.springframework.beans.factory.annotation.Autowired;
    7. import org.springframework.web.bind.annotation.*;
    8. @RestController
    9. @RequestMapping("/system/role")
    10. public class RoleController {
    11. @Autowired
    12. private RoleService roleService;
    13. @GetMapping("/list")
    14. public ResponseResult selectPageRole(Role role,Integer pageNum,Integer pageSize){
    15. return roleService.selectPageRole(role,pageNum,pageSize);
    16. }
    17. }

    第二步:service层新增

    1. package com.keke.service;
    2. import com.baomidou.mybatisplus.extension.service.IService;
    3. import com.keke.domain.ResponseResult;
    4. import com.keke.domain.dto.ChangeRoleStatusDto;
    5. import com.keke.domain.entity.Role;
    6. import java.util.List;
    7. /**
    8. * 角色信息表(Role)表服务接口
    9. *
    10. * @author makejava
    11. * @since 2023-10-18 21:04:06
    12. */
    13. public interface RoleService extends IService {
    14. List selectRoleKeyByUserId(Long userId);
    15. ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize);
    16. }

    第三步:impl层新增

    1. package com.keke.service.impl;
    2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    3. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    4. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    5. import com.keke.domain.ResponseResult;
    6. import com.keke.domain.dto.ChangeRoleStatusDto;
    7. import com.keke.domain.entity.Role;
    8. import com.keke.domain.vo.PageVo;
    9. import com.keke.mapper.RoleMapper;
    10. import com.keke.service.RoleService;
    11. import org.springframework.stereotype.Service;
    12. import org.springframework.util.StringUtils;
    13. import java.util.ArrayList;
    14. import java.util.List;
    15. /**
    16. * 角色信息表(Role)表服务实现类
    17. *
    18. * @author makejava
    19. * @since 2023-10-18 21:04:06
    20. */
    21. @Service("roleService")
    22. public class RoleServiceImpl extends ServiceImpl implements RoleService {
    23. //根据用户id查询角色信息
    24. @Override
    25. public List selectRoleKeyByUserId(Long userId) {
    26. //如果userId为1,那么角色权限字符串就只需要返回一个admin
    27. if(userId==1L){
    28. List roles = new ArrayList<>();
    29. roles.add("admin");
    30. return roles;
    31. }
    32. //如果用户id不为1,那么需要根据userId连表查询对应的roleId,然后再去角色表中去查询
    33. //对应的角色权限字符串
    34. //这里我们期望RoleMapper中封装一个方法去帮我们实现这个复杂的操作
    35. RoleMapper roleMapper = getBaseMapper();
    36. return roleMapper.selectRoleKeyByUserId(userId);
    37. }
    38. @Override
    39. public ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize) {
    40. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    41. lambdaQueryWrapper.like(StringUtils.hasText(role.getRoleName()),Role::getRoleName,role.getRoleName());
    42. lambdaQueryWrapper.eq(StringUtils.hasText(role.getStatus()),Role::getStatus,role.getStatus());
    43. lambdaQueryWrapper.orderByAsc(Role::getRoleSort);
    44. Page page = new Page<>(pageNum,pageSize);
    45. page(page,lambdaQueryWrapper);
    46. PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());
    47. return ResponseResult.okResult(pageVo);
    48. }
    49. }

    1.3 测试

    2. 改变角色状态

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

    2.1 接口分析

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

    请求体:

    1. {
    2. "roleId":"11",
    3. "status":"1"
    4. }

    响应格式:

    1. {
    2. "code":200,
    3. "msg":"操作成功"
    4. }

    2.2 代码实现

    第一步:controller层新增

    1. package com.keke.controller;
    2. import com.keke.domain.ResponseResult;
    3. import com.keke.domain.dto.ChangeRoleStatusDto;
    4. import com.keke.domain.entity.Role;
    5. import com.keke.service.RoleService;
    6. import org.springframework.beans.factory.annotation.Autowired;
    7. import org.springframework.web.bind.annotation.*;
    8. @RestController
    9. @RequestMapping("/system/role")
    10. public class RoleController {
    11. @Autowired
    12. private RoleService roleService;
    13. @GetMapping("/list")
    14. public ResponseResult selectPageRole(Role role,Integer pageNum,Integer pageSize){
    15. return roleService.selectPageRole(role,pageNum,pageSize);
    16. }
    17. @PutMapping("/changeStatus")
    18. public ResponseResult changeRoleStatus(@RequestBody ChangeRoleStatusDto changeRoleStatusDto){
    19. return roleService.changeRoleStatus(changeRoleStatusDto);
    20. }
    21. }

    第二步:domain/dto新增

    1. package com.keke.domain.dto;
    2. import lombok.AllArgsConstructor;
    3. import lombok.Data;
    4. import lombok.NoArgsConstructor;
    5. @Data
    6. @AllArgsConstructor
    7. @NoArgsConstructor
    8. public class ChangeRoleStatusDto {
    9. private Long roleId;
    10. //角色状态(0正常 1停用)
    11. private String status;
    12. }

    第三步:service层新增

    1. package com.keke.service;
    2. import com.baomidou.mybatisplus.extension.service.IService;
    3. import com.keke.domain.ResponseResult;
    4. import com.keke.domain.dto.ChangeRoleStatusDto;
    5. import com.keke.domain.entity.Role;
    6. import java.util.List;
    7. /**
    8. * 角色信息表(Role)表服务接口
    9. *
    10. * @author makejava
    11. * @since 2023-10-18 21:04:06
    12. */
    13. public interface RoleService extends IService {
    14. List selectRoleKeyByUserId(Long userId);
    15. ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize);
    16. ResponseResult changeRoleStatus(ChangeRoleStatusDto changeRoleStatusDto);
    17. }

    第四步:impl层新增

    1. package com.keke.service.impl;
    2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    3. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    4. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    5. import com.keke.domain.ResponseResult;
    6. import com.keke.domain.dto.ChangeRoleStatusDto;
    7. import com.keke.domain.entity.Role;
    8. import com.keke.domain.vo.PageVo;
    9. import com.keke.mapper.RoleMapper;
    10. import com.keke.service.RoleService;
    11. import org.springframework.stereotype.Service;
    12. import org.springframework.util.StringUtils;
    13. import java.util.ArrayList;
    14. import java.util.List;
    15. /**
    16. * 角色信息表(Role)表服务实现类
    17. *
    18. * @author makejava
    19. * @since 2023-10-18 21:04:06
    20. */
    21. @Service("roleService")
    22. public class RoleServiceImpl extends ServiceImpl implements RoleService {
    23. //根据用户id查询角色信息
    24. @Override
    25. public List selectRoleKeyByUserId(Long userId) {
    26. //如果userId为1,那么角色权限字符串就只需要返回一个admin
    27. if(userId==1L){
    28. List roles = new ArrayList<>();
    29. roles.add("admin");
    30. return roles;
    31. }
    32. //如果用户id不为1,那么需要根据userId连表查询对应的roleId,然后再去角色表中去查询
    33. //对应的角色权限字符串
    34. //这里我们期望RoleMapper中封装一个方法去帮我们实现这个复杂的操作
    35. RoleMapper roleMapper = getBaseMapper();
    36. return roleMapper.selectRoleKeyByUserId(userId);
    37. }
    38. @Override
    39. public ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize) {
    40. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    41. lambdaQueryWrapper.like(StringUtils.hasText(role.getRoleName()),Role::getRoleName,role.getRoleName());
    42. lambdaQueryWrapper.eq(StringUtils.hasText(role.getStatus()),Role::getStatus,role.getStatus());
    43. lambdaQueryWrapper.orderByAsc(Role::getRoleSort);
    44. Page page = new Page<>(pageNum,pageSize);
    45. page(page,lambdaQueryWrapper);
    46. PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());
    47. return ResponseResult.okResult(pageVo);
    48. }
    49. @Override
    50. public ResponseResult changeRoleStatus(ChangeRoleStatusDto changeRoleStatusDto) {
    51. Long roleId = changeRoleStatusDto.getRoleId();
    52. Role role = getById(roleId);
    53. role.setStatus(changeRoleStatusDto.getStatus());
    54. updateById(role);
    55. return ResponseResult.okResult();
    56. }
    57. }

    2.3 测试

    把该用户启用

    数据库中状态正常

    3. 新增角色

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

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

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

    3.1 获取菜单权限树接口

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

    无需请求参数

    响应格式:

    1. {
    2. "code":200,
    3. "data":[
    4. {
    5. "children":[],
    6. "id":"2023",
    7. "label":"写博文",
    8. "parentId":"0"
    9. },
    10. {
    11. "children":[
    12. {
    13. "children":[
    14. {
    15. "children":[],
    16. "id":"1001",
    17. "label":"用户查询",
    18. "parentId":"100"
    19. },
    20. {
    21. "children":[],
    22. "id":"1007",
    23. "label":"重置密码",
    24. "parentId":"100"
    25. }
    26. ],
    27. "id":"100",
    28. "label":"用户管理",
    29. "parentId":"1"
    30. },
    31. {
    32. "children":[
    33. {
    34. "children":[],
    35. "id":"2024",
    36. "label":"友链新增",
    37. "parentId":"2022"
    38. },
    39. {
    40. "children":[],
    41. "id":"2025",
    42. "label":"友链修改",
    43. "parentId":"2022"
    44. },
    45. {
    46. "children":[],
    47. "id":"2026",
    48. "label":"友链删除",
    49. "parentId":"2022"
    50. },
    51. {
    52. "children":[],
    53. "id":"2027",
    54. "label":"友链查询",
    55. "parentId":"2022"
    56. }
    57. ],
    58. "id":"2022",
    59. "label":"友链管理",
    60. "parentId":"2017"
    61. },
    62. {
    63. "children":[],
    64. "id":"2021",
    65. "label":"标签管理",
    66. "parentId":"2017"
    67. }
    68. ],
    69. "id":"2017",
    70. "label":"内容管理",
    71. "parentId":"0"
    72. }
    73. ],
    74. "msg":"操作成功"
    75. }
    3.1.2 代码实现

    第一步:keke-framework的domain/entity Role实体类修改如下,加上mp自填充,并且加上menuIds字段

    1. package com.keke.domain.entity;
    2. import java.util.Date;
    3. import java.io.Serializable;
    4. import java.util.List;
    5. import com.baomidou.mybatisplus.annotation.FieldFill;
    6. import com.baomidou.mybatisplus.annotation.TableField;
    7. import lombok.AllArgsConstructor;
    8. import lombok.Data;
    9. import lombok.NoArgsConstructor;
    10. import com.baomidou.mybatisplus.annotation.TableName;
    11. /**
    12. * 角色信息表(Role)表实体类
    13. *
    14. * @author makejava
    15. * @since 2023-10-18 21:03:52
    16. */
    17. @SuppressWarnings("serial")
    18. @Data
    19. @NoArgsConstructor
    20. @AllArgsConstructor
    21. @TableName("sys_role")
    22. public class Role {
    23. //角色ID
    24. private Long id;
    25. //角色名称
    26. private String roleName;
    27. //角色权限字符串
    28. private String roleKey;
    29. //显示顺序
    30. private Integer roleSort;
    31. //角色状态(0正常 1停用)
    32. private String status;
    33. //删除标志(0代表存在 1代表删除)
    34. private String delFlag;
    35. //创建者
    36. @TableField(fill = FieldFill.INSERT)
    37. private Long createBy;
    38. //创建时间
    39. @TableField(fill = FieldFill.INSERT)
    40. private Date createTime;
    41. //更新者
    42. @TableField(fill = FieldFill.INSERT_UPDATE)
    43. private Long updateBy;
    44. //更新时间
    45. @TableField(fill = FieldFill.INSERT_UPDATE)
    46. private Date updateTime;
    47. //备注
    48. private String remark;
    49. //角色关联的menuIds
    50. List menuIds;
    51. }

    第二步:vo层创建MenuTreeVo 

    1. package com.keke.domain.vo;
    2. import lombok.AllArgsConstructor;
    3. import lombok.Data;
    4. import lombok.NoArgsConstructor;
    5. import lombok.experimental.Accessors;
    6. import java.util.List;
    7. @Data
    8. @NoArgsConstructor
    9. @AllArgsConstructor
    10. @Accessors(chain = true)
    11. //新增角色-获取菜单下拉树列表
    12. public class MenuTreeVo {
    13. private static final long serialVersionUID = 1L;
    14. /** 节点ID */
    15. private Long id;
    16. /** 节点名称 */
    17. private String label;
    18. private Long parentId;
    19. /** 子节点 */
    20. private List children;
    21. }

    第三步:entity层新增RoleMenu实体类

    1. package com.keke.domain.entity;
    2. import java.io.Serializable;
    3. import lombok.AllArgsConstructor;
    4. import lombok.Data;
    5. import lombok.NoArgsConstructor;
    6. import com.baomidou.mybatisplus.annotation.TableName;
    7. /**
    8. * 角色和菜单关联表(RoleMenu)表实体类
    9. *
    10. * @author makejava
    11. * @since 2023-10-23 17:00:27
    12. */
    13. @SuppressWarnings("serial")
    14. @Data
    15. @NoArgsConstructor
    16. @AllArgsConstructor
    17. @TableName("sys_role_menu")
    18. public class RoleMenu {
    19. //角色ID
    20. private Long roleId;
    21. //菜单ID
    22. private Long menuId;
    23. }

    第四步:mapper层新增RoleMenuMapper

    1. package com.keke.mapper;
    2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    3. import com.keke.domain.entity.RoleMenu;
    4. /**
    5. * 角色和菜单关联表(RoleMenu)表数据库访问层
    6. *
    7. * @author makejava
    8. * @since 2023-10-23 17:01:09
    9. */
    10. public interface RoleMenuMapper extends BaseMapper {
    11. }

    第五步:service层新增RoleMenuService

    1. package com.keke.service;
    2. import com.baomidou.mybatisplus.extension.service.IService;
    3. import com.keke.domain.entity.RoleMenu;
    4. /**
    5. * 角色和菜单关联表(RoleMenu)表服务接口
    6. *
    7. * @author makejava
    8. * @since 2023-10-23 17:01:09
    9. */
    10. public interface RoleMenuService extends IService {
    11. }

    第六步:impl层新增RoleMenuServiceImpl

    1. package com.keke.service.impl;
    2. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    3. import com.keke.domain.entity.RoleMenu;
    4. import com.keke.mapper.RoleMenuMapper;
    5. import com.keke.service.RoleMenuService;
    6. import org.springframework.stereotype.Service;
    7. /**
    8. * 角色和菜单关联表(RoleMenu)表服务实现类
    9. *
    10. * @author makejava
    11. * @since 2023-10-23 17:01:09
    12. */
    13. @Service("roleMenuService")
    14. public class RoleMenuServiceImpl extends ServiceImpl implements RoleMenuService {
    15. }

     第七步:在keke-framework工程的utils目录新建SystemConverter类,写入如下,是一个获取菜单下拉树工具类

    1. package com.keke.utils;
    2. import com.keke.domain.entity.Menu;
    3. import com.keke.domain.vo.MenuTreeVo;
    4. import java.util.List;
    5. import java.util.Objects;
    6. import java.util.stream.Collectors;
    7. public class SystemConverter {
    8. private SystemConverter() {}
    9. public static List buildMenuSelectTree(List menus) {
    10. List MenuTreeVos = menus.stream()
    11. .map(m -> new MenuTreeVo(m.getId(), m.getMenuName(), m.getParentId(), null))
    12. .collect(Collectors.toList());
    13. List options = MenuTreeVos.stream()
    14. .filter(o -> o.getParentId().equals(0L))
    15. .map(o -> o.setChildren(getChildList(MenuTreeVos, o)))
    16. .collect(Collectors.toList());
    17. return options;
    18. }
    19. /**
    20. * 得到子节点列表
    21. */
    22. private static List getChildList(List list, MenuTreeVo option) {
    23. List options = list.stream()
    24. .filter(o -> Objects.equals(o.getParentId(), option.getId()))
    25. .map(o -> o.setChildren(getChildList(list, o)))
    26. .collect(Collectors.toList());
    27. return options;
    28. }
    29. }

    第八步:keke-admin的MenuController中写接口 

    1. package com.keke.controller;
    2. import com.keke.domain.ResponseResult;
    3. import com.keke.domain.entity.Menu;
    4. import com.keke.domain.vo.AdminMenuVo;
    5. import com.keke.domain.vo.MenuTreeVo;
    6. import com.keke.service.MenuService;
    7. import com.keke.utils.BeanCopyUtils;
    8. import com.keke.utils.SystemConverter;
    9. import org.springframework.beans.factory.annotation.Autowired;
    10. import org.springframework.web.bind.annotation.*;
    11. import java.util.List;
    12. @RestController
    13. @RequestMapping("/system/menu")
    14. public class MenuController {
    15. @Autowired
    16. private MenuService menuService;
    17. //查询菜单列表
    18. @GetMapping("/list")
    19. public ResponseResult selectAllMenu(Menu menu){
    20. List adminMenuVos = menuService.selectAllMenu(menu);
    21. return ResponseResult.okResult(adminMenuVos);
    22. }
    23. //新增菜单
    24. @PostMapping
    25. public ResponseResult add(@RequestBody Menu menu) {
    26. menuService.save(menu);
    27. return ResponseResult.okResult();
    28. }
    29. @GetMapping("/{menuId}")
    30. public ResponseResult getInfoById(@PathVariable("menuId") Long menuId){
    31. Menu menu = menuService.getById(menuId);
    32. return ResponseResult.okResult(menu);
    33. }
    34. @PutMapping
    35. public ResponseResult editMenu(@RequestBody Menu menu){
    36. return menuService.editMenu(menu);
    37. }
    38. @DeleteMapping("/{menuId}")
    39. public ResponseResult deleteMenu(@PathVariable("menuId") Long menuId){
    40. return menuService.deleteMenu(menuId);
    41. }
    42. @GetMapping("/treeselect")
    43. public ResponseResult treeSelect(){
    44. //复用之前的selectMenuList方法。方法需要参数,参数可以用来进行条件查询,而这个方法不需要条件,所以直接new Menu()传入
    45. //这样就可以获取所有的菜单了
    46. List adminMenuVos = menuService.selectAllMenu(new Menu());
    47. List menus = BeanCopyUtils.copyBeanList(adminMenuVos, Menu.class);
    48. List options = SystemConverter.buildMenuSelectTree(menus);
    49. return ResponseResult.okResult(options);
    50. }
    51. }
    3.1.3 测试

    3.2 新增角色接口

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

    请求体:

    1. {
    2. "roleName":"测试新增角色",
    3. "roleKey":"wds",
    4. "roleSort":0,
    5. "status":"0",
    6. "menuIds":[
    7. "1",
    8. "100"
    9. ],
    10. "remark":"我是角色备注"
    11. }

    响应格式:

    1. {
    2. "code":200,
    3. "msg":"操作成功"
    4. }
    3.2.2 代码实现

    第一步:domain/dto层新增

    1. package com.keke.domain.dto;
    2. import com.baomidou.mybatisplus.annotation.FieldFill;
    3. import com.baomidou.mybatisplus.annotation.TableField;
    4. import lombok.AllArgsConstructor;
    5. import lombok.Data;
    6. import lombok.NoArgsConstructor;
    7. import java.util.Date;
    8. import java.util.List;
    9. @Data
    10. @AllArgsConstructor
    11. @NoArgsConstructor
    12. public class AdminAddRoleDto {
    13. //角色名称
    14. private String roleName;
    15. //角色权限字符串
    16. private String roleKey;
    17. //显示顺序
    18. private Integer roleSort;
    19. //角色状态(0正常 1停用)
    20. private String status;
    21. //备注
    22. private String remark;
    23. //关联的menuId
    24. List menuIds;
    25. }

    第二步:domain/entity层新增

    1. package com.keke.domain.entity;
    2. import java.io.Serializable;
    3. import lombok.AllArgsConstructor;
    4. import lombok.Data;
    5. import lombok.NoArgsConstructor;
    6. import com.baomidou.mybatisplus.annotation.TableName;
    7. /**
    8. * 角色和菜单关联表(RoleMenu)表实体类
    9. *
    10. * @author makejava
    11. * @since 2023-10-23 17:00:27
    12. */
    13. @SuppressWarnings("serial")
    14. @Data
    15. @NoArgsConstructor
    16. @AllArgsConstructor
    17. @TableName("sys_role_menu")
    18. public class RoleMenu {
    19. //角色ID
    20. private Long roleId;
    21. //菜单ID
    22. private Long menuId;
    23. }

    第三步:mapper层新增RoleMenuMapper

    1. package com.keke.mapper;
    2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    3. import com.keke.domain.entity.RoleMenu;
    4. /**
    5. * 角色和菜单关联表(RoleMenu)表数据库访问层
    6. *
    7. * @author makejava
    8. * @since 2023-10-23 17:01:09
    9. */
    10. public interface RoleMenuMapper extends BaseMapper {
    11. }

    第四步:service新增RoleMenuService

    1. package com.keke.service;
    2. import com.baomidou.mybatisplus.extension.service.IService;
    3. import com.keke.domain.entity.RoleMenu;
    4. /**
    5. * 角色和菜单关联表(RoleMenu)表服务接口
    6. *
    7. * @author makejava
    8. * @since 2023-10-23 17:01:09
    9. */
    10. public interface RoleMenuService extends IService {
    11. }

    第五步:impl层新增RoleMenuServiceImpl

    1. package com.keke.service.impl;
    2. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    3. import com.keke.domain.entity.RoleMenu;
    4. import com.keke.mapper.RoleMenuMapper;
    5. import com.keke.service.RoleMenuService;
    6. import org.springframework.stereotype.Service;
    7. /**
    8. * 角色和菜单关联表(RoleMenu)表服务实现类
    9. *
    10. * @author makejava
    11. * @since 2023-10-23 17:01:09
    12. */
    13. @Service("roleMenuService")
    14. public class RoleMenuServiceImpl extends ServiceImpl implements RoleMenuService {
    15. }

    第六步:controller的RoleController新增

    1. package com.keke.controller;
    2. import com.keke.domain.ResponseResult;
    3. import com.keke.domain.dto.AdminAddRoleDto;
    4. import com.keke.domain.dto.ChangeRoleStatusDto;
    5. import com.keke.domain.entity.Role;
    6. import com.keke.service.RoleService;
    7. import org.springframework.beans.factory.annotation.Autowired;
    8. import org.springframework.web.bind.annotation.*;
    9. @RestController
    10. @RequestMapping("/system/role")
    11. public class RoleController {
    12. @Autowired
    13. private RoleService roleService;
    14. @GetMapping("/list")
    15. public ResponseResult selectPageRole(Role role,Integer pageNum,Integer pageSize){
    16. return roleService.selectPageRole(role,pageNum,pageSize);
    17. }
    18. @PutMapping("/changeStatus")
    19. public ResponseResult changeRoleStatus(@RequestBody ChangeRoleStatusDto changeRoleStatusDto){
    20. return roleService.changeRoleStatus(changeRoleStatusDto);
    21. }
    22. @PostMapping
    23. public ResponseResult addRole(@RequestBody AdminAddRoleDto adminAddRoleDto){
    24. return roleService.addRole(adminAddRoleDto);
    25. }
    26. }

    第七步:service层的RoleService新增

    1. package com.keke.service;
    2. import com.baomidou.mybatisplus.extension.service.IService;
    3. import com.keke.domain.ResponseResult;
    4. import com.keke.domain.dto.AdminAddRoleDto;
    5. import com.keke.domain.dto.ChangeRoleStatusDto;
    6. import com.keke.domain.entity.Role;
    7. import java.util.List;
    8. /**
    9. * 角色信息表(Role)表服务接口
    10. *
    11. * @author makejava
    12. * @since 2023-10-18 21:04:06
    13. */
    14. public interface RoleService extends IService {
    15. List selectRoleKeyByUserId(Long userId);
    16. ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize);
    17. ResponseResult changeRoleStatus(ChangeRoleStatusDto changeRoleStatusDto);
    18. ResponseResult addRole(AdminAddRoleDto adminAddRoleDto);
    19. }

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

    1. package com.keke.service.impl;
    2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    3. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    4. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    5. import com.keke.domain.ResponseResult;
    6. import com.keke.domain.dto.AdminAddRoleDto;
    7. import com.keke.domain.dto.ChangeRoleStatusDto;
    8. import com.keke.domain.entity.Role;
    9. import com.keke.domain.entity.RoleMenu;
    10. import com.keke.domain.vo.PageVo;
    11. import com.keke.mapper.RoleMapper;
    12. import com.keke.service.RoleMenuService;
    13. import com.keke.service.RoleService;
    14. import com.keke.utils.BeanCopyUtils;
    15. import org.springframework.beans.factory.annotation.Autowired;
    16. import org.springframework.stereotype.Service;
    17. import org.springframework.transaction.annotation.Transactional;
    18. import org.springframework.util.StringUtils;
    19. import java.util.ArrayList;
    20. import java.util.List;
    21. import java.util.function.Function;
    22. import java.util.stream.Collectors;
    23. /**
    24. * 角色信息表(Role)表服务实现类
    25. *
    26. * @author makejava
    27. * @since 2023-10-18 21:04:06
    28. */
    29. @Service("roleService")
    30. public class RoleServiceImpl extends ServiceImpl implements RoleService {
    31. @Autowired
    32. private RoleMenuService roleMenuService;
    33. //根据用户id查询角色信息
    34. @Override
    35. public List selectRoleKeyByUserId(Long userId) {
    36. //如果userId为1,那么角色权限字符串就只需要返回一个admin
    37. if(userId==1L){
    38. List roles = new ArrayList<>();
    39. roles.add("admin");
    40. return roles;
    41. }
    42. //如果用户id不为1,那么需要根据userId连表查询对应的roleId,然后再去角色表中去查询
    43. //对应的角色权限字符串
    44. //这里我们期望RoleMapper中封装一个方法去帮我们实现这个复杂的操作
    45. RoleMapper roleMapper = getBaseMapper();
    46. return roleMapper.selectRoleKeyByUserId(userId);
    47. }
    48. @Override
    49. public ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize) {
    50. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    51. lambdaQueryWrapper.like(StringUtils.hasText(role.getRoleName()),Role::getRoleName,role.getRoleName());
    52. lambdaQueryWrapper.eq(StringUtils.hasText(role.getStatus()),Role::getStatus,role.getStatus());
    53. lambdaQueryWrapper.orderByAsc(Role::getRoleSort);
    54. Page page = new Page<>(pageNum,pageSize);
    55. page(page,lambdaQueryWrapper);
    56. PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());
    57. return ResponseResult.okResult(pageVo);
    58. }
    59. @Override
    60. public ResponseResult changeRoleStatus(ChangeRoleStatusDto changeRoleStatusDto) {
    61. Long roleId = changeRoleStatusDto.getRoleId();
    62. Role role = getById(roleId);
    63. role.setStatus(changeRoleStatusDto.getStatus());
    64. updateById(role);
    65. return ResponseResult.okResult();
    66. }
    67. @Transactional
    68. @Override
    69. public ResponseResult addRole(AdminAddRoleDto adminAddRoleDto) {
    70. //Bean拷贝
    71. Role role = BeanCopyUtils.copyBean(adminAddRoleDto, Role.class);
    72. //拿到菜单权限id集合
    73. List menuIds = adminAddRoleDto.getMenuIds();
    74. //流式处理转化,把每一个menuId都设置到该roleId下
    75. List roleMenuList = menuIds.stream()
    76. .map(new Function() {
    77. @Override
    78. public RoleMenu apply(Long menuId) {
    79. RoleMenu roleMenu = new RoleMenu();
    80. roleMenu.setRoleId(role.getId());
    81. roleMenu.setMenuId(menuId);
    82. return roleMenu;
    83. }
    84. }).collect(Collectors.toList());
    85. //mp批量保存到数据库中
    86. roleMenuService.saveBatch(roleMenuList);
    87. //封装返回
    88. return ResponseResult.okResult();
    89. }
    90. }

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

    1. package com.keke.domain.entity;
    2. import java.util.Date;
    3. import java.io.Serializable;
    4. import com.baomidou.mybatisplus.annotation.FieldFill;
    5. import com.baomidou.mybatisplus.annotation.TableField;
    6. import lombok.AllArgsConstructor;
    7. import lombok.Data;
    8. import lombok.NoArgsConstructor;
    9. import com.baomidou.mybatisplus.annotation.TableName;
    10. /**
    11. * 角色信息表(Role)表实体类
    12. *
    13. * @author makejava
    14. * @since 2023-10-18 21:03:52
    15. */
    16. @SuppressWarnings("serial")
    17. @Data
    18. @NoArgsConstructor
    19. @AllArgsConstructor
    20. @TableName("sys_role")
    21. public class Role {
    22. //角色ID
    23. private Long id;
    24. //角色名称
    25. private String roleName;
    26. //角色权限字符串
    27. private String roleKey;
    28. //显示顺序
    29. private Integer roleSort;
    30. //角色状态(0正常 1停用)
    31. private String status;
    32. //删除标志(0代表存在 1代表删除)
    33. private String delFlag;
    34. //创建者
    35. @TableField(fill = FieldFill.INSERT)
    36. private Long createBy;
    37. //创建时间
    38. @TableField(fill = FieldFill.INSERT)
    39. private Date createTime;
    40. //更新者
    41. @TableField(fill = FieldFill.INSERT_UPDATE)
    42. private Long updateBy;
    43. //更新时间
    44. @TableField(fill = FieldFill.INSERT_UPDATE)
    45. private Date updateTime;
    46. //备注
    47. private String remark;
    48. }
    3.2.3 测试

    4. 修改角色

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

    这里可以分析到修改角色有三个接口,一个点击是修改角色后,角色信息的回显接口,回显中有一个菜单权限树接口,另一个是修改角色的接口

    4.1 角色信息回显接口

    4.1.1接口分析
    请求路径请求方式是否需求token头
    system/role/{id}Get

    请求参数PathVariable格式:

    id: 角色id

    响应格式:

    1. {
    2. "code":200,
    3. "data":{
    4. "id":"11",
    5. "remark":"嘎嘎嘎",
    6. "roleKey":"aggag",
    7. "roleName":"嘎嘎嘎",
    8. "roleSort":"5",
    9. "status":"0"
    10. },
    11. "msg":"操作成功"
    12. }
    4.1.2 代码实现

    第一步:controller层

    1. package com.keke.controller;
    2. import com.keke.domain.ResponseResult;
    3. import com.keke.domain.dto.AdminAddRoleDto;
    4. import com.keke.domain.dto.ChangeRoleStatusDto;
    5. import com.keke.domain.entity.Role;
    6. import com.keke.service.RoleService;
    7. import org.springframework.beans.factory.annotation.Autowired;
    8. import org.springframework.web.bind.annotation.*;
    9. @RestController
    10. @RequestMapping("/system/role")
    11. public class RoleController {
    12. @Autowired
    13. private RoleService roleService;
    14. @GetMapping("/list")
    15. public ResponseResult selectPageRole(Role role,Integer pageNum,Integer pageSize){
    16. return roleService.selectPageRole(role,pageNum,pageSize);
    17. }
    18. @PutMapping("/changeStatus")
    19. public ResponseResult changeRoleStatus(@RequestBody ChangeRoleStatusDto changeRoleStatusDto){
    20. return roleService.changeRoleStatus(changeRoleStatusDto);
    21. }
    22. @PostMapping
    23. public ResponseResult addRole(@RequestBody AdminAddRoleDto adminAddRoleDto){
    24. return roleService.addRole(adminAddRoleDto);
    25. }
    26. @GetMapping("/{id}")
    27. public ResponseResult getRoleInfo(@PathVariable("id") Long id){
    28. return roleService.getRoleInfo(id);
    29. }
    30. }

    第二步:domain/vo层新增

    1. package com.keke.domain.vo;
    2. import com.baomidou.mybatisplus.annotation.FieldFill;
    3. import com.baomidou.mybatisplus.annotation.TableField;
    4. import lombok.AllArgsConstructor;
    5. import lombok.Data;
    6. import lombok.NoArgsConstructor;
    7. import java.util.Date;
    8. @Data
    9. @AllArgsConstructor
    10. @NoArgsConstructor
    11. public class AdminRoleVo {
    12. //角色ID
    13. private Long id;
    14. //角色名称
    15. private String roleName;
    16. //角色权限字符串
    17. private String roleKey;
    18. //显示顺序
    19. private Integer roleSort;
    20. //角色状态(0正常 1停用)
    21. private String status;
    22. //备注
    23. private String remark;
    24. }

    第三步:service层

    1. package com.keke.service;
    2. import com.baomidou.mybatisplus.extension.service.IService;
    3. import com.keke.domain.ResponseResult;
    4. import com.keke.domain.dto.AdminAddRoleDto;
    5. import com.keke.domain.dto.ChangeRoleStatusDto;
    6. import com.keke.domain.entity.Role;
    7. import java.util.List;
    8. /**
    9. * 角色信息表(Role)表服务接口
    10. *
    11. * @author makejava
    12. * @since 2023-10-18 21:04:06
    13. */
    14. public interface RoleService extends IService {
    15. List selectRoleKeyByUserId(Long userId);
    16. ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize);
    17. ResponseResult changeRoleStatus(ChangeRoleStatusDto changeRoleStatusDto);
    18. ResponseResult addRole(AdminAddRoleDto adminAddRoleDto);
    19. ResponseResult getRoleInfo(Long id);
    20. }

    第四步:impl层新增

    1. package com.keke.service.impl;
    2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    3. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    4. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    5. import com.keke.domain.ResponseResult;
    6. import com.keke.domain.dto.AdminAddRoleDto;
    7. import com.keke.domain.dto.ChangeRoleStatusDto;
    8. import com.keke.domain.entity.Role;
    9. import com.keke.domain.entity.RoleMenu;
    10. import com.keke.domain.vo.AdminRoleVo;
    11. import com.keke.domain.vo.PageVo;
    12. import com.keke.mapper.RoleMapper;
    13. import com.keke.service.RoleMenuService;
    14. import com.keke.service.RoleService;
    15. import com.keke.utils.BeanCopyUtils;
    16. import org.springframework.beans.factory.annotation.Autowired;
    17. import org.springframework.stereotype.Service;
    18. import org.springframework.transaction.annotation.Transactional;
    19. import org.springframework.util.StringUtils;
    20. import java.util.ArrayList;
    21. import java.util.List;
    22. import java.util.function.Function;
    23. import java.util.stream.Collectors;
    24. /**
    25. * 角色信息表(Role)表服务实现类
    26. *
    27. * @author makejava
    28. * @since 2023-10-18 21:04:06
    29. */
    30. @Service("roleService")
    31. public class RoleServiceImpl extends ServiceImpl implements RoleService {
    32. @Autowired
    33. private RoleMenuService roleMenuService;
    34. //根据用户id查询角色信息
    35. @Override
    36. public List selectRoleKeyByUserId(Long userId) {
    37. //如果userId为1,那么角色权限字符串就只需要返回一个admin
    38. if(userId==1L){
    39. List roles = new ArrayList<>();
    40. roles.add("admin");
    41. return roles;
    42. }
    43. //如果用户id不为1,那么需要根据userId连表查询对应的roleId,然后再去角色表中去查询
    44. //对应的角色权限字符串
    45. //这里我们期望RoleMapper中封装一个方法去帮我们实现这个复杂的操作
    46. RoleMapper roleMapper = getBaseMapper();
    47. return roleMapper.selectRoleKeyByUserId(userId);
    48. }
    49. @Override
    50. public ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize) {
    51. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    52. lambdaQueryWrapper.like(StringUtils.hasText(role.getRoleName()),Role::getRoleName,role.getRoleName());
    53. lambdaQueryWrapper.eq(StringUtils.hasText(role.getStatus()),Role::getStatus,role.getStatus());
    54. lambdaQueryWrapper.orderByAsc(Role::getRoleSort);
    55. Page page = new Page<>(pageNum,pageSize);
    56. page(page,lambdaQueryWrapper);
    57. PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());
    58. return ResponseResult.okResult(pageVo);
    59. }
    60. @Override
    61. public ResponseResult changeRoleStatus(ChangeRoleStatusDto changeRoleStatusDto) {
    62. Long roleId = changeRoleStatusDto.getRoleId();
    63. Role role = getById(roleId);
    64. role.setStatus(changeRoleStatusDto.getStatus());
    65. updateById(role);
    66. return ResponseResult.okResult();
    67. }
    68. @Transactional
    69. @Override
    70. public ResponseResult addRole(AdminAddRoleDto adminAddRoleDto) {
    71. //Bean拷贝
    72. Role role = BeanCopyUtils.copyBean(adminAddRoleDto, Role.class);
    73. //拿到菜单权限id集合
    74. List menuIds = adminAddRoleDto.getMenuIds();
    75. //流式处理转化,把每一个menuId都设置到该roleId下
    76. List roleMenuList = menuIds.stream()
    77. .map(new Function() {
    78. @Override
    79. public RoleMenu apply(Long menuId) {
    80. RoleMenu roleMenu = new RoleMenu();
    81. roleMenu.setRoleId(role.getId());
    82. roleMenu.setMenuId(menuId);
    83. return roleMenu;
    84. }
    85. }).collect(Collectors.toList());
    86. //mp批量保存到数据库中
    87. roleMenuService.saveBatch(roleMenuList);
    88. //封装返回
    89. return ResponseResult.okResult();
    90. }
    91. @Override
    92. public ResponseResult getRoleInfo(Long id) {
    93. Role role = getById(id);
    94. AdminRoleVo adminRoleVo = BeanCopyUtils.copyBean(role, AdminRoleVo.class);
    95. return ResponseResult.okResult(adminRoleVo);
    96. }
    97. }
    4.1.3 测试

    回显成功

    4.2 对应角色菜单权限树接口

    4.2.1 接口分析
    请求方式请求路径是否需求token头
    Get/system/menu/roleMenuTreeselect/{id}

    请求参数PathVariable格式:

    id: 角色id

    响应格式:

    字段介绍

    menus:菜单树

    checkedKeys:角色所关联的菜单权限id列表

    1. {
    2. "code":200,
    3. "data":{
    4. "menus":[
    5. {
    6. "children":[],
    7. "id":"2023",
    8. "label":"写博文",
    9. "parentId":"0"
    10. },
    11. {
    12. "children":[
    13. {
    14. "children":[
    15. {
    16. "children":[],
    17. "id":"1001",
    18. "label":"用户查询",
    19. "parentId":"100"
    20. },
    21. {
    22. "children":[],
    23. "id":"1002",
    24. "label":"用户新增",
    25. "parentId":"100"
    26. },
    27. {
    28. "children":[],
    29. "id":"1003",
    30. "label":"用户修改",
    31. "parentId":"100"
    32. },
    33. {
    34. "children":[],
    35. "id":"1004",
    36. "label":"用户删除",
    37. "parentId":"100"
    38. },
    39. {
    40. "children":[],
    41. "id":"1005",
    42. "label":"用户导出",
    43. "parentId":"100"
    44. },
    45. {
    46. "children":[],
    47. "id":"1006",
    48. "label":"用户导入",
    49. "parentId":"100"
    50. },
    51. {
    52. "children":[],
    53. "id":"1007",
    54. "label":"重置密码",
    55. "parentId":"100"
    56. }
    57. ],
    58. "id":"100",
    59. "label":"用户管理",
    60. "parentId":"1"
    61. },
    62. {
    63. "children":[
    64. {
    65. "children":[],
    66. "id":"1008",
    67. "label":"角色查询",
    68. "parentId":"101"
    69. },
    70. {
    71. "children":[],
    72. "id":"1009",
    73. "label":"角色新增",
    74. "parentId":"101"
    75. },
    76. {
    77. "children":[],
    78. "id":"1010",
    79. "label":"角色修改",
    80. "parentId":"101"
    81. },
    82. {
    83. "children":[],
    84. "id":"1011",
    85. "label":"角色删除",
    86. "parentId":"101"
    87. },
    88. {
    89. "children":[],
    90. "id":"1012",
    91. "label":"角色导出",
    92. "parentId":"101"
    93. }
    94. ],
    95. "id":"101",
    96. "label":"角色管理",
    97. "parentId":"1"
    98. },
    99. {
    100. "children":[
    101. {
    102. "children":[],
    103. "id":"1013",
    104. "label":"菜单查询",
    105. "parentId":"102"
    106. },
    107. {
    108. "children":[],
    109. "id":"1014",
    110. "label":"菜单新增",
    111. "parentId":"102"
    112. },
    113. {
    114. "children":[],
    115. "id":"1015",
    116. "label":"菜单修改",
    117. "parentId":"102"
    118. },
    119. {
    120. "children":[],
    121. "id":"1016",
    122. "label":"菜单删除",
    123. "parentId":"102"
    124. }
    125. ],
    126. "id":"102",
    127. "label":"菜单管理",
    128. "parentId":"1"
    129. }
    130. ],
    131. "id":"1",
    132. "label":"系统管理",
    133. "parentId":"0"
    134. },
    135. {
    136. "children":[
    137. {
    138. "children":[],
    139. "id":"2019",
    140. "label":"文章管理",
    141. "parentId":"2017"
    142. },
    143. {
    144. "children":[
    145. {
    146. "children":[],
    147. "id":"2028",
    148. "label":"导出分类",
    149. "parentId":"2018"
    150. }
    151. ],
    152. "id":"2018",
    153. "label":"分类管理",
    154. "parentId":"2017"
    155. },
    156. {
    157. "children":[
    158. {
    159. "children":[],
    160. "id":"2024",
    161. "label":"友链新增",
    162. "parentId":"2022"
    163. },
    164. {
    165. "children":[],
    166. "id":"2025",
    167. "label":"友链修改",
    168. "parentId":"2022"
    169. },
    170. {
    171. "children":[],
    172. "id":"2026",
    173. "label":"友链删除",
    174. "parentId":"2022"
    175. },
    176. {
    177. "children":[],
    178. "id":"2027",
    179. "label":"友链查询",
    180. "parentId":"2022"
    181. }
    182. ],
    183. "id":"2022",
    184. "label":"友链管理",
    185. "parentId":"2017"
    186. },
    187. {
    188. "children":[],
    189. "id":"2021",
    190. "label":"标签管理",
    191. "parentId":"2017"
    192. }
    193. ],
    194. "id":"2017",
    195. "label":"内容管理",
    196. "parentId":"0"
    197. }
    198. ],
    199. "checkedKeys":[
    200. "1001"
    201. ]
    202. },
    203. "msg":"操作成功"
    204. }
    4.2.2 代码实现

    第一步:vo层新建RoleMenuTreeSelectVo,用于响应返回

    1. package com.keke.domain.vo;
    2. import lombok.AllArgsConstructor;
    3. import lombok.Data;
    4. import lombok.NoArgsConstructor;
    5. import java.util.List;
    6. @Data
    7. @AllArgsConstructor
    8. @NoArgsConstructor
    9. public class RoleMenuTreeSelectVo {
    10. private List menus;
    11. private List checkedKeys;
    12. }

    第二步: 把keke-framework工程的MenuService接口修改为如下,增加了 '根据角色id查询对应角色菜单列表树' 的接口 

    1. package com.keke.service;
    2. import com.baomidou.mybatisplus.extension.service.IService;
    3. import com.keke.domain.ResponseResult;
    4. import com.keke.domain.entity.Menu;
    5. import com.keke.domain.vo.AdminMenuVo;
    6. import java.util.List;
    7. /**
    8. * 菜单权限表(Menu)表服务接口
    9. *
    10. * @author makejava
    11. * @since 2023-10-18 20:55:48
    12. */
    13. public interface MenuService extends IService {
    14. //查询用户权限信息
    15. List selectPermsByUserId(Long userId);
    16. //查询用户的路由信息,也就是权限菜单
    17. List selectRouterMenuTreeByUserId(Long userId);
    18. List selectAllMenu(Menu menu);
    19. ResponseResult editMenu(Menu menu);
    20. ResponseResult deleteMenu(Long menuId);
    21. List selectMenuListByRoleId(Long roleId);
    22. }

    第三步:MenuServiceImpl

    1. package com.keke.service.impl;
    2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    3. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    4. import com.keke.constants.SystemConstants;
    5. import com.keke.domain.ResponseResult;
    6. import com.keke.domain.entity.Menu;
    7. import com.keke.domain.vo.AdminMenuVo;
    8. import com.keke.mapper.MenuMapper;
    9. import com.keke.service.MenuService;
    10. import com.keke.utils.BeanCopyUtils;
    11. import com.keke.utils.SecurityUtils;
    12. import org.springframework.beans.factory.annotation.Autowired;
    13. import org.springframework.stereotype.Service;
    14. import org.springframework.util.StringUtils;
    15. import java.util.List;
    16. import java.util.function.Function;
    17. import java.util.stream.Collectors;
    18. /**
    19. * 菜单权限表(Menu)表服务实现类
    20. *
    21. * @author makejava
    22. * @since 2023-10-18 20:55:48
    23. */
    24. @Service("menuService")
    25. public class MenuServiceImpl extends ServiceImpl implements MenuService {
    26. @Autowired
    27. private MenuService menuService;
    28. //根据用户id查询权限关键字
    29. @Override
    30. public List selectPermsByUserId(Long userId) {
    31. //如果用户id为1代表管理员,roles 中只需要有admin,
    32. // permissions中需要有所有菜单类型为C(菜单)或者F(按钮)的,状态为正常的,未被删除的权限
    33. if(SecurityUtils.isAdmin()) {
    34. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    35. lambdaQueryWrapper.in(Menu::getMenuType, SystemConstants.MENU, SystemConstants.BUTTON);
    36. lambdaQueryWrapper.eq(Menu::getStatus, SystemConstants.STATUS_NORMAL);
    37. //由于我们的逻辑删除字段已经配置了,所以无需封装lambdaQueryWrapper
    38. List menuList = list(lambdaQueryWrapper);
    39. //我们需要的是String类型的集合,这里我们要进行数据的处理,这里采用流的方式
    40. List permissions = menuList.stream()
    41. .map(new Function() {
    42. @Override
    43. public String apply(Menu menu) {
    44. String perms = menu.getPerms();
    45. return perms;
    46. }
    47. })
    48. .collect(Collectors.toList());
    49. return permissions;
    50. }
    51. //否则返回这个用户所具有的权限
    52. //这里我们需要进行连表查询,因为我们的用户先和角色关联,然后角色才跟权限关联
    53. MenuMapper menuMapper = getBaseMapper();
    54. //我们期望menuMapper中有一个方法可以直接帮我们去实现这个复杂的逻辑,这里直接返回
    55. return menuMapper.selectPermsByUserId(userId);
    56. }
    57. @Override
    58. public List selectRouterMenuTreeByUserId(Long userId) {
    59. MenuMapper menuMapper = getBaseMapper();
    60. List menus = null;
    61. //如果是管理员,返回所有
    62. if(SecurityUtils.isAdmin()){
    63. menus = menuMapper.selectAllRoutersMenu();
    64. }else {
    65. //如果不是管理员,返回对应用户的菜单
    66. menus = menuMapper.selectRoutersMenuTreeByUserId(userId);
    67. }
    68. //因为上面的查询都是从数据库进行查询,所以无法封装children,这里构建Tree
    69. List menuTree = buildMenuTree(menus,0L);
    70. return menuTree;
    71. }
    72. @Override
    73. public List selectAllMenu(Menu menu) {
    74. //可以针对菜单名进行模糊查询。也可以针对菜单的状态进行查询
    75. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    76. lambdaQueryWrapper.like(StringUtils.hasText(menu.getMenuName()),Menu::getMenuName,menu.getMenuName());
    77. lambdaQueryWrapper.eq(StringUtils.hasText(menu.getStatus()),Menu::getStatus,menu.getStatus());
    78. //排序 parent_id和order_num
    79. lambdaQueryWrapper.orderByAsc(Menu::getParentId,Menu::getOrderNum);
    80. List menus = list(lambdaQueryWrapper);
    81. List adminMenuVos = BeanCopyUtils.copyBeanList(menus, AdminMenuVo.class);
    82. return adminMenuVos;
    83. }
    84. @Override
    85. public ResponseResult editMenu(Menu menu) {
    86. if(menu.getParentId().equals(menu.getId())){
    87. return ResponseResult.okResult(500,"修改菜单" + "'" + menu.getMenuName() + "'" + "失败,上级菜单不能选择自己");
    88. }
    89. updateById(menu);
    90. return ResponseResult.okResult();
    91. }
    92. @Override
    93. public ResponseResult deleteMenu(Long menuId) {
    94. Menu menu = menuService.getById(menuId);
    95. //如果该菜单有子菜单,那么就提示不能删除,逻辑就是查询菜单表中是否有父菜单id是当前菜单id
    96. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    97. lambdaQueryWrapper.eq(Menu::getParentId,menuId);
    98. List menus = list(lambdaQueryWrapper);
    99. if(!menus.isEmpty()) {
    100. return ResponseResult.okResult(500, "存在子菜单不允许删除");
    101. }
    102. removeById(menuId);
    103. return ResponseResult.okResult();
    104. }
    105. @Override
    106. public List selectMenuListByRoleId(Long roleId) {
    107. return getBaseMapper().selectMenuListByRoleId(roleId);
    108. }
    109. /**
    110. * 构建MenuTree
    111. * 思路先找第一层级的菜单,就是找到id于parentId的对应关系,然后把parentId设置为Id的children
    112. * @param menus
    113. * @return
    114. */
    115. private List buildMenuTree(List menus,Long parentId) {
    116. //转化流处理
    117. List menuTree = menus.stream()
    118. //过滤掉除一级菜单之外的菜单
    119. .filter(menu -> menu.getParentId().equals(parentId))
    120. //然后将获取其子菜单设置到children字段,并返回
    121. .map(m -> m.setChildren(gerChildren(m, menus)))
    122. .collect(Collectors.toList());
    123. return menuTree;
    124. }
    125. //获取当前菜单的子菜单
    126. private List gerChildren(Menu menu, List menus) {
    127. //流处理,遍历每一个流对象,筛选出流对象的parentId=menu的id,即过滤
    128. List children = menus.stream()
    129. .filter(m -> m.getParentId().equals(menu.getId()))
    130. //这里其实不必要写,这一步的逻辑是如果有三级,
    131. //可以把流对象中再过筛选出子菜单设置给对应的children并返回
    132. .map(m -> m.setChildren(gerChildren(m,menus)))
    133. .collect(Collectors.toList());
    134. return children;
    135. }
    136. }

    第四步:MenuMapper新增 

    1. package com.keke.mapper;
    2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    3. import com.keke.domain.entity.Menu;
    4. import org.apache.ibatis.annotations.Mapper;
    5. import java.util.List;
    6. /**
    7. * 菜单权限表(Menu)表数据库访问层
    8. *
    9. * @author makejava
    10. * @since 2023-10-18 20:55:48
    11. */
    12. @Mapper
    13. public interface MenuMapper extends BaseMapper {
    14. //Mapper的实现类对应xml映射文件
    15. List selectPermsByUserId(Long userId);
    16. List selectAllRoutersMenu();
    17. List selectRoutersMenuTreeByUserId(Long userId);
    18. List selectMenuListByRoleId(Long roleId);
    19. }

     第五步: 把keke-framework工程的resources/mapper目录的MenuMapper.xml修改为如下,增加了 '根据角色id查询对应角色菜单列表树' 的具体实现代码

    1. "1.0" encoding="UTF-8" ?>
    2. mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
    3. <mapper namespace="com.keke.mapper.MenuMapper">
    4. <select id="selectPermsByUserId" resultType="java.lang.String">
    5. -- 这里的逻辑是先用userId连表查询roleId,再用roleId连表查询menuId,再根据menuId
    6. -- 查询对应的用户权限
    7. select
    8. DISTINCT m.`perms`
    9. from `sys_user_role` ur
    10. left join `sys_role_menu` rm on ur.`role_id`=rm.`role_id`
    11. left join `sys_menu` m on m.`id`=rm.`menu_id`
    12. where
    13. ur.`user_id`=#{userId} and
    14. m.`menu_type` in ('C','F') and
    15. m.`status`=0 and
    16. m.`del_flag`=0
    17. select>
    18. <select id="selectAllRoutersMenu" resultType="com.keke.domain.entity.Menu">
    19. -- 这里与上面的sql差不多,只是menu_type有差别,还有查询的字段个数
    20. SELECT
    21. DISTINCT m.id, m.parent_id, m.menu_name, m.path, m.component, m.visible, m.status,
    22. IFNULL(m.perms,'') AS perms, m.is_frame, m.menu_type, m.icon, m.order_num, m.create_time
    23. FROM
    24. `sys_menu` m
    25. WHERE
    26. -- 查询所有的,所以不需要加userId的条件
    27. m.`menu_type` IN ('C','M') AND
    28. m.`status` = 0 AND
    29. m.`del_flag` = 0
    30. ORDER BY
    31. m.parent_id,m.order_num
    32. select>
    33. <select id="selectRoutersMenuTreeByUserId" resultType="com.keke.domain.entity.Menu">
    34. -- 这里与上面的sql差不多
    35. SELECT
    36. DISTINCT m.id, m.parent_id, m.menu_name, m.path, m.component, m.visible, m.status,
    37. IFNULL(m.perms,'') AS perms, m.is_frame, m.menu_type, m.icon, m.order_num, m.create_time
    38. FROM
    39. `sys_user_role` ur
    40. LEFT JOIN `sys_role_menu` rm ON ur.`role_id` = rm.`role_id`
    41. LEFT JOIN `sys_menu` m ON m.`id` = rm.`menu_id`
    42. WHERE
    43. ur.`user_id` = #{userId} AND
    44. m.`menu_type` IN ('C','M') AND
    45. m.`status` = 0 AND
    46. m.`del_flag` = 0
    47. ORDER BY
    48. m.parent_id,m.order_num
    49. select>
    50. <select id="selectMenuListByRoleId" resultType="java.lang.Long">
    51. select m.id
    52. from sys_menu m
    53. left join sys_role_menu rm on m.id = rm.menu_id
    54. where rm.role_id = #{roleId}
    55. order by m.parent_id, m.order_num
    56. select>
    57. mapper>

    第六步:MenuController

    1. package com.keke.controller;
    2. import com.keke.domain.ResponseResult;
    3. import com.keke.domain.entity.Menu;
    4. import com.keke.domain.vo.AdminMenuVo;
    5. import com.keke.domain.vo.MenuTreeVo;
    6. import com.keke.domain.vo.RoleMenuTreeSelectVo;
    7. import com.keke.service.MenuService;
    8. import com.keke.utils.BeanCopyUtils;
    9. import com.keke.utils.SystemConverter;
    10. import org.springframework.beans.factory.annotation.Autowired;
    11. import org.springframework.security.core.parameters.P;
    12. import org.springframework.web.bind.annotation.*;
    13. import java.util.List;
    14. @RestController
    15. @RequestMapping("/system/menu")
    16. public class MenuController {
    17. @Autowired
    18. private MenuService menuService;
    19. //查询菜单列表
    20. @GetMapping("/list")
    21. public ResponseResult selectAllMenu(Menu menu){
    22. List adminMenuVos = menuService.selectAllMenu(menu);
    23. return ResponseResult.okResult(adminMenuVos);
    24. }
    25. //新增菜单
    26. @PostMapping
    27. public ResponseResult add(@RequestBody Menu menu) {
    28. menuService.save(menu);
    29. return ResponseResult.okResult();
    30. }
    31. @GetMapping("/{menuId}")
    32. public ResponseResult getInfoById(@PathVariable("menuId") Long menuId){
    33. Menu menu = menuService.getById(menuId);
    34. return ResponseResult.okResult(menu);
    35. }
    36. @GetMapping("/roleMenuTreeselect/{id}")
    37. public ResponseResult selectMenuListByRoleId(@PathVariable("id") Long roleId){
    38. List adminMenuVos = menuService.selectAllMenu(new Menu());
    39. List menus = BeanCopyUtils.copyBeanList(adminMenuVos, Menu.class);
    40. List checkedKeys = menuService.selectMenuListByRoleId(roleId);
    41. List menuTreeVos = SystemConverter.buildMenuSelectTree(menus);
    42. RoleMenuTreeSelectVo roleMenuTreeSelectVo = new RoleMenuTreeSelectVo(menuTreeVos,checkedKeys);
    43. return ResponseResult.okResult(roleMenuTreeSelectVo);
    44. }
    45. @PutMapping
    46. public ResponseResult editMenu(@RequestBody Menu menu){
    47. return menuService.editMenu(menu);
    48. }
    49. @DeleteMapping("/{menuId}")
    50. public ResponseResult deleteMenu(@PathVariable("menuId") Long menuId){
    51. return menuService.deleteMenu(menuId);
    52. }
    53. @GetMapping("/treeselect")
    54. public ResponseResult treeSelect(){
    55. //复用之前的selectMenuList方法。方法需要参数,参数可以用来进行条件查询,而这个方法不需要条件,所以直接new Menu()传入
    56. //这样就可以获取所有的菜单了
    57. List adminMenuVos = menuService.selectAllMenu(new Menu());
    58. List menus = BeanCopyUtils.copyBeanList(adminMenuVos, Menu.class);
    59. List options = SystemConverter.buildMenuSelectTree(menus);
    60. return ResponseResult.okResult(options);
    61. }
    62. }
    4.2.3 测试

    可用看到回显出了对应角色的权限信息

    4.3 修改角色接口

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

    请求体:

    1. {
    2. "id":"13",
    3. "remark":"我是角色备注",
    4. "roleKey":"wds",
    5. "roleName":"测试新增角色",
    6. "roleSort":0,
    7. "status":"0",
    8. "menuIds":[
    9. "1",
    10. "100",
    11. "1001"
    12. ]
    13. }

    响应体

    1. {
    2. "code":200,
    3. "msg":"操作成功"
    4. }
    4.3.2 代码实现

    第一步:domain/dto层新增

    1. package com.keke.domain.dto;
    2. import com.baomidou.mybatisplus.annotation.FieldFill;
    3. import com.baomidou.mybatisplus.annotation.TableField;
    4. import lombok.AllArgsConstructor;
    5. import lombok.Data;
    6. import lombok.NoArgsConstructor;
    7. import java.util.Date;
    8. import java.util.List;
    9. @Data
    10. @AllArgsConstructor
    11. @NoArgsConstructor
    12. public class EditRoleDto {
    13. //角色ID
    14. private Long id;
    15. //角色名称
    16. private String roleName;
    17. //角色权限字符串
    18. private String roleKey;
    19. //显示顺序
    20. private Integer roleSort;
    21. //角色状态(0正常 1停用)
    22. private String status;
    23. //备注
    24. private String remark;
    25. //权限id
    26. private List menuIds;
    27. }

    第二步:controller层新增EditRole接口

    1. package com.keke.controller;
    2. import com.keke.domain.ResponseResult;
    3. import com.keke.domain.dto.AdminAddRoleDto;
    4. import com.keke.domain.dto.ChangeRoleStatusDto;
    5. import com.keke.domain.dto.EditRoleDto;
    6. import com.keke.domain.entity.Role;
    7. import com.keke.service.RoleService;
    8. import org.springframework.beans.factory.annotation.Autowired;
    9. import org.springframework.web.bind.annotation.*;
    10. @RestController
    11. @RequestMapping("/system/role")
    12. public class RoleController {
    13. @Autowired
    14. private RoleService roleService;
    15. @GetMapping("/list")
    16. public ResponseResult selectPageRole(Role role,Integer pageNum,Integer pageSize){
    17. return roleService.selectPageRole(role,pageNum,pageSize);
    18. }
    19. @PutMapping("/changeStatus")
    20. public ResponseResult changeRoleStatus(@RequestBody ChangeRoleStatusDto changeRoleStatusDto){
    21. return roleService.changeRoleStatus(changeRoleStatusDto);
    22. }
    23. @PostMapping
    24. public ResponseResult addRole(@RequestBody AdminAddRoleDto adminAddRoleDto){
    25. return roleService.addRole(adminAddRoleDto);
    26. }
    27. @GetMapping("/{id}")
    28. public ResponseResult getRoleInfo(@PathVariable("id") Long id){
    29. return roleService.getRoleInfo(id);
    30. }
    31. @PutMapping
    32. public ResponseResult editRole(@RequestBody EditRoleDto editRoleDto){
    33. return roleService.editRole(editRoleDto);
    34. }
    35. }

    第二步:service新增

    1. package com.keke.service;
    2. import com.baomidou.mybatisplus.extension.service.IService;
    3. import com.keke.domain.ResponseResult;
    4. import com.keke.domain.dto.AdminAddRoleDto;
    5. import com.keke.domain.dto.ChangeRoleStatusDto;
    6. import com.keke.domain.dto.EditRoleDto;
    7. import com.keke.domain.entity.Role;
    8. import java.util.List;
    9. /**
    10. * 角色信息表(Role)表服务接口
    11. *
    12. * @author makejava
    13. * @since 2023-10-18 21:04:06
    14. */
    15. public interface RoleService extends IService {
    16. List selectRoleKeyByUserId(Long userId);
    17. ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize);
    18. ResponseResult changeRoleStatus(ChangeRoleStatusDto changeRoleStatusDto);
    19. ResponseResult addRole(AdminAddRoleDto adminAddRoleDto);
    20. ResponseResult getRoleInfo(Long id);
    21. ResponseResult editRole(EditRoleDto editRoleDto);
    22. }

    第三步:impl新增,逻辑和添加用户一样

    1. package com.keke.service.impl;
    2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    3. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    4. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    5. import com.keke.domain.ResponseResult;
    6. import com.keke.domain.dto.AdminAddRoleDto;
    7. import com.keke.domain.dto.ChangeRoleStatusDto;
    8. import com.keke.domain.dto.EditRoleDto;
    9. import com.keke.domain.entity.Role;
    10. import com.keke.domain.entity.RoleMenu;
    11. import com.keke.domain.vo.AdminRoleVo;
    12. import com.keke.domain.vo.PageVo;
    13. import com.keke.mapper.RoleMapper;
    14. import com.keke.service.RoleMenuService;
    15. import com.keke.service.RoleService;
    16. import com.keke.utils.BeanCopyUtils;
    17. import org.springframework.beans.factory.annotation.Autowired;
    18. import org.springframework.stereotype.Service;
    19. import org.springframework.transaction.annotation.Transactional;
    20. import org.springframework.util.StringUtils;
    21. import java.util.ArrayList;
    22. import java.util.List;
    23. import java.util.function.Function;
    24. import java.util.stream.Collectors;
    25. /**
    26. * 角色信息表(Role)表服务实现类
    27. *
    28. * @author makejava
    29. * @since 2023-10-18 21:04:06
    30. */
    31. @Service("roleService")
    32. public class RoleServiceImpl extends ServiceImpl implements RoleService {
    33. @Autowired
    34. private RoleMenuService roleMenuService;
    35. //根据用户id查询角色信息
    36. @Override
    37. public List selectRoleKeyByUserId(Long userId) {
    38. //如果userId为1,那么角色权限字符串就只需要返回一个admin
    39. if(userId==1L){
    40. List roles = new ArrayList<>();
    41. roles.add("admin");
    42. return roles;
    43. }
    44. //如果用户id不为1,那么需要根据userId连表查询对应的roleId,然后再去角色表中去查询
    45. //对应的角色权限字符串
    46. //这里我们期望RoleMapper中封装一个方法去帮我们实现这个复杂的操作
    47. RoleMapper roleMapper = getBaseMapper();
    48. return roleMapper.selectRoleKeyByUserId(userId);
    49. }
    50. @Override
    51. public ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize) {
    52. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    53. lambdaQueryWrapper.like(StringUtils.hasText(role.getRoleName()),Role::getRoleName,role.getRoleName());
    54. lambdaQueryWrapper.eq(StringUtils.hasText(role.getStatus()),Role::getStatus,role.getStatus());
    55. lambdaQueryWrapper.orderByAsc(Role::getRoleSort);
    56. Page page = new Page<>(pageNum,pageSize);
    57. page(page,lambdaQueryWrapper);
    58. PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());
    59. return ResponseResult.okResult(pageVo);
    60. }
    61. @Override
    62. public ResponseResult changeRoleStatus(ChangeRoleStatusDto changeRoleStatusDto) {
    63. Long roleId = changeRoleStatusDto.getRoleId();
    64. Role role = getById(roleId);
    65. role.setStatus(changeRoleStatusDto.getStatus());
    66. updateById(role);
    67. return ResponseResult.okResult();
    68. }
    69. @Transactional
    70. @Override
    71. public ResponseResult addRole(AdminAddRoleDto adminAddRoleDto) {
    72. //Bean拷贝
    73. Role role = BeanCopyUtils.copyBean(adminAddRoleDto, Role.class);
    74. //拿到菜单权限id集合
    75. List menuIds = adminAddRoleDto.getMenuIds();
    76. //流式处理转化,把每一个menuId都设置到该roleId下
    77. List roleMenuList = menuIds.stream()
    78. .map(new Function() {
    79. @Override
    80. public RoleMenu apply(Long menuId) {
    81. RoleMenu roleMenu = new RoleMenu();
    82. roleMenu.setRoleId(role.getId());
    83. roleMenu.setMenuId(menuId);
    84. return roleMenu;
    85. }
    86. }).collect(Collectors.toList());
    87. //mp批量保存到数据库中
    88. roleMenuService.saveBatch(roleMenuList);
    89. //封装返回
    90. return ResponseResult.okResult();
    91. }
    92. @Override
    93. public ResponseResult getRoleInfo(Long id) {
    94. Role role = getById(id);
    95. AdminRoleVo adminRoleVo = BeanCopyUtils.copyBean(role, AdminRoleVo.class);
    96. return ResponseResult.okResult(adminRoleVo);
    97. }
    98. @Override
    99. public ResponseResult editRole(EditRoleDto editRoleDto) {
    100. Role role = BeanCopyUtils.copyBean(editRoleDto, Role.class);
    101. List menuIds = editRoleDto.getMenuIds();
    102. List roleMenuList = menuIds.stream()
    103. .map(new Function() {
    104. @Override
    105. public RoleMenu apply(Long menuId) {
    106. RoleMenu roleMenu = new RoleMenu();
    107. roleMenu.setRoleId(role.getId());
    108. roleMenu.setMenuId(menuId);
    109. return roleMenu;
    110. }
    111. }).collect(Collectors.toList());
    112. roleMenuService.saveBatch(roleMenuList);
    113. return ResponseResult.okResult();
    114. }
    115. }
    4.3.3 测试

    可用新增角色,并且赋予其相应的菜单权限

    5. 删除角色

    删除固定的某个角色(逻辑删除)

    5.1 接口分析

    请求方式请求路径是否需求token头
    DELETEsystem/role/{id}

    请求参数PathVariable格式:

    id:要删除的角色id

     响应格式:

    1. {
    2. "code":200,
    3. "msg":"操作成功"
    4. }

    5.2 代码实现

    controller层新增接口,逻辑较少直接写

    1. package com.keke.controller;
    2. import com.keke.domain.ResponseResult;
    3. import com.keke.domain.dto.AdminAddRoleDto;
    4. import com.keke.domain.dto.ChangeRoleStatusDto;
    5. import com.keke.domain.dto.EditRoleDto;
    6. import com.keke.domain.entity.Role;
    7. import com.keke.service.RoleService;
    8. import org.springframework.beans.factory.annotation.Autowired;
    9. import org.springframework.web.bind.annotation.*;
    10. @RestController
    11. @RequestMapping("/system/role")
    12. public class RoleController {
    13. @Autowired
    14. private RoleService roleService;
    15. @GetMapping("/list")
    16. public ResponseResult selectPageRole(Role role,Integer pageNum,Integer pageSize){
    17. return roleService.selectPageRole(role,pageNum,pageSize);
    18. }
    19. @PutMapping("/changeStatus")
    20. public ResponseResult changeRoleStatus(@RequestBody ChangeRoleStatusDto changeRoleStatusDto){
    21. return roleService.changeRoleStatus(changeRoleStatusDto);
    22. }
    23. @PostMapping
    24. public ResponseResult addRole(@RequestBody AdminAddRoleDto adminAddRoleDto){
    25. return roleService.addRole(adminAddRoleDto);
    26. }
    27. @GetMapping("/{id}")
    28. public ResponseResult getRoleInfo(@PathVariable("id") Long id){
    29. return roleService.getRoleInfo(id);
    30. }
    31. @PutMapping
    32. public ResponseResult editRole(@RequestBody EditRoleDto editRoleDto){
    33. return roleService.editRole(editRoleDto);
    34. }
    35. @DeleteMapping("/{id}")
    36. public ResponseResult deleteRoleById(@PathVariable("id") Long id){
    37. roleService.removeById(id);
    38. return ResponseResult.okResult();
    39. }
    40. }

    5.3 测试

     只是逻辑删除

     十三、后台模块-用户列表

    1. 查询用户

    需要用户分页列表接口。

    可以根据用户名模糊搜索。

    可以进行手机号的搜索。

    可以进行状态的查询。

    1.1 接口分析

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

    请求参数query格式:  

    1. pageNum: 页码
    2. pageSize: 每页条数
    3. userName:用户名
    4. phonenumber:手机号
    5. status:状态

    响应格式:

    1. {
    2. "code":200,
    3. "data":{
    4. "rows":[
    5. {
    6. "avatar":"http://r7yxkqloa.bkt.clouddn.com/2022/03/05/75fd15587811443a9a9a771f24da458d.png",
    7. "createTime":"2022-01-05 17:01:56",
    8. "email":"23412332@qq.com",
    9. "id":"1",
    10. "nickName":"sg3334",
    11. "phonenumber":"18888888888",
    12. "sex":"1",
    13. "status":"0",
    14. "updateBy":"1",
    15. "updateTime":"2022-03-13 21:36:22",
    16. "userName":"sg"
    17. }
    18. ],
    19. "total":"1"
    20. },
    21. "msg":"操作成功"
    22. }

    1.2 代码实现

    第一步:keke-admin的controller层新建UserController

    1. package com.keke.controller;
    2. import com.keke.domain.ResponseResult;
    3. import com.keke.domain.entity.User;
    4. import com.keke.service.UserService;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.web.bind.annotation.GetMapping;
    7. import org.springframework.web.bind.annotation.RequestMapping;
    8. import org.springframework.web.bind.annotation.RestController;
    9. @RestController
    10. @RequestMapping("/system/user")
    11. public class UserController {
    12. @Autowired
    13. private UserService userService;
    14. @GetMapping("/list")
    15. public ResponseResult selectPageUser(User user, Integer pageNum, Integer pageSize){
    16. return userService.selectPageUser(user,pageNum,pageSize);
    17. }
    18. }

    第二步:service层

    1. package com.keke.service;
    2. import com.baomidou.mybatisplus.extension.service.IService;
    3. import com.keke.domain.ResponseResult;
    4. import com.keke.domain.dto.RegisterUserDto;
    5. import com.keke.domain.dto.UserInfoDto;
    6. import com.keke.domain.entity.User;
    7. import org.springframework.stereotype.Service;
    8. /**
    9. * 用户表(User)表服务接口
    10. *
    11. * @author makejava
    12. * @since 2023-10-13 09:08:38
    13. */
    14. public interface UserService extends IService {
    15. ResponseResult userInfo();
    16. ResponseResult updateUserInfo(UserInfoDto userInfoDto);
    17. ResponseResult register(RegisterUserDto registerUserDto);
    18. //后台接口分页查询用户
    19. ResponseResult selectPageUser(User user, Integer pageNum, Integer pageSize);
    20. }

    第三步:impl层

    1. package com.keke.service.impl;
    2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    3. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    4. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    5. import com.keke.domain.ResponseResult;
    6. import com.keke.domain.dto.RegisterUserDto;
    7. import com.keke.domain.dto.UserInfoDto;
    8. import com.keke.domain.entity.User;
    9. import com.keke.domain.vo.PageVo;
    10. import com.keke.domain.vo.UserInfoVo;
    11. import com.keke.enums.AppHttpCodeEnum;
    12. import com.keke.handler.exception.exception.SystemException;
    13. import com.keke.mapper.UserMapper;
    14. import com.keke.service.UserService;
    15. import com.keke.utils.BeanCopyUtils;
    16. import com.keke.utils.SecurityUtils;
    17. import org.springframework.beans.factory.annotation.Autowired;
    18. import org.springframework.security.crypto.password.PasswordEncoder;
    19. import org.springframework.stereotype.Service;
    20. import org.springframework.util.StringUtils;
    21. /**
    22. * 用户表(User)表服务实现类
    23. *
    24. * @author makejava
    25. * @since 2023-10-13 10:12:51
    26. */
    27. @Service("userService")
    28. public class UserServiceImpl extends ServiceImpl implements UserService {
    29. @Autowired
    30. private PasswordEncoder passwordEncoder;
    31. @Override
    32. public ResponseResult userInfo() {
    33. Long userId = SecurityUtils.getUserId();
    34. User user = getById(userId);
    35. UserInfoVo userInfoVo = BeanCopyUtils.copyBean(user, UserInfoVo.class);
    36. return ResponseResult.okResult(userInfoVo);
    37. }
    38. @Override
    39. public ResponseResult updateUserInfo(UserInfoDto userInfoDto) {
    40. User user = BeanCopyUtils.copyBean(userInfoDto, User.class);
    41. updateById(user);
    42. return ResponseResult.okResult();
    43. }
    44. @Override
    45. public ResponseResult register(RegisterUserDto registerUserDto) {
    46. //对前端传过来的用户名进行非空判断,例如null、"",就抛出异常
    47. if(!StringUtils.hasText(registerUserDto.getUserName())){
    48. //SystemException是我们写的异常类、AppHttpCodeEnum是我们写的枚举类
    49. throw new SystemException(AppHttpCodeEnum.USERNAME_NOT_NULL);
    50. }
    51. //密码
    52. if(!StringUtils.hasText(registerUserDto.getPassword())){
    53. throw new SystemException(AppHttpCodeEnum.PASSWORD_NOT_NULL);
    54. }
    55. //邮箱
    56. if(!StringUtils.hasText(registerUserDto.getEmail())){
    57. throw new SystemException(AppHttpCodeEnum.EMAIL_NOT_NULL);
    58. }
    59. //昵称
    60. if(!StringUtils.hasText(registerUserDto.getNickName())){
    61. throw new SystemException(AppHttpCodeEnum.NICKNAME_NOT_NULL);
    62. }
    63. //判断用户传给我们的用户名是否在数据库已经存在。userNameExist方法是下面定义的
    64. if(userNameExist(registerUserDto.getUserName())){
    65. //SystemException是我们写的异常类、AppHttpCodeEnum是我们写的枚举类
    66. throw new SystemException(AppHttpCodeEnum.USERNAME_EXIST);
    67. }
    68. //判断用户传给我们的昵称是否在数据库已经存在。NickNameExist方法是下面定义的
    69. if(nickNameExist(registerUserDto.getNickName())){
    70. //SystemException是我们写的异常类、AppHttpCodeEnum是我们写的枚举类
    71. throw new SystemException(AppHttpCodeEnum.NICKNAME_EXIST);
    72. }
    73. //判断用户传给我们的邮箱是否在数据库已经存在。NickNameExist方法是下面定义的
    74. if(emailExist(registerUserDto.getEmail())){
    75. //SystemException是我们写的异常类、AppHttpCodeEnum是我们写的枚举类
    76. throw new SystemException(AppHttpCodeEnum.EMAIL_EXIST);
    77. }
    78. //经过上面的判断,可以确保用户传给我们的用户名和昵称是数据库不存在的,且相关字段都不为空。就可以存入数据库
    79. //注意用户传给我们的密码是明文,对于密码我们要转成密文之后再存入数据库。注意加密要和解密用同一套算法
    80. //keke-blog工程的securityConfig类里面有解密算法,当时我们写了一个passwordEncoder方法,并且注入到了spring容器
    81. //解密
    82. String encodePassword = passwordEncoder.encode(registerUserDto.getPassword());
    83. //封装成user存数据库中
    84. User user = BeanCopyUtils.copyBean(registerUserDto, User.class);
    85. //设置密码为加密的密码
    86. user.setPassword(encodePassword);
    87. //存入数据库中
    88. save(user);
    89. //封装返回
    90. return ResponseResult.okResult();
    91. }
    92. @Override
    93. public ResponseResult selectPageUser(User user, Integer pageNum, Integer pageSize) {
    94. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    95. //可以根据用户名模糊搜索
    96. lambdaQueryWrapper.like(StringUtils.hasText(user.getUserName()),User::getUserName,user.getUserName());
    97. //可以进行手机号的搜索
    98. lambdaQueryWrapper.eq(StringUtils.hasText(user.getPhonenumber()),User::getPhonenumber,user.getPhonenumber());
    99. //可以根据状态进行搜索
    100. lambdaQueryWrapper.eq(StringUtils.hasText(user.getStatus()),User::getStatus,user.getStatus());
    101. //mp分页器
    102. Page page = new Page<>(pageNum,pageSize);
    103. page(page,lambdaQueryWrapper);
    104. //封装vo
    105. PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());
    106. //响应返回
    107. return ResponseResult.okResult(pageVo);
    108. }
    109. //'判断用户传给我们的用户名是否在数据库已经存在' 的方法
    110. public boolean userNameExist(String userName){
    111. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    112. //查询数据库中有用户名相同的数据没有
    113. lambdaQueryWrapper.eq(User::getUserName,userName);
    114. //如果查出来有,就说明存在,返回true
    115. int count = count(lambdaQueryWrapper);
    116. return count>0;
    117. }
    118. //'判断用户传给我们的昵称是否在数据库已经存在' 的方法
    119. public boolean nickNameExist(String nickName){
    120. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    121. //查询数据库中有用户名相同的数据没有
    122. lambdaQueryWrapper.eq(User::getNickName,nickName);
    123. //如果查出来有,就说明存在,返回true
    124. int count = count(lambdaQueryWrapper);
    125. return count>0;
    126. }
    127. //'判断用户传给我们的邮箱是否在数据库已经存在' 的方法
    128. public boolean emailExist(String email){
    129. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    130. //查询数据库中有用户名相同的数据没有
    131. lambdaQueryWrapper.eq(User::getEmail,email);
    132. //如果查出来有,就说明存在,返回true
    133. int count = count(lambdaQueryWrapper);
    134. return count>0;
    135. }
    136. }

     1.3 测试

    2. 新增用户

    需要新增用户功能。新增用户时可以直接关联角色。

    注意:新增用户时注意密码加密存储。

    用户名不能为空,否则提示:必需填写用户名

    用户名必须之前未存在,否则提示:用户名已存在

    手机号必须之前未存在,否则提示:手机号已存在

    邮箱必须之前未存在,否则提示:邮箱已存在

    2.1 查询角色列表接口

    注意:查询的是所有状态正常的角色

    2.1.1 接口分析
    请求方式请求路径是否需求token头
    GET/system/role/listAllRole

    响应格式:

    1. {
    2. "code":200,
    3. "data":[
    4. {
    5. "createBy":"0",
    6. "createTime":"2021-11-12 18:46:19",
    7. "delFlag":"0",
    8. "id":"1",
    9. "remark":"超级管理员",
    10. "roleKey":"admin",
    11. "roleName":"超级管理员",
    12. "roleSort":"1",
    13. "status":"0",
    14. "updateBy":"0"
    15. },
    16. {
    17. "createBy":"0",
    18. "createTime":"2021-11-12 18:46:19",
    19. "delFlag":"0",
    20. "id":"2",
    21. "remark":"普通角色",
    22. "roleKey":"common",
    23. "roleName":"普通角色",
    24. "roleSort":"2",
    25. "status":"0",
    26. "updateBy":"0",
    27. "updateTime":"2022-01-02 06:32:58"
    28. },
    29. {
    30. "createTime":"2022-01-06 22:07:40",
    31. "delFlag":"0",
    32. "id":"11",
    33. "remark":"嘎嘎嘎",
    34. "roleKey":"aggag",
    35. "roleName":"嘎嘎嘎",
    36. "roleSort":"5",
    37. "status":"0",
    38. "updateBy":"1",
    39. "updateTime":"2022-09-12 10:00:25"
    40. },
    41. {
    42. "createTime":"2022-01-16 14:49:30",
    43. "delFlag":"0",
    44. "id":"12",
    45. "roleKey":"link",
    46. "roleName":"友链审核员",
    47. "roleSort":"1",
    48. "status":"0",
    49. "updateTime":"2022-01-16 16:05:09"
    50. }
    51. ],
    52. "msg":"操作成功"
    53. }
    2.1.2 代码实现

    第一步:在keke-admin的controller层的RoleController新增查询状态正常角色的接口

    1. package com.keke.controller;
    2. import com.keke.domain.ResponseResult;
    3. import com.keke.domain.dto.AdminAddRoleDto;
    4. import com.keke.domain.dto.ChangeRoleStatusDto;
    5. import com.keke.domain.dto.EditRoleDto;
    6. import com.keke.domain.entity.Role;
    7. import com.keke.service.RoleService;
    8. import org.springframework.beans.factory.annotation.Autowired;
    9. import org.springframework.web.bind.annotation.*;
    10. @RestController
    11. @RequestMapping("/system/role")
    12. public class RoleController {
    13. @Autowired
    14. private RoleService roleService;
    15. @GetMapping("/list")
    16. public ResponseResult selectPageRole(Role role,Integer pageNum,Integer pageSize){
    17. return roleService.selectPageRole(role,pageNum,pageSize);
    18. }
    19. @PutMapping("/changeStatus")
    20. public ResponseResult changeRoleStatus(@RequestBody ChangeRoleStatusDto changeRoleStatusDto){
    21. return roleService.changeRoleStatus(changeRoleStatusDto);
    22. }
    23. @PostMapping
    24. public ResponseResult addRole(@RequestBody AdminAddRoleDto adminAddRoleDto){
    25. return roleService.addRole(adminAddRoleDto);
    26. }
    27. @GetMapping("/{id}")
    28. public ResponseResult getRoleInfo(@PathVariable("id") Long id){
    29. return roleService.getRoleInfo(id);
    30. }
    31. @PutMapping
    32. public ResponseResult editRole(@RequestBody EditRoleDto editRoleDto){
    33. return roleService.editRole(editRoleDto);
    34. }
    35. @DeleteMapping("/{id}")
    36. public ResponseResult deleteRoleById(@PathVariable("id") Long id){
    37. roleService.removeById(id);
    38. return ResponseResult.okResult();
    39. }
    40. @GetMapping("/listAllRole")
    41. public ResponseResult listAllRole(){
    42. return roleService.listAllRole();
    43. }
    44. }

    第二步:service层新增

    1. package com.keke.service;
    2. import com.baomidou.mybatisplus.extension.service.IService;
    3. import com.keke.domain.ResponseResult;
    4. import com.keke.domain.dto.AdminAddRoleDto;
    5. import com.keke.domain.dto.ChangeRoleStatusDto;
    6. import com.keke.domain.dto.EditRoleDto;
    7. import com.keke.domain.entity.Role;
    8. import java.util.List;
    9. /**
    10. * 角色信息表(Role)表服务接口
    11. *
    12. * @author makejava
    13. * @since 2023-10-18 21:04:06
    14. */
    15. public interface RoleService extends IService {
    16. List selectRoleKeyByUserId(Long userId);
    17. ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize);
    18. ResponseResult changeRoleStatus(ChangeRoleStatusDto changeRoleStatusDto);
    19. ResponseResult addRole(AdminAddRoleDto adminAddRoleDto);
    20. ResponseResult getRoleInfo(Long id);
    21. ResponseResult editRole(EditRoleDto editRoleDto);
    22. ResponseResult listAllRole();
    23. }

    第三步:impl层新增

    1. package com.keke.service.impl;
    2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    3. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    4. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    5. import com.keke.constants.SystemConstants;
    6. import com.keke.domain.ResponseResult;
    7. import com.keke.domain.dto.AdminAddRoleDto;
    8. import com.keke.domain.dto.ChangeRoleStatusDto;
    9. import com.keke.domain.dto.EditRoleDto;
    10. import com.keke.domain.entity.Role;
    11. import com.keke.domain.entity.RoleMenu;
    12. import com.keke.domain.vo.AdminRoleVo;
    13. import com.keke.domain.vo.PageVo;
    14. import com.keke.mapper.RoleMapper;
    15. import com.keke.service.RoleMenuService;
    16. import com.keke.service.RoleService;
    17. import com.keke.utils.BeanCopyUtils;
    18. import org.springframework.beans.factory.annotation.Autowired;
    19. import org.springframework.stereotype.Service;
    20. import org.springframework.transaction.annotation.Transactional;
    21. import org.springframework.util.StringUtils;
    22. import java.util.ArrayList;
    23. import java.util.List;
    24. import java.util.function.Function;
    25. import java.util.stream.Collectors;
    26. /**
    27. * 角色信息表(Role)表服务实现类
    28. *
    29. * @author makejava
    30. * @since 2023-10-18 21:04:06
    31. */
    32. @Service("roleService")
    33. public class RoleServiceImpl extends ServiceImpl implements RoleService {
    34. @Autowired
    35. private RoleMenuService roleMenuService;
    36. //根据用户id查询角色信息
    37. @Override
    38. public List selectRoleKeyByUserId(Long userId) {
    39. //如果userId为1,那么角色权限字符串就只需要返回一个admin
    40. if(userId==1L){
    41. List roles = new ArrayList<>();
    42. roles.add("admin");
    43. return roles;
    44. }
    45. //如果用户id不为1,那么需要根据userId连表查询对应的roleId,然后再去角色表中去查询
    46. //对应的角色权限字符串
    47. //这里我们期望RoleMapper中封装一个方法去帮我们实现这个复杂的操作
    48. RoleMapper roleMapper = getBaseMapper();
    49. return roleMapper.selectRoleKeyByUserId(userId);
    50. }
    51. @Override
    52. public ResponseResult selectPageRole(Role role, Integer pageNum, Integer pageSize) {
    53. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    54. lambdaQueryWrapper.like(StringUtils.hasText(role.getRoleName()),Role::getRoleName,role.getRoleName());
    55. lambdaQueryWrapper.eq(StringUtils.hasText(role.getStatus()),Role::getStatus,role.getStatus());
    56. lambdaQueryWrapper.orderByAsc(Role::getRoleSort);
    57. Page page = new Page<>(pageNum,pageSize);
    58. page(page,lambdaQueryWrapper);
    59. PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());
    60. return ResponseResult.okResult(pageVo);
    61. }
    62. @Override
    63. public ResponseResult changeRoleStatus(ChangeRoleStatusDto changeRoleStatusDto) {
    64. Long roleId = changeRoleStatusDto.getRoleId();
    65. Role role = getById(roleId);
    66. role.setStatus(changeRoleStatusDto.getStatus());
    67. updateById(role);
    68. return ResponseResult.okResult();
    69. }
    70. @Transactional
    71. @Override
    72. public ResponseResult addRole(AdminAddRoleDto adminAddRoleDto) {
    73. //Bean拷贝
    74. Role role = BeanCopyUtils.copyBean(adminAddRoleDto, Role.class);
    75. //拿到菜单权限id集合
    76. List menuIds = adminAddRoleDto.getMenuIds();
    77. //流式处理转化,把每一个menuId都设置到该roleId下
    78. List roleMenuList = menuIds.stream()
    79. .map(new Function() {
    80. @Override
    81. public RoleMenu apply(Long menuId) {
    82. RoleMenu roleMenu = new RoleMenu();
    83. roleMenu.setRoleId(role.getId());
    84. roleMenu.setMenuId(menuId);
    85. return roleMenu;
    86. }
    87. }).collect(Collectors.toList());
    88. //mp批量保存到数据库中
    89. roleMenuService.saveBatch(roleMenuList);
    90. //封装返回
    91. return ResponseResult.okResult();
    92. }
    93. @Override
    94. public ResponseResult getRoleInfo(Long id) {
    95. Role role = getById(id);
    96. AdminRoleVo adminRoleVo = BeanCopyUtils.copyBean(role, AdminRoleVo.class);
    97. return ResponseResult.okResult(adminRoleVo);
    98. }
    99. @Override
    100. public ResponseResult editRole(EditRoleDto editRoleDto) {
    101. Role role = BeanCopyUtils.copyBean(editRoleDto, Role.class);
    102. List menuIds = editRoleDto.getMenuIds();
    103. List roleMenuList = menuIds.stream()
    104. .map(new Function() {
    105. @Override
    106. public RoleMenu apply(Long menuId) {
    107. RoleMenu roleMenu = new RoleMenu();
    108. roleMenu.setRoleId(role.getId());
    109. roleMenu.setMenuId(menuId);
    110. return roleMenu;
    111. }
    112. }).collect(Collectors.toList());
    113. roleMenuService.saveBatch(roleMenuList);
    114. return ResponseResult.okResult();
    115. }
    116. @Override
    117. public ResponseResult listAllRole() {
    118. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    119. lambdaQueryWrapper.eq(Role::getStatus, SystemConstants.STATUS_NORMAL);
    120. List roleList = list(lambdaQueryWrapper);
    121. return ResponseResult.okResult(roleList);
    122. }
    123. }
    2.1.3 测试

    2.2 新增用户接口

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

    请求体:

    1. {
    2. "userName":"wqeree",
    3. "nickName":"测试新增用户",
    4. "password":"1234343",
    5. "phonenumber":"18889778907",
    6. "email":"233@sq.com",
    7. "sex":"0",
    8. "status":"0",
    9. "roleIds":[
    10. "2"
    11. ]
    12. }

    响应格式:

    1. {
    2. "code":200,
    3. "msg":"操作成功"
    4. }
    2.2.2 代码实现

    第一步:keke-framework的domain/entity新增

    1. package com.keke.domain.entity;
    2. import java.io.Serializable;
    3. import lombok.AllArgsConstructor;
    4. import lombok.Data;
    5. import lombok.NoArgsConstructor;
    6. import com.baomidou.mybatisplus.annotation.TableName;
    7. /**
    8. * 用户和角色关联表(UserRole)表实体类
    9. *
    10. * @author makejava
    11. * @since 2023-10-23 21:17:14
    12. */
    13. @SuppressWarnings("serial")
    14. @Data
    15. @NoArgsConstructor
    16. @AllArgsConstructor
    17. @TableName("sys_user_role")
    18. public class UserRole {
    19. //用户ID
    20. private Long userId;
    21. //角色ID
    22. private Long roleId;
    23. }

    第二步:keke-framework的service新增

    1. package com.keke.service;
    2. import com.baomidou.mybatisplus.extension.service.IService;
    3. /**
    4. * 用户和角色关联表(UserRole)表服务接口
    5. *
    6. * @author makejava
    7. * @since 2023-10-23 21:17:32
    8. */
    9. public interface UserRoleService extends IService {
    10. }

    第三步:keke-framework的service/impl新增

    1. package com.keke.service.impl;
    2. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    3. import com.keke.domain.entity.UserRole;
    4. import com.keke.mapper.UserRoleMapper;
    5. import com.keke.service.UserRoleService;
    6. import org.springframework.stereotype.Service;
    7. /**
    8. * 用户和角色关联表(UserRole)表服务实现类
    9. *
    10. * @author makejava
    11. * @since 2023-10-23 21:17:32
    12. */
    13. @Service("userRoleService")
    14. public class UserRoleServiceImpl extends ServiceImpl implements UserRoleService {
    15. }

    第四步:keke-framework的mapper新增

    1. package com.keke.mapper;
    2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    3. import com.keke.domain.entity.User;
    4. import org.apache.ibatis.annotations.Mapper;
    5. /**
    6. * 用户表(User)表数据库访问层
    7. *
    8. * @author makejava
    9. * @since 2023-10-11 20:26:26
    10. */
    11. @Mapper
    12. public interface UserMapper extends BaseMapper {
    13. }

    第五步:keke-framework的domain/dto新增

    1. package com.keke.domain.dto;
    2. import lombok.AllArgsConstructor;
    3. import lombok.Data;
    4. import lombok.NoArgsConstructor;
    5. import java.util.List;
    6. @Data
    7. @AllArgsConstructor
    8. @NoArgsConstructor
    9. public class AdminAddUserDto {
    10. //用户名
    11. private String userName;
    12. //昵称
    13. private String nickName;
    14. //密码
    15. private String password;
    16. //账号状态(0正常 1停用)
    17. private String status;
    18. //邮箱
    19. private String email;
    20. //手机号
    21. private String phonenumber;
    22. //用户性别(0男,1女,2未知)
    23. private String sex;
    24. //roleId
    25. List roleIds;
    26. }

    第六步:keke-admin的controller层UserController新增

    1. package com.keke.controller;
    2. import com.keke.domain.ResponseResult;
    3. import com.keke.domain.dto.AdminAddUserDto;
    4. import com.keke.domain.entity.User;
    5. import com.keke.service.UserService;
    6. import org.springframework.beans.factory.annotation.Autowired;
    7. import org.springframework.web.bind.annotation.*;
    8. @RestController
    9. @RequestMapping("/system/user")
    10. public class UserController {
    11. @Autowired
    12. private UserService userService;
    13. @GetMapping("/list")
    14. public ResponseResult selectPageUser(User user, Integer pageNum, Integer pageSize){
    15. return userService.selectPageUser(user,pageNum,pageSize);
    16. }
    17. @PostMapping
    18. public ResponseResult addUser(@RequestBody AdminAddUserDto adminAddUserDto){
    19. return userService.addUser(adminAddUserDto);
    20. }
    21. }

    第七步:service层UserService新增

    1. package com.keke.service;
    2. import com.baomidou.mybatisplus.extension.service.IService;
    3. import com.keke.domain.ResponseResult;
    4. import com.keke.domain.dto.AdminAddUserDto;
    5. import com.keke.domain.dto.RegisterUserDto;
    6. import com.keke.domain.dto.UserInfoDto;
    7. import com.keke.domain.entity.User;
    8. import org.springframework.stereotype.Service;
    9. /**
    10. * 用户表(User)表服务接口
    11. *
    12. * @author makejava
    13. * @since 2023-10-13 09:08:38
    14. */
    15. public interface UserService extends IService {
    16. ResponseResult userInfo();
    17. ResponseResult updateUserInfo(UserInfoDto userInfoDto);
    18. ResponseResult register(RegisterUserDto registerUserDto);
    19. //后台接口分页查询用户
    20. ResponseResult selectPageUser(User user, Integer pageNum, Integer pageSize);
    21. ResponseResult addUser(AdminAddUserDto adminAddUserDto);
    22. }

    第八步:impl层实现方法

    1. package com.keke.service.impl;
    2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    3. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    4. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    5. import com.keke.domain.ResponseResult;
    6. import com.keke.domain.dto.AdminAddUserDto;
    7. import com.keke.domain.dto.RegisterUserDto;
    8. import com.keke.domain.dto.UserInfoDto;
    9. import com.keke.domain.entity.User;
    10. import com.keke.domain.entity.UserRole;
    11. import com.keke.domain.vo.PageVo;
    12. import com.keke.domain.vo.UserInfoVo;
    13. import com.keke.enums.AppHttpCodeEnum;
    14. import com.keke.handler.exception.exception.SystemException;
    15. import com.keke.mapper.UserMapper;
    16. import com.keke.service.UserRoleService;
    17. import com.keke.service.UserService;
    18. import com.keke.utils.BeanCopyUtils;
    19. import com.keke.utils.SecurityUtils;
    20. import org.springframework.beans.factory.annotation.Autowired;
    21. import org.springframework.security.crypto.password.PasswordEncoder;
    22. import org.springframework.stereotype.Service;
    23. import org.springframework.util.StringUtils;
    24. import java.util.List;
    25. import java.util.function.Function;
    26. import java.util.stream.Collectors;
    27. /**
    28. * 用户表(User)表服务实现类
    29. *
    30. * @author makejava
    31. * @since 2023-10-13 10:12:51
    32. */
    33. @Service("userService")
    34. public class UserServiceImpl extends ServiceImpl implements UserService {
    35. @Autowired
    36. private PasswordEncoder passwordEncoder;
    37. @Autowired
    38. private UserRoleService userRoleService;
    39. @Override
    40. public ResponseResult userInfo() {
    41. Long userId = SecurityUtils.getUserId();
    42. User user = getById(userId);
    43. UserInfoVo userInfoVo = BeanCopyUtils.copyBean(user, UserInfoVo.class);
    44. return ResponseResult.okResult(userInfoVo);
    45. }
    46. @Override
    47. public ResponseResult updateUserInfo(UserInfoDto userInfoDto) {
    48. User user = BeanCopyUtils.copyBean(userInfoDto, User.class);
    49. updateById(user);
    50. return ResponseResult.okResult();
    51. }
    52. @Override
    53. public ResponseResult register(RegisterUserDto registerUserDto) {
    54. //对前端传过来的用户名进行非空判断,例如null、"",就抛出异常
    55. if(!StringUtils.hasText(registerUserDto.getUserName())){
    56. //SystemException是我们写的异常类、AppHttpCodeEnum是我们写的枚举类
    57. throw new SystemException(AppHttpCodeEnum.USERNAME_NOT_NULL);
    58. }
    59. //密码
    60. if(!StringUtils.hasText(registerUserDto.getPassword())){
    61. throw new SystemException(AppHttpCodeEnum.PASSWORD_NOT_NULL);
    62. }
    63. //邮箱
    64. if(!StringUtils.hasText(registerUserDto.getEmail())){
    65. throw new SystemException(AppHttpCodeEnum.EMAIL_NOT_NULL);
    66. }
    67. //昵称
    68. if(!StringUtils.hasText(registerUserDto.getNickName())){
    69. throw new SystemException(AppHttpCodeEnum.NICKNAME_NOT_NULL);
    70. }
    71. //判断用户传给我们的用户名是否在数据库已经存在。userNameExist方法是下面定义的
    72. if(userNameExist(registerUserDto.getUserName())){
    73. //SystemException是我们写的异常类、AppHttpCodeEnum是我们写的枚举类
    74. throw new SystemException(AppHttpCodeEnum.USERNAME_EXIST);
    75. }
    76. //判断用户传给我们的昵称是否在数据库已经存在。NickNameExist方法是下面定义的
    77. if(nickNameExist(registerUserDto.getNickName())){
    78. //SystemException是我们写的异常类、AppHttpCodeEnum是我们写的枚举类
    79. throw new SystemException(AppHttpCodeEnum.NICKNAME_EXIST);
    80. }
    81. //判断用户传给我们的邮箱是否在数据库已经存在。NickNameExist方法是下面定义的
    82. if(emailExist(registerUserDto.getEmail())){
    83. //SystemException是我们写的异常类、AppHttpCodeEnum是我们写的枚举类
    84. throw new SystemException(AppHttpCodeEnum.EMAIL_EXIST);
    85. }
    86. //经过上面的判断,可以确保用户传给我们的用户名和昵称是数据库不存在的,且相关字段都不为空。就可以存入数据库
    87. //注意用户传给我们的密码是明文,对于密码我们要转成密文之后再存入数据库。注意加密要和解密用同一套算法
    88. //keke-blog工程的securityConfig类里面有解密算法,当时我们写了一个passwordEncoder方法,并且注入到了spring容器
    89. //解密
    90. String encodePassword = passwordEncoder.encode(registerUserDto.getPassword());
    91. //封装成user存数据库中
    92. User user = BeanCopyUtils.copyBean(registerUserDto, User.class);
    93. //设置密码为加密的密码
    94. user.setPassword(encodePassword);
    95. //存入数据库中
    96. save(user);
    97. //封装返回
    98. return ResponseResult.okResult();
    99. }
    100. @Override
    101. public ResponseResult selectPageUser(User user, Integer pageNum, Integer pageSize) {
    102. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    103. //可以根据用户名模糊搜索
    104. lambdaQueryWrapper.like(StringUtils.hasText(user.getUserName()),User::getUserName,user.getUserName());
    105. //可以进行手机号的搜索
    106. lambdaQueryWrapper.eq(StringUtils.hasText(user.getPhonenumber()),User::getPhonenumber,user.getPhonenumber());
    107. //可以根据状态进行搜索
    108. lambdaQueryWrapper.eq(StringUtils.hasText(user.getStatus()),User::getStatus,user.getStatus());
    109. //mp分页器
    110. Page page = new Page<>(pageNum,pageSize);
    111. page(page,lambdaQueryWrapper);
    112. //封装vo
    113. PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());
    114. //响应返回
    115. return ResponseResult.okResult(pageVo);
    116. }
    117. @Override
    118. public ResponseResult addUser(AdminAddUserDto adminAddUserDto) {
    119. User user = BeanCopyUtils.copyBean(adminAddUserDto, User.class);
    120. List roleIds = adminAddUserDto.getRoleIds();
    121. List userRoleList = roleIds.stream()
    122. .map(new Function() {
    123. @Override
    124. public UserRole apply(Long roleId) {
    125. UserRole userRole = new UserRole();
    126. userRole.setUserId(user.getId());
    127. userRole.setRoleId(roleId);
    128. return userRole;
    129. }
    130. }).collect(Collectors.toList());
    131. userRoleService.saveBatch(userRoleList);
    132. return ResponseResult.okResult();
    133. }
    134. //'判断用户传给我们的用户名是否在数据库已经存在' 的方法
    135. public boolean userNameExist(String userName){
    136. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    137. //查询数据库中有用户名相同的数据没有
    138. lambdaQueryWrapper.eq(User::getUserName,userName);
    139. //如果查出来有,就说明存在,返回true
    140. int count = count(lambdaQueryWrapper);
    141. return count>0;
    142. }
    143. //'判断用户传给我们的昵称是否在数据库已经存在' 的方法
    144. public boolean nickNameExist(String nickName){
    145. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    146. //查询数据库中有用户名相同的数据没有
    147. lambdaQueryWrapper.eq(User::getNickName,nickName);
    148. //如果查出来有,就说明存在,返回true
    149. int count = count(lambdaQueryWrapper);
    150. return count>0;
    151. }
    152. //'判断用户传给我们的邮箱是否在数据库已经存在' 的方法
    153. public boolean emailExist(String email){
    154. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    155. //查询数据库中有用户名相同的数据没有
    156. lambdaQueryWrapper.eq(User::getEmail,email);
    157. //如果查出来有,就说明存在,返回true
    158. int count = count(lambdaQueryWrapper);
    159. return count>0;
    160. }
    161. }
    2.2.3 测试

    十四、后台模块-分类列表

    1. 查询分类

    需要分页查询分类列表。

    能根据分类名称进行模糊查询。

    能根据状态进行查询。

    1.1 接口分析

    请求方式请求路径是否需求token头
    GETcontent/category/list

    Query格式请求参数:

    1. pageNum: 页码
    2. pageSize: 每页条数
    3. name:分类名
    4. status: 状态

     响应格式:

    1. {
    2. "code":200,
    3. "data":{
    4. "rows":[
    5. {
    6. "description":"wsd",
    7. "id":"1",
    8. "name":"java",
    9. "status":"0"
    10. },
    11. {
    12. "description":"wsd",
    13. "id":"2",
    14. "name":"PHP",
    15. "status":"0"
    16. }
    17. ],
    18. "total":"2"
    19. },
    20. "msg":"操作成功"
    21. }

    1.2 代码实现

    第一步:keke-admin controller层

    1. package com.keke.controller;
    2. import com.alibaba.excel.EasyExcel;
    3. import com.alibaba.fastjson.JSON;
    4. import com.keke.domain.ResponseResult;
    5. import com.keke.domain.entity.Category;
    6. import com.keke.domain.vo.ExcelCategoryVo;
    7. import com.keke.enums.AppHttpCodeEnum;
    8. import com.keke.service.CategoryService;
    9. import com.keke.utils.BeanCopyUtils;
    10. import com.keke.utils.WebUtils;
    11. import io.swagger.annotations.Api;
    12. import org.springframework.beans.factory.annotation.Autowired;
    13. import org.springframework.security.access.prepost.PreAuthorize;
    14. import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
    15. import org.springframework.web.bind.annotation.GetMapping;
    16. import org.springframework.web.bind.annotation.RequestMapping;
    17. import org.springframework.web.bind.annotation.RestController;
    18. import javax.servlet.http.HttpServletResponse;
    19. import java.io.UnsupportedEncodingException;
    20. import java.util.List;
    21. @RestController
    22. @RequestMapping("/content/category")
    23. @Api(tags = "后台标签相关接口")
    24. public class CategoryController {
    25. @Autowired
    26. private CategoryService categoryService;
    27. @GetMapping("/listAllCategory")
    28. public ResponseResult listAllCategory(){
    29. return categoryService.listAllCategory();
    30. }
    31. //权限控制,ps是PermissionService类的bean名称
    32. @PreAuthorize("@ps.hasPermission('content:category:export')")
    33. @GetMapping("/export")
    34. public void export(HttpServletResponse response){
    35. try {
    36. //设置下载文件的请求头
    37. WebUtils.setDownLoadHeader("分类.xlsx",response);
    38. //获取需要导出的数据
    39. List categoryList = categoryService.list();
    40. List excelCategoryVos = BeanCopyUtils.copyBeanList(categoryList, ExcelCategoryVo.class);
    41. //把数据写入Excel中
    42. EasyExcel.write(response.getOutputStream(), ExcelCategoryVo.class).autoCloseStream(Boolean.FALSE).sheet("分类导出")
    43. .doWrite(excelCategoryVos);
    44. } catch (Exception e) {
    45. //如果出现异常,就返回失败的json数据给前端
    46. ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR);
    47. //WebUtils是我们在keke-framework工程写的类,里面的renderString方法是将json字符串写入到请求体,然后返回给前端
    48. WebUtils.renderString(response, JSON.toJSONString(result));
    49. }
    50. }
    51. @GetMapping("/list")
    52. public ResponseResult selectPageCategory(Category category,Integer pageNum,Integer pageSize){
    53. return categoryService.selectPageCategory(category,pageNum,pageSize);
    54. }
    55. }

    第二步:keke-framework service层

    1. package com.keke.service;
    2. import com.baomidou.mybatisplus.extension.service.IService;
    3. import com.keke.domain.ResponseResult;
    4. import com.keke.domain.entity.Category;
    5. import org.springframework.web.bind.annotation.RequestMapping;
    6. /**
    7. * 分类表(Category)表服务接口
    8. *
    9. * @author makejava
    10. * @since 2023-10-10 20:42:22
    11. */
    12. public interface CategoryService extends IService {
    13. ResponseResult getCategoryList();
    14. //后台接口,查询所有文章分类
    15. ResponseResult listAllCategory();
    16. ResponseResult selectPageCategory(Category category, Integer pageNum, Integer pageSize);
    17. }

     第三步:keke-framework impl层 实现selectPageCategory

    1. package com.keke.service.impl;
    2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    3. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    4. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    5. import com.keke.constants.SystemConstants;
    6. import com.keke.domain.ResponseResult;
    7. import com.keke.domain.entity.Article;
    8. import com.keke.domain.entity.Category;
    9. import com.keke.domain.vo.AdminCategoryVo;
    10. import com.keke.domain.vo.CategoryVo;
    11. import com.keke.domain.vo.PageVo;
    12. import com.keke.mapper.CategoryMapper;
    13. import com.keke.service.ArticleService;
    14. import com.keke.service.CategoryService;
    15. import com.keke.utils.BeanCopyUtils;
    16. import org.springframework.beans.factory.annotation.Autowired;
    17. import org.springframework.stereotype.Service;
    18. import org.springframework.util.StringUtils;
    19. import org.springframework.web.bind.annotation.RequestMapping;
    20. import java.util.List;
    21. import java.util.Set;
    22. import java.util.function.Function;
    23. import java.util.stream.Collectors;
    24. /**
    25. * 分类表(Category)表服务实现类
    26. *
    27. * @author makejava
    28. * @since 2023-10-10 20:42:22
    29. */
    30. @Service("categoryService")
    31. public class CategoryServiceImpl extends ServiceImpl implements CategoryService {
    32. @Autowired
    33. private ArticleService articleService;
    34. @Override
    35. public ResponseResult getCategoryList() {
    36. //查询文章表,状态已发布的文章,但是在CategoryService下,查询文章表,就要注入ArticleService
    37. LambdaQueryWrapper
      articleWrapper = new LambdaQueryWrapper<>();
    38. articleWrapper.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL);
    39. List
      articleList = articleService.list(articleWrapper);
    40. //获取文章的分类id,并去重
    41. Set categoryIds = articleList.stream()
    42. .map(article -> article.getCategoryId())
    43. //toSet可以去除重复的id
    44. .collect(Collectors.toSet());
    45. //查询分类表
    46. List categories = listByIds(categoryIds);
    47. //分类表中只获取正常状态非禁用的分类,用stream流过滤
    48. categories = categories.stream()
    49. .filter(category -> SystemConstants.STATUS_NORMAL.equals(category.getStatus()))
    50. .collect(Collectors.toList());
    51. //封装Vo
    52. List categoryVos = BeanCopyUtils.copyBeanList(categories, CategoryVo.class);
    53. //封装到响应体中,因为有数据,所以要调用有参okResult(),把参数传进去
    54. return ResponseResult.okResult(categoryVos);
    55. }
    56. @Override
    57. public ResponseResult listAllCategory() {
    58. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    59. lambdaQueryWrapper.eq(Category::getStatus,SystemConstants.STATUS_NORMAL);
    60. List categoryList = list(lambdaQueryWrapper);
    61. List adminCategoryVos = BeanCopyUtils.copyBeanList(categoryList, AdminCategoryVo.class);
    62. return ResponseResult.okResult(adminCategoryVos);
    63. }
    64. @Override
    65. public ResponseResult selectPageCategory(Category category, Integer pageNum, Integer pageSize) {
    66. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    67. //根据分类名进行模糊查询
    68. lambdaQueryWrapper.like(StringUtils.hasText(category.getName()),Category::getName,category.getName());
    69. lambdaQueryWrapper.eq(StringUtils.hasText(category.getStatus()),Category::getStatus,category.getStatus());
    70. //构造分页器
    71. Page page = new Page<>(pageNum,pageSize);
    72. page(page,lambdaQueryWrapper);
    73. //封装Vo
    74. PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());
    75. return ResponseResult.okResult(pageVo);
    76. }
    77. }

     1.3 测试

    2. 新增分类

    需要新增分类功能

    2.1 接口分析

    请求方式请求路径是否需求token头
    POST/content/category

    请求体:

    1. {
    2. "name":"威威",
    3. "description":"是的",
    4. "status":"0"
    5. }

    响应格式:

    1. {
    2. "code":200,
    3. "msg":"操作成功"
    4. }

    2.2 代码实现

    第一步:新增dto,接受前端传来的参数

    1. package com.keke.domain.dto;
    2. import lombok.AllArgsConstructor;
    3. import lombok.Data;
    4. import lombok.NoArgsConstructor;
    5. import java.util.Date;
    6. @Data
    7. @AllArgsConstructor
    8. @NoArgsConstructor
    9. public class AdminCategoryDto {
    10. //分类名
    11. private String name;
    12. //描述
    13. private String description;
    14. //状态0:正常,1禁用
    15. private String status;
    16. }

    第二步:entity层Category修改mp自填充

    1. package com.keke.domain.entity;
    2. import java.util.Date;
    3. import java.io.Serializable;
    4. import com.baomidou.mybatisplus.annotation.FieldFill;
    5. import com.baomidou.mybatisplus.annotation.TableField;
    6. import com.baomidou.mybatisplus.annotation.TableId;
    7. import com.baomidou.mybatisplus.annotation.TableName;
    8. import lombok.AllArgsConstructor;
    9. import lombok.Data;
    10. import lombok.NoArgsConstructor;
    11. /**
    12. * 分类表(Category)表实体类
    13. *
    14. * @author makejava
    15. * @since 2023-10-10 20:42:21
    16. */
    17. @SuppressWarnings("serial")
    18. @Data
    19. @NoArgsConstructor
    20. @AllArgsConstructor
    21. @TableName("ke_category")
    22. public class Category {
    23. @TableId
    24. private Long id;
    25. //分类名
    26. private String name;
    27. //父分类id,如果没有父分类为-1
    28. private Long pid;
    29. //描述
    30. private String description;
    31. //状态0:正常,1禁用
    32. private String status;
    33. //创建者
    34. @TableField(fill = FieldFill.INSERT)
    35. private Long createBy;
    36. //创建时间
    37. @TableField(fill = FieldFill.INSERT)
    38. private Date createTime;
    39. //更新者
    40. @TableField(fill = FieldFill.INSERT_UPDATE)
    41. private Long updateBy;
    42. //更新时间
    43. @TableField(fill = FieldFill.INSERT_UPDATE)
    44. private Date updateTime;
    45. //删除标志(0代表未删除,1代表已删除)
    46. private Integer delFlag;
    47. }

    第三步: keke-admin的Controller层

    1. package com.keke.controller;
    2. import com.alibaba.excel.EasyExcel;
    3. import com.alibaba.fastjson.JSON;
    4. import com.keke.domain.ResponseResult;
    5. import com.keke.domain.dto.AdminCategoryDto;
    6. import com.keke.domain.entity.Category;
    7. import com.keke.domain.vo.ExcelCategoryVo;
    8. import com.keke.enums.AppHttpCodeEnum;
    9. import com.keke.service.CategoryService;
    10. import com.keke.utils.BeanCopyUtils;
    11. import com.keke.utils.WebUtils;
    12. import io.swagger.annotations.Api;
    13. import org.springframework.beans.factory.annotation.Autowired;
    14. import org.springframework.security.access.prepost.PreAuthorize;
    15. import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
    16. import org.springframework.web.bind.annotation.*;
    17. import javax.servlet.http.HttpServletResponse;
    18. import java.io.UnsupportedEncodingException;
    19. import java.util.List;
    20. @RestController
    21. @RequestMapping("/content/category")
    22. @Api(tags = "后台标签相关接口")
    23. public class CategoryController {
    24. @Autowired
    25. private CategoryService categoryService;
    26. @GetMapping("/listAllCategory")
    27. public ResponseResult listAllCategory(){
    28. return categoryService.listAllCategory();
    29. }
    30. //权限控制,ps是PermissionService类的bean名称
    31. @PreAuthorize("@ps.hasPermission('content:category:export')")
    32. @GetMapping("/export")
    33. public void export(HttpServletResponse response){
    34. try {
    35. //设置下载文件的请求头
    36. WebUtils.setDownLoadHeader("分类.xlsx",response);
    37. //获取需要导出的数据
    38. List categoryList = categoryService.list();
    39. List excelCategoryVos = BeanCopyUtils.copyBeanList(categoryList, ExcelCategoryVo.class);
    40. //把数据写入Excel中
    41. EasyExcel.write(response.getOutputStream(), ExcelCategoryVo.class).autoCloseStream(Boolean.FALSE).sheet("分类导出")
    42. .doWrite(excelCategoryVos);
    43. } catch (Exception e) {
    44. //如果出现异常,就返回失败的json数据给前端
    45. ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR);
    46. //WebUtils是我们在keke-framework工程写的类,里面的renderString方法是将json字符串写入到请求体,然后返回给前端
    47. WebUtils.renderString(response, JSON.toJSONString(result));
    48. }
    49. }
    50. @GetMapping("/list")
    51. public ResponseResult selectPageCategory(Category category,Integer pageNum,Integer pageSize){
    52. return categoryService.selectPageCategory(category,pageNum,pageSize);
    53. }
    54. @PostMapping
    55. public ResponseResult addCategory(@RequestBody AdminCategoryDto adminCategoryDto){
    56. return categoryService.addCategory(adminCategoryDto);
    57. }
    58. }

    第四步:keke-framework的service层 

    1. package com.keke.service;
    2. import com.baomidou.mybatisplus.extension.service.IService;
    3. import com.keke.domain.ResponseResult;
    4. import com.keke.domain.dto.AdminCategoryDto;
    5. import com.keke.domain.entity.Category;
    6. import org.springframework.web.bind.annotation.RequestMapping;
    7. /**
    8. * 分类表(Category)表服务接口
    9. *
    10. * @author makejava
    11. * @since 2023-10-10 20:42:22
    12. */
    13. public interface CategoryService extends IService {
    14. ResponseResult getCategoryList();
    15. //后台接口,查询所有文章分类
    16. ResponseResult listAllCategory();
    17. ResponseResult selectPageCategory(Category category, Integer pageNum, Integer pageSize);
    18. ResponseResult addCategory(AdminCategoryDto adminCategoryDto);
    19. }

     第五步:keke-framework的impl层 

    1. package com.keke.service.impl;
    2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    3. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    4. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    5. import com.keke.constants.SystemConstants;
    6. import com.keke.domain.ResponseResult;
    7. import com.keke.domain.dto.AdminCategoryDto;
    8. import com.keke.domain.entity.Article;
    9. import com.keke.domain.entity.Category;
    10. import com.keke.domain.vo.AdminCategoryVo;
    11. import com.keke.domain.vo.CategoryVo;
    12. import com.keke.domain.vo.PageVo;
    13. import com.keke.mapper.CategoryMapper;
    14. import com.keke.service.ArticleService;
    15. import com.keke.service.CategoryService;
    16. import com.keke.utils.BeanCopyUtils;
    17. import org.springframework.beans.factory.annotation.Autowired;
    18. import org.springframework.stereotype.Service;
    19. import org.springframework.util.StringUtils;
    20. import org.springframework.web.bind.annotation.RequestMapping;
    21. import java.util.List;
    22. import java.util.Set;
    23. import java.util.function.Function;
    24. import java.util.stream.Collectors;
    25. /**
    26. * 分类表(Category)表服务实现类
    27. *
    28. * @author makejava
    29. * @since 2023-10-10 20:42:22
    30. */
    31. @Service("categoryService")
    32. public class CategoryServiceImpl extends ServiceImpl implements CategoryService {
    33. @Autowired
    34. private ArticleService articleService;
    35. @Override
    36. public ResponseResult getCategoryList() {
    37. //查询文章表,状态已发布的文章,但是在CategoryService下,查询文章表,就要注入ArticleService
    38. LambdaQueryWrapper
      articleWrapper = new LambdaQueryWrapper<>();
    39. articleWrapper.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL);
    40. List
      articleList = articleService.list(articleWrapper);
    41. //获取文章的分类id,并去重
    42. Set categoryIds = articleList.stream()
    43. .map(article -> article.getCategoryId())
    44. //toSet可以去除重复的id
    45. .collect(Collectors.toSet());
    46. //查询分类表
    47. List categories = listByIds(categoryIds);
    48. //分类表中只获取正常状态非禁用的分类,用stream流过滤
    49. categories = categories.stream()
    50. .filter(category -> SystemConstants.STATUS_NORMAL.equals(category.getStatus()))
    51. .collect(Collectors.toList());
    52. //封装Vo
    53. List categoryVos = BeanCopyUtils.copyBeanList(categories, CategoryVo.class);
    54. //封装到响应体中,因为有数据,所以要调用有参okResult(),把参数传进去
    55. return ResponseResult.okResult(categoryVos);
    56. }
    57. @Override
    58. public ResponseResult listAllCategory() {
    59. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    60. lambdaQueryWrapper.eq(Category::getStatus,SystemConstants.STATUS_NORMAL);
    61. List categoryList = list(lambdaQueryWrapper);
    62. List adminCategoryVos = BeanCopyUtils.copyBeanList(categoryList, AdminCategoryVo.class);
    63. return ResponseResult.okResult(adminCategoryVos);
    64. }
    65. @Override
    66. public ResponseResult selectPageCategory(Category category, Integer pageNum, Integer pageSize) {
    67. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    68. //根据分类名进行模糊查询
    69. lambdaQueryWrapper.like(StringUtils.hasText(category.getName()),Category::getName,category.getName());
    70. lambdaQueryWrapper.eq(StringUtils.hasText(category.getStatus()),Category::getStatus,category.getStatus());
    71. //构造分页器
    72. Page page = new Page<>(pageNum,pageSize);
    73. page(page,lambdaQueryWrapper);
    74. //封装Vo
    75. PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());
    76. return ResponseResult.okResult(pageVo);
    77. }
    78. @Override
    79. public ResponseResult addCategory(AdminCategoryDto adminCategoryDto) {
    80. Category category = BeanCopyUtils.copyBean(adminCategoryDto, Category.class);
    81. save(category);
    82. return ResponseResult.okResult();
    83. }
    84. }

    2.3 测试

    3. 修改分类

    需要提供修改分类的功能

    分析:点击修改按钮,需要回显出分类的相关信息

               点击确定按钮,修改成功

    所以这里需要实现两个接口

    3.1 回显分类信息接口

    3.1.1 接口分析

    根据id查询分类

    请求方式请求路径是否需求token头
    Getcontent/category/{id}
    3.1.2 代码实现

    逻辑较少,直接在Controller层写逻辑

    1. package com.keke.controller;
    2. import com.alibaba.excel.EasyExcel;
    3. import com.alibaba.fastjson.JSON;
    4. import com.keke.domain.ResponseResult;
    5. import com.keke.domain.dto.AdminCategoryDto;
    6. import com.keke.domain.entity.Category;
    7. import com.keke.domain.vo.ExcelCategoryVo;
    8. import com.keke.enums.AppHttpCodeEnum;
    9. import com.keke.service.CategoryService;
    10. import com.keke.utils.BeanCopyUtils;
    11. import com.keke.utils.WebUtils;
    12. import io.swagger.annotations.Api;
    13. import org.springframework.beans.factory.annotation.Autowired;
    14. import org.springframework.security.access.prepost.PreAuthorize;
    15. import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
    16. import org.springframework.web.bind.annotation.*;
    17. import javax.servlet.http.HttpServletResponse;
    18. import java.io.UnsupportedEncodingException;
    19. import java.util.List;
    20. @RestController
    21. @RequestMapping("/content/category")
    22. @Api(tags = "后台标签相关接口")
    23. public class CategoryController {
    24. @Autowired
    25. private CategoryService categoryService;
    26. @GetMapping("/listAllCategory")
    27. public ResponseResult listAllCategory(){
    28. return categoryService.listAllCategory();
    29. }
    30. //权限控制,ps是PermissionService类的bean名称
    31. @PreAuthorize("@ps.hasPermission('content:category:export')")
    32. @GetMapping("/export")
    33. public void export(HttpServletResponse response){
    34. try {
    35. //设置下载文件的请求头
    36. WebUtils.setDownLoadHeader("分类.xlsx",response);
    37. //获取需要导出的数据
    38. List categoryList = categoryService.list();
    39. List excelCategoryVos = BeanCopyUtils.copyBeanList(categoryList, ExcelCategoryVo.class);
    40. //把数据写入Excel中
    41. EasyExcel.write(response.getOutputStream(), ExcelCategoryVo.class).autoCloseStream(Boolean.FALSE).sheet("分类导出")
    42. .doWrite(excelCategoryVos);
    43. } catch (Exception e) {
    44. //如果出现异常,就返回失败的json数据给前端
    45. ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR);
    46. //WebUtils是我们在keke-framework工程写的类,里面的renderString方法是将json字符串写入到请求体,然后返回给前端
    47. WebUtils.renderString(response, JSON.toJSONString(result));
    48. }
    49. }
    50. @GetMapping("/list")
    51. public ResponseResult selectPageCategory(Category category,Integer pageNum,Integer pageSize){
    52. return categoryService.selectPageCategory(category,pageNum,pageSize);
    53. }
    54. @PostMapping
    55. public ResponseResult addCategory(@RequestBody AdminCategoryDto adminCategoryDto){
    56. return categoryService.addCategory(adminCategoryDto);
    57. }
    58. @GetMapping("/{id}")
    59. public ResponseResult getInfoById(@PathVariable("id") Long id){
    60. Category category = categoryService.getById(id);
    61. return ResponseResult.okResult(category);
    62. }
    63. }
    3.1.3 测试

    成功回显出分类信息

    3.2 修改分类接口

    3.2.1 接口分析
    请求方式请求路径是否需求token头
    PUT/content/category

     请求体:

    1. {
    2. "description":"是的",
    3. "id":"3",
    4. "name":"威威2",
    5. "status":"0"
    6. }

     响应格式:

    1. {
    2. "code":200,
    3. "msg":"操作成功"
    4. }
    3.2.2 代码实现

    逻辑较少,直接在controller层实现

    1. @PutMapping
    2. public ResponseResult editCategory(@RequestBody Category category){
    3. categoryService.updateById(category);
    4. return ResponseResult.okResult();
    5. }
    3.2.3 测试

    4. 删除分类

    删除某个分类(逻辑删除)

    4.1 接口分析

    请求方式请求路径是否需求token头
    DELETE/content/category/{id}

    请求参数PathVariable格式:

    id:要删除的分类id

    响应格式:  

    1. {
    2. "code":200,
    3. "msg":"操作成功"
    4. }

     4.2 代码实现

    逻辑较少,直接在controller层实现

    1. @DeleteMapping("/{id}")
    2. public ResponseResult deleteCategory(@PathVariable("/id") Long id){
    3. categoryService.removeById(id);
    4. return ResponseResult.okResult();
    5. }

    4.3 测试

     

    十五、后台模块-友链列表

    1.  查询友链

    需要分页查询友链列表。

    能根据友链名称进行模糊查询。

    能根据状态进行查询。

    1.1 接口分析

    请求方式请求路径是否需求token头
    GET/content/link/list

    Query格式请求参数:

    1. pageNum: 页码
    2. pageSize: 每页条数
    3. name:友链名
    4. status:状态

     响应格式:

    1. {
    2. "code":200,
    3. "data":{
    4. "rows":[
    5. {
    6. "address":"https://www.baidu.com",
    7. "description":"sda",
    8. "id":"1",
    9. "logo":"图片URL",
    10. "name":"sda",
    11. "status":"0"
    12. }
    13. ],
    14. "total":"1"
    15. },
    16. "msg":"操作成功"
    17. }

     1.2 代码实现

    第一步:keke-admin的controller层新建LinkController

    1. package com.keke.controller;
    2. import com.keke.domain.ResponseResult;
    3. import com.keke.domain.entity.Link;
    4. import com.keke.service.LinkService;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.web.bind.annotation.GetMapping;
    7. import org.springframework.web.bind.annotation.RequestMapping;
    8. import org.springframework.web.bind.annotation.RestController;
    9. @RestController
    10. @RequestMapping("/system/link")
    11. public class LinkController {
    12. @Autowired
    13. private LinkService linkService;
    14. @GetMapping("/list")
    15. public ResponseResult selectPageLink(Link link, Integer pageNum, Integer pageSize){
    16. return linkService.selectPageLink(link,pageNum,pageSize);
    17. }
    18. }

    第二步:keke-framework的service层新增方法

    1. package com.keke.service;
    2. import com.keke.domain.ResponseResult;
    3. import com.keke.domain.entity.Link;
    4. import com.baomidou.mybatisplus.extension.service.IService;
    5. /**
    6. * 友链(Link)表服务接口
    7. *
    8. * @author makejava
    9. * @since 2023-10-11 15:46:23
    10. */
    11. public interface LinkService extends IService {
    12. ResponseResult getAllLink();
    13. ResponseResult selectPageLink(Link link, Integer pageNum, Integer pageSize);
    14. }

    第三步:keke-framework的impl层实现方法

    1. package com.keke.service.impl;
    2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    3. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    4. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    5. import com.keke.constants.SystemConstants;
    6. import com.keke.domain.ResponseResult;
    7. import com.keke.domain.entity.Link;
    8. import com.keke.domain.vo.LinkVo;
    9. import com.keke.domain.vo.PageVo;
    10. import com.keke.mapper.LinkMapper;
    11. import com.keke.service.LinkService;
    12. import com.keke.utils.BeanCopyUtils;
    13. import org.springframework.stereotype.Service;
    14. import org.springframework.util.StringUtils;
    15. import java.util.List;
    16. /**
    17. * 友链(Link)表服务实现类
    18. *
    19. * @author makejava
    20. * @since 2023-10-11 16:53:47
    21. */
    22. @Service("linkService")
    23. public class LinkServiceImpl extends ServiceImpl implements LinkService {
    24. @Override
    25. public ResponseResult getAllLink() {
    26. //查询所有审核通过的
    27. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    28. lambdaQueryWrapper.eq(Link::getStatus, SystemConstants.Link_STATUS_NORMAL);
    29. List links = list(lambdaQueryWrapper);
    30. //转换Vo
    31. List linkVos = BeanCopyUtils.copyBeanList(links, LinkVo.class);
    32. //封装响应体
    33. return ResponseResult.okResult(linkVos);
    34. }
    35. @Override
    36. public ResponseResult selectPageLink(Link link, Integer pageNum, Integer pageSize) {
    37. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    38. //根据友链名进行模糊查询
    39. lambdaQueryWrapper.like(StringUtils.hasText(link.getName()),Link::getName,link.getName());
    40. //根据状态进行查询
    41. lambdaQueryWrapper.eq(StringUtils.hasText(link.getStatus()),Link::getStatus,link.getStatus());
    42. //构造分页器
    43. Page page = new Page<>(pageNum,pageSize);
    44. page(page,lambdaQueryWrapper);
    45. //封装Vo
    46. PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());
    47. //封装响应体返回
    48. return ResponseResult.okResult(pageVo);
    49. }
    50. }

    1.3 测试

    2. 新增友链

    需要新增友链功能

    2.1 接口分析

    请求方式请求路径是否需求token头
    POST/content/link

    请求体:

    1. {
    2. "name":"sda",
    3. "description":"weqw",
    4. "address":"wewe",
    5. "logo":"weqe",
    6. "status":"2"
    7. }

    响应格式:

    1. {
    2. "code":200,
    3. "msg":"操作成功"
    4. }

     2.2 代码实现

    第一步:修改entity层Link实体类,修改四字段为mp自动填充

    1. package com.keke.domain.entity;
    2. import java.util.Date;
    3. import java.io.Serializable;
    4. import com.baomidou.mybatisplus.annotation.FieldFill;
    5. import com.baomidou.mybatisplus.annotation.TableField;
    6. import lombok.AllArgsConstructor;
    7. import lombok.Data;
    8. import lombok.NoArgsConstructor;
    9. import com.baomidou.mybatisplus.annotation.TableName;
    10. /**
    11. * 友链(Link)表实体类
    12. *
    13. * @author makejava
    14. * @since 2023-10-11 15:45:54
    15. */
    16. @SuppressWarnings("serial")
    17. @Data
    18. @NoArgsConstructor
    19. @AllArgsConstructor
    20. @TableName("ke_link")
    21. public class Link {
    22. private Long id;
    23. private String name;
    24. private String logo;
    25. private String description;
    26. //网站地址
    27. private String address;
    28. //审核状态 (0代表审核通过,1代表审核未通过,2代表未审核)
    29. private String status;
    30. //创建者
    31. @TableField(fill = FieldFill.INSERT)
    32. private Long createBy;
    33. //创建时间
    34. @TableField(fill = FieldFill.INSERT)
    35. private Date createTime;
    36. //更新者
    37. @TableField(fill = FieldFill.INSERT_UPDATE)
    38. private Long updateBy;
    39. //更新时间
    40. @TableField(fill = FieldFill.INSERT_UPDATE)
    41. private Date updateTime;
    42. //删除标志(0代表未删除,1代表已删除)
    43. private Integer delFlag;
    44. }

    第二步:keke-admin的controller层新增添加友链的接口

    1. package com.keke.controller;
    2. import com.keke.domain.ResponseResult;
    3. import com.keke.domain.entity.Link;
    4. import com.keke.service.LinkService;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.web.bind.annotation.*;
    7. @RestController
    8. @RequestMapping("/content/link")
    9. public class LinkController {
    10. @Autowired
    11. private LinkService linkService;
    12. //分页查询友链
    13. @GetMapping("/list")
    14. public ResponseResult selectPageLink(Link link, Integer pageNum, Integer pageSize){
    15. return linkService.selectPageLink(link,pageNum,pageSize);
    16. }
    17. //新增友链
    18. @PostMapping
    19. public ResponseResult addLink(@RequestBody Link link){
    20. linkService.save(link);
    21. return ResponseResult.okResult();
    22. }
    23. }

     2.3 测试

    3. 修改友链

     需要提供修改友链的功能

    3.1 回显友链信息接口

    3.1.1 接口分析

    根据id查询友链

    请求方式请求路径是否需求token头
    Getcontent/link/{id}

    请求PathVariable格式参数:

    id: 友链id

     响应格式:

    1. {
    2. "code":200,
    3. "data":{
    4. "address":"wewe",
    5. "description":"weqw",
    6. "id":"4",
    7. "logo":"weqe",
    8. "name":"sda",
    9. "status":"2"
    10. },
    11. "msg":"操作成功"
    12. }
    3.1.2 代码实现

    逻辑较少,直接在controller层写

    1. //回显友链信息
    2. @GetMapping("/{id}")
    3. public ResponseResult getInfoById(@PathVariable("id") Long id){
    4. Link link = linkService.getById(id);
    5. return ResponseResult.okResult(link);
    6. }
    3.1.3 测试

    3.2 修改友链接口

    3.2.1 接口分析
    请求方式请求路径是否需求token头
    PUT/content/link

    请求头:

    1. {
    2. "address":"https://www.qq.com",
    3. "description":"dada2",
    4. "id":"2",
    5. "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",
    6. "name":"sda",
    7. "status":"0"
    8. }

    响应体:

    1. {
    2. "code":200,
    3. "msg":"操作成功"
    4. }
     3.2.2 代码实现
    1. //修改友链
    2. @PutMapping
    3. public ResponseResult editLink(@RequestBody Link link){
    4. linkService.updateById(link);
    5. return ResponseResult.okResult();
    6. }
     3.2.3 测试

    我们把刚才新增的友链LOG修改成猴子图标

    4. 删除友链

    删除某个友链(逻辑删除)

    4.1 接口分析

    请求方式请求路径是否需求token头
    DELETE/content/link/{id}

    请求参数PathVariable格式:

    id:要删除的友链id

     响应格式:

    1. {
    2. "code":200,
    3. "msg":"操作成功"
    4. }

     4.2 代码实现

    1. package com.keke.controller;
    2. import com.keke.domain.ResponseResult;
    3. import com.keke.domain.entity.Link;
    4. import com.keke.service.LinkService;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.web.bind.annotation.*;
    7. @RestController
    8. @RequestMapping("/content/link")
    9. public class LinkController {
    10. @Autowired
    11. private LinkService linkService;
    12. //分页查询友链
    13. @GetMapping("/list")
    14. public ResponseResult selectPageLink(Link link, Integer pageNum, Integer pageSize){
    15. return linkService.selectPageLink(link,pageNum,pageSize);
    16. }
    17. //新增友链
    18. @PostMapping
    19. public ResponseResult addLink(@RequestBody Link link){
    20. linkService.save(link);
    21. return ResponseResult.okResult();
    22. }
    23. //回显友链信息
    24. @GetMapping("/{id}")
    25. public ResponseResult getInfoById(@PathVariable("id") Long id){
    26. Link link = linkService.getById(id);
    27. return ResponseResult.okResult(link);
    28. }
    29. //修改友链
    30. @PutMapping
    31. public ResponseResult editLink(@RequestBody Link link){
    32. linkService.updateById(link);
    33. return ResponseResult.okResult();
    34. }
    35. //根据id删除友链
    36. @DeleteMapping("/{id}")
    37. public ResponseResult deleteLink(@PathVariable("id") Long id){
    38. linkService.removeById(id);
    39. return ResponseResult.okResult();
    40. }
    41. }

     4.3 测试

     

     

     至此,KekeBlog的后台模块开发全部结束

  • 相关阅读:
    关于自动化测试工具selenium
    程序员必备,一款让你提高工作效率N倍的神器uTools
    JVM(Java虚拟机)
    mysql—各种函数的使用
    25个例子学会Pandas Groupby 操作
    基于扩张观测器(LESO)的滑模控制
    神经网络激活函数的使用
    ccs: memory range overlaps existing memory range
    阿里云国际版云服务器防火墙设置
    Nacos集群配置以及在springboot中使用
  • 原文地址:https://blog.csdn.net/m0_63732435/article/details/134022825