使用多种 Redis 数据类型构建一个文章投票网站

<?php

/**
 * @description: 
 *     用hash"article:{$articleId}:content"存储文章的内容、标题、标签等
 *     用zset"article:time"存储文章的发布时间,作为发布时间时间排行榜
 *     用zset"article:score"存储文章的发布时间和点赞累计后的综合值,作为综合评分排行榜
 *     用set"article:{$articleId}:vote"存储给用户点赞的用户id,并设置一周的过期时间
 *     用set'article:tags'存储所有的文章标签
 *     用set"{$tag}:{$tagId}"存储每一个文章标签下的文章id
 * 
 * @author: 0Robert0
 */

class TestRedis
{
    const ADD_SCORE = 432;
    const EXPIRATION_TIME = 86400 * 7;
    private $redis;

    public function __construct()
    {
        $this->redis = new Redis();
        //连接参数:ip、端口、连接超时时间,连接成功返回true,否则返回false
        $this->redis->connect('127.0.0.1', 6379); 
    }

    // 发布文章
    // 先获取文章id;再创建文章发布时间有序集合;创建文章综合评分有序集合;
    // 然后创建为文章点赞的用户集合,并把作者本身写入进去,不允许自己给自己点赞,并设置一周的
    // 过期时间,为了内存考虑一周之后清掉用户点赞集合
    // 设置文章标签
    public function publishArticle($authorId, $tag)
    {
        // TODO 多个命令的串联操作,应该开启事务
        $articleId = $this->getId('article:id');
        $tagId = $this->getId('tag:id');
        $articleContent = [
            'title' => 'article title' . $articleId, 
            'author' => 'Rebort' . $articleId, 
            'content' => 'hello word',
            'tag' => $tag,
        ];
        $contentResult = $this->redis->hMset("article:{$articleId}:content", $articleContent);
        $timeResult = $this->redis->zAdd("article:time", time(), $articleId);
        $scoreResult = $this->redis->zAdd("article:score", time(), $articleId);
        $userResult = $this->redis->sAdd("article:{$articleId}:vote", $authorId);
        $expireResult = $this->redis->expire("article:{$articleId}:vote", self::EXPIRATION_TIME);
        $tagResult = $this->saveArticleTag($tag, $tagId);
        $userResult = $this->redis->sAdd("{$tag}:{$tagId}", $articleId);
        if ($articleId && $contentResult && $timeResult && $scoreResult && $userResult 
            && $expireResult && $tagResult) {
            return '发布文章成功';
        } else {
            return '发布文章失败';
        }
    }

    // 给文章设置标签;即把每一个文章根据标签分类;如果标签不存在,则创建标签
    public function saveArticleTag($tag, $tagId)
    {
        $exists = $this->redis->sismember('article:tags', $tag . ':' . $tagId);
        if (!$exists) {
            return $this->redis->sAdd('article:tags', $tag . ':' . $tagId);
        }
    }

    // 按发布时间展示指定范围内的文章
    public function showArticlesOfReleaseTime($start, $end)
    {
        // 按发布时间倒序排列
        $result = $this->redis->zRevRange('article:time', $start, $end, true);
        // $result = $redis->zRange('article:time', 0, -1, true); // 按发布时间正序排列
        return $this->joinArticle($result);
    }

    // 按发布时间和点赞数量综合后的顺序排列
    public function showArticlesOfLikeNumber($start, $end)
    {
        // 按发布时间和点赞数量综合后的倒序排列
        $result = $this->redis->zRevRange('article:score', $start, $end, true);
        // 按发布时间和点赞数量综合后的正序排列
        // $result = $redis->zRange('article:score', 0, -1, true); 
        return $this->joinArticle($result);
    }

    // 拼接文章内容
    public function joinArticle($articles)
    {
        $result = '';
        if (!empty($articles)) {
            foreach ($articles as $articleId => $value) {
                $articleHashKey = 'article:' . $articleId . ':content';
                $articleInfo = $this->redis->hGetAll($articleHashKey);
                $result .= 
                    '<div>'.
                        '<span>文章 id:' . $articleId . ' </span>' .
                        '<span>文章标题:'. $articleInfo['title'] . ' </span>' . 
                        '<span>文章内容:'. $articleInfo['content'] . ' </span>' .
                        '<span>文章作者:' . $articleInfo['author'] . ' </span>' .
                        '<span>文章标签:' . $articleInfo['tag'] . ' </span>' .
                    '</div>';
            }
        } else {
            $result = '还未发表文章';
        }
        return $result;
    }

    // 点赞
    public function giveLike($userId, $articleId)
    {
        // 首先判断文章是否存在以及发布时间是否超过一周,超过一周的不允许点赞
        $publishTime = $this->redis->zScore('article:time', $articleId);
        $isOverdue = (time() - $publishTime) < self::EXPIRATION_TIME;
        $isClick = $this->isGiveLike($userId, $articleId);
        if (!empty($publishTime) && $isOverdue && !$isClick) {
            $addUserResult = $this->redis->sAdd("article:{$articleId}", $userId);
            $addScoreResult = $this->redis->zIncrBy('article:score', self::ADD_SCORE, $articleId);
            return ($addUserResult && $addScoreResult) ? '点赞成功' : '点赞失败';
        }
        return '点赞失败';
    }

    // 判断用户是否给这片文章点过赞
    public function isGiveLike($userId, $articleId)
    {
        $isClick = $this->redis->sismember("article:{$articleId}", $userId);
        if ($isClick) {
            $result = true;
        } else {
            $result = false;
        }
        return $result;
    }

    // 获取主键id
    public function getId($key, $stepSize = 1)
    {
        return $this->redis->incrBy($key, $stepSize);
    }
}


$obj = new TestRedis();
var_dump($obj->publishArticle(999, 'Redis'));
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!