使用多种 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 协议》,转载必须注明作者和本文链接