AssetService.java 20 KB

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