AssetService.java 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. package com.izouma.nineth.service;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.JSONObject;
  4. import com.alipay.api.AlipayClient;
  5. import com.alipay.api.request.AlipayTradeWapPayRequest;
  6. import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
  7. import com.github.binarywang.wxpay.bean.order.WxPayMwebOrderResult;
  8. import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
  9. import com.github.binarywang.wxpay.constant.WxPayConstants;
  10. import com.github.binarywang.wxpay.exception.WxPayException;
  11. import com.github.binarywang.wxpay.service.WxPayService;
  12. import com.github.kevinsawicki.http.HttpRequest;
  13. import com.izouma.nineth.TokenHistory;
  14. import com.izouma.nineth.config.AlipayProperties;
  15. import com.izouma.nineth.config.WxPayProperties;
  16. import com.izouma.nineth.domain.*;
  17. import com.izouma.nineth.dto.NFT;
  18. import com.izouma.nineth.dto.NFTAccount;
  19. import com.izouma.nineth.dto.PageQuery;
  20. import com.izouma.nineth.enums.*;
  21. import com.izouma.nineth.event.CreateAssetEvent;
  22. import com.izouma.nineth.exception.BusinessException;
  23. import com.izouma.nineth.lock.RedisLockable;
  24. import com.izouma.nineth.repo.*;
  25. import com.izouma.nineth.utils.JpaUtils;
  26. import com.izouma.nineth.utils.SecurityUtils;
  27. import com.izouma.nineth.utils.SnowflakeIdWorker;
  28. import io.ipfs.api.IPFS;
  29. import io.ipfs.api.MerkleNode;
  30. import io.ipfs.api.NamedStreamable;
  31. import io.ipfs.multihash.Multihash;
  32. import lombok.AllArgsConstructor;
  33. import lombok.extern.slf4j.Slf4j;
  34. import org.apache.commons.codec.EncoderException;
  35. import org.apache.commons.codec.net.URLCodec;
  36. import org.apache.commons.lang3.StringUtils;
  37. import org.springframework.beans.BeanUtils;
  38. import org.springframework.context.ApplicationContext;
  39. import org.springframework.core.env.Environment;
  40. import org.springframework.data.domain.Page;
  41. import org.springframework.stereotype.Service;
  42. import org.springframework.ui.Model;
  43. import javax.transaction.Transactional;
  44. import java.io.File;
  45. import java.math.BigDecimal;
  46. import java.time.LocalDateTime;
  47. import java.util.ArrayList;
  48. import java.util.Arrays;
  49. import java.util.List;
  50. @Service
  51. @AllArgsConstructor
  52. @Slf4j
  53. public class AssetService {
  54. private AssetRepo assetRepo;
  55. private UserRepo userRepo;
  56. private NFTService nftService;
  57. private CollectionRepo collectionRepo;
  58. private ApplicationContext applicationContext;
  59. private OrderRepo orderRepo;
  60. private SysConfigService sysConfigService;
  61. private GiftOrderRepo giftOrderRepo;
  62. private TokenHistoryRepo tokenHistoryRepo;
  63. private AlipayProperties alipayProperties;
  64. private AlipayClient alipayClient;
  65. private WxPayProperties wxPayProperties;
  66. private WxPayService wxPayService;
  67. private Environment env;
  68. public Page<Asset> all(PageQuery pageQuery) {
  69. return assetRepo.findAll(JpaUtils.toSpecification(pageQuery, Asset.class), JpaUtils.toPageRequest(pageQuery));
  70. }
  71. public Asset createAsset(Collection collection, User user, Long orderId, BigDecimal price, String type) {
  72. Asset asset = Asset.create(collection, user);
  73. asset.setOrderId(orderId);
  74. asset.setPrice(price);
  75. asset.setIpfsUrl(ipfsUpload(collection.getPic().get(0).getUrl()));
  76. assetRepo.save(asset);
  77. TokenHistory tokenHistory = tokenHistoryRepo.save(TokenHistory.builder()
  78. .tokenId(asset.getTokenId())
  79. .fromUser(collection.getMinter())
  80. .fromUserId(collection.getMinterId())
  81. .toUser(user.getNickname())
  82. .toUserId(user.getId())
  83. .operation(type)
  84. .price(price)
  85. .build());
  86. mint(asset, tokenHistory.getId());
  87. return asset;
  88. }
  89. public Asset createAsset(BlindBoxItem winItem, User user, Long orderId, BigDecimal price, String type) {
  90. Asset asset = Asset.create(winItem, user);
  91. asset.setOrderId(orderId);
  92. asset.setPrice(price);
  93. asset.setIpfsUrl(ipfsUpload(winItem.getPic().get(0).getUrl()));
  94. assetRepo.save(asset);
  95. TokenHistory tokenHistory = tokenHistoryRepo.save(TokenHistory.builder()
  96. .tokenId(asset.getTokenId())
  97. .fromUser(winItem.getMinter())
  98. .fromUserId(winItem.getMinterId())
  99. .toUser(user.getNickname())
  100. .toUserId(user.getId())
  101. .operation(type)
  102. .price(price)
  103. .build());
  104. mint(asset, tokenHistory.getId());
  105. return asset;
  106. }
  107. public void mint(Asset asset, Long historyId) {
  108. User user = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在"));
  109. if (StringUtils.isEmpty(user.getPublicKey())) {
  110. NFTAccount account = nftService.createAccount(user.getUsername());
  111. user.setNftAccount(account.getAccountId());
  112. user.setKmsId(account.getAccountKmsId());
  113. user.setPublicKey(account.getPublicKey());
  114. userRepo.save(user);
  115. }
  116. try {
  117. NFT nft = nftService.createToken(user.getNftAccount());
  118. if (nft != null) {
  119. asset.setTokenId(nft.getTokenId());
  120. asset.setBlockNumber(nft.getBlockNumber());
  121. asset.setTxHash(nft.getTxHash());
  122. asset.setGasUsed(nft.getGasUsed());
  123. asset.setIpfsUrl(ipfsUpload(asset.getPic().get(0).getUrl()));
  124. assetRepo.save(asset);
  125. tokenHistoryRepo.findById(historyId).ifPresent(tokenHistory -> {
  126. tokenHistory.setTokenId(nft.getTokenId());
  127. tokenHistoryRepo.save(tokenHistory);
  128. });
  129. }
  130. } catch (Exception e) {
  131. e.printStackTrace();
  132. }
  133. applicationContext.publishEvent(new CreateAssetEvent(this, true, asset));
  134. }
  135. public String ipfsUpload(String url) {
  136. try {
  137. IPFS ipfs = new IPFS("112.74.34.84", 5001);
  138. HttpRequest request = HttpRequest.get(url);
  139. File file = File.createTempFile("ipfs", ".tmp");
  140. request.receive(file);
  141. NamedStreamable.FileWrapper file1 = new NamedStreamable.FileWrapper(file);
  142. MerkleNode put = ipfs.add(file1).get(0);
  143. Multihash multihash = ipfs.pin.add(put.hash).get(0);
  144. log.info("上传ipfs成功 {}", multihash.toBase58());
  145. return multihash.toBase58();
  146. } catch (Exception e) {
  147. }
  148. return null;
  149. }
  150. public void publicShow(Long id) {
  151. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  152. if (asset.isPublicShow()) {
  153. return;
  154. }
  155. if (asset.getStatus() != AssetStatus.NORMAL) {
  156. throw new BusinessException("当前状态不可展示");
  157. }
  158. User owner = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在"));
  159. Collection collection = Collection.builder()
  160. .name(asset.getName())
  161. .pic(asset.getPic())
  162. .minter(asset.getMinter())
  163. .minterId(asset.getMinterId())
  164. .minterAvatar(asset.getMinterAvatar())
  165. .owner(owner.getNickname())
  166. .ownerId(owner.getId())
  167. .ownerAvatar(owner.getAvatar())
  168. .detail(asset.getDetail())
  169. .type(CollectionType.DEFAULT)
  170. .source(CollectionSource.TRANSFER)
  171. .sale(0)
  172. .stock(1)
  173. .total(1)
  174. .onShelf(true)
  175. .salable(false)
  176. .price(BigDecimal.valueOf(0))
  177. .properties(asset.getProperties())
  178. .canResale(asset.isCanResale())
  179. .royalties(asset.getRoyalties())
  180. .serviceCharge(asset.getServiceCharge())
  181. .assetId(id)
  182. .build();
  183. collectionRepo.save(collection);
  184. asset.setPublicShow(true);
  185. asset.setPublicCollectionId(collection.getId());
  186. assetRepo.save(asset);
  187. }
  188. public void consignment(Long id, BigDecimal price) {
  189. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  190. if (asset.isConsignment()) {
  191. throw new BusinessException("已寄售,请勿重新操作");
  192. }
  193. if (asset.getStatus() != AssetStatus.NORMAL) {
  194. throw new BusinessException("当前状态不可寄售");
  195. }
  196. if (asset.isPublicShow()) {
  197. cancelPublic(asset);
  198. }
  199. User owner = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在"));
  200. Collection collection = Collection.builder()
  201. .name(asset.getName())
  202. .pic(asset.getPic())
  203. .minter(asset.getMinter())
  204. .minterId(asset.getMinterId())
  205. .minterAvatar(asset.getMinterAvatar())
  206. .owner(owner.getNickname())
  207. .ownerId(owner.getId())
  208. .ownerAvatar(owner.getAvatar())
  209. .detail(asset.getDetail())
  210. .type(CollectionType.DEFAULT)
  211. .source(CollectionSource.TRANSFER)
  212. .sale(0)
  213. .stock(1)
  214. .total(1)
  215. .onShelf(true)
  216. .salable(true)
  217. .price(price)
  218. .properties(asset.getProperties())
  219. .canResale(asset.isCanResale())
  220. .royalties(asset.getRoyalties())
  221. .serviceCharge(asset.getServiceCharge())
  222. .assetId(id)
  223. .build();
  224. collectionRepo.save(collection);
  225. asset.setPublicShow(true);
  226. asset.setConsignment(true);
  227. asset.setPublicCollectionId(collection.getId());
  228. assetRepo.save(asset);
  229. }
  230. public void cancelConsignment(Long id) {
  231. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  232. cancelConsignment(asset);
  233. }
  234. public void cancelConsignment(Asset asset) {
  235. if (asset.getPublicCollectionId() != null) {
  236. List<Order> orders = orderRepo.findByCollectionId(asset.getPublicCollectionId());
  237. if (orders.stream().anyMatch(o -> o.getStatus() != OrderStatus.CANCELLED)) {
  238. throw new BusinessException("已有订单不可取消");
  239. }
  240. collectionRepo.findById(asset.getPublicCollectionId())
  241. .ifPresent(collection -> {
  242. collection.setSalable(false);
  243. collectionRepo.save(collection);
  244. });
  245. }
  246. asset.setConsignment(false);
  247. assetRepo.save(asset);
  248. }
  249. public void cancelPublic(Long id) {
  250. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  251. cancelPublic(asset);
  252. }
  253. public void cancelPublic(Asset asset) {
  254. if (!asset.isPublicShow()) {
  255. return;
  256. }
  257. if (asset.isConsignment()) {
  258. cancelConsignment(asset);
  259. }
  260. Collection collection = collectionRepo.findById(asset.getPublicCollectionId())
  261. .orElseThrow(new BusinessException("无展示记录"));
  262. collectionRepo.delete(collection);
  263. asset.setPublicShow(false);
  264. asset.setCollectionId(null);
  265. assetRepo.save(asset);
  266. }
  267. public void usePrivilege(Long assetId, Long privilegeId) {
  268. Asset asset = assetRepo.findById(assetId).orElseThrow(new BusinessException("无记录"));
  269. asset.getPrivileges().stream().filter(p -> p.getId().equals(privilegeId)).forEach(p -> {
  270. p.setOpened(true);
  271. p.setOpenTime(LocalDateTime.now());
  272. p.setOpenedBy(SecurityUtils.getAuthenticatedUser().getId());
  273. });
  274. assetRepo.save(asset);
  275. }
  276. @Transactional
  277. public GiftOrder gift(Long userId, Long assetId, Long toUserId) {
  278. Asset asset = assetRepo.findById(assetId).orElseThrow(new BusinessException("资产不存在"));
  279. if (!(asset.getStatus() == AssetStatus.NORMAL)) {
  280. throw new BusinessException("当前状态不可转赠");
  281. }
  282. if (asset.isConsignment()) {
  283. throw new BusinessException("请先取消寄售");
  284. }
  285. if (asset.isPublicShow()) {
  286. cancelPublic(asset);
  287. }
  288. asset.setStatus(AssetStatus.GIFTING);
  289. assetRepo.save(asset);
  290. GiftOrder giftOrder = GiftOrder.builder()
  291. .userId(userId)
  292. .assetId(assetId)
  293. .toUserId(toUserId)
  294. .gasPrice(sysConfigService.getBigDecimal("gas_fee"))
  295. .status(OrderStatus.NOT_PAID)
  296. .build();
  297. return giftOrderRepo.save(giftOrder);
  298. }
  299. public void payOrderAlipay(Long id, Model model) {
  300. try {
  301. GiftOrder order = giftOrderRepo.findById(id).orElseThrow(new BusinessException("订单不存在"));
  302. if (order.getStatus() != OrderStatus.NOT_PAID) {
  303. throw new BusinessException("订单状态错误");
  304. }
  305. JSONObject bizContent = new JSONObject();
  306. bizContent.put("notifyUrl", alipayProperties.getNotifyUrl());
  307. bizContent.put("returnUrl", alipayProperties.getReturnUrl());
  308. bizContent.put("out_trade_no", String.valueOf(new SnowflakeIdWorker(0, 0).nextId()));
  309. bizContent.put("total_amount", order.getGasPrice().stripTrailingZeros().toPlainString());
  310. bizContent.put("disable_pay_channels", "pcredit,creditCard");
  311. if (Arrays.stream(env.getActiveProfiles()).noneMatch(s -> s.equals("prod"))) {
  312. // 测试环境设为1分
  313. bizContent.put("total_amount", "0.01");
  314. }
  315. bizContent.put("subject", "转赠GAS费");
  316. bizContent.put("product_code", "QUICK_WAP_PAY");
  317. JSONObject body = new JSONObject();
  318. body.put("action", "payGiftOrder");
  319. body.put("userId", order.getUserId());
  320. body.put("orderId", order.getId());
  321. bizContent.put("body", body.toJSONString());
  322. AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();
  323. alipayRequest.setReturnUrl(alipayProperties.getReturnUrl());
  324. alipayRequest.setNotifyUrl(alipayProperties.getNotifyUrl());
  325. alipayRequest.setBizContent(JSON.toJSONString(bizContent));
  326. String form = alipayClient.pageExecute(alipayRequest).getBody();
  327. model.addAttribute("form", form);
  328. } catch (BusinessException err) {
  329. model.addAttribute("errMsg", err.getError());
  330. } catch (Exception e) {
  331. model.addAttribute("errMsg", e.getMessage());
  332. }
  333. }
  334. public Object payOrderWeixin(Long id, String tradeType, String openId) throws WxPayException, EncoderException {
  335. GiftOrder order = giftOrderRepo.findById(id).orElseThrow(new BusinessException("订单不存在"));
  336. if (order.getStatus() != OrderStatus.NOT_PAID) {
  337. throw new BusinessException("订单状态错误");
  338. }
  339. WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
  340. request.setBody("转赠GAS费");
  341. request.setOutTradeNo(String.valueOf(new SnowflakeIdWorker(1, 1).nextId()));
  342. request.setTotalFee(order.getGasPrice().multiply(BigDecimal.valueOf(100)).intValue());
  343. if (Arrays.stream(env.getActiveProfiles()).noneMatch(s -> s.equals("prod"))) {
  344. // 测试环境设为1分
  345. // request.setTotalFee(1);
  346. }
  347. request.setSpbillCreateIp("180.102.110.170");
  348. request.setNotifyUrl(wxPayProperties.getNotifyUrl());
  349. request.setTradeType(tradeType);
  350. request.setOpenid(openId);
  351. request.setSignType("MD5");
  352. JSONObject body = new JSONObject();
  353. body.put("action", "payGiftOrder");
  354. body.put("userId", order.getUserId());
  355. body.put("orderId", order.getId());
  356. request.setAttach(body.toJSONString());
  357. if (WxPayConstants.TradeType.MWEB.equals(tradeType)) {
  358. WxPayMwebOrderResult result = wxPayService.createOrder(request);
  359. return result.getMwebUrl() + "&redirect_url=" + new URLCodec().encode(wxPayProperties.getReturnUrl());
  360. } else if (WxPayConstants.TradeType.JSAPI.equals(tradeType)) {
  361. return wxPayService.<WxPayMpOrderResult>createOrder(request);
  362. }
  363. throw new BusinessException("不支持此付款方式");
  364. }
  365. @Transactional
  366. public void giftNotify(Long orderId, PayMethod payMethod, String transactionId) {
  367. GiftOrder giftOrder = giftOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
  368. Asset asset = assetRepo.findById(giftOrder.getAssetId()).orElseThrow(new BusinessException("资产不存在"));
  369. User newOwner = userRepo.findById(giftOrder.getToUserId()).orElseThrow(new BusinessException("用户不存在"));
  370. giftOrder.setPayMethod(payMethod);
  371. giftOrder.setStatus(OrderStatus.FINISH);
  372. giftOrder.setTransactionId(transactionId);
  373. giftOrder.setPayTime(LocalDateTime.now());
  374. giftOrder.setPayMethod(PayMethod.ALIPAY);
  375. transfer(asset, newOwner);
  376. tokenHistoryRepo.save(TokenHistory.builder()
  377. .fromUser(asset.getOwner())
  378. .fromUserId(asset.getOwnerId())
  379. .toUser(newOwner.getNickname())
  380. .toUserId(newOwner.getId())
  381. .operation("转赠")
  382. .build());
  383. }
  384. public void transfer(Asset asset, User toUser) {
  385. Asset newAsset = new Asset();
  386. BeanUtils.copyProperties(asset, newAsset);
  387. newAsset.setId(null);
  388. newAsset.setUserId(toUser.getId());
  389. newAsset.setOwner(toUser.getNickname());
  390. newAsset.setOwnerId(toUser.getId());
  391. newAsset.setOwnerAvatar(toUser.getAvatar());
  392. newAsset.setPublicShow(false);
  393. newAsset.setPublicCollectionId(null);
  394. newAsset.setStatus(AssetStatus.NORMAL);
  395. assetRepo.save(newAsset);
  396. asset.setPublicShow(false);
  397. asset.setPublicCollectionId(null);
  398. asset.setStatus(AssetStatus.GIFTED);
  399. asset.setOwner(toUser.getNickname());
  400. asset.setOwnerId(toUser.getId());
  401. asset.setOwnerAvatar(toUser.getAvatar());
  402. assetRepo.save(asset);
  403. }
  404. public List<TokenHistory> tokenHistory(String tokenId, Long assetId) {
  405. if (tokenId == null) {
  406. if (assetId == null) return new ArrayList<>();
  407. tokenId = assetRepo.findById(assetId).map(Asset::getTokenId).orElse(null);
  408. }
  409. if (tokenId == null) return new ArrayList<>();
  410. return tokenHistoryRepo.findByTokenIdOrderByCreatedAtDesc(tokenId);
  411. }
  412. @RedisLockable(key = "#id", expiration = 60, isWaiting = true)
  413. public void testLock(String id, String i) throws InterruptedException {
  414. Thread.sleep(1000);
  415. log.info("" + i);
  416. }
  417. }