mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-22 23:31:52 +08:00
基于 Redis 实现幂等性操作
This commit is contained in:
parent
8fa9ba8ec6
commit
1c1f1c49fa
@ -45,6 +45,7 @@
|
|||||||
1. 代码生成:前后端代码的生成(Java、Vue、SQL),支持 CRUD 下载
|
1. 代码生成:前后端代码的生成(Java、Vue、SQL),支持 CRUD 下载
|
||||||
1. 系统接口:基于 Swagger 自动生成相关的 RESTful API 接口文档
|
1. 系统接口:基于 Swagger 自动生成相关的 RESTful API 接口文档
|
||||||
1. 数据库文档:基于 Screw 自动生成数据库文档
|
1. 数据库文档:基于 Screw 自动生成数据库文档
|
||||||
|
1. 幂等组件:基于 Redis 实现幂等组件,解决重复请求问题
|
||||||
|
|
||||||
## 在线体验
|
## 在线体验
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package cn.iocoder.dashboard.framework.idempotent.config;
|
package cn.iocoder.dashboard.framework.idempotent.config;
|
||||||
|
|
||||||
import cn.iocoder.dashboard.framework.idempotent.core.aop.IdempotentAspect;
|
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.impl.DefaultIdempotentKeyResolver;
|
||||||
import cn.iocoder.dashboard.framework.idempotent.core.keyresolver.ExpressionIdempotentKeyResolver;
|
import cn.iocoder.dashboard.framework.idempotent.core.keyresolver.impl.ExpressionIdempotentKeyResolver;
|
||||||
import cn.iocoder.dashboard.framework.idempotent.core.keyresolver.IdempotentKeyResolver;
|
import cn.iocoder.dashboard.framework.idempotent.core.keyresolver.IdempotentKeyResolver;
|
||||||
import cn.iocoder.dashboard.framework.idempotent.core.redis.IdempotentRedisDAO;
|
import cn.iocoder.dashboard.framework.idempotent.core.redis.IdempotentRedisDAO;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package cn.iocoder.dashboard.framework.idempotent.core.annotation;
|
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.impl.DefaultIdempotentKeyResolver;
|
||||||
import cn.iocoder.dashboard.framework.idempotent.core.keyresolver.IdempotentKeyResolver;
|
import cn.iocoder.dashboard.framework.idempotent.core.keyresolver.IdempotentKeyResolver;
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,8 +1,9 @@
|
|||||||
package cn.iocoder.dashboard.framework.idempotent.core.keyresolver;
|
package cn.iocoder.dashboard.framework.idempotent.core.keyresolver.impl;
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.crypto.SecureUtil;
|
import cn.hutool.crypto.SecureUtil;
|
||||||
import cn.iocoder.dashboard.framework.idempotent.core.annotation.Idempotent;
|
import cn.iocoder.dashboard.framework.idempotent.core.annotation.Idempotent;
|
||||||
|
import cn.iocoder.dashboard.framework.idempotent.core.keyresolver.IdempotentKeyResolver;
|
||||||
import org.aspectj.lang.JoinPoint;
|
import org.aspectj.lang.JoinPoint;
|
||||||
|
|
||||||
/**
|
/**
|
@ -0,0 +1,63 @@
|
|||||||
|
package cn.iocoder.dashboard.framework.idempotent.core.keyresolver.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import cn.iocoder.dashboard.framework.idempotent.core.annotation.Idempotent;
|
||||||
|
import cn.iocoder.dashboard.framework.idempotent.core.keyresolver.IdempotentKeyResolver;
|
||||||
|
import org.aspectj.lang.JoinPoint;
|
||||||
|
import org.aspectj.lang.reflect.MethodSignature;
|
||||||
|
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||||
|
import org.springframework.core.ParameterNameDiscoverer;
|
||||||
|
import org.springframework.expression.Expression;
|
||||||
|
import org.springframework.expression.ExpressionParser;
|
||||||
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
|
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于 Spring EL 表达式,
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
public class ExpressionIdempotentKeyResolver implements IdempotentKeyResolver {
|
||||||
|
|
||||||
|
private final ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
|
||||||
|
private final ExpressionParser expressionParser = new SpelExpressionParser();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String resolver(JoinPoint joinPoint, Idempotent idempotent) {
|
||||||
|
// 获得被拦截方法参数名列表
|
||||||
|
Method method = getMethod(joinPoint);
|
||||||
|
Object[] args = joinPoint.getArgs();
|
||||||
|
String[] parameterNames = this.parameterNameDiscoverer.getParameterNames(method);
|
||||||
|
// 准备 Spring EL 表达式解析的上下文
|
||||||
|
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
|
||||||
|
if (ArrayUtil.isNotEmpty(parameterNames)) {
|
||||||
|
for (int i = 0; i < parameterNames.length; i++) {
|
||||||
|
evaluationContext.setVariable(parameterNames[i], args[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析参数
|
||||||
|
Expression expression = expressionParser.parseExpression(idempotent.keyArg());
|
||||||
|
return expression.getValue(evaluationContext, String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Method getMethod(JoinPoint point) {
|
||||||
|
// 处理,声明在类上的情况
|
||||||
|
MethodSignature signature = (MethodSignature) point.getSignature();
|
||||||
|
Method method = signature.getMethod();
|
||||||
|
if (!method.getDeclaringClass().isInterface()) {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理,声明在接口上的情况
|
||||||
|
try {
|
||||||
|
return point.getTarget().getClass().getDeclaredMethod(
|
||||||
|
point.getSignature().getName(), method.getParameterTypes());
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,12 @@
|
|||||||
/**
|
/**
|
||||||
* 幂等组件,参考 https://github.com/it4alla/idempotent 项目实现
|
* 幂等组件,参考 https://github.com/it4alla/idempotent 项目实现
|
||||||
|
* 实现原理是,相同参数的方法,一段时间内,有且仅能执行一次。通过这样的方式,保证幂等性。
|
||||||
|
*
|
||||||
|
* 使用场景:例如说,用户快速的双击了某个按钮,前端没有禁用该按钮,导致发送了两次重复的请求。
|
||||||
|
*
|
||||||
|
* 和 it4alla/idempotent 组件的差异点,主要体现在两点:
|
||||||
|
* 1. 我们去掉了 @Idempotent 注解的 delKey 属性。原因是,本质上 delKey 为 true 时,实现的是分布式锁的能力
|
||||||
|
* 此时,我们偏向使用 Lock4j 组件。原则上,一个组件只提供一种单一的能力。
|
||||||
|
* 2. 考虑到组件的通用性,我们并未像 it4alla/idempotent 组件一样使用 Redisson RMap 结构,而是直接使用 Redis 的 String 数据格式。
|
||||||
*/
|
*/
|
||||||
package cn.iocoder.dashboard.framework.idempotent;
|
package cn.iocoder.dashboard.framework.idempotent;
|
||||||
|
@ -5,6 +5,7 @@ import cn.iocoder.dashboard.common.pojo.CommonResult;
|
|||||||
import cn.iocoder.dashboard.common.pojo.PageResult;
|
import cn.iocoder.dashboard.common.pojo.PageResult;
|
||||||
import cn.iocoder.dashboard.framework.excel.core.util.ExcelUtils;
|
import cn.iocoder.dashboard.framework.excel.core.util.ExcelUtils;
|
||||||
import cn.iocoder.dashboard.framework.idempotent.core.annotation.Idempotent;
|
import cn.iocoder.dashboard.framework.idempotent.core.annotation.Idempotent;
|
||||||
|
import cn.iocoder.dashboard.framework.idempotent.core.keyresolver.impl.ExpressionIdempotentKeyResolver;
|
||||||
import cn.iocoder.dashboard.modules.infra.controller.config.vo.*;
|
import cn.iocoder.dashboard.modules.infra.controller.config.vo.*;
|
||||||
import cn.iocoder.dashboard.modules.infra.convert.config.InfConfigConvert;
|
import cn.iocoder.dashboard.modules.infra.convert.config.InfConfigConvert;
|
||||||
import cn.iocoder.dashboard.modules.infra.dal.dataobject.config.InfConfigDO;
|
import cn.iocoder.dashboard.modules.infra.dal.dataobject.config.InfConfigDO;
|
||||||
@ -92,7 +93,7 @@ public class InfConfigController {
|
|||||||
@PostMapping("/create")
|
@PostMapping("/create")
|
||||||
// @PreAuthorize("@ss.hasPermi('infra:config:add')")
|
// @PreAuthorize("@ss.hasPermi('infra:config:add')")
|
||||||
// @Log(title = "参数管理", businessType = BusinessType.INSERT)
|
// @Log(title = "参数管理", businessType = BusinessType.INSERT)
|
||||||
@Idempotent(timeout = 10)
|
@Idempotent(timeout = 60, keyResolver = ExpressionIdempotentKeyResolver.class, keyArg = "#reqVO.key")
|
||||||
public CommonResult<Long> createConfig(@Validated @RequestBody InfConfigCreateReqVO reqVO) {
|
public CommonResult<Long> createConfig(@Validated @RequestBody InfConfigCreateReqVO reqVO) {
|
||||||
return success(configService.createConfig(reqVO));
|
return success(configService.createConfig(reqVO));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user