package org.jeecg.common.aspect;

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.jeecg.common.aspect.annotation.RepeatSubmit;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
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.concurrent.TimeUnit;

@Aspect  // 定义一个切面
@Component  // 标识为Spring组件
public class NoRepeatSubmitAspect {

    @Autowired
    private RedissonClient redissonClient;  // 注入RedissonClient，用于分布式锁

    // 定义切入点，匹配所有被RepeatSubmit注解标注的方法
    @Pointcut("@annotation(repeatSubmit)")
    public void pointCutNoRepeatSubmit(RepeatSubmit repeatSubmit) {
    }

    // 定义环绕通知
    @Around("pointCutNoRepeatSubmit(repeatSubmit)")
    public Object around(ProceedingJoinPoint joinPoint, RepeatSubmit repeatSubmit) throws Throwable {
        // 获取当前HTTP请求
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String serviceId = repeatSubmit.serviceId();  // 获取RepeatSubmit注解中的serviceId
        String type = repeatSubmit.limitType().name();  // 获取防重提交的类型
        if (type.equalsIgnoreCase(RepeatSubmit.Type.PARAM.name())) {
            long lockTime = repeatSubmit.lockTime();  // 获取锁的超时时间
            String ipAddr = request.getRemoteAddr();  // 获取客户端IP地址
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            Method method = methodSignature.getMethod();  // 获取目标方法

            // 生成唯一的key，用于获取分布式锁
            String key = generateUniqueKey(ipAddr, method, serviceId);
            boolean isLocked = redissonClient.getLock(key).tryLock(lockTime, TimeUnit.MILLISECONDS);  // 尝试获取锁

            if (!isLocked) {
                throw new RuntimeException("重复提交，请稍后再试");  // 如果获取锁失败，抛出异常
            }

            try {
                Object result = joinPoint.proceed();  // 执行目标方法 这里的目标方法是使用了@repeatSubmit注解的方法
                return result;  // 返回目标方法的执行结果
            } finally {
                redissonClient.getLock(key).unlock();  // 释放锁  不释放的话可以在上面上锁时定义自动释放
            }
        }
        return joinPoint.proceed();  // 默认执行目标方法
    }

    // 生成唯一的key，用于获取分布式锁
    private String generateUniqueKey(String ipAddr, Method method, String serviceId) {
        return ipAddr + ":" + method.getDeclaringClass().getName() + "." + method.getName() + ":" + serviceId;
    }
}
