《redis实战》之redis文章投票

前言 

最近几年,越来越多的网站开始提供部分对网页链接、文章或问题进行投票的功能,这些网站会根据文章的发布时间和文章获得的投票数量计算出一个评分,然后按照这个评分来决定如何排序和展示文章。

一、Springboot整合redis 

https://www.cnblogs.com/minmin123/p/13595734.html

二、对文章进行投票

要构建一个文章投票网站,我们首先要做的就是为了这个网站设置一些数值和限制条件:如果文章获得至少200张支持票,那么网站就认为这篇文章是一篇有趣的文章; 加入这个网站每天发布1000篇文章,而其中的50篇符合网站对有趣文章的要求,那么网站要做的就是把50篇文章放到文章列表前100位至少一天;

为了产生一个能够随着时间流逝而不断减少的评分,程序需要根据文章的发布时间和当前时间来计算文章的评分,具体计算方法为:将文章得到的支持票数量乘以一个常量,然后加上文章的发布时间,得出的结果就是文章的评分。

计算评分与支持票数量相乘的常量是432 (一天的秒数【86400】除以一天所需的支持票数量200)文章每获得一张支持票,程序就需要将文章的评分增加432分

1、准备数据

把文章标题,网址,发布文章的用户、文章的发布时间、文章的投票数量存入一个散列中

  

time:存时间戳

poster: 存用户的id

工具类代码:

  1 import org.springframework.data.redis.core.RedisTemplate;
  2 import org.springframework.data.redis.core.StringRedisTemplate;
  3 import org.springframework.stereotype.Component;
  4 import org.springframework.util.CollectionUtils;
  5 
  6 import javax.annotation.Resource;
  7 import java.util.List;
  8 import java.util.Map;
  9 import java.util.Set;
 10 import java.util.concurrent.TimeUnit;
 11 
 12 @Component
 13 public final class RedisUtil {
 14 
 15     @Resource
 16     private RedisTemplate<String, Object> redisTemplate;
 17 
 18 
 19     public Set<String> keys(String keys){
 20         try {
 21             return redisTemplate.keys(keys);
 22         }catch (Exception e){
 23             e.printStackTrace();
 24             return null;
 25         }
 26     }
 27 
 28     /**
 29      * 指定缓存失效时间
 30      * @param key 键
 31      * @param time 时间(秒)
 32      * @return boolean
 33      */
 34     public boolean expire(String key, long time) {
 35         try {
 36             if (time > 0) {
 37                 redisTemplate.expire(key, time, TimeUnit.SECONDS);
 38             }
 39             return true;
 40         } catch (Exception e) {
 41             e.printStackTrace();
 42             return false;
 43         }
 44     }
 45     /**
 46      * 根据key 获取过期时间
 47      * @param key 键 不能为null
 48      * @return 时间(秒) 返回0代表为永久有效
 49      */
 50     public long getExpire(String key) {
 51         return redisTemplate.getExpire(key, TimeUnit.SECONDS);
 52     }
 53     /**
 54      * 判断key是否存在
 55      * @param key 键
 56      * @return true 存在 false不存在
 57      */
 58     public boolean hasKey(String key) {
 59         try {
 60             return redisTemplate.hasKey(key);
 61         } catch (Exception e) {
 62             e.printStackTrace();
 63             return false;
 64         }
 65     }
 66     /**
 67      * 删除缓存
 68      * @param key 可以传一个值 或多个
 69      */
 70     @SuppressWarnings("unchecked")
 71     public void del(String... key) {
 72         if (key != null && key.length > 0) {
 73             if (key.length == 1) {
 74                 redisTemplate.delete(key[0]);
 75             } else {
 76                 redisTemplate.delete(CollectionUtils.arrayToList(key));
 77             }
 78         }
 79     }
 80     /**
 81      * 普通缓存获取
 82      * @param key 键
 83      * @return 84      */
 85     public Object get(String key) {
 86         return key == null ? null : redisTemplate.opsForValue().get(key);
 87     }
 88     /**
 89      * 普通缓存放入
 90      * @param key 键
 91      * @param value 值
 92      * @return true成功 false失败
 93      */
 94     public boolean set(String key, Object value) {
 95         try {
 96             redisTemplate.opsForValue().set(key, value);
 97             return true;
 98         } catch (Exception e) {
 99             e.printStackTrace();
100             return false;
101         }
102     }
103     /**
104      * 普通缓存放入, 不存在放入,存在返回
105      * @param key 键
106      * @param value 值
107      * @return true成功 false失败
108      */
109     public boolean setnx(String key, Object value) {
110         try {
111             redisTemplate.opsForValue().setIfAbsent(key,value);
112             return true;
113         } catch (Exception e) {
114             e.printStackTrace();
115             return false;
116         }
117     }
118     /**
119      * 普通缓存放入并设置时间
120      * @param key 键
121      * @param value 值
122      * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
123      * @return true成功 false 失败
124      */
125     public boolean set(String key, Object value, long time) {
126         try {
127             if (time > 0) {
128                 redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
129             } else {
130                 set(key, value);
131             }
132             return true;
133         } catch (Exception e) {
134             e.printStackTrace();
135             return false;
136         }
137     }
138 
139     /**
140      * 普通缓存放入并设置时间,不存在放入,存在返回
141      * @param key 键
142      * @param value 值
143      * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
144      * @return true成功 false 失败
145      */
146     public boolean setnx(String key, Object value, long time) {
147         try {
148             if (time > 0) {
149                 redisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS);
150             } else {
151                 set(key, value);
152             }
153             return true;
154         } catch (Exception e) {
155             e.printStackTrace();
156             return false;
157         }
158     }
159 
160     /**
161      * 递增
162      * @param key 键
163      * @param delta 要增加几(大于0)
164      * @return long
165      */
166     public long incr(String key, long delta) {
167         if (delta < 0) {
168             throw new RuntimeException("递增因子必须大于0");
169         }
170         return redisTemplate.opsForValue().increment(key, delta);
171     }
172     /**
173      * 递增
174      * @param key 键
175      * @param delta 要增加几(大于0)
176      * @return double
177      */
178     public Double zincrby(String key,Object value, long delta) {
179         if (delta < 0) {
180             throw new RuntimeException("递增因子必须大于0");
181         }
182         return redisTemplate.opsForZSet().incrementScore(key,value,delta);
183     }
184     /**
185      * 递减
186      * @param key 键
187      * @param delta 要减少几(小于0)
188      * @return long
189      */
190     public long decr(String key, long delta) {
191         if (delta < 0) {
192             throw new RuntimeException("递减因子必须大于0");
193         }
194         return redisTemplate.opsForValue().increment(key, -delta);
195     }
196     /**
197      * HashGet
198      * @param key 键 不能为null
199      * @param item 项 不能为null
200      * @return201      */
202     public Object hget(String key, String item) {
203         return redisTemplate.opsForHash().get(key, item);
204     }
205     /**
206      * HashGet
207      * @param key 键 不能为null
208      * @param item 项 不能为null
209      * @return210      */
211     public Object zget(String key, String item) {
212         return redisTemplate.opsForZSet().score(key,item);
213     }
214     /**
215      * HashGet
216      * @param key 键 不能为null
217      * @param  min max
218      * @return219      */
220     public Set<Object> zrangeByScorce(String key, double min, double max) {
221         return redisTemplate.opsForZSet().rangeByScore(key,min,max);
222     }
223     /**
224      * 获取hashKey对应的所有键值
225      * @param key 键
226      * @return 对应的多个键值
227      */
228     public Map<Object, Object> hmget(String key) {
229         return redisTemplate.opsForHash().entries(key);
230     }
231     /**
232      * HashSet
233      * @param key 键
234      * @param map 对应多个键值
235      * @return true 成功 false 失败
236      */
237     public boolean hmset(String key, Map<String, Object> map) {
238         try {
239             redisTemplate.opsForHash().putAll(key, map);
240             return true;
241         } catch (Exception e) {
242             e.printStackTrace();
243             return false;
244         }
245     }
246     /**
247      * HashSet 并设置时间
248      * @param key 键
249      * @param map 对应多个键值
250      * @param time 时间(秒)
251      * @return true成功 false失败
252      */
253     public boolean hmset(String key, Map<String, Object> map, long time) {
254         try {
255             redisTemplate.opsForHash().putAll(key, map);
256             if (time > 0) {
257                 expire(key, time);
258             }
259             return true;
260         } catch (Exception e) {
261             e.printStackTrace();
262             return false;
263         }
264     }
265     /**
266      * 向一张hash表中放入数据,如果不存在将创建
267      * @param key 键
268      * @param item 项
269      * @param value 值
270      * @return true 成功 false失败
271      */
272     public boolean hset(String key, String item, Object value) {
273         try {
274             redisTemplate.opsForHash().put(key, item, value);
275             return true;
276         } catch (Exception e) {
277             e.printStackTrace();
278             return false;
279         }
280     }
281 
282     /**
283      * 向一张hash表中放入数据,如果不存在将创建
284      * @param key 键
285      * @param item 项
286      * @param value 值
287      * @return true 成功 false失败
288      */
289     public boolean zset(String key, String item, Double value) {
290         try {
291             redisTemplate.opsForZSet().add(key,item,value);
292             return true;
293         } catch (Exception e) {
294             e.printStackTrace();
295             return false;
296         }
297     }
298     /**
299      * 向一张hash表中放入数据,如果不存在将创建
300      * @param key 键
301      * @param item 项
302      * @param value 值
303      * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
304      * @return true 成功 false失败
305      */
306     public boolean hset(String key, String item, Object value, long time) {
307         try {
308             redisTemplate.opsForHash().put(key, item, value);
309             if (time > 0) {
310                 expire(key, time);
311             }
312             return true;
313         } catch (Exception e) {
314             e.printStackTrace();
315             return false;
316         }
317     }
318     /**
319      * 删除hash表中的值
320      * @param key 键 不能为null
321      * @param item 项 可以使多个 不能为null
322      */
323     public void hdel(String key, Object... item) {
324         redisTemplate.opsForHash().delete(key, item);
325     }
326     /**
327      * 判断hash表中是否有该项的值
328      * @param key 键 不能为null
329      * @param item 项 不能为null
330      * @return true 存在 false不存在
331      */
332     public boolean hHasKey(String key, String item) {
333         return redisTemplate.opsForHash().hasKey(key, item);
334     }
335     /**
336      * 判断hash表中是否有该项的值
337      * @param key 键 不能为null
338      * @param item 项 不能为null
339      * @return true 存在 false不存在
340      */
341     public boolean sIsMember(String key, String item) {
342         return redisTemplate.opsForSet().isMember(key,item);
343     }
344     /**
345      * hash递增 如果不存在,就会创建一个 并把新增后的值返回
346      * @param key 键
347      * @param item 项
348      * @param by 要增加几(大于0)
349      * @return double
350      */
351     public double hincr(String key, String item, double by) {
352         return redisTemplate.opsForHash().increment(key, item, by);
353     }
354     /**
355      * hash递减
356      * @param key 键
357      * @param item 项
358      * @param by 要减少记(小于0)
359      * @return double
360      */
361     public double hdecr(String key, String item, double by) {
362         return redisTemplate.opsForHash().increment(key, item, -by);
363     }
364     /**
365      * 根据key获取Set中的所有值
366      * @param key 键
367      * @return Set<Object>
368      */
369     public Set<Object> sGet(String key) {
370         try {
371             return redisTemplate.opsForSet().members(key);
372         } catch (Exception e) {
373             e.printStackTrace();
374             return null;
375         }
376     }
377     /**
378      * 根据value从一个set中查询,是否存在
379      * @param key 键
380      * @param value 值
381      * @return true 存在 false不存在
382      */
383     public boolean sHasKey(String key, Object value) {
384         try {
385             return redisTemplate.opsForSet().isMember(key, value);
386         } catch (Exception e) {
387             e.printStackTrace();
388             return false;
389         }
390     }
391     /**
392      * 将数据放入set缓存
393      * @param key 键
394      * @param values 值 可以是多个
395      * @return 成功个数
396      */
397     public long sSet(String key, Object... values) {
398         try {
399             return redisTemplate.opsForSet().add(key, values);
400         } catch (Exception e) {
401             e.printStackTrace();
402             return 0;
403         }
404     }
405     /**
406      * 将set数据放入缓存
407      * @param key 键
408      * @param time 时间(秒)
409      * @param values 值 可以是多个
410      * @return 成功个数
411      */
412     public long sSetAndTime(String key, long time, Object... values) {
413         try {
414             Long count = redisTemplate.opsForSet().add(key, values);
415             if (time > 0)
416                 expire(key, time);
417             return count;
418         } catch (Exception e) {
419             e.printStackTrace();
420             return 0;
421         }
422     }
423     /**
424      * 获取set缓存的长度
425      * @param key 键
426      * @return long
427      */
428     public long sGetSetSize(String key) {
429         try {
430             return redisTemplate.opsForSet().size(key);
431         } catch (Exception e) {
432             e.printStackTrace();
433             return 0;
434         }
435     }
436     /**
437      * 移除值为value的
438      * @param key 键
439      * @param values 值 可以是多个
440      * @return 移除的个数
441      */
442     public long setRemove(String key, Object... values) {
443         try {
444             Long count = redisTemplate.opsForSet().remove(key, values);
445             return count;
446         } catch (Exception e) {
447             e.printStackTrace();
448             return 0;
449         }
450     }
451     // ===============================list=================================
452     /**
453      * 获取list缓存的内容
454      * @param key 键
455      * @param start 开始
456      * @param end 结束 0 到 -1代表所有值
457      * @return List<Object>
458      */
459     public List<Object> lGet(String key, long start, long end) {
460         try {
461             return redisTemplate.opsForList().range(key, start, end);
462         } catch (Exception e) {
463             e.printStackTrace();
464             return null;
465         }
466     }
467     /**
468      * 获取list缓存的长度
469      * @param key 键
470      * @return long
471      */
472     public long lGetListSize(String key) {
473         try {
474             return redisTemplate.opsForList().size(key);
475         } catch (Exception e) {
476             e.printStackTrace();
477             return 0;
478         }
479     }
480     /**
481      * 通过索引 获取list中的值
482      * @param key 键
483      * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
484      * @return Object
485      */
486     public Object lGetIndex(String key, long index) {
487         try {
488             return redisTemplate.opsForList().index(key, index);
489         } catch (Exception e) {
490             e.printStackTrace();
491             return null;
492         }
493     }
494     /**
495      * 将list放入缓存
496      * @param key 键
497      * @param value 值
498      * @return boolean
499      */
500     public boolean lSet(String key, Object value) {
501         try {
502             redisTemplate.opsForList().rightPush(key, value);
503             return true;
504         } catch (Exception e) {
505             e.printStackTrace();
506             return false;
507         }
508     }
509     /**
510      * 将list放入缓存
511      * @param key 键
512      * @param value 值
513      * @param time 时间(秒)
514      * @return boolean
515      */
516     public boolean lSet(String key, Object value, long time) {
517         try {
518             redisTemplate.opsForList().rightPush(key, value);
519             if (time > 0)
520                 expire(key, time);
521             return true;
522         } catch (Exception e) {
523             e.printStackTrace();
524             return false;
525         }
526     }
527     /**
528      * 将list放入缓存
529      * @param key 键
530      * @param value 值
531      * @return boolean
532      */
533     public boolean lSet(String key, List<Object> value) {
534         try {
535             redisTemplate.opsForList().rightPushAll(key, value);
536             return true;
537         } catch (Exception e) {
538             e.printStackTrace();
539             return false;
540         }
541     }
542     /**
543      * 将list放入缓存
544      *
545      * @param key 键
546      * @param value 值
547      * @param time 时间(秒)
548      * @return boolean
549      */
550     public boolean lSet(String key, List<Object> value, long time) {
551         try {
552             redisTemplate.opsForList().rightPushAll(key, value);
553             if (time > 0)
554                 expire(key, time);
555             return true;
556         } catch (Exception e) {
557             e.printStackTrace();
558             return false;
559         }
560     }
561     /**
562      * 根据索引修改list中的某条数据
563      * @param key 键
564      * @param index 索引
565      * @param value 值
566      * @return boolean
567      */
568     public boolean lUpdateIndex(String key, long index, Object value) {
569         try {
570             redisTemplate.opsForList().set(key, index, value);
571             return true;
572         } catch (Exception e) {
573             e.printStackTrace();
574             return false;
575         }
576     }
577     /**
578      * 移除N个值为value
579      * @param key 键
580      * @param count 移除多少个
581      * @param value 值
582      * @return 移除的个数
583      */
584     public long lRemove(String key, long count, Object value) {
585         try {
586             Long remove = redisTemplate.opsForList().remove(key, count, value);
587             return remove;
588         } catch (Exception e) {
589             e.printStackTrace();
590             return 0;
591         }
592     }
593     /**
594      * 移除N个值为value
595      * @param key 键
596      * @param count 移除多少个
597      * @param value 值
598      * @return 移除的个数
599      */
600     public long zscore(String key, long count, Object value) {
601         try {
602             Long remove = redisTemplate.opsForList().remove(key, count, value);
603             return remove;
604         } catch (Exception e) {
605             e.printStackTrace();
606             return 0;
607         }
608     }
609 }

 

参考代码:

@Autowired
private RedisUtil redisUtil;

@Test
public void test1(){ List<ActiveBO> list = new ArrayList<>(); ActiveBO activeBO = new ActiveBO(); activeBO.setId(1L); activeBO.setTitle("肖申克的救赎"); activeBO.setLink("https://www.cnblogs.com/minmin123/p/13595734.html"); activeBO.setPoster("user01"); activeBO.setVotes(0); ActiveBO activeBO1 = new ActiveBO(); activeBO1.setId(1L); activeBO1.setTitle("如何提升工作效率"); activeBO1.setLink("https://www.baidu.com"); activeBO1.setPoster("user02"); activeBO1.setVotes(0); list.add(activeBO); list.add(activeBO1); Integer i = 0; for (ActiveBO activeBO0 :list) {    redisUtil.hset("active:"+i, "title", activeBO0.getTitle()); redisUtil.hset("active:"+i, "link", activeBO0.getLink()); redisUtil.hset("active:"+i, "poster", activeBO0.getPoster()); redisUtil.hset("active:"+i, "time",System.currentTimeMillis()); redisUtil.hset("active:"+i, "votes", activeBO0.getVotes()); i++; } }

 

我们的文章投票网站使用两个有序集合来有序地存储文章;

 

 

 

发布时间:用时间戳 

注意:这个用的redis的有序集合zset

参考代码:

 1   @Test
 2     public void test3(){
 3         redisUtil.zset("time","active:0",1598945519501D);
 4         redisUtil.zset("time","active:1",1598945519511D);
 5     }
 6     @Test
 7     public void test4(){
 8         redisUtil.zset("score","active:0",0D);
 9         redisUtil.zset("score","active:1",0D);
10     }

 

为了防止用户对同一篇文章进行多次投票,网站需要为每篇文章记录一个已投票用户名单。创建一个集合来存储。

 

 参考代码:

1     public void test5(){
2         redisUtil.sSet("voted:0","user03");
3         redisUtil.sSet("voted:0","user04");
4     }

为了节约内存,我们规定当一篇文章发布期满一周后,用户将不能再对他进行投票,文章的评分将被固定下来,而记录文章已经投票用户名单的集合也会被删除。

 

实现对文章投票

1、voted 中增加点赞用户Id

2、评分 增加432分

3、存储文章的投票数量+1

参考代码:

 1     private static final Long WEEK_SENDS = 7 * 86400L;
 2     private static final Long VOTE_SCORE = 432L;
 3     @Autowired
 4     RedisUtil redisUtil;
 5 
 6     /**
 7      * 对文章进行投票
 8      * @param activeId 文章id
 9      * @param userId 用户id
10      * @return boolean 是否成功
11      */
12     public boolean articleVote(String activeId,String userId) {
13 
14 
15         //1、查看文章是否发表是否已经超过一周
16         Object time = redisUtil.zget("time", "active"+":" + activeId);
17         java.text.NumberFormat nf = java.text.NumberFormat.getInstance();
18         nf.setGroupingUsed(false);
19         String format = nf.format(time);
20         //2.Long.ValueOf(“String”)与Long.parseLong(“String”)的区别
21          //Long.ValueOf(“String”)返回Long包装类型
22          //Long.parseLong(“String”)返回long基本数据类型
23 
24         if (System.currentTimeMillis() - Long.parseLong(format) < WEEK_SENDS) {
25             return false;
26         }
27 
28         // 2、用户点击支持,判断该用户时候已经点击过  //3、把用户放在voted中,
29         String voted ="voted"+ ":" + activeId;
30         if (redisUtil.sSet(voted,userId) == 1) {
31             //用户以前没有点赞过
32 
33             //4、文章的score分值增加432(常量)
34             redisUtil.zincrby("score", "active"+":" + activeId, VOTE_SCORE);
35 
36             //5、用户active:1中 votes 的数值+1
37             redisUtil.hincr( "active"+":" + activeId, "votes", 1);
38 
39         }
40         return true;
41     }

 

 测试代码:

 activeService.articleVote("2020090211184300005","user:000007");

运行效果如下:

 

 

 

 

 

posted @ 2020-09-02 17:07  keepsummer  阅读(273)  评论(0编辑  收藏  举报