SpringBoot----物品收藏功能实现
SpringBoot----评论回复功能实现(数据库设计)
SpringBoot----文件(图片)上传与显示(下载)
这个功能耗费了我挺多时间,简单实现很简单,就++ – .但是还是感觉这种点赞是一个高频率的请求,而且搜的时候我看都是使用redis做缓存。b站也搜到一个视频来着,也是一样的。
效果:
功能:首先还是一个先发请求返回数据,但是先数据存到了redis中,然后使用springboot定时任务每隔一定时间将数据存到mysql中。这样可以防止redis挂掉之后数据丢失。
数据库设计:
前端就发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;
/**
* 保存点赞数据到redis
* 点赞数+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);
}
//恢复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;
/**
* 保存数据到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");
}
/**
* 删除数据,没有用到
* @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);
}
/**
* 自增,点赞数++
* @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);
//根据定时器时长有延迟
// if(likedMapper.selectByObjIdAndOpenid(objId,openid,Integer.valueOf(type)).getLikeStatus()==0){
// redisTemplate.opsForHash().increment(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, key, 1);
// }
//防止重复点赞
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中存放的点赞数据然后存放到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中点赞数量
@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中数据恢复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;
/**
* 将缓存中点赞记录持久化到数据库
*/
@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);
}
}
}
/**
* 将点赞数量持久化到数据库
*/
@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 {
//保存用户点赞数据的key
public static final String MAP_KEY_USER_LIKED = "redis_liked";
//保存用户被点赞数量的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();
}
}
定时任务配置类
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) //设置时间周期单位秒
.withIntervalInHours(2) //两个小时执行一次
.repeatForever();
return TriggerBuilder.newTrigger().forJob(quartzDetail())
.withIdentity(LIKE_TASK_IDENTITY)
.withSchedule(scheduleBuilder)
.build();
}
}
定时任务工具类
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 里的点赞信息同步到数据库里
likedService.transLikedFromRedisToDB();
likedService.transLikedCountFromRedisToDB();
}
}
参考:springboot如何实现点赞功能
点赞功能的实现及Springboot定时器的应用
还有好多,第一页搜出来的基本都看过,第一次搞真心不会。。。
f12去看看csdn点赞,会发现点文章的赞会有点赞量的返回。点评论的赞就没
哔哩哔哩就看不懂了!!!
2021/4/26 16:11