| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305 |
- 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<String, Object> redisTemplate;
- public RedisCacheAspect(RedisTemplate<String, Object> 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<String, Object> 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<String> 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;
- }
- }
|