redis實現文章投票邏輯

weixin_33716557發表於2018-07-15
<?php

/**
 * Class Vote
 *
 * 實現業務邏輯:一個文章投票系統:每一篇文章的投票期限是一週,文章釋出超過一週後無法投票,
 * 釋出的文章如果每天大於等於200票就認為是一個有趣的文章,就應該在顯示固定名次的分數排行榜上至少多排一天。
 */
class Vote
{
    private $redis;
    const ONE_WEEK_IN_SECONDS = 604800;
    const VOTE_SCORE = 432; //每天86400/200 得到432
    const ARTICLE_PAGE = 100; //每頁顯示的文章
    private static $instance;

    public static function getInstance()
    {
        if (is_null(self::$instance)) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    //連結redis初始化

    public function __construct()
    {
        $this->redis = new Redis();
        $this->redis->connect('127.0.0.1', '6379', '3');
    }

    //釋出文章
    public function postArticle($user, $title)
    {
        //用一個計數器不斷的增加生成文章的id
        $articleId = (string)$this->redis->incr('article:');
        $vote = 'vote:' . $articleId;
        $this->redis->sAdd($vote, $user); //用一個set儲存對文章投票的使用者,並把文章的建立者放入集合裡面
        $this->redis->expire($vote, self::ONE_WEEK_IN_SECONDS);//設定投票的集合的有效期
        $article = 'article:' . $articleId;
        $link = "http://www.article.com?id=" . $articleId;
        $articleData = [
            'votes' => 1,
            'user' => $user,
            'title' => $title,
            'link' => $link
        ];
        //生成文章的資訊,並按照規則把每個文章存入hash裡面
        $this->redis->hMset($article, $articleData);
        //在zset time裡面記錄文章的釋出時間為現在
        $this->redis->zAdd('time', time(), $article);
        //在zset score裡面增加文章的分數
        $this->redis->zAdd('score', time() +
            self::VOTE_SCORE, $article);
    }

    /**
     * 按時間順序或者投票和時間綜合順序獲取文章
     * @param $page
     * @param string $order
     * @return array
     */

    public function getArticle($page, $order = 'score')
    {
        $start = ($page - 1) * self::ARTICLE_PAGE;
        $end = $start + self::ARTICLE_PAGE - 1;
        //逆向獲取對應zset集合裡面範圍內的文章
        $ids = $this->redis->zRevRange($order, $start, $end);
        $articles = [];
        foreach ($ids as $id) {
            //根據值在用hash獲取資料
            $articleData = $this->redis->hGetAll($id);
            $articles[] = $articleData;
        }
        return $articles;
    }

    /**
     * @param $user  使用者給文章投票
     * @param $article
     */

    public function articleVote($user, $articleId)

    {
        $article = "article:" . $articleId;
        $cutoff = time() - self::ONE_WEEK_IN_SECONDS;
        /**
         * time是一個zset,有序結合,time是集合的名字,
         *member是根據規則"article:id"拼接出的字串,score
         * 就是文章的釋出時間
         * time用來記錄每個文章和文章對應的釋出時間
         */
        if ($this->redis->zScore('time', $article) < $cutoff) { //時間過期了就返回
            return;
        }
        /*** 每個文章用一個單獨的set記錄投票的使用者,集合命名
         * 規則是"vote:文章id",把投票的使用者加入到這個集合裡面
         */

        if ($this->redis->sAdd('voted:' . $articleId, $user)) {
            //對文章的votes值加1
            $this->redis->hIncrBy($article, 'votes', 1);
            //給用來記錄文章分數的zset裡面對對應文章增加分數
            $this->redis->zIncrBy('score', self::VOTE_SCORE, $article);
        }
    }
}

相關文章