com.alibaba
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatSubmit.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatSubmit.java
deleted file mode 100644
index 29c667f86..000000000
--- a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatSubmit.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.ruoyi.common.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;
-
-/**
- * 自定义注解防止表单重复提交
- *
- * @author ruoyi
- */
-@Inherited
-@Target(ElementType.METHOD)
-@Retention(RetentionPolicy.RUNTIME)
-@Documented
-public @interface RepeatSubmit {
-
-}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatSubmitInterceptor.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatSubmitInterceptor.java
deleted file mode 100644
index e07e6de65..000000000
--- a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatSubmitInterceptor.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.ruoyi.framework.interceptor;
-
-import java.lang.reflect.Method;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.springframework.stereotype.Component;
-import org.springframework.web.method.HandlerMethod;
-import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
-import com.alibaba.fastjson.JSONObject;
-import com.ruoyi.common.annotation.RepeatSubmit;
-import com.ruoyi.common.core.domain.AjaxResult;
-import com.ruoyi.common.utils.ServletUtils;
-
-/**
- * 防止重复提交拦截器
- *
- * @author ruoyi
- */
-@Component
-public abstract class RepeatSubmitInterceptor extends HandlerInterceptorAdapter {
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- if (handler instanceof HandlerMethod) {
- HandlerMethod handlerMethod = (HandlerMethod) handler;
- Method method = handlerMethod.getMethod();
- RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
- if (annotation != null) {
- if (this.isRepeatSubmit(request)) {
- AjaxResult ajaxResult = AjaxResult.error("不允许重复提交,请稍后再试");
- ServletUtils.renderString(response, JSONObject.toJSONString(ajaxResult));
- return false;
- }
- }
- return true;
- } else {
- return super.preHandle(request, response, handler);
- }
- }
-
- /**
- * 验证是否重复提交由子类实现具体的防重复提交的规则
- *
- * @param request
- * @return
- * @throws Exception
- */
- public abstract boolean isRepeatSubmit(HttpServletRequest request);
-}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/SameUrlDataInterceptor.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/SameUrlDataInterceptor.java
deleted file mode 100644
index 3386b4115..000000000
--- a/ruoyi-common/src/main/java/com/ruoyi/common/filter/SameUrlDataInterceptor.java
+++ /dev/null
@@ -1,114 +0,0 @@
-package com.ruoyi.framework.interceptor.impl;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-import javax.servlet.http.HttpServletRequest;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Component;
-import com.alibaba.fastjson.JSONObject;
-import com.ruoyi.common.constant.Constants;
-import com.ruoyi.common.core.redis.RedisCache;
-import com.ruoyi.common.filter.RepeatedlyRequestWrapper;
-import com.ruoyi.common.utils.StringUtils;
-import com.ruoyi.common.utils.http.HttpHelper;
-import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;
-
-/**
- * 判断请求url和数据是否和上一次相同,
- * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。
- *
- * @author ruoyi
- */
-@Component
-public class SameUrlDataInterceptor extends RepeatSubmitInterceptor {
- public final String REPEAT_PARAMS = "repeatParams";
-
- public final String REPEAT_TIME = "repeatTime";
-
- // 令牌自定义标识
- @Value("${token.header}")
- private String header;
-
- @Autowired
- private RedisCache redisCache;
-
- /**
- * 间隔时间,单位:秒 默认10秒
- *
- * 两次相同参数的请求,如果间隔时间大于该参数,系统不会认定为重复提交的数据
- */
- private int intervalTime = 10;
-
- public void setIntervalTime(int intervalTime) {
- this.intervalTime = intervalTime;
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public boolean isRepeatSubmit(HttpServletRequest request) {
- String nowParams = "";
- if (request instanceof RepeatedlyRequestWrapper) {
- RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request;
- nowParams = HttpHelper.getBodyString(repeatedlyRequest);
- }
-
- // body参数为空,获取Parameter的数据
- if (StringUtils.isEmpty(nowParams)) {
- nowParams = JSONObject.toJSONString(request.getParameterMap());
- }
- Map nowDataMap = new HashMap();
- nowDataMap.put(REPEAT_PARAMS, nowParams);
- nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());
-
- // 请求地址(作为存放cache的key值)
- String url = request.getRequestURI();
-
- // 唯一值(没有消息头则使用请求地址)
- String submitKey = request.getHeader(header);
- if (StringUtils.isEmpty(submitKey)) {
- submitKey = url;
- }
-
- // 唯一标识(指定key + 消息头)
- String cache_repeat_key = Constants.REPEAT_SUBMIT_KEY + submitKey;
-
- Object sessionObj = redisCache.getCacheObject(cache_repeat_key);
- if (sessionObj != null) {
- Map sessionMap = (Map) sessionObj;
- if (sessionMap.containsKey(url)) {
- Map preDataMap = (Map) sessionMap.get(url);
- if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap)) {
- return true;
- }
- }
- }
- Map cacheMap = new HashMap();
- cacheMap.put(url, nowDataMap);
- redisCache.setCacheObject(cache_repeat_key, cacheMap, intervalTime, TimeUnit.SECONDS);
- return false;
- }
-
- /**
- * 判断参数是否相同
- */
- private boolean compareParams(Map nowMap, Map preMap) {
- String nowParams = (String) nowMap.get(REPEAT_PARAMS);
- String preParams = (String) preMap.get(REPEAT_PARAMS);
- return nowParams.equals(preParams);
- }
-
- /**
- * 判断两次间隔时间
- */
- private boolean compareTime(Map nowMap, Map preMap) {
- long time1 = (Long) nowMap.get(REPEAT_TIME);
- long time2 = (Long) preMap.get(REPEAT_TIME);
- if ((time1 - time2) < (this.intervalTime * 1000)) {
- return true;
- }
- return false;
- }
-}
diff --git a/src/main/java/cn/iocoder/dashboard/common/exception/enums/GlobalErrorCodeConstants.java b/src/main/java/cn/iocoder/dashboard/common/exception/enums/GlobalErrorCodeConstants.java
index e215f2bc0..a57a8120c 100644
--- a/src/main/java/cn/iocoder/dashboard/common/exception/enums/GlobalErrorCodeConstants.java
+++ b/src/main/java/cn/iocoder/dashboard/common/exception/enums/GlobalErrorCodeConstants.java
@@ -28,6 +28,9 @@ public interface GlobalErrorCodeConstants {
ErrorCode INTERNAL_SERVER_ERROR = new ErrorCode(500, "系统异常");
+ // ========== 自定义错误段 ==========
+ ErrorCode REPEATED_REQUESTS = new ErrorCode(900, "重复请求");
+
ErrorCode UNKNOWN = new ErrorCode(999, "未知错误");
static boolean isMatch(Integer code) {
diff --git a/src/main/java/cn/iocoder/dashboard/framework/dict/package-info.java b/src/main/java/cn/iocoder/dashboard/framework/dict/package-info.java
index 753975d54..adc37f7b6 100644
--- a/src/main/java/cn/iocoder/dashboard/framework/dict/package-info.java
+++ b/src/main/java/cn/iocoder/dashboard/framework/dict/package-info.java
@@ -1,5 +1,5 @@
/**
- * 字典数据模块
+ * 字典数据模块,提供 {@link cn.iocoder.dashboard.framework.dict.core.util.DictUtils} 工具类
*
* 通过将字典缓存在内存中,保证性能
*/
diff --git a/src/main/java/cn/iocoder/dashboard/framework/idempotent/config/IdempotentConfiguration.java b/src/main/java/cn/iocoder/dashboard/framework/idempotent/config/IdempotentConfiguration.java
new file mode 100644
index 000000000..c73e723f9
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/framework/idempotent/config/IdempotentConfiguration.java
@@ -0,0 +1,42 @@
+package cn.iocoder.dashboard.framework.idempotent.config;
+
+import cn.iocoder.dashboard.framework.idempotent.core.aop.IdempotentAspect;
+import cn.iocoder.dashboard.framework.idempotent.core.keyresolver.DefaultIdempotentKeyResolver;
+import cn.iocoder.dashboard.framework.idempotent.core.keyresolver.ExpressionIdempotentKeyResolver;
+import cn.iocoder.dashboard.framework.idempotent.core.keyresolver.IdempotentKeyResolver;
+import cn.iocoder.dashboard.framework.idempotent.core.redis.IdempotentRedisDAO;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.core.StringRedisTemplate;
+
+import java.util.List;
+
+@Configuration(proxyBeanMethods = false)
+@AutoConfigureAfter(RedisAutoConfiguration.class)
+public class IdempotentConfiguration {
+
+ @Bean
+ public IdempotentAspect idempotentAspect(List keyResolvers, IdempotentRedisDAO idempotentRedisDAO) {
+ return new IdempotentAspect(keyResolvers, idempotentRedisDAO);
+ }
+
+ @Bean
+ public IdempotentRedisDAO idempotentRedisDAO(StringRedisTemplate stringRedisTemplate) {
+ return new IdempotentRedisDAO(stringRedisTemplate);
+ }
+
+ // ========== 各种 IdempotentKeyResolver Bean ==========
+
+ @Bean
+ public DefaultIdempotentKeyResolver defaultIdempotentKeyResolver() {
+ return new DefaultIdempotentKeyResolver();
+ }
+
+ @Bean
+ public ExpressionIdempotentKeyResolver expressionIdempotentKeyResolver() {
+ return new ExpressionIdempotentKeyResolver();
+ }
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/framework/idempotent/core/annotation/Idempotent.java b/src/main/java/cn/iocoder/dashboard/framework/idempotent/core/annotation/Idempotent.java
new file mode 100644
index 000000000..1b1a22352
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/framework/idempotent/core/annotation/Idempotent.java
@@ -0,0 +1,46 @@
+package cn.iocoder.dashboard.framework.idempotent.core.annotation;
+
+import cn.iocoder.dashboard.framework.idempotent.core.keyresolver.DefaultIdempotentKeyResolver;
+import cn.iocoder.dashboard.framework.idempotent.core.keyresolver.IdempotentKeyResolver;
+
+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 芋道源码
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Idempotent {
+
+ /**
+ * 幂等的超时时间,默认为 1 秒
+ *
+ * 注意,如果执行时间超过它,请求还是会进来
+ */
+ int timeout() default 1;
+ /**
+ * 时间单位,默认为 SECONDS 秒
+ */
+ TimeUnit timeUnit() default TimeUnit.SECONDS;
+
+ /**
+ * 提示信息,正在执行中的提示
+ */
+ String message() default "重复请求,请稍后重试";
+
+ /**
+ * 使用的 Key 解析器
+ */
+ Class extends IdempotentKeyResolver> keyResolver() default DefaultIdempotentKeyResolver.class;
+ /**
+ * 使用的 Key 参数
+ */
+ String keyArg() default "";
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/framework/idempotent/core/aop/IdempotentAspect.java b/src/main/java/cn/iocoder/dashboard/framework/idempotent/core/aop/IdempotentAspect.java
new file mode 100644
index 000000000..da7a1bfff
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/framework/idempotent/core/aop/IdempotentAspect.java
@@ -0,0 +1,56 @@
+package cn.iocoder.dashboard.framework.idempotent.core.aop;
+
+import cn.iocoder.dashboard.common.exception.ServiceException;
+import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants;
+import cn.iocoder.dashboard.framework.idempotent.core.annotation.Idempotent;
+import cn.iocoder.dashboard.framework.idempotent.core.keyresolver.IdempotentKeyResolver;
+import cn.iocoder.dashboard.framework.idempotent.core.redis.IdempotentRedisDAO;
+import cn.iocoder.dashboard.util.collection.CollectionUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.springframework.util.Assert;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 拦截声明了 {@link Idempotent} 注解的方法,实现幂等操作
+ *
+ * @author 芋道源码
+ */
+@Aspect
+@Slf4j
+public class IdempotentAspect {
+
+ /**
+ * IdempotentKeyResolver 集合
+ */
+ private final Map, IdempotentKeyResolver> keyResolvers;
+
+ private final IdempotentRedisDAO idempotentRedisDAO;
+
+ public IdempotentAspect(List keyResolvers, IdempotentRedisDAO idempotentRedisDAO) {
+ this.keyResolvers = CollectionUtils.convertMap(keyResolvers, IdempotentKeyResolver::getClass);
+ this.idempotentRedisDAO = idempotentRedisDAO;
+ }
+
+ @Before("@annotation(idempotent)")
+ public void beforePointCut(JoinPoint joinPoint, Idempotent idempotent) {
+ // 获得 IdempotentKeyResolver
+ IdempotentKeyResolver keyResolver = keyResolvers.get(idempotent.keyResolver());
+ Assert.notNull(keyResolver, "找不到对应的 IdempotentKeyResolver");
+ // 解析 Key
+ String key = keyResolver.resolver(joinPoint, idempotent);
+
+ // 锁定 Key。
+ boolean success = idempotentRedisDAO.setIfAbsent(key, idempotent.timeout(), idempotent.timeUnit());
+ // 锁定失败,抛出异常
+ if (!success) {
+ log.info("[beforePointCut][方法({}) 参数({}) 存在重复请求]", joinPoint.getSignature().toString(), joinPoint.getArgs());
+ throw new ServiceException(GlobalErrorCodeConstants.REPEATED_REQUESTS.getCode(), idempotent.message());
+ }
+ }
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/framework/idempotent/core/keyresolver/DefaultIdempotentKeyResolver.java b/src/main/java/cn/iocoder/dashboard/framework/idempotent/core/keyresolver/DefaultIdempotentKeyResolver.java
new file mode 100644
index 000000000..bcd4bbe91
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/framework/idempotent/core/keyresolver/DefaultIdempotentKeyResolver.java
@@ -0,0 +1,24 @@
+package cn.iocoder.dashboard.framework.idempotent.core.keyresolver;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.crypto.SecureUtil;
+import cn.iocoder.dashboard.framework.idempotent.core.annotation.Idempotent;
+import org.aspectj.lang.JoinPoint;
+
+/**
+ * 默认幂等 Key 解析器,使用方法名 + 方法参数,组装成一个 Key
+ *
+ * 为了避免 Key 过长,使用 MD5 进行“压缩”
+ *
+ * @author 芋道源码
+ */
+public class DefaultIdempotentKeyResolver implements IdempotentKeyResolver {
+
+ @Override
+ public String resolver(JoinPoint joinPoint, Idempotent idempotent) {
+ String methodName = joinPoint.getSignature().toString();
+ String argsStr = StrUtil.join(",", joinPoint.getArgs());
+ return SecureUtil.md5(methodName + argsStr);
+ }
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/framework/idempotent/core/keyresolver/ExpressionIdempotentKeyResolver.java b/src/main/java/cn/iocoder/dashboard/framework/idempotent/core/keyresolver/ExpressionIdempotentKeyResolver.java
new file mode 100644
index 000000000..c5d1588af
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/framework/idempotent/core/keyresolver/ExpressionIdempotentKeyResolver.java
@@ -0,0 +1,19 @@
+package cn.iocoder.dashboard.framework.idempotent.core.keyresolver;
+
+import cn.iocoder.dashboard.framework.idempotent.core.annotation.Idempotent;
+import org.aspectj.lang.JoinPoint;
+
+/**
+ * 基于 Spring EL 表达式,
+ *
+ * @author 芋道源码
+ */
+public class ExpressionIdempotentKeyResolver implements IdempotentKeyResolver {
+
+ @Override
+ public String resolver(JoinPoint joinPoint, Idempotent idempotent) {
+ // TODO 稍后实现
+ return null;
+ }
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/framework/idempotent/core/keyresolver/IdempotentKeyResolver.java b/src/main/java/cn/iocoder/dashboard/framework/idempotent/core/keyresolver/IdempotentKeyResolver.java
new file mode 100644
index 000000000..a81d6c8da
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/framework/idempotent/core/keyresolver/IdempotentKeyResolver.java
@@ -0,0 +1,22 @@
+package cn.iocoder.dashboard.framework.idempotent.core.keyresolver;
+
+import cn.iocoder.dashboard.framework.idempotent.core.annotation.Idempotent;
+import org.aspectj.lang.JoinPoint;
+
+/**
+ * 幂等 Key 解析器接口
+ *
+ * @author 芋道源码
+ */
+public interface IdempotentKeyResolver {
+
+ /**
+ * 解析一个 Key
+ *
+ * @param idempotent 幂等注解
+ * @param joinPoint AOP 切面
+ * @return Key
+ */
+ String resolver(JoinPoint joinPoint, Idempotent idempotent);
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/framework/idempotent/core/redis/IdempotentRedisDAO.java b/src/main/java/cn/iocoder/dashboard/framework/idempotent/core/redis/IdempotentRedisDAO.java
new file mode 100644
index 000000000..16b88cb23
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/framework/idempotent/core/redis/IdempotentRedisDAO.java
@@ -0,0 +1,34 @@
+package cn.iocoder.dashboard.framework.idempotent.core.redis;
+
+import cn.iocoder.dashboard.framework.redis.core.RedisKeyDefine;
+import lombok.AllArgsConstructor;
+import org.springframework.data.redis.core.StringRedisTemplate;
+
+import java.util.concurrent.TimeUnit;
+
+import static cn.iocoder.dashboard.framework.redis.core.RedisKeyDefine.KeyTypeEnum.STRING;
+
+/**
+ * 幂等 Redis DAO
+ *
+ * @author 芋道源码
+ */
+@AllArgsConstructor
+public class IdempotentRedisDAO {
+
+ private static final RedisKeyDefine IDEMPOTENT = new RedisKeyDefine("幂等操作",
+ "idempotent:%s", // 参数为 uuid
+ STRING, String.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC);
+
+ private final StringRedisTemplate redisTemplate;
+
+ public Boolean setIfAbsent(String key, long timeout, TimeUnit timeUnit) {
+ String redisKey = formatKey(key);
+ return redisTemplate.opsForValue().setIfAbsent(redisKey, "", timeout, timeUnit);
+ }
+
+ private static String formatKey(String key) {
+ return String.format(IDEMPOTENT.getKeyTemplate(), key);
+ }
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/framework/idempotent/package-info.java b/src/main/java/cn/iocoder/dashboard/framework/idempotent/package-info.java
new file mode 100644
index 000000000..7e4594180
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/framework/idempotent/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * 幂等组件,参考 https://github.com/it4alla/idempotent 项目实现
+ */
+package cn.iocoder.dashboard.framework.idempotent;
diff --git a/src/main/java/cn/iocoder/dashboard/framework/logger/operatelog/core/annotations/OperateLog.java b/src/main/java/cn/iocoder/dashboard/framework/logger/operatelog/core/annotations/OperateLog.java
index e5e941034..6dc3c09cc 100644
--- a/src/main/java/cn/iocoder/dashboard/framework/logger/operatelog/core/annotations/OperateLog.java
+++ b/src/main/java/cn/iocoder/dashboard/framework/logger/operatelog/core/annotations/OperateLog.java
@@ -9,6 +9,11 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+/**
+ * 操作日志注解
+ *
+ * @author 芋道源码
+ */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface OperateLog {
diff --git a/src/main/java/cn/iocoder/dashboard/modules/infra/controller/config/InfConfigController.java b/src/main/java/cn/iocoder/dashboard/modules/infra/controller/config/InfConfigController.java
index 3c001e0f0..04614cafa 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/infra/controller/config/InfConfigController.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/infra/controller/config/InfConfigController.java
@@ -4,6 +4,7 @@ import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.dashboard.common.pojo.CommonResult;
import cn.iocoder.dashboard.common.pojo.PageResult;
import cn.iocoder.dashboard.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.dashboard.framework.idempotent.core.annotation.Idempotent;
import cn.iocoder.dashboard.modules.infra.controller.config.vo.*;
import cn.iocoder.dashboard.modules.infra.convert.config.InfConfigConvert;
import cn.iocoder.dashboard.modules.infra.dal.dataobject.config.InfConfigDO;
@@ -91,7 +92,7 @@ public class InfConfigController {
@PostMapping("/create")
// @PreAuthorize("@ss.hasPermi('infra:config:add')")
// @Log(title = "参数管理", businessType = BusinessType.INSERT)
-// @RepeatSubmit
+ @Idempotent(timeout = 10)
public CommonResult createConfig(@Validated @RequestBody InfConfigCreateReqVO reqVO) {
return success(configService.createConfig(reqVO));
}
@@ -100,7 +101,8 @@ public class InfConfigController {
@PutMapping("/update")
// @PreAuthorize("@ss.hasPermi('infra:config:edit')")
// @Log(title = "参数管理", businessType = BusinessType.UPDATE)
- public CommonResult edit(@Validated @RequestBody InfConfigUpdateReqVO reqVO) {
+ @Idempotent(timeout = 60)
+ public CommonResult updateConfig(@Validated @RequestBody InfConfigUpdateReqVO reqVO) {
configService.updateConfig(reqVO);
return success(true);
}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/infra/controller/config/vo/InfConfigUpdateReqVO.java b/src/main/java/cn/iocoder/dashboard/modules/infra/controller/config/vo/InfConfigUpdateReqVO.java
index 3d0f5c7b4..478f3081b 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/infra/controller/config/vo/InfConfigUpdateReqVO.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/infra/controller/config/vo/InfConfigUpdateReqVO.java
@@ -4,12 +4,14 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
+import lombok.ToString;
import javax.validation.constraints.NotNull;
@ApiModel("参数配置创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
public class InfConfigUpdateReqVO extends InfConfigBaseVO {
@ApiModelProperty(value = "参数配置序号", required = true, example = "1024")
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/redis/RedisKeyConstants.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/redis/SysRedisKeyConstants.java
similarity index 94%
rename from src/main/java/cn/iocoder/dashboard/modules/system/dal/redis/RedisKeyConstants.java
rename to src/main/java/cn/iocoder/dashboard/modules/system/dal/redis/SysRedisKeyConstants.java
index aa0b1d828..729b734d8 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/redis/RedisKeyConstants.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/dal/redis/SysRedisKeyConstants.java
@@ -12,7 +12,7 @@ import static cn.iocoder.dashboard.framework.redis.core.RedisKeyDefine.KeyTypeEn
*
* @author 芋道源码
*/
-public interface RedisKeyConstants {
+public interface SysRedisKeyConstants {
RedisKeyDefine LOGIN_USER = new RedisKeyDefine("登陆用户的缓存",
"login_user:%s", // 参数为 sessionId
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/redis/auth/SysLoginUserRedisDAO.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/redis/auth/SysLoginUserRedisDAO.java
index d2d15c39a..c2d7bfd6b 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/redis/auth/SysLoginUserRedisDAO.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/dal/redis/auth/SysLoginUserRedisDAO.java
@@ -7,7 +7,7 @@ import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
-import static cn.iocoder.dashboard.modules.system.dal.redis.RedisKeyConstants.LOGIN_USER;
+import static cn.iocoder.dashboard.modules.system.dal.redis.SysRedisKeyConstants.LOGIN_USER;
/**
* {@link LoginUser} 的 RedisDAO
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/redis/common/SysCaptchaRedisDAO.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/redis/common/SysCaptchaRedisDAO.java
index dd6ac3362..520b4849b 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/redis/common/SysCaptchaRedisDAO.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/dal/redis/common/SysCaptchaRedisDAO.java
@@ -6,7 +6,7 @@ import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import java.time.Duration;
-import static cn.iocoder.dashboard.modules.system.dal.redis.RedisKeyConstants.CAPTCHA_CODE;
+import static cn.iocoder.dashboard.modules.system.dal.redis.SysRedisKeyConstants.CAPTCHA_CODE;
/**
* 验证码的 Redis DAO
diff --git a/src/main/resources/application-dev.yaml b/src/main/resources/application-dev.yaml
index 041178226..8fa9210b0 100644
--- a/src/main/resources/application-dev.yaml
+++ b/src/main/resources/application-dev.yaml
@@ -98,6 +98,8 @@ spring:
# Spring Boot Admin Client 客户端的相关配置
client:
url: http://127.0.0.1:${server.port}/${spring.boot.admin.context-path} # 设置 Spring Boot Admin Server 地址
+ instance:
+ prefer-ip: true # 注册实例时,优先使用 IP
# Spring Boot Admin Server 服务端的相关配置
context-path: /admin # 配置 Spring
diff --git a/src/main/resources/application-local.yaml b/src/main/resources/application-local.yaml
index 43930e285..878f5e377 100644
--- a/src/main/resources/application-local.yaml
+++ b/src/main/resources/application-local.yaml
@@ -99,7 +99,7 @@ spring:
client:
url: http://127.0.0.1:${server.port}/${spring.boot.admin.context-path} # 设置 Spring Boot Admin Server 地址
instance:
- prefer-ip: true
+ prefer-ip: true # 注册实例时,优先使用 IP
# Spring Boot Admin Server 服务端的相关配置
context-path: /admin # 配置 Spring