通用點(diǎn)贊設(shè)計(jì)(二)使用redis的hash和zset類(lèi)型

靡不有初,鮮克有終

這次我們來(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)的

image.png

使用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

image.png

具體實(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);

    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末淮野,一起剝皮案震驚了整個(gè)濱河市捧书,隨后出現(xiàn)的幾起案子吹泡,更是在濱河造成了極大的恐慌,老刑警劉巖经瓷,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件爆哑,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡舆吮,警方通過(guò)查閱死者的電腦和手機(jī)揭朝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)色冀,“玉大人潭袱,你說(shuō)我怎么就攤上這事》嫣瘢” “怎么了屯换?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)与学。 經(jīng)常有香客問(wèn)我彤悔,道長(zhǎng),這世上最難降的妖魔是什么索守? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任晕窑,我火速辦了婚禮,結(jié)果婚禮上卵佛,老公的妹妹穿的比我還像新娘杨赤。我一直安慰自己,他們只是感情好截汪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布望拖。 她就那樣靜靜地躺著,像睡著了一般挫鸽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鸥跟,一...
    開(kāi)封第一講書(shū)人閱讀 51,604評(píng)論 1 305
  • 那天丢郊,我揣著相機(jī)與錄音,去河邊找鬼医咨。 笑死枫匾,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的拟淮。 我是一名探鬼主播干茉,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼很泊!你這毒婦竟也來(lái)了角虫?” 一聲冷哼從身側(cè)響起沾谓,我...
    開(kāi)封第一講書(shū)人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎戳鹅,沒(méi)想到半個(gè)月后均驶,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡枫虏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年妇穴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片隶债。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡腾它,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出死讹,到底是詐尸還是另有隱情瞒滴,我是刑警寧澤,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布回俐,位于F島的核電站逛腿,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏仅颇。R本人自食惡果不足惜单默,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望忘瓦。 院中可真熱鬧搁廓,春花似錦、人聲如沸耕皮。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)凌停。三九已至粱年,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間罚拟,已是汗流浹背台诗。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赐俗,地道東北人拉队。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像阻逮,于是被迫代替她去往敵國(guó)和親粱快。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容