浏览代码

防抖注解添加,交易列表排序修改,热榜推荐修改

wxd 5 天之前
父节点
当前提交
14b8d56c0d

+ 38 - 0
alien-config/src/main/java/shop/alien/config/filter/NoRepeatSubmit.java

@@ -0,0 +1,38 @@
+package shop.alien.config.filter;
+
+import java.lang.annotation.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 防止重复提交注解
+ * 
+ * @author lingma
+ * @version 1.0
+ */
+@Inherited
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface NoRepeatSubmit {
+    
+    /**
+     * 锁过期时间,默认5秒
+     * 
+     * @return 过期时间
+     */
+    int expireTime() default 5;
+    
+    /**
+     * 时间单位,默认为秒
+     * 
+     * @return 时间单位
+     */
+    TimeUnit timeUnit() default TimeUnit.SECONDS;
+    
+    /**
+     * 错误提示信息
+     * 
+     * @return 提示信息
+     */
+    String message() default "请勿重复提交";
+}

+ 25 - 0
alien-config/src/main/java/shop/alien/config/filter/NoRepeatSubmitConfig.java

@@ -0,0 +1,25 @@
+package shop.alien.config.filter;
+
+import lombok.Setter;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * 防重复提交配置类
+ * 
+ * @author lingma
+ * @version 1.0
+ */
+@Setter
+@Configuration
+public class NoRepeatSubmitConfig implements WebMvcConfigurer {
+    
+    private NoRepeatSubmitInterceptor noRepeatSubmitInterceptor;
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(noRepeatSubmitInterceptor)
+                .addPathPatterns("/**");
+    }
+}

+ 117 - 0
alien-config/src/main/java/shop/alien/config/filter/NoRepeatSubmitInterceptor.java

@@ -0,0 +1,117 @@
+package shop.alien.config.filter;
+
+import com.alibaba.fastjson.JSONObject;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import org.springframework.util.DigestUtils;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.HandlerInterceptor;
+import shop.alien.config.redis.BaseRedisService;
+import shop.alien.entity.result.R;
+import shop.alien.util.common.JwtUtil;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.reflect.Method;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 防止重复提交拦截器
+ * 
+ * @author lingma
+ * @version 1.0
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class NoRepeatSubmitInterceptor implements HandlerInterceptor {
+    
+    private final BaseRedisService baseRedisService;
+    
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        if (handler instanceof HandlerMethod) {
+            HandlerMethod handlerMethod = (HandlerMethod) handler;
+            Method method = handlerMethod.getMethod();
+            NoRepeatSubmit noRepeatSubmit = method.getAnnotation(NoRepeatSubmit.class);
+            
+            if (noRepeatSubmit != null) {
+                String key = generateKey(request, method);
+                if (!lock(key, noRepeatSubmit)) {
+                    // 重复提交
+                    handleRepeatSubmit(response, noRepeatSubmit.message());
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+    
+    /**
+     * 生成锁的键值
+     * key 值组成: repeat_submit:方法所在类名:方法名:用户ID(如果已登录)或IP地址:请求URI的MD5摘
+     * @param request 请求
+     * @param method  方法
+     * @return 键值
+     */
+    private String generateKey(HttpServletRequest request, Method method) {
+        StringBuilder sb = new StringBuilder("repeat_submit:");
+        sb.append(method.getDeclaringClass().getName());
+        sb.append(":");
+        sb.append(method.getName());
+        sb.append(":");
+        
+        // 添加用户标识
+        JSONObject userInfo = JwtUtil.getCurrentUserInfo();
+        if (userInfo != null) {
+            sb.append(userInfo.getInteger("userId"));
+        } else {
+            sb.append(request.getRemoteAddr());
+        }
+        
+        // 添加请求参数
+        sb.append(":");
+        sb.append(DigestUtils.md5DigestAsHex(request.getRequestURI().getBytes()));
+        
+        return sb.toString();
+    }
+    
+    /**
+     * 加锁 分布式锁实现
+     * 如果键不存在则设置成功并返回 true,否则返回 false
+     * @param key 键
+     * @param noRepeatSubmit 注解
+     * @return 是否加锁成功
+     */
+    private boolean lock(String key, NoRepeatSubmit noRepeatSubmit) {
+        try {
+            // 使用Redis的SET命令的NX和EX选项实现分布式锁
+            // 键的过期时间由 @NoRepeatSubmit 注解中的参数决定
+            Boolean result = baseRedisService.setStringIfAbsent(key, "1", 
+                noRepeatSubmit.timeUnit().toSeconds(noRepeatSubmit.expireTime()));
+            return result != null && result;
+        } catch (Exception e) {
+            log.error("防重复提交加锁异常", e);
+            return true; // 出现异常不阻止用户操作
+        }
+    }
+    
+    /**
+     * 处理重复提交
+     * 
+     * @param response 响应
+     * @param message 提示信息
+     * @throws IOException IO异常
+     */
+    private void handleRepeatSubmit(HttpServletResponse response, String message) throws IOException {
+        response.setContentType("application/json;charset=UTF-8");
+        PrintWriter writer = response.getWriter();
+        R<Object> fail = R.fail(message);
+        writer.write(JSONObject.toJSONString(fail));
+        writer.flush();
+        writer.close();
+    }
+}

+ 14 - 4
alien-config/src/main/java/shop/alien/config/redis/BaseRedisService.java

@@ -11,9 +11,7 @@ import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
 /**
- * @author ssk
- * @version 1.0
- * @date 2022/3/14 15:14
+ * Redis基础服务类
  */
 @Component
 @RequiredArgsConstructor
@@ -115,6 +113,18 @@ public class BaseRedisService {
     public void setString(String key, String value, Long timeOut) {
         set(key, value, timeOut);
     }
+    
+    /**
+     * 添加String值, 如果key不存在则设置成功并返回true,否则返回false
+     *
+     * @param key     键
+     * @param value   值
+     * @param timeOut 时长(秒)
+     * @return 是否设置成功
+     */
+    public Boolean setStringIfAbsent(String key, String value, Long timeOut) {
+        return stringRedisTemplate.opsForValue().setIfAbsent(key, value, timeOut, TimeUnit.SECONDS);
+    }
 
     /**
      * 设置超时时间
@@ -164,4 +174,4 @@ public class BaseRedisService {
     public void delete(String key) {
         stringRedisTemplate.delete(key);
     }
-}
+}

+ 1 - 1
alien-second/src/main/java/shop/alien/second/controller/SearchGoodsController.java

@@ -57,7 +57,7 @@ public class SearchGoodsController {
     @GetMapping("/getHotSellingRanking")
     @ApiOperation("推荐 - 获取商品点赞热卖排行榜(前10名)")
     public R<List<SecondGoods>> getHotSellingRanking() {
-        return R.data(secondGoodsService.getHotSellingRankingTop10(), "获取成功");
+        return R.data(secondGoodsService.getCollectTop10(), "获取成功");
     }
 
     /**

+ 9 - 0
alien-second/src/main/java/shop/alien/second/controller/SecondGoodsController.java

@@ -16,6 +16,11 @@ import shop.alien.entity.second.vo.SecondGoodsVo;
 import shop.alien.mapper.second.SecondGoodsAuditMapper;
 import shop.alien.second.service.SecondGoodsService;
 import shop.alien.util.common.JwtUtil;
+import shop.alien.config.filter.NoRepeatSubmit;
+import shop.alien.util.common.safe.ImageModerationUtil;
+import shop.alien.util.common.safe.TextModerationResultVO;
+import shop.alien.util.common.safe.TextModerationUtil;
+import shop.alien.util.common.safe.TextReviewServiceEnum;
 import shop.alien.util.common.safe.*;
 
 import java.util.ArrayList;
@@ -58,6 +63,7 @@ public class SecondGoodsController {
      */
     @PostMapping("/save")
     @ApiOperation("发布二手商品")
+    @NoRepeatSubmit(expireTime = 5, message = "请勿重复提交发布商品请求")
     public R<Void> addSecondGoods(@ApiParam("二手商品信息") @RequestBody SecondGoodsVo secondGoods) throws Exception {
         log.info("SecondGoodsController.addSecondGoods?secondGoods={}", secondGoods.toString());
         JSONObject data = JwtUtil.getCurrentUserInfo();
@@ -84,6 +90,7 @@ public class SecondGoodsController {
      */
     @PostMapping("/edit")
     @ApiOperation("更新二手商品")
+    @NoRepeatSubmit(expireTime = 5, message = "请勿重复提交更新商品请求")
     public R<Void> updateSecondGoods(@ApiParam("二手商品信息") @RequestBody SecondGoodsVo secondGoods) throws Exception {
         log.info("SecondGoodsController.updateSecondGoods?secondGoods={}", secondGoods.toString());
         // 添加商品 0 创建 1 更新
@@ -99,6 +106,7 @@ public class SecondGoodsController {
      */
     @PostMapping("/shelve")
     @ApiOperation("下架二手商品")
+    @NoRepeatSubmit(expireTime = 3, message = "请勿重复提交下架商品请求")
     public R<Void> shelveSecondGoods(@ApiParam("下架二手商品") @RequestBody SecondGoodsVo secondGoods) {
         log.info("SecondGoodsController.shelveSecondGoods?secondGoods={}", secondGoods.toString());
         // 修改商品状态为4 - 已下架
@@ -112,6 +120,7 @@ public class SecondGoodsController {
      */
     @PostMapping("/delete")
     @ApiOperation("删除二手商品")
+    @NoRepeatSubmit(expireTime = 3, message = "请勿重复提交删除商品请求")
     public R<Void> deleteSecondGoods(@ApiParam("删除二手商品") @RequestBody SecondGoodsVo secondGoods) {
         log.info("SecondGoodsController.deleteSecondGoods?secondGoods={}", secondGoods.toString());
         secondGoodsService.removeById(secondGoods.getId());

+ 1 - 1
alien-second/src/main/java/shop/alien/second/service/impl/SecondGoodsServiceImpl.java

@@ -673,7 +673,7 @@ public class SecondGoodsServiceImpl extends ServiceImpl<SecondGoodsMapper, Secon
                 .eq("str.delete_flag", Constants.DeleteFlag.NOT_DELETED)
                 .and(wrapper -> wrapper.eq("str.buyer_id", userId)
                         .or()
-                        .eq("str.seller_id", userId));
+                        .eq("str.seller_id", userId)).orderByDesc("str.created_time");
         result = secondGoodsMapper.getTransactionList(page, userId, queryWrapper);
         // 批量设置用户信息
         batchSetSellUserInfo(result);