Compare commits
3
Commits
862893a5a1
...
c7375f3f99
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7375f3f99 | ||
|
|
1cc3d4f5b3 | ||
|
|
e7d8f2ba42 |
@@ -3,9 +3,11 @@ package cn.hezhaohui.thumb;
|
|||||||
import org.mybatis.spring.annotation.MapperScan;
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
@MapperScan("cn.hezhaohui.thumb.mapper")
|
@MapperScan("cn.hezhaohui.thumb.mapper")
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
|
@EnableScheduling
|
||||||
public class Application {
|
public class Application {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(Application.class, args);
|
SpringApplication.run(Application.class, args);
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package cn.hezhaohui.thumb.job;
|
||||||
|
|
||||||
|
import cn.hezhaohui.thumb.constant.ThumbConstant;
|
||||||
|
import cn.hezhaohui.thumb.util.RedisKeyUtil;
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.util.ObjUtil;
|
||||||
|
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 java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定时将 Redis 中的临时点赞数据同步到数据库的补偿措施
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class SyncThumb2DBCompensatoryJob {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RedisTemplate<String, Object> redisTemplate;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SyncThumb2DBJob syncThumb2DBJob;
|
||||||
|
|
||||||
|
@Scheduled(cron = "0 0 2 * * *")
|
||||||
|
public void run() {
|
||||||
|
log.info("开始补偿数据");
|
||||||
|
Set<String> thumbKeys = redisTemplate.keys(RedisKeyUtil.getTempThumbKey("") + "*");
|
||||||
|
Set<String> needHandleDataSet = new HashSet<>();
|
||||||
|
thumbKeys.stream().filter(ObjUtil::isNotNull).forEach(thumbKey -> needHandleDataSet.add(thumbKey.replace(ThumbConstant.TEMP_THUMB_KEY_PREFIX.formatted(""), "")));
|
||||||
|
|
||||||
|
if (CollUtil.isEmpty(needHandleDataSet)) {
|
||||||
|
log.info("没有需要补偿的临时数据");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 补偿数据
|
||||||
|
for (String date : needHandleDataSet) {
|
||||||
|
syncThumb2DBJob.syncThumb2DBByDate(date);
|
||||||
|
}
|
||||||
|
log.info("临时数据补偿完成");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,9 @@ package cn.hezhaohui.thumb.mapper;
|
|||||||
|
|
||||||
import cn.hezhaohui.thumb.model.entity.Blog;
|
import cn.hezhaohui.thumb.model.entity.Blog;
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author 23117
|
* @author 23117
|
||||||
@@ -10,7 +13,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
|||||||
* @Entity cn.hezhaohui.thumb.model.entity.Blog
|
* @Entity cn.hezhaohui.thumb.model.entity.Blog
|
||||||
*/
|
*/
|
||||||
public interface BlogMapper extends BaseMapper<Blog> {
|
public interface BlogMapper extends BaseMapper<Blog> {
|
||||||
|
void batchUpdateThumbCount(@Param("countMap") Map<Long, Long> countMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,4 +19,17 @@
|
|||||||
id,userId,title,coverImg,content,thumbCount,
|
id,userId,title,coverImg,content,thumbCount,
|
||||||
createTime,updateTime
|
createTime,updateTime
|
||||||
</sql>
|
</sql>
|
||||||
|
<update id="batchUpdateThumbCount">
|
||||||
|
UPDATE blog
|
||||||
|
SET thumbCount = thumbCount + CASE id
|
||||||
|
<foreach collection="countMap.entrySet()" item="value" index="key">
|
||||||
|
WHEN #{key} THEN #{value}
|
||||||
|
</foreach>
|
||||||
|
END
|
||||||
|
WHERE id IN
|
||||||
|
<foreach collection="countMap.keySet()" item="id" open="(" separator="," close=")">
|
||||||
|
#{id}
|
||||||
|
</foreach>
|
||||||
|
</update>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
Reference in New Issue
Block a user