Commit 8fa9ba8ec671639a89e517c7abca509f0af8d1a5

Authored by YunaiV
1 parent 3bf45886

基于 Redis 实现幂等性操作

Showing 22 changed files with 273 additions and 191 deletions
... ... @@ -197,6 +197,11 @@
197 197 <artifactId>hutool-http</artifactId>
198 198 <version>${hutool.version}</version>
199 199 </dependency>
  200 + <dependency>
  201 + <groupId>cn.hutool</groupId>
  202 + <artifactId>hutool-crypto</artifactId>
  203 + <version>${hutool.version}</version>
  204 + </dependency>
200 205  
201 206 <dependency>
202 207 <groupId>com.alibaba</groupId>
... ...
ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatSubmit.java deleted
1   -package com.ruoyi.common.annotation;
2   -
3   -import java.lang.annotation.Documented;
4   -import java.lang.annotation.ElementType;
5   -import java.lang.annotation.Inherited;
6   -import java.lang.annotation.Retention;
7   -import java.lang.annotation.RetentionPolicy;
8   -import java.lang.annotation.Target;
9   -
10   -/**
11   - * 自定义注解防止表单重复提交
12   - *
13   - * @author ruoyi
14   - */
15   -@Inherited
16   -@Target(ElementType.METHOD)
17   -@Retention(RetentionPolicy.RUNTIME)
18   -@Documented
19   -public @interface RepeatSubmit {
20   -
21   -}
ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatSubmitInterceptor.java deleted
1   -package com.ruoyi.framework.interceptor;
2   -
3   -import java.lang.reflect.Method;
4   -import javax.servlet.http.HttpServletRequest;
5   -import javax.servlet.http.HttpServletResponse;
6   -
7   -import org.springframework.stereotype.Component;
8   -import org.springframework.web.method.HandlerMethod;
9   -import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
10   -import com.alibaba.fastjson.JSONObject;
11   -import com.ruoyi.common.annotation.RepeatSubmit;
12   -import com.ruoyi.common.core.domain.AjaxResult;
13   -import com.ruoyi.common.utils.ServletUtils;
14   -
15   -/**
16   - * 防止重复提交拦截器
17   - *
18   - * @author ruoyi
19   - */
20   -@Component
21   -public abstract class RepeatSubmitInterceptor extends HandlerInterceptorAdapter {
22   - @Override
23   - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
24   - if (handler instanceof HandlerMethod) {
25   - HandlerMethod handlerMethod = (HandlerMethod) handler;
26   - Method method = handlerMethod.getMethod();
27   - RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
28   - if (annotation != null) {
29   - if (this.isRepeatSubmit(request)) {
30   - AjaxResult ajaxResult = AjaxResult.error("不允许重复提交,请稍后再试");
31   - ServletUtils.renderString(response, JSONObject.toJSONString(ajaxResult));
32   - return false;
33   - }
34   - }
35   - return true;
36   - } else {
37   - return super.preHandle(request, response, handler);
38   - }
39   - }
40   -
41   - /**
42   - * 验证是否重复提交由子类实现具体的防重复提交的规则
43   - *
44   - * @param request
45   - * @return
46   - * @throws Exception
47   - */
48   - public abstract boolean isRepeatSubmit(HttpServletRequest request);
49   -}
ruoyi-common/src/main/java/com/ruoyi/common/filter/SameUrlDataInterceptor.java deleted
1   -package com.ruoyi.framework.interceptor.impl;
2   -
3   -import java.util.HashMap;
4   -import java.util.Map;
5   -import java.util.concurrent.TimeUnit;
6   -import javax.servlet.http.HttpServletRequest;
7   -
8   -import org.springframework.beans.factory.annotation.Autowired;
9   -import org.springframework.beans.factory.annotation.Value;
10   -import org.springframework.stereotype.Component;
11   -import com.alibaba.fastjson.JSONObject;
12   -import com.ruoyi.common.constant.Constants;
13   -import com.ruoyi.common.core.redis.RedisCache;
14   -import com.ruoyi.common.filter.RepeatedlyRequestWrapper;
15   -import com.ruoyi.common.utils.StringUtils;
16   -import com.ruoyi.common.utils.http.HttpHelper;
17   -import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;
18   -
19   -/**
20   - * 判断请求url和数据是否和上一次相同,
21   - * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。
22   - *
23   - * @author ruoyi
24   - */
25   -@Component
26   -public class SameUrlDataInterceptor extends RepeatSubmitInterceptor {
27   - public final String REPEAT_PARAMS = "repeatParams";
28   -
29   - public final String REPEAT_TIME = "repeatTime";
30   -
31   - // 令牌自定义标识
32   - @Value("${token.header}")
33   - private String header;
34   -
35   - @Autowired
36   - private RedisCache redisCache;
37   -
38   - /**
39   - * 间隔时间,单位:秒 默认10秒
40   - * <p>
41   - * 两次相同参数的请求,如果间隔时间大于该参数,系统不会认定为重复提交的数据
42   - */
43   - private int intervalTime = 10;
44   -
45   - public void setIntervalTime(int intervalTime) {
46   - this.intervalTime = intervalTime;
47   - }
48   -
49   - @SuppressWarnings("unchecked")
50   - @Override
51   - public boolean isRepeatSubmit(HttpServletRequest request) {
52   - String nowParams = "";
53   - if (request instanceof RepeatedlyRequestWrapper) {
54   - RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request;
55   - nowParams = HttpHelper.getBodyString(repeatedlyRequest);
56   - }
57   -
58   - // body参数为空,获取Parameter的数据
59   - if (StringUtils.isEmpty(nowParams)) {
60   - nowParams = JSONObject.toJSONString(request.getParameterMap());
61   - }
62   - Map<String, Object> nowDataMap = new HashMap<String, Object>();
63   - nowDataMap.put(REPEAT_PARAMS, nowParams);
64   - nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());
65   -
66   - // 请求地址(作为存放cache的key值)
67   - String url = request.getRequestURI();
68   -
69   - // 唯一值(没有消息头则使用请求地址)
70   - String submitKey = request.getHeader(header);
71   - if (StringUtils.isEmpty(submitKey)) {
72   - submitKey = url;
73   - }
74   -
75   - // 唯一标识(指定key + 消息头)
76   - String cache_repeat_key = Constants.REPEAT_SUBMIT_KEY + submitKey;
77   -
78   - Object sessionObj = redisCache.getCacheObject(cache_repeat_key);
79   - if (sessionObj != null) {
80   - Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;
81   - if (sessionMap.containsKey(url)) {
82   - Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url);
83   - if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap)) {
84   - return true;
85   - }
86   - }
87   - }
88   - Map<String, Object> cacheMap = new HashMap<String, Object>();
89   - cacheMap.put(url, nowDataMap);
90   - redisCache.setCacheObject(cache_repeat_key, cacheMap, intervalTime, TimeUnit.SECONDS);
91   - return false;
92   - }
93   -
94   - /**
95   - * 判断参数是否相同
96   - */
97   - private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap) {
98   - String nowParams = (String) nowMap.get(REPEAT_PARAMS);
99   - String preParams = (String) preMap.get(REPEAT_PARAMS);
100   - return nowParams.equals(preParams);
101   - }
102   -
103   - /**
104   - * 判断两次间隔时间
105   - */
106   - private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap) {
107   - long time1 = (Long) nowMap.get(REPEAT_TIME);
108   - long time2 = (Long) preMap.get(REPEAT_TIME);
109   - if ((time1 - time2) < (this.intervalTime * 1000)) {
110   - return true;
111   - }
112   - return false;
113   - }
114   -}
src/main/java/cn/iocoder/dashboard/common/exception/enums/GlobalErrorCodeConstants.java
... ... @@ -28,6 +28,9 @@ public interface GlobalErrorCodeConstants {
28 28  
29 29 ErrorCode INTERNAL_SERVER_ERROR = new ErrorCode(500, "系统异常");
30 30  
  31 + // ========== 自定义错误段 ==========
  32 + ErrorCode REPEATED_REQUESTS = new ErrorCode(900, "重复请求");
  33 +
31 34 ErrorCode UNKNOWN = new ErrorCode(999, "未知错误");
32 35  
33 36 static boolean isMatch(Integer code) {
... ...
src/main/java/cn/iocoder/dashboard/framework/dict/package-info.java
1 1 /**
2   - * 字典数据模块
  2 + * 字典数据模块,提供 {@link cn.iocoder.dashboard.framework.dict.core.util.DictUtils} 工具类
3 3 *
4 4 * 通过将字典缓存在内存中,保证性能
5 5 */
... ...
src/main/java/cn/iocoder/dashboard/framework/idempotent/config/IdempotentConfiguration.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.idempotent.config;
  2 +
  3 +import cn.iocoder.dashboard.framework.idempotent.core.aop.IdempotentAspect;
  4 +import cn.iocoder.dashboard.framework.idempotent.core.keyresolver.DefaultIdempotentKeyResolver;
  5 +import cn.iocoder.dashboard.framework.idempotent.core.keyresolver.ExpressionIdempotentKeyResolver;
  6 +import cn.iocoder.dashboard.framework.idempotent.core.keyresolver.IdempotentKeyResolver;
  7 +import cn.iocoder.dashboard.framework.idempotent.core.redis.IdempotentRedisDAO;
  8 +import org.springframework.boot.autoconfigure.AutoConfigureAfter;
  9 +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
  10 +import org.springframework.context.annotation.Bean;
  11 +import org.springframework.context.annotation.Configuration;
  12 +import org.springframework.data.redis.core.StringRedisTemplate;
  13 +
  14 +import java.util.List;
  15 +
  16 +@Configuration(proxyBeanMethods = false)
  17 +@AutoConfigureAfter(RedisAutoConfiguration.class)
  18 +public class IdempotentConfiguration {
  19 +
  20 + @Bean
  21 + public IdempotentAspect idempotentAspect(List<IdempotentKeyResolver> keyResolvers, IdempotentRedisDAO idempotentRedisDAO) {
  22 + return new IdempotentAspect(keyResolvers, idempotentRedisDAO);
  23 + }
  24 +
  25 + @Bean
  26 + public IdempotentRedisDAO idempotentRedisDAO(StringRedisTemplate stringRedisTemplate) {
  27 + return new IdempotentRedisDAO(stringRedisTemplate);
  28 + }
  29 +
  30 + // ========== 各种 IdempotentKeyResolver Bean ==========
  31 +
  32 + @Bean
  33 + public DefaultIdempotentKeyResolver defaultIdempotentKeyResolver() {
  34 + return new DefaultIdempotentKeyResolver();
  35 + }
  36 +
  37 + @Bean
  38 + public ExpressionIdempotentKeyResolver expressionIdempotentKeyResolver() {
  39 + return new ExpressionIdempotentKeyResolver();
  40 + }
  41 +
  42 +}
... ...
src/main/java/cn/iocoder/dashboard/framework/idempotent/core/annotation/Idempotent.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.idempotent.core.annotation;
  2 +
  3 +import cn.iocoder.dashboard.framework.idempotent.core.keyresolver.DefaultIdempotentKeyResolver;
  4 +import cn.iocoder.dashboard.framework.idempotent.core.keyresolver.IdempotentKeyResolver;
  5 +
  6 +import java.lang.annotation.ElementType;
  7 +import java.lang.annotation.Retention;
  8 +import java.lang.annotation.RetentionPolicy;
  9 +import java.lang.annotation.Target;
  10 +import java.util.concurrent.TimeUnit;
  11 +
  12 +/**
  13 + * 幂等注解
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Target({ElementType.METHOD})
  18 +@Retention(RetentionPolicy.RUNTIME)
  19 +public @interface Idempotent {
  20 +
  21 + /**
  22 + * 幂等的超时时间,默认为 1 秒
  23 + *
  24 + * 注意,如果执行时间超过它,请求还是会进来
  25 + */
  26 + int timeout() default 1;
  27 + /**
  28 + * 时间单位,默认为 SECONDS 秒
  29 + */
  30 + TimeUnit timeUnit() default TimeUnit.SECONDS;
  31 +
  32 + /**
  33 + * 提示信息,正在执行中的提示
  34 + */
  35 + String message() default "重复请求,请稍后重试";
  36 +
  37 + /**
  38 + * 使用的 Key 解析器
  39 + */
  40 + Class<? extends IdempotentKeyResolver> keyResolver() default DefaultIdempotentKeyResolver.class;
  41 + /**
  42 + * 使用的 Key 参数
  43 + */
  44 + String keyArg() default "";
  45 +
  46 +}
... ...
src/main/java/cn/iocoder/dashboard/framework/idempotent/core/aop/IdempotentAspect.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.idempotent.core.aop;
  2 +
  3 +import cn.iocoder.dashboard.common.exception.ServiceException;
  4 +import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants;
  5 +import cn.iocoder.dashboard.framework.idempotent.core.annotation.Idempotent;
  6 +import cn.iocoder.dashboard.framework.idempotent.core.keyresolver.IdempotentKeyResolver;
  7 +import cn.iocoder.dashboard.framework.idempotent.core.redis.IdempotentRedisDAO;
  8 +import cn.iocoder.dashboard.util.collection.CollectionUtils;
  9 +import lombok.extern.slf4j.Slf4j;
  10 +import org.aspectj.lang.JoinPoint;
  11 +import org.aspectj.lang.annotation.Aspect;
  12 +import org.aspectj.lang.annotation.Before;
  13 +import org.springframework.util.Assert;
  14 +
  15 +import java.util.List;
  16 +import java.util.Map;
  17 +
  18 +/**
  19 + * 拦截声明了 {@link Idempotent} 注解的方法,实现幂等操作
  20 + *
  21 + * @author 芋道源码
  22 + */
  23 +@Aspect
  24 +@Slf4j
  25 +public class IdempotentAspect {
  26 +
  27 + /**
  28 + * IdempotentKeyResolver 集合
  29 + */
  30 + private final Map<Class<? extends IdempotentKeyResolver>, IdempotentKeyResolver> keyResolvers;
  31 +
  32 + private final IdempotentRedisDAO idempotentRedisDAO;
  33 +
  34 + public IdempotentAspect(List<IdempotentKeyResolver> keyResolvers, IdempotentRedisDAO idempotentRedisDAO) {
  35 + this.keyResolvers = CollectionUtils.convertMap(keyResolvers, IdempotentKeyResolver::getClass);
  36 + this.idempotentRedisDAO = idempotentRedisDAO;
  37 + }
  38 +
  39 + @Before("@annotation(idempotent)")
  40 + public void beforePointCut(JoinPoint joinPoint, Idempotent idempotent) {
  41 + // 获得 IdempotentKeyResolver
  42 + IdempotentKeyResolver keyResolver = keyResolvers.get(idempotent.keyResolver());
  43 + Assert.notNull(keyResolver, "找不到对应的 IdempotentKeyResolver");
  44 + // 解析 Key
  45 + String key = keyResolver.resolver(joinPoint, idempotent);
  46 +
  47 + // 锁定 Key。
  48 + boolean success = idempotentRedisDAO.setIfAbsent(key, idempotent.timeout(), idempotent.timeUnit());
  49 + // 锁定失败,抛出异常
  50 + if (!success) {
  51 + log.info("[beforePointCut][方法({}) 参数({}) 存在重复请求]", joinPoint.getSignature().toString(), joinPoint.getArgs());
  52 + throw new ServiceException(GlobalErrorCodeConstants.REPEATED_REQUESTS.getCode(), idempotent.message());
  53 + }
  54 + }
  55 +
  56 +}
... ...
src/main/java/cn/iocoder/dashboard/framework/idempotent/core/keyresolver/DefaultIdempotentKeyResolver.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.idempotent.core.keyresolver;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.hutool.crypto.SecureUtil;
  5 +import cn.iocoder.dashboard.framework.idempotent.core.annotation.Idempotent;
  6 +import org.aspectj.lang.JoinPoint;
  7 +
  8 +/**
  9 + * 默认幂等 Key 解析器,使用方法名 + 方法参数,组装成一个 Key
  10 + *
  11 + * 为了避免 Key 过长,使用 MD5 进行“压缩”
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +public class DefaultIdempotentKeyResolver implements IdempotentKeyResolver {
  16 +
  17 + @Override
  18 + public String resolver(JoinPoint joinPoint, Idempotent idempotent) {
  19 + String methodName = joinPoint.getSignature().toString();
  20 + String argsStr = StrUtil.join(",", joinPoint.getArgs());
  21 + return SecureUtil.md5(methodName + argsStr);
  22 + }
  23 +
  24 +}
... ...
src/main/java/cn/iocoder/dashboard/framework/idempotent/core/keyresolver/ExpressionIdempotentKeyResolver.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.idempotent.core.keyresolver;
  2 +
  3 +import cn.iocoder.dashboard.framework.idempotent.core.annotation.Idempotent;
  4 +import org.aspectj.lang.JoinPoint;
  5 +
  6 +/**
  7 + * 基于 Spring EL 表达式,
  8 + *
  9 + * @author 芋道源码
  10 + */
  11 +public class ExpressionIdempotentKeyResolver implements IdempotentKeyResolver {
  12 +
  13 + @Override
  14 + public String resolver(JoinPoint joinPoint, Idempotent idempotent) {
  15 + // TODO 稍后实现
  16 + return null;
  17 + }
  18 +
  19 +}
... ...
src/main/java/cn/iocoder/dashboard/framework/idempotent/core/keyresolver/IdempotentKeyResolver.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.idempotent.core.keyresolver;
  2 +
  3 +import cn.iocoder.dashboard.framework.idempotent.core.annotation.Idempotent;
  4 +import org.aspectj.lang.JoinPoint;
  5 +
  6 +/**
  7 + * 幂等 Key 解析器接口
  8 + *
  9 + * @author 芋道源码
  10 + */
  11 +public interface IdempotentKeyResolver {
  12 +
  13 + /**
  14 + * 解析一个 Key
  15 + *
  16 + * @param idempotent 幂等注解
  17 + * @param joinPoint AOP 切面
  18 + * @return Key
  19 + */
  20 + String resolver(JoinPoint joinPoint, Idempotent idempotent);
  21 +
  22 +}
... ...
src/main/java/cn/iocoder/dashboard/framework/idempotent/core/redis/IdempotentRedisDAO.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.idempotent.core.redis;
  2 +
  3 +import cn.iocoder.dashboard.framework.redis.core.RedisKeyDefine;
  4 +import lombok.AllArgsConstructor;
  5 +import org.springframework.data.redis.core.StringRedisTemplate;
  6 +
  7 +import java.util.concurrent.TimeUnit;
  8 +
  9 +import static cn.iocoder.dashboard.framework.redis.core.RedisKeyDefine.KeyTypeEnum.STRING;
  10 +
  11 +/**
  12 + * 幂等 Redis DAO
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +@AllArgsConstructor
  17 +public class IdempotentRedisDAO {
  18 +
  19 + private static final RedisKeyDefine IDEMPOTENT = new RedisKeyDefine("幂等操作",
  20 + "idempotent:%s", // 参数为 uuid
  21 + STRING, String.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC);
  22 +
  23 + private final StringRedisTemplate redisTemplate;
  24 +
  25 + public Boolean setIfAbsent(String key, long timeout, TimeUnit timeUnit) {
  26 + String redisKey = formatKey(key);
  27 + return redisTemplate.opsForValue().setIfAbsent(redisKey, "", timeout, timeUnit);
  28 + }
  29 +
  30 + private static String formatKey(String key) {
  31 + return String.format(IDEMPOTENT.getKeyTemplate(), key);
  32 + }
  33 +
  34 +}
... ...
src/main/java/cn/iocoder/dashboard/framework/idempotent/package-info.java 0 → 100644
  1 +/**
  2 + * 幂等组件,参考 https://github.com/it4alla/idempotent 项目实现
  3 + */
  4 +package cn.iocoder.dashboard.framework.idempotent;
... ...
src/main/java/cn/iocoder/dashboard/framework/logger/operatelog/core/annotations/OperateLog.java
... ... @@ -9,6 +9,11 @@ import java.lang.annotation.Retention;
9 9 import java.lang.annotation.RetentionPolicy;
10 10 import java.lang.annotation.Target;
11 11  
  12 +/**
  13 + * 操作日志注解
  14 + *
  15 + * @author 芋道源码
  16 + */
12 17 @Target({ElementType.METHOD})
13 18 @Retention(RetentionPolicy.RUNTIME)
14 19 public @interface OperateLog {
... ...
src/main/java/cn/iocoder/dashboard/modules/infra/controller/config/InfConfigController.java
... ... @@ -4,6 +4,7 @@ import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil;
4 4 import cn.iocoder.dashboard.common.pojo.CommonResult;
5 5 import cn.iocoder.dashboard.common.pojo.PageResult;
6 6 import cn.iocoder.dashboard.framework.excel.core.util.ExcelUtils;
  7 +import cn.iocoder.dashboard.framework.idempotent.core.annotation.Idempotent;
7 8 import cn.iocoder.dashboard.modules.infra.controller.config.vo.*;
8 9 import cn.iocoder.dashboard.modules.infra.convert.config.InfConfigConvert;
9 10 import cn.iocoder.dashboard.modules.infra.dal.dataobject.config.InfConfigDO;
... ... @@ -91,7 +92,7 @@ public class InfConfigController {
91 92 @PostMapping("/create")
92 93 // @PreAuthorize("@ss.hasPermi('infra:config:add')")
93 94 // @Log(title = "参数管理", businessType = BusinessType.INSERT)
94   -// @RepeatSubmit
  95 + @Idempotent(timeout = 10)
95 96 public CommonResult<Long> createConfig(@Validated @RequestBody InfConfigCreateReqVO reqVO) {
96 97 return success(configService.createConfig(reqVO));
97 98 }
... ... @@ -100,7 +101,8 @@ public class InfConfigController {
100 101 @PutMapping("/update")
101 102 // @PreAuthorize("@ss.hasPermi('infra:config:edit')")
102 103 // @Log(title = "参数管理", businessType = BusinessType.UPDATE)
103   - public CommonResult<Boolean> edit(@Validated @RequestBody InfConfigUpdateReqVO reqVO) {
  104 + @Idempotent(timeout = 60)
  105 + public CommonResult<Boolean> updateConfig(@Validated @RequestBody InfConfigUpdateReqVO reqVO) {
104 106 configService.updateConfig(reqVO);
105 107 return success(true);
106 108 }
... ...
src/main/java/cn/iocoder/dashboard/modules/infra/controller/config/vo/InfConfigUpdateReqVO.java
... ... @@ -4,12 +4,14 @@ import io.swagger.annotations.ApiModel;
4 4 import io.swagger.annotations.ApiModelProperty;
5 5 import lombok.Data;
6 6 import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
7 8  
8 9 import javax.validation.constraints.NotNull;
9 10  
10 11 @ApiModel("参数配置创建 Request VO")
11 12 @Data
12 13 @EqualsAndHashCode(callSuper = true)
  14 +@ToString(callSuper = true)
13 15 public class InfConfigUpdateReqVO extends InfConfigBaseVO {
14 16  
15 17 @ApiModelProperty(value = "参数配置序号", required = true, example = "1024")
... ...
src/main/java/cn/iocoder/dashboard/modules/system/dal/redis/RedisKeyConstants.java renamed to 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
12 12 *
13 13 * @author 芋道源码
14 14 */
15   -public interface RedisKeyConstants {
  15 +public interface SysRedisKeyConstants {
16 16  
17 17 RedisKeyDefine LOGIN_USER = new RedisKeyDefine("登陆用户的缓存",
18 18 "login_user:%s", // 参数为 sessionId
... ...
src/main/java/cn/iocoder/dashboard/modules/system/dal/redis/auth/SysLoginUserRedisDAO.java
... ... @@ -7,7 +7,7 @@ import org.springframework.stereotype.Repository;
7 7  
8 8 import javax.annotation.Resource;
9 9  
10   -import static cn.iocoder.dashboard.modules.system.dal.redis.RedisKeyConstants.LOGIN_USER;
  10 +import static cn.iocoder.dashboard.modules.system.dal.redis.SysRedisKeyConstants.LOGIN_USER;
11 11  
12 12 /**
13 13 * {@link LoginUser} 的 RedisDAO
... ...
src/main/java/cn/iocoder/dashboard/modules/system/dal/redis/common/SysCaptchaRedisDAO.java
... ... @@ -6,7 +6,7 @@ import org.springframework.stereotype.Repository;
6 6 import javax.annotation.Resource;
7 7 import java.time.Duration;
8 8  
9   -import static cn.iocoder.dashboard.modules.system.dal.redis.RedisKeyConstants.CAPTCHA_CODE;
  9 +import static cn.iocoder.dashboard.modules.system.dal.redis.SysRedisKeyConstants.CAPTCHA_CODE;
10 10  
11 11 /**
12 12 * 验证码的 Redis DAO
... ...
src/main/resources/application-dev.yaml
... ... @@ -98,6 +98,8 @@ spring:
98 98 # Spring Boot Admin Client 客户端的相关配置
99 99 client:
100 100 url: http://127.0.0.1:${server.port}/${spring.boot.admin.context-path} # 设置 Spring Boot Admin Server 地址
  101 + instance:
  102 + prefer-ip: true # 注册实例时,优先使用 IP
101 103 # Spring Boot Admin Server 服务端的相关配置
102 104 context-path: /admin # 配置 Spring
103 105  
... ...
src/main/resources/application-local.yaml
... ... @@ -99,7 +99,7 @@ spring:
99 99 client:
100 100 url: http://127.0.0.1:${server.port}/${spring.boot.admin.context-path} # 设置 Spring Boot Admin Server 地址
101 101 instance:
102   - prefer-ip: true
  102 + prefer-ip: true # 注册实例时,优先使用 IP
103 103 # Spring Boot Admin Server 服务端的相关配置
104 104 context-path: /admin # 配置 Spring
105 105  
... ...