1.实现投票功能,2.创建文章数据,3.对文章进行排序。
实现投票功能
实现投票功能,要注重文章的时效性与投票的公平性,所以需要给投票功能加上一些约束条件:
- 文章发布满一个星期后,不再允许用户对该文章投票
- 一个用户对一篇文章只能投一次票
所以我们需要使用:
- 一个有序集合 time,存储文章的发布时间
- 一个集合 voted:*,存储已投票用户名单- 其中 *是被投票文章的ID
 
- 其中 
- 一个有序集合 score,存储文章的得票数
- ONE_WEEK_IN_SECONDS = 7 * 24 * 60 * 60
- def article_vote(r, user_id, article_id):
- # 使用 time.time() 获取当前时间
- # 减去一周的秒数,从而获取一周前的Unix时间
- cutoff = time.time() - ONE_WEEK_IN_SECONDS
- if r.zscore('time', article_id) < cutoff:
- return
- if r.sadd('voted:' + article_id, user_id):
- r.zincrby('score', article_id, 1)
当用户尝试投票时,使用 ZSCORE 命令读取 time 有序集合,得到这篇文章的发布时间,再判断文章的发布时间是否超过一周。ZSCORE 命令的语法如下:
r.zscore(key, member)
- key:是有序集合的键名
- member:是有序集合中的某个成员
若未超过,则使用 SADD 命令尝试将用户追加到这篇文章的已投票用户名单中,如果添加成功,则说明该用户未投过票。SADD 命令的语法是:
r.sadd(key, member)
- key:是集合的键名
- member:是要添加进集合的元素
由于集合中的元素是唯一的,所以sadd函数会根据member是否存在在集合中做出不同返回:
- 若该元素不存在在集合中,返回 True
- 若该元素已存在在集合中,返回 False
所以返回为 True 时使用 ZINCRBY 命令来为文章的投票数加 1。zincrby 函数语法如下:
r.zincrby(key, member, increment)
- key:是有序集合的键名
- member:是有序集合中要增加分值的成员
- increment:是要增加的分值
创建文章数据
现在系统中还缺少文章数据,所以我们要提供一个创建文章的函数,并把文章数据存储到 Redis 中。创建文章的步骤如下:
- 创建新的文章 ID
- 将文章作者加入到这篇文章的已投票用户名单中
- 存储文章详细信息到 Redis中
- 将文章的发布时间和初始投票数加入到 time和score两个有序集合中
- def post_article(r, user, title, link):
- # 创建新的文章ID,使用一个整数计数器对 article 键执行自增
- # 如果该键不存在,article 的值会先被初始化为 0
- # 然后再执行自增命令
- article_id = str(r.incr('article'))
- voted = 'voted:' + article_id
- r.sadd(voted, user)
- r.expire(voted, ONE_WEEK_IN_SECONDS)
- now = time.time()
- article = 'article:' + article_id
- r.hmset(article, {
- 'title': title,
- 'link': link,
- 'poster': user,
- })
- r.zadd('score', article_id, 1)
- r.zadd('time', article_id, now)
- return article_id
将文章作者加入已投票用户名单中和之前一样,这里不再赘述,但在这里我们需要为这个已投票用户名单设置一个过期时间,让它在一周后(到期后)自动删除,减少 Redis 的内存消耗。为键设置过期时间的命令是:
r.expire(key, seconds)
- key:要设置过期时间的键名
- seconds:过期时间的长度(单位:秒)
这里我们要设置的时间是一周,所以我们可以使用上面定义好的全局变量 ONE_WEEK_IN_SECONDS。
接下来要存储文章详细信息了,前面介绍过 hset 可以执行单个字段(域)的设置,这里我们使用 hmset 一次性设置多个字段(域),其语法如下:
r.hmset(key, {field: value, [field: value ...]})
我们可以使用 Python 的散列来一次性存储多个字段(域)到 Redis,只需要将整个散列当作 key 对应的值通过 hmset 函数设置进去就行。
最后,将初始投票数和创建时间设置到 score 和 time 中都可以通过 ZADD 命令来实现:
r.zadd(key, member, score)
- key:有序集合的键名
- member:要加入有序集合的成员
- score:该成员的分值
这里需要注意的是,因为该篇文章的作者已经被加入到该文章已投票用户名单中,为了保持数据一致性,我们需要将文章的初始投票数设为 1。
对文章进行排序
实现了文章投票和创建文章功能,接下来我们就需要将评分最高的文章和最新发布的文章从 Redis 中取出了。
-  首先我们要根据排序方式的不同: - 按评分排序,则从 score有序集合中取出一定量的文章ID(score有序集合存放文章ID和对应的投票数)
- 按时间排序,则从 time有序集合中取出一定量的文章ID(time有序集合存放文章ID和对应的发布时间)
 
- 按评分排序,则从 
-  构成一个有序文章信息列表,每个元素都: - 使用 HGETALL命令,取出每篇文章的全部信息
 
- 使用 
- def get_articles(r, start, end, order='score'):
- ids = r.zrevrange(order, start, end)
- articles = []
- for id in ids:
- article_data = r.hgetall(id)
- article_data['id'] = id
- articles.append(article_data)
- return articles
这里因为需要对有序集合进行排序,所以我们在取出文章 ID 时需要使用到 ZREVRANGE 命令,以分值从大到小的排序方式取出文章 ID。ZREVRANGE 命令的语法是:
r.zrevrange(key, start, stop)
- key:有序集合的键名
- start:开始的数组下标
- stop:结束的数组下标
得到多个文章 ID 后,我们还需要根据每一个文章 ID 获取文章的全部信息,这时就需要使用到 HGETALL 命令,它的语法如下:
r.hgetall(key)
- key:哈希的键名
我们取出文章的全部信息后,还为文章信息添加了一个字段 id。这是因为文章 ID 在 Redis 中是作为键名存储的,不在值当中,所以我们需要附加这个字段到文章信息中。
实现这些方法后,我们大体实现了一个文章投票的后端处理逻辑,能够为文章投票并能根据投票结果改变文章的排序情况。
编程要求
根据提示,在右侧Begin-End区域补充代码,完成简化版文章投票网站的后端处理逻辑:
-  在 article_vote()函数中:- 该方法作用是:对文章投票
- 参数说明: - r:Redis 客户端
 
 
-  user_id:投票用户
-  article_id:被投票文章- 已提供一周前 Unix时间戳,存放在变量cutoff
- 当满足以下条件时,为文章投一票: - 该文章发布不超过一周
 
 
- 已提供一周前 
-  该用户没有为该文章投过票 
-  在 post_article()函数中:- 该方法作用是:创建文章
- 参数说明: - r:Redis 客户端
 
 
-  user:发布用户
-  title:文章标题
-  link:文章链接- 已提供: - article_id,新文章- ID
 
 
- 已提供: 
-  voted,新文章已投票用户名单存储键名
-  article,新文章详细信息存储键名
-  now,文章创建时间- 按照 ID递增的顺序依次创建文章
- 保证发布文章的用户不能给自己的文章投票
- 文章在发布一周后删除已投票用户名单
- 存储文章详细信息到 Redis中,包括字段:- 文章标题
 
 
- 按照 
-  文章链接 
-  发布用户 - 存储文章的发布时间和初始投票数 - 初始投票数为 1
 
- 初始投票数为 
 
- 存储文章的发布时间和初始投票数 
-  在 get_articles()函数中:- 该方法作用是:对文章进行排序
- 参数说明: - r:Redis 客户端
- start:从排序为- start的文章开始获取
 
 
-  end:到排序为end的文章结束获取
-  order:排序方式,分为两种:- time:按时间排序
- score:按投票数排序
 
-  已提供文章信息空列表, articles
-  实现按时间/投票数排序 
-  将排序后的文章及其全部信息组成一个列表: - 按照不同排序规则取出排序在参数提供的区间范围内的文章
- 及每篇文章的全部信息,包括文章 ID#!/usr/bin/env python #-*- coding:utf-8 -*- import time ONE_WEEK_IN_SECONDS = 7 * 24 * 60 * 60 def article_vote(r, user_id, article_id):cutoff = time.time() - ONE_WEEK_IN_SECONDS# 请在下面完成要求的功能#********* Begin *********#if r.zscore('time', article_id) < cutoff:returnif r.sadd('voted:' + article_id, user_id):r.zincrby('score', article_id, 1)#********* End *********# def post_article(r, user, title, link):article_id = str(r.incr('article'))voted = 'voted:' + article_idnow = time.time()article = 'article:' + article_id# 请在下面完成要求的功能#********* Begin *********#r.sadd(voted, user)r.expire(voted, ONE_WEEK_IN_SECONDS)r.hmset(article, {'title': title,'link': link,'poster': user,})r.zadd('score', article_id, 1)r.zadd('time', article_id, now)#********* End *********#return article_id def get_articles(r, start, end, order='score'):articles = []# 请在下面完成要求的功能#********* Begin *********#ids = r.zrevrange(order, start, end)for id in ids:article_data = r.hgetall(id)article_data['id'] = idarticles.append(article_data)#********* End *********#return articles