From 25b4cad47e9240035553ee4ef3379ae2249fb43e Mon Sep 17 00:00:00 2001 From: sxu <602087911@qq.com> Date: Wed, 21 Aug 2024 11:09:06 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=88=E5=B9=B6=E9=87=8D=E5=A4=8D=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=94=B9=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/controller/BackReceiveController.java | 2 + .../controller/LeaseOutDetailsController.java | 3 + .../annotation/PreventRepeatSubmit.java | 24 ++ .../aspect/PreventRepeatSubmitAspect.java | 71 +++++ .../{ => common}/config/ExceptionEnum.java | 2 +- .../bonus/sgzb/common/utils/RedisCache.java | 269 ++++++++++++++++++ .../service/impl/LargeScreenServiceImpl.java | 2 +- .../InventoryAndWarehousingController.java | 2 + .../PurchaseMacodeInfoController.java | 2 + .../material/controller/ToDoController.java | 2 - 10 files changed, 375 insertions(+), 4 deletions(-) create mode 100644 sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/common/annotation/PreventRepeatSubmit.java create mode 100644 sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/common/aspect/PreventRepeatSubmitAspect.java rename sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/{ => common}/config/ExceptionEnum.java (96%) create mode 100644 sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/common/utils/RedisCache.java diff --git a/sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/app/controller/BackReceiveController.java b/sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/app/controller/BackReceiveController.java index 86ca5e43..13b99bd4 100644 --- a/sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/app/controller/BackReceiveController.java +++ b/sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/app/controller/BackReceiveController.java @@ -3,6 +3,7 @@ package com.bonus.sgzb.app.controller; import com.bonus.sgzb.base.api.domain.BackApplyInfo; import com.bonus.sgzb.app.domain.TmTask; import com.bonus.sgzb.app.service.*; +import com.bonus.sgzb.common.annotation.PreventRepeatSubmit; import com.bonus.sgzb.common.core.text.Convert; import com.bonus.sgzb.common.core.utils.ListPagingUtil; import com.bonus.sgzb.common.core.utils.ServletUtils; @@ -221,6 +222,7 @@ public class BackReceiveController extends BaseController { } @Log(title = "退料接收-结束任务", businessType = BusinessType.MATERIAL) + @PreventRepeatSubmit @PostMapping("/endBack") public AjaxResult endBack(@RequestBody BackApplyInfo record) { try { diff --git a/sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/app/controller/LeaseOutDetailsController.java b/sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/app/controller/LeaseOutDetailsController.java index f713d489..073b1f4f 100644 --- a/sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/app/controller/LeaseOutDetailsController.java +++ b/sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/app/controller/LeaseOutDetailsController.java @@ -6,6 +6,7 @@ import com.bonus.sgzb.app.domain.TmTask; import com.bonus.sgzb.app.service.LeaseOutDetailsService; import com.bonus.sgzb.base.api.domain.LeaseOutDetails; import com.bonus.sgzb.base.api.domain.MaMachine; +import com.bonus.sgzb.common.annotation.PreventRepeatSubmit; import com.bonus.sgzb.common.core.web.controller.BaseController; import com.bonus.sgzb.common.core.web.domain.AjaxResult; import com.bonus.sgzb.common.core.web.page.TableDataInfo; @@ -129,6 +130,7 @@ public class LeaseOutDetailsController extends BaseController { * @param record 出库内容 */ @Log(title = "领料出库", businessType = BusinessType.MATERIAL) + @PreventRepeatSubmit @PostMapping("/submitOut") public AjaxResult submitOut(@RequestBody LeaseOutDetails record) { return leaseOutDetailsService.submitOut(record); @@ -151,6 +153,7 @@ public class LeaseOutDetailsController extends BaseController { * @return */ @Log(title = "领料出库", businessType = BusinessType.MATERIAL) + @PreventRepeatSubmit @PostMapping("/submitOutRfid") public AjaxResult submitOutRfid(@RequestBody List recordList) { if (CollUtil.isEmpty(recordList)){ diff --git a/sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/common/annotation/PreventRepeatSubmit.java b/sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/common/annotation/PreventRepeatSubmit.java new file mode 100644 index 00000000..b978b2d0 --- /dev/null +++ b/sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/common/annotation/PreventRepeatSubmit.java @@ -0,0 +1,24 @@ +package com.bonus.sgzb.common.annotation; + +import java.lang.annotation.*; + +/** + * 自定义注解防止表单重复提交 + * + */ +@Inherited +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface PreventRepeatSubmit +{ + /** + * 间隔时间(s),小于此时间视为重复提交 + */ + public int interval() default 3; + + /** + * 提示消息 + */ + public String message() default "不允许重复提交,请稍候再试"; +} diff --git a/sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/common/aspect/PreventRepeatSubmitAspect.java b/sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/common/aspect/PreventRepeatSubmitAspect.java new file mode 100644 index 00000000..2e2922fe --- /dev/null +++ b/sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/common/aspect/PreventRepeatSubmitAspect.java @@ -0,0 +1,71 @@ +package com.bonus.sgzb.common.aspect; + +import com.alibaba.fastjson2.JSON; +import com.bonus.sgzb.common.annotation.PreventRepeatSubmit; +import com.bonus.sgzb.common.core.enums.HttpCodeEnum; +import com.bonus.sgzb.common.core.exception.BusinessException; +import com.bonus.sgzb.common.utils.RedisCache; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import java.lang.reflect.Method; +import java.util.concurrent.TimeUnit; + +@Aspect +@Component +public class PreventRepeatSubmitAspect { + private static final Logger LOG = LoggerFactory.getLogger(PreventRepeatSubmitAspect.class); + private static final String header = "Authorization"; + + @Autowired + private RedisCache redisCache; + + // 定义一个切入点 + @Pointcut("@annotation(com.bonus.sgzb.common.annotation.PreventRepeatSubmit)") + public void preventRepeatSubmit() { + + } + + @Around("preventRepeatSubmit()") + public Object checkPrs(ProceedingJoinPoint pjp) throws Throwable { + LOG.info("进入preventRepeatSubmit切面"); + //得到request对象 + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + String requestURI = request.getRequestURI(); + LOG.info("防重复提交的请求地址:{} ,请求方式:{}",requestURI,request.getMethod()); + LOG.info("防重复提交拦截到的类名:{} ,方法:{}",pjp.getTarget().getClass().getSimpleName(),pjp.getSignature().getName()); + + //获取请求参数 + Object[] args = pjp.getArgs(); + String argStr = JSON.toJSONString(args); + //这里替换是为了在redis可视化工具中方便查看 + argStr=argStr.replace(":","#"); + // 唯一值(没有消息头则使用请求地址) + String submitKey = request.getHeader(header).trim(); + // 唯一标识(指定key + url +参数+token) + String cacheRepeatKey = "repeat_submit:" + requestURI+":" +argStr+":"+ submitKey; + MethodSignature ms = (MethodSignature) pjp.getSignature(); + Method method=ms.getMethod(); + PreventRepeatSubmit preventRepeatSubmit=method.getAnnotation(PreventRepeatSubmit.class); + int interval = preventRepeatSubmit.interval(); + LOG.info("获取到preventRepeatSubmit的有效期时间"+interval); + //redis分布式锁 + Boolean aBoolean = redisCache.setNxCacheObject(cacheRepeatKey, 1, preventRepeatSubmit.interval(), TimeUnit.SECONDS); + //aBoolean为true则证明没有重复提交 + if(!aBoolean){ + throw new BusinessException(HttpCodeEnum.REPEATE_ERROR); + } + return pjp.proceed(); + } + +} diff --git a/sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/config/ExceptionEnum.java b/sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/common/config/ExceptionEnum.java similarity index 96% rename from sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/config/ExceptionEnum.java rename to sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/common/config/ExceptionEnum.java index 48088b75..dae0c667 100644 --- a/sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/config/ExceptionEnum.java +++ b/sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/common/config/ExceptionEnum.java @@ -1,4 +1,4 @@ -package com.bonus.sgzb.config; +package com.bonus.sgzb.common.config; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/common/utils/RedisCache.java b/sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/common/utils/RedisCache.java new file mode 100644 index 00000000..0c2ceba3 --- /dev/null +++ b/sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/common/utils/RedisCache.java @@ -0,0 +1,269 @@ +package com.bonus.sgzb.common.utils; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.BoundSetOperations; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * spring redis 工具类 + * + **/ +@Component +public class RedisCache +{ + @Autowired + public RedisTemplate redisTemplate; + + //添加分布式锁 + public Boolean setNxCacheObject(final String key, final T value,long lt,TimeUnit tu) + { + return redisTemplate.opsForValue().setIfAbsent(key,value,lt,tu); + } + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + */ + public void setCacheObject(final String key, final T value) + { + redisTemplate.opsForValue().set(key, value); + } + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @param timeout 时间 + * @param timeUnit 时间颗粒度 + */ + public void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) + { + redisTemplate.opsForValue().set(key, value, timeout, timeUnit); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout) + { + return expire(key, timeout, TimeUnit.SECONDS); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @param unit 时间单位 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout, final TimeUnit unit) + { + return redisTemplate.expire(key, timeout, unit); + } + + /** + * 获取有效时间 + * + * @param key Redis键 + * @return 有效时间 + */ + public long getExpire(final String key) + { + return redisTemplate.getExpire(key); + } + + /** + * 判断 key是否存在 + * + * @param key 键 + * @return true 存在 false不存在 + */ + public Boolean hasKey(String key) + { + return redisTemplate.hasKey(key); + } + + /** + * 获得缓存的基本对象。 + * + * @param key 缓存键值 + * @return 缓存键值对应的数据 + */ + public T getCacheObject(final String key) + { + ValueOperations operation = redisTemplate.opsForValue(); + return operation.get(key); + } + + /** + * 删除单个对象 + * + * @param key + */ + public boolean deleteObject(final String key) + { + return redisTemplate.delete(key); + } + + /** + * 删除集合对象 + * + * @param collection 多个对象 + * @return + */ + public boolean deleteObject(final Collection collection) + { + return redisTemplate.delete(collection) > 0; + } + + /** + * 缓存List数据 + * + * @param key 缓存的键值 + * @param dataList 待缓存的List数据 + * @return 缓存的对象 + */ + public long setCacheList(final String key, final List dataList) + { + Long count = redisTemplate.opsForList().rightPushAll(key, dataList); + return count == null ? 0 : count; + } + + /** + * 获得缓存的list对象 + * + * @param key 缓存的键值 + * @return 缓存键值对应的数据 + */ + public List getCacheList(final String key) + { + return redisTemplate.opsForList().range(key, 0, -1); + } + + /** + * 缓存Set + * + * @param key 缓存键值 + * @param dataSet 缓存的数据 + * @return 缓存数据的对象 + */ + public BoundSetOperations setCacheSet(final String key, final Set dataSet) + { + BoundSetOperations setOperation = redisTemplate.boundSetOps(key); + Iterator it = dataSet.iterator(); + while (it.hasNext()) + { + setOperation.add(it.next()); + } + return setOperation; + } + + /** + * 获得缓存的set + * + * @param key + * @return + */ + public Set getCacheSet(final String key) + { + return redisTemplate.opsForSet().members(key); + } + + /** + * 缓存Map + * + * @param key + * @param dataMap + */ + public void setCacheMap(final String key, final Map dataMap) + { + if (dataMap != null) { + redisTemplate.opsForHash().putAll(key, dataMap); + } + } + + /** + * 获得缓存的Map + * + * @param key + * @return + */ + public Map getCacheMap(final String key) + { + return redisTemplate.opsForHash().entries(key); + } + + /** + * 往Hash中存入数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @param value 值 + */ + public void setCacheMapValue(final String key, final String hKey, final T value) + { + redisTemplate.opsForHash().put(key, hKey, value); + } + + /** + * 获取Hash中的数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return Hash中的对象 + */ + public T getCacheMapValue(final String key, final String hKey) + { + HashOperations opsForHash = redisTemplate.opsForHash(); + return opsForHash.get(key, hKey); + } + + /** + * 获取多个Hash中的数据 + * + * @param key Redis键 + * @param hKeys Hash键集合 + * @return Hash对象集合 + */ + public List getMultiCacheMapValue(final String key, final Collection hKeys) + { + return redisTemplate.opsForHash().multiGet(key, hKeys); + } + + /** + * 删除Hash中的某条数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return 是否成功 + */ + public boolean deleteCacheMapValue(final String key, final String hKey) + { + return redisTemplate.opsForHash().delete(key, hKey) > 0; + } + + /** + * 获得缓存的基本对象列表 + * + * @param pattern 字符串前缀 + * @return 对象列表 + */ + public Collection keys(final String pattern) + { + return redisTemplate.keys(pattern); + } +} diff --git a/sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/largeScreen/service/impl/LargeScreenServiceImpl.java b/sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/largeScreen/service/impl/LargeScreenServiceImpl.java index d1131d89..5f29b7c9 100644 --- a/sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/largeScreen/service/impl/LargeScreenServiceImpl.java +++ b/sgzb-modules/sgzb-base/src/main/java/com/bonus/sgzb/largeScreen/service/impl/LargeScreenServiceImpl.java @@ -10,7 +10,7 @@ import com.bonus.sgzb.common.core.utils.HttpHelper; import com.bonus.sgzb.common.core.utils.StringHelper; import com.bonus.sgzb.common.core.web.domain.AjaxResult; import com.bonus.sgzb.common.redis.service.RedisService; -import com.bonus.sgzb.config.ExceptionEnum; +import com.bonus.sgzb.common.config.ExceptionEnum; import com.bonus.sgzb.largeScreen.domain.*; import com.bonus.sgzb.largeScreen.mapper.LargeScreenMapper; import com.bonus.sgzb.largeScreen.service.ILargeScreenService; diff --git a/sgzb-modules/sgzb-material/src/main/java/com/bonus/sgzb/material/controller/InventoryAndWarehousingController.java b/sgzb-modules/sgzb-material/src/main/java/com/bonus/sgzb/material/controller/InventoryAndWarehousingController.java index 57217c42..fc657d61 100644 --- a/sgzb-modules/sgzb-material/src/main/java/com/bonus/sgzb/material/controller/InventoryAndWarehousingController.java +++ b/sgzb-modules/sgzb-material/src/main/java/com/bonus/sgzb/material/controller/InventoryAndWarehousingController.java @@ -6,6 +6,7 @@ import com.bonus.sgzb.common.core.web.domain.AjaxResult; import com.bonus.sgzb.common.core.web.page.TableDataInfo; import com.bonus.sgzb.common.log.annotation.Log; import com.bonus.sgzb.common.log.enums.BusinessType; +import com.bonus.sgzb.material.annotation.PreventRepeatSubmit; import com.bonus.sgzb.material.domain.PutInStorageBean; import com.bonus.sgzb.material.service.InventoryAndWarehousingService; import io.swagger.annotations.Api; @@ -47,6 +48,7 @@ public class InventoryAndWarehousingController extends BaseController { * @return */ @ApiOperation(value = "新增入库盘点") + @PreventRepeatSubmit @PostMapping("/addList") @Log(title = "盘点入库操作", businessType = BusinessType.MATERIAL) public AjaxResult savePutInfo(@RequestBody SavePutInfoDto dto) { diff --git a/sgzb-modules/sgzb-material/src/main/java/com/bonus/sgzb/material/controller/PurchaseMacodeInfoController.java b/sgzb-modules/sgzb-material/src/main/java/com/bonus/sgzb/material/controller/PurchaseMacodeInfoController.java index 002e1c01..5f712189 100644 --- a/sgzb-modules/sgzb-material/src/main/java/com/bonus/sgzb/material/controller/PurchaseMacodeInfoController.java +++ b/sgzb-modules/sgzb-material/src/main/java/com/bonus/sgzb/material/controller/PurchaseMacodeInfoController.java @@ -5,6 +5,7 @@ import javax.servlet.http.HttpServletResponse; import com.bonus.sgzb.common.log.annotation.Log; import com.bonus.sgzb.common.log.enums.BusinessType; +import com.bonus.sgzb.material.annotation.PreventRepeatSubmit; import com.bonus.sgzb.material.service.IPurchaseMacodeInfoService; import com.bonus.sgzb.material.domain.PurchaseMacodeInfo; import com.bonus.sgzb.base.api.domain.MaInputVO; @@ -119,6 +120,7 @@ public class PurchaseMacodeInfoController extends BaseController { */ @ApiOperation(value = "新购入库审核") @Log(title = "新购验收任务", businessType = BusinessType.MATERIAL) + @PreventRepeatSubmit @PutMapping("/manageStatus") public AjaxResult modifyManageStatus(@RequestBody MaInputVO maInputVO) throws Exception { try { diff --git a/sgzb-modules/sgzb-material/src/main/java/com/bonus/sgzb/material/controller/ToDoController.java b/sgzb-modules/sgzb-material/src/main/java/com/bonus/sgzb/material/controller/ToDoController.java index ab6bfa41..aa126618 100644 --- a/sgzb-modules/sgzb-material/src/main/java/com/bonus/sgzb/material/controller/ToDoController.java +++ b/sgzb-modules/sgzb-material/src/main/java/com/bonus/sgzb/material/controller/ToDoController.java @@ -3,7 +3,6 @@ package com.bonus.sgzb.material.controller; import com.bonus.sgzb.common.core.web.controller.BaseController; import com.bonus.sgzb.common.core.web.domain.AjaxResult; import com.bonus.sgzb.common.core.web.page.TableDataInfo; -import com.bonus.sgzb.material.annotation.PreventRepeatSubmit; import com.bonus.sgzb.material.domain.ToDoBean; import com.bonus.sgzb.material.service.ToDoService; import com.bonus.sgzb.material.vo.NoticeInfoVO; @@ -30,7 +29,6 @@ public class ToDoController extends BaseController { * 代办事项列表 */ @ApiOperation(value = "代办事项列表") - @PreventRepeatSubmit @GetMapping("/getToDoList") public TableDataInfo getToDoList(ToDoBean bean) {