前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Daily-Blog项目后台日志

Daily-Blog项目后台日志

作者头像
用户11097514
发布2024-05-30 21:32:34
2300
发布2024-05-30 21:32:34
举报
文章被收录于专栏:技术分享

博客后台

AOP实现日志记录

需求

通过日志记录接口调用信息,便于后期排查

格式如下 :

实现

  1. 先定义注解类
代码语言:javascript
复制
/**
 * 自定义注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SystemLog {

    String businessName();
}
  1. 定义切面类
代码语言:javascript
复制
/**
 * 切面类
 */
@Component
@Aspect
@Slf4j
public class LogAspect {

}
  1. 定义切点,及其通知方法
代码语言:javascript
复制
@Component
@Aspect
@Slf4j
public class LogAspect {

    //确定切点
    @Pointcut("@annotation(com.blog.annotation.SystemLog)")
    public void pt(){

    }


    //通知方法(使用环绕通知)
    @Around("pt()")
    public Object printLog(ProceedingJoinPoint joinPoint) throws Throwable {
        joinPoint.getArgs();
        Object res; //得到目标方法调用的返回值
        try {
            handleBefore(joinPoint);
            //目标方法的调用
            res = joinPoint.proceed();
            //打印响应信息
            handleAfter(res);
        }//无论有没有异常都需要打印异常信息
        finally {
            //换行
            log.info("============End============" + System.lineSeparator());
        }


        return res;
    }
    private void handleBefore(ProceedingJoinPoint joinPoint) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        //获取被增强方法上的注解对象
        SystemLog systemLog = getSystemLog(joinPoint);
        log.info("============Start============");
        // 打印请求 URL
        log.info("URL            : {}",request.getRequestURL());
        // 打印描述信息
        log.info("BusinessName   : {}",systemLog.businessName());
        // 打印 Http method
        log.info("HTTP Method    : {}",request.getMethod());
        // 打印调用 controller 的全路径以及执行方法
        log.info("Class Method   : {}.{}",joinPoint.getSignature().getDeclaringTypeName(),((MethodSignature) joinPoint.getSignature()).getName());
        // 打印请求的 IP
        log.info("IP             : {}",request.getRemoteHost());
        // 打印请求入参
        log.info("Request Args   : {}", JSON.toJSONString(joinPoint.getArgs()) );
    }




    private void handleAfter(Object res) {
        // 打印出参
        log.info("Response       : {}", JSON.toJSONString(res));

    }

    //todo 获取被增强方法上的注解对象
    private SystemLog getSystemLog(ProceedingJoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        SystemLog systemLog = methodSignature.getMethod().getAnnotation(SystemLog.class);
        return systemLog;
    }
}
  1. 在需要增强的方法上添加自定义注解
代码语言:javascript
复制
@SystemLog(businessName="更新用户信息")
@GetMapping("/userInfo")
public ResponseResult userInfo(){
    return userService.userInfo();
}

Swagger2

依赖

代码语言:javascript
复制
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
</dependency>
image-20230321215827890
image-20230321215827890

基本使用

代码语言:javascript
复制
@ApiOperation(value = "友链评论列表",notes = "获取一页友链评论")
@GetMapping("/linkCommentList")
public ResponseResult listCommentList(Integer pageNum , Integer pageSize){
    return commentService.commentList(SystemConstants.COMMENT_TYPE_FRIEND,null,pageNum,pageSize);
}

使用@ApiOperation(value = "友链评论列表",notes = "获取一页友链评论")来进行标注

配置形参

代码语言:javascript
复制
@ApiImplicitParams({
        @ApiImplicitParam(name = "pageNum", value = "页号"),
        @ApiImplicitParam(name = "pageSize", value = "每页大小")
})
image-20230322163816207
image-20230322163816207
image-20230322164820155
image-20230322164820155

**实体类接口 : **

一般一个实体类不止在一个接口中被用到,所以如果直接在实体类中添加的话就是使代码耦合,所以我们需要进行拆解

代码语言:javascript
复制
@ApiModel(description = "文章实体类")
public class Article{
    
}

所以上面的写法是不正规的

所以我们需要使用DTO对象

dto对象 :数据传输对象

按照开发规范,所有的controller层需要的实体类参数,我们都需要将其转换为dto对象

代码语言:javascript
复制
//todo 添加评论
@PostMapping
public ResponseResult addComment(@RequestBody AddCommentDto addCommentDto){
    Comment comment = BeanCopyUtils.copyBean(addCommentDto, Comment.class);
    return commentService.addComment(comment);
}
代码语言:javascript
复制
@ApiModel(description = "添加评论实体类")
public class AddCommentDto {
    private Long id;

    //评论类型(0代表文章评论,1代表友链评论)
    private String type;
    //文章id
    @ApiModelProperty(notes = "文章id")
    private Long articleId;
    //根评论id
	//......   
}

所有的dto都是需要添加的

image-20230322170449009
image-20230322170449009

获取所有标签

接口

image-20230323184335879
image-20230323184335879

实现

代码语言:javascript
复制
/**
 * 标签请求
 */
@RestController
@RequestMapping("/content/tag")
public class TagController {

    @Resource
    private TagService tagService;

    @GetMapping("/list")
    public ResponseResult list(){
        return ResponseResult.okResult(tagService.list());
    }
    
}

后台登录、登出

接口

image-20230323184730929
image-20230323184730929

登录

①自定义登录接口

调用ProviderManager的方法进行认证 如果认证成功生成jwt

把信息存入redis中

②自定义UserDetailsServic e

在这个实现类中进行查询数据库操作

注意配置密码加密BCryptPasswordCoder

校验

①自定义jwt认证过滤器

获取token

解析token获取其中的userId

从redis中获取用户信息

存入securityContextHolder

image-20230323190712580
image-20230323190712580

实现

代码语言:javascript
复制
@RestController
@RequestMapping("/user")
public class loginController {
    @Resource
    private AdminLoginService loginService;

    @PostMapping("/login")
    public ResponseResult login(@RequestBody User user){
        if (!StringUtils.hasText(user.getUserName())){
            //提示 要传用户名
            throw new SystemException(AppHttpCodeEnum.REQUIRE_USERNAME);
        }
        return loginService.login(user);
    }
    @PostMapping("/logout")
    public ResponseResult logout(){
        return loginService.logout();
    }
}
代码语言:javascript
复制
/**
 * 后台登陆实现
 */
@Service
public class AdminLoginServiceImpl implements AdminLoginService {

    @Autowired
    private AuthenticationManager authenticationManager;


    @Resource
    private RedisCache redisCache;

    //todo 登录业务
    @Override
    public ResponseResult login(User user) {
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUserName(),user.getPassword());
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        //判断是否认证通过
        //获取userId ,生成token
        //判断是否认证通过
        if(Objects.isNull(authenticate)){
            throw new RuntimeException("用户名或密码错误");
        }
        //获取userid 生成token
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        String userId = loginUser.getUser().getId().toString();
        String jwt = JwtUtil.createJWT(userId);
        //把用户信息存入redis
        redisCache.setCacheObject(SystemConstants.LOGIN_KEY + userId,loginUser);
        //封装响应  : 把token 和userInfoVo(由user转换而成) 封装 ,然后返回
        Map<String,String> map = new HashMap<>();
        map.put("token",jwt);
        return ResponseResult.okResult(map);
    }

    @Override
    public ResponseResult logout() {
        //获取 token 解析获取 userId
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        Long userId = loginUser.getUser().getId();

        redisCache.deleteObject(SystemConstants.LOGIN_KEY + userId);
        return ResponseResult.okResult();
    }
}

后台权限控制及其动态路由

表分析

权限表

image-20230323193145026
image-20230323193145026

对应的页面

image-20230323193349478
image-20230323193349478

权限表

image-20230323194436827
image-20230323194436827

角色权限表

image-20230323194501433
image-20230323194501433

获取当前用户的权限和角色信息

接口(getInfo)

image-20230323194702962
image-20230323194702962
image-20230323195059120
image-20230323195059120

实现getInfo

最终实现结果

代码语言:javascript
复制
{
	"code": 200,
	"data": {
		"permissions": [
			"system:menu:list",
			"system:menu:query",
			"system:menu:add",
			"system:menu:edit",
			"system:menu:remove",
			"content:article:writer"
		],
		"roles": [
			"common",
			"link"
		],
		"user": {
			"avatar": "http://rrpanx30j.hd-bkt.clouddn.com/images/91529822720e0cf3efed815e0446f21fbe09aa79.png",
			"email": "23412532@qq.com",
			"id": 4,
			"nickName": "红红火火恍恍惚惚",
			"sex": "1"
		}
	},
	"msg": "操作成功"
}
controller
代码语言:javascript
复制
@RestController
public class UserController {
    @Resource
    private RoleService roleService;

    @Resource
    private MenuService menuService;

    @GetMapping("/getInfo")
    public ResponseResult<AdminUserInfoVo> getInfo(){
        //1. 查询当前登陆的用户
        LoginUser loginUser = SecurityUtils.getLoginUser();
        //2. 根据用户id查询权限
        List<String> perms = menuService.selectPermsByUserId(loginUser.getUser().getId());
        // 根据id查询角色信息
        List<String> roleKeyList = roleService.selectRoleKeyByUserId(loginUser.getUser().getId());
        //3. 封装 返回
        User user = loginUser.getUser();
        UserInfoVo userInfoVo = BeanCopyUtils.copyBean(user, UserInfoVo.class);
        AdminUserInfoVo adminUserInfoVo = new AdminUserInfoVo(perms,roleKeyList,userInfoVo);

        return ResponseResult.okResult(adminUserInfoVo);
    }
}
service层两个实现类
代码语言:javascript
复制
/**
 * 查询角色权限信息
 */
@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {


    /**
     * 根据用户id查询权限信息<br>
     * 如果用户id为1 代表管理员 ,menus中需要有所有菜单类型为c或者F的,状态为,未被删除的权限
     * @param id 用户id
     * @return 返回该用户权限集合
     */
    @Override
    public List<String> selectPermsByUserId(Long id) {
        //管理员返回所有的权限
        if(id == 1){
            LambdaQueryWrapper<Menu> wrapper = new LambdaQueryWrapper<>();
            wrapper.in(Menu::getMenuType, SystemConstants.MENU_TYPE_C,SystemConstants.MENU_TYPE_F);  //菜单类型为C 和 F
            wrapper.eq(Menu::getStatus,SystemConstants.LINK_STATUS_NORMAL);//状态正常
            List<Menu> menus = list(wrapper);
            List<String> Perms = menus.stream().map(Menu::getPerms).collect(Collectors.toList());
            return Perms;
        }
        // 反之返回相对应用户所具有的权限

        //1. 先查询sys_user_roles查询用户角色id
        //2. 查到角色id之后再到sys_roles_menu查询对应的权限id(menuId)
        //3. 最后通过menuId查询对应的menu信息
        //4. 封装返回
        return getBaseMapper().selectPermsByUserId(id);
    }
}
代码语言:javascript
复制
/**
 * 查询角色信息
 */
@Service
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {

    @Override
    public List<String> selectRoleKeyByUserId(Long id) {
        //判断是否为管理员角色
        if(id == 1){
            List<String> roleKeys = new ArrayList<>();
            roleKeys.add("admin");  //管理员角色
            return roleKeys;
        }
        //如果不是返回对应id的角色信息(连表查询)
        return getBaseMapper().selectRoleKeyByUserId(id);
    }
}
对应多表联查的xml文件
代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.blog.mapper.RoleMapper">
<!--            selectRoleKeyByUserId-->
    <select id="selectRoleKeyByUserId" resultType="java.lang.String">
        SELECT
            r.`role_key`
        FROM
            `sys_user_role` ur
                LEFT JOIN `sys_role` r ON ur.`role_id` = r.`id`
        WHERE
            ur.`user_id` = #{userId} AND
            r.`status` = 0 AND
            r.`del_flag` = 0
    </select>
    <select id="selectRoleIdByUserId" resultType="java.lang.Long">
        select r.id
        from sys_role r
                 left join sys_user_role ur on ur.role_id = r.id
        where ur.user_id = #{userId}
    </select>
</mapper>
代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.blog.mapper.MenuMapper">

    <select id="selectPermsByUserId" resultType="java.lang.String">
        SELECT
            DISTINCT m.perms
        FROM
            `sys_user_role` ur
                LEFT JOIN `sys_role_menu` rm ON ur.`role_id` = rm.`role_id`
                LEFT JOIN `sys_menu` m ON m.`id` = rm.`menu_id`
        WHERE
            ur.`user_id` = #{userId} AND
            m.`menu_type` IN ('C','F') AND
            m.`status` = 0 AND
            m.`del_flag` = 0
    </select>
    <select id="selectAllRouterMenu" resultType="com.blog.domain.entity.Menu">
        SELECT
            DISTINCT m.id, m.parent_id, m.menu_name, m.path, m.component, m.visible, m.status, IFNULL(m.perms,'') AS perms, m.is_frame,  m.menu_type, m.icon, m.order_num, m.create_time
        FROM
            `sys_menu` m
        WHERE
            m.`menu_type` IN ('C','M') AND
            m.`status` = 0 AND
            m.`del_flag` = 0
        ORDER BY
            m.parent_id,m.order_num
    </select>
    <select id="selectRouterMenuTreeByUserId" resultType="com.blog.domain.entity.Menu">
        SELECT
            DISTINCT m.id, m.parent_id, m.menu_name, m.path, m.component, m.visible, m.status, IFNULL(m.perms,'') AS perms, m.is_frame,  m.menu_type, m.icon, m.order_num, m.create_time
        FROM
            `sys_user_role` ur
                LEFT JOIN `sys_role_menu` rm ON ur.`role_id` = rm.`role_id`
                LEFT JOIN `sys_menu` m ON m.`id` = rm.`menu_id`
        WHERE
            ur.`user_id` = #{userId} AND
            m.`menu_type` IN ('C','M') AND
            m.`status` = 0 AND
            m.`del_flag` = 0
        ORDER BY
            m.parent_id,m.order_num
    </select>
    <select id="selectMenuListByRoleId" resultType="java.lang.Long">
        select m.id
        from sys_menu m
                 left join sys_role_menu rm on m.id = rm.menu_id
        where rm.role_id = #{roleId}


        order by m.parent_id, m.order_num
    </select>
</mapper>

动态路由接口(getRouters)

image-20230323195215441
image-20230323195215441
响应格式

前端为了实现动态路由的效果,需要后端有接口能够返回所有的菜单数据

注意 :返回的菜单需要体现父子菜单的层级关系

如果用户id为1 代表管理员 ,menus中需要有所有菜单类型为c或者M的,状态为,未被删除的权限

image-20230323195723943
image-20230323195723943
代码语言:javascript
复制
{
	"code": 200,
	"data": {
		"menus": [
			{
				"children": [],
				"component": "content/article/write/index",
				"createTime": "2022-01-08 03:39:58",
				"icon": "build",
				"id": 2023,
				"isFrame": 1,
				"menuName": "写博文",
				"menuType": "C",
				"orderNum": 0,
				"parentId": 0,
				"path": "write",
				"perms": "content:article:writer",
				"status": "0",
				"visible": "0"
			},
			{
				"children": [
					{
						"children": [],
						"component": "system/menu/index",
						"createTime": "2021-11-12 10:46:19",
						"icon": "tree-table",
						"id": 102,
						"isFrame": 1,
						"menuName": "菜单管理",
						"menuType": "C",
						"orderNum": 3,
						"parentId": 1,
						"path": "menu",
						"perms": "system:menu:list",
						"status": "0",
						"visible": "0"
					}
				],
				"createTime": "2021-11-12 10:46:19",
				"icon": "system",
				"id": 1,
				"isFrame": 1,
				"menuName": "系统管理",
				"menuType": "M",
				"orderNum": 1,
				"parentId": 0,
				"path": "system",
				"perms": "",
				"status": "0",
				"visible": "0"
			}
		]
	},
	"msg": "操作成功"
}

实现getRouters

controller
代码语言:javascript
复制
@GetMapping("/getRouters")
public ResponseResult<RoutersVo> getRouters(){
    Long userId = SecurityUtils.getUserId();
    //查询menu 结果是tree形状
    List<Menu> menus = menuService.selectRouterMenuTreeByUserId(userId);
    RoutersVo routersVo = new RoutersVo(menus);
    //封装返回
    return ResponseResult.okResult(routersVo);
}
service层实现
代码语言:javascript
复制
@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {  
	/**
     * 根据用户id查询相关的权限菜单信息
     * @param userId 用户id
     * @return 返回符合要求的val
     */
    @Override
    public List<Menu> selectRouterMenuTreeByUserId(Long userId) {
        MenuMapper menuMapper = getBaseMapper();
        List<Menu> menus = null;    //封装Menu

        //如果是管理员,返回所有的菜单
        if(SecurityUtils.isAdmin()){
            menus = menuMapper.selectAllRouterMenu();
        }
        else{
            //如果不是管理员 那么返回对应有权限的菜单按钮
            menus = menuMapper.selectRouterMenuTreeByUserId(userId);
        }
        //通过上述得到的Menu无法得到我们需要的父子菜单管理,所以我们需要通过(buildMenuTree)来构建这种父子菜单关系
        //构建Tree
        //先找出第一层的菜单,接着找到他们的子菜单,然后就可以设置children属性中
        List<Menu> menuTree = buildMenuTree(menus,0L);
        return menuTree;
    }

    /**
     * 构建菜单的父子菜单关系
     * <br>
     * 找到父menu对应的子menu ,然后将他们放到一个集合中,最后设置给children这个字段
     * @param menus 传入的方法
     * @param parentId 父菜单id
     * @return
     */
    private List<Menu> buildMenuTree(List<Menu> menus, Long parentId) {
        List<Menu> menuList = menus.stream()//通过这样筛选就可以得到第一层级的menu
                .filter(menu -> menu.getParentId().equals(parentId))
                /*
                传入的menus是得到了第一层的menus(相当于Tree中的root节点),然后需要设置他的子菜单(left 和 right)
                因为menus中有所有的菜单(父子都有), 所以我们在设置left和right时需要找到他们的子菜单
                所以就调用getChildren找到left或者right的子菜单,然后得到之后再设置给他们
                 */
                .map(menu -> menu.setChildren(getChildren(menu, menus)))
                .collect(Collectors.toList());
        return menuList;
    }

    /**
     * 获取传入参数的子menu的list集合
     *  在menus中找打当前传入的menu的子菜单
     * @param menu
     * @param menus
     */
    private List<Menu> getChildren(Menu menu, List<Menu> menus){
        List<Menu> children = menus.stream()
                .filter(menu1 -> menu1.getParentId().equals(menu.getId()))
                .map(menu1 -> menu1.setChildren(getChildren(menu1,menus)))  //如果有很多的子菜单,那么就可以用到这个递归
                .collect(Collectors.toList());
        return children;
    }
}

后台项目改进点

关于项目中实现后台模块中的各个菜单及其子菜单的实现

  1. 首先查询对应用户的菜单
代码语言:javascript
复制
/**
   * 根据用户id查询相关的权限菜单信息
   * @param userId 用户id
   * @return 返回符合要求的val
   */
  @Override
  public List<Menu> selectRouterMenuTreeByUserId(Long userId) {
      MenuMapper menuMapper = getBaseMapper();
      List<Menu> menus = null;    //封装Menu

      //如果是管理员,返回所有的菜单
      if(SecurityUtils.isAdmin()){
          menus = menuMapper.selectAllRouterMenu();
      }
      else{
          //如果不是管理员 那么返回对应有权限的菜单按钮
          menus = menuMapper.selectRouterMenuTreeByUserId(userId);
      }
      //通过上述得到的Menu无法得到我们需要的父子菜单管理,所以我们需要通过(buildMenuTree)来构建这种父子菜单关系
      //构建Tree
      //先找出第一层的菜单,接着找到他们的子菜单,然后就可以设置children属性中
      List<Menu> menuTree = buildMenuTree(menus,0L);
      return menuTree;
  }
  1. 接着对于那些第一级菜单进行查询(parentId == 1)
代码语言:javascript
复制
/**
 * 构建菜单的父子菜单关系
 * <br>
 * 找到父menu对应的子menu ,然后将他们放到一个集合中,最后设置给children这个字段
 * @param menus 传入的方法
 * @param parentId 父菜单id
 * @return
 */
private List<Menu> buildMenuTree(List<Menu> menus, Long parentId) {
    //用常规的方法
    List<Menu> test = new ArrayList<>();
    for(Menu menu : menus){
        //查询出那些 父菜单的id为 【 1】 的 ,也就是第一级菜单
        if(menu.getParentId().equals(parentId)){
            //获取这些菜单的子菜单
            Menu menu1 = menu.setChildren(getChildren(menu, menus));
            test.add(menu1);
        }
    }
    return test;
}
  1. 查询对应的子菜单
代码语言:javascript
复制
/**
 * 获取传入参数的子menu的list集合
 *  在menus中找打当前传入的menu的子菜单
 * @param menu 获取它的子菜单
 * @param menus 全部菜单集合
 */
private List<Menu> getChildren(Menu menu, List<Menu> menus){
    List<Menu> test1 = new ArrayList<>();
    for (Menu menu1 : menus){
        if(menu1.getParentId().equals(menu.getId())){
            test1.add(menu1);
        }
    }
    return test1;
}

改进

在上述我们的代码中,如果仅仅是实现两层菜单还可以满足, 但是如果出现菜单层级是3 、4、5…等情况我上述实现的代码就无法满足。

这是我们就需要使用到递归算法 但是这里如果单单使用递归好像很难实现

所以我们在这里可以使用函数式编程,然后在其中套用递归来实现

代码语言:javascript
复制
/**
 * 构建菜单的父子菜单关系
 * <br>
 * 找到父menu对应的子menu ,然后将他们放到一个集合中,最后设置给children这个字段
 * @param menus 传入的方法
 * @param parentId 父菜单id
 * @return
 */
private List<Menu> buildMenuTree(List<Menu> menus, Long parentId) {
    List<Menu> menuList = menus.stream()//通过这样筛选就可以得到第一层级的menu
            .filter(menu -> menu.getParentId().equals(parentId))
            /*
            传入的menus是得到了第一层的menus(相当于Tree中的root节点),然后需要设置他的子菜单(left 和 right)
            因为menus中有所有的菜单(父子都有), 所以我们在设置left和right时需要找到他们的子菜单
            所以就调用getChildren找到left或者right的子菜单,然后得到之后再设置给他们
             */
            .map(menu -> menu.setChildren(getChildren(menu, menus)))
            .collect(Collectors.toList());
    return menuList;
}
代码语言:javascript
复制
/**
 * 获取传入参数的子menu的list集合
 *  在menus中找打当前传入的menu的子菜单
 * @param menu 获取它的子菜单
 * @param menus 全部菜单集合
 */
private List<Menu> getChildren(Menu menu, List<Menu> menus){
    List<Menu> children = menus.stream()
            .filter(menu1 -> menu1.getParentId().equals(menu.getId()))
            .map(menu1 -> menu1.setChildren(getChildren(menu1,menus)))  //如果有很多的子菜单,那么就可以用到这个递归
            .collect(Collectors.toList());
    return children;
}

通过这样的该进,我们就可以实现多层菜单的查询

查询标签列表

接口

image-20230324170257378
image-20230324170257378

需求 :

提供标签功能,一个文章可以有多个标签。

在后台需要分页查询标签功能,要求能够根据签名进行分页查询。后期可以增加备注查询等需求

注意 :不要把删除了的标签查询出来

image-20230324172918379
image-20230324172918379

实现

代码语言:javascript
复制
/**
 * 标签请求
 */
@RestController
@RequestMapping("/content/tag")
public class TagController {

    @Resource
    private TagService tagService;

    /**
     *提供标签功能,一个文章可以有多个标签。
     *
     * 在后台需要分页查询标签功能,要求能够根据签名进行分页查询。**后期可以增加备注查询等需求**
     *
     * 注意 :不要把删除了的标签查询出来
     * @param pageNum 第几页
     * @param pageSize 分页大小
//     * @param name 标签名
//     * @param remark 备注
     * @return
     */
    @GetMapping("/list")
    public ResponseResult<PageVo> list(int pageNum, int pageSize, TagListDto tagListDto){

        return tagService.pageTagList(pageNum,pageSize,tagListDto);
    }

}

service层实现

代码语言:javascript
复制
/**
 * 标签表服务接口
 * @author ray2310
 */
@Service("tagService")
public class TagServiceImpl extends ServiceImpl<TagMapper, Tag> implements TagService {


    //实现分页tag列表
    @Override
    public ResponseResult<PageVo> pageTagList(Integer pageNum, Integer pageSize, TagListDto tagListDto) {
        //分页查询
        Page<Tag> page = new Page<>();
        page.setCurrent(pageNum);
        page.setSize(pageSize);
        LambdaQueryWrapper<Tag> wrapper = new LambdaQueryWrapper<>();
        //如果他们有值 ,那么就会调用这个方法, 如果没有就不会调用
        wrapper.eq(StringUtils.hasText(tagListDto.getName()),Tag::getName,tagListDto.getName());
        wrapper.eq(StringUtils.hasText(tagListDto.getRemark()),Tag::getRemark,tagListDto.getRemark());
        page(page,wrapper);
        PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());
        //封装数据返回
        return ResponseResult.okResult(pageVo);
    }
}

新增标签

接口

image-20230326051807981
image-20230326051807981

实现

代码语言:javascript
复制
//TODO 新增标签, 测试时需要在数据库记录中有创建时间、更新时间、创建人、创建人字段
@PostMapping
public ResponseResult addTag(@RequestBody TagListDto tagListDto){
    return tagService.addTag(tagListDto);
}

serivce层

代码语言:javascript
复制
//todo 新增标签需求   需要在数据库记录中有创建时间、更新时间、创建人、创建人字段
@Override
public ResponseResult addTag(TagListDto tagListDto) {
    //1. 接收请求信息,判断信息是否为空
    if(ObjectUtils.isEmpty(tagListDto)){
        return ResponseResult.errorResult(AppHttpCodeEnum.TAG_ERROR);
    }
    //2. 获取标签创建者 、获取创建时间
    Long userId = SecurityUtils.getUserId();
    //3. 将的到的信息转换为tag 存储到数据库中
    Tag tag  = new Tag();
    tag.setName(tagListDto.getName());
    tag.setRemark(tagListDto.getRemark());
    tag.setCreateBy(userId);
    save(tag);
    return ResponseResult.okResult(tag);
}

删除标签

接口

image-20230326054428595
image-20230326054428595

实现

代码语言:javascript
复制
@DeleteMapping("/{id}")
public ResponseResult deleteTag(@PathVariable Long id){
    return tagService.deleteTag(id);
}

service层

代码语言:javascript
复制
 //todo 删除标签需求 需要设置逻辑删除 也就是
//`del_flag` int DEFAULT '0' COMMENT '删除标志(0代表未删除,1代表已删除)',
@Override
public ResponseResult deleteTag(Long id) {
    //1. 从数据库中查找要删除的id
    //2. 修改其中的delFlag = 1
    //返回删除信息
    UpdateWrapper<Tag> updateWrapper = new UpdateWrapper<>();
    updateWrapper.eq("id",id);
    updateWrapper.set("del_flag",1);
    update(updateWrapper);
    return ResponseResult.okResult();
}

修改标签

接口

  1. 先获取接口信息
image-20230326062601718
image-20230326062601718
  1. 修改接口
image-20230326063414438
image-20230326063414438

实现

  1. 首先获取指定id的标签
代码语言:javascript
复制
@GetMapping("/{id}")
public ResponseResult getTagById(@PathVariable Long id){
    return tagService.getTagById(id);
}
代码语言:javascript
复制
//todo 获取需要修改的标签信息
@Override
public ResponseResult getTagById(Long id) {
    Tag tag = getById(id);
    TagDto dto = BeanCopyUtils.copyBean(tag, TagDto.class);
    return ResponseResult.okResult(dto);
}
  1. 修改获取的内容
代码语言:javascript
复制
@PutMapping
public ResponseResult updateTag(@RequestBody TagDto tagDto){
    return tagService.updateTag(tagDto);
}
代码语言:javascript
复制
//todo 修改信息
@Override
public ResponseResult updateTag(TagDto tagDto) {
    System.out.println(tagDto.toString());
    UpdateWrapper<Tag> updateWrapper = new UpdateWrapper<>();
    updateWrapper.eq("id",tagDto.getId());
    updateWrapper.set("name",tagDto.getName());
    updateWrapper.set("remark",tagDto.getRemark());
    update(updateWrapper);
    return ResponseResult.okResult();
}

写文章

接口

  1. 首先获取所有的分类信息接口
image-20230326070053043
image-20230326070053043
  1. 获取所有的标签请求接口
image-20230326070301832
image-20230326070301832
  1. 上传图片接口
image-20230326070423388
image-20230326070423388
  1. 写博文接口
image-20230326070444466
image-20230326070444466

实现

  1. 首先获取所有的分类信息接口
代码语言:javascript
复制
//todo 获取所有分类信息
@GetMapping("/category/listAllCategory")
public ResponseResult listAllCategory(){
    return categoryService.listAllCategory();
}
代码语言:javascript
复制
@Override
public ResponseResult listAllCategory() {
    //查询出所有没有删除的分类
    LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(Category::getStatus, SystemConstants.CATEGORY_STATUS);//没有被禁用的
    queryWrapper.eq(Category::getDelFlag,SystemConstants.CATEGORY_NOTDEL);//没有被删除的
    List<Category> list = list(queryWrapper);
    List<CategoryVo> categoryVo1s = BeanCopyUtils.copyBeanList(list, CategoryVo.class);
    return ResponseResult.okResult(categoryVo1s);
}
  1. 获取所有的标签信息
代码语言:javascript
复制
//todo 获取所有的标签信息
@GetMapping("/tag/listAllTag")
public ResponseResult listAllTag(){
    return tagService.listAllTag();
}
代码语言:javascript
复制
//todo 获取所有的标签,不分页的
@Override
public ResponseResult listAllTag() {
    //查询出所有没有删除的标签
    LambdaQueryWrapper<Tag> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(Tag::getDelFlag, SystemConstants.TAG_NOTDEL);
    List<Tag> list = list(queryWrapper);
    List<TagVo> tagVos = BeanCopyUtils.copyBeanList(list, TagVo.class);
    return ResponseResult.okResult(tagVos);
}
  1. 上传图片接口
代码语言:javascript
复制
@RestController
public class UploadController {
    @Autowired
    private UploadService uploadService;

    @PostMapping("/upload")
    public ResponseResult uploadImg(MultipartFile img){
        return uploadService.uploadImg(img);
    }
}
代码语言:javascript
复制
package com.blog.service.impl;

import com.blog.domain.ResponseResult;
import com.blog.enums.AppHttpCodeEnum;
import com.blog.service.UploadService;
import com.google.gson.Gson;
import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.FileInputStream;
import java.io.InputStream;

/**
 * 上传文件到七牛云
 */
@ConfigurationProperties(prefix = "oss")
@Service
@Data
public class UploadServiceImpl implements UploadService {

    //todo 实现文件的上传
    @Override
    public ResponseResult uploadImg(MultipartFile img) {
        //判断文件的大小
        //获取原始文件名进行判断
        String originalFilename = img.getOriginalFilename();
        if(!originalFilename.endsWith(".png") && !originalFilename.endsWith(".jpg")){
            return ResponseResult.errorResult(AppHttpCodeEnum.FILE_TYPE_ERROR);
        }
        //如果通过,上传文件到oss
        String url = uploadOSS(img);

        return ResponseResult.okResult(url);
    }

    private String accessKey;
    private String secretKey;
    private String bucket;


    private String uploadOSS(MultipartFile imgFile){
        //构造一个带指定 Region 对象的配置类
        Configuration cfg = new Configuration(Region.autoRegion());
        //...其他参数参考类注释
        UploadManager uploadManager = new UploadManager(cfg);
        //默认不指定key的情况下,以文件内容的hash值作为文件名

        //images目录下的文件
        String originalFilename = imgFile.getOriginalFilename();

        String key = "images/"+originalFilename;
        try {
            //将前端传过来的imgFile文件转换成一个inputStream,然后
            InputStream inputStream = imgFile.getInputStream();
            Auth auth = Auth.create(accessKey, secretKey);
            String upToken = auth.uploadToken(bucket);
            try {
                Response response = uploadManager.put(inputStream,key,upToken,null, null);
                //解析上传成功的结果
                DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
                System.out.println(putRet.key);
                System.out.println(putRet.hash);
            } catch (QiniuException ex) {
                Response r = ex.response;
                System.err.println(r.toString());
                try {
                    System.err.println(r.bodyString());
                } catch (QiniuException ex2) {
                    //ignore
                }
            }
        } catch (Exception ex) {
            //ignore
        }
        //文件地址
        return "http://rrpanx30j.hd-bkt.clouddn.com/images/"+ originalFilename;
    }
}
  1. 写文章实现
代码语言:javascript
复制
@PostMapping("/article")
public ResponseResult writeArticle(@RequestBody ArticleVo articleVo){
    return articleService.writeArticle(articleVo);
}
代码语言:javascript
复制
//todo 后台写文章详情
@Override
@Transactional  //添加事务
public ResponseResult writeArticle(ArticleVo articleVo) {
    Article article = BeanCopyUtils.copyBean(articleVo, Article.class);
    save(article);
    //将标签id的集合存入标签文章集合表中
    List<ArticleTag> collect = articleVo.getTags().stream().map(tagId -> new ArticleTag(article.getId(), tagId)).collect(Collectors.toList());
    articleTagService.saveBatch(collect);
   return ResponseResult.okResult();
}

修改文章

需求 :点击修改文章时能够跳转回到写博文页面

回显该文章的全部信息

用户可以在该页面进行修改博文信息,点击更新后可以实现修改文章

接口

  1. 根据id获取博文
image-20230328201801346
image-20230328201801346
image-20230328201812622
image-20230328201812622
  1. 修改博文
image-20230328205243997
image-20230328205243997

实现

  1. 按文章id查询文章回显数据
代码语言:javascript
复制
//todo 获取要更新的博文
@GetMapping("/{id}")
public ResponseResult updateBefore(@PathVariable Long id){
    return articleService.updateBefore(id);
}
代码语言:javascript
复制
/**
 * 后台更新博文前获取博文所有信息
 * @param id 文章id
 * @return 返回所有信息
 */
@Override
public ResponseResult updateBefore(Long id) {
    //1. 首先根据id获取所有信息
    AdminArticleVo articleVo = BeanCopyUtils.copyBean(getById(id), AdminArticleVo.class);
    //2. 获取所有的标签id,然后找出我们需要的
    List<Long> ids = articleTagService.selectByArticleId(id);
    articleVo.setTags(ids);
    //2. 根据文章id 获取其所有的标签tags
    System.out.println(articleVo);
    //3. 封装返回
    return ResponseResult.okResult(articleVo);
}
  1. 修改更新
代码语言:javascript
复制
//todo 更新文章
@PutMapping
public ResponseResult updateNow(@RequestBody AdminArticleVo articleVo){
    return articleService.updateNow(articleVo);
}
代码语言:javascript
复制

    /**
     * 首先更新请求
     * @param articleVo 需要更新的文章
     * @return
     */
    @Override
    public ResponseResult updateNow(AdminArticleVo articleVo) {
       // UpdateWrapper<Article> wrapper = new UpdateWrapper<>();
       // wrapper.eq("id",articleVo.getId());
        Article article = BeanCopyUtils.copyBean(articleVo, Article.class);
//        wrapper.set("id",articleVo.getId());
//        wrapper.set("title",articleVo.getTitle());
//        wrapper.set("content",articleVo.getContent());
//        wrapper.set("summary",articleVo.getSummary());
//        wrapper.set("category_id",articleVo.getCategoryId());
//        wrapper.set("thumbnail",articleVo.getThumbnail());
//        wrapper.set("is_top",articleVo.getIsTop());
//        wrapper.set("status",articleVo.getStatus());
//        wrapper.set("view_count",articleVo.getViewCount());
//        wrapper.set("is_comment",articleVo.getIsComment());
//        wrapper.set("update_by",articleVo.getUpdateBy());
//        wrapper.set("update_time",articleVo.getUpdateTime());
//        update(wrapper);
        updateById(article);
        //先删除对应的映射关系
        articleTagService.deleteByArticleId(articleVo.getId(),articleVo.getTags());
        //然后重新添加新增的标签映射关系
        List<ArticleTag> collect = articleVo.getTags().stream().map(tagId -> new ArticleTag(article.getId(), tagId)).collect(Collectors.toList());
        articleTagService.saveBatch(collect);
        return ResponseResult.okResult();
    }
代码语言:javascript
复制
@Service
public class ArticleTagServiceImpl extends ServiceImpl<ArticleTagMapper, ArticleTag> implements ArticleTagService {

    @Override
    public List<Long> selectByArticleId(Long id){
        List<ArticleTag> list = list();
        List<Long> ids = new ArrayList<>();
        for(ArticleTag articleTag : list){
            if(articleTag.getArticleId().equals(id)){
                Long tagId = articleTag.getTagId();
                ids.add(tagId);
            }
        }
        return ids;
    }
    @Override
    public void deleteByArticleId(Long id,List<Long> ids){
        LambdaQueryWrapper<ArticleTag> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ArticleTag::getArticleId,id);
        remove(wrapper);

    }
}

删除文章

需求 ,删除文章是指逻辑删除,而不是真的删除

接口

image-20230328214440779
image-20230328214440779

实现

代码语言:javascript
复制
@DeleteMapping("/{id}")
public ResponseResult deleteArticleById(@PathVariable Long id){
    return articleService.deleteArticleById(id);
}
代码语言:javascript
复制
/**
 * 根据id逻辑删除文章
 * @param id 文章id
 * @return 删除结果
 */
@Override
public ResponseResult deleteArticleById(Long id) {
    UpdateWrapper<Article> wrapper = new UpdateWrapper<>();
    wrapper.eq("id",id);
    wrapper.set("del_flag",SystemConstants.DELETE);
    update(wrapper);
    return ResponseResult.okResult();
}

文章列表

接口

image-20230326081110657
image-20230326081110657
image-20230326081713597
image-20230326081713597

实现

代码语言:javascript
复制
@GetMapping("/article/list")
public ResponseResult articleList(int pageNum, int pageSize, ArticleSummaryDto articleSummary){
    return articleService.getAllArticle(pageNum,pageSize,articleSummary);
}
代码语言:javascript
复制
//todo 后台博文获取所有博文
@Override
public ResponseResult getAllArticle(int pageNum, int pageSize, ArticleSummaryDto articleSummary) {
    LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
    //如果有categoryId ,那么查询和传入的就需要相同
    //状态 : 正式发布
    queryWrapper.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_PUT);
    //置顶的文章(对isTop进行排序)
    //分页查询
    Page<Article> pageN = new Page<>(pageNum,pageSize);
    Page<Article> page = page(pageN, queryWrapper);
    //查询categoryName ,因为我们封装的是categoryName,但是查询出来的确实categoryId,所以需要在进行查询
    List<Article> articles = page.getRecords();
    List<AdminArticleVo> articleVos = BeanCopyUtils.copyBeanList(articles, AdminArticleVo.class);
    PageVo pageVo = new PageVo(articleVos, page.getTotal());

    return ResponseResult.okResult(pageVo);
}

导出Excel文件

需求 : 将我们需要的文件 ,比如标签信息等导出成为一个Excel文件

接口

image-20230327170559042
image-20230327170559042

实现

代码语言:javascript
复制
@GetMapping("/export")
public void exportExcel(HttpServletResponse response){
    //1. 设置下载文件的请求头
    try {
        WebUtils.setDownLoadHeader("分类.xlsx",response);
        List<CategoryVo> list = categoryService.listAllCategory();
        //2. 获取导出的数据
        //3. 把数据写道Excel
        List<ExcelCategoryVo> excelCategoryVos = BeanCopyUtils.copyBeanList(list, ExcelCategoryVo.class);
        EasyExcel.write(response.getOutputStream(), ExcelCategoryVo.class).autoCloseStream(Boolean.FALSE).sheet("分类导出")
                .doWrite(excelCategoryVos);
        //4. 如果出现异常响应json格式
    } catch (Exception e) {
        ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR);
        WebUtils.renderString(response, JSON.toJSONString(result));
    }
}

权限控制

只针对有权限的用户访问能够访问的信息

操作

  1. 在springSecurity中添加
代码语言:javascript
复制
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  1. 在需要访问的方法上加
image-20230327185502326
image-20230327185502326
  1. 封装权限到loginUser中
代码语言:javascript
复制
package com.blog.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.blog.domain.entity.LoginUser;
import com.blog.domain.entity.User;
import com.blog.mapper.MenuMapper;
import com.blog.mapper.UserMapper;
import com.blog.utils.SystemConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Resource
    private MenuMapper menuMapper;

    @Resource
    private UserMapper userMapper;

    /**
     * 使用该方法来重写用户登录的校验工作
     * @param username 传入username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根据用户名查询数据库用户信息
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUserName,username);
        User user = userMapper.selectOne(queryWrapper);
        //判断是否查询到  : 未查到抛出异常   : 查到然后作比较
        if(Objects.isNull(user)){
            throw new RuntimeException("用户不存在!!!");
        }
        /**
         * 还需要做的 : 后台用户查询权限信息封装
         *
         */

        //用户是管理员
        if(user.getType().equals(SystemConstants.ADMIN)){
            //查询用户权限集合
            List<String> perms = menuMapper.selectPermsByUserId(user.getId());
            //封装到loginUser
            return new LoginUser(user,perms);
        }
        //返回用户信息
        return new LoginUser(user,null);
    }
}

菜单列表

需求 :

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

接口

image-20230329110738310
image-20230329110738310

实现

代码语言:javascript
复制
//todo 菜单列表
@GetMapping("/list")
public ResponseResult getAll(String status,String menuName){
    return menuService.getAll(status,menuName);
}
代码语言:javascript
复制
/*
展示菜单列表,不需要进行分页。可以正对菜单名做模糊查询,也可以根据菜单状态进行查询。
菜单要按照父菜单id 和 OrderNum进行排序
 */
@Override
public ResponseResult getAll(String status, String menuName) {

    //1. 按要求查询出所有的菜单
    LambdaQueryWrapper<Menu> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(Menu::getDelFlag,SystemConstants.NOT_DELETE);
    wrapper.like(Objects.nonNull(menuName),Menu::getMenuName,menuName);
    wrapper.like(Objects.nonNull(status),Menu::getStatus,status);
    //2. 按照父菜单 和 orderNum进行排序
    wrapper.orderByAsc(Menu::getParentId);
    wrapper.orderByAsc(Menu::getOrderNum);
    List<Menu> list = list(wrapper);
    List<MenuVo> menuVos = BeanCopyUtils.copyBeanList(list, MenuVo.class);
    //3. 封装为Vo,然后在放到集合中返回
    return ResponseResult.okResult(menuVos);
}

新增菜单

接口

注意: 这里的接口路径应该是:system/menu

image-20230329114721619
image-20230329114721619

实现

代码语言:javascript
复制
//todo 新增菜单
@PostMapping
public ResponseResult addMenu(@RequestBody Menu menu){
    return menuService.addMenu(menu);
}
代码语言:javascript
复制
//todo 新增菜单 或者按钮
@Override
public ResponseResult addMenu(Menu menu) {
    if(ObjectUtils.isEmpty(menu.getIcon())){
       return ResponseResult.errorResult(AppHttpCodeEnum.ICON_NOT_NULL);
    }
    if(ObjectUtils.isEmpty(menu.getMenuName())){
        return ResponseResult.errorResult(AppHttpCodeEnum.MENU_NAME_NOT_NULL);
    }
    if(ObjectUtils.isEmpty(menu.getPath())){
        return ResponseResult.errorResult(AppHttpCodeEnum.PATH_NOT_NULL);
    }
    save(menu);
    return ResponseResult.okResult();
}

修改菜单

需求:

  1. 不能将自己的上级菜单设置自己 1 系统管理 0 1 system 1 M 0 0 system 0 2021-11-12 10:46:19 0 系统管理目录 0

接口

  1. 查询对应的菜单信息
image-20230329121149241
image-20230329121149241
  1. 更新菜单接口
image-20230329121228032
image-20230329121228032

实现

  1. 查询所要修改的信息
代码语言:javascript
复制
//todo 根据id查询对应菜单
@GetMapping("/{id}")
public ResponseResult selectById(@PathVariable Long id){
    return menuService.selectById(id);
}
代码语言:javascript
复制
//todo 根据id查询对应信息
@Override
public ResponseResult selectById(Long id) {
    Menu menu = getById(id);
    MenuVo menuVo = BeanCopyUtils.copyBean(menu, MenuVo.class);
    return ResponseResult.okResult(menuVo);
}
  1. 修改,要求 不能使父菜单设置成为自己本身
代码语言:javascript
复制
@PutMapping
public ResponseResult updateMenu(@RequestBody Menu menu){
    return menuService.updateMenu(menu);
}
代码语言:javascript
复制
//todo 更新菜单
//不能让菜单的父菜单 == 菜单本身
@Override
public ResponseResult updateMenu(Menu menu) {
    System.out.println(menu);
    if(menu.getParentId().equals(menu.getId())){
        return ResponseResult.errorResult(AppHttpCodeEnum.PARENT_NOT_SELF);
    }
    LambdaQueryWrapper<Menu> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(Menu::getId,menu.getId());
    remove(wrapper);
    save(menu);
    return ResponseResult.okResult();
}

删除菜单

要求 : 不能删除有子菜单的,逻辑删除

代码语言:javascript
复制
@DeleteMapping("/{id}")
public ResponseResult deleteById(@PathVariable Long id){
    return menuService.deleteById(id);
}
代码语言:javascript
复制
//todo 根据id删除菜单
//不能删除有子菜单的父菜单
@Override
public ResponseResult deleteById(Long id) {
    //查询是否有父菜单
    Menu menu = getById(id);
    List<Menu> list = list();
    for(Menu children: list) {
        if (children.getParentId().equals(id)) {
            System.out.println("不能删除!");
            return ResponseResult.errorResult(AppHttpCodeEnum.CHILDREN_NOT_NULL);
        }
    }
    UpdateWrapper<Menu> wrapper= new UpdateWrapper<>();
    wrapper.eq("id",id);
    wrapper.set("del_flag",SystemConstants.DELETE);
    update(wrapper);
    return ResponseResult.okResult();
}

角色列表

接口

image-20230330191331750
image-20230330191331750
image-20230330191955499
image-20230330191955499

实现

代码语言:javascript
复制
//todo 获取角色列表
@GetMapping("/list")
public ResponseResult getList(int pageNum, int pageSize,String roleName,String status){
    return roleService.getList(pageNum,pageSize,roleName,status);
}
代码语言:javascript
复制
/**
 * 获取所有的角色, 需要可以根据角色名 and 状态 模糊查询
 * @param pageNum 页数
 * @param pageSize 每页大小
 * @param roleName 角色名
 * @param status 状态
 * @return 封装返回
 */
@Override
public ResponseResult getList(int pageNum, int pageSize, String roleName, String status) {
    //1. 先查询出未删除的
    LambdaQueryWrapper<Role> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(Role::getDelFlag, SystemConstants.NOT_DELETE);
    //2. 按照求排序
    wrapper.orderByAsc(Role::getRoleSort);
    //3. 进行模糊查询
    wrapper.like(Objects.nonNull(roleName),Role::getRoleName,roleName);
    wrapper.like(Objects.nonNull(status),Role::getStatus,status);
    //4. 分页
    Page<Role> page = new Page<>(pageNum,pageSize);
    page(page,wrapper);
    //5. 封装返回
    List<Role> records = page.getRecords();
    List<RoleVo> roleVos = BeanCopyUtils.copyBeanList(records, RoleVo.class);
    PageVo pageVos = new PageVo(roleVos,page.getTotal());
    return ResponseResult.okResult(pageVos);
}

改变角色状态

接口

image-20230330194015196
image-20230330194015196

实现

代码语言:javascript
复制

/**
 * 这里注意, 前端接口中的请求体 中定义的id 不是和数据库中对应的 而使roleId
 * @param roleDto
 * @return
 */
@PutMapping("/changeStatus")
public ResponseResult changeStatus(@RequestBody RoleDto roleDto){
    System.out.println("----------"+ roleDto);
    return roleService.changeStatus(roleDto);
}
代码语言:javascript
复制
/**
 * 改变角色状态
 * @param roleDto 封装角色id 和状态
 * @return
 */
@Override
public ResponseResult changeStatus(RoleDto roleDto) {
    UpdateWrapper<Role> wrapper = new UpdateWrapper<>();
    wrapper.eq("id",roleDto.getRoleId());
    wrapper.set("status",roleDto.getStatus());
    update(wrapper);
    Role byId = getById(roleDto.getRoleId());
    return ResponseResult.okResult();
}

新增角色

接口

image-20230330200602548
image-20230330200602548

响应格式:

image-20230330200905395
image-20230330200905395
  1. 实现新增接口
image-20230330201053562
image-20230330201053562

实现

代码语言:javascript
复制
/**
 * 获取菜单下拉树列表
 */
@GetMapping("/treeselect")
public ResponseResult treeselect() {
    //复用之前的selectMenuList方法。方法需要参数,参数可以用来进行条件查询,而这个方法不需要条件,所以直接new Menu()传入
    List<Menu> menus = menuService.selectMenuList(new Menu());
    List<MenuTreeVo> options =  SystemConverter.buildMenuSelectTree(menus);
    return ResponseResult.okResult(options);
}

获取子菜单

代码语言:javascript
复制
public class SystemConverter {

    private SystemConverter() {
    }


    public static List<MenuTreeVo> buildMenuSelectTree(List<Menu> menus) {
        List<MenuTreeVo> MenuTreeVos = menus.stream()
                .map(m -> new MenuTreeVo(null, m.getId(), m.getMenuName(), m.getParentId()))
                .collect(Collectors.toList());
        List<MenuTreeVo> options = MenuTreeVos.stream()
                .filter(o -> o.getParentId().equals(0L))
                .map(o -> o.setChildren(getChildList(MenuTreeVos, o)))
                .collect(Collectors.toList());
        return options;
    }


    /**
     * 得到子节点列表
     */
    private static List<MenuTreeVo> getChildList(List<MenuTreeVo> list, MenuTreeVo option) {
        List<MenuTreeVo> options = list.stream()
                .filter(o -> Objects.equals(o.getParentId(), option.getId()))
                .map(o -> o.setChildren(getChildList(list, o)))
                .collect(Collectors.toList());
        return options;

    }
}
  1. 添加角色
代码语言:javascript
复制
@PostMapping
public ResponseResult AddRole(@RequestBody RoleDto roleDto){
    return roleService.AddRole(roleDto);
}
代码语言:javascript
复制
@Override
public ResponseResult AddRole(RoleDto roleDto) {
    Role role = BeanCopyUtils.copyBean(roleDto, Role.class);
    save(role);
    return ResponseResult.okResult();
}

修改角色信息

接口

  1. 信息回显
image-20230402121643054
image-20230402121643054
  1. 加载角色菜单树请求
image-20230402122531724
image-20230402122531724
  1. 进行修改
image-20230402125750495
image-20230402125750495

实现

  1. 实现信息回显
代码语言:javascript
复制
/**
 * 根据id获取需要修改的角色信息
 * @param id 角色id
 * @return
 */
@Override
public ResponseResult getRoleById(Long id) {
    Role role = getById(id);
    RoleVo roleVo = BeanCopyUtils.copyBean(role, RoleVo.class);
    return ResponseResult.okResult(roleVo);
}
  1. 实现权限树的回显
代码语言:javascript
复制
/**
 * 回显对应id的角色的权限树
 * @param id
 * @return
 */
@GetMapping("/roleMenuTreeselect/{id}")
public ResponseResult roleMenuTreeSelect(@PathVariable Long id){

    //1. 先查寻对应角色的权限id集合, 然后,将id集合一一对应查询出对应的权限集合
    List<Menu> menus = menuService.selectMenuList(new Menu());
    LambdaQueryWrapper<RoleMenu> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(RoleMenu::getRoleId,id);
    List<RoleMenu> list = roleMenuService.list(wrapper);    //对应的id集合
    List<Long> ids = new ArrayList<>();
    for(RoleMenu menu : list){
        Long menuId = menu.getMenuId();
        ids.add(menuId);
    }
    List<MenuTreeVo> menuTreeVos = SystemConverter.buildMenuSelectTree(menus);
    RoleMenuTreeSelectVo vo = new RoleMenuTreeSelectVo(ids, menuTreeVos);
    return ResponseResult.okResult(vo);
}
  1. 实现信息修改
代码语言:javascript
复制
/**
 * 进行修改 ,注意关联对应的菜单树信息
 * @param role 传入对应的信息
 * @return
 */
@Override
public ResponseResult updateRole(Role role) {
    updateById(role);
    //删除之前的菜单关系
    LambdaQueryWrapper<RoleMenu> roleMenuLambdaQueryWrapper = new LambdaQueryWrapper<>();
    roleMenuLambdaQueryWrapper.eq(RoleMenu::getRoleId,role.getId());
    roleMenuService.remove(roleMenuLambdaQueryWrapper);
    //重新建立菜单关系
    //还需要将角色和对应的菜单权限信息添加
    Long[] menuIds = role.getMenuIds();
    for(Long id : menuIds){
        roleMenuService.save(new RoleMenu(role.getId(),id));
    }
    return ResponseResult.okResult();
}

删除角色

接口&实现

代码语言:javascript
复制
@DeleteMapping("/{id}")
public ResponseResult deleteRole(@PathVariable Long id){
    return roleService.deleteRole(id);
}
代码语言:javascript
复制
/**
 * 根据id删除角色信息, 注意还要删除对应的角色关联的菜单
 * 还有就是逻辑删除
 * @param id 角色id
 * @return
 */
@Override
public ResponseResult deleteRole(Long id) {
    UpdateWrapper<Role> wrapper = new UpdateWrapper<>();
    wrapper.eq("id",id);
    wrapper.set("del_flag",SystemConstants.DELETE);
    update(wrapper);
    //删除对应的关联信息
    LambdaQueryWrapper<RoleMenu> roleMenuLambdaQueryWrapper = new LambdaQueryWrapper<>();
    roleMenuLambdaQueryWrapper.eq(RoleMenu::getRoleId,id);
    roleMenuService.remove(roleMenuLambdaQueryWrapper);
    return ResponseResult.okResult();
}

用户列表

接口

image-20230402133128250
image-20230402133128250
image-20230402133613323
image-20230402133613323

实现

代码语言:javascript
复制
//todo 后台获取用户列表
@GetMapping("/list")
public ResponseResult list(int pageNum ,int pageSize,String userName,String status,String phonenumber){

    return userService.listAll(pageNum,pageSize,userName,status,phonenumber);
}
代码语言:javascript
复制
/**
 * 进行用户列表查询
 * 要求: 1. 能够进行分页展示 2. 可以通过用户名 进行模糊查询 3. 可以通过手机号、状态进行查询
 * @param pageNum 当前页
 * @param pageSize 分页大小
 * @param userName 用户名
 * @param phonenumber 手机号
 * @return
 */
@Override
public ResponseResult listAll(int pageNum, int pageSize, String userName, String status,String phonenumber) {
    // 可以通过用户名 进行模糊查询
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    //可以通过手机号、状态进行查询
    wrapper.like(StringUtils.hasText(userName),User::getUserName,userName);
    wrapper.eq(StringUtils.hasText(status),User::getStatus,status);
    wrapper.eq(StringUtils.hasText(phonenumber),User::getPhonenumber,phonenumber);
    // 能够进行分页展示
    Page<User> page = new Page<>(pageNum,pageSize);
    page(page,wrapper);
    List<User> records = page.getRecords();
    List<UserVo> userVos = BeanCopyUtils.copyBeanList(records, UserVo.class);
    PageVo pageVo = new PageVo(userVos,page.getTotal());
    return ResponseResult.okResult(pageVo);
}

新增用户

需求分析

  1. 首先要返回所有的角色列表(状态正常,没有删除的 )
  2. 用户输入密码存储时需要进行加密存储
  3. 相关信息不能为空
  4. 相关用户名、手机号、邮箱…不能相同

接口

  1. 查询角色列表
image-20230402135245059
image-20230402135245059
  1. 添加用户
image-20230402140509829
image-20230402140509829

实现

代码语言:javascript
复制
/**
 * 新增用户
 1. 首先要返回所有的角色列表(状态正常,没有删除的    )
 2. 用户输入密码存储时需要进行加密存储
 3. 相关信息不能为空
 4. 相关用户名、手机号、邮箱...不能相同
 * @param userDto
 * @return
 */
@Override
@Transactional
public ResponseResult addUser(User userDto) {
    //对数据进行非空判断 要求用户名 密码 等都不为空
    if(!StringUtils.hasText(userDto.getUserName())){
        ResponseResult.errorResult(AppHttpCodeEnum.USERNAME_NOT_NULL);
    }
    if(!StringUtils.hasText(userDto.getPassword())){
        ResponseResult.errorResult(AppHttpCodeEnum.PASSWORD_NOT_NULL);
    }
    if( !StringUtils.hasText(userDto.getEmail())){
        ResponseResult.errorResult(AppHttpCodeEnum.EMAIL_NOT_NULL);
    }
    if( !StringUtils.hasText(userDto.getNickName())){
        ResponseResult.errorResult(AppHttpCodeEnum.NICKNAME_EXIST);
    }
    //判断数据库中是否存在用户
    if(usernameExist(userDto.getUserName())){
        //用户已经存在
        ResponseResult.errorResult(USERNAME_EXIST);
    }
    if(nickNameExist(userDto.getNickName())){
        //昵称存在
        ResponseResult.errorResult(NICKNAME_EXIST);
    }
    if(phoneNumberExist(userDto.getPhonenumber())){
        //昵称存在
        ResponseResult.errorResult(PHONENUMBER_EXIST);
    }
    if(EmailExist(userDto.getEmail())){
        //昵称存在
        ResponseResult.errorResult(EMAIL_EXIST);
    }
    //密码加密处理
    System.out.println("-------------" + userDto.getPassword());
    userDto.setPassword(passwordEncoder.encode(userDto.getPassword())); //设置加密之后的密码

    save(userDto);  
    //添加对应的角色用户信息关系
    List<Long> roleIds = userDto.getRoleIds();
    for (Long roleId : roleIds){
        userRoleService.save(new UserRole(userDto.getId(),roleId));
    }
    return ResponseResult.okResult();
}

修改用户

接口

  1. 回显信息
image-20230402150040611
image-20230402150040611
  1. 更新
image-20230402150017375
image-20230402150017375

实现

  1. 获取信息
代码语言:javascript
复制
/**
 * 回显要删除的用户信息
 * 需要回显用户关联的角色状态
 * @param id 用户id
 * @return
 */
@Override
public ResponseResult getUserById(Long id) {
    //1. 查询用户信息回显
    User user = getById(id);
    //2. 查询角色信息
    List<Role> roles = roleService.list();
    //3. 查询用户角色管理信息
    LambdaQueryWrapper<UserRole> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(UserRole::getUserId,id);
    List<UserRole> list = userRoleService.list(wrapper);
    List<Long> ids = new ArrayList<>();
    for(UserRole userRole : list){
        ids.add(userRole.getRoleId());
    }
    UserUpdateDataVo vo = new UserUpdateDataVo(ids, roles, user);
    return ResponseResult.okResult(vo);
}
  1. 进行修改
代码语言:javascript
复制
/**
 * 更新用户信息
 * @param user 用户信息
 * @return
 */
@Override
@Transactional
public ResponseResult updateUser(User user) {
    updateById(user);
    LambdaQueryWrapper<UserRole> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(UserRole::getUserId,user.getId());
    userRoleService.remove(wrapper);
    //添加对应的角色用户信息关系
    List<Long> roleIds = user.getRoleIds();
    for (Long roleId : roleIds){
        userRoleService.save(new UserRole(user.getId(),roleId));
    }
    return ResponseResult.okResult();
}

删除用户

接口&实现

image-20230402144510809
image-20230402144510809
代码语言:javascript
复制
/**
 * 逻辑删除用户
 * @param id
 * @return
 */
@Override
public ResponseResult deleteUser(Long id) {
    UpdateWrapper<User> wrapper = new UpdateWrapper<>();
    wrapper.eq("id",id);
    wrapper.set("del_flag", SystemConstants.DELETE);
    update(wrapper);
    return ResponseResult.okResult();
}

分类查询

接口

image-20230402162209400
image-20230402162209400
image-20230402162215972
image-20230402162215972

实现

代码语言:javascript
复制
//todo 获取所有分类,并分页展示
@GetMapping("list")
public ResponseResult listAll(int pageNum ,int pageSize,CategoryVo categoryVo){
    return categoryService.listAllPage(pageNum,pageSize,categoryVo);

}
代码语言:javascript
复制
/**
 * 分页导出所有分类
 * @param pageNum 页码
 * @param pageSize 分页大小
 * @return
 */
@Override
public ResponseResult listAllPage(int pageNum, int pageSize,CategoryVo categoryVo) {
    //先获取所有可用的分类
    LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
    //进行模糊查询
    queryWrapper.like(Objects.nonNull(categoryVo.getName()),Category::getName,categoryVo.getName());
    queryWrapper.like(Objects.nonNull(categoryVo.getStatus()),Category::getStatus,categoryVo.getStatus());
    //判断是否可用
    queryWrapper.eq(Category::getStatus, SystemConstants.CATEGORY_STATUS);//没有被禁用的
    queryWrapper.eq(Category::getDelFlag,SystemConstants.CATEGORY_NOTDEL);//没有被删除的
    //进行分页处理
    Page<Category> page = new Page<>(pageNum,pageSize);
    page(page,queryWrapper);
    //按照响应格式返回
    List<Category> records = page.getRecords();
    List<CategoryVo> list = BeanCopyUtils.copyBeanList(records, CategoryVo.class);
    PageVo pageVo = new PageVo(list,page.getTotal());
    return ResponseResult.okResult(pageVo);
}

新增分类

接口

image-20230402162430663
image-20230402162430663

实现

代码语言:javascript
复制
/**
 * 新增分类
 * @param categoryVo
 * @return
 */
@Override
public ResponseResult addCategory(CategoryVo categoryVo) {
    Category category = BeanCopyUtils.copyBean(categoryVo, Category.class);
    save(category);
    return ResponseResult.okResult();
}

修改分类

接口

image-20230402162935277
image-20230402162935277
  1. 修改
image-20230402163345281
image-20230402163345281

实现

代码语言:javascript
复制
/**
 * 根据id获取分类信息
 */
@Override
public ResponseResult getCategoryById(Long id) {
    Category byId = getById(id);
    CategoryVo categoryVo = BeanCopyUtils.copyBean(byId, CategoryVo.class);
    return ResponseResult.okResult(categoryVo);
}

/**
 * 更新分类
 * @param categoryVo
 * @return
 */
@Override
@Transactional
public ResponseResult updateCategory(CategoryVo categoryVo) {
    Category category = BeanCopyUtils.copyBean(categoryVo, Category.class);
    updateById(category);
    return ResponseResult.okResult();
}

删除分类

接口& 实现

image-20230402163637266
image-20230402163637266
代码语言:javascript
复制
/**
 * 逻辑删除分类
 * @param id
 * @return
 */
@Override
public ResponseResult deleteCategory(Long id) {
    UpdateWrapper<Category> wrapper = new UpdateWrapper<>();
    wrapper.eq("id",id);
    wrapper.set("del_flag",SystemConstants.DELETE);
    update(wrapper);
    return ResponseResult.okResult();
}

友链列表

接口

image-20230402164805566
image-20230402164805566
代码语言:javascript
复制
@GetMapping("/list")
public ResponseResult getAllLink(int pageNum ,int pageSize ,String name ,String status){
    return linkService.getAll(pageNum,pageSize,name,status);
}
代码语言:javascript
复制
@Override
public ResponseResult getAll(int pageNum, int pageSize, String name, String status) {
    LambdaQueryWrapper<Link> wrapper = new LambdaQueryWrapper<>();
    wrapper.like(StringUtils.hasText(name),Link::getName,name);
    wrapper.eq(StringUtils.hasText(status),Link::getStatus,status);
    Page<Link> page = new Page<>(pageNum,pageSize);
    page(page,wrapper);
    PageVo pageVo = new PageVo(page.getRecords(),page.getTotal());
    return ResponseResult.okResult(pageVo);
}

新增友链

接口

image-20230402165612366
image-20230402165612366
代码语言:javascript
复制
@PostMapping
public ResponseResult addLink(@RequestBody Link link){
    return ResponseResult.okResult(linkService.save(link));
}

修改友链

代码语言:javascript
复制
@GetMapping("/{id}")
public ResponseResult getLinkById(@PathVariable Long id){
    Link byId = linkService.getById(id);
    return ResponseResult.okResult(byId);
}
@PutMapping
@Transactional
public ResponseResult updateLink(@RequestBody Link link){
    linkService.updateById(link);
    return ResponseResult.okResult();
}

删除友链

代码语言:javascript
复制
@DeleteMapping("/{id}")
public ResponseResult deleteLink(@PathVariable Long id){
    UpdateWrapper<Link> wrapper = new UpdateWrapper<>();
    wrapper.eq("id",id);
    wrapper.set("del_flag", SystemConstants.DELETE);
    linkService.update(wrapper);
    return ResponseResult.okResult();
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-03-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 博客后台
    • AOP实现日志记录
      • 需求
      • 实现
    • Swagger2
      • 基本使用
      • 【dto对象 :数据传输对象】
    • 获取所有标签
      • 接口
      • 实现
    • 后台登录、登出
      • 接口
      • 实现
    • 后台权限控制及其动态路由
      • 表分析
    • 获取当前用户的权限和角色信息
      • 接口(getInfo)
      • 实现getInfo
      • 动态路由接口(getRouters)
      • 实现getRouters
    • 后台项目改进点
      • 改进
    • 查询标签列表
      • 接口
      • 实现
    • 新增标签
      • 接口
      • 实现
    • 删除标签
      • 接口
      • 实现
    • 修改标签
      • 接口
      • 实现
    • 写文章
      • 接口
      • 实现
    • 修改文章
      • 接口
      • 实现
    • 删除文章
      • 接口
      • 实现
    • 文章列表
      • 接口
      • 实现
    • 导出Excel文件
      • 接口
      • 实现
    • 权限控制
      • 操作
    • 菜单列表
      • 接口
      • 实现
    • 新增菜单
      • 接口
      • 实现
    • 修改菜单
      • 接口
      • 实现
    • 删除菜单
      • 角色列表
        • 接口
        • 实现
      • 改变角色状态
        • 接口
        • 实现
      • 新增角色
        • 接口
        • 实现
      • 修改角色信息
        • 接口
        • 实现
      • 删除角色
        • 接口&实现
      • 用户列表
        • 接口
        • 实现
      • 新增用户
        • 需求分析
        • 接口
        • 实现
      • 修改用户
        • 接口
        • 实现
      • 删除用户
        • 接口&实现
      • 分类查询
        • 接口
        • 实现
      • 新增分类
        • 接口
        • 实现
      • 修改分类
        • 接口
        • 实现
      • 删除分类
        • 接口& 实现
      • 友链列表
        • 接口
      • 新增友链
        • 接口
      • 修改友链
        • 删除友链
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档