实战系列都是需求+伪代码;
1 需求
如果一个文章获得200+票(up vote),则是一篇有趣的文章;假如每天发布1k文章,其中50篇是有趣的文章,则把这50篇放到文章列表的前100位,至少一天。
2 设计
为了产生一个能够随着时间流逝而不断减少的的评分,设计规则:文章票数 * 常量 + 文章发布时间 = 文章评分
假设常量 = 432 = 86400(一天的 秒数) / 200(有趣文章票数的阀值)
除了存储文章评分,还要存储文章的一些其他信息,设计使用hash存储如下
文章的投票文章使用2个有续集合有序的存储文章:
- 第一个有序集合的成员:文章ID,分值是文章的发布时间;
- 第二个有序集合的成员:文章ID,分值为文章的评分;
通过上面2个有序集合,网站既可以根据文章发布时间的先后顺序展示文章,又可以根据文章的评分高低展示文章;
为了防止用户对同一篇文章多次投票,需要记录一个已经投票的用户名单,为了尽量节省内存,规定:一篇文章发布期满一周后,用户不能对其投票;文章的评分将被固定,记录文章投票用户的集合也会被删除;
展示当115423号用户给100408号文章投票流程
-
100408文章得到一张支持票,它的评分增加了
-
115423号用户加入到“100408号文章投票用户”集合中
3 伪代码实现
已经设计好评分的方法,也设计了数据结构。现在理一理流程
当用户尝试投票时候,代码用zscore查询“文章发布时间的zset”,如果发布时间查过一周,可以投票(将用户加入“已投票用户set”),如果添加成功,说明用户第一次对改文章投票,代码使用zincryby对文章加432分。并用hincryby 对 散列结论的文章投票数量进行更新(hincrby命令用户对散列存储的值执行自增操作)
public static Integer ONE_WEEK_IN_SCORE= 7*86400; //一周有效期
public static Integer VOTE_SCORE =432 ;
public void articleVote(conn, user, article){
timeBtw = now.time() - ONE_WEEK_IN_SCORE; //获取时间差,看是否可以继续投票
// 判断是否还可以对文章进行投票
if(conn.zscore(‘time:’,article) < timeBtw){
return
}
articleId= article.partition(":")[-1] //从 article:id标识符中取出文章ID
//事务开始
conn.sadd('voted:'+articleId,user)
conn.zincryby('score:',article,VOTE_SCORE )
conn.hincryby(article,"votes",1)
//事务结束
}