✨Feat: 通过定时同步任务将 Redis 数据同步到数据库

This commit is contained in:
2025-10-23 15:21:17 +08:00
parent e7d8f2ba42
commit 1cc3d4f5b3
@@ -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<String, Object> 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<Object, Object> allTempThumbMap = redisTemplate.opsForHash().entries(tempThumbKey);
boolean thumbMapEmpty = CollUtil.isEmpty(allTempThumbMap);
// 同步 点赞 到数据库
// 构建插入列表并收集blogId
Map<Long, Long> blogThumbCountMap = new HashMap<>();
if (thumbMapEmpty) {
return;
}
ArrayList<Thumb> thumbList = new ArrayList<>();
LambdaQueryWrapper<Thumb> 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);
});
}
}