SpringBoot實現(xiàn)物品點贊功能
前后端分離項目–二手交易平臺小程序
SpringBoot----物品收藏功能實現(xiàn)
SpringBoot----評論回復(fù)功能實現(xiàn)(數(shù)據(jù)庫設(shè)計)
SpringBoot----文件(圖片)上傳與顯示(下載)
點贊
這個功能耗費了我挺多時間,簡單實現(xiàn)很簡單,就++ – .但是還是感覺這種點贊是一個高頻率的請求,而且搜的時候我看都是使用redis做緩存。b站也搜到一個視頻來著,也是一樣的。
效果:
功能:
首先還是一個先發(fā)請求返回數(shù)據(jù),但是先數(shù)據(jù)存到了redis中,然后使用springboot定時任務(wù)每隔一定時間將數(shù)據(jù)存到mysql中。這樣可以防止redis掛掉之后數(shù)據(jù)丟失。
數(shù)據(jù)庫設(shè)計:
MySQL使用了一張表和另外幾張表的一個字段,一張存放點贊信息,就是誰點贊了誰在啥時候。字段存放點贊數(shù)量。就是物品信息表。評論表這些。
-
redis,使用的是hash數(shù)據(jù)結(jié)構(gòu),redis_liked存放點贊數(shù)據(jù),redis_liked_count存放點贊數(shù)量數(shù)據(jù)。
解釋 :
對于 “1::字符串::1 ”
這個是一種存放方式,前面1為objid就是被點贊物品或者評論id,字符串為微信openid每個用戶唯一id,后面1為類型區(qū)分點贊的是物品還是主評論,子評論。
對于 "\"0\""
這個數(shù)據(jù)則是點贊的狀態(tài),1為點贊0為取消點贊對于"1::1"
這個前面1是objid就是物品或者子、主評論id,后面則是區(qū)別是哪個類型。“0”
就是點贊數(shù)量。
后臺代碼:
前端就發(fā)like或者取消unlike請求
package com.w.wx.controller; import com.w.wx.domain.Msg; import com.w.wx.service.impls.RedisServiceImpl; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @Slf4j @RestController @RequestMapping("/wx/liked") public class LikedContro { @Autowired private RedisServiceImpl redisService; /** * 保存點贊數(shù)據(jù)到redis * 點贊數(shù)+1 * 同一個不能點贊兩次 * @param objId * @param openid * @param type * @return */ @RequestMapping("like") public Msg saveLikedToRedis(Integer objId,String openid , String type){ redisService.incrementLikedCount(objId, type, openid); redisService.saveLikedToRedis(objId,openid,type); int oneInfoCount = redisService.getOneInfoCount(objId, type); return Msg.success().add("count",oneInfoCount); } @RequestMapping("unlike") public Msg decrementLikedCount(Integer objId,String openid,String type){ redisService.decrementLikedCount(objId,type,openid); redisService.unlikeFromRedis(objId,openid,type); int oneInfoCount = redisService.getOneInfoCount(objId, type); return Msg.success().add("count",oneInfoCount); } //恢復(fù)redis @RequestMapping("restore") public Msg restoreRedisCountInfo(){ redisService.savaInfoFromDb2Re(0); redisService.savaInfoFromDb2Re(1); redisService.savaInfoFromDb2Re(2); return Msg.success(); } }
操作redis代碼
package com.w.wx.service.impls; import com.w.wx.mapper.LikedMapper; import com.w.wx.domain.Liked; import com.w.wx.service.ImagesService; import com.w.wx.service.RedisService; import com.w.wx.utils.RedisKeyUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ScanOptions; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; @Service @Slf4j public class RedisServiceImpl implements RedisService { @Autowired private RedisTemplate redisTemplate; @Autowired private LikedServiceImpl likedService; @Autowired private ImagesService imagesService; /** * 保存數(shù)據(jù)到redis * @param objId * @param openid * @param type */ @Override public void saveLikedToRedis(Integer objId, String openid,String type) { imagesService.addLikedNotice(objId, openid,type); String likedKey = RedisKeyUtils.getLikedKey("" + objId, openid,type); redisTemplate.opsForHash().put(RedisKeyUtils.MAP_KEY_USER_LIKED,likedKey, "1"); } /** * 取消點贊 * @param objId * @param openid * @param type */ @Override public void unlikeFromRedis(Integer objId, String openid,String type) { String likedKey = RedisKeyUtils.getLikedKey("" + objId, openid,type); redisTemplate.opsForHash().put(RedisKeyUtils.MAP_KEY_USER_LIKED,likedKey, "0"); } /** * 刪除數(shù)據(jù),沒有用到 * @param objId * @param openid * @param type */ @Override public void deleteFromRedis(Integer objId, String openid,String type) { String key = RedisKeyUtils.getLikedKey("" + objId, openid,type); redisTemplate.opsForHash().delete(RedisKeyUtils.MAP_KEY_USER_LIKED, key); } /** * 自增,點贊數(shù)++ * @param objId * @param type * @param openid * @return */ @Override public Long incrementLikedCount(Integer objId,String type, String openid) { String key = RedisKeyUtils.getLikedKey("" + objId,type); String likedKey = RedisKeyUtils.getLikedKey("" + objId, openid,type); //根據(jù)定時器時長有延遲 // if(likedMapper.selectByObjIdAndOpenid(objId,openid,Integer.valueOf(type)).getLikeStatus()==0){ // redisTemplate.opsForHash().increment(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, key, 1); // } //防止重復(fù)點贊 if("0".equals(redisTemplate.opsForHash().get(RedisKeyUtils.MAP_KEY_USER_LIKED,likedKey)) || redisTemplate.opsForHash().get(RedisKeyUtils.MAP_KEY_USER_LIKED,likedKey) == null){ return redisTemplate.opsForHash().increment(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, key, 1); } return null; } @Override public void decrementLikedCount(Integer objId,String type, String openid) { String key = RedisKeyUtils.getLikedKey("" + objId,type); String likedKey = RedisKeyUtils.getLikedKey("" + objId, openid,type); // if(likedMapper.selectByObjIdAndOpenid(objId,openid,Integer.valueOf(type)).getLikeStatus()==1){ // redisTemplate.opsForHash().increment(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, key, -1); // } if("1".equals(redisTemplate.opsForHash().get(RedisKeyUtils.MAP_KEY_USER_LIKED,likedKey))){ redisTemplate.opsForHash().increment(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, key, -1); } } /** * 獲取redis中存放的點贊數(shù)據(jù)然后存放到mysql做持久化 * @return */ @Override public List<Liked> getLikedDataFromRedis() { Cursor<Map.Entry<Object, Object>> cursor = redisTemplate.opsForHash().scan(RedisKeyUtils.MAP_KEY_USER_LIKED, ScanOptions.NONE); List<Liked> list = new ArrayList<>(); while (cursor.hasNext()){ Map.Entry<Object, Object> entry = cursor.next(); String key = (String) entry.getKey(); String[] split = key.split("::"); int status = Integer.parseInt((String) entry.getValue()); Liked like = new Liked(); like.setObjId(Integer.valueOf(split[0])); like.setUserOpenid(split[1]); like.setType(Integer.valueOf(split[2])); like.setLikeStatus(status); list.add(like); //存到 list 后從 Redis 中刪除 // redisTemplate.opsForHash().delete(RedisKeyUtils.MAP_KEY_USER_LIKED, key); } return list; } //獲取redis中點贊數(shù)量 @Override public Cursor<Map.Entry<Object, Object>> getLikedCountFromRedis() { Cursor<Map.Entry<Object, Object>> cursor = redisTemplate.opsForHash().scan(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, ScanOptions.NONE); //redisTemplate.opsForHash().delete(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT); return cursor; } @Override public int getOneInfoCount(Integer objId, String type) { String key = RedisKeyUtils.getLikedKey("" + objId,type); return (int)redisTemplate.opsForHash().get(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT,key); } public void saveCountInfo(Integer objId,Integer type,Integer count){ String key = RedisKeyUtils.getLikedKey("" + objId,""+type); redisTemplate.opsForHash().put(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT,key,count); } //redis掛掉后通過mysql中數(shù)據(jù)恢復(fù)redis @Override public void savaInfoFromDb2Re(Integer type) { List<Map<String,Object>> likeds = likedService.selectLikedInfoByType(type); if (likeds.isEmpty()||likeds.equals("")){ return; } Iterator<Map<String,Object>> it = likeds.listIterator(); while(it.hasNext()){ Map<String,Object> map = it.next(); Integer objId = (Integer) map.get("objId"); Integer count = Integer.parseInt(map.get("num_liked").toString()); log.info("objId:"+objId+"count:"+count+"type:"+type); saveCountInfo(objId,type,count); } } }
操作mysql代碼
package com.w.wx.service.impls; import com.w.wx.mapper.CommentsInfoMapper; import com.w.wx.mapper.CommentsReplyMapper; import com.w.wx.mapper.GoodsMapper; import com.w.wx.mapper.LikedMapper; import com.w.wx.domain.CommentsInfo; import com.w.wx.domain.Liked; import com.w.wx.service.LikedService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.Cursor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.sql.Timestamp; import java.util.*; @Service @Slf4j public class LikedServiceImpl implements LikedService { @Autowired private RedisServiceImpl redisService; @Autowired private LikedMapper likedMapper; @Autowired private CommentsInfoMapper commentsInfoMapper; @Autowired private CommentsReplyMapper commentsReplyMapper; @Autowired private GoodsMapper goodsMapper; /** * 將緩存中點贊記錄持久化到數(shù)據(jù)庫 */ @Override @Transactional public void transLikedFromRedisToDB() { List<Liked> likeds = redisService.getLikedDataFromRedis(); for (Liked like : likeds) { Date date = new Date(); Timestamp timestamp = new Timestamp(date.getTime()); //首先判斷之前是否有 Liked liked = likedMapper.selectByObjIdAndOpenid(like.getObjId(),like.getUserOpenid(),like.getType()); if(liked==null ){ //沒有則新增 like.setCreateTime(timestamp); likedMapper.insert(like); }else{ //有則更新 likedMapper.updateByPrimaryKey(liked.getLikeId(),like.getLikeStatus(),timestamp); } } } /** * 將點贊數(shù)量持久化到數(shù)據(jù)庫 */ @Override @Transactional public void transLikedCountFromRedisToDB() { Cursor<Map.Entry<Object, Object>> cursor = redisService.getLikedCountFromRedis(); while (cursor.hasNext()){ Map.Entry<Object, Object> map = cursor.next(); String key = (String)map.getKey(); //分離出objId和type String[] split = key.split("::"); int type =Integer.parseInt(split[1]); int objId = Integer.parseInt(split[0]); int likeNum = (Integer) map.getValue(); if ( type == 1){ //為主評論 commentsInfoMapper.updateByPrimaryKey(objId,likeNum); }else if(type == 2){ //為子評論 commentsReplyMapper.updateByPrimaryKey(objId,likeNum); }else{ //為物品 goodsMapper.updateGoodsLikeSum(objId,likeNum); } } } @Override public List<Map<String,Object>> selectLikedInfoByType(Integer type) { return likedMapper.selectLikedInfoByType(type); } }
工具類
package com.w.wx.utils; public class RedisKeyUtils { //保存用戶點贊數(shù)據(jù)的key public static final String MAP_KEY_USER_LIKED = "redis_liked"; //保存用戶被點贊數(shù)量的key public static final String MAP_KEY_USER_LIKED_COUNT = "redis_liked_count"; /** * 拼接被點贊的用戶id和點贊的人的id作為key。格式 222222::333333::1 * @param likedUserId 被點贊的人id * @param likedPostId 點贊的人的id * @return */ public static String getLikedKey(String likedUserId, String likedPostId,String type){ StringBuilder builder = new StringBuilder(); builder.append(likedUserId); builder.append("::"); builder.append(likedPostId); builder.append("::"); builder.append(type); return builder.toString(); } public static String getLikedKey(String likedUserId,String type){ StringBuilder builder = new StringBuilder(); builder.append(likedUserId); builder.append("::"); builder.append(type); return builder.toString(); } }
定時任務(wù)配置類
package com.w.wx.config; import com.w.wx.utils.LikeTask; import org.quartz.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class QuartzConfig { private static final String LIKE_TASK_IDENTITY = "LikeTaskQuartz"; @Bean public JobDetail quartzDetail(){ return JobBuilder.newJob(LikeTask.class).withIdentity(LIKE_TASK_IDENTITY).storeDurably().build(); } @Bean public Trigger quartzTrigger(){ SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule() // .withIntervalInSeconds(5) //設(shè)置時間周期單位秒 .withIntervalInHours(2) //兩個小時執(zhí)行一次 .repeatForever(); return TriggerBuilder.newTrigger().forJob(quartzDetail()) .withIdentity(LIKE_TASK_IDENTITY) .withSchedule(scheduleBuilder) .build(); } }
定時任務(wù)工具類
package com.w.wx.utils; import com.w.wx.service.LikedService; import com.w.wx.service.impls.LikedServiceImpl; import lombok.extern.slf4j.Slf4j; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.quartz.QuartzJobBean; import java.text.SimpleDateFormat; import java.util.Date; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @Slf4j @Component public class LikeTask extends QuartzJobBean { @Autowired LikedServiceImpl likedService; private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Override protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { log.info("LikeTask-------- {}", sdf.format(new Date())); //將 Redis 里的點贊信息同步到數(shù)據(jù)庫里 likedService.transLikedFromRedisToDB(); likedService.transLikedCountFromRedisToDB(); } }
參考:
springboot如何實現(xiàn)點贊功能
點贊功能的實現(xiàn)及Springboot定時器的應(yīng)用
還有好多,第一頁搜出來的基本都看過,第一次搞真心不會。。。
f12去看看csdn點贊,會發(fā)現(xiàn)點文章的贊會有點贊量的返回。點評論的贊就沒
嗶哩嗶哩就看不懂了?。?!
到此這篇關(guān)于SpringBoot物品點贊功能實現(xiàn)的文章就介紹到這了,更多相關(guān)SpringBoot點贊內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
在Java Spring框架中使用的設(shè)計模式有哪些
面試中常會被問道Spring框架使用了哪些設(shè)計模式?關(guān)于這個問題本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-09-09Java Listener監(jiān)聽器使用規(guī)范詳細(xì)介紹
監(jiān)聽器是一個專門用于對其他對象身上發(fā)生的事件或狀態(tài)改變進行監(jiān)聽和相應(yīng)處理的對象,當(dāng)被監(jiān)視的對象發(fā)生情況時,立即采取相應(yīng)的行動。監(jiān)聽器其實就是一個實現(xiàn)特定接口的普通java程序,這個程序?qū)iT用于監(jiān)聽另一個java對象的方法調(diào)用或?qū)傩愿淖?/div> 2023-01-01Java的NIO之并發(fā)環(huán)境下非阻塞IO技術(shù)詳解
這篇文章主要介紹了Java的NIO之并發(fā)環(huán)境下非阻塞IO技術(shù)詳解,Java NIO(New IO)是Java平臺提供的一種用于高效處理I/O操作的API,它引入了一組新的類和概念,以提供更好的性能和可擴展性,需要的朋友可以參考下2023-09-09在Eclipse IDE使用Gradle構(gòu)建應(yīng)用程序(圖文)
這篇文章主要介紹了在Eclipse IDE使用Gradle構(gòu)建應(yīng)用程序(圖文),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12最新評論