diff --git a/riis-system/pom.xml b/riis-system/pom.xml
index e17185b..4243e98 100644
--- a/riis-system/pom.xml
+++ b/riis-system/pom.xml
@@ -224,6 +224,14 @@
TarsosDSP
2.4
+
+
+
+ org.redisson
+ redisson-spring-boot-starter
+ 3.40.2
+
+
diff --git a/riis-system/src/main/java/com/yfd/platform/annotation/Idempotent.java b/riis-system/src/main/java/com/yfd/platform/annotation/Idempotent.java
new file mode 100644
index 0000000..68296f4
--- /dev/null
+++ b/riis-system/src/main/java/com/yfd/platform/annotation/Idempotent.java
@@ -0,0 +1,46 @@
+package com.yfd.platform.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @Author: zcg
+ * @Description: 幂等注解
+ * @Date: 2024/3/12
+ **/
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Idempotent {
+
+ /**
+ * 幂等的超时时间,默认为 1 秒
+ *
+ * 注意,如果执行时间超过它,请求还是会进来
+ */
+ int timeout() default 1;
+
+ /**
+ * 时间单位,默认为 SECONDS 秒
+ */
+ TimeUnit timeUnit() default TimeUnit.SECONDS;
+
+ /**
+ * redis锁前缀
+ * @return
+ */
+ String keyPrefix() default "idempotent";
+
+ /**
+ * key分隔符
+ * @return
+ */
+ String delimiter() default "|";
+
+ /**
+ * 提示信息,正在执行中的提示
+ */
+ String message() default "重复请求,请稍后重试";
+}
diff --git a/riis-system/src/main/java/com/yfd/platform/annotation/RequestKeyParam.java b/riis-system/src/main/java/com/yfd/platform/annotation/RequestKeyParam.java
new file mode 100644
index 0000000..b86a012
--- /dev/null
+++ b/riis-system/src/main/java/com/yfd/platform/annotation/RequestKeyParam.java
@@ -0,0 +1,20 @@
+package com.yfd.platform.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @description 加上这个注解可以将参数设置为key
+ */
+@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface RequestKeyParam {
+
+}
+
diff --git a/riis-system/src/main/java/com/yfd/platform/aspect/IdempotentAspect.java b/riis-system/src/main/java/com/yfd/platform/aspect/IdempotentAspect.java
new file mode 100644
index 0000000..d9d5225
--- /dev/null
+++ b/riis-system/src/main/java/com/yfd/platform/aspect/IdempotentAspect.java
@@ -0,0 +1,73 @@
+package com.yfd.platform.aspect;
+
+import cn.hutool.core.util.StrUtil;
+import com.yfd.platform.annotation.Idempotent;
+import com.yfd.platform.utils.RequestKeyGenerator;
+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.reflect.MethodSignature;
+import org.redisson.api.RLock;
+import org.redisson.api.RedissonClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+
+import javax.annotation.Resource;
+import java.lang.reflect.Method;
+
+/**
+ * @Author: zcg
+ * @Description: 幂等切面实现
+ * @Date: 2024/3/12
+ **/
+@Aspect
+@Configuration
+@Order(2)
+@Slf4j
+public class IdempotentAspect {
+
+ @Resource
+ private RedissonClient redissonClient;
+
+ @Around("execution(public * * (..)) && @annotation(com.yfd.platform.annotation.Idempotent)")
+ public Object interceptor(ProceedingJoinPoint joinPoint) {
+ MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
+ Method method = methodSignature.getMethod();
+ Idempotent idempotent = method.getAnnotation(Idempotent.class);
+ if (StrUtil.isBlank(idempotent.keyPrefix())) {
+ throw new RuntimeException("重复提交前缀不能为空");
+ }
+ //获取自定义key
+ final String lockKey = RequestKeyGenerator.getLockKey(joinPoint);
+ // 使用Redisson分布式锁的方式判断是否重复提交
+ RLock lock = redissonClient.getLock(lockKey);
+ boolean isLocked = false;
+ try {
+ //尝试抢占锁
+ isLocked = lock.tryLock();
+ //没有拿到锁说明已经有了请求了
+ if (!isLocked) {
+ throw new RuntimeException(idempotent.message());
+ }
+ //拿到锁后设置过期时间
+ lock.lock(idempotent.timeout(), idempotent.timeUnit());
+ try {
+ return joinPoint.proceed();
+ } catch (Throwable throwable) {
+ log.info("系统异常,", throwable);
+ throw new RuntimeException("系统异常," + throwable.getMessage());
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e.getMessage());
+ } finally {
+ //释放锁
+ if (isLocked && lock.isHeldByCurrentThread()) {
+ lock.unlock();
+ }
+ }
+ }
+}
+
+
diff --git a/riis-system/src/main/java/com/yfd/platform/modules/algorithm/controller/AlgorithmLogsController.java b/riis-system/src/main/java/com/yfd/platform/modules/algorithm/controller/AlgorithmLogsController.java
index f284767..8fbb4c0 100644
--- a/riis-system/src/main/java/com/yfd/platform/modules/algorithm/controller/AlgorithmLogsController.java
+++ b/riis-system/src/main/java/com/yfd/platform/modules/algorithm/controller/AlgorithmLogsController.java
@@ -2,6 +2,7 @@ package com.yfd.platform.modules.algorithm.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.yfd.platform.annotation.Idempotent;
import com.yfd.platform.config.ResponseResult;
import com.yfd.platform.modules.algorithm.domain.AlgorithmLogs;
import com.yfd.platform.modules.algorithm.service.IAlgorithmLogsService;
@@ -45,6 +46,7 @@ public class AlgorithmLogsController {
// 算法分类,区域,间隔,部件
@PostMapping("/testQueryWrapperBuilder")
@ApiOperation("测试构造器")
+ @Idempotent
public ResponseResult testQueryWrapperBuilder(@RequestBody QueryCondition queryCondition) {
Page page = new Page<>();
page.setSize(queryCondition.getSize());
diff --git a/riis-system/src/main/java/com/yfd/platform/utils/RequestKeyGenerator.java b/riis-system/src/main/java/com/yfd/platform/utils/RequestKeyGenerator.java
new file mode 100644
index 0000000..c65b6d8
--- /dev/null
+++ b/riis-system/src/main/java/com/yfd/platform/utils/RequestKeyGenerator.java
@@ -0,0 +1,76 @@
+package com.yfd.platform.utils;
+
+import com.yfd.platform.annotation.Idempotent;
+import com.yfd.platform.annotation.RequestKeyParam;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.util.ReflectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+
+/**
+ * @Author: zcg
+ * @Description: 生成LockKey
+ * @Date: 2024/3/12
+ **/
+public class RequestKeyGenerator {
+
+ /**
+ * 获取LockKey
+ *
+ * @param joinPoint 切入点
+ * @return
+ */
+ public static String getLockKey(ProceedingJoinPoint joinPoint) {
+ //获取连接点的方法签名对象
+ MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
+ //Method对象
+ Method method = methodSignature.getMethod();
+ //获取Method对象上的注解对象
+ Idempotent idempotent = method.getAnnotation(Idempotent.class);
+ //获取方法参数
+ final Object[] args = joinPoint.getArgs();
+ //获取Method对象上所有的注解
+ final Parameter[] parameters = method.getParameters();
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < parameters.length; i++) {
+ final RequestKeyParam keyParam = parameters[i].getAnnotation(RequestKeyParam.class);
+ //如果属性不是RequestKeyParam注解,则不处理
+ if (keyParam == null) {
+ continue;
+ }
+ //如果属性是RequestKeyParam注解,则拼接 连接符 "& + RequestKeyParam"
+ sb.append(idempotent.delimiter()).append(args[i]);
+ }
+ //如果方法上没有加RequestKeyParam注解
+ if (StringUtils.isEmpty(sb.toString())) {
+ //获取方法上的多个注解(为什么是两层数组:因为第二层数组是只有一个元素的数组)
+ final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
+ //循环注解
+ for (int i = 0; i < parameterAnnotations.length; i++) {
+ final Object object = args[i];
+ //获取注解类中所有的属性字段
+ final Field[] fields = object.getClass().getDeclaredFields();
+ for (Field field : fields) {
+ //判断字段上是否有RequestKeyParam注解
+ final RequestKeyParam annotation = field.getAnnotation(RequestKeyParam.class);
+ //如果没有,跳过
+ if (annotation == null) {
+ continue;
+ }
+ //如果有,设置Accessible为true(为true时可以使用反射访问私有变量,否则不能访问私有变量)
+ field.setAccessible(true);
+ //如果属性是RequestKeyParam注解,则拼接 连接符" & + RequestKeyParam"
+ sb.append(idempotent.delimiter()).append(ReflectionUtils.getField(field, object));
+ }
+ }
+ }
+ //返回指定前缀的key
+ return idempotent.keyPrefix() + sb;
+ }
+}
+