|
|
@@ -1,16 +1,14 @@
|
|
|
package com.izouma.nineth.service;
|
|
|
|
|
|
-import cn.hutool.core.convert.Convert;
|
|
|
-import com.fasterxml.jackson.annotation.JsonView;
|
|
|
+import com.google.common.collect.Lists;
|
|
|
import com.izouma.nineth.TokenHistory;
|
|
|
+import com.izouma.nineth.config.Constants;
|
|
|
import com.izouma.nineth.config.GeneralProperties;
|
|
|
+import com.izouma.nineth.converter.LongArrayConverter;
|
|
|
import com.izouma.nineth.domain.Collection;
|
|
|
import com.izouma.nineth.domain.*;
|
|
|
-import com.izouma.nineth.dto.PageQuery;
|
|
|
-import com.izouma.nineth.dto.PageWrapper;
|
|
|
-import com.izouma.nineth.dto.UserHistory;
|
|
|
+import com.izouma.nineth.dto.*;
|
|
|
import com.izouma.nineth.enums.*;
|
|
|
-import com.izouma.nineth.event.TransferAssetEvent;
|
|
|
import com.izouma.nineth.exception.BusinessException;
|
|
|
import com.izouma.nineth.repo.*;
|
|
|
import com.izouma.nineth.utils.JpaUtils;
|
|
|
@@ -20,13 +18,11 @@ import lombok.AllArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.apache.commons.collections.CollectionUtils;
|
|
|
import org.apache.commons.lang3.ObjectUtils;
|
|
|
+import org.apache.commons.lang3.RandomStringUtils;
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
import org.apache.rocketmq.spring.core.RocketMQTemplate;
|
|
|
import org.springframework.beans.BeanUtils;
|
|
|
-import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.cache.annotation.Cacheable;
|
|
|
-import org.springframework.context.ApplicationContext;
|
|
|
-import org.springframework.context.annotation.Lazy;
|
|
|
import org.springframework.data.domain.Page;
|
|
|
import org.springframework.data.domain.PageImpl;
|
|
|
import org.springframework.data.domain.Pageable;
|
|
|
@@ -36,96 +32,144 @@ import org.springframework.scheduling.annotation.Scheduled;
|
|
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
-import javax.persistence.criteria.CriteriaBuilder;
|
|
|
-import javax.persistence.criteria.CriteriaQuery;
|
|
|
import javax.persistence.criteria.Predicate;
|
|
|
-import javax.persistence.criteria.Root;
|
|
|
import java.math.BigDecimal;
|
|
|
+import java.time.Duration;
|
|
|
import java.time.LocalDateTime;
|
|
|
import java.time.temporal.ChronoUnit;
|
|
|
import java.util.*;
|
|
|
import java.util.concurrent.ExecutionException;
|
|
|
import java.util.concurrent.ForkJoinPool;
|
|
|
+import java.util.concurrent.atomic.AtomicInteger;
|
|
|
+import java.util.regex.Pattern;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
@Service
|
|
|
+@AllArgsConstructor
|
|
|
@Slf4j
|
|
|
public class AssetService {
|
|
|
|
|
|
- @Autowired
|
|
|
private AssetRepo assetRepo;
|
|
|
- @Autowired
|
|
|
private UserRepo userRepo;
|
|
|
- @Autowired
|
|
|
private CollectionRepo collectionRepo;
|
|
|
- @Autowired
|
|
|
- private ApplicationContext applicationContext;
|
|
|
- @Autowired
|
|
|
private OrderRepo orderRepo;
|
|
|
- @Autowired
|
|
|
private TokenHistoryRepo tokenHistoryRepo;
|
|
|
- @Autowired
|
|
|
private SysConfigService sysConfigService;
|
|
|
- @Autowired
|
|
|
private RocketMQTemplate rocketMQTemplate;
|
|
|
- @Autowired
|
|
|
private GeneralProperties generalProperties;
|
|
|
- @Autowired
|
|
|
private ShowroomRepo showroomRepo;
|
|
|
- @Autowired
|
|
|
private ShowCollectionRepo showCollectionRepo;
|
|
|
- @Autowired
|
|
|
private CollectionPrivilegeRepo collectionPrivilegeRepo;
|
|
|
- @Autowired
|
|
|
private PasswordEncoder passwordEncoder;
|
|
|
- @Autowired
|
|
|
private MintActivityRepo mintActivityRepo;
|
|
|
- @Autowired
|
|
|
- @Lazy
|
|
|
+ private DestroyRecordRepo destroyRecordRepo;
|
|
|
private AirDropService airDropService;
|
|
|
+ private HCChainService hcChainService;
|
|
|
+ private RockRecordService rockRecordService;
|
|
|
+ private RockRecordRepo rockRecordRepo;
|
|
|
+ private AssetLockRepo assetLockRepo;
|
|
|
+ private UserBalanceService userBalanceService;
|
|
|
|
|
|
public Page<Asset> all(PageQuery pageQuery) {
|
|
|
Page<Asset> all = assetRepo
|
|
|
.findAll(JpaUtils.toSpecification(pageQuery, Asset.class), JpaUtils.toPageRequest(pageQuery));
|
|
|
- Map<String, Object> query = pageQuery.getQuery();
|
|
|
- if (query.containsKey("userId")) {
|
|
|
- List<Long> orderId = orderRepo
|
|
|
- .findAllByUserIdAndOpenedFalse(Convert.convert(Long.class, query.get("userId")));
|
|
|
- return all.map(asset -> {
|
|
|
- if (orderId.contains(asset.getOrderId())) {
|
|
|
- asset.setOpened(false);
|
|
|
- }
|
|
|
- return asset;
|
|
|
+// Map<String, Object> query = pageQuery.getQuery();
|
|
|
+// if (query.containsKey("userId")) {
|
|
|
+// List<Long> orderId = orderRepo
|
|
|
+// .findAllByUserIdAndOpenedFalse(Convert.convert(Long.class, query.get("userId")));
|
|
|
+// return all.map(asset -> {
|
|
|
+// if (orderId.contains(asset.getOrderId())) {
|
|
|
+// asset.setOpened(false);
|
|
|
+// }
|
|
|
+// return asset;
|
|
|
+// });
|
|
|
+// }
|
|
|
+ return all;
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<AssetDTO> userSummary(PageQuery pageQuery) {
|
|
|
+ List<AssetDTO> assetDTOs = new ArrayList<>();
|
|
|
+ // 根据条件查询所有资产
|
|
|
+ List<Asset> assets = assetRepo.findAll(JpaUtils.toSpecification(pageQuery, Asset.class));
|
|
|
+ if (CollectionUtils.isEmpty(assets)) {
|
|
|
+ return assetDTOs;
|
|
|
+ }
|
|
|
+ // 取出资产中未开启盲盒数据
|
|
|
+ List<Asset> blindBoxClosedAssets = assets.stream()
|
|
|
+ .filter(asset -> !asset.isOpened() && CollectionType.BLIND_BOX.equals(asset.getType()))
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ if (CollectionUtils.isNotEmpty(blindBoxClosedAssets)) {
|
|
|
+ blindBoxClosedAssets.forEach(asset -> {
|
|
|
+ assetDTOs.add(AssetDTO.create(Lists.newArrayList(asset)));
|
|
|
});
|
|
|
+ // 移除资产中未开启盲盒数据
|
|
|
+ assets.removeAll(blindBoxClosedAssets);
|
|
|
}
|
|
|
- return all;
|
|
|
+ // 取出资产中所有未设置prefixName的值
|
|
|
+ List<Asset> prefixNameIsNullAssets = assets.stream()
|
|
|
+ .filter(asset -> StringUtils.isBlank(asset.getPrefixName()))
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ if (CollectionUtils.isNotEmpty(prefixNameIsNullAssets)) {
|
|
|
+ prefixNameIsNullAssets.forEach(asset -> {
|
|
|
+ assetDTOs.add(AssetDTO.create(Lists.newArrayList(asset)));
|
|
|
+ });
|
|
|
+ assets.removeAll(prefixNameIsNullAssets);
|
|
|
+ }
|
|
|
+ if (CollectionUtils.isNotEmpty(assets)) {
|
|
|
+ // 取出资产中所有prefixName
|
|
|
+ List<String> prefixNames = assets.stream()
|
|
|
+ .map(Asset::getPrefixName)
|
|
|
+ .distinct()
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ // 将资产中相同prefixName归类(除未开启盲盒和未设置prefixName)
|
|
|
+ prefixNames.forEach(str -> {
|
|
|
+ List<Asset> collect = assets.stream()
|
|
|
+ .filter(asset -> str.equals(asset.getPrefixName()))
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ assetDTOs.add(AssetDTO.create(collect));
|
|
|
+ });
|
|
|
+ }
|
|
|
+ return assetDTOs;
|
|
|
}
|
|
|
|
|
|
- public Asset createAsset(Collection collection, User user, Long orderId, BigDecimal price, String type, Integer number) {
|
|
|
+ public Asset createAsset(Collection collection, User user, Long orderId, BigDecimal price, String type,
|
|
|
+ Integer number, boolean safeFlag) {
|
|
|
Asset asset = Asset.create(collection, user);
|
|
|
asset.setTokenId(TokenUtils.genTokenId());
|
|
|
asset.setNumber(number);
|
|
|
asset.setOasisId(collection.getOasisId());
|
|
|
- asset.setRaceId(collection.getRaceId());
|
|
|
- asset.setGroupId(collection.getGroupId());
|
|
|
- asset.setItemId(collection.getItemId());
|
|
|
asset.setOrderId(orderId);
|
|
|
asset.setPrice(price);
|
|
|
+ asset.setPrefixName(collection.getPrefixName());
|
|
|
+ asset.setTags(new HashSet<>());
|
|
|
+ if (collection.getTags() != null) {
|
|
|
+ asset.getTags().addAll(collection.getTags());
|
|
|
+ }
|
|
|
+ User fakeUser = null;
|
|
|
+ if (safeFlag) {
|
|
|
+ fakeUser = createFakeUser();
|
|
|
+ asset.setOwner(fakeUser.getNickname());
|
|
|
+ asset.setOwnerId(fakeUser.getId());
|
|
|
+ asset.setOwnerAvatar(fakeUser.getAvatar());
|
|
|
+ }
|
|
|
assetRepo.saveAndFlush(asset);
|
|
|
-
|
|
|
tokenHistoryRepo.save(TokenHistory.builder()
|
|
|
.tokenId(asset.getTokenId())
|
|
|
.fromUser(collection.getMinter())
|
|
|
.fromUserId(collection.getMinterId())
|
|
|
.fromAvatar(collection.getMinterAvatar())
|
|
|
- .toUser(user.getNickname())
|
|
|
- .toUserId(user.getId())
|
|
|
- .toAvatar(user.getAvatar())
|
|
|
+ .toUser((safeFlag ? fakeUser : user).getNickname())
|
|
|
+ .toUserId((safeFlag ? fakeUser : user).getId())
|
|
|
+ .toAvatar((safeFlag ? fakeUser : user).getAvatar())
|
|
|
.operation(type)
|
|
|
.price(price)
|
|
|
.build());
|
|
|
+
|
|
|
+ //绿洲石
|
|
|
+ rockRecordService.addRock(user.getId(), price, "购买");
|
|
|
+
|
|
|
rocketMQTemplate.syncSend(generalProperties.getMintTopic(), asset.getId());
|
|
|
- if (asset.getOasisId() != null & asset.getCollectionId() != 207012L) {
|
|
|
+ if (asset.getOasisId() != null & asset.getSource().equals(AssetSource.OFFICIAL)) {
|
|
|
AirDrop airDrop = new AirDrop();
|
|
|
airDrop.setName("建筑空投展厅");
|
|
|
airDrop.setCollectionId(207012L);
|
|
|
@@ -144,7 +188,6 @@ public class AssetService {
|
|
|
airDrop.setTargets(dropTargets);
|
|
|
airDrop.setUserIds(userIds);
|
|
|
airDrop.setNum(nums);
|
|
|
- airDrop.setOasisId(asset.getOasisId());
|
|
|
airDropService.create(airDrop);
|
|
|
|
|
|
}
|
|
|
@@ -152,7 +195,7 @@ public class AssetService {
|
|
|
}
|
|
|
|
|
|
public Asset createAsset(BlindBoxItem winItem, User user, Long orderId, BigDecimal price, String type,
|
|
|
- Integer number, Integer holdDays) {
|
|
|
+ Integer number, Integer holdDays, boolean safeFlag) {
|
|
|
Collection blindBox = collectionRepo.findDetailById(winItem.getBlindBoxId())
|
|
|
.orElseThrow(new BusinessException("盲盒不存在"));
|
|
|
Collection collection = collectionRepo.findDetailById(winItem.getCollectionId())
|
|
|
@@ -163,6 +206,8 @@ public class AssetService {
|
|
|
asset.setOasisId(winItem.getOasisId());
|
|
|
asset.setOrderId(orderId);
|
|
|
asset.setPrice(price);
|
|
|
+ asset.setPrefixName(collection.getPrefixName());
|
|
|
+ asset.setEmpower(collection.getEmpower());
|
|
|
asset.setTags(new HashSet<>());
|
|
|
if (blindBox.getTags() != null) {
|
|
|
asset.getTags().addAll(blindBox.getTags());
|
|
|
@@ -170,19 +215,29 @@ public class AssetService {
|
|
|
if (collection.getTags() != null) {
|
|
|
asset.getTags().addAll(collection.getTags());
|
|
|
}
|
|
|
+ User fakeUser = null;
|
|
|
+ if (safeFlag) {
|
|
|
+ fakeUser = createFakeUser();
|
|
|
+ asset.setOwner(fakeUser.getNickname());
|
|
|
+ asset.setOwnerId(fakeUser.getId());
|
|
|
+ asset.setOwnerAvatar(fakeUser.getAvatar());
|
|
|
+ }
|
|
|
assetRepo.saveAndFlush(asset);
|
|
|
-
|
|
|
tokenHistoryRepo.save(TokenHistory.builder()
|
|
|
.tokenId(asset.getTokenId())
|
|
|
.fromUser(winItem.getMinter())
|
|
|
.fromUserId(winItem.getMinterId())
|
|
|
.fromAvatar(winItem.getMinterAvatar())
|
|
|
- .toUser(user.getNickname())
|
|
|
- .toUserId(user.getId())
|
|
|
- .toAvatar(user.getAvatar())
|
|
|
+ .toUser((safeFlag ? fakeUser : user).getNickname())
|
|
|
+ .toUserId((safeFlag ? fakeUser : user).getId())
|
|
|
+ .toAvatar((safeFlag ? fakeUser : user).getAvatar())
|
|
|
.operation(type)
|
|
|
.price(price)
|
|
|
.build());
|
|
|
+
|
|
|
+ //绿洲石
|
|
|
+ rockRecordService.addRock(user.getId(), price, "购买");
|
|
|
+
|
|
|
rocketMQTemplate.syncSend(generalProperties.getMintTopic(), asset.getId());
|
|
|
return asset;
|
|
|
}
|
|
|
@@ -193,13 +248,18 @@ public class AssetService {
|
|
|
if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
|
|
|
throw new BusinessException("此藏品不属于你");
|
|
|
}
|
|
|
+ if (asset.getLockTo() != null && asset.getLockTo().isAfter(LocalDateTime.now())) {
|
|
|
+ throw new BusinessException("已锁仓,不能上架展示");
|
|
|
+ }
|
|
|
if (asset.isPublicShow()) {
|
|
|
return;
|
|
|
}
|
|
|
if (asset.getStatus() != AssetStatus.NORMAL) {
|
|
|
throw new BusinessException("当前状态不可展示");
|
|
|
}
|
|
|
- User owner = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在"));
|
|
|
+ User owner = asset.isSafeFlag() ?
|
|
|
+ userRepo.findById(asset.getOwnerId()).orElseThrow(new BusinessException("用户不存在"))
|
|
|
+ : userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在"));
|
|
|
Collection collection = Collection.builder()
|
|
|
.name(asset.getName())
|
|
|
.pic(asset.getPic())
|
|
|
@@ -208,9 +268,6 @@ public class AssetService {
|
|
|
.minterAvatar(asset.getMinterAvatar())
|
|
|
.owner(owner.getNickname())
|
|
|
.oasisId(asset.getOasisId())
|
|
|
- .raceId(asset.getRaceId())
|
|
|
- .groupId(asset.getGroupId())
|
|
|
- .itemId(asset.getItemId())
|
|
|
.ownerId(owner.getId())
|
|
|
.ownerAvatar(owner.getAvatar())
|
|
|
.detail(asset.getDetail())
|
|
|
@@ -228,19 +285,28 @@ public class AssetService {
|
|
|
.serviceCharge(asset.getServiceCharge())
|
|
|
.assetId(id)
|
|
|
.number(asset.getNumber())
|
|
|
+ .tags(new HashSet<>())
|
|
|
+ .prefixName(asset.getPrefixName())
|
|
|
.build();
|
|
|
+ if (asset.getTags() != null) {
|
|
|
+ collection.getTags().addAll(asset.getTags());
|
|
|
+ }
|
|
|
collectionRepo.save(collection);
|
|
|
asset.setPublicShow(true);
|
|
|
asset.setPublicCollectionId(collection.getId());
|
|
|
- assetRepo.save(asset);
|
|
|
+ assetRepo.saveAndFlush(asset);
|
|
|
}
|
|
|
|
|
|
- public synchronized void consignment(Long id, BigDecimal price, String tradeCode) {
|
|
|
+ public synchronized void consignment(Long id, BigDecimal price, String tradeCode, boolean safeFlag) {
|
|
|
Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
|
|
|
if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
|
|
|
throw new BusinessException("此藏品不属于你");
|
|
|
}
|
|
|
|
|
|
+ if (asset.getLockTo() != null && asset.getLockTo().isAfter(LocalDateTime.now())) {
|
|
|
+ throw new BusinessException("已锁仓,不能寄售");
|
|
|
+ }
|
|
|
+
|
|
|
int holdDays;
|
|
|
if (asset.getSource() == AssetSource.GIFT) {
|
|
|
holdDays = sysConfigService.getInt("gift_days");
|
|
|
@@ -251,12 +317,39 @@ public class AssetService {
|
|
|
holdDays = asset.getHoldDays();
|
|
|
}
|
|
|
}
|
|
|
+ if (holdDays == 0 && AssetSource.OFFICIAL.equals(asset.getSource())) {
|
|
|
+ BigDecimal officialConsignment = sysConfigService.getBigDecimal("OFFICIAL_CONSIGNMENT");
|
|
|
+ //天转小时
|
|
|
+ int hour = officialConsignment.multiply(new BigDecimal("24")).intValue();
|
|
|
+ if (ChronoUnit.HOURS.between(asset.getCreatedAt(), LocalDateTime.now()) < hour) {
|
|
|
+ throw new BusinessException("需持有满" + hour + "小时后才能寄售上架");
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
if (ChronoUnit.DAYS.between(asset.getCreatedAt(), LocalDateTime.now()) < holdDays) {
|
|
|
throw new BusinessException("需持有满" + holdDays + "天才能寄售上架");
|
|
|
}
|
|
|
- User owner = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在"));
|
|
|
- if (!passwordEncoder.matches(tradeCode, owner.getTradeCode())) {
|
|
|
+ User owner;
|
|
|
+ if (safeFlag && !asset.isSafeFlag()) {
|
|
|
+ owner = createFakeUser();
|
|
|
+ asset.setOwner(owner.getNickname());
|
|
|
+ asset.setOwnerId(owner.getId());
|
|
|
+ asset.setOwnerAvatar(owner.getAvatar());
|
|
|
+ asset.setSafeFlag(true);
|
|
|
+ tokenHistoryRepo.findByTokenIdOrderByCreatedAtDesc(asset.getTokenId()).stream()
|
|
|
+ .filter(t -> t.getToUserId().equals(asset.getUserId())).findFirst()
|
|
|
+ .ifPresent(tokenHistory -> {
|
|
|
+ tokenHistory.setToUserId(owner.getId());
|
|
|
+ tokenHistory.setToUser(owner.getNickname());
|
|
|
+ tokenHistory.setToAvatar(owner.getAvatar());
|
|
|
+ tokenHistoryRepo.save(tokenHistory);
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ owner = asset.isSafeFlag() ?
|
|
|
+ userRepo.findById(asset.getOwnerId()).orElseThrow(new BusinessException("用户不存在"))
|
|
|
+ : userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在"));
|
|
|
+ }
|
|
|
+ if (!passwordEncoder.matches(tradeCode, userRepo.findTradeCode(asset.getUserId()))) {
|
|
|
throw new BusinessException("交易密码错误");
|
|
|
}
|
|
|
// if (StringUtils.isBlank(owner.getSettleAccountId())) {
|
|
|
@@ -304,13 +397,18 @@ public class AssetService {
|
|
|
.serviceCharge(asset.getServiceCharge())
|
|
|
.assetId(id)
|
|
|
.number(asset.getNumber())
|
|
|
+ .tags(new HashSet<>())
|
|
|
+ .prefixName(asset.getPrefixName())
|
|
|
.build();
|
|
|
+ if (asset.getTags() != null) {
|
|
|
+ collection.getTags().addAll(asset.getTags());
|
|
|
+ }
|
|
|
collectionRepo.save(collection);
|
|
|
asset.setPublicShow(true);
|
|
|
asset.setConsignment(true);
|
|
|
asset.setPublicCollectionId(collection.getId());
|
|
|
asset.setSellPrice(price);
|
|
|
- assetRepo.save(asset);
|
|
|
+ assetRepo.saveAndFlush(asset);
|
|
|
}
|
|
|
|
|
|
public void cancelConsignment(Long id) {
|
|
|
@@ -337,7 +435,7 @@ public class AssetService {
|
|
|
});
|
|
|
}
|
|
|
asset.setConsignment(false);
|
|
|
- assetRepo.save(asset);
|
|
|
+ assetRepo.saveAndFlush(asset);
|
|
|
}
|
|
|
|
|
|
public void cancelPublic(Long id) {
|
|
|
@@ -366,7 +464,7 @@ public class AssetService {
|
|
|
|
|
|
asset.setPublicShow(false);
|
|
|
asset.setPublicCollectionId(null);
|
|
|
- assetRepo.save(asset);
|
|
|
+ assetRepo.saveAndFlush(asset);
|
|
|
}
|
|
|
|
|
|
public void usePrivilege(Long assetId, Long privilegeId) {
|
|
|
@@ -378,21 +476,40 @@ public class AssetService {
|
|
|
p.setOpenedBy(SecurityUtils.getAuthenticatedUser().getId());
|
|
|
}
|
|
|
});
|
|
|
- assetRepo.save(asset);
|
|
|
+ assetRepo.saveAndFlush(asset);
|
|
|
}
|
|
|
|
|
|
public void transfer(Asset asset, BigDecimal price, User toUser, TransferReason reason, Long orderId) {
|
|
|
+ transfer(asset, price, toUser, reason, orderId, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ private User createFakeUser() {
|
|
|
+ String name = "0x" + RandomStringUtils.randomAlphabetic(8);
|
|
|
+ return userRepo.save(User.builder()
|
|
|
+ .username(name)
|
|
|
+ .nickname(name)
|
|
|
+ .avatar(Constants.DEFAULT_AVATAR)
|
|
|
+ .isPublicShow(true)
|
|
|
+ .build());
|
|
|
+ }
|
|
|
+
|
|
|
+ public void transfer(Asset asset, BigDecimal price, User toUser, TransferReason reason, Long orderId, boolean safeFlag) {
|
|
|
Objects.requireNonNull(asset, "原藏品不能为空");
|
|
|
Objects.requireNonNull(toUser, "转让人不能为空");
|
|
|
Objects.requireNonNull(reason, "转让原因不能为空");
|
|
|
|
|
|
+ User newOwner = toUser;
|
|
|
+ if (safeFlag) {
|
|
|
+ newOwner = createFakeUser();
|
|
|
+ }
|
|
|
+
|
|
|
Asset newAsset = new Asset();
|
|
|
BeanUtils.copyProperties(asset, newAsset);
|
|
|
newAsset.setId(null);
|
|
|
newAsset.setUserId(toUser.getId());
|
|
|
- newAsset.setOwner(toUser.getNickname());
|
|
|
- newAsset.setOwnerId(toUser.getId());
|
|
|
- newAsset.setOwnerAvatar(toUser.getAvatar());
|
|
|
+ newAsset.setOwner(newOwner.getNickname());
|
|
|
+ newAsset.setOwnerId(newOwner.getId());
|
|
|
+ newAsset.setOwnerAvatar(newOwner.getAvatar());
|
|
|
newAsset.setPublicShow(false);
|
|
|
newAsset.setConsignment(false);
|
|
|
newAsset.setPublicCollectionId(null);
|
|
|
@@ -405,19 +522,27 @@ public class AssetService {
|
|
|
newAsset.setType(CollectionType.DEFAULT);
|
|
|
newAsset.setSource(TransferReason.GIFT == reason ? AssetSource.GIFT : AssetSource.TRANSFER);
|
|
|
newAsset.setTags(new HashSet<>(asset.getTags()));
|
|
|
- assetRepo.save(newAsset);
|
|
|
+ newAsset.setSafeFlag(safeFlag);
|
|
|
+ newAsset.setHoldDays(asset.getOldHoldDays());
|
|
|
+ assetRepo.saveAndFlush(newAsset);
|
|
|
|
|
|
- tokenHistoryRepo.save(TokenHistory.builder()
|
|
|
+ TokenHistory tokenHistory = TokenHistory.builder()
|
|
|
.tokenId(asset.getTokenId())
|
|
|
.fromUser(asset.getOwner())
|
|
|
.fromUserId(asset.getOwnerId())
|
|
|
.fromAvatar(asset.getOwnerAvatar())
|
|
|
- .toUser(toUser.getNickname())
|
|
|
- .toUserId(toUser.getId())
|
|
|
- .toAvatar(toUser.getAvatar())
|
|
|
+ .toUser(newOwner.getNickname())
|
|
|
+ .toUserId(newOwner.getId())
|
|
|
+ .toAvatar(newOwner.getAvatar())
|
|
|
.operation(reason.getDescription())
|
|
|
.price(TransferReason.GIFT == reason ? null : price)
|
|
|
- .build());
|
|
|
+ .build();
|
|
|
+ tokenHistoryRepo.save(tokenHistory);
|
|
|
+
|
|
|
+ //购买者加绿洲石
|
|
|
+ if (TransferReason.TRANSFER.equals(reason) || TransferReason.AUCTION.equals(reason)) {
|
|
|
+ rockRecordService.addRock(newOwner.getId(), price, "购买");
|
|
|
+ }
|
|
|
|
|
|
asset.setPublicShow(false);
|
|
|
asset.setConsignment(false);
|
|
|
@@ -432,10 +557,10 @@ public class AssetService {
|
|
|
case TRANSFER:
|
|
|
asset.setStatus(AssetStatus.TRANSFERRED);
|
|
|
}
|
|
|
- asset.setOwner(toUser.getNickname());
|
|
|
- asset.setOwnerId(toUser.getId());
|
|
|
- asset.setOwnerAvatar(toUser.getAvatar());
|
|
|
- assetRepo.save(asset);
|
|
|
+ asset.setOwner(newOwner.getNickname());
|
|
|
+ asset.setOwnerId(newOwner.getId());
|
|
|
+ asset.setOwnerAvatar(newOwner.getAvatar());
|
|
|
+ assetRepo.saveAndFlush(asset);
|
|
|
|
|
|
//vip权限转让
|
|
|
CollectionPrivilege collectionPrivilege = collectionPrivilegeRepo.findByCollectionId(asset.getCollectionId());
|
|
|
@@ -505,12 +630,11 @@ public class AssetService {
|
|
|
case "转让":
|
|
|
userHistory.setDescription(tokenHistory.getToUserId().equals(userId) ? "作品交易——买入" : "作品交易——售出");
|
|
|
break;
|
|
|
- case "空投":
|
|
|
- userHistory.setDescription("空投");
|
|
|
- break;
|
|
|
case "转赠":
|
|
|
userHistory.setDescription(tokenHistory.getToUserId().equals(userId) ? "他人赠送" : "作品赠送");
|
|
|
break;
|
|
|
+ default:
|
|
|
+ userHistory.setDescription(tokenHistory.getOperation());
|
|
|
}
|
|
|
return userHistory;
|
|
|
});
|
|
|
@@ -549,12 +673,11 @@ public class AssetService {
|
|
|
case "转让":
|
|
|
userHistory.setDescription(tokenHistory.getToUserId().equals(userId) ? "作品交易——买入" : "作品交易——售出");
|
|
|
break;
|
|
|
- case "空投":
|
|
|
- userHistory.setDescription("空投");
|
|
|
- break;
|
|
|
case "转赠":
|
|
|
userHistory.setDescription(tokenHistory.getToUserId().equals(userId) ? "他人赠送" : "作品赠送");
|
|
|
break;
|
|
|
+ default:
|
|
|
+ userHistory.setDescription(tokenHistory.getOperation());
|
|
|
}
|
|
|
return userHistory;
|
|
|
});
|
|
|
@@ -572,18 +695,18 @@ public class AssetService {
|
|
|
|
|
|
@Cacheable(value = "userStat", key = "#userId")
|
|
|
public Map<String, BigDecimal> breakdown(Long userId) {
|
|
|
- List<TokenHistory> page = tokenHistoryRepo.userHistory(userId);
|
|
|
- BigDecimal sale = page.stream()
|
|
|
- .filter(th -> th.getFromUserId().equals(userId) && ObjectUtils.isNotEmpty(th.getPrice()))
|
|
|
- .map(TokenHistory::getPrice)
|
|
|
- .reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
|
- BigDecimal buy = page.stream()
|
|
|
- .filter(th -> th.getToUserId().equals(userId) && ObjectUtils.isNotEmpty(th.getPrice()))
|
|
|
- .map(TokenHistory::getPrice)
|
|
|
- .reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
|
+// List<TokenHistory> page = tokenHistoryRepo.userHistory(userId);
|
|
|
+// BigDecimal sale = page.stream()
|
|
|
+// .filter(th -> th.getFromUserId().equals(userId) && ObjectUtils.isNotEmpty(th.getPrice()))
|
|
|
+// .map(TokenHistory::getPrice)
|
|
|
+// .reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
|
+// BigDecimal buy = page.stream()
|
|
|
+// .filter(th -> th.getToUserId().equals(userId) && ObjectUtils.isNotEmpty(th.getPrice()))
|
|
|
+// .map(TokenHistory::getPrice)
|
|
|
+// .reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
|
Map<String, BigDecimal> map = new HashMap<>();
|
|
|
- map.put("sale", sale);
|
|
|
- map.put("buy", buy);
|
|
|
+ map.put("sale", tokenHistoryRepo.userSale(userId));
|
|
|
+ map.put("buy", rockRecordService.getRock(userId).getRecord());
|
|
|
return map;
|
|
|
}
|
|
|
|
|
|
@@ -656,7 +779,7 @@ public class AssetService {
|
|
|
});
|
|
|
}
|
|
|
asset.setConsignment(false);
|
|
|
- assetRepo.save(asset);
|
|
|
+ assetRepo.saveAndFlush(asset);
|
|
|
}
|
|
|
|
|
|
// @Cacheable(cacheNames = "fmaa", key = "#userId+'#'+#mintActivityId+'#'+#pageable.hashCode()")
|
|
|
@@ -672,13 +795,172 @@ public class AssetService {
|
|
|
Set<Tag> tags = mintActivity.getRule().getTags();
|
|
|
if (tags.isEmpty()) return new PageImpl<>(Collections.emptyList());
|
|
|
return assetRepo.findAll((Specification<Asset>) (root, query, criteriaBuilder) ->
|
|
|
- query.distinct(true).where(criteriaBuilder.equal(root.get("userId"), userId),
|
|
|
- criteriaBuilder.equal(root.get("status"), AssetStatus.NORMAL),
|
|
|
- root.join("tags").get("id").in(tags.stream().map(Tag::getId).toArray()))
|
|
|
+ query.distinct(true).where(
|
|
|
+ // where userId=some id
|
|
|
+ criteriaBuilder.equal(root.get("userId"), userId),
|
|
|
+ // and (lockTo is null or (lockTo is not null and lockTo < now))
|
|
|
+ criteriaBuilder.or(criteriaBuilder.isNull(root.get("lockTo")),
|
|
|
+ criteriaBuilder.and(criteriaBuilder.isNotNull(root.get("lockTo")),
|
|
|
+ criteriaBuilder.lessThan(root.get("lockTo"), LocalDateTime.now()))),
|
|
|
+ // and status = 'NORMAL'
|
|
|
+ criteriaBuilder.equal(root.get("status"), AssetStatus.NORMAL),
|
|
|
+ // and has some tagId
|
|
|
+ root.join("tags").get("id").in(tags.stream().map(Tag::getId).toArray()))
|
|
|
.getRestriction(), pageable);
|
|
|
} else {
|
|
|
return assetRepo.findByUserIdAndStatusAndNameLike(userId, AssetStatus.NORMAL,
|
|
|
"%" + mintActivity.getCollectionName() + "%", pageable);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ public void destroy(Long id, Long userId ,String tradeCode) {
|
|
|
+ Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
|
|
|
+ if (!asset.getUserId().equals(userId)) {
|
|
|
+ throw new BusinessException("此藏品不属于你");
|
|
|
+ }
|
|
|
+ if (asset.getStatus() != AssetStatus.NORMAL) {
|
|
|
+ throw new BusinessException("当前状态不可销毁");
|
|
|
+ }
|
|
|
+ if (asset.isPublicShow()) {
|
|
|
+ throw new BusinessException("请先取消公开展示");
|
|
|
+// cancelPublic(asset);
|
|
|
+ }
|
|
|
+ User user = userRepo.findById(userId).orElseThrow(new BusinessException("用户不存在"));
|
|
|
+ if (StringUtils.isEmpty(user.getTradeCode())){
|
|
|
+ throw new BusinessException("未设置交易密码");
|
|
|
+ }
|
|
|
+ if (!passwordEncoder.matches(tradeCode, user.getTradeCode())) {
|
|
|
+ throw new BusinessException("交易密码错误");
|
|
|
+ }
|
|
|
+
|
|
|
+ User toUser = userRepo.findById(Constants.BLACK_HOLE_USER_ID).orElseThrow(new BusinessException("无记录"));
|
|
|
+
|
|
|
+ TokenHistory tokenHistory = TokenHistory.builder()
|
|
|
+ .tokenId(asset.getTokenId())
|
|
|
+ .fromUser(asset.getOwner())
|
|
|
+ .fromUserId(asset.getOwnerId())
|
|
|
+ .fromAvatar(asset.getOwnerAvatar())
|
|
|
+ .toUser(toUser.getNickname())
|
|
|
+ .toUserId(toUser.getId())
|
|
|
+ .toAvatar(toUser.getAvatar())
|
|
|
+ .operation(TransferReason.DESTROY.getDescription())
|
|
|
+ .price(null)
|
|
|
+ .build();
|
|
|
+ tokenHistoryRepo.save(tokenHistory);
|
|
|
+
|
|
|
+ asset.setPublicShow(false);
|
|
|
+ asset.setConsignment(false);
|
|
|
+ asset.setPublicCollectionId(null);
|
|
|
+ asset.setStatus(AssetStatus.DESTROYED);
|
|
|
+ asset.setOwner(toUser.getNickname());
|
|
|
+ asset.setOwnerId(toUser.getId());
|
|
|
+ asset.setOwnerAvatar(toUser.getAvatar());
|
|
|
+ assetRepo.saveAndFlush(asset);
|
|
|
+ //积分记录
|
|
|
+ destroyRecordRepo.save(DestroyRecord.builder()
|
|
|
+ .userId(userId)
|
|
|
+ .assetId(asset.getId())
|
|
|
+ .name(asset.getName())
|
|
|
+ .pic(asset.getPic().get(0).getUrl())
|
|
|
+ .record(1)
|
|
|
+ .type(RecordType.OBTAIN)
|
|
|
+ .build());
|
|
|
+
|
|
|
+ //加积分
|
|
|
+ userRepo.addDestroyPoint(userId, 1);
|
|
|
+ }
|
|
|
+ public double getRoyalties(Long minterId, double royalties, Long userId) {
|
|
|
+ if (royalties == 3) {
|
|
|
+ return 3;
|
|
|
+ }
|
|
|
+ LongArrayConverter converter = new LongArrayConverter();
|
|
|
+ String discountMinter = sysConfigService.getString("discount_minter");
|
|
|
+ List<Long> minterIds = converter.convertToEntityAttribute(discountMinter);
|
|
|
+ if (minterIds.contains(minterId)) {
|
|
|
+ String discountCollection = sysConfigService.getString("discount_collection");
|
|
|
+ List<Long> collectionIds = converter.convertToEntityAttribute(discountCollection);
|
|
|
+ Long assetId = assetRepo.findDiscount(userId, collectionIds);
|
|
|
+ if (ObjectUtils.isNotEmpty(assetId)) {
|
|
|
+ return 3;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return royalties;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Async
|
|
|
+ public void hcChain() throws ExecutionException, InterruptedException {
|
|
|
+ new ForkJoinPool(1000).submit(() -> {
|
|
|
+ AtomicInteger num = new AtomicInteger();
|
|
|
+ assetRepo.findByStatus(AssetStatus.NORMAL).parallelStream()
|
|
|
+ .forEach(asset -> {
|
|
|
+ if (asset.getHcTxHash() == null) {
|
|
|
+ User user = userRepo.findById(asset.getUserId()).orElse(null);
|
|
|
+ if (user != null) {
|
|
|
+ if (user.getHcChainAddress() == null) {
|
|
|
+ user.setHcChainAddress(hcChainService.createAccount(asset.getUserId()));
|
|
|
+ }
|
|
|
+ NFT nft = hcChainService.mint(user.getHcChainAddress(), asset.getTokenId());
|
|
|
+ asset.setHcTokenId(nft.getTokenId());
|
|
|
+ asset.setHcTxHash(nft.getTxHash());
|
|
|
+ asset.setGasUsed(nft.getGasUsed());
|
|
|
+ assetRepo.saveAndFlush(asset);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ log.info("hcChain:" + num.getAndIncrement());
|
|
|
+ });
|
|
|
+ }).get();
|
|
|
+ }
|
|
|
+
|
|
|
+ public void lockAsset(Long userId, Long assetId, Duration duration) {
|
|
|
+ User user = userRepo.findById(userId).orElseThrow(new BusinessException("用户不存在"));
|
|
|
+ Asset asset = assetRepo.findById(assetId).orElseThrow(new BusinessException("藏品不存在"));
|
|
|
+ if (!asset.getUserId().equals(userId)) {
|
|
|
+ throw new BusinessException("无权限");
|
|
|
+ }
|
|
|
+ if (asset.getLockTo() != null && asset.getLockTo().isAfter(LocalDateTime.now())) {
|
|
|
+ throw new BusinessException("已是锁仓状态");
|
|
|
+ }
|
|
|
+ if (asset.getType() == CollectionType.SHOWROOM) {
|
|
|
+ throw new BusinessException("展厅不可锁定");
|
|
|
+ }
|
|
|
+ if (asset.getStatus() != AssetStatus.NORMAL) {
|
|
|
+ throw new BusinessException("当前状态不可锁定");
|
|
|
+ }
|
|
|
+ if (asset.isPublicShow() || asset.isConsignment()) {
|
|
|
+ throw new BusinessException("请先取消展示和寄售");
|
|
|
+ }
|
|
|
+ if (duration.compareTo(Duration.parse("P1D")) < 0) {
|
|
|
+ throw new BusinessException("最小锁定1天");
|
|
|
+ }
|
|
|
+ asset.setLockAt(LocalDateTime.now());
|
|
|
+ asset.setLockTo(asset.getLockAt().plus(duration));
|
|
|
+ assetRepo.saveAndFlush(asset);
|
|
|
+
|
|
|
+ assetLockRepo.save(AssetLock.builder()
|
|
|
+ .userId(userId)
|
|
|
+ .phone(user.getPhone())
|
|
|
+ .nickname(user.getNickname())
|
|
|
+ .assetId(assetId)
|
|
|
+ .name(asset.getName())
|
|
|
+ .number(asset.getNumber())
|
|
|
+ .lockAt(asset.getLockAt())
|
|
|
+ .lockTo(asset.getLockTo())
|
|
|
+ .duration(duration)
|
|
|
+ .build());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Async
|
|
|
+ public void giveBonus() {
|
|
|
+ List<TokenHistory> list = tokenHistoryRepo.findByOperationAndCreatedAtBefore("销毁", LocalDateTime.of(2022, 7, 23, 21, 0, 0));
|
|
|
+ list.parallelStream().forEach(tokenHistory -> {
|
|
|
+ String name = assetRepo.findFirstByTokenId(tokenHistory.getTokenId()).getName();
|
|
|
+ if (Pattern.matches(".*僵尸动物园SSR #.*", name)) {
|
|
|
+ log.info("SSR奖励 {}", name);
|
|
|
+ userBalanceService.modifyBalance(tokenHistory.getFromUserId(), new BigDecimal("1000"), BalanceType.BONUS, null, false, null);
|
|
|
+ } else if (Pattern.matches(".*僵尸动物园SR #.*", name)) {
|
|
|
+ log.info("SR奖励 {}", name);
|
|
|
+ userBalanceService.modifyBalance(tokenHistory.getFromUserId(), new BigDecimal("25"), BalanceType.BONUS, null, false, null);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
}
|