parent
e683171570
commit
b006059b41
@ -0,0 +1,86 @@
|
||||
package com.luojia_channel.modules.post.algorithm;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.luojia_channel.common.utils.RedisUtil;
|
||||
import com.luojia_channel.modules.post.dto.resp.PostBasicInfoDTO;
|
||||
import com.luojia_channel.modules.post.entity.Comment;
|
||||
import com.luojia_channel.modules.post.entity.Post;
|
||||
import com.luojia_channel.modules.post.mapper.CommentMapper;
|
||||
import com.luojia_channel.modules.post.mapper.PostMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class PostSelector {
|
||||
|
||||
private final PostMapper postMapper;
|
||||
private final CommentMapper commentMapper;
|
||||
private final RedisUtil redisUtil;
|
||||
|
||||
public void calculatePostScore(Post post){
|
||||
long hours = calculateHoursSince(post.getCreateTime());
|
||||
|
||||
double likes = post.getLikeCount();
|
||||
double comments = post.getCommentCount();
|
||||
double favorites = post.getFavoriteCount();
|
||||
|
||||
double newScore = (1 + likes + comments*0.7 + favorites*0.5)
|
||||
/ Math.pow(1 + hours/24.0, 1.8);
|
||||
|
||||
redisUtil.zAdd("post:hot:", post.getId(), newScore);
|
||||
}
|
||||
|
||||
public void calculateCommentScore(Comment comment){
|
||||
long hours = calculateHoursSince(comment.getCreateTime());
|
||||
|
||||
double likes = comment.getLikeCount();
|
||||
double replies = comment.getReplyCount();
|
||||
double newScore = (1 + likes + replies*0.7)
|
||||
/ Math.pow(1 + hours/24.0, 1.8);
|
||||
|
||||
if(comment.getId().equals(comment.getTopId())) {
|
||||
redisUtil.zAdd("post:comment_by_hot:" + comment.getPostId(), comment.getId(), newScore);
|
||||
} else {
|
||||
redisUtil.zAdd("comment:reply_by_hot:" + comment.getTopId(), comment.getId(), newScore);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新帖子热度分数
|
||||
public void updatePostScore(Long postId) {
|
||||
Post post = postMapper.selectById(postId);
|
||||
if (post == null) return;
|
||||
calculatePostScore(post);
|
||||
}
|
||||
|
||||
// 更新评论热度分数
|
||||
public void updateCommentScore(Long commentId) {
|
||||
Comment comment = commentMapper.selectById(commentId);
|
||||
if(comment == null) return;
|
||||
calculateCommentScore(comment);
|
||||
}
|
||||
|
||||
// 计算从指定时间到现在经过的小时数
|
||||
public long calculateHoursSince(LocalDateTime startTime) {
|
||||
if (startTime == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Instant startInstant = startTime.atZone(ZoneId.systemDefault()).toInstant();
|
||||
Instant nowInstant = Instant.now();
|
||||
|
||||
long millisDiff = Duration.between(startInstant, nowInstant).toMillis();
|
||||
return TimeUnit.HOURS.convert(millisDiff, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
// TODO 自定义首页帖子算法
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,91 @@
|
||||
package com.luojia_channel.modules.post.task;
|
||||
|
||||
import com.luojia_channel.common.utils.RedisUtil;
|
||||
import com.luojia_channel.modules.post.algorithm.PostSelector;
|
||||
import com.luojia_channel.modules.post.entity.Comment;
|
||||
import com.luojia_channel.modules.post.entity.Post;
|
||||
import com.luojia_channel.modules.post.mapper.CommentMapper;
|
||||
import com.luojia_channel.modules.post.mapper.PostMapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class UpdateScoreTask {
|
||||
|
||||
private static final int BATCH_SIZE = 100;
|
||||
// 每半小时更新热度
|
||||
private static final long POST_UPDATE_INTERVAL = 30 * 60;
|
||||
private static final long COMMENT_UPDATE_INTERVAL = 30 * 60;
|
||||
|
||||
private final PostSelector postSelector;
|
||||
private final PostMapper postMapper;
|
||||
private final CommentMapper commentMapper;
|
||||
private final RedisUtil redisUtil;
|
||||
|
||||
// 定时更新帖子热度分数
|
||||
@Scheduled(fixedRate = POST_UPDATE_INTERVAL * 1000)
|
||||
public void updatePostScores() {
|
||||
// 扩大更新范围到最近30天的帖子
|
||||
LocalDateTime startTime = LocalDateTime.now().minusDays(30);
|
||||
|
||||
LambdaQueryWrapper<Post> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.ge(Post::getCreateTime, startTime);
|
||||
|
||||
long totalPosts = postMapper.selectCount(queryWrapper);
|
||||
long batches = (totalPosts + BATCH_SIZE - 1) / BATCH_SIZE;
|
||||
|
||||
for (int i = 0; i < batches; i++) {
|
||||
queryWrapper.last("LIMIT " + i * BATCH_SIZE + ", " + BATCH_SIZE);
|
||||
List<Post> posts = postMapper.selectList(queryWrapper);
|
||||
posts.forEach(post -> postSelector.updatePostScore(post.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
// 定时更新评论热度分数
|
||||
@Scheduled(fixedRate = COMMENT_UPDATE_INTERVAL * 1000)
|
||||
public void updateCommentScores() {
|
||||
// 扩大更新范围到最近15天的评论
|
||||
LocalDateTime startTime = LocalDateTime.now().minusDays(15);
|
||||
|
||||
LambdaQueryWrapper<Comment> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.ge(Comment::getCreateTime, startTime);
|
||||
|
||||
long totalComments = commentMapper.selectCount(queryWrapper);
|
||||
long batches = (totalComments + BATCH_SIZE - 1) / BATCH_SIZE;
|
||||
|
||||
for (int i = 0; i < batches; i++) {
|
||||
queryWrapper.last("LIMIT " + i * BATCH_SIZE + ", " + BATCH_SIZE);
|
||||
List<Comment> comments = commentMapper.selectList(queryWrapper);
|
||||
comments.forEach(comment -> postSelector.updateCommentScore(comment.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
// 定期清理过期的缓存数据
|
||||
@Scheduled(cron = "0 0 4 * * ?") // 每天凌晨4点执行
|
||||
public void cleanExpiredData() {
|
||||
LocalDateTime expiredTime = LocalDateTime.now().minusDays(60);
|
||||
|
||||
// 清理过期的帖子缓存
|
||||
LambdaQueryWrapper<Post> postQueryWrapper = new LambdaQueryWrapper<>();
|
||||
postQueryWrapper.le(Post::getCreateTime, expiredTime);
|
||||
List<Post> expiredPosts = postMapper.selectList(postQueryWrapper);
|
||||
expiredPosts.forEach(post -> {
|
||||
redisUtil.zRemove("post:hot:", post.getId());
|
||||
redisUtil.zRemove("post:time:", post.getId());
|
||||
});
|
||||
|
||||
// 清理过期的评论缓存
|
||||
LambdaQueryWrapper<Comment> commentQueryWrapper = new LambdaQueryWrapper<>();
|
||||
commentQueryWrapper.le(Comment::getCreateTime, expiredTime);
|
||||
List<Comment> expiredComments = commentMapper.selectList(commentQueryWrapper);
|
||||
expiredComments.forEach(comment -> {
|
||||
// 评论相关缓存清理
|
||||
});
|
||||
}
|
||||
}
|
@ -1,36 +1,36 @@
|
||||
###本地开发环境
|
||||
# lj:
|
||||
# db:
|
||||
# host: localhost
|
||||
# password: 123456
|
||||
# redis:
|
||||
# host: localhost
|
||||
# port: 6379
|
||||
# password: 123456
|
||||
# rabbitmq:
|
||||
# host: localhost
|
||||
# port: 5672
|
||||
# username: root
|
||||
# password: 123456
|
||||
# minio:
|
||||
# endpoint: http://localhost:9000
|
||||
# accessKey: root
|
||||
# secretKey: 12345678
|
||||
|
||||
##本地开发环境
|
||||
lj:
|
||||
db:
|
||||
host: 192.168.125.128
|
||||
password: MySQL@5678
|
||||
host: 192.168.59.129
|
||||
password: Forely123!
|
||||
redis:
|
||||
host: 192.168.125.128
|
||||
host: 192.168.59.129
|
||||
port: 6379
|
||||
password: Redis@9012
|
||||
password: Forely123!
|
||||
rabbitmq:
|
||||
host: 192.168.125.128
|
||||
host: 192.168.59.129
|
||||
port: 5672
|
||||
username: rabbit_admin
|
||||
password: Rabbit@3456
|
||||
username: admin
|
||||
password: Forely123!
|
||||
minio:
|
||||
endpoint: http://192.168.125.128:9000
|
||||
accessKey: minio_admin
|
||||
secretKey: Minio@1234
|
||||
endpoint: http://192.168.59.129:9000
|
||||
accessKey: forely
|
||||
secretKey: Forely123!
|
||||
|
||||
#lj:
|
||||
# db:
|
||||
# host: 192.168.125.128
|
||||
# password: MySQL@5678
|
||||
# redis:
|
||||
# host: 192.168.125.128
|
||||
# port: 6379
|
||||
# password: Redis@9012
|
||||
# rabbitmq:
|
||||
# host: 192.168.125.128
|
||||
# port: 5672
|
||||
# username: rabbit_admin
|
||||
# password: Rabbit@3456
|
||||
# minio:
|
||||
# endpoint: http://192.168.125.128:9000
|
||||
# accessKey: minio_admin
|
||||
# secretKey: Minio@1234
|
||||
|
Loading…
Reference in new issue