Files
Jarvis_java/src/main/java/cn/van/business/util/JDScheduleJob.java
雷欧(林平凡) 473e305bb7 1
2025-09-09 11:39:09 +08:00

649 lines
28 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package cn.van.business.util;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.van.business.model.jd.OrderRow;
import cn.van.business.model.pl.Comment;
import cn.van.business.model.wx.SuperAdmin;
import cn.van.business.repository.CommentRepository;
import cn.van.business.repository.OrderRowRepository;
import cn.van.business.util.jdReq.*;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.util.DateUtils;
import com.jd.open.api.sdk.DefaultJdClient;
import com.jd.open.api.sdk.JdClient;
import com.jd.open.api.sdk.domain.kplunion.OrderService.request.query.OrderRowReq;
import com.jd.open.api.sdk.domain.kplunion.OrderService.response.query.OrderRowResp;
import com.jd.open.api.sdk.request.kplunion.UnionOpenOrderRowQueryRequest;
import com.jd.open.api.sdk.response.kplunion.UnionOpenOrderRowQueryResponse;
import lombok.Getter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
import static cn.van.business.util.JDUtil.DATE_TIME_FORMATTER;
import static cn.van.business.util.WXUtil.super_admins;
/**
* @author Leo
* @version 1.0
* @create 2025/3/16 17:01
* @description
*/
@Component
public class JDScheduleJob {
private static final Logger logger = LoggerFactory.getLogger(JDScheduleJob.class);
private static final String SERVER_URL = "https://api.jd.com/routerjson";
//accessToken
private static final String ACCESS_TOKEN = "";
// 标记是否拉取过小时的订单空订单会set 一个 tag避免重复拉取
private static final String JD_REFRESH_TAG = "jd:refresh:tag:";
private final StringRedisTemplate redisTemplate;
private final OrderRowRepository orderRowRepository;
private final OrderUtil orderUtil;
private final JDUtil jdUtil;
private final CommentRepository commentRepository;
private final WXUtil wxUtil;
@Getter
@Value("${isRunning.wx}")
private String isRunning_wx;
@Getter
@Value("${isRunning.jd}")
private String isRunning_jd;
// 构造函数中注入StringRedisTemplate
@Autowired
public JDScheduleJob(WXUtil wxUtil, StringRedisTemplate redisTemplate, OrderRowRepository orderRowRepository, OrderUtil orderUtil, JDUtil jdUtil, CommentRepository commentRepository) {
this.redisTemplate = redisTemplate;
this.orderRowRepository = orderRowRepository;
this.orderUtil = orderUtil;
this.jdUtil = jdUtil;
this.commentRepository = commentRepository;
this.wxUtil = wxUtil;
}
/**
* 将 响应参数转化为 OrderRow并返回
*/
private OrderRow createOrderRow(OrderRowResp orderRowResp) {
OrderRow orderRow = new OrderRow();
orderRow.setOrderId(orderRowResp.getOrderId());
orderRow.setSkuId(orderRowResp.getSkuId());
orderRow.setSkuName(orderRowResp.getSkuName());
orderRow.setItemId(orderRowResp.getItemId());
orderRow.setSkuNum(orderRowResp.getSkuNum());
orderRow.setPrice(orderRowResp.getPrice());
orderRow.setActualCosPrice(orderRowResp.getActualCosPrice());
orderRow.setActualFee(orderRowResp.getActualFee());
orderRow.setEstimateCosPrice(orderRowResp.getEstimateCosPrice());
orderRow.setEstimateFee(orderRowResp.getEstimateFee());
orderRow.setSubSideRate(orderRowResp.getSubSideRate());
orderRow.setSubsidyRate(orderRowResp.getSubsidyRate());
orderRow.setCommissionRate(orderRowResp.getCommissionRate());
orderRow.setFinalRate(orderRowResp.getFinalRate());
orderRow.setOrderTime(DateUtils.parseDate(orderRowResp.getOrderTime()));
orderRow.setFinishTime(DateUtils.parseDate(orderRowResp.getFinishTime()));
orderRow.setOrderTag(orderRowResp.getOrderTag());
orderRow.setOrderEmt(orderRowResp.getOrderEmt());
orderRow.setUnionId(orderRowResp.getUnionId());
orderRow.setUnionRole(orderRowResp.getUnionRole());
orderRow.setUnionAlias(orderRowResp.getUnionAlias());
orderRow.setUnionTag(orderRowResp.getUnionTag());
orderRow.setTraceType(orderRowResp.getTraceType());
orderRow.setValidCode(orderRowResp.getValidCode());
orderRow.setPayMonth(orderRowResp.getPayMonth());
orderRow.setSiteId(orderRowResp.getSiteId());
orderRow.setParentId(orderRowResp.getParentId());
//GoodsInfo goodsInfo = orderRowResp.getGoodsInfo();
//GoodsInfoVO goodsInfoVO = new GoodsInfoVO();
//goodsInfoVO.setShopId(String.valueOf(goodsInfo.getShopId()));
//goodsInfoVO.setShopName(goodsInfo.getShopName());
//goodsInfoVO.setOwner(goodsInfo.getOwner());
//goodsInfoVO.setProductId(String.valueOf(goodsInfo.getProductId()));
//goodsInfoVO.setImageUrl(goodsInfo.getImageUrl());
//orderRow.setGoodsInfo(goodsInfoVO);
orderRow.setCallerItemId(orderRowResp.getCallerItemId());
orderRow.setPid(orderRowResp.getPid());
orderRow.setCid1(orderRowResp.getCid1());
orderRow.setCid2(orderRowResp.getCid2());
orderRow.setCid3(orderRowResp.getCid3());
orderRow.setChannelId(orderRowResp.getChannelId());
orderRow.setProPriceAmount(orderRowResp.getProPriceAmount());
orderRow.setSkuFrozenNum(orderRowResp.getSkuFrozenNum());
orderRow.setSkuReturnNum(orderRowResp.getSkuReturnNum());
orderRow.setSkuTag(orderRowResp.getSkuTag());
orderRow.setPositionId(orderRowResp.getPositionId());
orderRow.setPopId(orderRowResp.getPopId());
orderRow.setRid(orderRowResp.getRid());
orderRow.setPlus(orderRowResp.getPlus());
orderRow.setCpActId(orderRowResp.getCpActId());
orderRow.setGiftCouponKey(orderRowResp.getGiftCouponKey());
orderRow.setModifyTime(new Date());
orderRow.setSign(orderRowResp.getSign());
orderRow.setBalanceExt(orderRowResp.getBalanceExt());
orderRow.setExpressStatus(orderRowResp.getExpressStatus());
orderRow.setExt1(orderRowResp.getExt1());
orderRow.setSubUnionId(orderRowResp.getSubUnionId());
orderRow.setGiftCouponOcsAmount(orderRowResp.getGiftCouponOcsAmount());
orderRow.setTraceType(orderRowResp.getTraceType());
orderRow.setExpressStatus(orderRowResp.getExpressStatus());
orderRow.setTraceType(orderRowResp.getTraceType());
orderRow.setId(orderRowResp.getId());
orderRow.setValidCode(orderRowResp.getValidCode());
orderRow.setExpressStatus(orderRowResp.getExpressStatus());
orderRow.setTraceType(orderRowResp.getTraceType());
return orderRow;
}
/**
* 根据指定的日期时间拉取订单
*
* @param startTime 开始时间
* isRealTime 是否是实时订单 是的话不会判断是否拉取过
* page 分页页码
* isRealTime 是否是分钟级订单 分钟的每次加10分钟小时每小时加1小时
*/
public UnionOpenOrderRowQueryResponse fetchOrdersForDateTime(LocalDateTime startTime, boolean isRealTime, Integer page, boolean isMinutes, String appKey, String secretKey) {
LocalDateTime endTime = isMinutes ? startTime.plusMinutes(10) : startTime.plusHours(1);
String hourMinuteTag = isRealTime ? "minute" : "hour";
//String oldTimeTag = JD_REFRESH_TAG + startTime.format(DATE_TIME_FORMATTER);
String newTimeTag = JD_REFRESH_TAG + appKey + ":" + startTime.format(DATE_TIME_FORMATTER);
HashOperations<String, String, String> hashOps = redisTemplate.opsForHash();
// 检查这个小时或分钟是否已经被处理过
if (hashOps.hasKey(newTimeTag, hourMinuteTag)) {
// 0007需要暴力拉取
if (!isMinutes && !isRealTime) {
return null;
}
}
// 调用 API 以拉取订单
try {
UnionOpenOrderRowQueryResponse unionOpenOrderRowQueryResponse = getUnionOpenOrderRowQueryResponse(startTime, endTime, page, appKey, secretKey);
// 历史的订单才进行标记为已拉取,小时分钟的都进行拉取并且标记
if (!isRealTime) {
// 只有没有订单的才进行标记为已拉取
if (unionOpenOrderRowQueryResponse != null && unionOpenOrderRowQueryResponse.getQueryResult() != null && unionOpenOrderRowQueryResponse.getQueryResult().getData() == null) {
hashOps.put(newTimeTag, hourMinuteTag, "done");
logger.info(" 账号 {} -- 没有订单 -- 开始时间:{} --- 结束时间:{}", appKey.substring(appKey.length() - 4), startTime.format(DATE_TIME_FORMATTER), endTime.format(DATE_TIME_FORMATTER));
}
}
// 打印方法调用和开始结束时间
//if (isRealTime && (LocalDateTime.now().getMinute() % 10 == 0)) {
// logger.debug(" {} --- 拉取订单, 分钟还是秒 {} , 开始时间:{} --- 结束时间:{}", appKey.substring(appKey.length() - 4), hourMinuteTag, startTime.format(DATE_TIME_FORMATTER), endTime.format(DATE_TIME_FORMATTER));
//}
return unionOpenOrderRowQueryResponse;
} catch (Exception e) {
return null;
}
}
// 响应校验方法
private boolean isValidResponse(UnionOpenOrderRowQueryResponse response) {
return response != null && response.getQueryResult() != null && response.getQueryResult().getCode() == 200 && response.getQueryResult().getData() != null;
}
// 订单处理方法
private void processOrderResponse(UnionOpenOrderRowQueryResponse response, SuperAdmin admin) {
Arrays.stream(response.getQueryResult().getData()).parallel().map(this::createOrderRow).forEach(orderRowRepository::save);
}
public int fetchOrders(OrderFetchStrategy strategy, String appKey, String secretKey) {
TimeRange range = strategy.calculateRange(LocalDateTime.now());
int count = 0;
// 复用原有的抓取逻辑
LocalDateTime current = range.getStart();
while (!current.isAfter(range.getEnd())) {
// 调用分页抓取API...
Integer pageIndex = 1;
boolean hasMore = true;
while (hasMore) {
try {
// 30-60 天 ,非实时,非分钟
UnionOpenOrderRowQueryResponse response = fetchOrdersForDateTime(current, strategy.isRealTime(), pageIndex, false, appKey, secretKey);
if (response != null && response.getQueryResult() != null) {
if (response.getQueryResult().getCode() == 200) {
OrderRowResp[] orderRowResps = response.getQueryResult().getData();
if (orderRowResps != null) {
for (OrderRowResp orderRowResp : orderRowResps) {
if (orderRowResp != null) { // Check each orderRowResp is not null
OrderRow orderRow = createOrderRow(orderRowResp);
// Ensure orderRow is not null after creation
orderRowRepository.save(orderRow);
count++;
}
}
}
hasMore = Boolean.TRUE.equals(response.getQueryResult().getHasMore());
} else {
hasMore = false;
}
} else {
hasMore = false;
}
} catch (Exception e) {
hasMore = false; // Optionally break out of the while loop if required
}
if (hasMore) pageIndex++;
}
current = current.plusHours(1);
}
return count;
}
/**
* 实时刷新最近10分钟的订单Resilience4j限流集成
*/
@Scheduled(cron = "0 * * * * ?")
public void fetchLatestOrder() {
if (isRunning_jd.equals("true")) {
LocalDateTime now = LocalDateTime.now();
LocalDateTime startTime = now.minusMinutes(10).withSecond(0).withNano(0);
super_admins.values().parallelStream().forEach(admin -> {
if (Util.isAnyEmpty(admin.getAppKey(), admin.getSecretKey())) return;
try {
UnionOpenOrderRowQueryResponse response = fetchOrdersForDateTime(startTime, true, 1, true, admin.getAppKey(), admin.getSecretKey());
if (isValidResponse(response)) {
processOrderResponse(response, admin);
}
} catch (JDUtil.RateLimitExceededException e) {
logger.warn("[限流] {} 请求频率受限", admin.getName());
} catch (Exception e) {
logger.error("{} 订单抓取异常: {}", admin.getName(), e.getMessage());
}
});
}
}
/**
* 扫描订单发送到微信
* 每分钟的30秒执行一次
*/
@Scheduled(cron = "3 * * * * ?")
public void sendOrderToWx() {
if (isRunning_wx.equals("true")) {
//long start = System.currentTimeMillis();
int[] validCodes = {-1};
// 只要三个月的,更多的也刷新不出来的
Date threeMonthsAgo = Date.from(LocalDateTime.now().minusMonths(3).atZone(ZoneId.systemDefault()).toInstant());
List<OrderRow> orderRows = orderRowRepository.findByValidCodeNotInAndOrderTimeAfter(validCodes, threeMonthsAgo);
for (OrderRow orderRow : orderRows) {
orderUtil.orderToWx(orderRow, true, false);
}
//logger.info("扫描订单发送到微信耗时:{} ms, 订单数:{} ", System.currentTimeMillis() - start, orderRows.size());
}
}
/**
* 一天拉取三次 30天到60天前的订单
*/
@Scheduled(cron = "0 0 */4 * * ?")
public void fetchHistoricalOrders3090() {
if (isRunning_jd.equals("true")) {
try {
OrderFetchStrategy strategy = new Days3090Strategy();
for (SuperAdmin admin : super_admins.values()) {
try {
if (Util.isAnyEmpty(admin.getAppKey(), admin.getSecretKey())) {
continue;
}
int count = fetchOrders(strategy, admin.getAppKey(), admin.getSecretKey());
logger.info("账号{} 3090订单拉取完成新增{}条", admin.getName(), count);
} catch (Exception e) {
logger.error("账号 {} 拉取异常: {}", admin.getName(), e.getMessage());
}
}
} catch (Exception ex) {
logger.error("策略执行异常", ex);
}
}
}
/**
* 一天拉取6次 14天到30天前的订单
*/
@Scheduled(cron = "0 0 * * * ?")
public void fetchHistoricalOrders1430() {
if (isRunning_jd.equals("true")) {
try {
OrderFetchStrategy strategy = new Days1430Strategy(); // 需补充Days1430Strategy实现
for (SuperAdmin admin : super_admins.values()) {
try {
if (Util.isAnyEmpty(admin.getAppKey(), admin.getSecretKey())) {
continue;
}
int count = fetchOrders(strategy, admin.getAppKey(), admin.getSecretKey());
logger.info("账号{} 1430订单拉取完成新增{}条", admin.getName(), count);
} catch (Exception e) {
logger.error("账号 {} 拉取异常: {}", admin.getName(), e.getMessage());
}
}
} catch (Exception ex) {
logger.error("1430策略执行异常", ex);
}
}
}
/**
* 每10分钟拉取07-14天的订单
*/
@Scheduled(cron = "0 0 * * * ?")
public void fetchHistoricalOrders0714() {
if (isRunning_jd.equals("true")) {
try {
OrderFetchStrategy strategy = new Days0714Strategy();
super_admins.values().parallelStream().forEach(admin -> {
if (Util.isAnyEmpty(admin.getAppKey(), admin.getSecretKey())) return;
try {
int count = fetchOrders(strategy, admin.getAppKey(), admin.getSecretKey());
logger.info("账号{} 0714订单拉取完成新增{}条", admin.getName(), count);
} catch (Exception e) {
logger.error("账号 {} 0714拉取异常: {}", admin.getName(), e.getMessage());
}
});
} catch (Exception ex) {
logger.error("0714策略执行异常", ex);
}
}
}
@Scheduled(cron = "30 */10 * * * ?")
public void fetchHistoricalOrders0007() {
if (isRunning_jd.equals("true")) {
try {
OrderFetchStrategy strategy = new Days0007Strategy();
super_admins.values().parallelStream().forEach(admin -> {
if (Util.isAnyEmpty(admin.getAppKey(), admin.getSecretKey())) return;
try {
int count = fetchOrders(strategy, admin.getAppKey(), admin.getSecretKey());
logger.info("账号{} 0007订单拉取完成新增{}条", admin.getName(), count);
} catch (JDUtil.RateLimitExceededException e) {
logger.warn("[限流] {} 0007请求受限", admin.getName());
} catch (Exception e) {
logger.error("账号{}0007拉取异常: {}", admin.getName(), e.getMessage());
}
});
} catch (Exception ex) {
logger.error("0007策略执行异常", ex);
}
}
}
/**
* 获取订单列表
*
* @param start 开始时间
* @param end 结束时间
* @return
* @throws Exception
*/
public UnionOpenOrderRowQueryResponse getUnionOpenOrderRowQueryResponse(LocalDateTime start, LocalDateTime end, Integer pageIndex, String appKey, String secretKey) throws Exception {
String startTime = start.format(DATE_TIME_FORMATTER);
String endTime = end.format(DATE_TIME_FORMATTER);
// 模拟 API 调用
//System.out.println("调用API - 从 " + startTime
// + " 到 " + endTime);
// 实际的 API 调用逻辑应在这里进行
JdClient client = new DefaultJdClient(SERVER_URL, ACCESS_TOKEN, appKey, secretKey);
UnionOpenOrderRowQueryRequest request = new UnionOpenOrderRowQueryRequest();
OrderRowReq orderReq = new OrderRowReq();
orderReq.setPageIndex(pageIndex);
orderReq.setPageSize(200);
orderReq.setStartTime(startTime);
orderReq.setEndTime(endTime);
orderReq.setType(1);
request.setOrderReq(orderReq);
request.setVersion("1.0");
request.setSignmethod("md5");
// 时间戳格式为yyyy-MM-dd HH:mm:ss时区为GMT+8。API服务端允许客户端请求最大时间误差为10分钟
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
request.setTimestamp(simpleDateFormat.format(date));
return client.execute(request);
}
//@Scheduled(cron = "0 0 8-20 * * ?") // 每天从 8:00 到 20:00每小时执行一次
public void fetchPL() {
logger.info("开始执行fetchPL任务");
// 设置每天最多执行 3 次
Set<String> executedHours = getExecutedHoursFromRedis(); // 从 Redis 获取已执行的小时数
LocalDateTime now = LocalDateTime.now();
String currentHour = String.valueOf(now.getHour());
// 如果今天已经执行了3次则跳过
if (executedHours.size() >= 2) {
logger.info("今天已经执行了2次跳过本次任务");
return;
}
// 随机决定是否执行本次任务(例如 50% 概率)
if (new Random().nextBoolean()) {
logger.info("执行fetchPL任务");
// 执行任务逻辑
executeFetchPL();
// 记录该小时已执行
executedHours.add(currentHour);
saveExecutedHoursToRedis(executedHours); // 存入 Redis
}
}
private void executeFetchPL() {
HashMap<String, String> productTypeMap = jdUtil.getProductTypeMap();
int usedCommentCount;
int canUseComentCount;
int addCommentCount;
for (Map.Entry<String, String> entry : productTypeMap.entrySet()) {
// 随机睡眠1-5分钟
int sleepTime = new Random().nextInt(3000) + 60;
try {
Thread.sleep(sleepTime * 1000);
} catch (InterruptedException e) {
logger.error("线程中断", e);
}
String product_id = entry.getKey();
List<Comment> availableComments = commentRepository.findByProductIdAndIsUseNotAndPictureUrlsIsNotNull(product_id, 1);
List<Comment> usedComments = commentRepository.findByProductIdAndIsUseNotAndPictureUrlsIsNotNull(product_id, 0);
canUseComentCount = availableComments.size();
usedCommentCount = usedComments.size();
if (canUseComentCount > 5) {
logger.info("商品{} 评论可用数量大于5{}", product_id, canUseComentCount);
return;
}
try {
String fetchUrl = "http://192.168.8.6:5000/fetch_comments?product_id=" + product_id;
// 用hutool发起post请求
HttpResponse response = HttpRequest.post(fetchUrl).timeout(60000).execute();
logger.info("fetchUrl: {}", fetchUrl);
// code = 200 表示成功,-200 表示失败
if (response.getStatus() == 200) {
// ✅ 关键修改:重新从数据库中查询,而不是使用内存中的 fetchedComments
availableComments = commentRepository.findByProductIdAndIsUseNotAndPictureUrlsIsNotNull(product_id, 1);
if (!availableComments.isEmpty()) {
addCommentCount = availableComments.size() - canUseComentCount;
logger.info("自动刷新并且获取评论成功");
logger.info("型号{} 总评论数量 {} 可用数量 {} 新增评论数量:{}", entry.getValue(), availableComments.size() + usedCommentCount, canUseComentCount, addCommentCount);
}
} else if (response.getStatus() == -200) {
return;
}
} catch (Exception e) {
logger.error("调用外部接口获取评论失败", e);
return;
}
}
}
private Set<String> getExecutedHoursFromRedis() {
String key = "fetchPL:executedHours";
HashOperations<String, String, String> hashOps = redisTemplate.opsForHash();
return hashOps.entries(key).keySet();
}
private void saveExecutedHoursToRedis(Set<String> hours) {
String key = "fetchPL:executedHours";
HashOperations<String, String, String> hashOps = redisTemplate.opsForHash();
hashOps.putAll(key, hours.stream().collect(Collectors.toMap(h -> h, h -> "1")));
}
@Scheduled(cron = "0 0 0 * * ?")
public void checkGiftCouponsExpiry() {
Set<String> keys = redisTemplate.keys("gift_coupon:*");
if (keys.isEmpty()) return;
for (String key : keys) {
Map<Object, Object> entries = redisTemplate.opsForHash().entries(key);
if (entries.isEmpty()) continue;
for (Map.Entry<Object, Object> entry : entries.entrySet()) {
String giftJson = (String) entry.getValue();
JSONObject gift = JSON.parseObject(giftJson);
String giftKey = gift.getString("giftKey");
String skuName = gift.getString("skuName");
String owner = gift.getString("owner");
LocalDateTime expireTime = LocalDateTime.parse(gift.getString("expireTime"), DateTimeFormatter.ISO_DATE_TIME);
boolean isAboutToExpire = false;
if ("g".equals(owner)) {
// 自营:当天过期
isAboutToExpire = !expireTime.isAfter(LocalDateTime.now().with(LocalTime.MAX));
} else if ("p".equals(owner)) {
// POP7天后过期提前一天提醒
isAboutToExpire = ChronoUnit.HOURS.between(LocalDateTime.now(), expireTime) <= 24;
}
if (isAboutToExpire) {
String message = String.format("[礼金提醒]\n商品%s\n礼金Key%s\n类型%s\n将在 %s 过期", skuName, giftKey, "g".equals(owner) ? "自营" : "POP", expireTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")));
wxUtil.sendTextMessage(WXUtil.default_super_admin_wxid, message, 1, "bot", false);
redisTemplate.delete(key);
}
}
}
}
/**
* 清理三个月前的Redis hash数据
* 修复了时间解析异常的问题
*/
@Scheduled(cron = "0 45 11 * * ?") // 每月1日的凌晨3点执行
public void cleanOldRedisHashData() {
try {
// 获取三个月前的时间
LocalDateTime threeMonthsAgo = LocalDateTime.now().minusMonths(3);
// 获取所有以JD_REFRESH_TAG开头的键
Set<String> keys = redisTemplate.keys(JD_REFRESH_TAG + "*");
if (keys != null && !keys.isEmpty()) {
for (String key : keys) {
try {
// 提取时间部分,处理两种格式:
// 1. jd:refresh:tag:hash值:2025-02-02 16:00:00
// 2. jd:refresh:tag:2024-11-30 09:26:00
String timePart;
// 检查是否包含hash值32位十六进制字符串
if (key.matches("jd:refresh:tag:[a-f0-9]{32}:[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}")) {
// 格式jd:refresh:tag:hash值:时间
timePart = key.substring(key.lastIndexOf(":") + 1);
} else if (key.matches("jd:refresh:tag:[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}")) {
// 格式jd:refresh:tag:时间
timePart = key.substring("jd:refresh:tag:".length());
} else {
logger.warn("无法识别Redis键格式{}", key);
continue;
}
LocalDateTime time;
try {
// 解析为完整的时间格式 yyyy-MM-dd HH:mm:ss
time = LocalDateTime.parse(timePart, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
} catch (DateTimeParseException e) {
logger.warn("无法解析Redis键时间{},时间部分:{}", key, timePart);
continue;
}
// 检查是否在三个月前
if (time.isBefore(threeMonthsAgo)) {
redisTemplate.delete(key);
logger.info("已删除过期的Redis键{}", key);
}
} catch (Exception e) {
logger.warn("解析Redis键时间失败{}", key, e);
}
}
}
} catch (Exception e) {
logger.error("清理Redis hash数据时发生错误", e);
}
}
}