package com.izouma.nineth.service; import cn.hutool.core.convert.Convert; import com.izouma.nineth.TokenHistory; import com.izouma.nineth.config.GeneralProperties; 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.repo.*; import com.izouma.nineth.utils.JpaUtils; import com.izouma.nineth.utils.PageUtil; import com.izouma.nineth.utils.SecurityUtils; import com.izouma.nineth.utils.TokenUtils; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import net.coobird.thumbnailator.filters.Pipeline; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.beans.BeanUtils; import org.springframework.cache.annotation.Cacheable; import org.springframework.context.ApplicationContext; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import javax.persistence.criteria.Predicate; import java.math.BigDecimal; 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.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 SysConfigService sysConfigService; private RocketMQTemplate rocketMQTemplate; private GeneralProperties generalProperties; private ShowroomRepo showroomRepo; private ShowCollectionRepo showCollectionRepo; public Page all(PageQuery pageQuery) { Page all = assetRepo.findAll(JpaUtils.toSpecification(pageQuery, Asset.class), JpaUtils.toPageRequest(pageQuery)); Map query = pageQuery.getQuery(); if (query.containsKey("userId")) { List 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 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.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()) .operation(type) .price(price) .build()); rocketMQTemplate.syncSend(generalProperties.getMintTopic(), asset.getId()); return asset; } public Asset createAsset(BlindBoxItem winItem, User user, Long orderId, BigDecimal price, String type, Integer number, Integer holdDays) { Asset asset = Asset.create(winItem, user, holdDays); 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) .build()); rocketMQTemplate.syncSend(generalProperties.getMintTopic(), asset.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()) .build(); collectionRepo.save(collection); asset.setPublicShow(true); asset.setPublicCollectionId(collection.getId()); assetRepo.save(asset); } public synchronized 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; if (ObjectUtils.isEmpty(asset.getHoldDays())) { holdDays = sysConfigService.getInt("hold_days"); } else { holdDays = asset.getHoldDays(); } 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); } //寄售中的展厅需要先删除展厅 if (CollectionType.SHOWROOM.equals(asset.getType())) { if (showroomRepo.findByAssetId(id).isPresent()) { throw 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(true) .price(price) .properties(asset.getProperties()) .canResale(asset.isCanResale()) .royalties(asset.getRoyalties()) .serviceCharge(asset.getServiceCharge()) .assetId(id) .number(asset.getNumber()) .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); // 如果展厅有此藏品 showCollectionRepo.softDeleteCollection(asset.getPublicCollectionId()); 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()); newAsset.setType(CollectionType.DEFAULT); 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) .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()); 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); } 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()) .build(); t.setCreatedAt(asset.getCreatedAt()); tokenHistoryRepo.save(t); } catch (Exception e) { } }); } public Page userHistory(Long userId, Long toUserId, Long fromUserId, Pageable pageable) { Page page; if (ObjectUtils.isNotEmpty(toUserId)) { page = tokenHistoryRepo.userHistoryTo(userId, toUserId, pageable); } else if (ObjectUtils.isNotEmpty(fromUserId)) { page = tokenHistoryRepo.userHistoryFrom(userId, fromUserId, pageable); } else { page = tokenHistoryRepo.userHistory(userId, 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 Page userHistory(Long userId, PageQuery pageQuery) { Page page = tokenHistoryRepo.findAll(((root, criteriaQuery, criteriaBuilder) -> { List and = JpaUtils.toPredicates(pageQuery, TokenHistory.class, root, criteriaQuery, criteriaBuilder); Map query = pageQuery.getQuery(); //String operation = (String) query.get("operation"); if (ObjectUtils.isEmpty(query.get("toUserId")) && ObjectUtils.isEmpty(query.get("fromUserId"))) { and.add(criteriaBuilder.or(criteriaBuilder.equal(root.get("toUserId"), userId), criteriaBuilder.equal(root.get("fromUserId"), userId))); } else { if (ObjectUtils.isNotEmpty(query.get("toUserId"))) { and.add(criteriaBuilder.and(criteriaBuilder.equal(root.get("toUserId"), userId))); } else { and.add(criteriaBuilder.and(criteriaBuilder.equal(root.get("fromUserId"), userId))); } } /*if (ObjectUtils.isNotEmpty(query.get("operation"))){ and.add(criteriaBuilder.and(criteriaBuilder.equal(root.get("operation"),operation))); }*/ return criteriaBuilder.and(and.toArray(new Predicate[0])); }), JpaUtils.toPageRequest(pageQuery)); Set tokenIds = page.stream().map(TokenHistory::getTokenId).collect(Collectors.toSet()); List assets = tokenIds.isEmpty() ? new ArrayList<>() : assetRepo.findByTokenIdIn(tokenIds); Map query = pageQuery.getQuery(); Page map = 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) ? "他人赠送" : "作品赠送"); userHistory.setDescription("赠送"); break; } Object operation1 = query.get("operation"); if (operation1!=null){ String operation = (String) operation1; if (!userHistory.getOperation().equals(operation)) { return null; } } return userHistory; }); List content = map.getContent(); if (content!=null&&content.size()>0){ content.removeIf(Objects::isNull); Pageable pageable = PageRequest.of(pageQuery.getPage() - 1, pageQuery.getSize()); map = PageUtil.createPageFromList(content, pageable); } return map; } public String mint(LocalDateTime time) { if (time == null) { time = LocalDateTime.now(); } for (Asset asset : assetRepo.toMint(time)) { rocketMQTemplate.syncSend(generalProperties.getMintTopic(), asset.getId()); } return "ok"; } @Cacheable(value = "userStat", key = "#userId") public Map breakdown(Long userId) { List 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 map = new HashMap<>(); map.put("sale", sale); map.put("buy", buy); return map; } public void transferCDN() throws ExecutionException, InterruptedException { ForkJoinPool customThreadPool = new ForkJoinPool(100); customThreadPool.submit(() -> { collectionRepo.selectResource().parallelStream().forEach(list -> { for (int i = 0; i < list.size(); i++) { list.set(i, replaceCDN(list.get(i))); } collectionRepo.updateCDN(Long.parseLong(list.get(0)), list.get(1), list.get(2), list.get(3), list.get(4), list.get(5)); }); assetRepo.selectResource().parallelStream().forEach(list -> { for (int i = 0; i < list.size(); i++) { list.set(i, replaceCDN(list.get(i))); } assetRepo.updateCDN(Long.parseLong(list.get(0)), list.get(1), list.get(2), list.get(3), list.get(4), list.get(5)); }); }).get(); } public String replaceCDN(String url) { if (url == null) return null; return url.replaceAll("https://raex-meta\\.oss-cn-shenzhen\\.aliyuncs\\.com", "https://cdn.raex.vip"); } }