黑马点评--达人探店模块

达人探店模块 完整源代码 + 核心总结

1. 实体类 Blog

java 复制代码
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("tb_blog")
public class Blog implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    /**
     * 商户id
     */
    private Long shopId;

    /**
     * 用户id
     */
    private Long userId;

    /**
     * 用户图标
     */
    @TableField(exist = false)
    private String icon;

    /**
     * 用户姓名
     */
    @TableField(exist = false)
    private String name;

    /**
     * 标题
     */
    private String title;

    /**
     * 探店的照片,最多9张,多张以","隔开
     */
    private String images;

    /**
     * 探店的文字描述
     */
    private String content;

    /**
     * 点赞数量
     */
    private Integer liked;

    /**
     * 评论数量
     */
    private Integer comments;

    /**
     * 创建时间
     */
    private LocalDateTime createTime;

    /**
     * 更新时间
     */
    private LocalDateTime updateTime;

    /**
     * 是否被当前用户点赞
     */
    @TableField(exist = false)
    private Boolean isLike;
}

2. UploadController(图片上传)

java 复制代码
@Slf4j
@RestController
@RequestMapping("upload")
public class UploadController {

    @PostMapping("blog")
    public Result uploadImage(@RequestParam("file") MultipartFile image) {
        try {
            // 1.获取原始文件名称
            String originalFilename = image.getOriginalFilename();
            // 2.生成新文件名
            String fileName = createNewFileName(originalFilename);
            // 3.保存文件
            image.transferTo(new File(SystemConstants.IMAGE_UPLOAD_DIR, fileName));
            // 4.返回结果
            log.debug("文件上传成功,{}", fileName);
            return Result.ok(fileName);
        } catch (IOException e) {
            throw new RuntimeException("文件上传失败", e);
        }
    }

    // 生成随机文件名
    private String createNewFileName(String originalFilename) {
        String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
        return UUID.randomUUID().toString() + suffix;
    }
}

3. BlogController

java 复制代码
@RestController
@RequestMapping("/blog")
public class BlogController {

    @Resource
    private IBlogService blogService;

    // 发布探店笔记
    @PostMapping
    public Result saveBlog(@RequestBody Blog blog) {
        return blogService.saveBlog(blog);
    }

    // 查看笔记详情
    @GetMapping("/{id}")
    public Result queryBlogById(@PathVariable Long id) {
        return blogService.queryBlogById(id);
    }

    // 点赞/取消点赞
    @PutMapping("/like/{id}")
    public Result likeBlog(@PathVariable Long id) {
        return blogService.likeBlog(id);
    }

    // 点赞排行榜 Top5
    @GetMapping("/likes/{id}")
    public Result queryBlogLikes(@PathVariable Long id) {
        return blogService.queryBlogLikes(id);
    }

    // 分页查询热门笔记
    @GetMapping("/hot")
    public Result queryHotBlog(@RequestParam(value = "current", defaultValue = "1") Integer current) {
        return blogService.queryHotBlog(current);
    }
}

4. IBlogService

java 复制代码
public interface IBlogService extends IService<Blog> {
    Result saveBlog(Blog blog);

    Result queryBlogById(Long id);

    Result likeBlog(Long id);

    Result queryBlogLikes(Long id);

    Result queryHotBlog(Integer current);
}

5. BlogServiceImpl(核心业务)

java 复制代码
@Service
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {

    @Resource
    private IUserService userService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    public static final String BLOG_LIKED_KEY = "blog:liked:";

    // 发布笔记
    @Override
    public Result saveBlog(Blog blog) {
        // 1.获取登录用户
        UserDTO user = UserHolder.getUser();
        blog.setUserId(user.getId());
        // 2.保存探店笔记
        boolean save = save(blog);
        if (!save) {
            return Result.fail("发布笔记失败");
        }
        // 3.返回笔记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.查询发布者信息
        queryBlogUser(blog);
        // 3.查询是否被当前用户点赞
        isBlogLiked(blog);
        return Result.ok(blog);
    }

    // 点赞/取消点赞(ZSet 实现)
    @Override
    public Result likeBlog(Long id) {
        // 1.获取登录用户
        Long userId = UserHolder.getUser().getId();
        String key = BLOG_LIKED_KEY + id;
        // 2.判断是否点赞过
        Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());

        if (score == null) {
            // 3.未点赞:数据库+1,Redis保存
            boolean success = update().setSql("liked = liked + 1").eq("id", id).update();
            if (success) {
                stringRedisTemplate.opsForZSet().add(key, userId.toString(), System.currentTimeMillis());
            }
        } else {
            // 4.已点赞:数据库-1,Redis移除
            boolean success = update().setSql("liked = liked - 1").eq("id", id).update();
            if (success) {
                stringRedisTemplate.opsForZSet().remove(key, userId.toString());
            }
        }
        return Result.ok();
    }

    // 点赞排行榜 Top5
    @Override
    public Result queryBlogLikes(Long id) {
        String key = BLOG_LIKED_KEY + id;
        // 1.查询 Top5 用户id
        Set<String> top5 = stringRedisTemplate.opsForZSet().range(key, 0, 4);
        if (CollUtil.isEmpty(top5)) {
            return Result.ok(Collections.emptyList());
        }
        // 2.转换id
        List<Long> ids = top5.stream().map(Long::valueOf).collect(Collectors.toList());
        String idStr = StrUtil.join(",", ids);
        // 3.根据id查询用户,并保持排序
        List<UserDTO> userDTOList = userService.query()
                .in("id", ids)
                .last("ORDER BY FIELD(id," + idStr + ")")
                .list()
                .stream()
                .map(user -> BeanUtil.copyProperties(user, UserDTO.class))
                .collect(Collectors.toList());

        return Result.ok(userDTOList);
    }

    // 分页查询热门笔记
    @Override
    public Result queryHotBlog(Integer current) {
        Page<Blog> page = this.query()
                .orderByDesc("liked")
                .page(new Page<>(current, SystemConstants.MAX_PAGE_SIZE));

        List<Blog> records = page.getRecords();
        // 封装用户信息和点赞状态
        records.forEach(blog -> {
            queryBlogUser(blog);
            isBlogLiked(blog);
        });
        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());
    }

    // 封装:判断当前用户是否点赞
    private void isBlogLiked(Blog blog) {
        UserDTO user = UserHolder.getUser();
        if (user == null) {
            blog.setIsLike(false);
            return;
        }
        Long userId = user.getId();
        String key = BLOG_LIKED_KEY + blog.getId();
        Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());
        blog.setIsLike(score != null);
    }
}

二、核心知识点总结(你必须掌握)

1 注解使用(你最容易混淆)

  • 图片上传@RequestParam("file")
  • JSON提交笔记@RequestBody
  • 路径参数@PathVariable

2 点赞功能核心

  • 防止重复点赞:Redis ZSet
  • 数据库存数量、Redis存用户ID
  • 点赞:zadd 取消点赞:zrem

3 你最容易忘的引用传递

java 复制代码
queryBlogUser(blog);
isBlogLiked(blog);
  • 无返回值,直接修改原对象
  • 企业级通用:抽取私有方法复用代码

4 排行榜关键

  • zrange key 0 4 取Top5
  • ORDER BY FIELD 保证查询顺序和Redis一致

5 企业级规范

  • 图片不上传到本地,用Nginx/云存储
  • 点赞必须用Redis防重
  • 未登录用户做非空判断
  • 多表数据统一封装返回
相关推荐
!停2 小时前
C++入门基础—类和对象3
java·数据库·c++
寂静or沉默2 小时前
Java程序员技术面试:如何清晰描述项目难点?逻辑模板!Java的原因与解决方案最新发布!
java·开发语言·面试
东离与糖宝3 小时前
Gradle 9.4+Java26:大型项目构建提速100倍实战配置
java·人工智能
菜鸟程序员专写BUG3 小时前
SpringBoot 接口返回异常全集|JSON解析失败/响应乱码/状态码错误完美解决
spring boot·后端·json
想进大厂的小徐3 小时前
maven的子模块和子pom的区别
java·maven
pengles3 小时前
基于RuoYi-Vue-Plus项目实现移动端项目
java·vue.js·uni-app
希望永不加班3 小时前
SpringBoot 编写第一个 REST 接口(Get/Post/Put/Delete)
java·spring boot·后端·spring
菜菜小狗的学习笔记3 小时前
Java SE(一)内部类、Lambda表达式、Stream流
java·开发语言
码云数智-园园3 小时前
Java中的重载(Overload)与重写(Override):本质区别、场景与注意事项
java·开发语言