黑馬點評-10實現用戶點贊和點贊排行榜功能

用戶點贊功能

如果用戶只要點贊一次就對數據庫中blog表中的liked字段的值加1就會導致一個用戶無限點贊

PutMapping("/like/{id}")
public Result likeBlog(@PathVariable("id") Long id) {// 修改點贊數量,update tb_blog set liked = liked + 1 where id = ?	blogService.update().setSql("liked = liked + 1").eq("id", id).update();return Result.ok();
}

需求: 同一個用戶只能對同一篇筆記點贊一次再次點擊則取消點贊,如果當前用戶已經點贊則點贊按鈕高亮顯示

  • 增加isLike字段: 給Blog實體類中添加一個isLike字段,首頁查詢熱門筆記或用戶查看筆記詳情內容時會根據isLike字段的屬性值決定點贊按鈕是否高亮顯示
  • 點贊修改功能: 利用Redis中的set集合是否包含點贊用戶的Id來判斷用戶是否點贊過,未點贊則點贊數+1,已點贊則點贊數-1
  • 查看筆記詳情時根據id查詢筆記: 判斷當前登錄用戶是否點贊過,賦值給isLike字段決定點贊圖標是否高亮顯示
  • 訪問首頁時分頁查詢熱門筆記: 判斷當前登錄用戶是否點贊過,賦值給isLike字段決定點贊圖標是否高亮顯示

在這里插入圖片描述

一人一贊

第一步: 給Blog實體類增加isLike字段,首頁查詢熱門筆記或用戶查看筆記詳情內容時會根據isLike字段的屬性值決定點贊按鈕是否高亮顯示

@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("tb_blog")
public class Blog implements Serializable {private static final long serialVersionUID = 1L;// isLike屬性不屬于Blog表中的字段@TableField(exist = false)private Boolean isLike;//..........
}

第二步: 編寫控制器方法處理用戶點贊的請求

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

第三步: 編寫業務方法,以blog:liked:筆記Id作為Set集合的Key,集合中的元素就是點贊用戶的Id

// 在RedisConstants類中聲明一個常量作為Set集合的key,集合中包含了所有點贊的用戶
public static final String BLOG_LIKED_KEY = "blog:liked:";
// 操作Redis,key和value要求是String類型
@Resource
private StringRedisTemplate stringRedisTemplate;@Override
public Result likeBlog(Long id) {//1. 獲取當前登陸用戶信息Long userId = UserHolder.getUser().getId();//2. 判斷當前用戶是否已經點贊//2.1如果用戶未點贊則Blog表中like字段值加1,同時將點贊用戶的Id加入set集合String key = BLOG_LIKED_KEY + id;Boolean isLiked = stringRedisTemplate.opsForSet().isMember(key, userId.toString());if (BooleanUtil.isFalse(isLiked)) {// update tb_blog set liked = liked + 1 where id = ?boolean success = update().setSql("liked = liked + 1").eq("id", id).update();// 更新成功將點贊用戶Id加入set集合if (success) {stringRedisTemplate.opsForSet().add(key, userId.toString());}//2.2如果當前用戶已點贊則取消點贊即like字段值減1,同時將點贊用戶Id從set集合中移除}else {// update tb_blog set liked = liked - 1 where id = ?boolean success = update().setSql("liked = liked - 1").eq("id", id).update();if (success){// 更新成功則將點贊用戶Id從set集合移除stringRedisTemplate.opsForSet().remove(key, userId.toString());}}return Result.ok();
}

修改查詢筆記點贊高亮

訪問首頁時分頁查詢熱門筆記: 判斷當前登錄用戶是否點贊過,根據Blog實體類isLike屬性的值決定點贊圖標是否高亮顯示

@Override
public 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(blog -> {// 查詢用戶昵稱和頭像封裝到Blog實體類當中queryBlogUser(blog);// 判斷筆記是否被當前用戶點贊isBlogLiked(blog);});return Result.ok(records);
}

查看筆記詳情時根據id查詢筆記: 判斷當前登錄用戶是否點贊過,根據Blog實體類isLike屬性的值決定點贊圖標是否高亮顯示

@Override
public Result queryBlogById(Integer id) {Blog blog = getById(id);if (blog == null) {return Result.fail("評價不存在或已被刪除");}// 查詢用戶昵稱和頭像封裝到Blog實體類當中queryBlogUser(blog);// 判斷筆記是否被當前用戶點贊isBlogLiked(blog);return Result.ok(blog);
}

由于查看用戶信息判斷筆記是否被當前用戶點贊的業務邏輯比較通用,所以抽取成獨立的方法

// 查看用戶信息
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) {//1. 獲取當前用戶信息Long userId = UserHolder.getUser().getId();//2. 判斷當前用戶是否點贊String key = BLOG_LIKED_KEY + blog.getId();Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, userId.toString());//3. 如果點贊了則將Blog類的isLike屬性設置為trueblog.setIsLike(BooleanUtil.isTrue(isMember));
}

點贊排行榜

需求: 當我們點擊探店筆記詳情頁面時,應該按點贊順序展示點贊過的用戶,比如顯示最早點贊的TOP5形成點贊排行榜

在這里插入圖片描述

因為Set集合不能對點贊的用戶進行排序,所以我們需要使用SortedSet(Zset)集合存儲點贊的用戶Id,score屬性的值是當前時間戳(默認按照從小到大排序)

在這里插入圖片描述

第一步: ZSet沒有判斷元素是否存在的方法,是通過獲取集合中元素的score屬性的值來判斷集合中是否有該元素

  • ZSCORE key e1: 獲取集合元素的score屬性值,若元素存在則返回對應score值,若不存在則返回null
@Override
public Result likeBlog(Long id) {//1. 獲取當前登陸用戶信息Long userId = UserHolder.getUser().getId();//2. 判斷當前用戶是否已經點贊//2.1如果用戶未點贊則Blog表中like字段值加1,同時將點贊用戶的Id加入set集合String key = BLOG_LIKED_KEY + id;// 嘗試獲取當前用戶的score屬性值Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());// 如果score為null則表示集合中沒有該用戶if (score == null) {// update tb_blog set liked = liked + 1 where id = ?boolean success = update().setSql("liked = liked + 1").eq("id", id).update();//更新成功則將點贊用戶Id加入SortedSet集合,score屬性的值就是當前的時間戳(默認按照從小到大排序)if (success) {stringRedisTemplate.opsForZSet().add(key, userId.toString(), System.currentTimeMillis());}//2.2如果當前用戶已點贊則取消點贊即like字段值減1,同時將點贊用戶Id從SortedSet集合中移除} else {// update tb_blog set liked = liked - 1 where id = ?boolean success = update().setSql("liked = liked - 1").eq("id", id).update();if (success) {//更新成功將點贊用戶Id從SortedSet集合移除stringRedisTemplate.opsForZSet().remove(key, userId.toString());}}return Result.ok();
}

第二步: 判斷用戶是否點贊時如果用戶沒有登錄就不需要判斷用戶是否點過贊了,因為用戶沒有登陸就獲取不到userId此時會報空指針異常

private void isBlogLiked(Blog blog) {//1. 獲取當前用戶信息UserDTO userDTO = UserHolder.getUser();//2. 當用戶未登錄時就不判斷用戶是否點贊,直接return結束邏輯if (userDTO == null) {return;}//3. 判斷當前用戶是否點贊String key = BLOG_LIKED_KEY + blog.getId();Double score = stringRedisTemplate.opsForZSet().score(key, userDTO.getId().toString());// score不等于null返回true表示用戶已經點過贊同時給Blog類的isLike屬性賦值trueblog.setIsLike(score != null);
}

第三步: 顯示點贊排行列表當瀏覽器發起GET請求blog/likes/4時服務器返回一個List集合包含top5點贊的用戶信息

  • ZRANGE key start end: 獲取指定范圍的元素,SortedSet內的元素會自動排序(默認升序)
@GetMapping("/likes/{id}")
public Result queryBlogLikes(@PathVariable Integer id){return blogService.queryBlogLikes(id);
}
@Override
public Result queryBlogLikes(Integer id) {String key = BLOG_LIKED_KEY + id;// 查詢SortedSet集合的前5個元素即用戶的id(不包含元素的分數)Set<String> top5 = stringRedisTemplate.opsForZSet().range(key, 0, 4);// 如果返回的set集合是空的即沒人點贊,直接返回一個空的List集合if (top5 == null || top5.isEmpty()) {return Result.ok(Collections.emptyList());}// 將Set集合中String類型的用戶id轉變為Long類型的id然后收集到List集合中List<Long> ids = top5.stream().map(Long::valueOf).collect(Collectors.toList());//select * from tb_user where id in (ids[0],...) order by field(id, ids[0],...)String idsStr = StrUtil.join(",", ids);// 把ids集合拼成一個以","分隔的字符串List<UserDTO> userDTOS = userService.query().in("id", ids).last("order by field(id," + idsStr + ")").list().stream().map(user -> BeanUtil.copyProperties(user, UserDTO.class))// 將查詢出來的用戶隱私信息隱藏.collect(Collectors.toList());return Result.ok(userDTOS);
}

由于MySQL默認會對查詢出來的所有結果按照id從小到大的方式排序,它并不會按照我們查詢到的用戶Id順序去排序

  • order by field(排序字段,排序順序) : 可以指定查詢結果按照某個字段的排序方式

select * from tb_user where id in (ids[0], ids[1] ...): 這種方式并不會按照Set集合中Id的順序對查詢結果進行排序

List<UserDTO> userDTOS = userService.listByIds(ids).steram().map(user -> BeanUtil.copyProperties(user, UserDTO.class))// 將查詢出來的用戶隱私信息隱藏.collect(Collectors.toList());

select * from tb_user where id in (ids[0],...) order by field(id, ids[0],...): 指定查詢結果按照我們指定的字段順序排序

// 把ids集合拼成一個以","分隔的字符串
String idsStr = StrUtil.join(",", ids);
List<UserDTO> userDTOS = userService.query().in("id", ids).last("order by field(id," + idsStr + ")").list().stream().map(user -> BeanUtil.copyProperties(user, UserDTO.class))// 將查詢出來的用戶隱私信息隱藏.collect(Collectors.toList());

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/164558.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/164558.shtml
英文地址,請注明出處:http://en.pswp.cn/news/164558.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

編譯器核心技術概覽

編譯技術是一門龐大的學科&#xff0c;我們無法對其做完善的講解。但不同用途的編譯器或編譯技術的難度可能相差很大&#xff0c;對知識的掌握要求也會相差很多。如果你要實現諸如 C、JavaScript 這類通用用途語言&#xff08;general purpose language&#xff09;&#xff0c…

buck降壓電路

一、Buck電路的拓撲結構 Buck是直流轉直流的降壓電路,下面是拓撲結構,作為硬件工程師,這個最好是能夠記下來,了然于胸。 為啥要記下來,自然是因為這個電路太基礎了,并且誰都會用到,更重要的一點,面試可能會考。。。 上圖是個異步buck,同步buck就是將里面的二極管換成M…

3D火山圖繪制教程

一邊學習&#xff0c;一邊總結&#xff0c;一邊分享&#xff01; 本期教程內容 **注&#xff1a;**本教程詳細內容 Volcano3D繪制3D火山圖 一、前言 火山圖是做差異分析中最常用到的圖形&#xff0c;在前面的推文中&#xff0c;我們也推出了好幾期火山圖的繪制教程&#xff0…

Android——資源IDnonFinalResIds和“Attribute value must be constant”錯誤

一、異常描述 通過資源ID引用資源提示錯誤 Attribute value must be constant 二、解決方案 在根目錄下的文件 gradle.properties 中添加如下配置&#xff0c;然后Sync Project android.nonFinalResIdsfalse 三、問題原因 android.nonFinalResIds 是Android開發中一個用于解…

此處不允許使用特性namespace

1.DOCTYPE 后面改成 mapper 2.PUBLIC一行中的Config改為Mapper 3.將下一行config變為小寫的mapper <?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.or…

交叉編譯安裝時報錯 ./install.sh: 15: ./install.sh: Bad substitution

報錯信息截圖如下&#xff1a; 解決方法 vim install.sh #!/bin/sh -e 修改為 !/bin/bash -e重新執行 sudo ./install.sh 成功運行

【Java并發】聊聊線程池原理以及實際應用

線程其實對于操作系統來說是寶貴的資源&#xff0c;java層面的線程其實本質還是依賴于操作系統內核的線程進行處理任務&#xff0c;如果頻繁的創建、使用、銷毀線程&#xff0c;那么勢必會非常浪費資源以及性能不高&#xff0c;所以池化技術&#xff08;數據庫連接池、線程池&a…

暢談Linux在小型微型企業中的應用

在這篇文章里我們討論和暢談一下linux系統在小微型企業中的應用&#xff0c;為什么會寫這篇文章呢&#xff1f;因為在平時的工作中&#xff0c;認識的一些做小微型企業的朋友&#xff0c;他們經常找我咨詢或是去解決一些平時工作中的IT相關的問題&#xff0c;那么小微型企業中的…

相同結構體不同類型轉換

緣由&#xff1a; 最近開發上遇到一個問題&#xff0c;通過grpcgateway 處理后的int64&uint64類型數據均轉換成了字符串類型&#xff0c;本身服務于前端&#xff0c;沒有任何問題。但是 項目部署現場后&#xff0c;發現需要兩套環境&#xff0c;那么就出現一個問題&#x…

2022 年十大 JavaScript 框架

2022 年十大 Web 應用開發 JavaScript 框架。 React.js jQuery Express Angular Vue.js Angular.js Svelte Next.js Ember.js Meteor React.js React.js 于 2013 年由 Meta(Facebook 前身) 推出&#xff0c;是一款開源的、免費的 JavaScript 庫。React.js 被用于開…

C++中的map和set的使用

C中的map詳解 關聯式容器鍵值對樹形結構的關聯式容器set的使用1. set的模板參數列表2. set的構造3. set的迭代器4. set的容量5. set修改操作6. set的使用舉例 map1. map的簡介2. map的模板參數說明3. map的構造4. map的迭代器5. map的容量與元素訪問6. map的元素修改 multimap和…

Linux vim操作教程(vim 基操、vim替換和查找、 vim改變文本顏色、判斷和循環語句)

vim 基操 vim 是一個強大的文本編輯器,常用于在終端環境下編輯文件。下面是一些常用的 vim 操作: 打開文件:在終端中輸入 vim 文件名 來打開一個文件,如果文件不存在,則會創建一個新文件。 模式切換: 按下 i 進入插入模式,在該模式下可以輸入和編輯文本。按下 Esc 鍵返…

python單例模式

單例模式是一種創建型設計模式&#xff0c;它保證一個類僅有一個實例&#xff0c;并提供一個全局訪問點。 在 Python 中&#xff0c;可以使用以下幾種方式來創建單例模式&#xff1a; 使用 __new__ 方法 在 Python 中&#xff0c; __new__ 方法是一個類方法&#xff0c;它在…

msvcp120.dll丟失是什么意思,哪個修復方法最簡單

在計算機使用過程中&#xff0c;我們經常會遇到一些錯誤提示&#xff0c;其中之一就是“找不到msvcp120.dll”。這個錯誤通常發生在運行某些程序或游戲時&#xff0c;它會導致程序無法正常啟動或運行。那么&#xff0c;這個錯誤提示到底是什么意思呢&#xff1f;為了解決這個問…

深入了解Java8新特性-日期時間API_LocalDate類

閱讀建議 嗨&#xff0c;伙計&#xff01;刷到這篇文章咱們就是有緣人&#xff0c;在閱讀這篇文章前我有一些建議&#xff1a; 本篇文章大概12000多字&#xff0c;預計閱讀時間長需要10分鐘。本篇文章的實戰性、理論性較強&#xff0c;是一篇質量分數較高的技術干貨文章&…

【iOS】數據持久化(一)之Plist文件、Preference(NSUserDefaults類)

目錄 什么是Plist文件&#xff1f;plist可以存儲哪些數據類型plist文件數據的讀取與存儲 Perference&#xff08;NSUserDefaults&#xff09;使用方法registerDefaults: 方法的使用 什么是Plist文件&#xff1f; Plist文件&#xff08;屬性列表&#xff09;是將某些特定的類&a…

python運行hhblits二進制命令的包裝器類

hhblits 是 HMM-HMM&#xff08;Hidden Markov Model to Hidden Markov Model&#xff09;比對方法的一部分&#xff0c;也是 HMMER 軟件套件中的工具之一。與 hhsearch 類似&#xff0c;hhblits 也用于進行高效的蛋白質序列比對&#xff0c;特別擅長于檢測遠緣同源性。 hh-su…

筑牢思想防線——建行駐江門市分行紀檢組舉辦2023年清廉合規大講堂

為推動廉潔教育打通“最后一公里”&#xff0c;近日&#xff0c;建行駐江門市分行紀檢組舉辦江門市分行2023年清廉合規大講堂。 本次大講堂檢察官結合一線辦案經歷&#xff0c;從防范化解金融風險、預防金融從業人員犯罪等方面對全轄員工進行了深入淺出地的講解&#xff0c;引導…

代碼隨想錄算法訓練營第五十二天|1143.最長公共子序列 1035.不相交的線 53. 最大子序和

文檔講解&#xff1a;代碼隨想錄 視頻講解&#xff1a;代碼隨想錄B站賬號 狀態&#xff1a;看了視頻題解和文章解析后做出來了 1143.最長公共子序列 class Solution:def longestCommonSubsequence(self, text1: str, text2: str) -> int:dp [[0] * (len(text2) 1) for _ i…

C++——stack和queue

目錄 stack的介紹和使用 stack的使用 queue的介紹和使用 queue的使用 容器適配器 deque的介紹 deque的缺陷 priority_queue的介紹和使用 priority_queue的使用 仿函數 反向迭代器 stack的介紹和使用 在原來的數據結構中已經介紹過什么是棧了&#xff0c;再來回顧一下…