| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- 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;
- import com.izouma.nineth.enums.MetaPropOperationType;
- import com.izouma.nineth.enums.MetaPropUsedType;
- import com.izouma.nineth.enums.MetaStoreCommodityType;
- import com.izouma.nineth.repo.MetaPropRepo;
- import com.izouma.nineth.repo.MetaStorePurchaseRecordRepo;
- import com.izouma.nineth.repo.MetaStoreRepo;
- 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.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.Objects;
- @Service
- @AllArgsConstructor
- public class MetaStoreService {
- private RedisTemplate<String, String> redisTemplate;
- private MetaStoreRepo metaStoreRepo;
- private MetaPropRepo metaPropRepo;
- private MetaUserPropRepo metaUserPropRepo;
- private MetaUserGoldService metaUserGoldService;
- private MetaUserPropRecordService metaUserPropRecordService;
- private MetaStorePurchaseRecordRepo metaStorePurchaseRecordRepo;
- public Page<MetaStore> all(PageQuery pageQuery) {
- return metaStoreRepo.findAll(JpaUtils.toSpecification(pageQuery, MetaStore.class), JpaUtils.toPageRequest(pageQuery));
- }
- @Transactional
- public MetaRestResult<Void> purchase(Long id) {
- MetaStore metaStore = metaStoreRepo.findByIdAndDel(id, false);
- if (Objects.isNull(metaStore)) {
- return MetaRestResult.returnError("商品不存在");
- }
- if (!metaStore.isOnShelf()) {
- return MetaRestResult.returnError("商品未上架");
- }
- Long userId = SecurityUtils.getAuthenticatedUser().getId();
- if (MetaStoreCommodityType.META_PROP.equals(metaStore.getCommodityType())) {
- return purchaseProp(metaStore, userId);
- }
- return purchaseNFT(metaStore, userId);
- }
- @Transactional
- public MetaRestResult<Void> purchaseProp(MetaStore metaStore, Long userId) {
- int price = metaStore.getPrice();
- if (0 >= metaStore.getPrice()) {
- return MetaRestResult.returnError("道具价格不合法");
- }
- MetaProp metaProp = metaPropRepo.findByIdAndDel(metaStore.getMetaPropId(), false);
- if (Objects.isNull(metaProp)) {
- return MetaRestResult.returnError("道具信息为空");
- }
- MetaUserProp dbMetaUserProp = metaUserPropRepo.findByUserIdAndMetaPropIdAndDel(userId, metaProp.getId(), false);
- if (Objects.isNull(dbMetaUserProp)) {
- dbMetaUserProp = MetaUserProp.create(userId, metaProp, 1);
- MetaRestResult<MetaUserGold> restResult = metaUserGoldService.changeNum(userId, -price, String.format("购买道具:[%S],消耗金币[%s]", metaProp.getId(), price));
- if (restResult.getCode() != Constants.MetaRestCode.success) {
- return MetaRestResult.returnError(restResult.getMessage());
- }
- metaUserPropRepo.save(dbMetaUserProp);
- metaUserPropRecordService.save(userId, metaProp, MetaPropOperationType.RECEIVE, 1);
- metaStorePurchaseRecordRepo.save(new MetaStorePurchaseRecord(metaStore.getId(), userId, LocalDateTime.now()));
- return MetaRestResult.returnSuccess("购买成功!");
- }
- if (MetaPropUsedType.PERMANENT.equals(metaProp.getUsedType()) && dbMetaUserProp.getNum() >= 1) {
- return MetaRestResult.returnError("已拥有永久道具,不可购买");
- }
- MetaRestResult<MetaUserGold> restResult = metaUserGoldService.changeNum(userId, -price, String.format("购买道具:[%S],消耗金币[%s]", metaProp.getId(), price));
- if (restResult.getCode() != Constants.MetaRestCode.success) {
- return MetaRestResult.returnError(restResult.getMessage());
- }
- dbMetaUserProp.setNum(dbMetaUserProp.getNum() + 1);
- metaUserPropRepo.save(dbMetaUserProp);
- metaUserPropRecordService.save(userId, metaProp, MetaPropOperationType.RECEIVE, 1);
- metaStorePurchaseRecordRepo.save(new MetaStorePurchaseRecord(metaStore.getId(), userId, LocalDateTime.now()));
- 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;
- }
- }
|