package com.app.framework.core.support.resubmit.aspect;

import cn.hutool.crypto.SecureUtil;
import com.app.framework.core.support.auth.LoginUser;
import com.app.framework.core.support.resubmit.Resubmit;
import com.app.framework.core.support.resubmit.ResubmitLock;
import com.app.framework.core.support.view.form.AjaxResult;
import com.app.framework.core.utils.request.RequestUtil;
import com.app.framework.core.utils.request.SessionUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.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.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * 重复提交校验
 **/
@Slf4j
@Aspect
@Component
public class ResubmitAspect {

    private final static Object PRESENT = new Object();

    @Resource
    ResubmitLock resubmitLock;

    public String buildKey(Method method, LoginUser loginUser) {
        String ip = RequestUtil.getIpAddr(RequestUtil.getRequest());

        //目标类、方法
        String className = method.getDeclaringClass().getName();
        String methodName = method.getName();

        return "resubmit:" + SecureUtil.md5(String.format("%s-%s-%s-%s", ip, className, methodName, loginUser.getUsername()));
    }

    @Around("execution(public * *(..)) && @annotation(com.app.framework.core.support.resubmit.Resubmit)")
    public Object handleResubmit(ProceedingJoinPoint joinPoint) throws Throwable {
        HttpServletRequest request = RequestUtil.getRequest();
        LoginUser loginUser = SessionUtil.getCurrentUser(request);
        if (loginUser == null) {
            loginUser = (LoginUser) request.getAttribute("apiLoginUser");
        }

        if (loginUser == null) {//放行
            return joinPoint.proceed();
        }

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        //执行锁
        boolean lock = false;

        //通过反射技术来获取注解对象
        Resubmit resubmit = method.getAnnotation(Resubmit.class);
        //防重提交类型
        String type = resubmit.type().name();

        if (type.equalsIgnoreCase(Resubmit.Type.PARAM.name())) {
            //方式一，参数形式防重提交

            int delaySeconds = resubmit.delaySeconds();
            if (delaySeconds <= 0) {//放行
                return joinPoint.proceed();
            }

            String key = this.buildKey(method, loginUser);
            lock = resubmitLock.lock(key, PRESENT, delaySeconds);
        } else {
            //方式二，令牌形式防重提交

            String resubmitToken = request.getHeader("resubmit-token");
            if (StringUtils.isBlank(resubmitToken)) {
                resubmitToken = request.getParameter("resubmit-token");
            }
            if (StringUtils.isBlank(resubmitToken)) {
                return AjaxResult.wrapError("防重复提交令牌不能为空");
            }

            //提交表单的token key，直接删除成功则说明token正确
            lock = resubmitLock.tokenDelete(loginUser.getUsername(), resubmitToken);
        }

        if (lock) {
            //放行
            return joinPoint.proceed();
        } else {
            //响应重复提交异常
            return AjaxResult.wrap("resubmit", "请勿重复提交");
        }
    }

}
