|
- package com.iformall.aop;
-
-
- import com.alibaba.fastjson.JSONObject;
- import com.iformall.annotation.RedisCache;
- import com.iformall.common.ErrorCode;
- import com.iformall.domain.po.WxCUser;
- import com.iformall.domain.po.base.BaseCUserEntity;
- import com.iformall.exception.MallinkException;
- import com.iformall.service.CUserTokenService;
- import com.iformall.utils.HashUtil;
- import com.iformall.utils.RedisLock;
- 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.annotation.Pointcut;
- import org.aspectj.lang.reflect.MethodSignature;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.data.redis.core.StringRedisTemplate;
- import org.springframework.data.redis.core.ValueOperations;
- import org.springframework.stereotype.Component;
- import org.springframework.web.context.request.RequestContextHolder;
- import org.springframework.web.context.request.ServletRequestAttributes;
-
- import javax.servlet.http.HttpServletRequest;
- import java.lang.reflect.Method;
- import java.util.Arrays;
- import java.util.Objects;
-
- @Slf4j
- @Aspect
- @Component
- public class RedisCacheAspect {
-
- /**
- * 参数分隔符 param1|param2|param3
- **/
- private static final String DELIMITER_PARAMS = "|";
-
- /**
- * key 分隔符
- */
- private static final String DELIMITER_KEY = ":";
-
- @Autowired
- private StringRedisTemplate stringRedisTemplate;
-
- @Autowired
- RedisLock redisLock;
-
- @Autowired
- private CUserTokenService cUserTokenService;
-
- @Pointcut("@annotation(com.iformall.annotation.RedisCache)")
- public void cacheAspect() {
- }
-
- @Around("cacheAspect()")
- public Object round(ProceedingJoinPoint jp) throws Throwable {
- HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
- // 查询token信息
- //从header中获取token
- String token = request.getHeader("token");
- //如果header中不存在token,则从参数中获取token
- if (StringUtils.isBlank(token)) {
- token = request.getParameter("token");
- }
- if(StringUtils.isBlank(token) || "null".equals(token) || "undefined".equals(token)){
- token="";
- //throw new MallinkException(ErrorCode.NET_TOKEN_EMPTY.getCode(),"token为空["+token+"]");
- }
- log.info("token>>>>>>>>>>>>>>>"+token);
- String tenantId;
- BaseCUserEntity cUser = null ;
- if(StringUtils.isNotBlank(token)) {
- cUser = cUserTokenService.getByToken(token);
- }
- if (Objects.isNull(cUser) || cUser.getExpireTime().getTime() < System.currentTimeMillis()) {
- tenantId = "0";
- } else {
- tenantId = cUser.getTenantId();
- }
-
- // 请求参数
- Object[] args = jp.getArgs();
- // 接口 返回结果
- Object result;
- String redisKey;
-
- //得到注释的名称
- MethodSignature signature = (MethodSignature) jp.getSignature();
- //判断tag是否在用户权限中 如果存在加入参数查询
- Method method = signature.getMethod();
- RedisCache cache = method.getAnnotation(RedisCache.class) ;
- if (cache.autoKey()) {
- // 构建 redisKey => reqPath:tenantId:md5(param1_param2_param3)
- String requestUrl = request.getRequestURI();
- // 接口路径 reqPath => /api/xxx
- String reqPath = requestUrl.substring(requestUrl.indexOf("/api/"));
- if (cache.md5()) {
- String md5Before = StringUtils.join(args, DELIMITER_PARAMS);
- redisKey = StringUtils.join(reqPath, DELIMITER_KEY, tenantId, DELIMITER_KEY, HashUtil.md5(StringUtils.join(token, md5Before, DELIMITER_PARAMS)));
- } else {
- redisKey = StringUtils.join(reqPath, DELIMITER_KEY, tenantId, DELIMITER_KEY, StringUtils.join(token,args, DELIMITER_PARAMS));
- }
- } else {
- redisKey = cache.key();
- }
-
- // redisKey 不存在,获取接口数据并返回
- if (StringUtils.isBlank(redisKey)) {
- result = jp.proceed(args);
- } else {
- ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();
- String value = valueOperations.get(redisKey);
- if (value == null) {
- String lockKey = StringUtils.join(redisKey, ":", "lock");
- long time = System.currentTimeMillis() + 2000;
- String timeStr = String.valueOf(time);
- try {
- //分布式锁,保证一个线程读DB,其它线程排队
- if (redisLock.lock2(lockKey, timeStr)) {
- log.debug("CacheAspect 读库中, key:{}: " + lockKey);
- result = jp.proceed(args);
- String json_data = JSONObject.toJSONString(result);
- valueOperations.set(redisKey, json_data, cache.expireTime(), cache.dateUnit());
- log.debug("CacheAspect 缓存不存在,获取接口数据并放入缓存,key:{}, expire:{}", redisKey, cache.expireTime());
- } else {
- log.debug("CacheAspect 读库等待中, key:{}: " + lockKey);
- Thread.sleep(2000);
- value = valueOperations.get(redisKey);
- result = parseCache(jp, redisKey, value);
- }
- } catch (Throwable throwable) {
- log.error("CacheAspect proceed error , Illegal argument: {} in {}.{}()", Arrays.toString(jp.getArgs()),
- jp.getSignature().getDeclaringTypeName(), jp.getSignature().getName(), throwable);
- throw throwable;
- } finally {
- redisLock.unlock(lockKey, timeStr);
- }
- } else {
- result = parseCache(jp, redisKey, value);
- }
- // 计数器
- stringRedisTemplate.opsForHash().increment("hotapi", redisKey, 1);
- }
- return result;
- }
-
- private Object parseCache(ProceedingJoinPoint jp, String redisKey, String value) {
- if (StringUtils.isBlank(value)) {
- log.debug("CacheAspect 缓存不存在,解析缓存数据为空:{},key:{}", value, redisKey);
- return null;
- }
- Class<?> returnType = ((MethodSignature) jp.getSignature()).getReturnType();
- log.debug("CacheAspect 缓存存在,解析缓存并返回,key:{}", redisKey);
- return JSONObject.parseObject(value, returnType);
- }
- }
|