SpringBoot+Vue博客前后端分离项目 四评论 后端(springboot + mybatisplus+redis+mysql+jwt+缓存)

目录

Bug修正

1. 评论列表

1.1涉及的表以及pojo

1.1 接口说明

1.2 Controller

1.3 Service

2. 评论

2.1 接口说明

2.2 加入到登录拦截器中

2.3 Controller


 

Bug修正

之前Article中的commentCounts,viewCounts,weight 字段为int,会造成更新阅读次数的时候,将其余两个字段设为初始值0,

@Data
public class Article {

    public static final int Article_TOP = 1;

    public static final int Article_Common = 0;

    private Long id;

    private String title;

    private String summary;

    private Integer commentCounts;

    private Integer viewCounts;

    /**
     * 作者id
     */
    private Long authorId;
    /**
     * 内容id
     */
    private Long bodyId;
    /**
     *类别id
     */
    private Long categoryId;

    /**
     * 置顶
     */
    private Integer weight;


    /**
     * 创建时间
     */
    private Long createDate;
}

MyBatisPlus会修改updateArticle中不为null的值

@Service
public class ThreadService {
    /**
     * 更新阅读数量
     *
     * @param articleMapper
     * @param article
     */

    @Async("taskExecutor")
    //期望此操作在线程池执行 不会影响原有的主线程
    public void updateViewCount(ArticleMapper articleMapper, Article article) {
        Integer viewCounts = article.getViewCounts();
        Article updateArticle = new Article();
        updateArticle.setViewCounts(viewCounts + 1);
        LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Article::getId,article.getId());
        //多个线程操作同一个变量
        //为了线程安全 如果操作的时候发现与期望的阅读数量不一致,修改失败
        queryWrapper.eq(Article::getViewCounts,viewCounts);
        //UPDATE ms_article SET view_counts=? WHERE (id = ? AND view_counts = ?)
        articleMapper.update(updateArticle,queryWrapper);
    }
}

1. 评论列表

1.1涉及的表以及pojo

CREATE TABLE `blog`.`ms_comment`  (
  `id` bigint(0) NOT NULL AUTO_INCREMENT,
  `content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  `create_date` bigint(0) NOT NULL,
  `article_id` int(0) NOT NULL,
  `author_id` bigint(0) NOT NULL,
  `parent_id` bigint(0) NOT NULL,
  `to_uid` bigint(0) NOT NULL,
  `level` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `article_id`(`article_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
package com.pjp.blog.dao.pojo;

import lombok.Data;

@Data
public class Comment {

    private Long id;

    private String content;

    private Long createDate;

    private Long articleId;

    private Long authorId;

    private Long parentId;

    private Long toUid;

    private Integer level;
}

1.1 接口说明

接口url:/comments/article/{id}

请求方式:GET

请求参数:

参数名称

参数类型

说明

id

long

文章id(路径参数)

返回数据:

{
  "success": true,
  "code": 200,
  "msg": "success",
  "data": [
    {
      "id": "53",
      "author": {
        "nickname": "土豆",
        "avatar": "/static/user/user_1.png",
        "id": 1689624302930862082
      },
      "content": "good job",
      "children": [
        {
          "id": "54",
          "author": {
            "nickname": "pjp",
            "avatar": "http://localhost:8080/static/user/admin.png",
            "id": 1
          },
          "content": "厉害呀!土豆",
          "children": [],
          "createDate": "1970-01-20 21:58",
          "level": 2,
          "toUser": {
            "nickname": "土豆",
            "avatar": "/static/user/user_1.png",
            "id": 1689624302930862082
          }
        }
      ],
      "createDate": "1970-01-20 21:57",
      "level": 1,
      "toUser": null
    },
    {
      "id": "55",
      "author": {
        "nickname": "pjp",
        "avatar": "http://localhost:8080/static/user/admin.png",
        "id": 1
      },
      "content": "写的好",
      "children": [
        {
          "id": "56",
          "author": {
            "nickname": "pjp",
            "avatar": "http://localhost:8080/static/user/admin.png",
            "id": 1
          },
          "content": "111",
          "children": [],
          "createDate": "1973-11-26 08:52",
          "level": 2,
          "toUser": {
            "nickname": "pjp",
            "avatar": "http://localhost:8080/static/user/admin.png",
            "id": 1
          }
        }
      ],
      "createDate": "1973-11-27 09:53",
      "level": 1,
      "toUser": null
    }
  ]
}

1.2 Controller

package com.pjp.blog.controller;

import com.pjp.blog.service.CommentsService;
import com.pjp.blog.vo.Result;
import com.pjp.blog.vo.params.CommentParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("comments")
public class CommentsController {
    @Autowired
    private CommentsService commentsService;

    @GetMapping("article/{id}")
    public Result comments(@PathVariable("id") Long articleId) {
        return commentsService.findCommentsByArticleId(articleId);
    }
}

1.3 Service

package com.pjp.blog.service;

import com.pjp.blog.vo.Result;
import com.pjp.blog.vo.params.CommentParam;

public interface CommentsService {
    /**
     * 根据文章Id 查找评论列表
     * @param articleId
     * @return
     */
    Result findCommentsByArticleId(Long articleId);
}
package com.pjp.blog.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.pjp.blog.dao.mapper.CommentsMapper;
import com.pjp.blog.dao.pojo.Comment;
import com.pjp.blog.dao.pojo.SysUser;
import com.pjp.blog.service.CommentsService;
import com.pjp.blog.service.SysUserService;
import com.pjp.blog.utils.UserThreadLocal;
import com.pjp.blog.vo.CommentVo;
import com.pjp.blog.vo.Result;
import com.pjp.blog.vo.UserVo;
import com.pjp.blog.vo.params.CommentParam;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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

@Service
public class CommentsServiceImpl implements CommentsService {

    @Autowired
    private CommentsMapper commentsMapper;

    @Autowired
    private SysUserService sysUserService;

    @Override
    public Result findCommentsByArticleId(Long articleId) {
        /**
         * 1. 根据文章id 查询 评论列表 从 comment 表中查询
         * 2. 根据作者的id 查询作者的信息
         * 3. 判断 如果level = 1 要去查询它有没有子评论
         * 4. 如果有 根据评论id 进行查询 (parent_id)
         */
        LambdaQueryWrapper<Comment> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Comment::getArticleId, articleId);
        queryWrapper.eq(Comment::getLevel, 1);
        List<Comment> comments = commentsMapper.selectList(queryWrapper);
        return Result.success(copyList(comments));
    }

   @Override
    public Result comment(CommentParam commentParam) {
        SysUser sysUser = UserThreadLocal.get();
        Comment comment = new Comment();
        comment.setArticleId(commentParam.getArticleId());
        comment.setAuthorId(sysUser.getId());
        comment.setContent(commentParam.getContent());
        comment.setCreateDate(System.currentTimeMillis());
        Long parent = commentParam.getParent();
        if (parent == null || parent == 0) {
            comment.setLevel(1);
        } else {
            comment.setLevel(2);
        }
        comment.setParentId(parent == null ? 0 : parent);
        Long toUserId = commentParam.getToUserId();
        comment.setToUid(toUserId == null ? 0 : toUserId);
        this.commentsMapper.insert(comment);
        UpdateWrapper<Article> updateWrapper = Wrappers.update();
        updateWrapper.eq("id",comment.getArticleId());
        updateWrapper.setSql(true,"comment_counts=comment_counts+1");
        this.articleMapper.update(null,updateWrapper);
        CommentVo commentVo = copy(comment);
        return Result.success(commentVo);
    }


    private List<CommentVo> copyList(List<Comment> comments) {
        List<CommentVo> commentVoList = new ArrayList<>();
        for (Comment comment : comments) {
            commentVoList.add(copy(comment));
        }
        return commentVoList;
    }


    private CommentVo copy(Comment comment) {
        CommentVo commentVo = new CommentVo();
        BeanUtils.copyProperties(comment, commentVo);
        //时间格式化
        commentVo.setCreateDate(new DateTime(comment.getCreateDate()).toString("YYYY-MM-dd HH:mm"));
        //作者信息
        Long authorId = comment.getAuthorId();
        UserVo userVo = sysUserService.findUserVoById(authorId);
        commentVo.setAuthor(userVo);
        //查询评论的评论 去comment表中找parent_id等于当前评论的id
        List<CommentVo> childrenComlist = findChildrenComById(comment.getId());
        commentVo.setChildren(childrenComlist);
        //to user 评论的评论 给谁的评论评论
        Integer level = comment.getLevel();
        if (level > 1) {
        Long toUid = comment.getToUid();
        UserVo userVoById = sysUserService.findUserVoById(toUid);
        commentVo.setToUser(userVoById);
    }
        return commentVo;
    }


        private List<CommentVo> findChildrenComById(Long id) {
        LambdaQueryWrapper<Comment> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Comment::getParentId, id);
        queryWrapper.eq(Comment::getLevel, 2);
        List<Comment> comments = commentsMapper.selectList(queryWrapper);
        return copyList(comments);
    }


    }

返回的数据:

@Data
public class CommentVo  {

    private Long id;

    private UserVo author;

    private String content;

    private List<CommentVo> childrens;

    private String createDate;

    private Integer level;

    private UserVo toUser;
}
@Data
public class UserVo {

    private String nickname;

    private String avatar;

    private Long id;
}

在SysUserService中提供 查询用户信息的服务:

/**
     * 根据 id 查询用户 转换为UserVo
     * @param authorId
     * @return
     */
    UserVo findUserVoById(Long authorId);
package com.pjp.blog.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.pjp.blog.dao.mapper.SysUserMapper;
import com.pjp.blog.dao.pojo.SysUser;
import com.pjp.blog.service.SysUserService;
import com.pjp.blog.service.TokenService;
import com.pjp.blog.utils.JWTUtils;
import com.pjp.blog.vo.ErrorCode;
import com.pjp.blog.vo.LoginUserVo;
import com.pjp.blog.vo.Result;
import com.pjp.blog.vo.UserVo;
import org.apache.catalina.User;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Map;

@Service
public class SysUserServiceIml implements SysUserService {

    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private TokenService tokenService;


    @Override
    public UserVo findUserVoById(Long authorId) {
        SysUser sysUser = sysUserMapper.selectById(authorId);
        if (sysUser == null) {
            sysUser = new SysUser();
            sysUser.setId(1L);
            sysUser.setNickname("游客");
            sysUser.setAvatar("/static/user/user_6.png");
        }
        UserVo userVo = new UserVo();
        BeanUtils.copyProperties(sysUser, userVo);
        return userVo;
    }

}

测试  成功显示一级评论和二级评论

2. 评论

2.1 接口说明

接口url:/comments/create/change

请求方式:POST

请求参数:

参数名称

参数类型

说明

articleId

long

文章id

content

string

评论内容

parent

long

父评论id

toUserId

long

被评论的用户id

返回数据:

{
    "success": true,
    "code": 200,
    "msg": "success",
    "data": null
}

2.2 加入到登录拦截器中

package com.pjp.blog.config;


import com.pjp.blog.handler.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMVCConfig implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //        registry.addInterceptor(loginInterceptor).addPathPatterns("/**")
        //                .excludePathPatterns("/login").excludePathPatterns("/register");
        //        拦截test接口,后续遇到实际需要拦截的接口时,再配置拦截相应的接口
        registry.addInterceptor(loginInterceptor)
        .addPathPatterns("/test")
        .addPathPatterns("/comments/create/change");
        ;
    }
}

2.3 Controller

构建评论参数对象:

@Data
public class CommentParam {

    private Long articleId;

    private String content;

    private Long parent;

    private Long toUserId;
}
@RestController
@RequestMapping("comments")
public class CommentsController {
    @Autowired
    private CommentsService commentsService;

    @PostMapping("create/change")
    public Result comment(@RequestBody CommentParam commentParam) {
        return commentsService.comment(commentParam);
    }
}
//防止前端 精度损失 把id转为string
// 分布式id 比较长,传到前端 会有精度损失,必须转为string类型 进行传输,就不会有问题了
    @JsonSerialize(using = ToStringSerializer.class)
    private Long id;

 输入评论 即可成功评论

点击评论

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PJP__00

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值