一个关于面经的开发网站全程记录--5
总览:完成点赞,取消点赞,收藏,取消收藏,浏览数量显示
浏览数量:
@ApiOperation(value = "查看某一篇面经,查看完之后游览量+1")
@GetMapping(value = "getById/{id}")
public R getById(@PathVariable String id){
AllExper allExper = allExperService.getById(id);
Map map = new HashMap<>();
map.put("experence",allExper);
return R.ok().data(map);
}
上面的接口作用就是根据id查看一篇面经的具体内容,此时一旦调用该接口浏览数量+1
第一种方法直接操作mysql:在这里可以直接在存放面经的表中设置一个浏览数量的字段,每次调用将该字段的值+1 第二种方法使用redis,大致思路如下:每次启动时将每篇面经的浏览数量写入redis中,在redis中进行浏览量的+1,之后通过周期任务的方式定期同步数据在MySQL,最后也要在每次程序关闭是进行数据同步之后才能关闭。
前提准备: 要用到Redis中操作其中数据结构的部分方法,自己对其封装名称为RedisUtil,这玩意网上大把,不用自己写。
@Component
public class RedisUtil {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private StringRedisTemplate stringRedisTemplate;
public RedisUtil(RedisTemplate<String, Object> redisTemplate, StringRedisTemplate stringRedisTemplate) {
this.redisTemplate = redisTemplate;
this.stringRedisTemplate = stringRedisTemplate;
}
/**
* 指定缓存失效时间
* @param key 键
* @param time 时间(秒)
* @return
*/
public boolean expire(String key,long time){
try {
if(time>0){
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key 获取过期时间
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key){
return redisTemplate.getExpire(key,TimeUnit.SECONDS);
}
/**
* 判断key是否存在
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key){
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String ... key){
if(key!=null&&key.length>0){
if(key.length==1){
redisTemplate.delete(key[0]);
}else{
//报错 更改
redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));
}
}
}
//============================String=============================
/**
* 普通缓存获取
* @param key 键
* @return 值
*/
public Object get(String key){
return key==null?null:redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key,Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key,Object value,long time){
try {
if(time>0){
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
}else{
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
* @param key 键
* @param delta 要增加几(大于0)
* @return
*/
public long incr(String key, long delta){
if(delta<0){
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
* @param key 键
* @param delta 要减少几(小于0)
* @return
*/
public long decr(String key, long delta){
if(delta<0){
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
//================================Map=================================
/**
* HashGet
* @param key 键 不能为null
* @param item 项 不能为null
* @return 值
*/
public Object hget(String key,String item){
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
* @param key 键
* @return 对应的多个键值
*/
public Map<Object,Object> hmget(String key){
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
* @param key 键
* @param map 对应多个键值
* @return true 成功 false 失败
*/
public boolean hmset(String key, Map<String,Object> map){
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 并设置时间
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String,Object> map, long time){
try {
redisTemplate.opsForHash().putAll(key, map);
if(time>0){
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key,String item,Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key,String item,Object value,long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if(time>0){
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除hash表中的值
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item){
redisTemplate.opsForHash().delete(key,item);
}
/**
* 判断hash表中是否有该项的值
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item){
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
* @return
*/
public double hincr(String key, String item,double by){
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
* @return
*/
public double hdecr(String key, String item,double by){
return redisTemplate.opsForHash().increment(key, item,-by);
}
//============================set=============================
/**
* 根据key获取Set中的所有值
* @param key 键
* @return
*/
public Set<Object> sGet(String key){
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key,Object value){
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将数据放入set缓存
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object...values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 将set数据放入缓存
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key,long time,Object...values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if(time>0) {
expire(key, time);
}
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取set缓存的长度
* @param key 键
* @return
*/
public long sGetSetSize(String key){
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object ...values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
//===============================list=================================
/**
* 获取list缓存的内容
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
* @return
*/
public List<Object> lGet(String key, long start, long end){
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取list缓存的长度
* @param key 键
* @return
*/
public long lGetListSize(String key){
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 通过索引 获取list中的值
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
* @return
*/
public Object lGetIndex(String key,long index){
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据索引修改list中的某条数据
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index,Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移除N个值为value
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key,long count,Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 模糊查询获取key值
* @param pattern
* @return
*/
public Set keys(String pattern){
return redisTemplate.keys(pattern);
}
/**
* 使用Redis的消息队列
* @param channel
* @param message 消息内容
*/
public void convertAndSend(String channel, Object message){
redisTemplate.convertAndSend(channel,message);
}
/**
* 根据起始结束序号遍历Redis中的list
* @param listKey
* @param start 起始序号
* @param end 结束序号
* @return
*/
public List<Object> rangeList(String listKey, long start, long end) {
//绑定操作
BoundListOperations<String, Object> boundValueOperations = redisTemplate.boundListOps(listKey);
//查询数据
return boundValueOperations.range(start, end);
}
/**
* 弹出右边的值 --- 并且移除这个值
* @param listKey
*/
public Object rifhtPop(String listKey){
//绑定操作
BoundListOperations<String, Object> boundValueOperations = redisTemplate.boundListOps(listKey);
return boundValueOperations.rightPop();
}
/**----------------zSet相关操作------------------*/
/**
* 添加元素,有序集合是按照元素的score值由小到大排列
*
* @param key
* @param value
* @param score
* @return
*/
public Boolean zAdd(String key, String value, double score) {
return stringRedisTemplate.opsForZSet().add(key, value, score);
}
public Long zAdd(String key, Set<ZSetOperations.TypedTuple<String>> values) {
return stringRedisTemplate.opsForZSet().add(key, values);
}
public Long zRemove(String key, Object... values) {
return stringRedisTemplate.opsForZSet().remove(key, values);
}
/**
* 增加元素的score值,并返回增加后的值
* @return
*/
public Double zIncrementScore(String key, String value, double delta) {
return stringRedisTemplate.opsForZSet().incrementScore(key, value, delta);
}
/**
* 返回元素在集合的排名,有序集合是按照元素的score值由小到大排列
* @param key
* @param value
* @return
*/
public Long zRank(String key, Object value) {
return stringRedisTemplate.opsForZSet().rank(key, value);
}
/**
* 返回元素在集合的排名,按元素的score值由大到小排列
* @param key
* @param value
* @return
*/
public Long zReverseRank(String key, Object value) {
return stringRedisTemplate.opsForZSet().reverseRank(key ,value);
}
/**
* 获取集合的元素, 从小到大排序
* @param key
* @param start
* @param end
* @return
*/
public Set<String> zRange(String key, long start, long end) {
return stringRedisTemplate.opsForZSet().range(key, start, end);
}
/**
* 获取集合元素, 并且把score值也获取
*/
public Set<ZSetOperations.TypedTuple<String>> zRangeWithScores(String key, long start, long end) {
return stringRedisTemplate.opsForZSet().rangeWithScores(key, start, end);
}
/**
* 根据Score值查询集合元素
* @param key
* @param min
* @param max
* @return
*/
public Set<String> zRangeByScore(String key, double min, double max) {
return stringRedisTemplate.opsForZSet().rangeByScore(key, min, max);
}
/**
* 根据Score值查询集合元素, 从小到大排序
* @param key
* @param min
* @param max
* @return
*/
public Set<ZSetOperations.TypedTuple<String>> zRangeByScoreWithScores(String key, double min, double max) {
return stringRedisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max);
}
public Set<ZSetOperations.TypedTuple<String>> zRangeByScoreWithScores(String key, double min, double max, long start, long end) {
return stringRedisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max, start, end);
}
/**
* 获取集合的元素, 从大到小排序
* @param key
* @param start
* @param end
* @return
*/
public Set<String> zReverseRange(String key, long start, long end) {
return stringRedisTemplate.opsForZSet().reverseRange(key, start, end);
}
/**
* 获取集合的元素, 从大到小排序, 并返回score值
* @param key
* @param start
* @param end
* @return
*/
public Set<ZSetOperations.TypedTuple<String>> zReverseRangeWithScores(String key, long start, long end) {
return stringRedisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);
}
}
首先写一个aop的切面方法,对前边controll中的public R getById(@PathVariable String id)这个接口进行监听,一旦调用了该接口,浏览量直接+1
//指定为切面类
@Aspect
@Component
public class MyAspect {
private final AllExperService allExperService;
private final RedisUtil redisUtil;
public MyAspect(AllExperService allExperService, RedisUtil redisUtil) {
this.allExperService = allExperService;
this.redisUtil = redisUtil;
}
//定义一个名为"myPointCut()"的切面,getById()这个方法中
@Pointcut("execution(public * com.fourpeople.interview.controller.AllExperController.getById(..))")
public void myScanPointCut(){}
//打开一个面经之后,将面经的游览量+1
@After("myScanPointCut()")
public void doScanAfter(JoinPoint joinPoint) throws Throwable {
Object[] objs=joinPoint.getArgs();
String id = (String) objs[0];
//这个地方是操作Redis中的ZSet类型的scanCount 这个scanCount的数据是从mysql读入到redis读取的
redisUtil.zIncrementScore("scanCount",id,1);
}
}
接下来实现,mysql和redis的数据同步问题,即启动将mysql中的数据读入redis,定期同步数据,关闭时同步数据,写一个监听类用来实现该功能。(该类中也包含了点赞和收藏的同步读写)
/**
* @author zzy
* ,
*/
@Slf4j
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SyncRMDataListenerHandler {
// private final ArticleService articleService;
private final RedisUtil redisUtil;
private final AllExperService allExperService;
private final UserGoodService userGoodService;
private final CollExperService collExperService;
@Autowired
public SyncRMDataListenerHandler(AllExperService allExperService, RedisUtil redisUtil,
UserGoodService userGoodService, CollExperService collExperService){
this.allExperService = allExperService;
this.redisUtil = redisUtil;
this.userGoodService = userGoodService;
this.collExperService = collExperService;
}
public static final String SCAN_KEY = "scanCount";
public static final String COLLECT_KEY = "collectCount";
public static final String GOOD_KEY = "goodCount";
@PostConstruct
public void init() throws Exception {
log.info("数据初始化开始...");
//将数据库中的数据写入redis
List<AllExper> articleLst = allExperService.list();
articleLst.forEach(article -> {
//将浏览量、点赞数和收藏数写入redis 操作allexper表
redisUtil.zAdd(SCAN_KEY, article.getId(), article.getScanCount());
redisUtil.zAdd(COLLECT_KEY, article.getId(), article.getCollectCount());
redisUtil.zAdd(GOOD_KEY, article.getId(), article.getGoodCount());
});
//将usergood表中的信息写入redis
List<UserGood> userGoodList = userGoodService.list();
userGoodList.forEach(userGood -> {
//写入usergood表中的信息
String key = RedisKeyUtils.getGoodKey(userGood.getUserId(), userGood.getBeExperId());
UserGoodVo userGoodVo = new UserGoodVo();
BeanUtils.copyProperties(userGood,userGoodVo);
redisUtil.hset(MAP_GOOD_KEY,key,userGoodVo);
});
//将collExper表中的信息写入Redis
List<CollExper> collExperList = collExperService.list();
collExperList.forEach(collExper -> {
//写入
String key = RedisKeyUtils.getGoodKey(collExper.getUserId(), collExper.getExperId());
CollExperVo collExperVo = new CollExperVo();
BeanUtils.copyProperties(collExper,collExperVo); //源——>目标
redisUtil.hset(MAP_COLL,key,collExperVo);
});
log.info("数据已写入redis...");
}
/**
* 关闭时操作
*/
@PreDestroy
public void afterDestroy() {
log.info("开始关闭...");
//将redis中的数据写入数据库
Set<ZSetOperations.TypedTuple<String>> scanCount = redisUtil.zReverseRangeWithScores("scanCount", 0, 10);
Set<ZSetOperations.TypedTuple<String>> collectCount = redisUtil.zReverseRangeWithScores("collectCount", 0, 10);
Set<ZSetOperations.TypedTuple<String>> goodCount = redisUtil.zReverseRangeWithScores("goodCount", 0, 10);
writeNum(scanCount, SCAN_KEY);
writeNum(collectCount, COLLECT_KEY);
writeNum(goodCount, GOOD_KEY);
//将redis中的点赞数据写入数据库
Map<Object, Object> map = redisUtil.hmget(MAP_GOOD_KEY);
log.info("点赞的map{}",map);
writeGood(map);
//将redis中的收藏数据写入数据库中
Map<Object,Object> mapColl = redisUtil.hmget(MAP_COLL);
log.info("收藏的map{}",map);
writeColl(mapColl);
log.info("redis写入数据库完毕");
}
@Scheduled(cron = "*/30 * * * * ?")
public void updateNum() {
log.info("周期任务开始执行...");
Set<ZSetOperations.TypedTuple<String>> scanCount = redisUtil.zReverseRangeWithScores("scanCount", 0, 10);
writeNum(scanCount, SCAN_KEY);
Set<ZSetOperations.TypedTuple<String>> collectCount = redisUtil.zReverseRangeWithScores("collectCount", 0, 10);
writeNum(collectCount, COLLECT_KEY);
Set<ZSetOperations.TypedTuple<String>> goodCount = redisUtil.zReverseRangeWithScores("goodCount", 0, 10);
writeNum(goodCount, GOOD_KEY);
Map<Object, Object> map = redisUtil.hmget(MAP_GOOD_KEY);
log.info("点赞的map{}",map);
writeGood(map);
//将redis中的收藏数据写入数据库中
Map<Object,Object> mapColl = redisUtil.hmget(MAP_COLL);
log.info("收藏的map{}",map);
writeColl(mapColl);
log.info("周期任务执行完毕,redis写入数据库完毕");
}
//将redis数据写入数据库的具体方法
private void writeNum(Set<ZSetOperations.TypedTuple<String>> set, String fieldName) {
set.forEach(item -> {
//人家的这个是转换为整形 我的不用 我的ID本来就是String
// Long id = Long.valueOf(item.getValue());
String id = item.getValue();
log.info("正在写入的id:{}",id);
//数量
Integer num = item.getScore().intValue();
log.info("数量:{}",num);
AllExper article = allExperService.getById(id);
switch (fieldName) {
case SCAN_KEY:
article.setScanCount(num);
break;
case COLLECT_KEY:
article.setCollectCount(num);
break;
case GOOD_KEY:
article.setGoodCount(num);
break;
default:
return;
}
//更新数据库
allExperService.updateNumById(article);
log.info("{} 更新完毕", fieldName);
});
}
}
注意:allExperService.updateNumById(article);这个更新数据库的方法,是自己写的sql,当然也可以用MP自带的方法,根据情况而定。
点赞和取消点赞的大致思路:判断此次点击是点赞还是取消点赞,如果是点赞就将goodCount的数量+1,同时将点赞人的id和被点赞文章的id拼接为redis中map的key存储到MAP_GOOD_KEY中,如果时取消点赞,就将点赞数-1.
拼接key的工具类;
@ApiOperation(value = "Redis拼接userID和beExperId,用于生成hash中的key")
public class RedisKeyUtils {
public static final String MAP_GOOD_KEY = "map_good";
public static final String MAP_COLL = "map_good";
public static String getGoodKey(String likedUserId, String likedPostId){
StringBuilder builder = new StringBuilder();
builder.append(likedUserId);
builder.append("::");
builder.append(likedPostId);
return builder.toString();
}
}
vo类:
@Data
public class UserGoodVo {
//自动生成
private String id;
private String userId;
private String beUserId;
private String beExperId;
private Integer state;
}
controller:
@ApiOperation(value = "取消点赞,删除redis中的key,点赞量-1,点赞,判断key是否在redis的map中,是的话就是点赞,存储UserGoodVO类到map中")
@PostMapping(value = "deleteGoodOne")
public R clickAddOrdeleteGoodOne(@RequestBody UserGoodVo userGoodVo){
userGoodService.clickAddOrdeleteGoodOne(userGoodVo);
return R.ok();
}
service:
@Override
public void clickAddOrdeleteGoodOne(UserGoodVo userGoodVo) {
String userId = userGoodVo.getUserId();
String beExperId = userGoodVo.getBeExperId();
int state = userGoodVo.getState();
String key = RedisKeyUtils.getGoodKey(userId,beExperId);
//如果redis中存在对应的key并且state=1标志着已经点赞,要进行取消点赞操作
if(redisUtil.hHasKey(MAP_GOOD_KEY,key) && state == 1){
userGoodVo.setState(0);
redisUtil.hset(MAP_GOOD_KEY,key,userGoodVo);
redisUtil.zIncrementScore("goodCount",beExperId,-1);
}else{
//进行点赞操作
userGoodVo.setState(1);
redisUtil.hset(MAP_GOOD_KEY,key,userGoodVo);
redisUtil.zIncrementScore("goodCount",beExperId,1);
}
}
将redis中数据写入mysql中,该方法要写到上面监听类中:
public void writeGood(Map<Object,Object> map){
//将redis中map存放数据的value取出来,将UserGoodVo对象转换为UserGood对象存放到数据库中
Iterator<Map.Entry<Object, Object>> it = map.entrySet().iterator();
while(it.hasNext()){
Map.Entry<Object, Object> entry = it.next();
log.info("map中的一个entry:{}",entry);
//将object类型 转换为UserGoodVo
UserGoodVo entryUserGoodVo = new UserGoodVo();
BeanUtils.copyProperties(entry.getValue(),entryUserGoodVo);
log.info("entryUserGoodVo:{}",entryUserGoodVo);
//将UserGoodVo转换诶UserGood
UserGood entryUserGood = new UserGood();
BeanUtils.copyProperties(entryUserGoodVo, entryUserGood);
log.info("entryUserGood:{}",entryUserGood);
//如果state是0,表明是取消点赞,故不用写入mysql中
QueryWrapper<UserGood> wrapper = new QueryWrapper<>();
wrapper.ne("state",0);
//存入数据库 将redis中存在的数据
userGoodService.saveOrUpdate(entryUserGood,wrapper);
}
}
以上是点赞,取消点赞。
下面是收藏和取消收藏。
这个原理跟点赞相同。
controll:
@ApiOperation(value = "收藏和取消收藏,判断state状态,如果已经收藏之后再次点击收藏是取消,否则是收藏")
@PostMapping(value = "addCollExper")
public R addOrDeleCollExper(@RequestBody CollExper collExper){
collExperService.addOrDeleCollExper(collExper);
return R.ok();
}
vo类:
@Data
public class CollExperVo {
private String id;
private String userId;
private String experId;
private Integer collState;
}
service:
@Override
public void addOrDeleCollExper(CollExper collExper) {
String key = RedisKeyUtils.getGoodKey(collExper.getUserId(),collExper.getExperId());
int collState = collExper.getCollState();
if(redisUtil.hHasKey(MAP_COLL,key) && collState == 1){
//如果collState值为1,并且key值已经在MAP_COLL中存在 则要进行的是取消收藏
//首先将收藏的数量-1
redisUtil.zIncrementScore("collectCount",collExper.getExperId(),-1);
//修改collState状态 将1改为0
collExper.setCollState(0);
//将collExper写入redis中数据格式是map
redisUtil.hset(MAP_COLL,key,collExper);
}else{
//进行收藏
collExper.setCollState(1);
//将数据加入map
redisUtil.hset(MAP_COLL,key,collExper);
//收藏量+1
redisUtil.zIncrementScore("collectCount",collExper.getExperId(),1);
}
}
将redis中数据写入mysql中,该方法要写到上面监听类中:
public void writeColl(Map<Object,Object> mapColl){
//将redis中的收藏数据map.entity依次取出来,转换为CollExper对象存入数据库
//利用迭代器遍历map
Iterator<Map.Entry<Object,Object>> it = mapColl.entrySet().iterator();
while(it.hasNext()){
Map.Entry<Object, Object> entry = it.next();
log.info("redis中存储收藏数据的其中一个map:{}", entry);
//将object类型对象转换为CollExperVo对象
CollExperVo collExperVo = new CollExperVo();
BeanUtils.copyProperties(entry.getValue(),collExperVo);
//将CollExperVo对象转换为CollExper对象
CollExper collExper = new CollExper();
BeanUtils.copyProperties(collExperVo,collExper);
//如果state是0,表明取消收藏,不用写入mysql
QueryWrapper<CollExper> wrapper = new QueryWrapper<>();
wrapper.ne("state",0);
collExperService.saveOrUpdate(collExper,wrapper);
}
}
以上就是总览中的所有功能,本文章中的监听类是刚开始的时候借鉴某位大佬的,但是我现在找不到那个连接了,勿怪,aop切面类是以前没有用过的,这次长见识了,监听类中的部分注解也是以前没有用过的,看不懂的自己查吧。
如有错误,请勿介意,当然也可以评论我进行修改。欢迎指正。