SpringBoot實(shí)現(xiàn)物品點(diǎn)贊功能
前后端分離項(xiàng)目–二手交易平臺(tái)小程序
SpringBoot----物品收藏功能實(shí)現(xiàn)
SpringBoot----評(píng)論回復(fù)功能實(shí)現(xiàn)(數(shù)據(jù)庫設(shè)計(jì))
SpringBoot----文件(圖片)上傳與顯示(下載)
點(diǎn)贊
這個(gè)功能耗費(fèi)了我挺多時(shí)間,簡(jiǎn)單實(shí)現(xiàn)很簡(jiǎn)單,就++ – .但是還是感覺這種點(diǎn)贊是一個(gè)高頻率的請(qǐng)求,而且搜的時(shí)候我看都是使用redis做緩存。b站也搜到一個(gè)視頻來著,也是一樣的。
效果:




功能:
首先還是一個(gè)先發(fā)請(qǐng)求返回?cái)?shù)據(jù),但是先數(shù)據(jù)存到了redis中,然后使用springboot定時(shí)任務(wù)每隔一定時(shí)間將數(shù)據(jù)存到mysql中。這樣可以防止redis掛掉之后數(shù)據(jù)丟失。
數(shù)據(jù)庫設(shè)計(jì):

MySQL使用了一張表和另外幾張表的一個(gè)字段,一張存放點(diǎn)贊信息,就是誰點(diǎn)贊了誰在啥時(shí)候。字段存放點(diǎn)贊數(shù)量。就是物品信息表。評(píng)論表這些。
-

redis,使用的是hash數(shù)據(jù)結(jié)構(gòu),redis_liked存放點(diǎn)贊數(shù)據(jù),redis_liked_count存放點(diǎn)贊數(shù)量數(shù)據(jù)。
解釋 :
對(duì)于 “1::字符串::1 ” 這個(gè)是一種存放方式,前面1為objid就是被點(diǎn)贊物品或者評(píng)論id,字符串為微信openid每個(gè)用戶唯一id,后面1為類型區(qū)分點(diǎn)贊的是物品還是主評(píng)論,子評(píng)論。
對(duì)于 "\"0\"" 這個(gè)數(shù)據(jù)則是點(diǎn)贊的狀態(tài),1為點(diǎn)贊0為取消點(diǎn)贊對(duì)于"1::1"這個(gè)前面1是objid就是物品或者子、主評(píng)論id,后面則是區(qū)別是哪個(gè)類型。“0”就是點(diǎn)贊數(shù)量。



后臺(tái)代碼:
前端就發(fā)like或者取消unlike請(qǐng)求
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;
/**
* 保存點(diǎn)贊數(shù)據(jù)到redis
* 點(diǎn)贊數(shù)+1
* 同一個(gè)不能點(diǎn)贊兩次
* @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");
}
/**
* 取消點(diǎn)贊
* @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);
}
/**
* 自增,點(diǎn)贊數(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ù)定時(shí)器時(shí)長有延遲
// if(likedMapper.selectByObjIdAndOpenid(objId,openid,Integer.valueOf(type)).getLikeStatus()==0){
// redisTemplate.opsForHash().increment(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, key, 1);
// }
//防止重復(fù)點(diǎn)贊
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中存放的點(diǎn)贊數(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中點(diǎn)贊數(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;
/**
* 將緩存中點(diǎn)贊記錄持久化到數(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);
}
}
}
/**
* 將點(diǎn)贊數(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){
//為主評(píng)論
commentsInfoMapper.updateByPrimaryKey(objId,likeNum);
}else if(type == 2){
//為子評(píng)論
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 {
//保存用戶點(diǎn)贊數(shù)據(jù)的key
public static final String MAP_KEY_USER_LIKED = "redis_liked";
//保存用戶被點(diǎn)贊數(shù)量的key
public static final String MAP_KEY_USER_LIKED_COUNT = "redis_liked_count";
/**
* 拼接被點(diǎn)贊的用戶id和點(diǎn)贊的人的id作為key。格式 222222::333333::1
* @param likedUserId 被點(diǎn)贊的人id
* @param likedPostId 點(diǎn)贊的人的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();
}
}
定時(shí)任務(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è)置時(shí)間周期單位秒
.withIntervalInHours(2) //兩個(gè)小時(shí)執(zhí)行一次
.repeatForever();
return TriggerBuilder.newTrigger().forJob(quartzDetail())
.withIdentity(LIKE_TASK_IDENTITY)
.withSchedule(scheduleBuilder)
.build();
}
}
定時(shí)任務(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 里的點(diǎn)贊信息同步到數(shù)據(jù)庫里
likedService.transLikedFromRedisToDB();
likedService.transLikedCountFromRedisToDB();
}
}
參考:
springboot如何實(shí)現(xiàn)點(diǎn)贊功能
點(diǎn)贊功能的實(shí)現(xiàn)及Springboot定時(shí)器的應(yīng)用
還有好多,第一頁搜出來的基本都看過,第一次搞真心不會(huì)。。。
f12去看看csdn點(diǎn)贊,會(huì)發(fā)現(xiàn)點(diǎn)文章的贊會(huì)有點(diǎn)贊量的返回。點(diǎn)評(píng)論的贊就沒


嗶哩嗶哩就看不懂了?。?!



到此這篇關(guān)于SpringBoot物品點(diǎn)贊功能實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)SpringBoot點(diǎn)贊內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
在Java Spring框架中使用的設(shè)計(jì)模式有哪些
面試中常會(huì)被問道Spring框架使用了哪些設(shè)計(jì)模式?關(guān)于這個(gè)問題本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09
Java實(shí)現(xiàn)簡(jiǎn)單的郵件發(fā)送功能
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)單的郵件發(fā)送功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07
Java Listener監(jiān)聽器使用規(guī)范詳細(xì)介紹
監(jiān)聽器是一個(gè)專門用于對(duì)其他對(duì)象身上發(fā)生的事件或狀態(tài)改變進(jìn)行監(jiān)聽和相應(yīng)處理的對(duì)象,當(dāng)被監(jiān)視的對(duì)象發(fā)生情況時(shí),立即采取相應(yīng)的行動(dòng)。監(jiān)聽器其實(shí)就是一個(gè)實(shí)現(xiàn)特定接口的普通java程序,這個(gè)程序?qū)iT用于監(jiān)聽另一個(gè)java對(duì)象的方法調(diào)用或?qū)傩愿淖?/div> 2023-01-01
Java的NIO之并發(fā)環(huán)境下非阻塞IO技術(shù)詳解
這篇文章主要介紹了Java的NIO之并發(fā)環(huán)境下非阻塞IO技術(shù)詳解,Java NIO(New IO)是Java平臺(tái)提供的一種用于高效處理I/O操作的API,它引入了一組新的類和概念,以提供更好的性能和可擴(kuò)展性,需要的朋友可以參考下2023-09-09
在Eclipse IDE使用Gradle構(gòu)建應(yīng)用程序(圖文)
這篇文章主要介紹了在Eclipse IDE使用Gradle構(gòu)建應(yīng)用程序(圖文),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-12-12最新評(píng)論

