需求如下:
1、用户可以发布文章或者帖子,每个用户发布的信息,从发布日期开始算,只能在一周内被点赞,且不能被同一个用户重复点赞。
2、文章或者帖子信息可以按照积分或者时间来进行排序,且能够分页显示。
3、每个用户发布的文章应该归属到一个组里面,例如张三发布的属于科技篇和互联网篇,李四发布的属于互联网篇和人工智能篇,王五发布的属于互联网篇
4、归组以后可以查询出任何组与组之间交集的文章或者帖子信息。
需求分析:
1、首先我们需要知道文章信息应该用redis哪种方式去存储,纵观其五种存储方式,选择了hash,为什么呢,因为hash方便扩展和维护,后期需要修改里面某个字段内容非常方便或者新增字段也非常方便。
2、控制一周内部被重复点赞,则直接设置文章的点赞过期时间即可,每次点赞前先判断文章是否有效
3、控制重复点赞用set方式存储,技能统计点赞用户集合,又能控制重复
4、既然涉及到排序,那么毫无疑问用sortSet方式存储
5、这里注意到无论是交集、并集、差集都只能用set集合去存。
代码如下:
package redis.clients.test;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.util.DateTimeUtil;
public class D01_Test {
private static final int ONE_WEEK_IN_SECONDS = 7 * 86400;//每周多少秒 控制点赞过期时间
private static final int VOTE_SCORE = 10;//点赞积分
private static final int ARTICLES_PER_PAGE = 25;//每页多少篇文章
static Jedis jedis=null;
static{
jedis=new Jedis("127.0.0.1");
}
/**
* 用户发布文章信息
* @param wzzt 文章主题
* @param wznr文章内容
* @param wzzb 文章组别 多个用逗号","隔开
* @param username 用户名
* @return
*/
public String postArticle(String wzzt,String wznr,String wzzb,String username){
try {
/**
* 用hash方式存放文章信息(hash方式方便扩展子弹和修改数据)
*/
String id=DateTimeUtil.getDateSecondFormatNoSplit(new Date());
Map articleMap=new HashMap();
articleMap.put("wzzt", wzzt);
articleMap.put("wzid", id);
articleMap.put("wznr", wznr);
articleMap.put("username", username);
articleMap.put("fbsj", DateTimeUtil.getDateSecondFormat(new Date()));//文章发布时间
articleMap.put("votes", "1");//初始化文章的点赞信息
jedis.hmset("article:"+id, articleMap);
/**
* 控制文章点赞的过期时间为一周(set集合可以去重,设置过期时间)
*/
jedis.sadd("voted:"+id, username);//默认存在自己点赞信息
jedis.expire("voted:"+id, ONE_WEEK_IN_SECONDS);
/**
* 存放文章对应积分和时间 --初始化积分为100 (排序必须用sortSet集合方式)
*/
jedis.zadd("article_score:", 100, "article:"+id);
jedis.zadd("article_time:", System.currentTimeMillis() / 1000, "article:"+id);
/**
* 分组存放文章信息 (只有set集合才可以去交集、并集、差集)
*/
String[] wzzbs=wzzb.split(",");
for (int i = 0; i < wzzbs.length; i++) {
String str=wzzbs[i];//遍历出每个组别的名称
jedis.sadd("article_group:"+str, "article:"+id);//此处只能用set集合存放组别,因为需要取交集
}
System.out.println(username+"============文章发布成功===========");
return "1";
} catch (Exception e) {
System.out.println("文章发布异常!");
return "-1";
}
}
/**
* 点赞操作
* @param wzid 文章ID
* @param username 点赞用户
* @return
*/
public int votedArticle(String wzid,String username){
try {
//文章只有在一周内才可以被点赞,在发布文章的时候此处的key过期时间为1周
if(jedis.exists("voted:"+wzid)){
//判断是否用户重复点赞
long num=jedis.sadd("voted:"+wzid, username);//因为当用户点赞后,此处再次新增时结果为0
if(num==1){
System.out.println(username+"点赞成功");
jedis.zincrby("article_score:", VOTE_SCORE, "article:"+wzid);//加积分
jedis.hincrBy("article:"+wzid, "votes", 1L);//修改hash里面的点赞数量
return 1;
}else{
System.out.println(username+"不允许重复点赞");
return -1;
}
}else{
System.out.println("文章已超过点赞时间,不允许点赞");
return -1;
}
} catch (Exception e) {
System.out.println("文章点赞异常!");
return -1;
}
}
/**
* 分页倒序查询文章信息
* @param type 1-按照积分 2-按照时间
* @param pageNum 当前页数
* @return
*/
public List> getArticles(String type,int pageNum){
List> lists=new ArrayList>();
try {
int start = (pageNum - 1) * ARTICLES_PER_PAGE;//0
int end = start + ARTICLES_PER_PAGE - 1;//24
if("1".equals(type)){
Set sets= jedis.zrevrange("article_score:", start, end);
for (String id : sets) {
Map data=jedis.hgetAll(id);
lists.add(data);
}
}else{
Set sets= jedis.zrevrange("article_time:", start, end);
for (String id : sets) {
Map data=jedis.hgetAll(id);
lists.add(data);
}
}
} catch (Exception e) {
System.out.println("查询文章信息失败");
}
return lists;
}
/**
* 分组获取对应文章信息
* 此处分组交集查询是不能按照规则去排序的,
* 如果需要排序,可以在hash里面加时间和积分或者点赞数量字段后,直接将此处的List集合按照对应条件进行排序
* @param groups 组别 按照逗号分隔 ","
* @return
*/
public List> getArticlesByGroup(String groups){
List> lists=new ArrayList>();
try {
String[] strs=groups.split(",");
Set sets= jedis.sinter(strs);
for (String id : sets) {
Map data=jedis.hgetAll(id);
lists.add(data);
}
} catch (Exception e) {
System.out.println("分组查询文章信息失败");
}
return lists;
}
@Test
public void testD01_Test(){
D01_Test test=new D01_Test();
/**
* 第一步:张三 、李四 、王五各发布一篇文章,
* 其中张三发布文章为科技(technology)篇和互联网(internet)篇
* 李四发布文章互联网(internet)篇和人工智能(artificial)篇
* 王五发布的属于互联网(internet)篇
//jedis.flushDB();
test.postArticle("张三测试", "张三发布的问内容为科技篇和互联网篇", "technology,internet", "zhangsan");
// test.postArticle("李四测试", "李四发布文章互联网和人工智能", "internet,artificial", "lisi");
// test.postArticle("王五测试", "王五发布的为互联网篇", "internet", "wangwu");
*/
/**
* 第二步:
* 李四和王五都点赞张三 张三的文章ID=20170725143410
* 王五点赞李四李四的文章ID=20170725143502
* 王五再次点赞张三王五的文章ID=20170725143532
*/
// test.votedArticle("20170725143410","lisi");
//test.votedArticle("20170725143410","wangwu");
// test.votedArticle("20170725143502","wangwu");
// test.votedArticle("20170725143410","wangwu");
/**
* 第三步:此时查询出所有文章内容
List> resultLists=test.getArticles("1", 1);
System.out.println("==========按照积分倒叙排序start==========");
System.out.println(resultLists);
System.out.println("==========按照积分倒叙排序end==========");
List> resultLists2=test.getArticles("2", 1);
System.out.println("==========按照时间倒叙排序start==========");
System.out.println(resultLists2);
System.out.println("==========按照时间倒叙排序end==========");*/
/**
*第四步:
* 查询和互联网相关的文章
* 查询出互联网 、科技相关的文章
*/
/* List> resultLists3=test.getArticlesByGroup("article_group:internet");
System.out.println("==========按照组别交集查询为互联网的文章start==========");
System.out.println(resultLists3);
System.out.println("==========按照组别交集查询为互联网的文章end==========");*/
List> resultLists4=test.getArticlesByGroup("article_group:internet,article_group:technology");
System.out.println("==========按照组别交集查询为互联网且为科技的文start==========");
System.out.println(resultLists4);
System.out.println("==========按照组别交集查询为互联网且为科技的文end==========");
}
}
总结分析:实际上很多人会疑问,一篇文章或者帖子起始数据量很大,redis能否承受,当然,文章的整篇内容是不适合存放在redis中的,毕竟数据量比较大,所以这里适合去存放每篇文章或者帖子的简单主题介绍,这样无论是在PC端还是APP端都可以很快查询出对应的列表信息,当点击某一篇文章时,再去mysql或者oracle数据库查询整篇文章详情。