소스 검색

meta store

sunkean 3 년 전
부모
커밋
639eb3fe56

+ 1 - 0
src/main/java/com/izouma/nineth/config/MetaConstants.java

@@ -20,4 +20,5 @@ public interface MetaConstants {
 
     String BLANK = " ";
 
+    String REDIS_STOCK_PREFIX = "stock:";
 }

+ 1 - 3
src/main/java/com/izouma/nineth/repo/MetaStorePurchaseRecordRepo.java

@@ -4,10 +4,8 @@ import com.izouma.nineth.domain.MetaStorePurchaseRecord;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 
-import java.util.List;
-
 
 public interface MetaStorePurchaseRecordRepo extends JpaRepository<MetaStorePurchaseRecord, Long>, JpaSpecificationExecutor<MetaStorePurchaseRecord> {
 
-    List<MetaStorePurchaseRecord> findAllByMetaStoreIdAndUserId(Long metaStoreId, Long userId);
+    int countByMetaStoreIdAndUserId(Long metaStoreId, Long userId);
 }

+ 7 - 1
src/main/java/com/izouma/nineth/repo/MetaStoreRepo.java

@@ -22,5 +22,11 @@ public interface MetaStoreRepo extends JpaRepository<MetaStore, Long>, JpaSpecif
 
     MetaStore findByIdAndDel(Long id, boolean del);
 
-    List<MetaStore> findAllByCommodityTypeAndOnShelfAndDel(MetaStoreCommodityType commodityType,boolean onShelf, boolean del);
+    List<MetaStore> findAllByCommodityTypeAndOnShelfAndDel(MetaStoreCommodityType commodityType, boolean onShelf, boolean del);
+
+    @Query("update MetaStore t set t.stockNum = ?2 where t.id = ?1")
+    @Modifying
+    @Transactional
+    void updateStockNum(Long updateStockNum, int stockNum);
+
 }

+ 87 - 30
src/main/java/com/izouma/nineth/service/MetaStoreService.java

@@ -2,6 +2,7 @@ package com.izouma.nineth.service;
 
 import com.izouma.nineth.annotations.RedisLock;
 import com.izouma.nineth.config.Constants;
+import com.izouma.nineth.config.MetaConstants;
 import com.izouma.nineth.domain.*;
 import com.izouma.nineth.dto.MetaRestResult;
 import com.izouma.nineth.dto.PageQuery;
@@ -15,19 +16,22 @@ import com.izouma.nineth.repo.MetaUserPropRepo;
 import com.izouma.nineth.utils.JpaUtils;
 import com.izouma.nineth.utils.SecurityUtils;
 import lombok.AllArgsConstructor;
-import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang.StringUtils;
 import org.springframework.data.domain.Page;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 
 import javax.transaction.Transactional;
+import java.time.Duration;
 import java.time.LocalDateTime;
-import java.util.List;
 import java.util.Objects;
 
 @Service
 @AllArgsConstructor
 public class MetaStoreService {
 
+    private RedisTemplate<String, String> redisTemplate;
+
     private MetaStoreRepo metaStoreRepo;
 
     private MetaPropRepo metaPropRepo;
@@ -60,34 +64,6 @@ public class MetaStoreService {
         return purchaseNFT(metaStore, userId);
     }
 
-    @RedisLock("#userId")
-    @Transactional
-    public MetaRestResult<Void> purchaseNFT(MetaStore metaStore, Long userId) {
-        int price = metaStore.getPrice();
-        if (0 >= metaStore.getPrice()) {
-            return MetaRestResult.returnError("购买失败,道具价格不合法");
-        }
-        if (metaStore.getStockNum() <= 0) {
-            return MetaRestResult.returnError("购买失败,库存不足");
-        }
-        // 限购
-        List<MetaStorePurchaseRecord> metaStorePurchaseRecords = metaStorePurchaseRecordRepo.findAllByMetaStoreIdAndUserId(metaStore.getId(), userId);
-        if (CollectionUtils.isNotEmpty(metaStorePurchaseRecords) && metaStorePurchaseRecords.size() >= metaStore.getPurchaseLimitNum()) {
-            return MetaRestResult.returnError(String.format("购买失败,当前商品限购[%S]个", metaStore.getPurchaseLimitNum()));
-        }
-        // 扣减金币
-        MetaRestResult<MetaUserGold> restResult = metaUserGoldService.changeNum(userId, -price, String.format("购买NFT:[%S],消耗金币[%s]", metaStore.getName(), price));
-        if (restResult.getCode() != Constants.MetaRestCode.success) {
-            return MetaRestResult.returnError(restResult.getMessage());
-        }
-        // 保存购买记录
-        metaStorePurchaseRecordRepo.save(new MetaStorePurchaseRecord(metaStore.getId(), userId, LocalDateTime.now()));
-        // 商品库存减1
-        metaStore.setStockNum(metaStore.getStockNum() - 1);
-        metaStoreRepo.save(metaStore);
-        return MetaRestResult.returnSuccess("购买成功!");
-    }
-
     @Transactional
     public MetaRestResult<Void> purchaseProp(MetaStore metaStore, Long userId) {
         int price = metaStore.getPrice();
@@ -124,4 +100,85 @@ public class MetaStoreService {
         return MetaRestResult.returnSuccess("购买成功!");
 
     }
+
+    @Transactional
+    @RedisLock("'updateStockNumLock::'+#metaStore.getId()")
+    public MetaRestResult<Void> purchaseNFT(MetaStore metaStore, Long userId) {
+        int price = metaStore.getPrice();
+        if (price <= 0) {
+            return MetaRestResult.returnError("购买失败,道具价格不合法");
+        }
+        // 从缓存中获取库存
+        int stockNum = getStockNumFromCache(metaStore.getId());
+        if (stockNum <= 0) {
+            return MetaRestResult.returnError("购买失败,库存不足");
+        }
+        // 限购
+        if (isPurchaseLimitReached(metaStore.getId(), userId, metaStore.getPurchaseLimitNum())) {
+            return MetaRestResult.returnError(String.format("购买失败,当前商品限购[%s]个", metaStore.getPurchaseLimitNum()));
+        }
+        // 扣减金币
+        MetaRestResult<MetaUserGold> restResult = metaUserGoldService.changeNum(userId, -price, String.format("购买NFT:[%s],消耗金币[%s]", metaStore.getName(), price));
+        if (restResult.getCode() != Constants.MetaRestCode.success) {
+            return MetaRestResult.returnError(restResult.getMessage());
+        }
+        // 更新库存
+        updateStockNum(metaStore.getId(), stockNum - 1);
+        // 保存购买记录
+        metaStorePurchaseRecordRepo.save(new MetaStorePurchaseRecord(metaStore.getId(), userId, LocalDateTime.now()));
+        return MetaRestResult.returnSuccess("购买成功!");
+    }
+
+
+    /**
+     * 从 Redis 中获取库存信息
+     *
+     * @param metaStoreId 商品id
+     * @return 库存数量
+     */
+    private int getStockNumFromCache(Long metaStoreId) {
+        String key = MetaConstants.REDIS_STOCK_PREFIX.concat(String.valueOf(metaStoreId));
+        String stockStr = redisTemplate.opsForValue().get(key);
+        if (StringUtils.isNotBlank(stockStr)) {
+            return Integer.parseInt(stockStr);
+        }
+        // 如果 Redis 中不存在,则从数据库中加载,并写入 Redis 缓存
+        MetaStore metaStore = metaStoreRepo.findByIdAndDel(metaStoreId, false);
+        if (metaStore != null) {
+            int stockNum = metaStore.getStockNum();
+            redisTemplate.opsForValue().set(key, String.valueOf(stockNum), Duration.ofMinutes(5)); // 设置过期时间 5 分钟
+            return stockNum;
+        }
+        return 0;
+    }
+
+    /**
+     * 更新 Redis 中的库存信息,异步更新数据库中的库存
+     *
+     * @param metaStoreId 商品id
+     * @param stockNum    库存数量
+     */
+    @RedisLock("'updateStockNumLock::'+#metaStoreId")
+    public void updateStockNum(Long metaStoreId, int stockNum) {
+        String key = MetaConstants.REDIS_STOCK_PREFIX.concat(String.valueOf(metaStoreId));
+        redisTemplate.opsForValue().set(key, String.valueOf(stockNum), Duration.ofMinutes(5)); // 设置过期时间 5 分钟
+        metaStoreRepo.updateStockNum(metaStoreId, stockNum);
+    }
+
+    /**
+     * 判断是否达到购买限制
+     *
+     * @param metaStoreId      商品id
+     * @param userId           用户id
+     * @param purchaseLimitNum 最大购买数量
+     * @return 是否达到购买限制
+     */
+    private boolean isPurchaseLimitReached(Long metaStoreId, Long userId, Integer purchaseLimitNum) {
+        if (purchaseLimitNum == null || purchaseLimitNum <= 0) {
+            return false;
+        }
+        int count = metaStorePurchaseRecordRepo.countByMetaStoreIdAndUserId(metaStoreId, userId);
+        return count >= purchaseLimitNum;
+    }
+
 }