package cn.iocoder.yudao.module.pms.aspect;

import cn.hutool.core.lang.hash.MurmurHash;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.pms.annotation.AntiDuplicateSubmit;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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.data.redis.core.StringRedisTemplate;
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;

/**
 * @author yc
 * @version 1.0
 * @className AntiDuplicateSubmitAspect
 * @date 2026/1/8 9:21
 * @description
 */
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class AntiDuplicateSubmitAspect {
    private final StringRedisTemplate redisTemplate;
    private final ObjectMapper objectMapper;

    // 切入点：标注了AntiDuplicateSubmit注解的方法
    @Pointcut("@annotation(cn.iocoder.yudao.module.pms.annotation.AntiDuplicateSubmit)")
    public void antiDuplicateSubmitPointcut() {}

    @Around("antiDuplicateSubmitPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 1. 获取注解信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        AntiDuplicateSubmit annotation = method.getAnnotation(AntiDuplicateSubmit.class);
        long timeout = annotation.timeout();
        String message = annotation.message();

        // 2. 获取请求信息
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String requestURI = request.getRequestURI();

        // 3. 生成请求唯一标识（请求URI + 参数哈希）
        String requestBody = "";
        Object[] args = joinPoint.getArgs();
        if (args != null && args.length > 0) {
            try {
                requestBody = objectMapper.writeValueAsString(args);
            } catch (Exception e) {
                log.warn("序列化请求参数失败", e);
                requestBody = args.toString();
            }
        }

        // 使用MurmurHash生成哈希值，避免参数过长
        int hash = MurmurHash.hash32(requestBody.getBytes());
        String redisKey = "anti_duplicate:".concat(requestURI).concat(":").concat(String.valueOf(hash));

        // 4. 检查Redis中是否存在该请求
        Boolean exists = redisTemplate.opsForValue().setIfAbsent(redisKey, "1", timeout, TimeUnit.SECONDS);
        if (Boolean.FALSE.equals(exists)) {
            // 5秒内重复请求，直接返回错误
            log.warn("接口防刷触发：{}，请求参数：{}", requestURI, requestBody);
            return CommonResult.error(2,message); // 替换为你项目中实际的返回格式
        }

        // 5. 正常执行方法
        try {
            return joinPoint.proceed();
        } catch (Exception e) {
            // 如果方法执行异常，删除Redis中的标记（可选，根据业务需求）
            redisTemplate.delete(redisKey);
            throw e;
        }
    }
}