diff --git a/src/main/java/cn/hezhaohui/thumb/job/SyncThumb2DBJob.java b/src/main/java/cn/hezhaohui/thumb/job/SyncThumb2DBJob.java new file mode 100644 index 0000000..1c30ef9 --- /dev/null +++ b/src/main/java/cn/hezhaohui/thumb/job/SyncThumb2DBJob.java @@ -0,0 +1,113 @@ +package cn.hezhaohui.thumb.job; + +import cn.hezhaohui.thumb.mapper.BlogMapper; +import cn.hezhaohui.thumb.model.entity.Thumb; +import cn.hezhaohui.thumb.model.enums.ThumbTypeEnum; +import cn.hezhaohui.thumb.service.ThumbService; +import cn.hezhaohui.thumb.util.RedisKeyUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.text.StrPool; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +/** + * 定时将 Redis 中的临时点赞数据同步到数据库 + * + */ +@Component +@Slf4j +public class SyncThumb2DBJob { + + @Resource + private ThumbService thumbService; + + @Resource + private BlogMapper blogMapper; + + @Resource + private RedisTemplate redisTemplate; + + @Scheduled(fixedRate = 10000) + @Transactional(rollbackFor = Exception.class) + public void run() { + log.info("开始执行"); + DateTime nowDate = DateUtil.date(); + // 如果秒数为0~9 则回到上一分钟的50秒 + int second = (DateUtil.second(nowDate) / 10 - 1) * 10; + if (second == -10) { + second = 50; + // 回到上一分钟 + nowDate = DateUtil.offsetMinute(nowDate, -1); + } + String date = DateUtil.format(nowDate, "HH:mm:") + second; + syncThumb2DBByDate(date); + log.info("临时数据同步完成"); + } + + public void syncThumb2DBByDate(String date) { + // 获取到临时点赞和取消点赞数据 + String tempThumbKey = RedisKeyUtil.getTempThumbKey(date); + Map allTempThumbMap = redisTemplate.opsForHash().entries(tempThumbKey); + boolean thumbMapEmpty = CollUtil.isEmpty(allTempThumbMap); + + // 同步 点赞 到数据库 + // 构建插入列表并收集blogId + Map blogThumbCountMap = new HashMap<>(); + if (thumbMapEmpty) { + return; + } + ArrayList thumbList = new ArrayList<>(); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + boolean needRemove = false; + for (Object userIdBlogIdObj : allTempThumbMap.keySet()) { + String userIdBlogId = (String) userIdBlogIdObj; + String[] userIdAndBlogId = userIdBlogId.split(StrPool.COLON); + Long userId = Long.valueOf(userIdAndBlogId[0]); + Long blogId = Long.valueOf(userIdAndBlogId[1]); + // -1 取消点赞,1 点赞 + Integer thumbType = Integer.valueOf(allTempThumbMap.get(userIdBlogId).toString()); + if (thumbType == ThumbTypeEnum.INCR.getValue()) { + Thumb thumb = new Thumb(); + thumb.setUserid(userId); + thumb.setBlogId(blogId); + thumbList.add(thumb); + } else if (thumbType == ThumbTypeEnum.DECR.getValue()) { + // 拼接查询条件,批量删除 + needRemove = true; + wrapper.or().eq(Thumb::getUserid, userId).eq(Thumb::getBlogId, blogId); + } else { + if (thumbType != ThumbTypeEnum.NON.getValue()) { + log.warn("数据异常:{}", userId + "," + blogId + "," + thumbType); + } + continue; + } + // 计算点赞增量 + blogThumbCountMap.put(blogId, blogThumbCountMap.getOrDefault(blogId, 0L) + thumbType); + } + // 批量插入 + thumbService.saveBatch(thumbList); + // 批量删除 + if (needRemove) { + thumbService.remove(wrapper); + } + // 批量更新博客点赞量 + if (!blogThumbCountMap.isEmpty()) { + blogMapper.batchUpdateThumbCount(blogThumbCountMap); + } + // 异步删除 + Thread.startVirtualThread(() -> { + redisTemplate.delete(tempThumbKey); + }); + } +}