发布探店笔记
数据库表
- tb_blog:探店笔记表,包含笔记中的标题、文字、图片等
- tb_blog_comments:其他用户对探店笔记的评价

点击首页最下方+,发布探店图文
发笔记中的上传照片是单独的功能,点击上传照片后请求发上传到后端/upload接口,获取到照片后将其fileName返回前端。
两个功能的逻辑代码:
@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);}}
@PostMappingpublic Result saveBlog(@RequestBody Blog blog) {// 获取登录用户UserDTO user = UserHolder.getUser();blog.setUserId(user.getId());// 保存探店博文blogService.save(blog);// 返回idreturn Result.ok(blog.getId());}
查看探店笔记
需求:点击首页探店笔记进入详情界面,实现该查询接口
查看探店笔记Controller相关代码:
@GetMapping("/hot")public Result queryHotBlog(@RequestParam(value = "current", defaultValue = "1") Integer current) {return blogService.queryHotBlog(current);}@GetMapping("/{id}")public Result queryBlogById(@PathVariable("id")Long id){return blogService.queryBlogById(id);}
查看探店笔记ServiceImpl代码:
@Servicepublic class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {@Resourceprivate IUserService userService;@Overridepublic Result queryBlogById(Long id) {//1.查询blogBlog blog = getById(id);if (blog == null) {return Result.fail("博客不存在!");}//2.查询blog有关的用户queryBlogUser(blog);return Result.ok(blog);}@Overridepublic Result queryHotBlog(Integer current) {// 根据用户查询Page<Blog> page = query().orderByDesc("liked").page(new Page<>(current, SystemConstants.MAX_PAGE_SIZE));// 获取当前页数据List<Blog> records = page.getRecords();// 查询用户records.forEach(this::queryBlogUser);return Result.ok(records);}private void queryBlogUser(Blog blog) {Long userId = blog.getUserId();User user = userService.getById(userId);blog.setName(user.getNickName());blog.setIcon(user.getIcon());}}
点赞
首页的探店笔记排行榜和探店图文详情页面都有点赞的功能:
原点赞逻辑代码:
@PutMapping("/like/{id}")public Result likeBlog(@PathVariable("id") Long id) {// 修改点赞数量blogService.update().setSql("liked = liked + 1").eq("id", id).update();return Result.ok();}
点赞bug:一个用户可以无限次点赞
需求:
- 一个用户只能点赞一次,再次点击取消点赞
- 当前用户已点赞,高亮显示
实现:
- 给Blog添加isLike字段,判断是否被当前用户点赞
- 修改点赞功能,利用Redis的set集合判断是否点赞过,未点赞+1,已点赞-1
- 修改根据id查询Blog业务,判断是否点赞,赋值isLike字段
- 修改分页查询Blog业务,判断是否点赞,赋值isLike字段
/*** 是否点赞过了*/@TableField(exist = false)private Boolean isLike;
修改点赞功能
@Overridepublic void likeBlog(Long id) {//1.获取登录用户Long userId = UserHolder.getUser().getId();//2.判断当前用户是否点赞String key = "blog:liked:" + id;Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, userId.toString());if(BooleanUtil.isFalse(isMember)){//3.如果未点赞,可以点赞//3.1数据库点赞数+1boolean isSuccess = update().setSql("like = like + 1").eq("id", id).update();//3.2保存用户到Redis的set集合if(isSuccess){stringRedisTemplate.opsForSet().add(key, userId.toString());}}else {//4.如果已点赞,取消点赞//4.1数据库点赞-1boolean isSuccess = update().setSql("like = like + 1").eq("id", id).update();//4.2把用户从Redis的set集合移除if(isSuccess){stringRedisTemplate.opsForSet().remove(key, userId.toString());}}}
查询Blog是否点赞,修改两处:
在queryBlogById和queryHotBlog里接口添加
//3.查询blog是否被点赞isBlogLiked(blog);private void isBlogLiked(Blog blog) {//1.获取登录用户Long userId = UserHolder.getUser().getId();//2.判断当前用户是否点赞String key = "blog:liked:" + blog.getId();Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, userId.toString());blog.setIsLike(BooleanUtil.isTrue(isMember));}
点赞排行榜
在探店笔记详情页面,显示给该笔记点赞的Top5,形成点赞排行榜。
需求:按照点赞先后排序,返回Top5
对比后选择用SortedSet替代Set实现点赞排行榜功能,需要用到
修改原先点赞逻辑:
@Overridepublic void likeBlog(Long id) {//1.获取登录用户Long userId = UserHolder.getUser().getId();//2.判断当前用户是否点赞String key = "blog:liked:" + id;// Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, userId.toString());Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());if(score == null){//3.如果未点赞,可以点赞//3.1数据库点赞数+1boolean isSuccess = update().setSql("liked = liked + 1").eq("id", id).update();//3.2保存用户到Redis的set集合if(isSuccess){// stringRedisTemplate.opsForSet().add(key, userId.toString());stringRedisTemplate.opsForZSet().add(key, userId.toString(), System.currentTimeMillis());}}else {//4.如果已点赞,取消点赞//4.1数据库点赞-1boolean isSuccess = update().setSql("liked = liked - 1").eq("id", id).update();//4.2把用户从Redis的set集合移除if(isSuccess){// stringRedisTemplate.opsForSet().remove(key, userId.toString());stringRedisTemplate.opsForZSet().remove(key, userId.toString());}}}
查询点赞Top5
@Overridepublic Result queryBlogLikes(Long id) {//1.查询top5点赞用户String key = BLOG_LIKED_KEY + id;Set<String> top5 = stringRedisTemplate.opsForZSet().range(key, 0, 4);if(top5 == null || top5.isEmpty()){return Result.ok(Collections.emptyList());}//2.解析其中用户idList<Long> ids = top5.stream().map(Long::valueOf).collect(Collectors.toList());//3.根据用户id查询用户List<UserDTO> userDTOS = userService.listByIds(ids).stream().map(user -> BeanUtil.copyProperties(user, UserDTO.class)).collect(Collectors.toList());//4.返回return Result.ok(userDTOS);}
此处发现之前isBlogLiked代码存在bug,未登录无需判断是否点赞,所以在isBlogLiked里添加如下代码
private void isBlogLiked(Blog blog) {//1.获取登录用户UserDTO user = UserHolder.getUser();if (user == null) {//用户未登录,无需查询是否点赞return;}...}
排序Bug:使用ORDER BY FIELD
@Overridepublic Result queryBlogLikes(Long id) {...String idStr = StrUtil.join(",", ids);//3.根据用户id查询用户List<UserDTO> 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);}

