探店笔记类似点评网站的评价,往往是图文结合。
对应的表有两个:
tb_blog:探店笔记表,包含笔记中的标题、文字、图片等
tb_blog_comments:其他用户对探店笔记的评价
流程如下:

上传接口:
- @Slf4j
- @RestController
- @RequestMapping("upload")
- public class UploadController {
-
- @PostMapping("blog")
- public Result uploadImage(@RequestParam("file") MultipartFile image) {
- try {
- // 获取原始文件名称
- String originalFilename = image.getOriginalFilename();
- // 生成新文件名
- String fileName = createNewFileName(originalFilename);
- // 保存文件
- image.transferTo(new File(SystemConstants.IMAGE_UPLOAD_DIR, fileName));
- // 返回结果
- log.debug("文件上传成功,{}", fileName);
- return Result.ok(fileName);
- } catch (IOException e) {
- throw new RuntimeException("文件上传失败", e);
- }
- }
-
- }
博客接口:
- @RestController
- @RequestMapping("/blog")
- public class BlogController {
-
- @Resource
- private IBlogService blogService;
-
- @PostMapping
- public Result saveBlog(@RequestBody Blog blog) {
- //获取登录用户
- UserDTO user = UserHolder.getUser();
- blog.setUpdateTime(user.getId());
- //保存探店博文
- blogService.saveBlog(blog);
- //返回id
- return Result.ok(blog.getId());
- }
- }
查看探店笔记代码:
- @Override
- public Result queryBlogById(Long id) {
- // 1.查询blog
- Blog blog = getById(id);
- if (blog == null) {
- return Result.fail("笔记不存在!");
- }
- // 2.查询blog有关的用户
- queryBlogUser(blog);
-
- return Result.ok(blog);
- }
接口如下:
- @GetMapping("/likes/{id}")
- public Result queryBlogLikes(@PathVariable("id") Long id) {
- //修改点赞数量
- blogService.update().setSql("liked = liked +1 ").eq("id",id).update();
- return Result.ok();
- }
但是这种方式会导致一个用户无限点赞,明显是不合理的。造成这个问题的原因是,我们现在的逻辑,发起请求只是给数据库+1,所以才会出现这个问题。

我们需要实现以下功能:
同一个用户只能点赞一次,再次点击则取消点赞
如果当前用户已经点赞,则点赞按钮高亮显示(前端判断字段Blog类的isLike属性)
实现步骤:
给Blog类中添加一个isLike字段,标示是否被当前用户点赞
修改点赞功能,利用Redis的set集合判断是否点赞过,未点赞过则点赞数+1,已点赞过则点赞数-1
修改根据id查询Blog的业务,判断当前登录用户是否点赞过,赋值给isLike字段
修改分页查询Blog业务,判断当前登录用户是否点赞过,赋值给isLike字段
实现代码:
- @Override
- public Result likeBlog(Long id){
- // 1.获取登录用户
- Long userId = UserHolder.getUser().getId();
- // 2.判断当前登录用户是否已经点赞
- String key = BLOG_LIKED_KEY + id;
- Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, userId.toString());
- if(BooleanUtil.isFalse(isMember)){
- //3.如果未点赞,可以点赞
- //3.1 数据库点赞数+1
- boolean isSuccess = update().setSql("liked = liked + 1").eq("id", id).update();
- //3.2 保存用户到Redis的set集合
- if(isSuccess){
- stringRedisTemplate.opsForSet().add(key,userId.toString());
- }
- }else{
- //4.如果已点赞,取消点赞
- //4.1 数据库点赞数-1
- boolean isSuccess = update().setSql("liked = liked - 1").eq("id", id).update();
- //4.2 把用户从Redis的set集合移除
- if(isSuccess){
- stringRedisTemplate.opsForSet().remove(key,userId.toString());
- }
- }
然后把给该笔记点赞的人显示出来,比如最早点赞的TOP5,形成点赞排行榜,Redis使用sortedSet(代码中为ZSet):
点赞逻辑代码:
- @Override
- public Result likeBlog(Long id) {
- // 1.获取登录用户
- Long userId = UserHolder.getUser().getId();
- // 2.判断当前登录用户是否已经点赞
- String key = BLOG_LIKED_KEY + id;
- Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());
- if (score == null) {
- // 3.如果未点赞,可以点赞
- // 3.1.数据库点赞数 + 1
- boolean isSuccess = update().setSql("liked = liked + 1").eq("id", id).update();
- // 3.2.保存用户到Redis的set集合 zadd key value score
- if (isSuccess) {
- stringRedisTemplate.opsForZSet().add(key, userId.toString(), System.currentTimeMillis());
- }
- } else {
- // 4.如果已点赞,取消点赞
- // 4.1.数据库点赞数 -1
- boolean isSuccess = update().setSql("liked = liked - 1").eq("id", id).update();
- // 4.2.把用户从Redis的set集合移除
- if (isSuccess) {
- stringRedisTemplate.opsForZSet().remove(key, userId.toString());
- }
- }
- return Result.ok();
- }
-
-
- private void isBlogLiked(Blog blog) {
- // 1.获取登录用户
- UserDTO user = UserHolder.getUser();
- if (user == null) {
- // 用户未登录,无需查询是否点赞
- return;
- }
- Long userId = user.getId();
- // 2.判断当前登录用户是否已经点赞
- String key = "blog:liked:" + blog.getId();
- Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());
- blog.setIsLike(score != null);
- }
点赞列表查询:
接口:
- @GetMapping("/likes/{id}")
- public Result queryBlogLikes(@PathVariable("id") Long id) {
-
- return blogService.queryBlogLikes(id);
- }
逻辑代码:
- @Override
- public Result queryBlogLikes(Long id) {
- String key = BLOG_LIKED_KEY + id;
- // 1.查询top5的点赞用户 zrange key 0 4
- Set
top5 = stringRedisTemplate.opsForZSet().range(key, 0, 4); - if (top5 == null || top5.isEmpty()) {
- return Result.ok(Collections.emptyList());
- }
- // 2.解析出其中的用户id
- List
ids = top5.stream().map(Long::valueOf).collect(Collectors.toList()); - String idStr = StrUtil.join(",", ids);
- // 3.根据用户id查询用户 WHERE id IN ( 5 , 1 ) ORDER BY FIELD(id, 5, 1)
- List
userDTOS = userService.query() - .in("id", ids).last("ORDER BY FIELD(id," + idStr + ")").list()
- .stream()
- .map(user -> BeanUtil.copyProperties(user, UserDTO.class))
- .collect(Collectors.toList());
- // 4.返回
- return Result.ok(userDTOS);
- }