package com.poyee.aspectj; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.parser.Feature; import com.poyee.annotation.RedisCache; import com.poyee.base.dto.BaseReq; import com.poyee.base.dto.Page; import com.poyee.base.dto.Result; import com.poyee.base.dto.UserInfo; import com.poyee.enums.CacheExpirePolicy; import com.poyee.util.DateUtils; import com.poyee.util.ServletUtils; import com.poyee.util.SpElUtils; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.annotation.Order; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; /** * Redis 缓存切面 */ @Slf4j @Aspect @Component @Order(1) public class RedisCacheAspect { private final RedisTemplate redisTemplate; public RedisCacheAspect(RedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; } @Around("@annotation(redisCache)") public Object doAround(ProceedingJoinPoint joinPoint, RedisCache redisCache) throws Throwable { String keyExpression = redisCache.key(); String keySource = redisCache.keySource(); CacheExpirePolicy expirePolicy = redisCache.expirePolicy(); long customExpireTime = redisCache.expireTime(); if (expirePolicy == CacheExpirePolicy.CUSTOM) { expirePolicy.setExpireTime(customExpireTime); } // 构建上下文变量 Map contextVars = new HashMap<>(); //如果获取不到用户信息, 判断方法参数中 doCache 是否为 true,如果是则解析方法参数中数据 进行缓存, 否则直接返回结果 Object[] args = joinPoint.getArgs(); MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer(); String[] paramNames = nameDiscoverer.getParameterNames(method); // 检查方法参数中是否有 doCache 参数且为 true boolean doCache = false; Object arg = null; for (int i = 0; i < args.length; i++) { if (paramNames != null && i < paramNames.length) { arg = args[i]; if(arg instanceof BaseReq && ((BaseReq) arg).isDoRedis()){ doCache = true; } } } // 从指定 source 获取变量值(如 userContext) switch (keySource) { case "userContext": //根据 List strings = SpElUtils.extractLabels(keyExpression); String keyWord = strings.get(0); if(doCache && Objects.nonNull(arg)){ Field declaredField = arg.getClass().getDeclaredField(keyWord); declaredField.setAccessible(true); Object valueObj = declaredField.get(arg); contextVars.put(keyWord, valueObj); } else { //获取当前用户信息 UserInfo userInfo = ServletUtils.getUserInfo(); try { Class aClass = userInfo.getClass(); Field declaredField = aClass.getDeclaredField(keyWord); declaredField.setAccessible(true); Object valueObj = declaredField.get(userInfo); contextVars.put(keyWord, valueObj); } catch (Exception e) { } } break; case "args": // Object[] args = joinPoint.getArgs(); // MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); // Method method = methodSignature.getMethod(); // DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer(); // String[] paramNames = nameDiscoverer.getParameterNames(method); for (int i = 0; i < args.length; i++) { if (paramNames != null && i < paramNames.length) { contextVars.put(paramNames[i], args[i]); } } break; default: // 自定义 source,可扩展 break; } // 解析 key String finalKey = SpElUtils.parseKey(keyExpression, contextVars); // 查询缓存 Object cachedValue = redisTemplate.opsForValue().get(finalKey); if (cachedValue != null) { //判断是否为JSONObject 进行转换 if(cachedValue instanceof String){ //获取过期时间 long expireTime = redisTemplate.getExpire(finalKey); log.debug("缓存命中[剩余时间:{} s],返回结果:{}", expireTime,(cachedValue.toString().length() > 200 ? cachedValue.toString().substring(0, 200) +"..." : cachedValue.toString())); //解析为json 并 取得 type 区分是 page 还是result String type = "result"; if(cachedValue.toString().startsWith("{")){ JSONObject jsonObject = JSONObject.parseObject((String) cachedValue); type = jsonObject.getString("type"); } if(type.equals("page")){ Page page = JSONObject.parseObject((String) cachedValue, Page.class, Feature.OrderedField); //设置缓存过期时间 page.setRedisCacheTimes(expireTime); //如果缓存key 为空 则设置 key if(StringUtils.isBlank(page.getRedisCacheKey())){ page.setRedisCacheKey(finalKey); } //如果是分页 则判断 结果是否排序 排序字段和规则, 对结果重新排序 // doCheckSortByPageOrderBy(page); return page; }else{ Result result = JSONObject.parseObject((String) cachedValue, Result.class, Feature.OrderedField); //设置缓存过期时间 result.setRedisCacheTimes(expireTime); //如果缓存key 为空 则设置 key if(StringUtils.isBlank(result.getRedisCacheKey())){ result.setRedisCacheKey(finalKey); } return result; } } return cachedValue; } // 执行目标方法 Object result = joinPoint.proceed(); // 判断返回值是否有效(非空、非异常) if (result == null || result instanceof Boolean && !(Boolean) result) { return result; } //处理 返回 code if((result.getClass().getName().equals("com.poyee.base.dto.Result") || result.getClass().getName().equals("com.poyee.base.dto.Page") ) && result.getClass().getDeclaredField("code") != null){ try{ Field codeField = result.getClass().getDeclaredField("code"); codeField.setAccessible(true); Object codeObj = codeField.get(result); if(codeObj != null && Integer.parseInt(codeObj.toString()) != 200 && Integer.parseInt(codeObj.toString()) != 0 ){ return result; } }catch (Exception e){ //忽略异常 } } // 设置缓存 long expireSeconds = expirePolicy.getExpireSeconds(); //设置 查询数据库操作时间 try{ Field codeField = result.getClass().getDeclaredField("lastOptTime"); codeField.setAccessible(true); Object codeObj = codeField.get(result); //设置当前时间为最后一次操作时间 if(codeObj == null){ codeField.set(result, DateUtils.dateTimeNow(DateUtils.YYYY_MM_DD_HH_MM_SS)); } //设置缓存key codeField = result.getClass().getDeclaredField("redisCacheKey"); codeField.setAccessible(true); codeField.set(result, finalKey); //设置缓存时间 codeField = result.getClass().getDeclaredField("redisCacheTimes"); codeField.setAccessible(true); codeField.set(result, expirePolicy.getExpireSeconds()); }catch (Exception e){ log.error("设置 查询数据库操作时间 异常",e); } String jsonString = JSONObject.toJSONString(result); if (expireSeconds > 0) { } else { // 默认设置缓存时间为1小时 (3600秒) expireSeconds = 3600; } redisTemplate.opsForValue().set(finalKey, jsonString, expireSeconds, java.util.concurrent.TimeUnit.SECONDS); //判断是否需要缓存key集合 if(redisCache.logKey() && StringUtils.isNotBlank(redisCache.logKeyRule()) && !Objects.equals(redisCache.logKeyRule(),"key")){ String logKeyExpression = redisCache.logKeyRule(); // 解析 key String finalLogKey = SpElUtils.parseKey(logKeyExpression, contextVars); // redis 缓存 设置 list add 值 redisTemplate.opsForList() .leftPush(finalLogKey, finalKey); // 设置列表过期时间,与缓存过期时间一致 redisTemplate.expire(finalLogKey, expireSeconds, java.util.concurrent.TimeUnit.SECONDS); } log.debug("缓存结果:{}", jsonString.length() > 200 ? jsonString.substring(0, 200) + "..." : jsonString); return result; } private void doCheckSortByPageOrderBy(Page page) { String orderByField = page.getOrderBy(); if(StringUtils.isNotBlank(orderByField)) { List rows = page.getRows(); boolean isDesc = page.isDesc(); // 是否降序 try { rows.sort((o1, o2) -> { if (o1 == null || o2 == null) return 0; // 使用反射获取 orderByField 的值 Object v1 = getFieldValue(o1, orderByField); Object v2 = getFieldValue(o2, orderByField); if (v1 == null && v2 == null) return 0; if (v1 == null) return isDesc ? 1 : -1; if (v2 == null) return isDesc ? -1 : 1; // 按类型安全比较 int result; if (v1 instanceof Comparable && v2.getClass() .isAssignableFrom(v1.getClass())) { result = ((Comparable) v1).compareTo(v2); } else { // 转为字符串比较(兜底) result = v1.toString() .compareTo(v2.toString()); } return isDesc ? -result : result; // 反转符号实现降序 }); } catch (Exception e) { log.warn("缓存分页数据排序失败,orderByField={}", orderByField, e); } } } // 使用反射获取字段值 private Object getFieldValue(Object obj, String fieldName) { if (obj == null || StringUtils.isBlank(fieldName)) return null; Class clazz = obj.getClass(); String[] fields = fieldName.split("\\."); // 支持嵌套属性 a.b.c try { for (String field : fields) { Field declaredField = findField(clazz, field); if (declaredField == null) return null; declaredField.setAccessible(true); obj = declaredField.get(obj); if (obj == null) break; clazz = obj.getClass(); } return obj; } catch (Exception e) { log.debug("获取对象 {} 字段 {} 值失败", obj.getClass().getSimpleName(), fieldName, e); return null; } } // 支持父类字段查找 private Field findField(Class clazz, String name) { while (clazz != null) { try { return clazz.getDeclaredField(name); } catch (NoSuchFieldException e) { clazz = clazz.getSuperclass(); } } return null; } }