package com.izouma.nineth.service; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alipay.api.AlipayClient; import com.alipay.api.request.AlipayTradeWapPayRequest; import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; import com.github.binarywang.wxpay.bean.order.WxPayMwebOrderResult; import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; import com.github.binarywang.wxpay.constant.WxPayConstants; import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.service.WxPayService; import com.github.kevinsawicki.http.HttpRequest; import com.izouma.nineth.TokenHistory; import com.izouma.nineth.config.AlipayProperties; import com.izouma.nineth.config.WxPayProperties; import com.izouma.nineth.domain.*; import com.izouma.nineth.dto.NFT; import com.izouma.nineth.dto.NFTAccount; import com.izouma.nineth.dto.PageQuery; import com.izouma.nineth.enums.*; import com.izouma.nineth.event.CreateAssetEvent; import com.izouma.nineth.exception.BusinessException; import com.izouma.nineth.repo.*; import com.izouma.nineth.utils.JpaUtils; import com.izouma.nineth.utils.SecurityUtils; import com.izouma.nineth.utils.SnowflakeIdWorker; import io.ipfs.api.IPFS; import io.ipfs.api.MerkleNode; import io.ipfs.api.NamedStreamable; import io.ipfs.multihash.Multihash; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.EncoderException; import org.apache.commons.codec.net.URLCodec; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; import org.springframework.context.ApplicationContext; import org.springframework.core.env.Environment; import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; import org.springframework.ui.Model; import javax.transaction.Transactional; import java.io.File; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @Service @AllArgsConstructor @Slf4j public class AssetService { private AssetRepo assetRepo; private UserRepo userRepo; private NFTService nftService; private CollectionRepo collectionRepo; private ApplicationContext applicationContext; private OrderRepo orderRepo; private SysConfigService sysConfigService; private GiftOrderRepo giftOrderRepo; private TokenHistoryRepo tokenHistoryRepo; private AlipayProperties alipayProperties; private AlipayClient alipayClient; private WxPayProperties wxPayProperties; private WxPayService wxPayService; private Environment env; public Page all(PageQuery pageQuery) { return assetRepo.findAll(JpaUtils.toSpecification(pageQuery, Asset.class), JpaUtils.toPageRequest(pageQuery)); } public Asset createAsset(Order order) { User user = userRepo.findById(order.getUserId()).orElseThrow(new BusinessException("用户不存在")); Asset asset = Asset.builder() .userId(user.getId()) .orderId(order.getId()) .collectionId(order.getCollectionId()) .minter(order.getMinter()) .minterId(order.getMinterId()) .minterAvatar(order.getMinterAvatar()) .name(order.getName()) .detail(order.getDetail()) .pic(order.getPic()) .properties(order.getProperties()) .category(order.getCategory()) .canResale(order.isCanResale()) .royalties(order.getRoyalties()) .serviceCharge(order.getServiceCharge()) .price(order.getPrice()) .status(AssetStatus.NORMAL) .owner(user.getNickname()) .ownerId(user.getId()) .ownerAvatar(user.getAvatar()) .build(); assetRepo.save(asset); tokenHistoryRepo.save(TokenHistory.builder() .tokenId(asset.getTokenId()) .fromUser(order.getMinter()) .fromUserId(order.getMinterId()) .toUser(user.getNickname()) .toUserId(user.getId()) .operation("出售") .price(order.getPrice()) .build()); return asset; } public Asset createAsset(Order order, BlindBoxItem winItem) { User user = userRepo.findById(order.getUserId()).orElseThrow(new BusinessException("用户不存在")); Asset asset = Asset.builder() .userId(user.getId()) .orderId(order.getId()) .collectionId(order.getCollectionId()) .minter(winItem.getMinter()) .minterId(winItem.getMinterId()) .minterAvatar(winItem.getMinterAvatar()) .name(winItem.getName()) .detail(winItem.getDetail()) .pic(winItem.getPic()) .properties(winItem.getProperties()) .canResale(winItem.isCanResale()) .royalties(winItem.getRoyalties()) .serviceCharge(winItem.getServiceCharge()) .price(order.getPrice()) .status(AssetStatus.NORMAL) .ipfsUrl(ipfsUpload(winItem.getPic().get(0).getUrl())) .build(); assetRepo.save(asset); tokenHistoryRepo.save(TokenHistory.builder() .tokenId(asset.getTokenId()) .fromUser(order.getMinter()) .fromUserId(order.getMinterId()) .toUser(user.getNickname()) .toUserId(user.getId()) .operation("出售") .price(order.getPrice()) .build()); return asset; } public void mint(Asset asset) { User user = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在")); if (StringUtils.isEmpty(user.getPublicKey())) { NFTAccount account = nftService.createAccount(user.getUsername()); user.setNftAccount(account.getAccountId()); user.setKmsId(account.getAccountKmsId()); user.setPublicKey(account.getPublicKey()); userRepo.save(user); } try { NFT nft = nftService.createToken(user.getNftAccount()); if (nft != null) { asset.setTokenId(nft.getTokenId()); asset.setBlockNumber(nft.getBlockNumber()); asset.setTxHash(nft.getTxHash()); asset.setGasUsed(nft.getGasUsed()); asset.setIpfsUrl(ipfsUpload(asset.getPic().get(0).getUrl())); assetRepo.save(asset); } } catch (Exception e) { e.printStackTrace(); } applicationContext.publishEvent(new CreateAssetEvent(this, true, asset)); } public String ipfsUpload(String url) { try { IPFS ipfs = new IPFS("112.74.34.84", 5001); HttpRequest request = HttpRequest.get(url); File file = File.createTempFile("ipfs", ".tmp"); request.receive(file); NamedStreamable.FileWrapper file1 = new NamedStreamable.FileWrapper(file); MerkleNode put = ipfs.add(file1).get(0); Multihash multihash = ipfs.pin.add(put.hash).get(0); log.info("上传ipfs成功 {}", multihash.toBase58()); return multihash.toBase58(); } catch (Exception e) { } return null; } public void publicShow(Long id) { Asset asset = assetRepo.findById(id).orElseThrow(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) .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.isConsignment()) { throw new BusinessException("已寄售,请勿重新操作"); } if (asset.getStatus() != AssetStatus.NORMAL) { throw new BusinessException("当前状态不可寄售"); } if (asset.isPublicShow()) { cancelPublic(asset); } 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(true) .price(price) .properties(asset.getProperties()) .canResale(asset.isCanResale()) .royalties(asset.getRoyalties()) .serviceCharge(asset.getServiceCharge()) .assetId(id) .build(); collectionRepo.save(collection); asset.setPublicShow(true); asset.setConsignment(true); asset.setPublicCollectionId(collection.getId()); assetRepo.save(asset); } public void cancelConsignment(Long id) { Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录")); cancelConsignment(asset); } public void cancelConsignment(Asset asset) { 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("无记录")); cancelPublic(asset); } public void cancelPublic(Asset asset) { 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.setCollectionId(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); } @Transactional public GiftOrder gift(Long userId, Long assetId, Long toUserId) { Asset asset = assetRepo.findById(assetId).orElseThrow(new BusinessException("资产不存在")); if (!(asset.getStatus() == AssetStatus.NORMAL)) { throw new BusinessException("当前状态不可转赠"); } if (asset.isConsignment()) { throw new BusinessException("请先取消寄售"); } if (asset.isPublicShow()) { cancelPublic(asset); } asset.setStatus(AssetStatus.GIFTING); assetRepo.save(asset); GiftOrder giftOrder = GiftOrder.builder() .userId(userId) .assetId(assetId) .toUserId(toUserId) .gasPrice(sysConfigService.getBigDecimal("gas_fee")) .status(OrderStatus.NOT_PAID) .build(); return giftOrderRepo.save(giftOrder); } public void payOrderAlipay(Long id, Model model) { try { GiftOrder order = giftOrderRepo.findById(id).orElseThrow(new BusinessException("订单不存在")); if (order.getStatus() != OrderStatus.NOT_PAID) { throw new BusinessException("订单状态错误"); } JSONObject bizContent = new JSONObject(); bizContent.put("notifyUrl", alipayProperties.getNotifyUrl()); bizContent.put("returnUrl", alipayProperties.getReturnUrl()); bizContent.put("out_trade_no", String.valueOf(new SnowflakeIdWorker(0, 0).nextId())); bizContent.put("total_amount", order.getGasPrice().stripTrailingZeros().toPlainString()); bizContent.put("disable_pay_channels", "pcredit,creditCard"); if (Arrays.stream(env.getActiveProfiles()).noneMatch(s -> s.equals("prod"))) { // 测试环境设为1分 bizContent.put("total_amount", "0.01"); } bizContent.put("subject", "转赠GAS费"); bizContent.put("product_code", "QUICK_WAP_PAY"); JSONObject body = new JSONObject(); body.put("action", "payGiftOrder"); body.put("userId", order.getUserId()); body.put("orderId", order.getId()); bizContent.put("body", body.toJSONString()); AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest(); alipayRequest.setReturnUrl(alipayProperties.getReturnUrl()); alipayRequest.setNotifyUrl(alipayProperties.getNotifyUrl()); alipayRequest.setBizContent(JSON.toJSONString(bizContent)); String form = alipayClient.pageExecute(alipayRequest).getBody(); model.addAttribute("form", form); } catch (BusinessException err) { model.addAttribute("errMsg", err.getError()); } catch (Exception e) { model.addAttribute("errMsg", e.getMessage()); } } public Object payOrderWeixin(Long id, String tradeType, String openId) throws WxPayException, EncoderException { GiftOrder order = giftOrderRepo.findById(id).orElseThrow(new BusinessException("订单不存在")); if (order.getStatus() != OrderStatus.NOT_PAID) { throw new BusinessException("订单状态错误"); } WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest(); request.setBody("转赠GAS费"); request.setOutTradeNo(String.valueOf(new SnowflakeIdWorker(1, 1).nextId())); request.setTotalFee(order.getGasPrice().multiply(BigDecimal.valueOf(100)).intValue()); if (Arrays.stream(env.getActiveProfiles()).noneMatch(s -> s.equals("prod"))) { // 测试环境设为1分 // request.setTotalFee(1); } request.setSpbillCreateIp("180.102.110.170"); request.setNotifyUrl(wxPayProperties.getNotifyUrl()); request.setTradeType(tradeType); request.setOpenid(openId); request.setSignType("MD5"); JSONObject body = new JSONObject(); body.put("action", "payGiftOrder"); body.put("userId", order.getUserId()); body.put("orderId", order.getId()); request.setAttach(body.toJSONString()); if (WxPayConstants.TradeType.MWEB.equals(tradeType)) { WxPayMwebOrderResult result = wxPayService.createOrder(request); return result.getMwebUrl() + "&redirect_url=" + new URLCodec().encode(wxPayProperties.getReturnUrl()); } else if (WxPayConstants.TradeType.JSAPI.equals(tradeType)) { return wxPayService.createOrder(request); } throw new BusinessException("不支持此付款方式"); } @Transactional public void giftNotify(Long orderId, PayMethod payMethod, String transactionId) { GiftOrder giftOrder = giftOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在")); Asset asset = assetRepo.findById(giftOrder.getAssetId()).orElseThrow(new BusinessException("资产不存在")); User newOwner = userRepo.findById(giftOrder.getToUserId()).orElseThrow(new BusinessException("用户不存在")); giftOrder.setPayMethod(payMethod); giftOrder.setStatus(OrderStatus.FINISH); giftOrder.setTransactionId(transactionId); giftOrder.setPayTime(LocalDateTime.now()); giftOrder.setPayMethod(PayMethod.ALIPAY); transfer(asset, newOwner); tokenHistoryRepo.save(TokenHistory.builder() .fromUser(asset.getOwner()) .fromUserId(asset.getOwnerId()) .toUser(newOwner.getNickname()) .toUserId(newOwner.getId()) .operation("转赠") .build()); } public void transfer(Asset asset, User toUser) { 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.setPublicCollectionId(null); newAsset.setStatus(AssetStatus.NORMAL); assetRepo.save(newAsset); asset.setPublicShow(false); asset.setPublicCollectionId(null); asset.setStatus(AssetStatus.GIFTED); asset.setOwner(toUser.getNickname()); asset.setOwnerId(toUser.getId()); asset.setOwnerAvatar(toUser.getAvatar()); assetRepo.save(asset); } 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); } }