AssetService.java 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856
  1. package com.izouma.nineth.service;
  2. import com.google.common.collect.Lists;
  3. import com.izouma.nineth.TokenHistory;
  4. import com.izouma.nineth.config.Constants;
  5. import com.izouma.nineth.config.GeneralProperties;
  6. import com.izouma.nineth.converter.LongArrayConverter;
  7. import com.izouma.nineth.domain.Collection;
  8. import com.izouma.nineth.domain.*;
  9. import com.izouma.nineth.dto.*;
  10. import com.izouma.nineth.enums.*;
  11. import com.izouma.nineth.exception.BusinessException;
  12. import com.izouma.nineth.repo.*;
  13. import com.izouma.nineth.utils.JpaUtils;
  14. import com.izouma.nineth.utils.SecurityUtils;
  15. import com.izouma.nineth.utils.TokenUtils;
  16. import lombok.AllArgsConstructor;
  17. import lombok.extern.slf4j.Slf4j;
  18. import org.apache.commons.collections.CollectionUtils;
  19. import org.apache.commons.lang3.ObjectUtils;
  20. import org.apache.commons.lang3.RandomStringUtils;
  21. import org.apache.commons.lang3.StringUtils;
  22. import org.apache.rocketmq.spring.core.RocketMQTemplate;
  23. import org.springframework.beans.BeanUtils;
  24. import org.springframework.cache.annotation.Cacheable;
  25. import org.springframework.data.domain.Page;
  26. import org.springframework.data.domain.PageImpl;
  27. import org.springframework.data.domain.Pageable;
  28. import org.springframework.data.jpa.domain.Specification;
  29. import org.springframework.scheduling.annotation.Async;
  30. import org.springframework.scheduling.annotation.Scheduled;
  31. import org.springframework.security.crypto.password.PasswordEncoder;
  32. import org.springframework.stereotype.Service;
  33. import javax.persistence.criteria.Predicate;
  34. import java.math.BigDecimal;
  35. import java.time.LocalDateTime;
  36. import java.time.temporal.ChronoUnit;
  37. import java.util.*;
  38. import java.util.concurrent.ExecutionException;
  39. import java.util.concurrent.ForkJoinPool;
  40. import java.util.concurrent.atomic.AtomicInteger;
  41. import java.util.stream.Collectors;
  42. @Service
  43. @AllArgsConstructor
  44. @Slf4j
  45. public class AssetService {
  46. private AssetRepo assetRepo;
  47. private UserRepo userRepo;
  48. private CollectionRepo collectionRepo;
  49. private OrderRepo orderRepo;
  50. private TokenHistoryRepo tokenHistoryRepo;
  51. private SysConfigService sysConfigService;
  52. private RocketMQTemplate rocketMQTemplate;
  53. private GeneralProperties generalProperties;
  54. private ShowroomRepo showroomRepo;
  55. private ShowCollectionRepo showCollectionRepo;
  56. private CollectionPrivilegeRepo collectionPrivilegeRepo;
  57. private PasswordEncoder passwordEncoder;
  58. private MintActivityRepo mintActivityRepo;
  59. private DestroyRecordRepo destroyRecordRepo;
  60. private AirDropService airDropService;
  61. private HCChainService hcChainService;
  62. private AssetSuperimpositionService assetSuperimpositionService;
  63. public Page<Asset> all(PageQuery pageQuery) {
  64. Page<Asset> all = assetRepo
  65. .findAll(JpaUtils.toSpecification(pageQuery, Asset.class), JpaUtils.toPageRequest(pageQuery));
  66. // Map<String, Object> query = pageQuery.getQuery();
  67. // if (query.containsKey("userId")) {
  68. // List<Long> orderId = orderRepo
  69. // .findAllByUserIdAndOpenedFalse(Convert.convert(Long.class, query.get("userId")));
  70. // return all.map(asset -> {
  71. // if (orderId.contains(asset.getOrderId())) {
  72. // asset.setOpened(false);
  73. // }
  74. // return asset;
  75. // });
  76. // }
  77. return all;
  78. }
  79. public List<AssetDTO> superimposition(PageQuery pageQuery) {
  80. List<AssetDTO> assetDTOs = new ArrayList<>();
  81. // 根据条件查询所有资产
  82. List<Asset> assets = assetRepo.findAll(JpaUtils.toSpecification(pageQuery, Asset.class));
  83. if (CollectionUtils.isEmpty(assets)) {
  84. return assetDTOs;
  85. }
  86. // 取出资产中未开启盲盒数据
  87. List<Asset> blindBoxClosedAssets = assets.stream().filter(asset -> !asset.isOpened() && CollectionType.BLIND_BOX.equals(asset.getType())).collect(Collectors.toList());
  88. if (CollectionUtils.isNotEmpty(blindBoxClosedAssets)) {
  89. blindBoxClosedAssets.forEach(asset -> {
  90. assetDTOs.add(AssetDTO.create(Lists.newArrayList(asset)));
  91. });
  92. // 移除资产中未开启盲盒数据
  93. assets.removeAll(blindBoxClosedAssets);
  94. }
  95. // 取出资产中所有未设置prefixName的值
  96. List<Asset> prefixNameIsNullAssets = assets.stream().filter(asset -> StringUtils.isBlank(asset.getPrefixName())).collect(Collectors.toList());
  97. if (CollectionUtils.isNotEmpty(prefixNameIsNullAssets)) {
  98. prefixNameIsNullAssets.forEach(asset -> {
  99. assetDTOs.add(AssetDTO.create(Lists.newArrayList(asset)));
  100. });
  101. assets.removeAll(prefixNameIsNullAssets);
  102. }
  103. if (CollectionUtils.isNotEmpty(assets)) {
  104. // 取出资产中所有prefixName
  105. List<String> prefixNames = assets.stream().map(Asset::getPrefixName).distinct().collect(Collectors.toList());
  106. // 将资产中相同prefixName归类(除未开启盲盒和未设置prefixName)
  107. prefixNames.forEach(str -> {
  108. List<Asset> collect = assets.stream().filter(asset -> str.equals(asset.getPrefixName())).collect(Collectors.toList());
  109. assetDTOs.add(AssetDTO.create(collect));
  110. });
  111. }
  112. return assetDTOs;
  113. }
  114. public Asset createAsset(Collection collection, User user, Long orderId, BigDecimal price, String type,
  115. Integer number, boolean safeFlag) {
  116. Asset asset = Asset.create(collection, user);
  117. asset.setTokenId(TokenUtils.genTokenId());
  118. asset.setNumber(number);
  119. asset.setOasisId(collection.getOasisId());
  120. asset.setOrderId(orderId);
  121. asset.setPrice(price);
  122. asset.setTags(new HashSet<>());
  123. if (collection.getTags() != null) {
  124. asset.getTags().addAll(collection.getTags());
  125. }
  126. User fakeUser = null;
  127. if (safeFlag) {
  128. fakeUser = createFakeUser();
  129. asset.setOwner(fakeUser.getNickname());
  130. asset.setOwnerId(fakeUser.getId());
  131. asset.setOwnerAvatar(fakeUser.getAvatar());
  132. }
  133. assetRepo.saveAndFlush(asset);
  134. assetSuperimpositionService.adjustNum(asset, AssetOperationType.BUY, null);
  135. tokenHistoryRepo.save(TokenHistory.builder()
  136. .tokenId(asset.getTokenId())
  137. .fromUser(collection.getMinter())
  138. .fromUserId(collection.getMinterId())
  139. .fromAvatar(collection.getMinterAvatar())
  140. .toUser((safeFlag ? fakeUser : user).getNickname())
  141. .toUserId((safeFlag ? fakeUser : user).getId())
  142. .toAvatar((safeFlag ? fakeUser : user).getAvatar())
  143. .operation(type)
  144. .price(price)
  145. .build());
  146. rocketMQTemplate.syncSend(generalProperties.getMintTopic(), asset.getId());
  147. if (asset.getOasisId() != null & asset.getSource().equals(AssetSource.OFFICIAL)) {
  148. AirDrop airDrop = new AirDrop();
  149. airDrop.setName("建筑空投展厅");
  150. airDrop.setCollectionId(207012L);
  151. List<Long> userIds = new ArrayList<>();
  152. userIds.add(user.getId());
  153. List<Long> nums = new ArrayList<>();
  154. nums.add(1L);
  155. airDrop.setType(AirDropType.asset);
  156. List<DropTarget> dropTargets = new ArrayList<>();
  157. DropTarget dropTarget = new DropTarget();
  158. dropTarget.setNickname(user.getNickname());
  159. dropTarget.setNum(1);
  160. dropTarget.setPhone(user.getPhone());
  161. dropTarget.setUserId(user.getId());
  162. dropTargets.add(dropTarget);
  163. airDrop.setTargets(dropTargets);
  164. airDrop.setUserIds(userIds);
  165. airDrop.setNum(nums);
  166. airDropService.create(airDrop);
  167. }
  168. return asset;
  169. }
  170. public Asset createAsset(BlindBoxItem winItem, User user, Long orderId, BigDecimal price, String type,
  171. Integer number, Integer holdDays, boolean safeFlag) {
  172. Collection blindBox = collectionRepo.findDetailById(winItem.getBlindBoxId())
  173. .orElseThrow(new BusinessException("盲盒不存在"));
  174. Collection collection = collectionRepo.findDetailById(winItem.getCollectionId())
  175. .orElseThrow(new BusinessException("藏品不存在"));
  176. Asset asset = Asset.create(winItem, user, holdDays);
  177. asset.setTokenId(TokenUtils.genTokenId());
  178. asset.setNumber(number);
  179. asset.setOasisId(winItem.getOasisId());
  180. asset.setOrderId(orderId);
  181. asset.setPrice(price);
  182. asset.setPrefixName(collection.getPrefixName());
  183. asset.setTags(new HashSet<>());
  184. if (blindBox.getTags() != null) {
  185. asset.getTags().addAll(blindBox.getTags());
  186. }
  187. if (collection.getTags() != null) {
  188. asset.getTags().addAll(collection.getTags());
  189. }
  190. User fakeUser = null;
  191. if (safeFlag) {
  192. fakeUser = createFakeUser();
  193. asset.setOwner(fakeUser.getNickname());
  194. asset.setOwnerId(fakeUser.getId());
  195. asset.setOwnerAvatar(fakeUser.getAvatar());
  196. }
  197. assetRepo.saveAndFlush(asset);
  198. assetSuperimpositionService.adjustNum(asset, AssetOperationType.BUY, null);
  199. tokenHistoryRepo.save(TokenHistory.builder()
  200. .tokenId(asset.getTokenId())
  201. .fromUser(winItem.getMinter())
  202. .fromUserId(winItem.getMinterId())
  203. .fromAvatar(winItem.getMinterAvatar())
  204. .toUser((safeFlag ? fakeUser : user).getNickname())
  205. .toUserId((safeFlag ? fakeUser : user).getId())
  206. .toAvatar((safeFlag ? fakeUser : user).getAvatar())
  207. .operation(type)
  208. .price(price)
  209. .build());
  210. rocketMQTemplate.syncSend(generalProperties.getMintTopic(), asset.getId());
  211. return asset;
  212. }
  213. public void publicShow(Long id) {
  214. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  215. if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  216. throw new BusinessException("此藏品不属于你");
  217. }
  218. if (asset.isPublicShow()) {
  219. return;
  220. }
  221. if (asset.getStatus() != AssetStatus.NORMAL) {
  222. throw new BusinessException("当前状态不可展示");
  223. }
  224. User owner = asset.isSafeFlag() ?
  225. userRepo.findById(asset.getOwnerId()).orElseThrow(new BusinessException("用户不存在"))
  226. : userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在"));
  227. Collection collection = Collection.builder()
  228. .name(asset.getName())
  229. .pic(asset.getPic())
  230. .minter(asset.getMinter())
  231. .minterId(asset.getMinterId())
  232. .minterAvatar(asset.getMinterAvatar())
  233. .owner(owner.getNickname())
  234. .oasisId(asset.getOasisId())
  235. .ownerId(owner.getId())
  236. .ownerAvatar(owner.getAvatar())
  237. .detail(asset.getDetail())
  238. .type(CollectionType.DEFAULT)
  239. .source(CollectionSource.TRANSFER)
  240. .sale(0)
  241. .stock(1)
  242. .total(1)
  243. .onShelf(true)
  244. .salable(false)
  245. .price(BigDecimal.valueOf(0))
  246. .properties(asset.getProperties())
  247. .canResale(asset.isCanResale())
  248. .royalties(asset.getRoyalties())
  249. .serviceCharge(asset.getServiceCharge())
  250. .assetId(id)
  251. .number(asset.getNumber())
  252. .tags(new HashSet<>())
  253. .build();
  254. if (asset.getTags() != null) {
  255. collection.getTags().addAll(asset.getTags());
  256. }
  257. collectionRepo.save(collection);
  258. asset.setPublicShow(true);
  259. asset.setPublicCollectionId(collection.getId());
  260. assetRepo.save(asset);
  261. assetSuperimpositionService.adjustNum(asset, AssetOperationType.OPEN, null);
  262. }
  263. public synchronized void consignment(Long id, BigDecimal price, String tradeCode) {
  264. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  265. boolean publicShow = asset.isPublicShow();
  266. if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  267. throw new BusinessException("此藏品不属于你");
  268. }
  269. int holdDays;
  270. if (asset.getSource() == AssetSource.GIFT) {
  271. holdDays = sysConfigService.getInt("gift_days");
  272. } else {
  273. if (ObjectUtils.isEmpty(asset.getHoldDays())) {
  274. holdDays = sysConfigService.getInt("hold_days");
  275. } else {
  276. holdDays = asset.getHoldDays();
  277. }
  278. }
  279. if (holdDays == 0 && AssetSource.OFFICIAL.equals(asset.getSource())) {
  280. BigDecimal officialConsignment = sysConfigService.getBigDecimal("OFFICIAL_CONSIGNMENT");
  281. //天转小时
  282. int hour = officialConsignment.multiply(new BigDecimal("24")).intValue();
  283. if (ChronoUnit.HOURS.between(asset.getCreatedAt(), LocalDateTime.now()) < hour) {
  284. throw new BusinessException("需持有满" + hour + "小时后才能寄售上架");
  285. }
  286. }
  287. if (ChronoUnit.DAYS.between(asset.getCreatedAt(), LocalDateTime.now()) < holdDays) {
  288. throw new BusinessException("需持有满" + holdDays + "天才能寄售上架");
  289. }
  290. User owner = asset.isSafeFlag() ?
  291. userRepo.findById(asset.getOwnerId()).orElseThrow(new BusinessException("用户不存在"))
  292. : userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在"));
  293. if (!passwordEncoder.matches(tradeCode, userRepo.findTradeCode(asset.getUserId()))) {
  294. throw new BusinessException("交易密码错误");
  295. }
  296. // if (StringUtils.isBlank(owner.getSettleAccountId())) {
  297. // throw new BusinessException("请先绑定银行卡");
  298. // }
  299. if (asset.isConsignment()) {
  300. throw new BusinessException("已寄售,请勿重新操作");
  301. }
  302. if (asset.getStatus() != AssetStatus.NORMAL) {
  303. throw new BusinessException("当前状态不可寄售");
  304. }
  305. if (asset.isPublicShow()) {
  306. cancelPublic(asset);
  307. }
  308. //寄售中的展厅需要先删除展厅
  309. if (CollectionType.SHOWROOM.equals(asset.getType())) {
  310. if (showroomRepo.findByAssetId(id).isPresent()) {
  311. throw new BusinessException("请先删除展厅");
  312. }
  313. }
  314. Collection collection = Collection.builder()
  315. .name(asset.getName())
  316. .pic(asset.getPic())
  317. .minter(asset.getMinter())
  318. .minterId(asset.getMinterId())
  319. .minterAvatar(asset.getMinterAvatar())
  320. .owner(owner.getNickname())
  321. .ownerId(owner.getId())
  322. .oasisId(asset.getOasisId())
  323. .ownerAvatar(owner.getAvatar())
  324. .detail(asset.getDetail())
  325. .type(CollectionType.DEFAULT)
  326. .source(CollectionSource.TRANSFER)
  327. .sale(0)
  328. .stock(1)
  329. .total(1)
  330. .onShelf(true)
  331. .salable(true)
  332. .price(price)
  333. .properties(asset.getProperties())
  334. .canResale(asset.isCanResale())
  335. .royalties(asset.getRoyalties())
  336. .serviceCharge(asset.getServiceCharge())
  337. .assetId(id)
  338. .number(asset.getNumber())
  339. .tags(new HashSet<>())
  340. .build();
  341. if (asset.getTags() != null) {
  342. collection.getTags().addAll(asset.getTags());
  343. }
  344. collectionRepo.save(collection);
  345. asset.setPublicShow(true);
  346. asset.setConsignment(true);
  347. asset.setPublicCollectionId(collection.getId());
  348. asset.setSellPrice(price);
  349. assetRepo.save(asset);
  350. asset.setPublicShow(publicShow);
  351. assetSuperimpositionService.adjustNum(asset, AssetOperationType.CONSIGNMENT, null);
  352. }
  353. public void cancelConsignment(Long id) {
  354. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  355. if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  356. throw new BusinessException("此藏品不属于你");
  357. }
  358. cancelConsignment(asset);
  359. assetSuperimpositionService.adjustNum(asset, AssetOperationType.CANCEL_CONSIGNMENT, null);
  360. }
  361. public void cancelConsignment(Asset asset) {
  362. if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  363. throw new BusinessException("此藏品不属于你");
  364. }
  365. if (asset.getPublicCollectionId() != null) {
  366. List<Order> orders = orderRepo.findByCollectionId(asset.getPublicCollectionId());
  367. if (orders.stream().anyMatch(o -> o.getStatus() != OrderStatus.CANCELLED)) {
  368. throw new BusinessException("已有订单不可取消");
  369. }
  370. collectionRepo.findById(asset.getPublicCollectionId())
  371. .ifPresent(collection -> {
  372. collection.setSalable(false);
  373. collectionRepo.save(collection);
  374. });
  375. }
  376. asset.setConsignment(false);
  377. assetRepo.save(asset);
  378. }
  379. public void cancelPublic(Long id) {
  380. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  381. if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  382. throw new BusinessException("此藏品不属于你");
  383. }
  384. cancelPublic(asset);
  385. assetSuperimpositionService.adjustNum(asset, AssetOperationType.CLOSE, null);
  386. }
  387. public void cancelPublic(Asset asset) {
  388. if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  389. throw new BusinessException("此藏品不属于你");
  390. }
  391. if (!asset.isPublicShow()) {
  392. return;
  393. }
  394. if (asset.isConsignment()) {
  395. cancelConsignment(asset);
  396. }
  397. Collection collection = collectionRepo.findById(asset.getPublicCollectionId())
  398. .orElseThrow(new BusinessException("无展示记录"));
  399. collectionRepo.delete(collection);
  400. // 如果展厅有此藏品
  401. showCollectionRepo.deleteAllByCollectionId(asset.getPublicCollectionId());
  402. asset.setPublicShow(false);
  403. asset.setPublicCollectionId(null);
  404. assetRepo.save(asset);
  405. }
  406. public void usePrivilege(Long assetId, Long privilegeId) {
  407. Asset asset = assetRepo.findById(assetId).orElseThrow(new BusinessException("无记录"));
  408. asset.getPrivileges().stream().filter(p -> p.getId().equals(privilegeId)).forEach(p -> {
  409. if (!p.getName().equals("铸造")) {
  410. p.setOpened(true);
  411. p.setOpenTime(LocalDateTime.now());
  412. p.setOpenedBy(SecurityUtils.getAuthenticatedUser().getId());
  413. }
  414. });
  415. assetRepo.save(asset);
  416. }
  417. public void transfer(Asset asset, BigDecimal price, User toUser, TransferReason reason, Long orderId) {
  418. transfer(asset, price, toUser, reason, orderId, false);
  419. }
  420. private User createFakeUser() {
  421. String name = "0x" + RandomStringUtils.randomAlphabetic(8);
  422. return userRepo.save(User.builder()
  423. .username(name)
  424. .nickname(name)
  425. .avatar(Constants.DEFAULT_AVATAR)
  426. .build());
  427. }
  428. public void transfer(Asset asset, BigDecimal price, User toUser, TransferReason reason, Long orderId, boolean safeFlag) {
  429. Objects.requireNonNull(asset, "原藏品不能为空");
  430. Objects.requireNonNull(toUser, "转让人不能为空");
  431. Objects.requireNonNull(reason, "转让原因不能为空");
  432. User newOwner = toUser;
  433. if (safeFlag) {
  434. newOwner = createFakeUser();
  435. }
  436. Asset newAsset = new Asset();
  437. BeanUtils.copyProperties(asset, newAsset);
  438. newAsset.setId(null);
  439. newAsset.setUserId(toUser.getId());
  440. newAsset.setOwner(newOwner.getNickname());
  441. newAsset.setOwnerId(newOwner.getId());
  442. newAsset.setOwnerAvatar(newOwner.getAvatar());
  443. newAsset.setPublicShow(false);
  444. newAsset.setConsignment(false);
  445. newAsset.setPublicCollectionId(null);
  446. newAsset.setStatus(AssetStatus.NORMAL);
  447. newAsset.setPrice(price);
  448. newAsset.setSellPrice(null);
  449. newAsset.setOrderId(orderId);
  450. newAsset.setOasisId(asset.getOasisId());
  451. newAsset.setFromAssetId(asset.getId());
  452. newAsset.setType(CollectionType.DEFAULT);
  453. newAsset.setSource(TransferReason.GIFT == reason ? AssetSource.GIFT : AssetSource.TRANSFER);
  454. newAsset.setTags(new HashSet<>(asset.getTags()));
  455. newAsset.setSafeFlag(safeFlag);
  456. newAsset.setHoldDays(asset.getOldHoldDays());
  457. assetRepo.save(newAsset);
  458. TokenHistory tokenHistory = TokenHistory.builder()
  459. .tokenId(asset.getTokenId())
  460. .fromUser(asset.getOwner())
  461. .fromUserId(asset.getOwnerId())
  462. .fromAvatar(asset.getOwnerAvatar())
  463. .toUser(newOwner.getNickname())
  464. .toUserId(newOwner.getId())
  465. .toAvatar(newOwner.getAvatar())
  466. .operation(reason.getDescription())
  467. .price(TransferReason.GIFT == reason ? null : price)
  468. .build();
  469. tokenHistoryRepo.save(tokenHistory);
  470. asset.setPublicShow(false);
  471. asset.setConsignment(false);
  472. asset.setPublicCollectionId(null);
  473. switch (reason) {
  474. case GIFT:
  475. asset.setStatus(AssetStatus.GIFTED);
  476. assetSuperimpositionService.adjustNum(asset, AssetOperationType.GIFTING, toUser.getId());
  477. break;
  478. case AUCTION:
  479. asset.setStatus(AssetStatus.AUCTIONED);
  480. assetSuperimpositionService.adjustNum(asset, AssetOperationType.AUCTIONING_SUCCESS, toUser.getId());
  481. break;
  482. case TRANSFER:
  483. asset.setStatus(AssetStatus.TRANSFERRED);
  484. assetSuperimpositionService.adjustNum(asset, AssetOperationType.CONSIGNMENT_SUCCESS, toUser.getId());
  485. }
  486. asset.setOwner(newOwner.getNickname());
  487. asset.setOwnerId(newOwner.getId());
  488. asset.setOwnerAvatar(newOwner.getAvatar());
  489. assetRepo.save(asset);
  490. //vip权限转让
  491. CollectionPrivilege collectionPrivilege = collectionPrivilegeRepo.findByCollectionId(asset.getCollectionId());
  492. if (ObjectUtils.isNotEmpty(collectionPrivilege)) {
  493. if (collectionPrivilege.isVip()) {
  494. //更新vip信息
  495. userRepo.updateVipPurchase(toUser.getId(), 1);
  496. userRepo.updateVipPurchase(asset.getUserId(), 0);
  497. }
  498. }
  499. }
  500. public List<TokenHistory> tokenHistory(String tokenId, Long assetId) {
  501. if (tokenId == null) {
  502. if (assetId == null) return new ArrayList<>();
  503. tokenId = assetRepo.findById(assetId).map(Asset::getTokenId).orElse(null);
  504. }
  505. if (tokenId == null) return new ArrayList<>();
  506. return tokenHistoryRepo.findByTokenIdOrderByCreatedAtDesc(tokenId);
  507. }
  508. public void setHistory() {
  509. List<Asset> assets = assetRepo.findByCreatedAtBefore(LocalDateTime.of(2021, 11, 22, 23, 59, 59));
  510. assets.parallelStream().forEach(asset -> {
  511. try {
  512. User owner = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException(""));
  513. Order order = orderRepo.findById(asset.getOrderId()).orElseThrow(new BusinessException(""));
  514. TokenHistory t = TokenHistory.builder()
  515. .tokenId(asset.getTokenId())
  516. .fromUser(asset.getMinter())
  517. .fromUserId(asset.getMinterId())
  518. .fromAvatar(asset.getMinterAvatar())
  519. .toUser(owner.getNickname())
  520. .toUserId(owner.getId())
  521. .toAvatar(owner.getAvatar())
  522. .operation("出售")
  523. .price(order.getPrice())
  524. .build();
  525. t.setCreatedAt(asset.getCreatedAt());
  526. tokenHistoryRepo.save(t);
  527. } catch (Exception e) {
  528. }
  529. });
  530. }
  531. public Page<UserHistory> userHistory(Long userId, Long toUserId, Long fromUserId, Pageable pageable) {
  532. Page<TokenHistory> page;
  533. if (ObjectUtils.isNotEmpty(toUserId)) {
  534. page = tokenHistoryRepo.userHistoryTo(userId, toUserId, pageable);
  535. } else if (ObjectUtils.isNotEmpty(fromUserId)) {
  536. page = tokenHistoryRepo.userHistoryFrom(userId, fromUserId, pageable);
  537. } else {
  538. page = tokenHistoryRepo.userHistory(userId, pageable);
  539. }
  540. Set<String> tokenIds = page.stream().map(TokenHistory::getTokenId).collect(Collectors.toSet());
  541. List<Asset> assets = tokenIds.isEmpty() ? new ArrayList<>() : assetRepo.findByTokenIdIn(tokenIds);
  542. return page.map(tokenHistory -> {
  543. UserHistory userHistory = new UserHistory();
  544. BeanUtils.copyProperties(tokenHistory, userHistory);
  545. Optional<Asset> asset = assets.stream().filter(a -> a.getTokenId().equals(tokenHistory.getTokenId()))
  546. .findAny();
  547. userHistory.setAssetName(asset.map(Asset::getName).orElse(null));
  548. userHistory.setPic(asset.map(Asset::getPic).orElse(new ArrayList<>()));
  549. switch (tokenHistory.getOperation()) {
  550. case "出售":
  551. case "转让":
  552. userHistory.setDescription(tokenHistory.getToUserId().equals(userId) ? "作品交易——买入" : "作品交易——售出");
  553. break;
  554. case "转赠":
  555. userHistory.setDescription(tokenHistory.getToUserId().equals(userId) ? "他人赠送" : "作品赠送");
  556. break;
  557. default:
  558. userHistory.setDescription(tokenHistory.getOperation());
  559. }
  560. return userHistory;
  561. });
  562. }
  563. public Page<UserHistory> userHistory(Long userId, PageQuery pageQuery) {
  564. Page<TokenHistory> page = tokenHistoryRepo.findAll(((root, criteriaQuery, criteriaBuilder) -> {
  565. List<Predicate> and = JpaUtils
  566. .toPredicates(pageQuery, TokenHistory.class, root, criteriaQuery, criteriaBuilder);
  567. Map<String, Object> query = pageQuery.getQuery();
  568. if (ObjectUtils.isEmpty(query.get("toUserId")) && ObjectUtils.isEmpty(query.get("fromUserId"))) {
  569. and.add(criteriaBuilder.or(criteriaBuilder.equal(root.get("toUserId"), userId), criteriaBuilder
  570. .equal(root.get("fromUserId"), userId)));
  571. } else {
  572. if (ObjectUtils.isNotEmpty(query.get("toUserId"))) {
  573. and.add(criteriaBuilder.and(criteriaBuilder.equal(root.get("toUserId"), userId)));
  574. } else {
  575. and.add(criteriaBuilder.and(criteriaBuilder.equal(root.get("fromUserId"), userId)));
  576. }
  577. }
  578. return criteriaBuilder.and(and.toArray(new Predicate[0]));
  579. }), JpaUtils.toPageRequest(pageQuery));
  580. Set<String> tokenIds = page.stream().map(TokenHistory::getTokenId).collect(Collectors.toSet());
  581. List<Asset> assets = tokenIds.isEmpty() ? new ArrayList<>() : assetRepo.findByTokenIdIn(tokenIds);
  582. return page.map(tokenHistory -> {
  583. UserHistory userHistory = new UserHistory();
  584. BeanUtils.copyProperties(tokenHistory, userHistory);
  585. Optional<Asset> asset = assets.stream().filter(a -> a.getTokenId().equals(tokenHistory.getTokenId()))
  586. .findAny();
  587. userHistory.setAssetName(asset.map(Asset::getName).orElse(null));
  588. userHistory.setPic(asset.map(Asset::getPic).orElse(new ArrayList<>()));
  589. switch (tokenHistory.getOperation()) {
  590. case "出售":
  591. case "转让":
  592. userHistory.setDescription(tokenHistory.getToUserId().equals(userId) ? "作品交易——买入" : "作品交易——售出");
  593. break;
  594. case "转赠":
  595. userHistory.setDescription(tokenHistory.getToUserId().equals(userId) ? "他人赠送" : "作品赠送");
  596. break;
  597. default:
  598. userHistory.setDescription(tokenHistory.getOperation());
  599. }
  600. return userHistory;
  601. });
  602. }
  603. public String mint(LocalDateTime time) {
  604. if (time == null) {
  605. time = LocalDateTime.now();
  606. }
  607. for (Asset asset : assetRepo.toMint(time)) {
  608. rocketMQTemplate.syncSend(generalProperties.getMintTopic(), asset.getId());
  609. }
  610. return "ok";
  611. }
  612. @Cacheable(value = "userStat", key = "#userId")
  613. public Map<String, BigDecimal> breakdown(Long userId) {
  614. List<TokenHistory> page = tokenHistoryRepo.userHistory(userId);
  615. BigDecimal sale = page.stream()
  616. .filter(th -> th.getFromUserId().equals(userId) && ObjectUtils.isNotEmpty(th.getPrice()))
  617. .map(TokenHistory::getPrice)
  618. .reduce(BigDecimal.ZERO, BigDecimal::add);
  619. BigDecimal buy = page.stream()
  620. .filter(th -> th.getToUserId().equals(userId) && ObjectUtils.isNotEmpty(th.getPrice()))
  621. .map(TokenHistory::getPrice)
  622. .reduce(BigDecimal.ZERO, BigDecimal::add);
  623. Map<String, BigDecimal> map = new HashMap<>();
  624. map.put("sale", sale);
  625. map.put("buy", buy);
  626. return map;
  627. }
  628. public void transferCDN() throws ExecutionException, InterruptedException {
  629. ForkJoinPool customThreadPool = new ForkJoinPool(100);
  630. customThreadPool.submit(() -> {
  631. collectionRepo.selectResource().parallelStream().forEach(list -> {
  632. for (int i = 0; i < list.size(); i++) {
  633. list.set(i, replaceCDN(list.get(i)));
  634. }
  635. collectionRepo.updateCDN(Long.parseLong(list.get(0)),
  636. list.get(1),
  637. list.get(2),
  638. list.get(3),
  639. list.get(4),
  640. list.get(5));
  641. });
  642. assetRepo.selectResource().parallelStream().forEach(list -> {
  643. for (int i = 0; i < list.size(); i++) {
  644. list.set(i, replaceCDN(list.get(i)));
  645. }
  646. assetRepo.updateCDN(Long.parseLong(list.get(0)),
  647. list.get(1),
  648. list.get(2),
  649. list.get(3),
  650. list.get(4),
  651. list.get(5));
  652. });
  653. }).get();
  654. }
  655. public String replaceCDN(String url) {
  656. if (url == null) return null;
  657. return url.replaceAll("https://raex-meta\\.oss-cn-shenzhen\\.aliyuncs\\.com",
  658. "https://cdn.raex.vip");
  659. }
  660. // @Scheduled(cron = "0 0 0/1 * * ?")
  661. // public void offTheShelf() {
  662. // LocalDateTime lastTime = LocalDateTime.now().minusHours(120);
  663. // Set<Long> assetIds = collectionRepo
  664. // .findResaleCollectionPriceOver20K(BigDecimal
  665. // .valueOf(20000L), CollectionSource.TRANSFER, lastTime, true);
  666. // assetIds.forEach(this::cancelConsignmentBySystem);
  667. // }
  668. @Scheduled(cron = "0 0 0/1 * * ?")
  669. public void offTheShelfAll() {
  670. LocalDateTime lastTime = LocalDateTime.now().minusHours(240);
  671. Set<Long> assetIds = collectionRepo
  672. .findResaleCollectionPriceOver20K(BigDecimal
  673. .valueOf(0L), CollectionSource.TRANSFER, lastTime, true);
  674. assetIds.forEach(this::cancelConsignmentBySystem);
  675. }
  676. public void cancelConsignmentBySystem(Long id) {
  677. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  678. if (asset.getPublicCollectionId() != null) {
  679. List<Order> orders = orderRepo.findByCollectionId(asset.getPublicCollectionId());
  680. if (orders.stream().anyMatch(o -> o.getStatus() != OrderStatus.CANCELLED)) {
  681. throw new BusinessException("已有订单不可取消");
  682. }
  683. collectionRepo.findById(asset.getPublicCollectionId())
  684. .ifPresent(collection -> {
  685. collection.setSalable(false);
  686. collectionRepo.save(collection);
  687. });
  688. }
  689. asset.setConsignment(false);
  690. assetRepo.save(asset);
  691. }
  692. // @Cacheable(cacheNames = "fmaa", key = "#userId+'#'+#mintActivityId+'#'+#pageable.hashCode()")
  693. public PageWrapper<Asset> findMintActivityAssetsWrap(Long userId, Long mintActivityId, Pageable pageable) {
  694. return PageWrapper.of(findMintActivityAssets(userId, mintActivityId, pageable));
  695. }
  696. public Page<Asset> findMintActivityAssets(Long userId, Long mintActivityId, Pageable pageable) {
  697. MintActivity mintActivity = mintActivityRepo.findById(mintActivityId).orElse(null);
  698. if (mintActivity == null) return new PageImpl<>(Collections.emptyList());
  699. if (!mintActivity.isAudit()) {
  700. Set<Tag> tags = mintActivity.getRule().getTags();
  701. if (tags.isEmpty()) return new PageImpl<>(Collections.emptyList());
  702. return assetRepo.findAll((Specification<Asset>) (root, query, criteriaBuilder) ->
  703. query.distinct(true).where(criteriaBuilder.equal(root.get("userId"), userId),
  704. criteriaBuilder.equal(root.get("status"), AssetStatus.NORMAL),
  705. root.join("tags").get("id").in(tags.stream().map(Tag::getId).toArray()))
  706. .getRestriction(), pageable);
  707. } else {
  708. return assetRepo.findByUserIdAndStatusAndNameLike(userId, AssetStatus.NORMAL,
  709. "%" + mintActivity.getCollectionName() + "%", pageable);
  710. }
  711. }
  712. public void destroy(Long id, Long userId) {
  713. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  714. if (!asset.getUserId().equals(userId)) {
  715. throw new BusinessException("此藏品不属于你");
  716. }
  717. if (asset.getStatus() != AssetStatus.NORMAL) {
  718. throw new BusinessException("当前状态不可销毁");
  719. }
  720. if (asset.isPublicShow()) {
  721. throw new BusinessException("请先取消公开展示");
  722. // cancelPublic(asset);
  723. }
  724. User toUser = userRepo.findById(1435297L).orElseThrow(new BusinessException("无记录"));
  725. TokenHistory tokenHistory = TokenHistory.builder()
  726. .tokenId(asset.getTokenId())
  727. .fromUser(asset.getOwner())
  728. .fromUserId(asset.getOwnerId())
  729. .fromAvatar(asset.getOwnerAvatar())
  730. .toUser(toUser.getNickname())
  731. .toUserId(toUser.getId())
  732. .toAvatar(toUser.getAvatar())
  733. .operation(TransferReason.DESTROY.getDescription())
  734. .price(null)
  735. .build();
  736. tokenHistoryRepo.save(tokenHistory);
  737. asset.setPublicShow(false);
  738. asset.setConsignment(false);
  739. asset.setPublicCollectionId(null);
  740. asset.setStatus(AssetStatus.DESTROYED);
  741. asset.setOwner(toUser.getNickname());
  742. asset.setOwnerId(toUser.getId());
  743. asset.setOwnerAvatar(toUser.getAvatar());
  744. assetRepo.save(asset);
  745. assetSuperimpositionService.adjustNum(asset, AssetOperationType.DESTORY, null);
  746. //积分记录
  747. destroyRecordRepo.save(DestroyRecord.builder()
  748. .userId(userId)
  749. .assetId(asset.getId())
  750. .name(asset.getName())
  751. .pic(asset.getPic().get(0).getUrl())
  752. .record(1)
  753. .type(RecordType.OBTAIN)
  754. .build());
  755. //加积分
  756. userRepo.addDestroyPoint(userId, 1);
  757. }
  758. public int getRoyalties(Long minterId, int royalties, Long userId) {
  759. if (royalties == 3) {
  760. return 3;
  761. }
  762. LongArrayConverter converter = new LongArrayConverter();
  763. String discountMinter = sysConfigService.getString("discount_minter");
  764. List<Long> minterIds = converter.convertToEntityAttribute(discountMinter);
  765. if (minterIds.contains(minterId)) {
  766. String discountCollection = sysConfigService.getString("discount_collection");
  767. List<Long> collectionIds = converter.convertToEntityAttribute(discountCollection);
  768. Long assetId = assetRepo.findDiscount(userId, collectionIds);
  769. if (ObjectUtils.isNotEmpty(assetId)) {
  770. return 3;
  771. }
  772. }
  773. return royalties;
  774. }
  775. @Async
  776. public void hcChain() throws ExecutionException, InterruptedException {
  777. new ForkJoinPool(1000).submit(() -> {
  778. AtomicInteger num = new AtomicInteger();
  779. assetRepo.findByStatus(AssetStatus.NORMAL).parallelStream()
  780. .forEach(asset -> {
  781. if (asset.getHcTxHash() == null) {
  782. User user = userRepo.findById(asset.getUserId()).orElse(null);
  783. if (user != null) {
  784. if (user.getHcChainAddress() == null) {
  785. user.setHcChainAddress(hcChainService.createAccount(asset.getUserId()));
  786. }
  787. NFT nft = hcChainService.mint(user.getHcChainAddress(), asset.getTokenId());
  788. asset.setHcTokenId(nft.getTokenId());
  789. asset.setHcTxHash(nft.getTxHash());
  790. asset.setGasUsed(nft.getGasUsed());
  791. assetRepo.save(asset);
  792. }
  793. }
  794. log.info("hcChain:" + num.getAndIncrement());
  795. });
  796. }).get();
  797. }
  798. }