package com.izouma.nineth.service; import com.izouma.nineth.TokenHistory; import com.izouma.nineth.domain.Collection; import com.izouma.nineth.domain.*; import com.izouma.nineth.dto.PageQuery; import com.izouma.nineth.dto.UserHistory; import com.izouma.nineth.enums.AssetStatus; import com.izouma.nineth.enums.CollectionSource; import com.izouma.nineth.enums.CollectionType; import com.izouma.nineth.enums.OrderStatus; import com.izouma.nineth.event.TransferAssetEvent; import com.izouma.nineth.exception.BusinessException; import com.izouma.nineth.lock.RedisLockable; import com.izouma.nineth.repo.*; import com.izouma.nineth.utils.JpaUtils; import com.izouma.nineth.utils.SecurityUtils; import com.izouma.nineth.utils.TokenUtils; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; import org.springframework.context.ApplicationContext; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.*; import java.util.stream.Collectors; @Service @AllArgsConstructor @Slf4j public class AssetService { private AssetRepo assetRepo; private UserRepo userRepo; private CollectionRepo collectionRepo; private ApplicationContext applicationContext; private OrderRepo orderRepo; private TokenHistoryRepo tokenHistoryRepo; private AssetMintService assetMintService; private SysConfigService sysConfigService; public Page all(PageQuery pageQuery) { return assetRepo.findAll(JpaUtils.toSpecification(pageQuery, Asset.class), JpaUtils.toPageRequest(pageQuery)); } public Asset createAsset(Collection collection, User user, Long orderId, BigDecimal price, String type, Integer number) { Asset asset = Asset.create(collection, user); asset.setTokenId(TokenUtils.genTokenId()); asset.setNumber(number); asset.setOrderId(orderId); asset.setPrice(price); assetRepo.save(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()) .operation(type) .price(price) .projectId(asset.getProjectId()) .build()); assetMintService.mint(asset); return asset; } public Asset createAsset(BlindBoxItem winItem, User user, Long orderId, BigDecimal price, String type, Integer number) { Asset asset = Asset.create(winItem, user); asset.setTokenId(TokenUtils.genTokenId()); asset.setNumber(number); asset.setOrderId(orderId); asset.setPrice(price); 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()) .operation(type) .price(price) .projectId(asset.getProjectId()) .build()); assetMintService.mint(asset.getId(), user.getId()); return asset; } public void publicShow(Long id) { Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录")); if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) { throw new BusinessException("此藏品不属于你"); } if (asset.isPublicShow()) { return; } if (asset.getStatus() != AssetStatus.NORMAL) { throw new BusinessException("当前状态不可展示"); } User owner = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在")); Collection collection = Collection.builder() .name(asset.getName()) .pic(asset.getPic()) .minter(asset.getMinter()) .minterId(asset.getMinterId()) .minterAvatar(asset.getMinterAvatar()) .owner(owner.getNickname()) .ownerId(owner.getId()) .ownerAvatar(owner.getAvatar()) .detail(asset.getDetail()) .type(CollectionType.DEFAULT) .source(CollectionSource.TRANSFER) .sale(0) .stock(1) .total(1) .onShelf(true) .salable(false) .price(BigDecimal.valueOf(0)) .properties(asset.getProperties()) .canResale(asset.isCanResale()) .royalties(asset.getRoyalties()) .serviceCharge(asset.getServiceCharge()) .assetId(id) .number(asset.getNumber()) .projectId(asset.getProjectId()) .build(); collectionRepo.save(collection); asset.setPublicShow(true); asset.setPublicCollectionId(collection.getId()); assetRepo.save(asset); } public void consignment(Long id, BigDecimal price) { Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录")); if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) { throw new BusinessException("此藏品不属于你"); } int holdDays = sysConfigService.getInt("hold_days"); if (ChronoUnit.DAYS.between(asset.getCreatedAt(), LocalDateTime.now()) < holdDays) { throw new BusinessException("需持有满" + holdDays + "天才能寄售上架"); } User owner = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在")); if (StringUtils.isBlank(owner.getSettleAccountId())) { throw new BusinessException("请先绑定银行卡"); } if (asset.isConsignment()) { throw new BusinessException("已寄售,请勿重新操作"); } if (asset.getStatus() != AssetStatus.NORMAL) { throw new BusinessException("当前状态不可寄售"); } if (asset.isPublicShow()) { cancelPublic(asset); } Collection collection = Collection.builder() .name(asset.getName()) .pic(asset.getPic()) .minter(asset.getMinter()) .minterId(asset.getMinterId()) .minterAvatar(asset.getMinterAvatar()) .owner(owner.getNickname()) .ownerId(owner.getId()) .ownerAvatar(owner.getAvatar()) .detail(asset.getDetail()) .type(CollectionType.DEFAULT) .source(CollectionSource.TRANSFER) .sale(0) .stock(1) .total(1) .onShelf(true) .salable(true) .price(price) .properties(asset.getProperties()) .canResale(asset.isCanResale()) .royalties(asset.getRoyalties()) .serviceCharge(asset.getServiceCharge()) .assetId(id) .number(asset.getNumber()) .projectId(asset.getProjectId()) .build(); collectionRepo.save(collection); asset.setPublicShow(true); asset.setConsignment(true); asset.setPublicCollectionId(collection.getId()); asset.setSellPrice(price); assetRepo.save(asset); } public void cancelConsignment(Long id) { Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录")); if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) { throw new BusinessException("此藏品不属于你"); } cancelConsignment(asset); } public void cancelConsignment(Asset asset) { if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) { throw new BusinessException("此藏品不属于你"); } if (asset.getPublicCollectionId() != null) { List orders = orderRepo.findByCollectionId(asset.getPublicCollectionId()); if (orders.stream().anyMatch(o -> o.getStatus() != OrderStatus.CANCELLED)) { throw new BusinessException("已有订单不可取消"); } collectionRepo.findById(asset.getPublicCollectionId()) .ifPresent(collection -> { collection.setSalable(false); collectionRepo.save(collection); }); } asset.setConsignment(false); assetRepo.save(asset); } public void cancelPublic(Long id) { Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录")); if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) { throw new BusinessException("此藏品不属于你"); } cancelPublic(asset); } public void cancelPublic(Asset asset) { if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) { throw new BusinessException("此藏品不属于你"); } if (!asset.isPublicShow()) { return; } if (asset.isConsignment()) { cancelConsignment(asset); } Collection collection = collectionRepo.findById(asset.getPublicCollectionId()) .orElseThrow(new BusinessException("无展示记录")); collectionRepo.delete(collection); asset.setPublicShow(false); asset.setPublicCollectionId(null); assetRepo.save(asset); } public void usePrivilege(Long assetId, Long privilegeId) { Asset asset = assetRepo.findById(assetId).orElseThrow(new BusinessException("无记录")); asset.getPrivileges().stream().filter(p -> p.getId().equals(privilegeId)).forEach(p -> { p.setOpened(true); p.setOpenTime(LocalDateTime.now()); p.setOpenedBy(SecurityUtils.getAuthenticatedUser().getId()); }); assetRepo.save(asset); } @Async public void transfer(Asset asset, BigDecimal price, User toUser, String reason, Long orderId) { 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.setPublicShow(false); newAsset.setConsignment(false); newAsset.setPublicCollectionId(null); newAsset.setStatus(AssetStatus.NORMAL); newAsset.setPrice(price); newAsset.setSellPrice(null); newAsset.setOrderId(orderId); newAsset.setFromAssetId(asset.getId()); assetRepo.save(newAsset); tokenHistoryRepo.save(TokenHistory.builder() .tokenId(asset.getTokenId()) .fromUser(asset.getOwner()) .fromUserId(asset.getOwnerId()) .fromAvatar(asset.getOwnerAvatar()) .toUser(toUser.getNickname()) .toUserId(toUser.getId()) .toAvatar(toUser.getAvatar()) .operation(reason) .price("转赠".equals(reason) ? null : price) .projectId(asset.getProjectId()) .build()); asset.setPublicShow(false); asset.setConsignment(false); asset.setPublicCollectionId(null); asset.setStatus("转赠".equals(reason) ? AssetStatus.GIFTED : AssetStatus.TRANSFERRED); asset.setOwner(toUser.getNickname()); asset.setOwnerId(toUser.getId()); asset.setOwnerAvatar(toUser.getAvatar()); asset.setOutTime(LocalDateTime.now()); assetRepo.save(asset); if (orderId != null) { applicationContext.publishEvent(new TransferAssetEvent(this, true, newAsset)); } } public List tokenHistory(String tokenId, Long assetId) { if (tokenId == null) { if (assetId == null) return new ArrayList<>(); tokenId = assetRepo.findById(assetId).map(Asset::getTokenId).orElse(null); } if (tokenId == null) return new ArrayList<>(); return tokenHistoryRepo.findByTokenIdOrderByCreatedAtDesc(tokenId); } @RedisLockable(key = "#id", expiration = 60, isWaiting = true) public void testLock(String id, String i) throws InterruptedException { Thread.sleep(1000); log.info("" + i); } public void setHistory() { List assets = assetRepo.findByCreatedAtBefore(LocalDateTime.of(2021, 11, 22, 23, 59, 59)); assets.parallelStream().forEach(asset -> { try { User owner = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("")); Order order = orderRepo.findById(asset.getOrderId()).orElseThrow(new BusinessException("")); TokenHistory t = TokenHistory.builder() .tokenId(asset.getTokenId()) .fromUser(asset.getMinter()) .fromUserId(asset.getMinterId()) .fromAvatar(asset.getMinterAvatar()) .toUser(owner.getNickname()) .toUserId(owner.getId()) .toAvatar(owner.getAvatar()) .operation("出售") .price(order.getPrice()) .projectId(asset.getProjectId()) .build(); t.setCreatedAt(asset.getCreatedAt()); tokenHistoryRepo.save(t); } catch (Exception e) { } }); } public Page userHistory(Long userId, int projectId, Pageable pageable) { Page page = tokenHistoryRepo.userHistoryAndProjectId(userId, projectId, pageable); Set tokenIds = page.stream().map(TokenHistory::getTokenId).collect(Collectors.toSet()); List assets = tokenIds.isEmpty() ? new ArrayList<>() : assetRepo.findByTokenIdIn(tokenIds); return page.map(tokenHistory -> { UserHistory userHistory = new UserHistory(); BeanUtils.copyProperties(tokenHistory, userHistory); Optional asset = assets.stream().filter(a -> a.getTokenId().equals(tokenHistory.getTokenId())) .findAny(); userHistory.setAssetName(asset.map(Asset::getName).orElse(null)); userHistory.setPic(asset.map(Asset::getPic).orElse(new ArrayList<>())); switch (tokenHistory.getOperation()) { case "出售": case "转让": userHistory.setDescription(tokenHistory.getToUserId().equals(userId) ? "作品交易——买入" : "作品交易——售出"); break; case "空投": userHistory.setDescription("空投"); break; case "转赠": userHistory.setDescription(tokenHistory.getToUserId().equals(userId) ? "他人赠送" : "作品赠送"); break; } return userHistory; }); } public Map holdQuery(List names, LocalDateTime startTime, LocalDateTime endTime) { Map result = new HashMap<>(); userRepo.findAll().stream().parallel().forEach(user -> { List assets = assetRepo.findByUserId(user.getId()); assets = assets.stream().filter(a -> names.stream().anyMatch(n -> n.equals(a.getName()))) .collect(Collectors.toList()); if (assets.size() < names.size()) { return; } assets = assets.stream().filter(a -> a.getCreatedAt().isBefore(startTime)) .collect(Collectors.toList()); if (assets.size() < names.size()) { return; } assets = assets.stream().filter(a -> { if (a.getStatus() != AssetStatus.GIFTED && a.getStatus() != AssetStatus.TRANSFERRED) { return true; } else { Asset a1 = assetRepo.findFirstByTokenIdAndCreatedAtAfterOrderByCreatedAt(a.getTokenId(), a.getCreatedAt()); return a1 != null && a.getCreatedAt().isAfter(endTime); } }) .collect(Collectors.toList()); boolean flag = true; Map map = new HashMap<>(); for (String name : names) { int c = (int) assets.stream().filter(a -> name.equals(a.getName())).count(); map.put(name, c); flag = flag && (c > 0); } if (flag) { result.put(user, map.values().stream().mapToInt(i -> i).min().orElse(0)); } }); return result; } }