AssetService.java 39 KB

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