package com.ruoyi.approve.utils; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ruoyi.approve.mapper.ApproveProcessMapper; import com.ruoyi.approve.pojo.ApproveProcess; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.concurrent.TimeUnit; //基于redis的一个每日计数器 @Component public class DailyRedisCounter implements ApplicationRunner { private static final String KEY_PREFIX = "daily_counter:"; private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyyMMdd"); private final StringRedisTemplate redisTemplate; public DailyRedisCounter(StringRedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; } @Value("${ruoyi.approvalNumberPrefix}") private String approvalNumberPrefix; @Autowired private ApproveProcessMapper approveProcessMapper; @Override public void run(ApplicationArguments args) { syncFromDb(); } /** * 强制从数据库同步当前计数到 Redis */ public void syncFromDb() { String key = approvalNumberPrefix + ":approveNum"; String lockKey = key + ":sync_lock"; Boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 30, TimeUnit.SECONDS); if (Boolean.TRUE.equals(lock)) { try { long dbCount = getCountFromDb(); redisTemplate.opsForValue().set(key, String.valueOf(dbCount), calculateSecondsUntilMidnight(), TimeUnit.SECONDS); } finally { redisTemplate.delete(lockKey); } } } /** * 从数据库获取今日审批单总量 */ private long getCountFromDb() { StartAndEndDateDto dateTime = getDateTime(); LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(ApproveProcess::getApproveDelete, 0) .ge(ApproveProcess::getCreateTime, dateTime.getStartDate()) .lt(ApproveProcess::getCreateTime, dateTime.getEndDate()); Long count = approveProcessMapper.selectCount(wrapper); return count == null ? 0 : count; } /**查缓存 * 获取指定计数器在今日的数值,并自增1 * @param counterName 计数器名称(例如:login_count、order_count) * @return 今日自增后的计数值 */ public long incrementAndGet(String counterName) { String key = getKey(counterName); long count = redisTemplate.opsForValue().increment(key, 1); // 仅在第一次设置时设置过期时间(避免重复设置) if (count == 1) { long secondsUntilMidnight = calculateSecondsUntilMidnight(); redisTemplate.expire(key, secondsUntilMidnight, TimeUnit.SECONDS); } return count; } /** * 获取当前时间的 开始日期 ,结束日期 * @return */ public static StartAndEndDateDto getDateTime(){ LocalDate now = LocalDate.now(); String startDateTime = now.toString(); String endDateTime = now.plusDays(1).toString(); StartAndEndDateDto startAndEndDateDto = new StartAndEndDateDto(); startAndEndDateDto.setStartDate(startDateTime); startAndEndDateDto.setEndDate(endDateTime); return startAndEndDateDto; } /**查数据库 * 获取指定计数器在今日的数值,并自增1 * @return 今日自增后的计数值 */ public long incrementAndGetByDb() { String key = approvalNumberPrefix + ":approveNum"; // 获取现有值 String approveId = redisTemplate.opsForValue().get(key); if (approveId == null) { // 缓存不存在进行初始化 String lockKey = key + ":lock"; Boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS); if (Boolean.TRUE.equals(lock)) { try { approveId = redisTemplate.opsForValue().get(key); if (approveId == null) { long count = getCountFromDb(); long nextVal = count + 1; redisTemplate.opsForValue().set(key, String.valueOf(nextVal), calculateSecondsUntilMidnight(), TimeUnit.SECONDS); return nextVal; } } finally { redisTemplate.delete(lockKey); } } else { try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return incrementAndGetByDb(); } } return redisTemplate.opsForValue().increment(key); } /** * 获取指定计数器在今日的当前数值 * @param counterName 计数器名称 * @return 今日当前计数值,若不存在则返回0 */ public long getCurrentCount(String counterName) { String key = getKey(counterName); String value = redisTemplate.opsForValue().get(key); return value != null ? Long.parseLong(value) : 0; } /** * 计算距离次日凌晨的秒数 */ private long calculateSecondsUntilMidnight() { LocalDateTime now = LocalDateTime.now(); LocalDateTime midnight = now.toLocalDate().plusDays(1).atStartOfDay(); return java.time.Duration.between(now, midnight).getSeconds(); } /** * 生成Redis键 */ private String getKey(String counterName) { String today = LocalDate.now().format(DATE_FORMAT); return KEY_PREFIX + counterName + ":" + today; } }