java设置redis当天有效,次日失效

本文介绍了一种在Redis中实现仅当日有效的缓存策略,通过在key中加入日期标识,结合设置key过期时间,确保缓存只在当日生效。

今天业务场景需要设置redis当天有效,次日就不可使用redis的缓存。但是redis设置过期时间的方法都是设置key一段时间后失效(例如30分后失效)。

解决思路:将时间添加到key 中,在key的生成上来区分今天。

key ="项目名"+“yyyy-MM-dd”+"唯一标识"

设置key的过期时间,以防止key一直保存到redis中,不失效。

package com.fugui.star.business.business.service; import cn.hutool.core.bean.BeanUtil; import cn.idev.excel.util.StringUtils; import com.baomidou.mybatisplus.core.metadata.IPage; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fugui.cloud.common.entity.page.PageR; import com.fugui.cloud.common.result.R; import com.fugui.star.business.business.query.DwScanOrderPayRecordQuery; import com.fugui.star.business.business.vo.DwScanOrderPayRecordVO; import com.fugui.star.business.business.vo.PaymentPlatformStatVO; import com.fugui.star.persist.fg_business.dao.DwScanOrderPayRecordDAO; import com.fugui.star.persist.fg_business.dto.DwScanOrderPayRecordDTO; import com.fugui.star.persist.fg_business.dto.PaymentPlatformStatDTO; import com.fugui.star.persist.fg_business.search.DwScanOrderPayRecordSearch; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import java.time.*; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @Slf4j @Service public class DwScanOrderPayRecordService { @Resource private DwScanOrderPayRecordDAO dwScanOrderPayRecordDAO; @Resource private RedisTemplate<String, String> redisTemplate; private final ObjectMapper privateMapper = createPrivateMapper(); private ObjectMapper createPrivateMapper() { ObjectMapper mapper = new ObjectMapper(); mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); return mapper; } public R<PageR<DwScanOrderPayRecordVO>> page(DwScanOrderPayRecordQuery query) { DwScanOrderPayRecordSearch search = BeanUtil.copyProperties(query, DwScanOrderPayRecordSearch.class); IPage<DwScanOrderPayRecordDTO> page = dwScanOrderPayRecordDAO.page(search); PageR<DwScanOrderPayRecordVO> pageR = PageR.get(page).convert(dto -> { DwScanOrderPayRecordVO RecordVo = BeanUtil.copyProperties(dto, DwScanOrderPayRecordVO.class); return RecordVo; }); return R.success(pageR); } public R<List<PaymentPlatformStatVO>> stats(DwScanOrderPayRecordQuery query){ DwScanOrderPayRecordSearch Search = BeanUtil.copyProperties(query, DwScanOrderPayRecordSearch.class); ZonedDateTime now = ZonedDateTime.now(); if (Search.getEndCreateTime() == null) { Search.setEndCreateTime(Date.from(now.toInstant())); } if (Search.getBeginCreateTime() == null) { // 修复:使用ZonedDateTime进行月份运算 ZonedDateTime sixMonthsAgo = now.minusMonths(6); Search.setBeginCreateTime(Date.from(sixMonthsAgo.toInstant())); } // 转换为 LocalDateTime ZonedDateTime startZdt = Search.getBeginCreateTime().toInstant() .atZone(ZoneId.systemDefault()); ZonedDateTime endZdt = Search.getEndCreateTime().toInstant() .atZone(ZoneId.systemDefault()); List<YearMonth> requiredMonths = generateMonthRange(startZdt, endZdt); List<PaymentPlatformStatVO> resultList = new ArrayList<>(); Set<YearMonth> missingMonths = new HashSet<>(); //尝试从缓存获取已有月份数据 for (YearMonth month : requiredMonths) { String cacheKey = "paymentStats:" + month; try { String cachedData = redisTemplate.opsForValue().get(cacheKey); if (StringUtils.isNotBlank(cachedData)) { PaymentPlatformStatVO cachedVO = privateMapper.readValue(cachedData, PaymentPlatformStatVO.class); resultList.add(cachedVO); } else { missingMonths.add(month); } } catch (Exception e) { log.error("缓存读取失败 [月份: {}]", month, e); missingMonths.add(month); } } //如果所有月份都有缓存,直接返回结果 if (missingMonths.isEmpty()) { // 按年月倒序排序 resultList.sort(Comparator.comparing(PaymentPlatformStatVO::getStatYear) .thenComparing(PaymentPlatformStatVO::getStatMonth) .reversed()); return R.success(resultList); } try { //查询缺失月份的数据 List<PaymentPlatformStatDTO> dtoList; if (missingMonths.size() == requiredMonths.size()) { // 全部缺失时查询整个时间范围 dtoList = dwScanOrderPayRecordDAO.stats(Search); } else { // 只查询缺失月份 dtoList = new ArrayList<>(); for (YearMonth month : missingMonths) { // 构建单月查询条件 DwScanOrderPayRecordSearch monthQuery = new DwScanOrderPayRecordSearch(); BeanUtil.copyProperties(Search, monthQuery); // 使用 TemporalAdjusters 精确设置月首/月末时间 LocalDateTime monthStart = month.atDay(1).atStartOfDay(); LocalDateTime monthEnd = month.atEndOfMonth().atTime(23, 59, 59); monthQuery.setBeginCreateTime(Date.from(monthStart.atZone(ZoneId.systemDefault()).toInstant())); monthQuery.setEndCreateTime(Date.from(monthEnd.atZone(ZoneId.systemDefault()).toInstant())); dtoList.addAll(dwScanOrderPayRecordDAO.stats(monthQuery)); } } //处理查询结果并缓存 Map<String, PaymentPlatformStatVO> monthMap = new HashMap<>(); for (PaymentPlatformStatDTO dto : dtoList) { LocalDate localDate = dto.getStatDay().toInstant() .atZone(ZoneId.systemDefault()) .toLocalDate(); String monthKey = localDate.getYear() + "-" + String.format("%02d", localDate.getMonthValue()); PaymentPlatformStatVO monthVo = monthMap.computeIfAbsent(monthKey, k -> { PaymentPlatformStatVO vo = new PaymentPlatformStatVO(); vo.setStatYear(localDate.getYear()); vo.setStatMonth(localDate.getMonthValue()); vo.setMonthAlipayCount(0L); vo.setMonthWechatPayCount(0L); vo.setOtherPayCount(0L); return vo; }); monthVo.setMonthAlipayCount(monthVo.getMonthAlipayCount() + dto.getAlipayCount()); monthVo.setMonthWechatPayCount(monthVo.getMonthWechatPayCount() + dto.getWechatPayCount()); monthVo.setOtherPayCount(monthVo.getOtherPayCount() + dto.getOtherPayCount()); } //缓存缺失月份数据并添加到结果集 for (PaymentPlatformStatVO vo : monthMap.values()) { YearMonth month = YearMonth.of(vo.getStatYear(), vo.getStatMonth()); if (missingMonths.contains(month)) { vo.setMonthTotalOrderCount(vo.getMonthAlipayCount() + vo.getMonthWechatPayCount() + vo.getOtherPayCount()); try { String cacheKey = "paymentStats:" + month; String jsonData = privateMapper.writeValueAsString(vo); if (month.equals(YearMonth.from(now))) { // 当月缓存:设置次日凌晨过期 ZonedDateTime nextDayStart = now.toLocalDate().plusDays(1).atStartOfDay(ZoneId.systemDefault()); Duration duration = Duration.between(now, nextDayStart); redisTemplate.opsForValue().set(cacheKey, jsonData, duration.getSeconds(), TimeUnit.SECONDS); } else { // 非当月:设置为永久缓存 redisTemplate.opsForValue().set(cacheKey, jsonData); } } catch (Exception e) { log.error("缓存写入失败 [月份: {}]", month, e); } resultList.add(vo); } } //合并缓存数据和新增数据后排序 resultList.sort(Comparator.comparing(PaymentPlatformStatVO::getStatYear) .thenComparing(PaymentPlatformStatVO::getStatMonth) .reversed()); return R.success(resultList); } catch (Exception e) { return R.failed("按月统计失败:" + e.getMessage()); } } private List<YearMonth> generateMonthRange(ZonedDateTime start, ZonedDateTime end) { List<YearMonth> months = new ArrayList<>(); YearMonth startMonth = YearMonth.from(start); // 自动提取年月 YearMonth endMonth = YearMonth.from(end); for (YearMonth month = startMonth; !month.isAfter(endMonth); month = month.plusMonths(1)) { months.add(month); } return months; } public R<List<PaymentPlatformStatVO>> Stats(DwScanOrderPayRecordQuery query) { ZonedDateTime now = ZonedDateTime.now(); // 处理日期范围 if (query.getBeginCreateTime() == null) { ZonedDateTime sevenDaysAgo = now.minusDays(7); query.setBeginCreateTime(Date.from(sevenDaysAgo.toInstant())); } if (query.getEndCreateTime() == null) { query.setEndCreateTime(Date.from(now.toInstant())); } LocalDate startDate = query.getBeginCreateTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); LocalDate endDate = query.getEndCreateTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); List<LocalDate> requiredDates = startDate.datesUntil(endDate.plusDays(1)) .collect(Collectors.toList()); //创建默认结果集 Map<LocalDate, PaymentPlatformStatVO> resultMap = new HashMap<>(); for (LocalDate date : requiredDates) { // 创建默认VO,所有值为0 PaymentPlatformStatVO defaultVo = new PaymentPlatformStatVO(); defaultVo.setStatDay(Date.from(date.atStartOfDay(ZoneId.systemDefault()).toInstant())); defaultVo.setOtherPayCount(0L); defaultVo.setAlipayCount(0L); defaultVo.setWechatPayCount(0L); defaultVo.setTotalOrderCount(0L); resultMap.put(date, defaultVo); } List<PaymentPlatformStatVO> resultList = new ArrayList<>(); List<LocalDate> missingDates = new ArrayList<>(); Map<String, String> cacheKeys = new HashMap<>(); for (LocalDate date : requiredDates) { if (!date.isBefore(now.toLocalDate())) { missingDates.add(date); continue; } String dateKey = date.format(DateTimeFormatter.ISO_LOCAL_DATE); String cacheKey = "DailyOrderStats::" + dateKey; cacheKeys.put(dateKey, cacheKey); try { String cachedData = redisTemplate.opsForValue().get(cacheKey); if (StringUtils.isNotBlank(cachedData)) { PaymentPlatformStatVO vo = privateMapper.readValue(cachedData, PaymentPlatformStatVO.class); // 用缓存数据替换默认值 resultMap.put(date, vo); } else { missingDates.add(date); } } catch (Exception e) { log.error("缓存读取失败 [日期: {}]", date, e); missingDates.add(date); } } DwScanOrderPayRecordSearch Search = BeanUtil.copyProperties(query, DwScanOrderPayRecordSearch.class); try { // 处理缺失日期 if (!missingDates.isEmpty()) { // 获取最小和最大缺失日期 LocalDate minDate = missingDates.stream().min(LocalDate::compareTo).orElse(startDate); LocalDate maxDate = missingDates.stream().max(LocalDate::compareTo).orElse(endDate); // 设置查询时间范围 Search.setBeginCreateTime(Date.from(minDate.atStartOfDay(ZoneId.systemDefault()).toInstant())); Search.setEndCreateTime(Date.from(maxDate.atTime(23, 59, 59).atZone(ZoneId.systemDefault()).toInstant())); // 执行数据库查询 List<PaymentPlatformStatDTO> dtoList = dwScanOrderPayRecordDAO.stats(Search); for (PaymentPlatformStatDTO dto : dtoList) { LocalDate date = dto.getStatDay().toInstant() .atZone(ZoneId.systemDefault()) .toLocalDate(); if (!missingDates.contains(date)) continue; PaymentPlatformStatVO vo = new PaymentPlatformStatVO(); vo.setStatDay(dto.getStatDay()); vo.setAlipayCount(dto.getAlipayCount()); vo.setWechatPayCount(dto.getWechatPayCount()); vo.setOtherPayCount(dto.getOtherPayCount()); vo.setTotalOrderCount( dto.getAlipayCount() + dto.getWechatPayCount() + dto.getOtherPayCount() ); resultMap.put(date, vo); //缓存时跳过当日及之后日期 if (date.isBefore(now.toLocalDate())) { try { String dateKey = date.format(DateTimeFormatter.ISO_LOCAL_DATE); String cacheKey = cacheKeys.get(dateKey); String jsonData = privateMapper.writeValueAsString(vo); // 设置固定缓存时间(例如30天) redisTemplate.opsForValue().set( cacheKey, jsonData ); } catch (Exception e) { log.error("缓存写入失败 [日期: {}]", date, e); } } } } // 准备最终结果 resultList = new ArrayList<>(resultMap.values()); resultList.sort(Comparator.comparing( vo -> { // 如果statDay为空,则使用最小可能日期 return vo.getStatDay() != null ? vo.getStatDay().toInstant() : Instant.MIN; }, Comparator.nullsLast(Comparator.naturalOrder()) )); return R.success(resultList); } catch (Exception e) { log.error("订单统计异常", e); // 发生异常时返回默认结果 resultList = new ArrayList<>(resultMap.values()); resultList.sort(Comparator.comparing( vo -> { // 如果statDay为空,则使用最小可能日期 return vo.getStatDay() != null ? vo.getStatDay().toInstant() : Instant.MIN; }, Comparator.nullsLast(Comparator.naturalOrder()) )); return R.success(resultList); } } } 如何评价上述接口的service层
最新发布
08-14
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值