|
|
@@ -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;
|
|
|
+ }
|
|
|
+
|
|
|
}
|