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<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 今日自增后的计数值
|
*/
|
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;
|
}
|
}
|