收藏+点赞(8.13)
对比之前的阅读量和评论量,收藏和点赞除了需要记录统计数据外,还需要记录一些状态。
一、收藏
1、业务分析
第一次点击是收藏文章,数量+1;再次点击是取消收藏,数量-1。因为 HTTP 协议无法记忆历史操作,所以必须在后端存储具体的收藏关系数据,才能准确判断当前状态并执行正确的切换逻辑。
①记录收藏状态 ---> Redis中存储状态信息
②记录文章的收藏数 ---> 复用之前的 strategy_static_hash: xx
2、存储方式
Set类型 Map<String, Set
方式一:
前缀:文章id [用户1id,用户2id,用户3id,……]
方式二(本次):
前缀:用户id [文章1id,文章2id,文章3id,……]
以上两种方式都可以,具体看业务需求。要考虑节省存储空间的情况:
- 文章比用户多,选第一种
- 用户比文章多,选第二种
在实际开发中,除了单纯比较数据体量以节省空间外,还需要结合具体的业务查询场景(如:是高频查询“文章的点赞列表”,还是高频查询“用户的收藏列表”)来最终决定存储方案。
3、代码实现
@PostMapping("/favor")
public R<Map<String, Object>> favor(Long sid) {
Map<String, Object> map = strategyService.favor(sid);
return R.ok(map);
}
---------------------------------------------------------------------------------------------------
@Getter
public enum RedisKeys {
USER_STRATEGY_FAVOR("user_strategy_favor", -1),
...
}
---------------------------------------------------------------------------------------------
/**
* 收藏
* @param sid
* @return
*/
@Override
public Map<String, Object> favor(Long sid) {
// 获取uid
Long uid = SecurityContextHolder.getUserId();
// 用uid拼接key (用户收藏文章的key)
String key = RedisKeys.USER_STRATEGY_FAVOR.join(uid.toString());
// 判断key,如果redis中没有这个key,存储一个空集合
if(!redisService.hasKey(key)){
Set<Long> set = new HashSet<>();
set.add(-1L);
redisService.setCacheSet(key, set);
}
// 统计数字的key
String statisKey = RedisKeys.STRATEGY_STATIS_HASH.join(sid.toString());
Boolean result = null;
// 判断key的集合中有没有sid文章,如果有,说明用户收藏了这篇文章,本次是取消收藏操作
if(redisService.isCacheSetContains(key,sid)){
// 取消收藏,收藏数 -1,result设置为false ,集合中删除sid
redisService.incrementCacheMapValue(statisKey, "favornum",-1);
result = false;
redisService.deleteCacheSetValue(key,sid);
}else{
// 收藏操作,收藏数 +1,result设置为true ,集合中添加sid
redisService.incrementCacheMapValue(statisKey, "favornum",1);
result = true;
redisService.addCacheSetValue(key,sid);
}
// 查询统计对象数据
Map<String, Object> cacheMap = redisService.getCacheMap(statisKey);
// 将result添加到统计数据的map中
cacheMap.put("result", result);
// 返回统计数据的map
return cacheMap;
}
@GetMapping("/isUserFavor")
public R<Boolean> isUserFavor(Long sid,Long uid) {
return R.ok(strategyService.isUserFavor(sid,uid));
}
----------------------------------------------------------------------------------------------------
@Override
public Boolean isUserFavor(Long id, Long uid) {
return redisService.isCacheSetContains(RedisKeys.USER_STRATEGY_FAVOR.join(uid.toString()),id);
}
二、点赞
对于同一篇文章,一个用户一天最多点5个赞,超过了就不能再点了。设置过期时间为一天,等到了第二天就可以再次点赞了。
1、业务分析
①记录当前的点赞状态
②记录文章的点赞数
2、存储方式
方式一(独立 Key 模式):Set类型 Map<String, Set
前缀: uid [sid1,sid2,sid3,......] 当天过期时间
缺点:只能记录当前用户今天点没点过赞,点了几次赞记不住
方式二(本次,文章维度聚合模式):String类型 Map<String, Integer>
前缀: sid:uid 2 当天过期时间
缺点:
- key过多
- 同一时间过期,Redis压力大。一般选择延时删除。
方式三(用户维度聚合模式,最推荐,与收藏类似): Hash类型 Map<String, Map<String, Integer>>
前缀: uid [sid1:1,sid2:2,sid3:1,......] 当天过期时间
方案二的优化
优点:
-
节省大量的key
-
过期只用删除一个key
方式四(全局聚合模式):Hash类型 Map<String, Map<String, Integer>>
点赞key [sid1:uid1:1,sid1:uid2:2,......] 当天过期时间
方案三的优化
3、代码实现
@PostMapping("/thumbsup")
public R<Map<String, Object>> thumbsup(Long sid) {
Map<String, Object> map = strategyService.thumbsup(sid);
return R.ok(map);
}
----------------------------------------------------------------------------------------------------
@Getter
public enum RedisKeys {
USER_STRATEGY_THUMBSUP("user_strategy_thumbsup", -1),
STRATEGY_STATIS_HASH("strategy_statis_hash", -1),
}
----------------------------------------------------------------------------------------------------
public class DateUtil {
// 获取给定日期的最后一分一秒
public static Date getEndDate(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 23);
calendar.set(Calendar.MINUTE, 59);
calendar.set(Calendar.SECOND, 59);
return calendar.getTime();
}
public static long getDateBetween(Date d1, Date d2) {
return Math.abs(d1.getTime() - d2.getTime()) / 1000;
}
}
----------------------------------------------------------------------------------------------------
@Override
public Map<String, Object> thumbsup(Long sid) {
//获取uid
Long uid = SecurityContextHolder.getUserId();
// 用uid拼接key (用户点赞文章的key)
String key = RedisKeys.USER_STRATEGY_THUMBSUP.join(sid.toString(),uid.toString());
if(!redisService.hasKey(key)){
Date now = new Date();
Date endDate = DateUtil.getEndDate(now);
long time = DateUtil.getDateBetween(now, endDate);
time = time == 0 ? 1L : time;
redisService.setCacheObject(key, 0,time, TimeUnit.SECONDS);
}
Long ret = redisService.incrementCacheObjectValue(key, 1);
Boolean result = null;
String statisKey = RedisKeys.STRATEGY_STATIS_HASH.join(sid.toString());
if(ret > 5){
// 今天点赞超过5次,点赞失败
result = false;
}else {
// 点赞成功
result = true;
redisService.incrementCacheMapValue(statisKey, "thumbsupnum",1);
}
Map<String, Object> cacheMap = redisService.getCacheMap(statisKey);
cacheMap.put("result", result);
return cacheMap;
}
浙公网安备 33010602011771号