靡不有初,鮮克有終
這次我們來(lái)使用hash和zset完成點(diǎn)贊功能
使用hash類(lèi)型設(shè)計(jì)點(diǎn)贊功能
我們知道redis的hash類(lèi)型需要三個(gè)參數(shù)。key衡查、hashkey 和 value。在點(diǎn)贊業(yè)務(wù)中,可以做如下設(shè)計(jì)
key 設(shè)計(jì) ==> 使用
枚舉標(biāo)識(shí)符:文章的ID
作為key
hashkey 設(shè)計(jì)==> 點(diǎn)贊用戶的id作為hashkey
value 設(shè)計(jì) ==> 使用點(diǎn)贊的用戶信息做為value
這樣就能完成直接展示點(diǎn)贊人的具體信息假抄,而不需要到mysql中再次查詢了。之前使用set類(lèi)型是做不到這點(diǎn)的
使用zset設(shè)計(jì)獲贊主體排行榜功能
要展示一個(gè)被點(diǎn)贊數(shù)據(jù)從大到小排序的文章列表丽猬,這個(gè)功能就可以使用redis的Zset類(lèi)型來(lái)實(shí)現(xiàn)宿饱,因?yàn)閆set是可排序的
那么可以做如下設(shè)計(jì)
key 設(shè)計(jì) ==> 使用 主體類(lèi)型的
枚舉標(biāo)識(shí)符
作為key
value 設(shè)計(jì) ==> 使用被點(diǎn)贊的主體信息做為value
具體實(shí)現(xiàn)
主體類(lèi)型枚舉類(lèi)
package com.springboot.study.demo1.like.model;
/**
*@description: BType 點(diǎn)贊主體的枚舉類(lèi)型
*@author: yinkai
*@create: 2020/3/11 15:31
*/
public enum BType {
//點(diǎn)贊
LIKED_ARTICLE("文章點(diǎn)贊"),
LIKED_ARTICLECOMMENT("文章評(píng)論點(diǎn)贊"),
LIKED_VIDEO("視頻點(diǎn)贊"),
LIKED_VIDEOCOMMENT("視頻評(píng)論點(diǎn)贊"),
//點(diǎn)贊統(tǒng)計(jì)
LIKED_ARTICLE_REPORT ("文章點(diǎn)贊統(tǒng)計(jì)"),
LIKED_ARTICLECOMMENT_REPORT("文章評(píng)論點(diǎn)贊統(tǒng)計(jì)"),
LIKED_VIDEO_REPORT("視頻點(diǎn)贊統(tǒng)計(jì)"),
LIKED_VIDEOCOMMENT_REPORT("視頻評(píng)論點(diǎn)贊統(tǒng)計(jì)");
private String bType;
BType(String bType) {
this.bType = bType;
}
public String getbType() {
return bType;
}
public void setbType(String bType) {
this.bType = bType;
}
}
redis的 key生成工具類(lèi)
package com.springboot.study.demo1.like.utils;
import com.springboot.study.demo1.like.model.BType;
/**
* 生成Redis的 key
*/
public class LikedUtil {
/**
* 生成點(diǎn)贊的key
*
* @param bType
* @param subjectId
* @return
*/
public static String getKey(BType bType, Object subjectId) {
return bType + ":" + subjectId;
}
/**
* 生成點(diǎn)贊數(shù)量的key
*
* @param bType
* @return
*/
public static String getReportKey(BType bType) {
BType type = null;
switch (bType) {
case LIKED_ARTICLE:
type = BType.LIKED_ARTICLE_REPORT;
break;
case LIKED_ARTICLECOMMENT:
type = BType.LIKED_VIDEO_REPORT;
break;
case LIKED_VIDEO:
type = BType.LIKED_VIDEO_REPORT;
break;
case LIKED_VIDEOCOMMENT:
type = BType.LIKED_VIDEOCOMMENT_REPORT;
break;
//默認(rèn)返回LIKED_ARTICLE_REPORT 文章數(shù)量key
default:
type = BType.LIKED_ARTICLE_REPORT;
}
return type.name();
}
public static void main(String[] args) {
String key = LikedUtil.getReportKey(BType.LIKED_ARTICLE);
System.out.println(key);
}
}
創(chuàng)建User實(shí)體類(lèi)
package com.springboot.study.demo1.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import sun.plugin2.message.Serializer;
import java.io.Serializable;
/**
*@description: User 實(shí)體類(lèi)
*@author: yinkai
*@create: 2020/2/25 9:21
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class User implements Serializable {
private Long id;
private String name;
private Integer age;
private String email;
@TableField(exist = false)
private Integer count;
}
主體(文章)實(shí)體的實(shí)體類(lèi)
package com.springboot.study.demo1.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class Article {
//文章id
private Integer articleId;
//文章標(biāo)題
private String title;
//文章摘要
private String summary;
//文章圖片
private String img;
//點(diǎn)贊數(shù),該字段只用于排行版統(tǒng)計(jì)
private Integer likeNum;
}
定義service接口
package com.springboot.study.demo1.like.service;
import com.springboot.study.demo1.like.model.BType;
import java.util.Set;
/**
*@description: ILikedService 點(diǎn)贊接口
*@author: yinkai
*@create: 2020/3/11 16:39
*/
public interface ILikedService {
/**
* 點(diǎn)贊/取消點(diǎn)贊
*
* @param bType 業(yè)務(wù)類(lèi)型
* @param subjectId 被點(diǎn)贊主體ID
* @param postId 點(diǎn)贊主體ID
* @param user 點(diǎn)贊用戶
* @param subject 被點(diǎn)贊主體
*/
void liked(BType bType, Object subjectId, Object postId, Object user,Object subject);
/**
* 查詢單個(gè)主體(如文章)的獲贊個(gè)數(shù)脚祟,如 id為 1的文章被點(diǎn)贊的數(shù)量
*
* @param bType 實(shí)體類(lèi)型
* @param subjectId 被點(diǎn)贊主體ID
* @return 點(diǎn)贊數(shù)量
*/
Long count(BType bType, Object subjectId);
/**
* 獲得獲得點(diǎn)贊數(shù) 前top名的bType排行版
* @param bType 實(shí)體類(lèi)型
* @return top 排行版截取數(shù)
*/
Set<Object> getSubjectTopN(BType bType, Long top);
}
編寫(xiě)實(shí)現(xiàn)類(lèi)
實(shí)現(xiàn)了三個(gè)方法谬以。 liked方法 實(shí)現(xiàn) 點(diǎn)贊和取消點(diǎn)贊、count方法查詢具體主體(文章)的獲贊總數(shù)由桌、getSubjectTopN方法 獲得點(diǎn)贊數(shù)指定前多少名的主體排行榜
liked方法在實(shí)現(xiàn)點(diǎn)贊邏輯以外還 插入了被點(diǎn)贊文章的信息为黎。這樣為之后的排行榜查詢提供了數(shù)據(jù)
package com.springboot.study.demo1.like.service.impl;
import com.springboot.study.demo1.entity.Article;
import com.springboot.study.demo1.like.model.BType;
import com.springboot.study.demo1.like.service.ILikedService;
import com.springboot.study.demo1.like.utils.LikedUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import java.util.Set;
@Service
public class ILikedServiceImpl implements ILikedService {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private HashOperations<String, Object, Object> hashOperations;
@Autowired
private ZSetOperations<String, Object> zSetOperations;
/**
* 點(diǎn)贊/取消點(diǎn)贊
*
* @param bType 業(yè)務(wù)類(lèi)型
* @param subjectId 被點(diǎn)贊主體ID
* @param postId 點(diǎn)贊主體ID
*/
@Override
public void liked(BType bType, Object subjectId, Object postId, Object user, Object subject) {
/**
* 點(diǎn)贊實(shí)現(xiàn),操作hash
*/
//生成點(diǎn)贊key
String key = LikedUtil.getKey(bType, subjectId);
//判斷key對(duì)應(yīng)的set中是否存在subjectId
//存在則取消點(diǎn)贊
Boolean aBoolean = hashOperations.hasKey(key, postId);
if (aBoolean) {
hashOperations.delete(key, postId);
} else {
//不存在則點(diǎn)贊
hashOperations.put(key, postId, user);
}
/**
*
*
* 在點(diǎn)贊和取消點(diǎn)贊的同時(shí)進(jìn)行 點(diǎn)贊統(tǒng)計(jì)排序?qū)崿F(xiàn)行您,操作zset
*/
//生成點(diǎn)贊統(tǒng)計(jì)key
String reportKey = LikedUtil.getReportKey(bType);
if (aBoolean) {
//已經(jīng)存在贊
//需要取消點(diǎn)贊铭乾,設(shè)置分?jǐn)?shù)為 -1 , 最低分?jǐn)?shù)
zSetOperations.incrementScore(reportKey, subject, -1);
} else {
//不存在贊
//需要點(diǎn)贊
//獲得分?jǐn)?shù)
Double score = zSetOperations.score(reportKey, subject);
if (score == null) {
//score分?jǐn)?shù)不存在則設(shè)置 score為1
zSetOperations.add(reportKey, subject, 1);
} else {
//score分?jǐn)?shù)存在則 zset的score分?jǐn)?shù)加1
zSetOperations.incrementScore(reportKey, subject, 1);
}
}
}
/**
* 查詢單個(gè)主體(如文章)的獲贊個(gè)數(shù)邑雅,如 id為 1的文章被點(diǎn)贊的數(shù)量
*
* @param bType 業(yè)務(wù)類(lèi)型
* @param subjectId 被點(diǎn)贊主體ID
* @return 點(diǎn)贊數(shù)量
*/
@Override
public Long count(BType bType, Object subjectId) {
//生成key
String key = LikedUtil.getKey(bType, subjectId);
//獲得點(diǎn)贊數(shù)量
Long size = hashOperations.size(key);
return size;
}
@Override
public Set<Object> getSubjectTopN(BType bType, Long top) {
//獲得點(diǎn)贊主體排行版
Set<Object> set = zSetOperations.reverseRange(LikedUtil.getReportKey(bType), 0, top);
//得到以bType開(kāi)頭的所有key組成的集合
Set<String> keys = redisTemplate.keys(bType + ":*");
//給返回結(jié)果集設(shè)置 點(diǎn)贊數(shù)
for (Object obj : set) {
for (String key : keys) {
Integer id = Integer.valueOf(key.substring(key.lastIndexOf(":") + 1));
if (((Article) obj).getArticleId().equals(id)) {
((Article) obj).setLikeNum(hashOperations.size(key).intValue());
}
}
}
return set;
}
}
功能測(cè)試
package com.springboot.study.demo1;
import com.springboot.study.demo1.entity.Article;
import com.springboot.study.demo1.entity.User;
import com.springboot.study.demo1.like.model.BType;
import com.springboot.study.demo1.like.service.ILikedService;
import org.junit.runner.RunWith;
import org.omg.PortableInterceptor.SYSTEM_EXCEPTION;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import reactor.core.publisher.Flux;
import java.util.Map;
import java.util.Set;
/**
* @author ceshi
* @Title:
* @Package
* @Description:
* @date 2020/3/1115:49
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class ILikedServiceImplTest {
@Autowired
ILikedService iLikedService;
@org.junit.Test
public void liked() {
//文章1片橡、文章2、文章3分被點(diǎn)贊
Article article1 = new Article().setArticleId(1000).setTitle("文章1").setSummary("aaaaaaaaaaaaa").setImg("圖");
Article article2 = new Article().setArticleId(1001).setTitle("文章2").setSummary("bbbbbbbbbbbbb").setImg("圖");
Article article3 = new Article().setArticleId(1002).setTitle("文章3").setSummary("ccccccccccccc").setImg("圖");
//對(duì)id為 1000點(diǎn)贊
iLikedService.liked(BType.LIKED_ARTICLE, 1000, 1, new User().setId((long) 1).setName("A").setAge(22), article1);
//對(duì)id為 1001點(diǎn)贊
iLikedService.liked(BType.LIKED_ARTICLE, 1001, 2, new User().setId((long) 2).setName("B").setAge(23),article2);
iLikedService.liked(BType.LIKED_ARTICLE, 1001, 3, new User().setId((long) 3).setName("B").setAge(24),article2);
//對(duì)id為 1002點(diǎn)贊
iLikedService.liked(BType.LIKED_ARTICLE, 1002, 1, new User().setId((long) 1).setName("C").setAge(24),article3);
iLikedService.liked(BType.LIKED_ARTICLE, 1002, 2, new User().setId((long) 2).setName("C").setAge(24),article3);
iLikedService.liked(BType.LIKED_ARTICLE, 1002, 3, new User().setId((long) 3).setName("C").setAge(24),article3);
}
@org.junit.Test
public void likedCancel() {
//取消id為2的用戶對(duì)LIKED_ARTICLE文章id為1000的點(diǎn)贊
Article article1 = new Article().setArticleId(1000).setTitle("文章1").setSummary("aaaaaaaaaaaaa").setImg("圖");
iLikedService.liked(BType.LIKED_ARTICLE, 1000, 0, new User().setId((long) 0).setName("A").setAge(22), article1);
}
@org.junit.Test
public void count() {
Long count = iLikedService.count(BType.LIKED_ARTICLE, 1000);
System.out.println(count);
}
@org.junit.Test
public void getSubjectTopN() {
Set<Object> subjectTopN = iLikedService.getSubjectTopN(BType.LIKED_ARTICLE, 100L);
System.out.println(subjectTopN);
}
}