- 在点赞,评论,收藏,加精等地方都要重新计算帖子分数。
- 使用redis中的set集合数据结构,在以上操作处理中, 往set集合中存入帖子id
- 设置一个定时任务,每隔半小时去重新计算帖子的分数
- 具体为: 根据set中的id从数据库中查询出DiscussPost, 利用commentCount, likeCount等字段计算score,
- 配置JobDetail和Trigger
- 计算完了以后需要将帖子同步到数据库和ElasticSerach服务器中去
- 再提供一个根据热度排行的服务处理方法
规划key
private static final String PREFIX_POST = "post";
//帖子分数
public static String getPostScoreKey(){
return PREFIX_POST + SPLIT + "score";
}
再评论,点赞,加精等地方, 存入discusspostId至Redis的set集合中
//加精
@RequestMapping(path = "/wonderful", method = RequestMethod.POST)
@ResponseBody
public String setWonderful(int id){
discussPostService.updateStatus(id, 1);
//触发发帖事件,把数据重新同步到es服务器中去
Event event = new Event()
.setTopic(TOPIC_PUBLISH)
.setUserId(hostHolder.getUser().getId())
.setEntityType(ENTITY_TYPE_POST)
.setEntityId(id);
eventProducer.fireEvent(event);
//计算帖子分数
String redisKey = RedisKeyUtil.getPostScoreKey();
redisTemplate.opsForSet().add(redisKey, id);
return CommunityUtil.getJSONString(0);
}
设置定时任务
package com.newcoder.community.quartz;
import com.newcoder.community.entity.DiscussPost;
import com.newcoder.community.service.DiscussPostService;
import com.newcoder.community.service.ElasticsearchService;
import com.newcoder.community.service.LikeService;
import com.newcoder.community.util.CommunityConstant;
import com.newcoder.community.util.RedisKeyUtil;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.RedisTemplate;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class PostScoreRefreshJob implements Job, CommunityConstant {
private static final Logger logger = LoggerFactory.getLogger(PostScoreRefreshJob.class);
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private DiscussPostService discussPostService;
@Autowired
private LikeService likeService;
@Autowired
private ElasticsearchService elasticsearchService;
//牛客记元
private static final Date epoch;
static {
try {
epoch = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2014-08-01 00:00:00");
} catch (ParseException e) {
throw new RuntimeException("初始化牛客纪元失败", e);
}
}
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("execute.......");
String redisKey = RedisKeyUtil.getPostScoreKey();
BoundSetOperations operations = redisTemplate.boundSetOps(redisKey);
if(operations.size() == 0){
logger.info("[任务取消] 没有需要刷新的帖子!");
return;
}
logger.info("[任务开始] 正在刷新的帖子分数: " + operations.size());
//每次刷新一个帖子的分数即可
while(operations.size() > 0){
this.refresh((Integer) operations.pop());
}
logger.info("[任务开始] 帖子分数刷新完毕");
}
private void refresh(int postId){
DiscussPost post = discussPostService.findDiscussPostById(postId);
if(post == null){
logger.error("该帖子不存在: id =" + post.getId());
return;
}
//是否加精
boolean wonderful = post.getStatus() == 1;
//评论数量
int commentCount = post.getCommentCount();
//点赞数量
long likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_POST, postId);
//计算权重
double w = (wonderful ? 75 : 0) + commentCount * 10 + likeCount * 2;
//分数 = 帖子权重 + 距离天数
double score = Math.log10(Math.max(w, 1))
+ (post.getCreateTime().getTime() - epoch.getTime()) / (1000 * 3600 * 24);
//更新帖子的分数
discussPostService.updateScore(postId,score);
post.setScore(score);
//同步搜索数据
elasticsearchService.saveDiscussPost(post);
}
}
配置定时任务
// 配置JobDetail 刷新帖子分数任务
@Bean
public JobDetailFactoryBean postScoreRefreshJobDetail(){
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
factoryBean.setJobClass(PostScoreRefreshJob.class);
factoryBean.setName("postScoreRefreshJob");//任务的名字
factoryBean.setGroup("communityJobGroup");//任务的所属组
factoryBean.setDurability(true);//是不是长久保存,哪怕以后任务的触发器都没了,是不是还会一直保存
factoryBean.setRequestsRecovery(true);//任务是不是可恢复的
return factoryBean;
}
//配置Trigger(SimpleTriggerFactoryBean, CronTriggerFactoryBean)
@Bean
public SimpleTriggerFactoryBean postScoreRefreshTrigger(JobDetail postScoreRefreshJobDetail){
SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean();
factoryBean.setJobDetail(postScoreRefreshJobDetail);//对哪个job生效
factoryBean.setName("postScoreRefreshTrigger");//给trigger取个名字
factoryBean.setGroup("communityTriggerGroup");//给trigger取一个组名
factoryBean.setRepeatInterval(1000 * 60 * 2);//2分钟刷新一次
factoryBean.setJobDataMap(new JobDataMap());//需要存储job的一些状态
return factoryBean;
}