diff --git a/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/consumer/CountNoteCollect2DBConsumer.java b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/consumer/CountNoteCollect2DBConsumer.java index e35c0d1..ca874b8 100644 --- a/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/consumer/CountNoteCollect2DBConsumer.java +++ b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/consumer/CountNoteCollect2DBConsumer.java @@ -5,13 +5,16 @@ import com.google.common.util.concurrent.RateLimiter; import com.hanserwei.framework.common.utils.JsonUtils; import com.hanserwei.hannote.count.biz.constant.MQConstants; import com.hanserwei.hannote.count.biz.domain.mapper.NoteCountDOMapper; +import com.hanserwei.hannote.count.biz.domain.mapper.UserCountDOMapper; +import com.hanserwei.hannote.count.biz.model.dto.AggregationCountCollectedUncollectedNoteMqDTO; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; import org.apache.rocketmq.spring.core.RocketMQListener; import org.springframework.stereotype.Component; +import org.springframework.transaction.support.TransactionTemplate; -import java.util.Map; +import java.util.List; @SuppressWarnings("UnstableApiUsage") @Component @@ -26,6 +29,10 @@ public class CountNoteCollect2DBConsumer implements RocketMQListener { private final RateLimiter rateLimiter = RateLimiter.create(5000); @Resource private NoteCountDOMapper noteCountDOMapper; + @Resource + private UserCountDOMapper userCountDOMapper; + @Resource + private TransactionTemplate transactionTemplate; @Override public void onMessage(String body) { @@ -34,16 +41,32 @@ public class CountNoteCollect2DBConsumer implements RocketMQListener { log.info("## 消费到了 MQ 【计数: 笔记收藏数入库】, {}...", body); - Map countMap = null; + List countList = null; try { - countMap = JsonUtils.parseMap(body, Long.class, Integer.class); + countList = JsonUtils.parseList(body, AggregationCountCollectedUncollectedNoteMqDTO.class); } catch (Exception e) { - log.error("## 解析 JSON 字符串异常", e); + log.error("## 解析 JSON 字符串异常"); } - if (CollUtil.isNotEmpty(countMap)) { - // 判断数据库中 t_note_count 表,若笔记计数记录不存在,则插入;若记录已存在,则直接更新 - countMap.forEach((k, v) -> noteCountDOMapper.insertOrUpdateCollectTotalByNoteId(v, k)); + if (CollUtil.isNotEmpty(countList)) { + countList.forEach(item -> { + Long creatorId = item.getCreatorId(); + Long noteId = item.getNoteId(); + Integer count = item.getCount(); + + // 编程式事务,保证两条语句的原子性 + transactionTemplate.execute(status -> { + try { + noteCountDOMapper.insertOrUpdateCollectTotalByNoteId(count, noteId); + userCountDOMapper.insertOrUpdateCollectTotalByUserId(count, creatorId); + return true; + } catch (Exception ex) { + status.setRollbackOnly(); // 标记事务为回滚 + log.error("", ex); + } + return false; + }); + }); } } } diff --git a/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/consumer/CountNoteCollectConsumer.java b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/consumer/CountNoteCollectConsumer.java index 1539d79..34c2cbd 100644 --- a/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/consumer/CountNoteCollectConsumer.java +++ b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/consumer/CountNoteCollectConsumer.java @@ -1,11 +1,12 @@ package com.hanserwei.hannote.count.biz.consumer; import com.github.phantomthief.collection.BufferTrigger; -import com.google.common.collect.Maps; +import com.google.common.collect.Lists; import com.hanserwei.framework.common.utils.JsonUtils; import com.hanserwei.hannote.count.biz.constant.MQConstants; import com.hanserwei.hannote.count.biz.constant.RedisKeyConstants; import com.hanserwei.hannote.count.biz.enums.CollectUnCollectNoteTypeEnum; +import com.hanserwei.hannote.count.biz.model.dto.AggregationCountCollectedUncollectedNoteMqDTO; import com.hanserwei.hannote.count.biz.model.dto.CountCollectUnCollectNoteMqDTO; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; @@ -63,14 +64,18 @@ public class CountNoteCollectConsumer implements RocketMQListener { Map> groupMap = countCollectUnCollectNoteMqDTOS.stream() .collect(Collectors.groupingBy(CountCollectUnCollectNoteMqDTO::getNoteId)); // 按组汇总数据,统计出最终的计数 - // key 为笔记 ID, value 为最终操作的计数 - Map countMap = Maps.newHashMap(); + List countList = Lists.newArrayList(); for (Map.Entry> entry : groupMap.entrySet()) { + // 笔记 ID + Long noteId = entry.getKey(); + // 笔记发布者 ID + Long creatorId = null; List list = entry.getValue(); // 默认计数为0 int finalCount = 0; for (CountCollectUnCollectNoteMqDTO countCollectUnCollectNoteMqDTO : list) { Integer type = countCollectUnCollectNoteMqDTO.getType(); + creatorId = countCollectUnCollectNoteMqDTO.getNoteCreatorId(); // 获取枚举类 CollectUnCollectNoteTypeEnum collectUnCollectNoteTypeEnum = CollectUnCollectNoteTypeEnum.valueOf(type); switch (Objects.requireNonNull(collectUnCollectNoteTypeEnum)) { @@ -78,28 +83,46 @@ public class CountNoteCollectConsumer implements RocketMQListener { case UN_COLLECT -> finalCount--; } } - // 将分组后统计出的最终计数,存入 countMap 中 - countMap.put(entry.getKey(), finalCount); + // 将分组后统计出的最终计数,存入 countList 中 + countList.add(AggregationCountCollectedUncollectedNoteMqDTO.builder() + .noteId(noteId) + .creatorId(creatorId) + .count(finalCount) + .build()); } - log.info("==> 【笔记收藏数】最终结果, {}", JsonUtils.toJsonString(countMap)); + log.info("==> 【笔记收藏数】最终结果, {}", JsonUtils.toJsonString(countList)); // 更新 Redis - countMap.forEach((k, v) -> { - // Redis Hash Key - String redisKey = RedisKeyConstants.buildCountNoteKey(k); - // 判断 Redis 中 Hash 是否存在 - boolean isExisted = redisTemplate.hasKey(redisKey); + countList.forEach(item -> { + // 笔记发布者 ID + Long creatorId = item.getCreatorId(); + // 笔记 ID + Long noteId = item.getNoteId(); + // 聚合后的计数 + Integer count = item.getCount(); + // 笔记维度计数 Redis Key + String countNoteRedisKey = RedisKeyConstants.buildCountNoteKey(noteId); + // 判断Redis 中 Hash 是否存在 + boolean isCountNoteExisted = redisTemplate.hasKey(countNoteRedisKey); // 若存在才会更新 // (因为缓存设有过期时间,考虑到过期后,缓存会被删除,这里需要判断一下,存在才会去更新,而初始化工作放在查询计数来做) - if (isExisted) { - // 对目标用户 Hash 中的收藏总数字段进行计数操作 - redisTemplate.opsForHash().increment(redisKey, RedisKeyConstants.FIELD_COLLECT_TOTAL, v); + if (isCountNoteExisted) { + // 对目标用户 Hash 中的点赞数字段进行计数操作 + redisTemplate.opsForHash().increment(countNoteRedisKey, RedisKeyConstants.FIELD_COLLECT_TOTAL, count); + } + + // 更新 Redis 用户维度收藏数 + String countUserRedisKey = RedisKeyConstants.buildCountUserKey(creatorId); + Boolean isCountUserExisted = redisTemplate.hasKey(countUserRedisKey); + if (isCountUserExisted) { + // 对目标用户 Hash 中的收藏数字段进行计数操作 + redisTemplate.opsForHash().increment(countUserRedisKey, RedisKeyConstants.FIELD_COLLECT_TOTAL, count); } }); // 发送 MQ, 笔记收藏数据落库 - Message message = MessageBuilder.withPayload(JsonUtils.toJsonString(countMap)) + Message message = MessageBuilder.withPayload(JsonUtils.toJsonString(countList)) .build(); // 异步发送 MQ 消息 diff --git a/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/consumer/CountNoteLike2DBConsumer.java b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/consumer/CountNoteLike2DBConsumer.java index 4cbf415..ea56a42 100644 --- a/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/consumer/CountNoteLike2DBConsumer.java +++ b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/consumer/CountNoteLike2DBConsumer.java @@ -5,13 +5,16 @@ import com.google.common.util.concurrent.RateLimiter; import com.hanserwei.framework.common.utils.JsonUtils; import com.hanserwei.hannote.count.biz.constant.MQConstants; import com.hanserwei.hannote.count.biz.domain.mapper.NoteCountDOMapper; +import com.hanserwei.hannote.count.biz.domain.mapper.UserCountDOMapper; +import com.hanserwei.hannote.count.biz.model.dto.AggregationCountLikeUnlikeNoteMqDTO; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; import org.apache.rocketmq.spring.core.RocketMQListener; import org.springframework.stereotype.Component; +import org.springframework.transaction.support.TransactionTemplate; -import java.util.Map; +import java.util.List; @Component @Slf4j @@ -26,6 +29,10 @@ public class CountNoteLike2DBConsumer implements RocketMQListener { private final RateLimiter rateLimiter = RateLimiter.create(5000); @Resource private NoteCountDOMapper noteCountDOMapper; + @Resource + private UserCountDOMapper userCountDOMapper; + @Resource + private TransactionTemplate transactionTemplate; @Override public void onMessage(String body) { @@ -34,16 +41,33 @@ public class CountNoteLike2DBConsumer implements RocketMQListener { log.info("## 消费到了 MQ 【计数: 笔记点赞数入库】, {}...", body); - Map countMap = null; + List countList = null; try { - countMap = JsonUtils.parseMap(body, Long.class, Integer.class); + countList = JsonUtils.parseList(body, AggregationCountLikeUnlikeNoteMqDTO.class); } catch (Exception e) { log.error("## 解析 JSON 字符串异常", e); } - if (CollUtil.isNotEmpty(countMap)) { - // 判断数据库中 t_note_count 表,若笔记计数记录不存在,则插入;若记录已存在,则直接更新 - countMap.forEach((k, v) -> noteCountDOMapper.insertOrUpdateLikeTotalByNoteId(v, k)); + if (CollUtil.isNotEmpty(countList)) { + // 判断数据库中 t_user_count 和 t_note_count 表,若笔记计数记录不存在,则插入;若记录已存在,则直接更新 + countList.forEach(item -> { + Long creatorId = item.getCreatorId(); + Long noteId = item.getNoteId(); + Integer count = item.getCount(); + + // 编程式事务,保证两条语句的原子性 + transactionTemplate.execute(status -> { + try { + noteCountDOMapper.insertOrUpdateLikeTotalByNoteId(count, noteId); + userCountDOMapper.insertOrUpdateLikeTotalByUserId(count, creatorId); + return true; + } catch (Exception ex) { + status.setRollbackOnly(); // 标记事务为回滚 + log.error("", ex); + } + return false; + }); + }); } } } diff --git a/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/consumer/CountNoteLikeConsumer.java b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/consumer/CountNoteLikeConsumer.java index 4bce40f..14a4077 100644 --- a/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/consumer/CountNoteLikeConsumer.java +++ b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/consumer/CountNoteLikeConsumer.java @@ -1,11 +1,12 @@ package com.hanserwei.hannote.count.biz.consumer; import com.github.phantomthief.collection.BufferTrigger; -import com.google.common.collect.Maps; +import com.google.common.collect.Lists; import com.hanserwei.framework.common.utils.JsonUtils; import com.hanserwei.hannote.count.biz.constant.MQConstants; import com.hanserwei.hannote.count.biz.constant.RedisKeyConstants; import com.hanserwei.hannote.count.biz.enums.LikeUnlikeNoteTypeEnum; +import com.hanserwei.hannote.count.biz.model.dto.AggregationCountLikeUnlikeNoteMqDTO; import com.hanserwei.hannote.count.biz.model.dto.CountLikeUnlikeNoteMqDTO; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; @@ -60,13 +61,18 @@ public class CountNoteLikeConsumer implements RocketMQListener { .collect(Collectors.groupingBy(CountLikeUnlikeNoteMqDTO::getNoteId)); // 按组汇总统计处最终计数 - // key为笔记ID,value为最终操作计数 - Map countMap = Maps.newHashMap(); + List countList = Lists.newArrayList(); for (Map.Entry> entry : groupMap.entrySet()) { + // 笔记 ID + Long noteId = entry.getKey(); + // 笔记发布者 ID + Long creatorId = null; List list = entry.getValue(); - // 最终计数默认为0 + // 最终地计数值,默认为 0 int finalCount = 0; for (CountLikeUnlikeNoteMqDTO countLikeUnlikeNoteMqDTO : list) { + // 设置笔记发布者用户 ID + creatorId = countLikeUnlikeNoteMqDTO.getNoteCreatorId(); Integer type = countLikeUnlikeNoteMqDTO.getType(); LikeUnlikeNoteTypeEnum likeUnlikeNoteTypeEnum = LikeUnlikeNoteTypeEnum.valueOf(type); if (likeUnlikeNoteTypeEnum == null) { @@ -77,26 +83,45 @@ public class CountNoteLikeConsumer implements RocketMQListener { case UNLIKE -> finalCount--; } } - countMap.put(entry.getKey(), finalCount); + // 将分组后统计出的最终计数,存入 countList 中 + countList.add(AggregationCountLikeUnlikeNoteMqDTO.builder() + .noteId(noteId) + .creatorId(creatorId) + .count(finalCount) + .build()); } - log.info("## 【笔记点赞数】聚合后的计数数据: {}", JsonUtils.toJsonString(countMap)); - // 更新Redis - countMap.forEach((k, v) -> { - // Redis Key - String redisKey = RedisKeyConstants.buildCountNoteKey(k); + log.info("## 【笔记点赞数】聚合后的计数数据: {}", JsonUtils.toJsonString(countList)); + // 更新 Redis + countList.forEach(item -> { + // 笔记发布者 ID + Long creatorId = item.getCreatorId(); + // 笔记 ID + Long noteId = item.getNoteId(); + // 聚合后的计数 + Integer count = item.getCount(); + + // 笔记维度计数 Redis Key + String countNoteRedisKey = RedisKeyConstants.buildCountNoteKey(noteId); // 判断 Redis 中 Hash 是否存在 - boolean isExisted = redisTemplate.hasKey(redisKey); + boolean isCountNoteExisted = redisTemplate.hasKey(countNoteRedisKey); // 若存在才会更新 // (因为缓存设有过期时间,考虑到过期后,缓存会被删除,这里需要判断一下,存在才会去更新,而初始化工作放在查询计数来做) - if (isExisted) { + if (isCountNoteExisted) { // 对目标用户 Hash 中的点赞数字段进行计数操作 - redisTemplate.opsForHash().increment(redisKey, RedisKeyConstants.FIELD_LIKE_TOTAL, v); + redisTemplate.opsForHash().increment(countNoteRedisKey, RedisKeyConstants.FIELD_LIKE_TOTAL, count); + } + + // 更新 Redis 用户维度点赞数 + String countUserRedisKey = RedisKeyConstants.buildCountUserKey(creatorId); + boolean isCountUserExisted = redisTemplate.hasKey(countUserRedisKey); + if (isCountUserExisted) { + redisTemplate.opsForHash().increment(countUserRedisKey, RedisKeyConstants.FIELD_LIKE_TOTAL, count); } }); // 发送 MQ, 笔记点赞数据落库 - Message message = MessageBuilder.withPayload(JsonUtils.toJsonString(countMap)) + Message message = MessageBuilder.withPayload(JsonUtils.toJsonString(countList)) .build(); // 异步发送 MQ 消息 diff --git a/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/domain/mapper/UserCountDOMapper.java b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/domain/mapper/UserCountDOMapper.java index e15f62b..80a19a3 100644 --- a/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/domain/mapper/UserCountDOMapper.java +++ b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/domain/mapper/UserCountDOMapper.java @@ -25,4 +25,22 @@ public interface UserCountDOMapper extends BaseMapper { * @return 影响行数 */ int insertOrUpdateFollowingTotalByUserId(@Param("count") Integer count, @Param("userId") Long userId); + + /** + * 添加记录或更新笔记点赞数 + * + * @param count 点赞数 + * @param userId 用户ID + * @return 影响行数 + */ + int insertOrUpdateLikeTotalByUserId(@Param("count") Integer count, @Param("userId") Long userId); + + /** + * 添加记录或更新笔记收藏数 + * + * @param count 收藏数 + * @param userId 用户ID + * @return 影响行数 + */ + int insertOrUpdateCollectTotalByUserId(@Param("count") Integer count, @Param("userId") Long userId); } \ No newline at end of file diff --git a/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/model/dto/AggregationCountCollectedUncollectedNoteMqDTO.java b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/model/dto/AggregationCountCollectedUncollectedNoteMqDTO.java new file mode 100644 index 0000000..8809b49 --- /dev/null +++ b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/model/dto/AggregationCountCollectedUncollectedNoteMqDTO.java @@ -0,0 +1,32 @@ +package com.hanserwei.hannote.count.biz.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 聚合后的收藏、取消收藏笔记消息体 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class AggregationCountCollectedUncollectedNoteMqDTO { + + /** + * 笔记发布者 ID + */ + private Long creatorId; + + /** + * 笔记 ID + */ + private Long noteId; + + /** + * 聚合后的计数 + */ + private Integer count; + +} \ No newline at end of file diff --git a/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/model/dto/AggregationCountLikeUnlikeNoteMqDTO.java b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/model/dto/AggregationCountLikeUnlikeNoteMqDTO.java new file mode 100644 index 0000000..e6e9da9 --- /dev/null +++ b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/model/dto/AggregationCountLikeUnlikeNoteMqDTO.java @@ -0,0 +1,32 @@ +package com.hanserwei.hannote.count.biz.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 聚合后的点赞、取消点赞笔记消息体 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class AggregationCountLikeUnlikeNoteMqDTO { + + /** + * 笔记发布者 ID + */ + private Long creatorId; + + /** + * 笔记 ID + */ + private Long noteId; + + /** + * 聚合后的计数 + */ + private Integer count; + +} \ No newline at end of file diff --git a/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/model/dto/CountCollectUnCollectNoteMqDTO.java b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/model/dto/CountCollectUnCollectNoteMqDTO.java index 9ca5f2b..f8a8878 100644 --- a/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/model/dto/CountCollectUnCollectNoteMqDTO.java +++ b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/model/dto/CountCollectUnCollectNoteMqDTO.java @@ -23,4 +23,9 @@ public class CountCollectUnCollectNoteMqDTO { private Integer type; private LocalDateTime createTime; + + /** + * 笔记发布者 ID + */ + private Long noteCreatorId; } \ No newline at end of file diff --git a/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/model/dto/CountLikeUnlikeNoteMqDTO.java b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/model/dto/CountLikeUnlikeNoteMqDTO.java index a01bf7c..cb57696 100644 --- a/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/model/dto/CountLikeUnlikeNoteMqDTO.java +++ b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/model/dto/CountLikeUnlikeNoteMqDTO.java @@ -23,4 +23,9 @@ public class CountLikeUnlikeNoteMqDTO { private Integer type; private LocalDateTime createTime; + + /** + * 笔记发布者 ID + */ + private Long noteCreatorId; } \ No newline at end of file diff --git a/han-note-count/han-note-count-biz/src/main/resources/mapperxml/UserCountDOMapper.xml b/han-note-count/han-note-count-biz/src/main/resources/mapperxml/UserCountDOMapper.xml index 4560939..ed1c31f 100644 --- a/han-note-count/han-note-count-biz/src/main/resources/mapperxml/UserCountDOMapper.xml +++ b/han-note-count/han-note-count-biz/src/main/resources/mapperxml/UserCountDOMapper.xml @@ -28,4 +28,16 @@ VALUES (#{userId}, #{count}) ON DUPLICATE KEY UPDATE following_total = following_total + (#{count}); + + + INSERT INTO t_user_count (user_id, like_total) + VALUES (#{userId}, #{count}) + ON DUPLICATE KEY UPDATE like_total = like_total + (#{count}); + + + + INSERT INTO t_user_count (user_id, collect_total) + VALUES (#{userId}, #{count}) + ON DUPLICATE KEY UPDATE collect_total = collect_total + (#{count}); + \ No newline at end of file diff --git a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/model/dto/CollectUnCollectNoteMqDTO.java b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/model/dto/CollectUnCollectNoteMqDTO.java index c0ff6bd..5d2c008 100644 --- a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/model/dto/CollectUnCollectNoteMqDTO.java +++ b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/model/dto/CollectUnCollectNoteMqDTO.java @@ -23,4 +23,9 @@ public class CollectUnCollectNoteMqDTO { private Integer type; private LocalDateTime createTime; + + /** + * 笔记发布者 ID + */ + private Long noteCreatorId; } diff --git a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/model/dto/LikeUnlikeNoteMqDTO.java b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/model/dto/LikeUnlikeNoteMqDTO.java index 3740dc5..49e7a2b 100644 --- a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/model/dto/LikeUnlikeNoteMqDTO.java +++ b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/model/dto/LikeUnlikeNoteMqDTO.java @@ -23,4 +23,9 @@ public class LikeUnlikeNoteMqDTO { private Integer type; private LocalDateTime createTime; + + /** + * 笔记发布者 ID + */ + private Long noteCreatorId; } \ No newline at end of file diff --git a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/service/impl/NoteServiceImpl.java b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/service/impl/NoteServiceImpl.java index 24ae338..8f4a4d7 100644 --- a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/service/impl/NoteServiceImpl.java +++ b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/service/impl/NoteServiceImpl.java @@ -571,7 +571,7 @@ public class NoteServiceImpl extends ServiceImpl implement public Response likeNote(LikeNoteReqVO likeNoteReqVO) { Long noteId = likeNoteReqVO.getId(); // 1. 校验被点赞的笔记是否存在 - checkNoteIsExist(noteId); + Long creatorId = checkNoteIsExistAndGetCreatorId(noteId); // 2. 判断目标笔记,是否已经点赞过 Long userId = LoginUserContextHolder.getUserId(); @@ -593,7 +593,7 @@ public class NoteServiceImpl extends ServiceImpl implement switch (noteLikeLuaResultEnum) { // Redis 中布隆过滤器不存在 case NOT_EXIST -> { - // TODO: 从数据库中校验笔记是否被点赞,并异步初始化布隆过滤器,设置过期时间 + //从数据库中校验笔记是否被点赞,并异步初始化布隆过滤器,设置过期时间 long count = noteLikeDOService.count(new LambdaQueryWrapper<>(NoteLikeDO.class) .eq(NoteLikeDO::getNoteId, noteId) .eq(NoteLikeDO::getStatus, LikeStatusEnum.LIKE.getCode())); @@ -689,6 +689,7 @@ public class NoteServiceImpl extends ServiceImpl implement .noteId(noteId) .type(LikeStatusEnum.LIKE.getCode()) // 点赞 .createTime(now) + .noteCreatorId(creatorId) .build(); // 构建消息,将DTO转换为JSON字符串设置到消息体中 Message message = MessageBuilder.withPayload(JsonUtils.toJsonString(likeUnlikeNoteMqDTO)).build(); @@ -717,7 +718,7 @@ public class NoteServiceImpl extends ServiceImpl implement Long noteId = unlikeNoteReqVO.getId(); // 1. 校验笔记是否真实存在 - checkNoteIsExist(noteId); + Long creatorId = checkNoteIsExistAndGetCreatorId(noteId); // 2. 校验笔记是否被点赞过 // 当前登录用户ID @@ -774,6 +775,7 @@ public class NoteServiceImpl extends ServiceImpl implement .noteId(noteId) .type(LikeUnlikeNoteTypeEnum.UNLIKE.getCode()) // 取消点赞笔记 .createTime(LocalDateTime.now()) + .noteCreatorId(creatorId) .build(); // 构建消息,将DTO转换为JSON字符串设置到消息体中 @@ -806,7 +808,7 @@ public class NoteServiceImpl extends ServiceImpl implement Long noteId = collectNoteReqVO.getId(); // 1. 校验被收藏的笔记是否存在 - checkNoteIsExist(noteId); + Long creatorId = checkNoteIsExistAndGetCreatorId(noteId); // 2. 判断目标笔记,是否已经收藏过 // 当前登录用户ID @@ -828,8 +830,7 @@ public class NoteServiceImpl extends ServiceImpl implement NoteCollectLuaResultEnum noteCollectLuaResultEnum = NoteCollectLuaResultEnum.valueOf(result); log.info("==> 【笔记收藏】Lua 脚本返回结果: {}", noteCollectLuaResultEnum); - assert noteCollectLuaResultEnum != null; - switch (noteCollectLuaResultEnum) { + switch (Objects.requireNonNull(noteCollectLuaResultEnum)) { // 布隆过滤器不存在 case NOT_EXIST -> { // 从数据库中校验笔记是否被收藏,并异步初始化布隆过滤器,设置过期时间 @@ -932,6 +933,7 @@ public class NoteServiceImpl extends ServiceImpl implement .noteId(noteId) .type(CollectUnCollectNoteTypeEnum.COLLECT.getCode()) // 收藏笔记 .createTime(now) + .noteCreatorId(creatorId) .build(); // 构建消息对象,并将 DTO 转成 Json 字符串设置到消息体中 @@ -965,7 +967,7 @@ public class NoteServiceImpl extends ServiceImpl implement Long noteId = unCollectNoteReqVO.getId(); // 1. 校验笔记是否真实存在 - checkNoteIsExist(noteId); + Long creatorId = checkNoteIsExistAndGetCreatorId(noteId); // 2. 校验笔记是否被收藏过 // 当前登录用户ID @@ -1021,6 +1023,7 @@ public class NoteServiceImpl extends ServiceImpl implement .noteId(noteId) .type(CollectUnCollectNoteTypeEnum.UN_COLLECT.getCode()) // 取消收藏笔记 .createTime(LocalDateTime.now()) + .noteCreatorId(creatorId) .build(); // 构建消息对象,并将 DTO 转成 Json 字符串设置到消息体中 @@ -1238,17 +1241,17 @@ public class NoteServiceImpl extends ServiceImpl implement } /** - * 校验笔记是否存在 + * 校验笔记是否存在,若存在,则获取笔记的发布者 ID * * @param noteId 笔记 ID */ - private void checkNoteIsExist(Long noteId) { + private Long checkNoteIsExistAndGetCreatorId(Long noteId) { // 先从本地缓存中检验 String findNoteDetailRspVOStrLocalCache = LOCAL_CACHE.getIfPresent(noteId); // 解析 JSON 为 FindNoteDetailRspVO FindNoteDetailRspVO findNoteDetailRspVO = JsonUtils.parseObject(findNoteDetailRspVOStrLocalCache, FindNoteDetailRspVO.class); - // 若缓存不存在 + // 若本地缓存不存在 if (Objects.isNull(findNoteDetailRspVO)) { // 从 Redis 中获取 String noteDetailRedisKey = RedisKeyConstants.buildNoteDetailKey(noteId); @@ -1260,10 +1263,15 @@ public class NoteServiceImpl extends ServiceImpl implement // 若 Redis 中不存在,则从数据库中获取 if (Objects.isNull(findNoteDetailRspVO)) { - boolean isExist = this.exists(new LambdaQueryWrapper<>(NoteDO.class) + // 查询笔记的发布者用户 ID + NoteDO noteDO = this.getOne(new LambdaQueryWrapper<>(NoteDO.class) + .select(NoteDO::getCreatorId) .eq(NoteDO::getId, noteId) .eq(NoteDO::getStatus, NoteStatusEnum.NORMAL.getCode())); - if (!isExist) { + // 笔记发布者用户 ID + Long creatorId = noteDO.getCreatorId(); + // 若数据库中也不存在,提示用户 + if (Objects.isNull(creatorId)) { throw new ApiException(ResponseCodeEnum.NOTE_NOT_FOUND); } // 缓存 @@ -1271,8 +1279,11 @@ public class NoteServiceImpl extends ServiceImpl implement FindNoteDetailReqVO findNoteDetailReqVO = FindNoteDetailReqVO.builder().id(noteId).build(); findNoteDetail(findNoteDetailReqVO); }); + return creatorId; } } + + return findNoteDetailRspVO.getCreatorId(); } /** diff --git a/hanserwei-framework/hanserwei-common/src/main/java/com/hanserwei/framework/common/utils/JsonUtils.java b/hanserwei-framework/hanserwei-common/src/main/java/com/hanserwei/framework/common/utils/JsonUtils.java index 3acd7c9..5335630 100755 --- a/hanserwei-framework/hanserwei-common/src/main/java/com/hanserwei/framework/common/utils/JsonUtils.java +++ b/hanserwei-framework/hanserwei-common/src/main/java/com/hanserwei/framework/common/utils/JsonUtils.java @@ -4,10 +4,12 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.type.CollectionType; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; +import java.util.List; import java.util.Map; public class JsonUtils { @@ -74,4 +76,23 @@ public class JsonUtils { // 将 JSON 字符串转换为 Map return OBJECT_MAPPER.readValue(jsonStr, OBJECT_MAPPER.getTypeFactory().constructMapType(Map.class, keyClass, valueClass)); } + + /** + * 将 JSON 字符串解析为指定类型的 List 对象 + * + * @param jsonStr JSON 字符串 + * @param clazz 目标对象类型 + * @param 目标对象类型 + * @return List 对象 + * @throws Exception 抛出异常 + */ + public static List parseList(String jsonStr, Class clazz) throws Exception { + // 使用 TypeReference 指定 List 的泛型类型 + return OBJECT_MAPPER.readValue(jsonStr, new TypeReference>() { + @Override + public CollectionType getType() { + return OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, clazz); + } + }); + } }