@@ -38,6 +38,7 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils ;
import org.apache.logging.log4j.util.Strings ;
import org.jspecify.annotations.NonNull ;
import org.springframework.dao.DataAccessException ;
import org.springframework.data.redis.core.* ;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor ;
import org.springframework.stereotype.Service ;
@@ -311,13 +312,30 @@ public class CommentServiceImpl extends ServiceImpl<CommentDOMapper, CommentDO>
// 每页展示的二级评论数 (小红书 APP 中是一次查询 6 条)
long pageSize = 6 ;
// TODO: 先从缓存中查询,待完善
// 先从缓存中查询
String countCommentKey = RedisKeyConstants . buildCountCommentKey ( parentCommentId ) ;
// 子评论总数
Number redisCount = ( Number ) redisTemplate . opsForHash ( )
. get ( countCommentKey , RedisKeyConstants . FIELD_CHILD_COMMENT_TOTAL ) ;
long count = Objects . isNull ( redisCount ) ? 0L : redisCount . longValue ( ) ;
// 查询一级评论下的子评论数目
Long count = commentDOMapper . selectChildCommentTotalById ( parentCommentId ) ;
// 若缓存不存在,走数据库查询
if ( Objects . isNull ( redisCount ) ) {
// 查询一级评论下子评论的总数 (直接查询 t_comment 表的 child_comment_total 字段,提升查询性能, 避免 count(*))
Long dbCount = commentDOMapper . selectChildCommentTotalById ( parentCommentId ) ;
// 若一级评论不存在或者评论数目为0, 则直接返回
if ( Objects . isNull ( c ount) | | count = = 0 ) {
// 若数据库中也不存在,则抛出业务异常
if ( Objects . isNull ( dbC ount) ) {
throw new ApiException ( ResponseCodeEnum . PARENT_COMMENT_NOT_FOUND ) ;
}
count = dbCount ;
// 异步将子评论总数同步到 Redis 中
threadPoolTaskExecutor . execute ( ( ) - > syncCommentCount2Redis ( countCommentKey , dbCount ) ) ;
}
// 若子评论总数为 0, 直接返参
if ( count = = 0 ) {
return PageResponse . success ( null , pageNo , 0 ) ;
}
@@ -327,9 +345,217 @@ public class CommentServiceImpl extends ServiceImpl<CommentDOMapper, CommentDO>
// 计算分页查询的偏移量offset( 需要加1, 因为最早回复的二级评论已经被展示了)
long offset = PageResponse . getOffset ( pageNo , pageSize ) + 1 ;
// 子评论分页缓存使用Zset+String实现
// 构建子评论ZSETKey
String childCommentZsetKey = RedisKeyConstants . buildChildCommentListKey ( parentCommentId ) ;
// 先判断ZSET是否存在
boolean hasKey = redisTemplate . hasKey ( childCommentZsetKey ) ;
// 若不存在
if ( ! hasKey ) {
// 异步将子评论同步到 Redis 中(最多同步 6*10 条)
threadPoolTaskExecutor . execute ( ( ) - > syncChildComments2Redis ( parentCommentId , childCommentZsetKey ) ) ;
}
// 若子评论 ZSET 缓存存在, 并且查询的是前 10 页的子评论
if ( hasKey & & offset < 6 * 10 ) {
// 使用ZRevRange获取某个一级评论下的子评论, 按回复时间升序排序
Set < Object > childCommentIds = redisTemplate
. opsForZSet ( ) . rangeByScore ( childCommentZsetKey , 0 , Double . MAX_VALUE , offset , pageSize ) ;
// 若结果不为空
if ( CollUtil . isNotEmpty ( childCommentIds ) ) {
// Set转List
List < Object > childCommentIdList = Lists . newArrayList ( childCommentIds ) ;
// 构建 MGET 批量查询子评论详情的 Key 集合
List < String > commentIdKeys = childCommentIds . stream ( )
. map ( RedisKeyConstants : : buildCommentDetailKey )
. toList ( ) ;
// MGET 批量获取评论数据
List < Object > commentsJsonList = redisTemplate . opsForValue ( ) . multiGet ( commentIdKeys ) ;
// 可能存在部分评论不在缓存中,已经过期被删除,这些评论 ID 需要提取出来,等会查数据库
List < Long > expiredChildCommentIds = Lists . newArrayList ( ) ;
if ( commentsJsonList ! = null ) {
for ( int i = 0 ; i < commentsJsonList . size ( ) ; i + + ) {
String commentJson = ( String ) commentsJsonList . get ( i ) ;
Long commentId = Long . valueOf ( childCommentIdList . get ( i ) . toString ( ) ) ;
if ( Objects . nonNull ( commentJson ) ) {
// 缓存中存在的评论 Json, 直接转换为 VO 添加到返参集合中
FindChildCommentItemRspVO childCommentRspVO = JsonUtils . parseObject ( commentJson , FindChildCommentItemRspVO . class ) ;
childCommentRspVOS . add ( childCommentRspVO ) ;
} else {
// 评论失效,添加到失效评论列表
expiredChildCommentIds . add ( commentId ) ;
}
}
}
// 对于缓存中存在的子评论, 需要再次查询 Hash, 获取其计数数据
if ( CollUtil . isNotEmpty ( childCommentRspVOS ) ) {
setChildCommentCountData ( childCommentRspVOS , expiredChildCommentIds ) ;
}
// 对于不存在的子评论,需要批量从数据库中查询,并添加到 commentRspVOS 中
if ( CollUtil . isNotEmpty ( expiredChildCommentIds ) ) {
List < CommentDO > commentDOS = commentDOMapper . selectByCommentIds ( expiredChildCommentIds ) ;
getChildCommentDataAndSync2Redis ( commentDOS , childCommentRspVOS ) ;
}
// 按评论 ID 升序排列(等同于按回复时间升序)
childCommentRspVOS = childCommentRspVOS . stream ( )
. sorted ( Comparator . comparing ( FindChildCommentItemRspVO : : getCommentId ) )
. collect ( Collectors . toList ( ) ) ;
return PageResponse . success ( childCommentRspVOS , pageNo , count , pageSize ) ;
}
}
// 分页查询子评论
List < CommentDO > childCommentDOS = commentDOMapper . selectChildPageList ( parentCommentId , offset , pageSize ) ;
getChildCommentDataAndSync2Redis ( childCommentDOS , childCommentRspVOS ) ;
return PageResponse . success ( childCommentRspVOS , pageNo , count , pageSize ) ;
}
/**
* 设置子评论计数数据
*
* @param commentRspVOS 子评论VO
* @param expiredCommentIds 失效的子评论ID
*/
private void setChildCommentCountData ( List < FindChildCommentItemRspVO > commentRspVOS , List < Long > expiredCommentIds ) {
// 准备从评论 Hash 中查询计数 (被点赞数)
// 缓存中存在的子评论 ID
List < Long > notExpiredCommentIds = Lists . newArrayList ( ) ;
// 遍历从缓存中解析出的 VO 集合,提取二级评论 ID
commentRspVOS . forEach ( commentRspVO - > {
Long childCommentId = commentRspVO . getCommentId ( ) ;
notExpiredCommentIds . add ( childCommentId ) ;
} ) ;
// 从 Redis 中查询评论计数 Hash 数据
Map < Long , Map < Object , Object > > commentIdAndCountMap = getCommentCountDataAndSync2RedisHash ( notExpiredCommentIds ) ;
// 遍历 VO, 设置对应子评论的点赞数
for ( FindChildCommentItemRspVO commentRspVO : commentRspVOS ) {
// 评论 ID
Long commentId = commentRspVO . getCommentId ( ) ;
// 若当前这条评论是从数据库中查询出来的, 则无需设置点赞数,以数据库查询出来的为主
if ( CollUtil . isNotEmpty ( expiredCommentIds )
& & expiredCommentIds . contains ( commentId ) ) {
continue ;
}
// 设置子评论的点赞数
Map < Object , Object > hash = commentIdAndCountMap . get ( commentId ) ;
if ( CollUtil . isNotEmpty ( hash ) ) {
Long likeTotal = Long . valueOf ( hash . get ( RedisKeyConstants . FIELD_LIKE_TOTAL ) . toString ( ) ) ;
commentRspVO . setLikeTotal ( likeTotal ) ;
}
}
}
/**
* 获取评论计数数据,并同步到 Redis 中
*
* @param notExpiredCommentIds 缓存中存在的评论 ID
* @return 评论计数数据
*/
@SuppressWarnings ( " unchecked " )
private Map < Long , Map < Object , Object > > getCommentCountDataAndSync2RedisHash ( List < Long > notExpiredCommentIds ) {
// 已失效的 Hash 评论 ID
List < Long > expiredCountCommentIds = Lists . newArrayList ( ) ;
// 构建需要查询的 Hash Key 集合
List < String > commentCountKeys = notExpiredCommentIds . stream ( )
. map ( RedisKeyConstants : : buildCountCommentKey ) . toList ( ) ;
// 使用 RedisTemplate 执行管道批量操作
List < Object > results = redisTemplate . executePipelined ( new SessionCallback < > ( ) {
@Override
public Object execute ( @NonNull RedisOperations operations ) {
// 遍历需要查询的评论计数的 Hash 键集合
commentCountKeys . forEach ( key - >
// 在管道中执行 Redis 的 hash.entries 操作
// 此操作会获取指定 Hash 键中所有的字段和值
operations . opsForHash ( ) . entries ( key ) ) ;
return null ;
}
} ) ;
// 评论 ID - 计数数据字典
Map < Long , Map < Object , Object > > commentIdAndCountMap = Maps . newHashMap ( ) ;
// 遍历未过期的评论 ID 集合
for ( int i = 0 ; i < notExpiredCommentIds . size ( ) ; i + + ) {
// 当前评论 ID
Long currCommentId = Long . valueOf ( notExpiredCommentIds . get ( i ) . toString ( ) ) ;
// 从缓存查询结果中,获取对应 Hash
Map < Object , Object > hash = ( Map < Object , Object > ) results . get ( i ) ;
// 若 Hash 结果为空,说明缓存中不存在,添加到 expiredCountCommentIds 中,保存一下
if ( CollUtil . isEmpty ( hash ) ) {
expiredCountCommentIds . add ( currCommentId ) ;
continue ;
}
// 若存在,则将数据添加到 commentIdAndCountMap 中,方便后续读取
commentIdAndCountMap . put ( currCommentId , hash ) ;
}
// 若已过期的计数评论 ID 集合大于 0, 说明部分计数数据不在 Redis 缓存中
// 需要查询数据库,并将这部分的评论计数 Hash 同步到 Redis 中
if ( CollUtil . size ( expiredCountCommentIds ) > 0 ) {
// 查询数据库
List < CommentDO > commentDOS = commentDOMapper . selectCommentCountByIds ( expiredCountCommentIds ) ;
commentDOS . forEach ( commentDO - > {
Integer level = commentDO . getLevel ( ) ;
Map < Object , Object > map = Maps . newHashMap ( ) ;
map . put ( RedisKeyConstants . FIELD_LIKE_TOTAL , commentDO . getLikeTotal ( ) ) ;
// 只有一级评论需要统计子评论总数
if ( Objects . equals ( level , CommentLevelEnum . ONE . getCode ( ) ) ) {
map . put ( RedisKeyConstants . FIELD_CHILD_COMMENT_TOTAL , commentDO . getChildCommentTotal ( ) ) ;
}
// 统一添加到 commentIdAndCountMap 字典中,方便后续查询
commentIdAndCountMap . put ( commentDO . getId ( ) , map ) ;
} ) ;
// 异步同步到 Redis 中
threadPoolTaskExecutor . execute ( ( ) - > redisTemplate . executePipelined ( new SessionCallback < > ( ) {
@Override
public Object execute ( @NonNull RedisOperations operations ) {
commentDOS . forEach ( commentDO - > {
// 构建 Hash Key
String key = RedisKeyConstants . buildCountCommentKey ( commentDO . getId ( ) ) ;
// 评论级别
Integer level = commentDO . getLevel ( ) ;
// 设置 Field 数据
Map < String , Long > fieldsMap = Objects . equals ( level , CommentLevelEnum . ONE . getCode ( ) ) ?
Map . of ( RedisKeyConstants . FIELD_CHILD_COMMENT_TOTAL , commentDO . getChildCommentTotal ( ) ,
RedisKeyConstants . FIELD_LIKE_TOTAL , commentDO . getLikeTotal ( ) ) : Map . of ( RedisKeyConstants . FIELD_LIKE_TOTAL , commentDO . getLikeTotal ( ) ) ;
// 添加 Hash 数据
operations . opsForHash ( ) . putAll ( key , fieldsMap ) ;
// 设置随机过期时间 (5小时以内)
long expireTime = 60 * 60 + RandomUtil . randomInt ( 4 * 60 * 60 ) ;
operations . expire ( key , expireTime , TimeUnit . SECONDS ) ;
} ) ;
return null ;
}
} ) ) ;
}
return commentIdAndCountMap ;
}
/**
* 获取子评论数据
*
* @param childCommentDOS 子评论DO
* @param childCommentRspVOS 子评论VO
*/
private void getChildCommentDataAndSync2Redis ( List < CommentDO > childCommentDOS , List < FindChildCommentItemRspVO > childCommentRspVOS ) {
// 调用KV服务需要的入参
List < FindCommentContentReqDTO > findCommentContentReqDTOS = Lists . newArrayList ( ) ;
// 调用用户服务需要的入参
@@ -426,7 +652,99 @@ public class CommentServiceImpl extends ServiceImpl<CommentDOMapper, CommentDO>
childCommentRspVOS . add ( childCommentRspVO ) ;
}
return PageResponse . success ( childCommentRspVOS , pageNo , count , pageSize ) ;
// 异步将笔记详情,同步到 Redis 中
threadPoolTaskExecutor . execute ( ( ) - > {
// 准备批量写入的数据
Map < String , String > data = Maps . newHashMap ( ) ;
childCommentRspVOS . forEach ( commentRspVO - > {
// 评论 ID
Long commentId = commentRspVO . getCommentId ( ) ;
// 构建 Key
String key = RedisKeyConstants . buildCommentDetailKey ( commentId ) ;
data . put ( key , JsonUtils . toJsonString ( commentRspVO ) ) ;
} ) ;
batchAddCommentDetailJson2Redis ( data ) ;
} ) ;
}
/**
* 批量添加评论详情 Json 到 Redis 中
*
* @param data 批量写入的数据
*/
private void batchAddCommentDetailJson2Redis ( Map < String , String > data ) {
// 使用 Redis Pipeline 提升写入性能
redisTemplate . executePipelined ( ( RedisCallback < ? > ) ( connection ) - > {
for ( Map . Entry < String , String > entry : data . entrySet ( ) ) {
// 将 Java 对象序列化为 JSON 字符串
String jsonStr = JsonUtils . toJsonString ( entry . getValue ( ) ) ;
// 随机生成过期时间 (5小时以内)
int randomExpire = 60 * 60 + RandomUtil . randomInt ( 4 * 60 * 60 ) ;
// 批量写入并设置过期时间
connection . stringCommands ( ) . setEx (
Objects . requireNonNull ( redisTemplate . getStringSerializer ( ) . serialize ( entry . getKey ( ) ) ) ,
randomExpire ,
Objects . requireNonNull ( redisTemplate . getStringSerializer ( ) . serialize ( jsonStr ) )
) ;
}
return null ;
} ) ;
}
/**
* 同步子评论到 Redis 中
*
* @param parentCommentId 父评论ID
* @param childCommentZSetKey 子评论ZSet Key
*/
private void syncChildComments2Redis ( Long parentCommentId , String childCommentZSetKey ) {
List < CommentDO > childCommentDOS = commentDOMapper . selectChildCommentsByParentIdAndLimit ( parentCommentId , 6 * 10 ) ;
if ( CollUtil . isNotEmpty ( childCommentDOS ) ) {
// 使用 Redis Pipeline 提升写入性能
redisTemplate . executePipelined ( ( RedisCallback < Object > ) connection - > {
ZSetOperations < String , Object > zSetOps = redisTemplate . opsForZSet ( ) ;
// 遍历子评论数据并批量写入 ZSet
for ( CommentDO childCommentDO : childCommentDOS ) {
Long commentId = childCommentDO . getId ( ) ;
// create_time 转时间戳
long commentTimestamp = DateUtils . localDateTime2Timestamp ( childCommentDO . getCreateTime ( ) ) ;
zSetOps . add ( childCommentZSetKey , commentId , commentTimestamp ) ;
}
// 设置随机过期时间, ( 保底1小时 + 随机时间),单位:秒
int randomExpiryTime = 60 * 60 + RandomUtil . randomInt ( 4 * 60 * 60 ) ; // 5小时以内
redisTemplate . expire ( childCommentZSetKey , randomExpiryTime , TimeUnit . SECONDS ) ;
return null ; // 无返回值
} ) ;
}
}
/**
* 同步评论数量到 Redis 中
*
* @param countCommentKey 评论数量缓存的键值
* @param dbCount 数据库中的评论数量
*/
@SuppressWarnings ( " unchecked " )
private void syncCommentCount2Redis ( String countCommentKey , Long dbCount ) {
redisTemplate . executePipelined ( new SessionCallback < Void > ( ) {
@Override
public < K , V > Void execute ( @NonNull RedisOperations < K , V > operations ) throws DataAccessException {
// 这里用强制类型转换指定类型
RedisOperations < String , Object > ops = ( RedisOperations < String , Object > ) operations ;
ops . opsForHash ( )
. put ( countCommentKey , RedisKeyConstants . FIELD_CHILD_COMMENT_TOTAL , dbCount ) ;
long expireTime = 60 * 60 + RandomUtil . randomInt ( 4 * 60 * 60 ) ;
ops . expire ( countCommentKey , expireTime , TimeUnit . SECONDS ) ;
return null ;
}
} ) ;
}
/**
@@ -574,25 +892,7 @@ public class CommentServiceImpl extends ServiceImpl<CommentDOMapper, CommentDO>
data . put ( key , JsonUtils . toJsonString ( commentRspVO ) ) ;
} ) ;
// 使用 Redis Pipeline 提升写入性能
redisTemplate . executePipelined ( ( RedisCallback < ? > ) ( connection ) - > {
for ( Map . Entry < String , String > entry : data . entrySet ( ) ) {
// 将 Java 对象序列化为 JSON 字符串
String jsonStr = JsonUtils . toJsonString ( entry . getValue ( ) ) ;
// 随机生成过期时间 (5小时以内)
int randomExpire = RandomUtil . randomInt ( 5 * 60 * 60 ) ;
// 批量写入并设置过期时间
connection . stringCommands ( ) . setEx (
Objects . requireNonNull ( redisTemplate . getStringSerializer ( ) . serialize ( entry . getKey ( ) ) ) ,
randomExpire ,
Objects . requireNonNull ( redisTemplate . getStringSerializer ( ) . serialize ( jsonStr ) )
) ;
}
return null ;
} ) ;
batchAddCommentDetailJson2Redis ( data ) ;
} ) ;
}
@@ -662,85 +962,7 @@ public class CommentServiceImpl extends ServiceImpl<CommentDOMapper, CommentDO>
} ) ;
// 已失效的 Hash 评论 ID
List < Long > expiredCountCommentIds = Lists . newArrayList ( ) ;
// 构建需要查询的 Hash Key 集合
List < String > commentCountKeys = notExpiredCommentIds . stream ( )
. map ( RedisKeyConstants : : buildCountCommentKey ) . toList ( ) ;
// 使用 RedisTemplate 执行管道批量操作
List < Object > results = redisTemplate . executePipelined ( new SessionCallback < > ( ) {
@Override
public Object execute ( @NonNull RedisOperations operations ) {
// 遍历需要查询的评论计数的 Hash 键集合
commentCountKeys . forEach ( key - >
// 在管道中执行 Redis 的 hash.entries 操作
// 此操作会获取指定 Hash 键中所有的字段和值
operations . opsForHash ( ) . entries ( key ) ) ;
return null ;
}
} ) ;
// 评论 ID - 计数数据字典
Map < Long , Map < Object , Object > > commentIdAndCountMap = Maps . newHashMap ( ) ;
// 遍历未过期的评论 ID 集合
for ( int i = 0 ; i < notExpiredCommentIds . size ( ) ; i + + ) {
// 当前评论 ID
Long currCommentId = Long . valueOf ( notExpiredCommentIds . get ( i ) . toString ( ) ) ;
// 从缓存查询结果中,获取对应 Hash
Map < Object , Object > hash = ( Map < Object , Object > ) results . get ( i ) ;
// 若 Hash 结果为空,说明缓存中不存在,添加到 expiredCountCommentIds 中,保存一下
if ( CollUtil . isEmpty ( hash ) ) {
expiredCountCommentIds . add ( currCommentId ) ;
continue ;
}
// 若存在,则将数据添加到 commentIdAndCountMap 中,方便后续读取
commentIdAndCountMap . put ( currCommentId , hash ) ;
}
// 若已过期的计数评论 ID 集合大于 0, 说明部分计数数据不在 Redis 缓存中
// 需要查询数据库,并将这部分的评论计数 Hash 同步到 Redis 中
if ( CollUtil . size ( expiredCountCommentIds ) > 0 ) {
// 查询数据库
List < CommentDO > commentDOS = commentDOMapper . selectCommentCountByIds ( expiredCountCommentIds ) ;
commentDOS . forEach ( commentDO - > {
Integer level = commentDO . getLevel ( ) ;
Map < Object , Object > map = Maps . newHashMap ( ) ;
map . put ( RedisKeyConstants . FIELD_LIKE_TOTAL , commentDO . getLikeTotal ( ) ) ;
// 只有一级评论需要统计子评论总数
if ( Objects . equals ( level , CommentLevelEnum . ONE . getCode ( ) ) ) {
map . put ( RedisKeyConstants . FIELD_CHILD_COMMENT_TOTAL , commentDO . getChildCommentTotal ( ) ) ;
}
// 统一添加到 commentIdAndCountMap 字典中,方便后续查询
commentIdAndCountMap . put ( commentDO . getId ( ) , map ) ;
} ) ;
// 异步同步到 Redis 中
threadPoolTaskExecutor . execute ( ( ) - > {
redisTemplate . executePipelined ( new SessionCallback < > ( ) {
@Override
public Object execute ( RedisOperations operations ) {
commentDOS . forEach ( commentDO - > {
// 构建 Hash Key
String key = RedisKeyConstants . buildCountCommentKey ( commentDO . getId ( ) ) ;
// 评论级别
Integer level = commentDO . getLevel ( ) ;
// 设置 Field 数据
Map < String , Long > fieldsMap = Objects . equals ( level , CommentLevelEnum . ONE . getCode ( ) ) ?
Map . of ( RedisKeyConstants . FIELD_CHILD_COMMENT_TOTAL , commentDO . getChildCommentTotal ( ) ,
RedisKeyConstants . FIELD_LIKE_TOTAL , commentDO . getLikeTotal ( ) ) : Map . of ( RedisKeyConstants . FIELD_LIKE_TOTAL , commentDO . getLikeTotal ( ) ) ;
// 添加 Hash 数据
operations . opsForHash ( ) . putAll ( key , fieldsMap ) ;
// 设置随机过期时间 (5小时以内)
long expireTime = RandomUtil . randomInt ( 5 * 60 * 60 ) ;
operations . expire ( key , expireTime , TimeUnit . SECONDS ) ;
} ) ;
return null ;
}
} ) ;
} ) ;
}
Map < Long , Map < Object , Object > > commentIdAndCountMap = getCommentCountDataAndSync2RedisHash ( notExpiredCommentIds ) ;
// 遍历 VO, 设置对应评论的二级评论数、点赞数
for ( FindCommentItemRspVO commentRspVO : commentRspVOS ) {
@@ -756,10 +978,9 @@ public class CommentServiceImpl extends ServiceImpl<CommentDOMapper, CommentDO>
// 设置一级评论的子评论总数、点赞数
Map < Object , Object > hash = commentIdAndCountMap . get ( commentId ) ;
if ( CollUtil . isNotEmpty ( hash ) ) {
Object childComment TotalObj = hash . get ( RedisKeyConstants . FIELD_CHILD_COMMENT_TOTAL ) ;
Long childCommentTotal = Objects . isNull ( childComment TotalObj) ? 0 : Long . parseLong ( childComment TotalObj. toString ( ) ) ;
Object likeTotalObj = hash . get ( RedisKeyConstants . FIELD_LIKE_TOTAL ) ;
Long likeTotal = Objects . isNull ( likeTotalObj ) ? 0 : Long . parseLong ( likeTotalObj . toString ( ) ) ;
Object like TotalObj = hash . get ( RedisKeyConstants . FIELD_CHILD_COMMENT_TOTAL ) ;
Long childCommentTotal = Objects . isNull ( like TotalObj) ? 0 : Long . parseLong ( like TotalObj. toString ( ) ) ;
Long likeTotal = Long . valueOf ( hash. get ( RedisKeyConstants . FIELD_LIKE_TOTAL ) . toString ( ) ) ;
commentRspVO . setChildCommentTotal ( childCommentTotal ) ;
commentRspVO . setLikeTotal ( likeTotal ) ;
// 最初回复的二级评论
@@ -776,4 +997,5 @@ public class CommentServiceImpl extends ServiceImpl<CommentDOMapper, CommentDO>
}
}
}