AssetService.java 19 KB

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