Kaynağa Gözat

抢购优化

xiongzhu 3 yıl önce
ebeveyn
işleme
2c1a874b81

+ 6 - 0
pom.xml

@@ -424,6 +424,12 @@
             <artifactId>hutool-core</artifactId>
             <version>5.7.21</version>
         </dependency>
+
+        <dependency>
+            <groupId>com.github.vladimir-bukhtoyarov</groupId>
+            <artifactId>bucket4j-core</artifactId>
+            <version>7.3.0</version>
+        </dependency>
     </dependencies>
 
 </project>

+ 6 - 0
src/main/java/com/izouma/nineth/config/RedisKeys.java

@@ -26,4 +26,10 @@ public class RedisKeys {
     public static final String UPDATE_STOCK = "updateSale";
 
     public static final String LIMIT_REQ = "limitReq::";
+
+    public static final String LIMIT_USER = "limitUser::";
+
+    public static final String BLACK_LIST = "blackList::";
+
+    private static final String VIP_COLLECTION_LIST = "vipCollectionList";
 }

+ 4 - 0
src/main/java/com/izouma/nineth/repo/SmsRecordRepo.java

@@ -21,4 +21,8 @@ public interface SmsRecordRepo extends JpaRepository<SmsRecord, Long> {
     @Modifying
     @Transactional
     void expire(String phone);
+
+    @Modifying
+    @Transactional
+    void deleteByCreatedAtBefore(LocalDateTime time);
 }

+ 19 - 5
src/main/java/com/izouma/nineth/service/OrderService.java

@@ -110,12 +110,26 @@ public class OrderService {
             throw new BusinessException("签名已过期");
         }
 
-//        Integer stock = collectionService.getStock(collectionId);
-//        if (stock == null || stock <= 0) {
-//            throw new BusinessException("藏品已售罄", ErrorCode.SOLD_OUT);
-//        }
+        if (redisTemplate.opsForValue().get(RedisKeys.BLACK_LIST + userId) != null) {
+            throw new BusinessException("频繁操作,请稍后再试");
+        }
+        BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps(RedisKeys.LIMIT_USER + userId);
+        ops.setIfAbsent(0, Duration.ofSeconds(10));
+        long val = Optional.ofNullable(ops.increment()).orElse(0L);
+        if (val > 5) {
+            if (val > 10) {
+                redisTemplate.opsForValue().set(RedisKeys.BLACK_LIST + userId, 1, Duration.ofSeconds(60 * 10));
+            }
+            throw new BusinessException("频繁操作,请稍后再试");
+        }
+
         limitReq(collectionId);
 
+        Integer stock = collectionService.getStock(collectionId);
+        if (stock == null || stock <= 0) {
+            throw new BusinessException("藏品已售罄", ErrorCode.SOLD_OUT);
+        }
+
         Long id = snowflakeIdWorker.nextId();
         SendResult result = rocketMQTemplate.syncSend(generalProperties.getCreateOrderTopic(),
                 new CreateOrderEvent(id, userId, collectionId, qty, addressId, userCouponId, invitor, vip), 100000);
@@ -129,7 +143,7 @@ public class OrderService {
         ops.setIfAbsent(3000, Duration.ofSeconds(30));
         Long val = ops.decrement();
         if (val == null || val < 0) {
-            throw new BusinessException("很遗憾,藏品已售罄");
+            throw new BusinessException("前方拥堵,请稍后再试");
         }
     }
 

+ 14 - 1
src/main/java/com/izouma/nineth/service/sms/AliSmsService.java

@@ -19,8 +19,11 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.configurationprocessor.json.JSONException;
 import org.springframework.boot.configurationprocessor.json.JSONObject;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
 
+import java.time.Duration;
 import java.time.LocalDateTime;
 import java.time.ZoneOffset;
 import java.util.Date;
@@ -41,7 +44,9 @@ public class AliSmsService implements SmsService {
     private String sellOutCode;
 
     @Autowired
-    private SmsRecordRepo smsRecordRepo;
+    private SmsRecordRepo                 smsRecordRepo;
+    @Autowired
+    private RedisTemplate<String, Object> redisTemplate;
 
     @Override
     public String sendVerify(String phone) {
@@ -102,6 +107,10 @@ public class AliSmsService implements SmsService {
 
     @Override
     public String verify(String phone, String code) {
+        if (Boolean.FALSE.equals(redisTemplate.opsForValue()
+                .setIfAbsent("verify::" + phone, 1, Duration.ofSeconds(10)))) {
+            throw new BusinessException("频繁操作,请稍后再试");
+        }
         SmsRecord smsRecord = smsRecordRepo.findLastByPhoneAndExpiresAtAfterAndExpiredFalse(phone, LocalDateTime.now())
                 .orElseThrow(new BusinessException("验证码错误"));
         if (!smsRecord.getCode().equalsIgnoreCase(code)) {
@@ -151,4 +160,8 @@ public class AliSmsService implements SmsService {
         }
     }
 
+    @Scheduled(cron = "0 0 3 * * ?")
+    void deleteExpired() {
+        smsRecordRepo.deleteByCreatedAtBefore(LocalDateTime.now().minusDays(2));
+    }
 }

+ 2 - 1
src/main/java/com/izouma/nineth/web/SmsController.java

@@ -4,6 +4,7 @@ import com.izouma.nineth.config.Constants;
 import com.izouma.nineth.exception.BusinessException;
 import com.izouma.nineth.service.sms.SmsService;
 import lombok.AllArgsConstructor;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
@@ -15,7 +16,7 @@ import java.util.regex.Pattern;
 @RequestMapping("/sms")
 @AllArgsConstructor
 public class SmsController {
-    private SmsService smsService;
+    private SmsService                    smsService;
 
     @GetMapping("/sendVerify")
     public String sendVerify(@RequestParam String phone) {

+ 0 - 0
src/main/resources/lua/mqCreate.lua