Redis筆記點(diǎn)贊排行榜的實現(xiàn)示例
一、發(fā)布探店筆記
探店筆記類似點(diǎn)評網(wǎng)站的評價,往往是圖文結(jié)合。對應(yīng)的表有兩個
- 探店筆記表(主鍵、商戶id、用戶id、標(biāo)題、文字、圖片、探店文字描述、點(diǎn)贊數(shù)量、評論數(shù)量)
- 評價表(筆記的評價)
先上傳圖片請求一次保存圖片接口,再點(diǎn)發(fā)布請求發(fā)布接口。這兩個接口已經(jīng)寫好
二、實現(xiàn)查看筆記接口
BlogController
@RestController @RequestMapping("/blog") public class BlogController { @Resource private IBlogService blogService; @GetMapping("/hot") public Result queryHotBlog(@RequestParam(value = "current", defaultValue = "1") Integer current) { return blogService.queryHotBlog(current); } @GetMapping("/{id}") public Result queryBlogById(@PathVariable("id") String id){ return blogService.queryBlogById(id); } }
IBlogService
public interface IBlogService extends IService<Blog> { Result queryBlogById(String id); Result queryHotBlog(Integer current); }
BlogServiceImpl
@Service public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService { @Autowired private IUserService userService; @Override public Result queryHotBlog(Integer current) { // 根據(jù)用戶查詢 Page<Blog> page = query() .orderByDesc("liked") .page(new Page<>(current, SystemConstants.MAX_PAGE_SIZE)); // 獲取當(dāng)前頁數(shù)據(jù) 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()); } @Override public Result queryBlogById(String id) { Blog blog = getById(id); if(blog == null){ return Result.fail("筆記不存在!"); } queryBlogUser(blog); return Result.ok(blog); } }
三、點(diǎn)贊功能
現(xiàn)在已經(jīng)寫好的點(diǎn)贊接口
問題:這樣寫接口,可以一直按點(diǎn)贊重復(fù)點(diǎn)贊
需求
- 同一個用戶只能點(diǎn)贊一次,再次點(diǎn)贊則取消點(diǎn)贊
- 如果當(dāng)前用戶已經(jīng)點(diǎn)贊,則點(diǎn)贊按鈕高亮顯(前端已經(jīng)實現(xiàn),判斷blog類的isLike屬性)
實現(xiàn)步驟
- 給Blog類中添加一個isLike字段,表示是否被當(dāng)前用戶點(diǎn)贊
- 修改點(diǎn)贊功能,利用redis的set集合判斷用戶是否贊過,未贊則點(diǎn)贊數(shù)+1,贊過則-1
- 修改根據(jù)id查詢blog的業(yè)務(wù),判斷當(dāng)前用戶是否點(diǎn)贊過,賦值給isLike字段
- 修改分頁查詢blog業(yè)務(wù),判斷當(dāng)前用戶是否贊過,賦值isLike字段
業(yè)務(wù)實現(xiàn)
@RestController @RequestMapping("/blog") public class BlogController { @Resource private IBlogService blogService; @PutMapping("/like/{id}") public Result likeBlog(@PathVariable("id") Long id) { return blogService.likeBlog(id); } @GetMapping("/hot") public Result queryHotBlog(@RequestParam(value = "current", defaultValue = "1") Integer current) { return blogService.queryHotBlog(current); } @GetMapping("/{id}") public Result queryBlogById(@PathVariable("id") String id){ return blogService.queryBlogById(id); } }
編寫完點(diǎn)贊操作的接口后還要修改之前的查詢接口,增加查詢是否已經(jīng)點(diǎn)贊
@Service public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService { @Autowired private IUserService userService; @Autowired private StringRedisTemplate stringRedisTemplate; @Override public Result queryHotBlog(Integer current) { // 根據(jù)用戶查詢 Page<Blog> page = query() .orderByDesc("liked") .page(new Page<>(current, SystemConstants.MAX_PAGE_SIZE)); // 獲取當(dāng)前頁數(shù)據(jù) List<Blog> records = page.getRecords(); // 查詢用戶 records.forEach(blog -> { this.queryBlogUser(blog); this.isBlogLiked(blog); }); return Result.ok(records); } @Override public Result likeBlog(Long id) { // 1、獲取登錄用戶 UserDTO user = UserHolder.getUser(); // 2、判斷當(dāng)前登錄用戶是否已經(jīng)點(diǎn)贊 Boolean isMember = stringRedisTemplate.opsForSet().isMember(RedisConstants.BLOG_LIKED_KEY + id, user.getId().toString()); if(BooleanUtil.isFalse(isMember)) { // 3、如果未點(diǎn)贊,可以點(diǎn)贊 // 3.1、數(shù)據(jù)庫點(diǎn)贊數(shù) +1 boolean isSuccess = update().setSql("liked = liked+1").eq("id", id).update(); // 3.2、保存用戶到 Redis 的 set 集合 if(isSuccess){ stringRedisTemplate.opsForSet().add(RedisConstants.BLOG_LIKED_KEY + id, user.getId().toString()); } } else { // 4、如果已點(diǎn)贊,取消點(diǎn)贊 // 4.1、數(shù)據(jù)庫點(diǎn)贊數(shù) -1 boolean isSuccess = update().setSql("liked = liked - 1").eq("id", id).update(); // 4.2、把用戶從 Redis 的 set 集合移除 if(isSuccess){ stringRedisTemplate.opsForSet().remove(RedisConstants.BLOG_LIKED_KEY + id, user.getId().toString()); } } return Result.ok(); } private void queryBlogUser(Blog blog) { Long userId = blog.getUserId(); User user = userService.getById(userId); blog.setName(user.getNickName()); blog.setIcon(user.getIcon()); } @Override public Result queryBlogById(String id) { Blog blog = getById(id); if(blog == null){ return Result.fail("筆記不存在!"); } queryBlogUser(blog); // 查詢 Blog 是否被點(diǎn)贊 isBlogLiked(blog); return Result.ok(blog); } private void isBlogLiked(Blog blog) { Long userId = blog.getUserId(); String key = RedisConstants.BLOG_LIKED_KEY + blog.getId(); Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, userId.toString()); blog.setIsLike(BooleanUtil.isTrue(isMember)); } }
四、點(diǎn)贊排行榜
查出給這個筆記點(diǎn)贊的人,類似微信朋友圈的點(diǎn)贊,可以展示誰點(diǎn)贊了,而且我們要進(jìn)行排序
所以我們得用SortedSet這種數(shù)據(jù)類型
1、修改點(diǎn)贊邏輯
把原本存入set改為存入zset多加個分?jǐn)?shù),分?jǐn)?shù)就是時間戳
@Override public Result likeBlog(Long id) { // 1、獲取登錄用戶 UserDTO user = UserHolder.getUser(); // 2、判斷當(dāng)前登錄用戶是否已經(jīng)點(diǎn)贊 Double score = stringRedisTemplate.opsForZSet().score(RedisConstants.BLOG_LIKED_KEY + id, user.getId().toString()); if(score == null) { // 3、如果未點(diǎn)贊,可以點(diǎn)贊 // 3.1、數(shù)據(jù)庫點(diǎn)贊數(shù) +1 boolean isSuccess = update().setSql("liked = liked+1").eq("id", id).update(); // 3.2、保存用戶到 Redis 的 set 集合 if(isSuccess){ // 時間作為 key 的 score stringRedisTemplate.opsForZSet().add(RedisConstants.BLOG_LIKED_KEY + id, user.getId().toString(), System.currentTimeMillis()); } } else { // 4、如果已點(diǎn)贊,取消點(diǎn)贊 // 4.1、數(shù)據(jù)庫點(diǎn)贊數(shù) -1 boolean isSuccess = update().setSql("liked = liked - 1").eq("id", id).update(); // 4.2、把用戶從 Redis 的 set 集合移除 if(isSuccess){ stringRedisTemplate.opsForZSet().remove(RedisConstants.BLOG_LIKED_KEY + id, user.getId().toString()); } } return Result.ok(); }
然后是否被點(diǎn)贊的方法也要修改,根據(jù)key取出分?jǐn)?shù),分?jǐn)?shù)不為null就是點(diǎn)贊過了
private void isBlogLiked(Blog blog) { UserDTO user = UserHolder.getUser(); if(user == null){ return; } Long userId = user.getId(); String key = RedisConstants.BLOG_LIKED_KEY + blog.getId(); Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString()); blog.setIsLike(score != null); }
2、點(diǎn)贊排行榜功能
需求:實現(xiàn)前五個點(diǎn)贊的用戶返回
我們先用動態(tài)id去redis中查詢出前五個點(diǎn)贊用戶的id
然后根據(jù)id去數(shù)據(jù)庫中查詢信息封裝到dto再返回
@GetMapping("/likes/{id}") public Result queryBlogLikes(@PathVariable("id") String id) { return blogService.queryBlogLikes(id); }
@Override public Result queryBlogLikes(String id) { String key = RedisConstants.BLOG_LIKED_KEY + id; // 查詢 top5 的點(diǎn)贊用戶 Set<String> top5 = stringRedisTemplate.opsForZSet().range(key, 0, 4); if(top5 == null){ return Result.ok(Collections.emptyList()); } // 解析出其中的用戶id List<Long> ids = top5.stream().map(Long::valueOf).collect(Collectors.toList()); String join = StrUtil.join(",", ids); // 根據(jù)用戶id查詢用戶 List<UserDTO> userDTOS = userService.query().in("id", ids).last("order by filed(id, "+join+")").list() .stream() .map(user -> BeanUtil.copyProperties(user, UserDTO.class)) .collect(Collectors.toList()); return Result.ok(userDTOS); }
注意:如果我們mp直接用in來查詢根本不能保證點(diǎn)贊的順序,因為in查詢出來的是按照id順序返回的,沒有排序,我們要按照查詢id的順序來查,order by field(id,5,1)這樣
到此這篇關(guān)于Redis筆記點(diǎn)贊排行榜的實現(xiàn)示例的文章就介紹到這了,更多相關(guān)Redis筆記點(diǎn)贊內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于redis實現(xiàn)世界杯排行榜功能項目實戰(zhàn)
前段時間,做了一個世界杯競猜積分排行榜。對世界杯64場球賽勝負(fù)平進(jìn)行猜測,猜對+1分,錯誤+0分,一人一場只能猜一次。下面通過本文給大家分享基于redis實現(xiàn)世界杯排行榜功能項目實戰(zhàn),感興趣的朋友一起看看吧2018-10-10利用Redis實現(xiàn)訪問次數(shù)限流的方法詳解
這篇文章主要給大家介紹了關(guān)于如何利用Redis實現(xiàn)訪問次數(shù)限流的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2022-02-02