评论列表新增根据热度排序功能

This commit is contained in:
tianfeng 2026-02-04 17:30:47 +08:00
parent f25d77675f
commit 9a96b016cc
8 changed files with 158 additions and 4 deletions

View File

@ -132,7 +132,7 @@ public class DynamicCommentQryExe {
}
private PageResult<DynamicComment> pageData(DynamicCommentLimitCmd cmd) {
return dynamicCommentService.pageRootCommentsByTimeDesc(cmd.getPageReq(), cmd.getDynamicId());
return dynamicCommentService.pageRootCommentsByHotScore(cmd.getPageReq(), cmd.getDynamicId());
}
/**

View File

@ -12,6 +12,7 @@ import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.infra.database.rds.entity.dynamic.DynamicContent;
import com.red.circle.other.infra.database.rds.entity.dynamic.DynamicPicture;
import com.red.circle.other.infra.database.rds.entity.dynamic.DynamicTag;
import com.red.circle.other.infra.database.rds.service.approval.ApprovalDynamicContentService;
import com.red.circle.other.infra.database.rds.service.dynamic.DynamicCommentService;
import com.red.circle.other.infra.database.rds.service.dynamic.DynamicContentService;
import com.red.circle.other.infra.database.rds.service.dynamic.DynamicLikeService;
@ -49,10 +50,13 @@ public class DynamicMyQryExe {
private final DynamicCommentService dynamicCommentService;
private final UserSubscriptionService userSubscriptionService;
private final UserProfileAppConvertor userProfileAppConvertor;
private final ApprovalDynamicContentService approvalDynamicContentService;
public PageResult<DynamicListCO> execute(DynamicMyLimitCmd cmd) {
List<Long> pendingDynamicIds = approvalDynamicContentService.listPendingDynamicIds();
PageResult<DynamicContent> dynamics = dynamicContentService
.pageByTimeDesc(cmd.getUserId(), cmd.getPageReq());
.pageByTimeDesc(cmd.getUserId(), cmd.getPageReq(), pendingDynamicIds);
if (CollectionUtils.isEmpty(dynamics.getRecords())) {
return PageResult.newPageResult(0);

View File

@ -0,0 +1,125 @@
package com.red.circle.other.app.scheduler;
import com.red.circle.component.redis.annotation.TaskCacheLock;
import com.red.circle.component.redis.service.RedisService;
import com.red.circle.framework.mybatis.constant.PageConstant;
import com.red.circle.other.infra.database.cache.service.other.DynamicCacheService;
import com.red.circle.other.infra.database.rds.entity.dynamic.DynamicComment;
import com.red.circle.other.infra.database.rds.service.dynamic.DynamicCommentService;
import com.red.circle.tool.core.collection.CollectionUtils;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* 评论热度分数更新定时任务.
* 每10分钟执行一次更新最近7天的评论热度分数
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class DynamicCommentHotScoreTask {
private final DynamicCommentService dynamicCommentService;
private final DynamicCacheService dynamicCacheService;
/**
* 每10分钟执行一次
*/
@Scheduled(cron = "0 */10 * * * ?")
@TaskCacheLock(key = "COMMENT_HOT_SCORE_UPDATE", expireSecond = 540)
public void updateHotScore() {
try {
log.info("评论热度分数更新任务 - 开始执行");
long startTime = System.currentTimeMillis();
// 查询最近7天的根评论分批处理
long sevenDaysAgo = System.currentTimeMillis() - (7 * 24 * 60 * 60 * 1000L);
int totalUpdated = 0;
Long lastId = null;
boolean hasMore = true;
while (hasMore) {
// 分批查询每次1000条
List<DynamicComment> comments = queryRecentComments(sevenDaysAgo, lastId, 1000);
if (CollectionUtils.isEmpty(comments)) {
hasMore = false;
break;
}
// 批量更新热度分数
for (DynamicComment comment : comments) {
updateCommentHotScore(comment);
totalUpdated++;
}
// 准备下一批
lastId = comments.get(comments.size() - 1).getId();
hasMore = comments.size() >= 1000;
}
long costTime = System.currentTimeMillis() - startTime;
log.info("评论热度分数更新任务 - 执行完成,更新{}条评论,耗时{}ms", totalUpdated, costTime);
} catch (Exception e) {
log.error("评论热度分数更新任务 - 执行失败", e);
}
}
/**
* 查询最近的根评论
*/
private List<DynamicComment> queryRecentComments(long since, Long lastId, int limit) {
return dynamicCommentService.query()
.isNull(DynamicComment::getRootCommentId)
.ge(DynamicComment::getCreateTime, new java.sql.Timestamp(since))
.gt(lastId != null, DynamicComment::getId, lastId)
.orderByAsc(DynamicComment::getId)
.last(PageConstant.formatLimit(limit))
.list();
}
/**
* 更新单条评论的热度分数
*/
private void updateCommentHotScore(DynamicComment comment) {
try {
// 获取点赞数
Long likeCount = Optional.ofNullable(
dynamicCacheService.getCommentLikeQuantity(comment.getDynamicContentId(), comment.getId())
).orElse(0L);
// 计算热度分数
double hotScore = calculateHotScore(comment, likeCount);
// 更新到数据库
dynamicCommentService.update()
.set(DynamicComment::getHotScore, hotScore)
.eq(DynamicComment::getId, comment.getId())
.last(PageConstant.LIMIT_ONE)
.execute();
} catch (Exception e) {
log.error("更新评论热度分数失败commentId={}", comment.getId(), e);
}
}
/**
* 计算热度分数
* 公式: 点赞数 / (时间差小时数 + 2)^1.5
*/
private double calculateHotScore(DynamicComment comment, Long likeCount) {
long currentTime = System.currentTimeMillis();
long ageMillis = currentTime - comment.getCreateTime().getTime();
double ageHours = Math.max(ageMillis / (1000.0 * 60 * 60), 0.1);
return likeCount / Math.pow(ageHours + 2, 1.5);
}
}

View File

@ -54,4 +54,10 @@ public class DynamicComment extends TimestampBaseEntity {
@TableField("root_comment_id")
private Long rootCommentId;
/**
* 热度分数
*/
@TableField("hot_score")
private Double hotScore;
}

View File

@ -92,6 +92,14 @@ public interface DynamicCommentService extends BaseService<DynamicComment> {
*/
void deleteById(Long id);
/**
* 分页查询根评论按热度排序
*
* @param pageQuery 分页参数
* @param contentId 动态内容ID
*/
PageResult<DynamicComment> pageRootCommentsByHotScore(PageQuery pageQuery, Long contentId);
/**
* 根据动态ID获得动态评论数量
*

View File

@ -54,7 +54,7 @@ public interface DynamicContentService extends BaseService<DynamicContent> {
* @param pageNumber 第几页
* @param userId 创建人
*/
PageResult<DynamicContent> pageByTimeDesc(Long userId, PageQuery pageQuery);
PageResult<DynamicContent> pageByTimeDesc(Long userId, PageQuery pageQuery, List<Long> pendingDynamicIds);
/**
* 获得分页数据

View File

@ -68,6 +68,16 @@ public class DynamicCommentServiceImpl extends
.page(pageQuery);
}
@Override
public PageResult<DynamicComment> pageRootCommentsByHotScore(PageQuery pageQuery, Long contentId) {
return query()
.eq(DynamicComment::getDynamicContentId, contentId)
.isNull(DynamicComment::getRootCommentId)
.orderByDesc(DynamicComment::getHotScore)
.orderByDesc(DynamicComment::getCreateTime)
.page(pageQuery);
}
@Override
public Integer getNumberByRootIdByNotUserId(Long rootCommentId, Long notUserId) {

View File

@ -82,11 +82,12 @@ public class DynamicContentServiceImpl extends
}
@Override
public PageResult<DynamicContent> pageByTimeDesc(Long userId, PageQuery pageQuery) {
public PageResult<DynamicContent> pageByTimeDesc(Long userId, PageQuery pageQuery, List<Long> pendingDynamicIds) {
return query()
.eq(DynamicContent::getDel, Boolean.FALSE)
.eq(DynamicContent::getCreateUser, userId)
.notIn(CollectionUtils.isNotEmpty(pendingDynamicIds), DynamicContent::getId, pendingDynamicIds)
.orderByDesc(DynamicContent::getCreateTime)
.page(pageQuery);
}