add 增加 ruoyi-common-idempotent 分布式幂等模块
parent
42dd74f196
commit
6afbf8734e
|
|
@ -26,6 +26,7 @@
|
||||||
<module>ruoyi-common-seata</module>
|
<module>ruoyi-common-seata</module>
|
||||||
<module>ruoyi-common-loadbalancer</module>
|
<module>ruoyi-common-loadbalancer</module>
|
||||||
<module>ruoyi-common-oss</module>
|
<module>ruoyi-common-oss</module>
|
||||||
|
<module>ruoyi-common-idempotent</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<artifactId>ruoyi-common</artifactId>
|
<artifactId>ruoyi-common</artifactId>
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,12 @@
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.ruoyi</groupId>
|
||||||
|
<artifactId>ruoyi-common-idempotent</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,10 @@ public interface Constants {
|
||||||
*/
|
*/
|
||||||
long CAPTCHA_EXPIRATION = 2;
|
long CAPTCHA_EXPIRATION = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 防重提交 redis key
|
||||||
|
*/
|
||||||
|
String REPEAT_SUBMIT_KEY = "repeat_submit:";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 参数管理 cache key
|
* 参数管理 cache key
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,40 @@
|
||||||
#错误消息
|
#\u9519\u8BEF\u6D88\u606F
|
||||||
not.null=* 必须填写
|
not.null=* \u5FC5\u987B\u586B\u5199
|
||||||
user.jcaptcha.not.blank=验证码不能为空
|
user.jcaptcha.not.blank=\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A
|
||||||
user.jcaptcha.error=验证码错误
|
user.jcaptcha.error=\u9A8C\u8BC1\u7801\u9519\u8BEF
|
||||||
user.jcaptcha.expire=验证码已失效
|
user.jcaptcha.expire=\u9A8C\u8BC1\u7801\u5DF2\u5931\u6548
|
||||||
user.not.exists=对不起, 您的账号:{0} 不存在.
|
user.not.exists=\u5BF9\u4E0D\u8D77, \u60A8\u7684\u8D26\u53F7\uFF1A{0} \u4E0D\u5B58\u5728.
|
||||||
user.password.not.match=用户不存在/密码错误
|
user.password.not.match=\u7528\u6237\u4E0D\u5B58\u5728/\u5BC6\u7801\u9519\u8BEF
|
||||||
user.password.retry.limit.count=密码输入错误{0}次
|
user.password.retry.limit.count=\u5BC6\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21
|
||||||
user.password.retry.limit.exceed=密码错误次数过多,帐户锁定{0}分钟
|
user.password.retry.limit.exceed=\u5BC6\u7801\u9519\u8BEF\u6B21\u6570\u8FC7\u591A\uFF0C\u5E10\u6237\u9501\u5B9A{0}\u5206\u949F
|
||||||
user.password.delete=对不起,您的账号:{0} 已被删除
|
user.password.delete=\u5BF9\u4E0D\u8D77\uFF0C\u60A8\u7684\u8D26\u53F7\uFF1A{0} \u5DF2\u88AB\u5220\u9664
|
||||||
user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员
|
user.blocked=\u5BF9\u4E0D\u8D77\uFF0C\u60A8\u7684\u8D26\u53F7\uFF1A{0} \u5DF2\u7981\u7528\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
|
||||||
role.blocked=角色已封禁,请联系管理员
|
role.blocked=\u89D2\u8272\u5DF2\u5C01\u7981\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
|
||||||
user.logout.success=退出成功
|
user.logout.success=\u9000\u51FA\u6210\u529F
|
||||||
length.not.valid=长度必须在{min}到{max}个字符之间
|
length.not.valid=\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4
|
||||||
user.username.not.blank=用户名不能为空
|
user.username.not.blank=\u7528\u6237\u540D\u4E0D\u80FD\u4E3A\u7A7A
|
||||||
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
|
user.username.not.valid=* 2\u523020\u4E2A\u6C49\u5B57\u3001\u5B57\u6BCD\u3001\u6570\u5B57\u6216\u4E0B\u5212\u7EBF\u7EC4\u6210\uFF0C\u4E14\u5FC5\u987B\u4EE5\u975E\u6570\u5B57\u5F00\u5934
|
||||||
user.username.length.valid=账户长度必须在{min}到{max}个字符之间
|
user.username.length.valid=\u8D26\u6237\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4
|
||||||
user.password.not.blank=用户密码不能为空
|
user.password.not.blank=\u7528\u6237\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A
|
||||||
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
|
user.password.length.valid=\u7528\u6237\u5BC6\u7801\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4
|
||||||
user.password.not.valid=* 5-50个字符
|
user.password.not.valid=* 5-50\u4E2A\u5B57\u7B26
|
||||||
user.email.not.valid=邮箱格式错误
|
user.email.not.valid=\u90AE\u7BB1\u683C\u5F0F\u9519\u8BEF
|
||||||
user.mobile.phone.number.not.valid=手机号格式错误
|
user.mobile.phone.number.not.valid=\u624B\u673A\u53F7\u683C\u5F0F\u9519\u8BEF
|
||||||
user.login.success=登录成功
|
user.login.success=\u767B\u5F55\u6210\u529F
|
||||||
user.register.success=注册成功
|
user.register.success=\u6CE8\u518C\u6210\u529F
|
||||||
user.register.save.error=保存用户 {0} 失败,注册账号已存在
|
user.register.save.error=\u4FDD\u5B58\u7528\u6237 {0} \u5931\u8D25\uFF0C\u6CE8\u518C\u8D26\u53F7\u5DF2\u5B58\u5728
|
||||||
user.register.error=注册失败,请联系系统管理人员
|
user.register.error=\u6CE8\u518C\u5931\u8D25\uFF0C\u8BF7\u8054\u7CFB\u7CFB\u7EDF\u7BA1\u7406\u4EBA\u5458
|
||||||
user.notfound=请重新登录
|
user.notfound=\u8BF7\u91CD\u65B0\u767B\u5F55
|
||||||
user.forcelogout=管理员强制退出,请重新登录
|
user.forcelogout=\u7BA1\u7406\u5458\u5F3A\u5236\u9000\u51FA\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55
|
||||||
user.unknown.error=未知错误,请重新登录
|
user.unknown.error=\u672A\u77E5\u9519\u8BEF\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55
|
||||||
##文件上传消息
|
##\u6587\u4EF6\u4E0A\u4F20\u6D88\u606F
|
||||||
upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB!
|
upload.exceed.maxSize=\u4E0A\u4F20\u7684\u6587\u4EF6\u5927\u5C0F\u8D85\u51FA\u9650\u5236\u7684\u6587\u4EF6\u5927\u5C0F\uFF01<br/>\u5141\u8BB8\u7684\u6587\u4EF6\u6700\u5927\u5927\u5C0F\u662F\uFF1A{0}MB\uFF01
|
||||||
upload.filename.exceed.length=上传的文件名最长{0}个字符
|
upload.filename.exceed.length=\u4E0A\u4F20\u7684\u6587\u4EF6\u540D\u6700\u957F{0}\u4E2A\u5B57\u7B26
|
||||||
##权限
|
##\u6743\u9650
|
||||||
no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
|
no.permission=\u60A8\u6CA1\u6709\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
|
||||||
no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
|
no.create.permission=\u60A8\u6CA1\u6709\u521B\u5EFA\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
|
||||||
no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
|
no.update.permission=\u60A8\u6CA1\u6709\u4FEE\u6539\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
|
||||||
no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
|
no.delete.permission=\u60A8\u6CA1\u6709\u5220\u9664\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
|
||||||
no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
|
no.export.permission=\u60A8\u6CA1\u6709\u5BFC\u51FA\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
|
||||||
no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]
|
no.view.permission=\u60A8\u6CA1\u6709\u67E5\u770B\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
|
||||||
|
repeat.submit.message=\u4E0D\u5141\u8BB8\u91CD\u590D\u63D0\u4EA4\uFF0C\u8BF7\u7A0D\u5019\u518D\u8BD5
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#错误消息
|
#\u9519\u8BEF\u6D88\u606F
|
||||||
not.null=* Required fill in
|
not.null=* Required fill in
|
||||||
user.jcaptcha.not.blank=Captcha cannot be blank
|
user.jcaptcha.not.blank=Captcha cannot be blank
|
||||||
user.jcaptcha.error=Captcha error
|
user.jcaptcha.error=Captcha error
|
||||||
|
|
@ -7,9 +7,9 @@ user.not.exists=Sorry, your account: {0} does not exist
|
||||||
user.password.not.match=User does not exist/Password error
|
user.password.not.match=User does not exist/Password error
|
||||||
user.password.retry.limit.count=Password input error {0} times
|
user.password.retry.limit.count=Password input error {0} times
|
||||||
user.password.retry.limit.exceed=Too many password errors, account locked for {0} minutes
|
user.password.retry.limit.exceed=Too many password errors, account locked for {0} minutes
|
||||||
user.password.delete=Sorry, your account:{0} has been deleted
|
user.password.delete=Sorry, your account\uFF1A{0} has been deleted
|
||||||
user.blocked=Sorry, your account: {0} has been disabled. Please contact the administrator
|
user.blocked=Sorry, your account: {0} has been disabled. Please contact the administrator
|
||||||
role.blocked=Role disabled,please contact administrators
|
role.blocked=Role disabled\uFF0Cplease contact administrators
|
||||||
user.logout.success=Exit successful
|
user.logout.success=Exit successful
|
||||||
length.not.valid=The length must be between {min} and {max} characters
|
length.not.valid=The length must be between {min} and {max} characters
|
||||||
user.username.not.blank=Username cannot be blank
|
user.username.not.blank=Username cannot be blank
|
||||||
|
|
@ -25,15 +25,16 @@ user.register.success=Register successful
|
||||||
user.register.save.error=Failed to save user {0}, The registered account already exists
|
user.register.save.error=Failed to save user {0}, The registered account already exists
|
||||||
user.register.error=Register failed, please contact system administrator
|
user.register.error=Register failed, please contact system administrator
|
||||||
user.notfound=Please login again
|
user.notfound=Please login again
|
||||||
user.forcelogout=The administrator is forced to exit,please login again
|
user.forcelogout=The administrator is forced to exit\uFF0Cplease login again
|
||||||
user.unknown.error=Unknown error, please login again
|
user.unknown.error=Unknown error, please login again
|
||||||
##文件上传消息
|
##\u6587\u4EF6\u4E0A\u4F20\u6D88\u606F
|
||||||
upload.exceed.maxSize=The uploaded file size exceeds the limit file size!<br/>the maximum allowed file size is:{0}MB!
|
upload.exceed.maxSize=The uploaded file size exceeds the limit file size\uFF01<br/>the maximum allowed file size is\uFF1A{0}MB\uFF01
|
||||||
upload.filename.exceed.length=The maximum length of uploaded file name is {0} characters
|
upload.filename.exceed.length=The maximum length of uploaded file name is {0} characters
|
||||||
##权限
|
##\u6743\u9650
|
||||||
no.permission=You do not have permission to the data,please contact your administrator to add permissions [{0}]
|
no.permission=You do not have permission to the data\uFF0Cplease contact your administrator to add permissions [{0}]
|
||||||
no.create.permission=You do not have permission to create data,please contact your administrator to add permissions [{0}]
|
no.create.permission=You do not have permission to create data\uFF0Cplease contact your administrator to add permissions [{0}]
|
||||||
no.update.permission=You do not have permission to modify data,please contact your administrator to add permissions [{0}]
|
no.update.permission=You do not have permission to modify data\uFF0Cplease contact your administrator to add permissions [{0}]
|
||||||
no.delete.permission=You do not have permission to delete data,please contact your administrator to add permissions [{0}]
|
no.delete.permission=You do not have permission to delete data\uFF0Cplease contact your administrator to add permissions [{0}]
|
||||||
no.export.permission=You do not have permission to export data,please contact your administrator to add permissions [{0}]
|
no.export.permission=You do not have permission to export data\uFF0Cplease contact your administrator to add permissions [{0}]
|
||||||
no.view.permission=You do not have permission to view data,please contact your administrator to add permissions [{0}]
|
no.view.permission=You do not have permission to view data\uFF0Cplease contact your administrator to add permissions [{0}]
|
||||||
|
repeat.submit.message=Repeat submit is not allowed, please try again later
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,40 @@
|
||||||
#错误消息
|
#\u9519\u8BEF\u6D88\u606F
|
||||||
not.null=* 必须填写
|
not.null=* \u5FC5\u987B\u586B\u5199
|
||||||
user.jcaptcha.not.blank=验证码不能为空
|
user.jcaptcha.not.blank=\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A
|
||||||
user.jcaptcha.error=验证码错误
|
user.jcaptcha.error=\u9A8C\u8BC1\u7801\u9519\u8BEF
|
||||||
user.jcaptcha.expire=验证码已失效
|
user.jcaptcha.expire=\u9A8C\u8BC1\u7801\u5DF2\u5931\u6548
|
||||||
user.not.exists=对不起, 您的账号:{0} 不存在.
|
user.not.exists=\u5BF9\u4E0D\u8D77, \u60A8\u7684\u8D26\u53F7\uFF1A{0} \u4E0D\u5B58\u5728.
|
||||||
user.password.not.match=用户不存在/密码错误
|
user.password.not.match=\u7528\u6237\u4E0D\u5B58\u5728/\u5BC6\u7801\u9519\u8BEF
|
||||||
user.password.retry.limit.count=密码输入错误{0}次
|
user.password.retry.limit.count=\u5BC6\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21
|
||||||
user.password.retry.limit.exceed=密码错误次数过多,帐户锁定{0}分钟
|
user.password.retry.limit.exceed=\u5BC6\u7801\u9519\u8BEF\u6B21\u6570\u8FC7\u591A\uFF0C\u5E10\u6237\u9501\u5B9A{0}\u5206\u949F
|
||||||
user.password.delete=对不起,您的账号:{0} 已被删除
|
user.password.delete=\u5BF9\u4E0D\u8D77\uFF0C\u60A8\u7684\u8D26\u53F7\uFF1A{0} \u5DF2\u88AB\u5220\u9664
|
||||||
user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员
|
user.blocked=\u5BF9\u4E0D\u8D77\uFF0C\u60A8\u7684\u8D26\u53F7\uFF1A{0} \u5DF2\u7981\u7528\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
|
||||||
role.blocked=角色已封禁,请联系管理员
|
role.blocked=\u89D2\u8272\u5DF2\u5C01\u7981\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
|
||||||
user.logout.success=退出成功
|
user.logout.success=\u9000\u51FA\u6210\u529F
|
||||||
length.not.valid=长度必须在{min}到{max}个字符之间
|
length.not.valid=\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4
|
||||||
user.username.not.blank=用户名不能为空
|
user.username.not.blank=\u7528\u6237\u540D\u4E0D\u80FD\u4E3A\u7A7A
|
||||||
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
|
user.username.not.valid=* 2\u523020\u4E2A\u6C49\u5B57\u3001\u5B57\u6BCD\u3001\u6570\u5B57\u6216\u4E0B\u5212\u7EBF\u7EC4\u6210\uFF0C\u4E14\u5FC5\u987B\u4EE5\u975E\u6570\u5B57\u5F00\u5934
|
||||||
user.username.length.valid=账户长度必须在{min}到{max}个字符之间
|
user.username.length.valid=\u8D26\u6237\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4
|
||||||
user.password.not.blank=用户密码不能为空
|
user.password.not.blank=\u7528\u6237\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A
|
||||||
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
|
user.password.length.valid=\u7528\u6237\u5BC6\u7801\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4
|
||||||
user.password.not.valid=* 5-50个字符
|
user.password.not.valid=* 5-50\u4E2A\u5B57\u7B26
|
||||||
user.email.not.valid=邮箱格式错误
|
user.email.not.valid=\u90AE\u7BB1\u683C\u5F0F\u9519\u8BEF
|
||||||
user.mobile.phone.number.not.valid=手机号格式错误
|
user.mobile.phone.number.not.valid=\u624B\u673A\u53F7\u683C\u5F0F\u9519\u8BEF
|
||||||
user.login.success=登录成功
|
user.login.success=\u767B\u5F55\u6210\u529F
|
||||||
user.register.success=注册成功
|
user.register.success=\u6CE8\u518C\u6210\u529F
|
||||||
user.register.save.error=保存用户 {0} 失败,注册账号已存在
|
user.register.save.error=\u4FDD\u5B58\u7528\u6237 {0} \u5931\u8D25\uFF0C\u6CE8\u518C\u8D26\u53F7\u5DF2\u5B58\u5728
|
||||||
user.register.error=注册失败,请联系系统管理人员
|
user.register.error=\u6CE8\u518C\u5931\u8D25\uFF0C\u8BF7\u8054\u7CFB\u7CFB\u7EDF\u7BA1\u7406\u4EBA\u5458
|
||||||
user.notfound=请重新登录
|
user.notfound=\u8BF7\u91CD\u65B0\u767B\u5F55
|
||||||
user.forcelogout=管理员强制退出,请重新登录
|
user.forcelogout=\u7BA1\u7406\u5458\u5F3A\u5236\u9000\u51FA\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55
|
||||||
user.unknown.error=未知错误,请重新登录
|
user.unknown.error=\u672A\u77E5\u9519\u8BEF\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55
|
||||||
##文件上传消息
|
##\u6587\u4EF6\u4E0A\u4F20\u6D88\u606F
|
||||||
upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB!
|
upload.exceed.maxSize=\u4E0A\u4F20\u7684\u6587\u4EF6\u5927\u5C0F\u8D85\u51FA\u9650\u5236\u7684\u6587\u4EF6\u5927\u5C0F\uFF01<br/>\u5141\u8BB8\u7684\u6587\u4EF6\u6700\u5927\u5927\u5C0F\u662F\uFF1A{0}MB\uFF01
|
||||||
upload.filename.exceed.length=上传的文件名最长{0}个字符
|
upload.filename.exceed.length=\u4E0A\u4F20\u7684\u6587\u4EF6\u540D\u6700\u957F{0}\u4E2A\u5B57\u7B26
|
||||||
##权限
|
##\u6743\u9650
|
||||||
no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
|
no.permission=\u60A8\u6CA1\u6709\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
|
||||||
no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
|
no.create.permission=\u60A8\u6CA1\u6709\u521B\u5EFA\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
|
||||||
no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
|
no.update.permission=\u60A8\u6CA1\u6709\u4FEE\u6539\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
|
||||||
no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
|
no.delete.permission=\u60A8\u6CA1\u6709\u5220\u9664\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
|
||||||
no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
|
no.export.permission=\u60A8\u6CA1\u6709\u5BFC\u51FA\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
|
||||||
no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]
|
no.view.permission=\u60A8\u6CA1\u6709\u67E5\u770B\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
|
||||||
|
repeat.submit.message=\u4E0D\u5141\u8BB8\u91CD\u590D\u63D0\u4EA4\uFF0C\u8BF7\u7A0D\u5019\u518D\u8BD5
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<groupId>com.ruoyi</groupId>
|
||||||
|
<artifactId>ruoyi-common</artifactId>
|
||||||
|
<version>0.8.0</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>ruoyi-common-idempotent</artifactId>
|
||||||
|
|
||||||
|
<description>
|
||||||
|
ruoyi-common-idempotent 幂等功能
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<!-- SpringBoot core -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.ruoyi</groupId>
|
||||||
|
<artifactId>ruoyi-common-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.ruoyi</groupId>
|
||||||
|
<artifactId>ruoyi-common-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.dev33</groupId>
|
||||||
|
<artifactId>sa-token-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-crypto</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.ruoyi.common.idempotent.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义注解防止表单重复提交
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Inherited
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
public @interface RepeatSubmit {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 间隔时间(ms),小于此时间视为重复提交
|
||||||
|
*/
|
||||||
|
int interval() default 5000;
|
||||||
|
|
||||||
|
TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提示消息 支持国际化 格式为 {code}
|
||||||
|
*/
|
||||||
|
String message() default "{repeat.submit.message}";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
package com.ruoyi.common.idempotent.aspectj;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.SaManager;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import cn.hutool.crypto.SecureUtil;
|
||||||
|
import com.ruoyi.common.core.constant.Constants;
|
||||||
|
import com.ruoyi.common.core.domain.R;
|
||||||
|
import com.ruoyi.common.core.exception.ServiceException;
|
||||||
|
import com.ruoyi.common.core.utils.JsonUtils;
|
||||||
|
import com.ruoyi.common.core.utils.MessageUtils;
|
||||||
|
import com.ruoyi.common.core.utils.ServletUtils;
|
||||||
|
import com.ruoyi.common.core.utils.StringUtils;
|
||||||
|
import com.ruoyi.common.idempotent.annotation.RepeatSubmit;
|
||||||
|
import com.ruoyi.common.redis.utils.RedisUtils;
|
||||||
|
import org.aspectj.lang.JoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.AfterReturning;
|
||||||
|
import org.aspectj.lang.annotation.AfterThrowing;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.aspectj.lang.annotation.Before;
|
||||||
|
import org.springframework.validation.BindingResult;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 防止重复提交(参考美团GTIS防重系统)
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Aspect
|
||||||
|
public class RepeatSubmitAspect {
|
||||||
|
|
||||||
|
private static final ThreadLocal<String> KEY_CACHE = new ThreadLocal<>();
|
||||||
|
|
||||||
|
@Before("@annotation(repeatSubmit)")
|
||||||
|
public void doBefore(JoinPoint point, RepeatSubmit repeatSubmit) throws Throwable {
|
||||||
|
// 如果注解不为0 则使用注解数值
|
||||||
|
long interval = repeatSubmit.timeUnit().toMillis(repeatSubmit.interval());
|
||||||
|
|
||||||
|
if (interval < 1000) {
|
||||||
|
throw new ServiceException("重复提交间隔时间不能小于'1'秒");
|
||||||
|
}
|
||||||
|
HttpServletRequest request = ServletUtils.getRequest();
|
||||||
|
String nowParams = argsArrayToString(point.getArgs());
|
||||||
|
|
||||||
|
// 请求地址(作为存放cache的key值)
|
||||||
|
String url = request.getRequestURI();
|
||||||
|
|
||||||
|
// 唯一值(没有消息头则使用请求地址)
|
||||||
|
String submitKey = StringUtils.trimToEmpty(request.getHeader(SaManager.getConfig().getTokenName()));
|
||||||
|
|
||||||
|
submitKey = SecureUtil.md5(submitKey + ":" + nowParams);
|
||||||
|
// 唯一标识(指定key + url + 消息头)
|
||||||
|
String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + url + submitKey;
|
||||||
|
String key = RedisUtils.getCacheObject(cacheRepeatKey);
|
||||||
|
if (key == null) {
|
||||||
|
RedisUtils.setCacheObject(cacheRepeatKey, "", interval, TimeUnit.MILLISECONDS);
|
||||||
|
KEY_CACHE.set(cacheRepeatKey);
|
||||||
|
} else {
|
||||||
|
String message = repeatSubmit.message();
|
||||||
|
if (StringUtils.startsWith(message, "{") && StringUtils.endsWith(message, "}")) {
|
||||||
|
message = MessageUtils.message(StringUtils.substring(message, 1, message.length() - 1));
|
||||||
|
}
|
||||||
|
throw new ServiceException(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理完请求后执行
|
||||||
|
*
|
||||||
|
* @param joinPoint 切点
|
||||||
|
*/
|
||||||
|
@AfterReturning(pointcut = "@annotation(repeatSubmit)", returning = "jsonResult")
|
||||||
|
public void doAfterReturning(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Object jsonResult) {
|
||||||
|
if (jsonResult instanceof R) {
|
||||||
|
R<?> r = (R<?>) jsonResult;
|
||||||
|
if (r.getCode() == R.SUCCESS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RedisUtils.deleteObject(KEY_CACHE.get());
|
||||||
|
KEY_CACHE.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拦截异常操作
|
||||||
|
*
|
||||||
|
* @param joinPoint 切点
|
||||||
|
* @param e 异常
|
||||||
|
*/
|
||||||
|
@AfterThrowing(value = "@annotation(repeatSubmit)", throwing = "e")
|
||||||
|
public void doAfterThrowing(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Exception e) {
|
||||||
|
RedisUtils.deleteObject(KEY_CACHE.get());
|
||||||
|
KEY_CACHE.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数拼装
|
||||||
|
*/
|
||||||
|
private String argsArrayToString(Object[] paramsArray) {
|
||||||
|
StringBuilder params = new StringBuilder();
|
||||||
|
if (paramsArray != null && paramsArray.length > 0) {
|
||||||
|
for (Object o : paramsArray) {
|
||||||
|
if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) {
|
||||||
|
try {
|
||||||
|
params.append(JsonUtils.toJsonString(o)).append(" ");
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params.toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否需要过滤的对象。
|
||||||
|
*
|
||||||
|
* @param o 对象信息。
|
||||||
|
* @return 如果是需要过滤的对象,则返回true;否则返回false。
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public boolean isFilterObject(final Object o) {
|
||||||
|
Class<?> clazz = o.getClass();
|
||||||
|
if (clazz.isArray()) {
|
||||||
|
return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
|
||||||
|
} else if (Collection.class.isAssignableFrom(clazz)) {
|
||||||
|
Collection collection = (Collection) o;
|
||||||
|
for (Object value : collection) {
|
||||||
|
return value instanceof MultipartFile;
|
||||||
|
}
|
||||||
|
} else if (Map.class.isAssignableFrom(clazz)) {
|
||||||
|
Map map = (Map) o;
|
||||||
|
for (Object value : map.entrySet()) {
|
||||||
|
Map.Entry entry = (Map.Entry) value;
|
||||||
|
return entry.getValue() instanceof MultipartFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
|
||||||
|
|| o instanceof BindingResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.ruoyi.common.idempotent.config;
|
||||||
|
|
||||||
|
import com.ruoyi.common.idempotent.aspectj.RepeatSubmitAspect;
|
||||||
|
import com.ruoyi.common.redis.config.RedisConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 幂等功能配置
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@AutoConfigureAfter(RedisConfiguration.class)
|
||||||
|
public class IdempotentAutoConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RepeatSubmitAspect repeatSubmitAspect() {
|
||||||
|
return new RepeatSubmitAspect();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||||
|
com.ruoyi.common.idempotent.config.IdempotentAutoConfiguration
|
||||||
Loading…
Reference in New Issue