package com.yxcheng.token.common.aop;


import com.yxcheng.token.common.annotation.RedisLimit;
import com.yxcheng.token.common.exception.LimitException;
import com.yxcheng.token.common.limit.RedisLimiter;
import com.yxcheng.token.common.model.SafeModel;
import com.yxcheng.token.utils.IpUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * 集群限流处理类，采用spring AOP切面基础，进行限流拦截，对超过限流的请求
 * <p>
 * 返回限流异常<b>LimitException</b>,异常码枚举<b>SystemErrorEnum.TOO_MUCH_ERROR</b>
 * <p>
 * 
 * @author wuyuegang
 *
 */
@Aspect
@Configuration
public class RedisLimiterAop
{
    
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    
    /**
     * redis限流器，主要是对redis的操作封装
     */
    @Autowired
    private RedisLimiter limiter;
    
    @Before("@annotation(annotation)")
    public void doBefore(JoinPoint point, RedisLimit annotation)
        throws Throwable
    {
        
        String methodName = point.getSignature().getName();// 限流注解所在方法名
        String className = point.getTarget().getClass().getName();// 限流注解所在类名

        int index = className.lastIndexOf(".");
//        className = className.substring(index+1,className.length());
        String key = className + "." + methodName;// 用类名+方法名，可以唯一确定一个限流函数，作为限流器生成主键
        // 判断拦截到的方法是不是有注解 BWRedisLimit，如果没有这个注解，说明不是需要拦截的方法，放过。如果有注解，开始进入限流拦截
        Object o = point.getArgs()[0];
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        if(o instanceof SafeModel){
            SafeModel safeModel = (SafeModel) o;
            key += (":" + safeModel.getAccessKeyID());
        }
        if (annotation != null)
        {
            // 执行限流
            long start = System.currentTimeMillis();
            int count = annotation.count();// 从注解中得到限流值 count
            long unitTime = annotation.unitTime();// 从注解中得到限流单位时间 unitTime
            if (limiter.tryAcquire(key, count, unitTime))// 执行限流判断
            {
                // 成功进入限流，继续执行业务
                if (logger.isDebugEnabled())
                {
                    logger.debug("RedisLimiter tryAcquire success, key [{}] count [{}] cost[{}]ms",
                        key,
                        count,
                        (System.currentTimeMillis() - start));
                }
            }
            else
            {
                // 请求被限流，抛出异常，中断业务
                if (logger.isInfoEnabled())
                {
                    logger.info("RedisLimiter tryAcquire failed, key [{}] count [{}] cost[{}]ms",
                        key,
                        count,
                        (System.currentTimeMillis() - start));
                }
                throw new LimitException(key + ",ip:"+ IpUtil.getRequestRealIp(request)+" 系统限流，请稍后再试");
            }
            
        }
    }
}
