| | |
| | | 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 { |
| | | 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; |
| | |
| | | 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<ApproveProcess> 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 今日自增后的计数值 |
| | |
| | | } |
| | | |
| | | /** |
| | | * 获取当前时间的 开始日期 ,结束日期 |
| | | * @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 |
| | |
| | | * 计算距离次日凌晨的秒数 |
| | | */ |
| | | private long calculateSecondsUntilMidnight() { |
| | | LocalDate tomorrow = LocalDate.now().plusDays(1); |
| | | LocalDate midnight = tomorrow.atStartOfDay().toLocalDate(); |
| | | return java.time.Duration.between( |
| | | LocalDate.now().atTime(23, 59, 59), |
| | | midnight.atTime(0, 0, 0) |
| | | ).getSeconds() + 1; |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | LocalDateTime midnight = now.toLocalDate().plusDays(1).atStartOfDay(); |
| | | return java.time.Duration.between(now, midnight).getSeconds(); |
| | | } |
| | | |
| | | /** |