AssetService.java 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. package com.izouma.nineth.service;
  2. import cn.hutool.core.convert.Convert;
  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.domain.Collection;
  7. import com.izouma.nineth.domain.*;
  8. import com.izouma.nineth.dto.PageQuery;
  9. import com.izouma.nineth.dto.PageWrapper;
  10. import com.izouma.nineth.dto.UserHistory;
  11. import com.izouma.nineth.enums.*;
  12. import com.izouma.nineth.exception.BusinessException;
  13. import com.izouma.nineth.repo.*;
  14. import com.izouma.nineth.utils.JpaUtils;
  15. import com.izouma.nineth.utils.SecurityUtils;
  16. import com.izouma.nineth.utils.TokenUtils;
  17. import lombok.AllArgsConstructor;
  18. import lombok.extern.slf4j.Slf4j;
  19. import org.apache.commons.lang3.ObjectUtils;
  20. import org.apache.commons.lang3.RandomStringUtils;
  21. import org.apache.rocketmq.spring.core.RocketMQTemplate;
  22. import org.springframework.beans.BeanUtils;
  23. import org.springframework.cache.annotation.Cacheable;
  24. import org.springframework.context.ApplicationContext;
  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.Scheduled;
  30. import org.springframework.security.crypto.password.PasswordEncoder;
  31. import org.springframework.stereotype.Service;
  32. import javax.persistence.criteria.Predicate;
  33. import java.math.BigDecimal;
  34. import java.time.LocalDateTime;
  35. import java.time.temporal.ChronoUnit;
  36. import java.util.*;
  37. import java.util.concurrent.ExecutionException;
  38. import java.util.concurrent.ForkJoinPool;
  39. import java.util.stream.Collectors;
  40. @Service
  41. @AllArgsConstructor
  42. @Slf4j
  43. public class AssetService {
  44. private AssetRepo assetRepo;
  45. private UserRepo userRepo;
  46. private CollectionRepo collectionRepo;
  47. private ApplicationContext applicationContext;
  48. private OrderRepo orderRepo;
  49. private TokenHistoryRepo tokenHistoryRepo;
  50. private SysConfigService sysConfigService;
  51. private RocketMQTemplate rocketMQTemplate;
  52. private GeneralProperties generalProperties;
  53. private ShowroomRepo showroomRepo;
  54. private ShowCollectionRepo showCollectionRepo;
  55. private CollectionPrivilegeRepo collectionPrivilegeRepo;
  56. private PasswordEncoder passwordEncoder;
  57. private MintActivityRepo mintActivityRepo;
  58. public Page<Asset> all(PageQuery pageQuery) {
  59. Page<Asset> all = assetRepo
  60. .findAll(JpaUtils.toSpecification(pageQuery, Asset.class), JpaUtils.toPageRequest(pageQuery));
  61. Map<String, Object> query = pageQuery.getQuery();
  62. if (query.containsKey("userId")) {
  63. List<Long> orderId = orderRepo
  64. .findAllByUserIdAndOpenedFalse(Convert.convert(Long.class, query.get("userId")));
  65. return all.map(asset -> {
  66. if (orderId.contains(asset.getOrderId())) {
  67. asset.setOpened(false);
  68. }
  69. return asset;
  70. });
  71. }
  72. return all;
  73. }
  74. public Asset createAsset(Collection collection, User user, Long orderId, BigDecimal price, String type, Integer number) {
  75. Asset asset = Asset.create(collection, user);
  76. asset.setTokenId(TokenUtils.genTokenId());
  77. asset.setNumber(number);
  78. asset.setOrderId(orderId);
  79. asset.setPrice(price);
  80. assetRepo.saveAndFlush(asset);
  81. tokenHistoryRepo.save(TokenHistory.builder()
  82. .tokenId(asset.getTokenId())
  83. .fromUser(collection.getMinter())
  84. .fromUserId(collection.getMinterId())
  85. .fromAvatar(collection.getMinterAvatar())
  86. .toUser(user.getNickname())
  87. .toUserId(user.getId())
  88. .toAvatar(user.getAvatar())
  89. .operation(type)
  90. .price(price)
  91. .build());
  92. rocketMQTemplate.syncSend(generalProperties.getMintTopic(), asset.getId());
  93. return asset;
  94. }
  95. public Asset createAsset(BlindBoxItem winItem, User user, Long orderId, BigDecimal price, String type,
  96. Integer number, Integer holdDays) {
  97. Collection blindBox = collectionRepo.findDetailById(winItem.getBlindBoxId())
  98. .orElseThrow(new BusinessException("盲盒不存在"));
  99. Collection collection = collectionRepo.findDetailById(winItem.getCollectionId())
  100. .orElseThrow(new BusinessException("藏品不存在"));
  101. Asset asset = Asset.create(winItem, user, holdDays);
  102. asset.setTokenId(TokenUtils.genTokenId());
  103. asset.setNumber(number);
  104. asset.setOrderId(orderId);
  105. asset.setPrice(price);
  106. asset.setTags(new HashSet<>());
  107. if (blindBox.getTags() != null) {
  108. asset.getTags().addAll(blindBox.getTags());
  109. }
  110. if (collection.getTags() != null) {
  111. asset.getTags().addAll(collection.getTags());
  112. }
  113. assetRepo.saveAndFlush(asset);
  114. tokenHistoryRepo.save(TokenHistory.builder()
  115. .tokenId(asset.getTokenId())
  116. .fromUser(winItem.getMinter())
  117. .fromUserId(winItem.getMinterId())
  118. .fromAvatar(winItem.getMinterAvatar())
  119. .toUser(user.getNickname())
  120. .toUserId(user.getId())
  121. .toAvatar(user.getAvatar())
  122. .operation(type)
  123. .price(price)
  124. .build());
  125. rocketMQTemplate.syncSend(generalProperties.getMintTopic(), asset.getId());
  126. return asset;
  127. }
  128. public void publicShow(Long id) {
  129. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  130. if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  131. throw new BusinessException("此藏品不属于你");
  132. }
  133. if (asset.isPublicShow()) {
  134. return;
  135. }
  136. if (asset.getStatus() != AssetStatus.NORMAL) {
  137. throw new BusinessException("当前状态不可展示");
  138. }
  139. User owner = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在"));
  140. Collection collection = Collection.builder()
  141. .name(asset.getName())
  142. .pic(asset.getPic())
  143. .minter(asset.getMinter())
  144. .minterId(asset.getMinterId())
  145. .minterAvatar(asset.getMinterAvatar())
  146. .owner(owner.getNickname())
  147. .ownerId(owner.getId())
  148. .ownerAvatar(owner.getAvatar())
  149. .detail(asset.getDetail())
  150. .type(CollectionType.DEFAULT)
  151. .source(CollectionSource.TRANSFER)
  152. .sale(0)
  153. .stock(1)
  154. .total(1)
  155. .onShelf(true)
  156. .salable(false)
  157. .price(BigDecimal.valueOf(0))
  158. .properties(asset.getProperties())
  159. .canResale(asset.isCanResale())
  160. .royalties(asset.getRoyalties())
  161. .serviceCharge(asset.getServiceCharge())
  162. .assetId(id)
  163. .number(asset.getNumber())
  164. .tags(new HashSet<>())
  165. .build();
  166. if (asset.getTags() != null) {
  167. collection.getTags().addAll(asset.getTags());
  168. }
  169. collectionRepo.save(collection);
  170. asset.setPublicShow(true);
  171. asset.setPublicCollectionId(collection.getId());
  172. assetRepo.save(asset);
  173. }
  174. public synchronized void consignment(Long id, BigDecimal price, String tradeCode) {
  175. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  176. if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  177. throw new BusinessException("此藏品不属于你");
  178. }
  179. int holdDays;
  180. if (asset.getSource() == AssetSource.GIFT) {
  181. holdDays = sysConfigService.getInt("gift_days");
  182. } else {
  183. if (ObjectUtils.isEmpty(asset.getHoldDays())) {
  184. holdDays = sysConfigService.getInt("hold_days");
  185. } else {
  186. holdDays = asset.getHoldDays();
  187. }
  188. }
  189. if (ChronoUnit.DAYS.between(asset.getCreatedAt(), LocalDateTime.now()) < holdDays) {
  190. throw new BusinessException("需持有满" + holdDays + "天才能寄售上架");
  191. }
  192. User owner = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在"));
  193. if (!passwordEncoder.matches(tradeCode, owner.getTradeCode())) {
  194. throw new BusinessException("交易密码错误");
  195. }
  196. // if (StringUtils.isBlank(owner.getSettleAccountId())) {
  197. // throw new BusinessException("请先绑定银行卡");
  198. // }
  199. if (asset.isConsignment()) {
  200. throw new BusinessException("已寄售,请勿重新操作");
  201. }
  202. if (asset.getStatus() != AssetStatus.NORMAL) {
  203. throw new BusinessException("当前状态不可寄售");
  204. }
  205. if (asset.isPublicShow()) {
  206. cancelPublic(asset);
  207. }
  208. //寄售中的展厅需要先删除展厅
  209. if (CollectionType.SHOWROOM.equals(asset.getType())) {
  210. if (showroomRepo.findByAssetId(id).isPresent()) {
  211. throw new BusinessException("请先删除展厅");
  212. }
  213. }
  214. Collection collection = Collection.builder()
  215. .name(asset.getName())
  216. .pic(asset.getPic())
  217. .minter(asset.getMinter())
  218. .minterId(asset.getMinterId())
  219. .minterAvatar(asset.getMinterAvatar())
  220. .owner(owner.getNickname())
  221. .ownerId(owner.getId())
  222. .ownerAvatar(owner.getAvatar())
  223. .detail(asset.getDetail())
  224. .type(CollectionType.DEFAULT)
  225. .source(CollectionSource.TRANSFER)
  226. .sale(0)
  227. .stock(1)
  228. .total(1)
  229. .onShelf(true)
  230. .salable(true)
  231. .price(price)
  232. .properties(asset.getProperties())
  233. .canResale(asset.isCanResale())
  234. .royalties(asset.getRoyalties())
  235. .serviceCharge(asset.getServiceCharge())
  236. .assetId(id)
  237. .number(asset.getNumber())
  238. .tags(new HashSet<>())
  239. .build();
  240. if (asset.getTags() != null) {
  241. collection.getTags().addAll(asset.getTags());
  242. }
  243. collectionRepo.save(collection);
  244. asset.setPublicShow(true);
  245. asset.setConsignment(true);
  246. asset.setPublicCollectionId(collection.getId());
  247. asset.setSellPrice(price);
  248. assetRepo.save(asset);
  249. }
  250. public void cancelConsignment(Long id) {
  251. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  252. if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  253. throw new BusinessException("此藏品不属于你");
  254. }
  255. cancelConsignment(asset);
  256. }
  257. public void cancelConsignment(Asset asset) {
  258. if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  259. throw new BusinessException("此藏品不属于你");
  260. }
  261. if (asset.getPublicCollectionId() != null) {
  262. List<Order> orders = orderRepo.findByCollectionId(asset.getPublicCollectionId());
  263. if (orders.stream().anyMatch(o -> o.getStatus() != OrderStatus.CANCELLED)) {
  264. throw new BusinessException("已有订单不可取消");
  265. }
  266. collectionRepo.findById(asset.getPublicCollectionId())
  267. .ifPresent(collection -> {
  268. collection.setSalable(false);
  269. collectionRepo.save(collection);
  270. });
  271. }
  272. asset.setConsignment(false);
  273. assetRepo.save(asset);
  274. }
  275. public void cancelPublic(Long id) {
  276. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  277. if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  278. throw new BusinessException("此藏品不属于你");
  279. }
  280. cancelPublic(asset);
  281. }
  282. public void cancelPublic(Asset asset) {
  283. if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  284. throw new BusinessException("此藏品不属于你");
  285. }
  286. if (!asset.isPublicShow()) {
  287. return;
  288. }
  289. if (asset.isConsignment()) {
  290. cancelConsignment(asset);
  291. }
  292. Collection collection = collectionRepo.findById(asset.getPublicCollectionId())
  293. .orElseThrow(new BusinessException("无展示记录"));
  294. collectionRepo.delete(collection);
  295. // 如果展厅有此藏品
  296. showCollectionRepo.deleteAllByCollectionId(asset.getPublicCollectionId());
  297. asset.setPublicShow(false);
  298. asset.setPublicCollectionId(null);
  299. assetRepo.save(asset);
  300. }
  301. public void usePrivilege(Long assetId, Long privilegeId) {
  302. Asset asset = assetRepo.findById(assetId).orElseThrow(new BusinessException("无记录"));
  303. asset.getPrivileges().stream().filter(p -> p.getId().equals(privilegeId)).forEach(p -> {
  304. if (!p.getName().equals("铸造")) {
  305. p.setOpened(true);
  306. p.setOpenTime(LocalDateTime.now());
  307. p.setOpenedBy(SecurityUtils.getAuthenticatedUser().getId());
  308. }
  309. });
  310. assetRepo.save(asset);
  311. }
  312. public void transfer(Asset asset, BigDecimal price, User toUser, TransferReason reason, Long orderId) {
  313. transfer(asset, price, toUser, reason, orderId, false);
  314. }
  315. public void transfer(Asset asset, BigDecimal price, User toUser, TransferReason reason, Long orderId, boolean safeTransfer) {
  316. Objects.requireNonNull(asset, "原藏品不能为空");
  317. Objects.requireNonNull(toUser, "转让人不能为空");
  318. Objects.requireNonNull(reason, "转让原因不能为空");
  319. User newOwner = toUser;
  320. if (safeTransfer) {
  321. String name = "0x" + RandomStringUtils.randomAlphabetic(8);
  322. newOwner = userRepo.save(User.builder()
  323. .username(name)
  324. .nickname(name)
  325. .avatar(Constants.DEFAULT_AVATAR)
  326. .build());
  327. }
  328. Asset newAsset = new Asset();
  329. BeanUtils.copyProperties(asset, newAsset);
  330. newAsset.setId(null);
  331. newAsset.setUserId(toUser.getId());
  332. newAsset.setOwner(newOwner.getNickname());
  333. newAsset.setOwnerId(newOwner.getId());
  334. newAsset.setOwnerAvatar(newOwner.getAvatar());
  335. newAsset.setPublicShow(false);
  336. newAsset.setConsignment(false);
  337. newAsset.setPublicCollectionId(null);
  338. newAsset.setStatus(AssetStatus.NORMAL);
  339. newAsset.setPrice(price);
  340. newAsset.setSellPrice(null);
  341. newAsset.setOrderId(orderId);
  342. newAsset.setFromAssetId(asset.getId());
  343. newAsset.setType(CollectionType.DEFAULT);
  344. newAsset.setSource(TransferReason.GIFT == reason ? AssetSource.GIFT : AssetSource.TRANSFER);
  345. newAsset.setTags(new HashSet<>(asset.getTags()));
  346. newAsset.setSafeTransfer(safeTransfer);
  347. newAsset.setHoldDays(asset.getOldHoldDays());
  348. assetRepo.save(newAsset);
  349. TokenHistory tokenHistory = TokenHistory.builder()
  350. .tokenId(asset.getTokenId())
  351. .fromUser(asset.getOwner())
  352. .fromUserId(asset.getOwnerId())
  353. .fromAvatar(asset.getOwnerAvatar())
  354. .toUser(toUser.getNickname())
  355. .toUserId(toUser.getId())
  356. .toAvatar(toUser.getAvatar())
  357. .operation(reason.getDescription())
  358. .price(TransferReason.GIFT == reason ? null : price)
  359. .build();
  360. tokenHistoryRepo.save(tokenHistory);
  361. asset.setPublicShow(false);
  362. asset.setConsignment(false);
  363. asset.setPublicCollectionId(null);
  364. asset.setStatus(TransferReason.GIFT == reason ? AssetStatus.GIFTED : AssetStatus.TRANSFERRED);
  365. asset.setOwner(newOwner.getNickname());
  366. asset.setOwnerId(newOwner.getId());
  367. asset.setOwnerAvatar(newOwner.getAvatar());
  368. assetRepo.save(asset);
  369. //vip权限转让
  370. CollectionPrivilege collectionPrivilege = collectionPrivilegeRepo.findByCollectionId(asset.getCollectionId());
  371. if (ObjectUtils.isNotEmpty(collectionPrivilege)) {
  372. if (collectionPrivilege.isVip()) {
  373. //更新vip信息
  374. userRepo.updateVipPurchase(toUser.getId(), 1);
  375. userRepo.updateVipPurchase(asset.getUserId(), 0);
  376. }
  377. }
  378. }
  379. public List<TokenHistory> tokenHistory(String tokenId, Long assetId) {
  380. if (tokenId == null) {
  381. if (assetId == null) return new ArrayList<>();
  382. tokenId = assetRepo.findById(assetId).map(Asset::getTokenId).orElse(null);
  383. }
  384. if (tokenId == null) return new ArrayList<>();
  385. return tokenHistoryRepo.findByTokenIdOrderByCreatedAtDesc(tokenId);
  386. }
  387. public void setHistory() {
  388. List<Asset> assets = assetRepo.findByCreatedAtBefore(LocalDateTime.of(2021, 11, 22, 23, 59, 59));
  389. assets.parallelStream().forEach(asset -> {
  390. try {
  391. User owner = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException(""));
  392. Order order = orderRepo.findById(asset.getOrderId()).orElseThrow(new BusinessException(""));
  393. TokenHistory t = TokenHistory.builder()
  394. .tokenId(asset.getTokenId())
  395. .fromUser(asset.getMinter())
  396. .fromUserId(asset.getMinterId())
  397. .fromAvatar(asset.getMinterAvatar())
  398. .toUser(owner.getNickname())
  399. .toUserId(owner.getId())
  400. .toAvatar(owner.getAvatar())
  401. .operation("出售")
  402. .price(order.getPrice())
  403. .build();
  404. t.setCreatedAt(asset.getCreatedAt());
  405. tokenHistoryRepo.save(t);
  406. } catch (Exception e) {
  407. }
  408. });
  409. }
  410. public Page<UserHistory> userHistory(Long userId, Long toUserId, Long fromUserId, Pageable pageable) {
  411. Page<TokenHistory> page;
  412. if (ObjectUtils.isNotEmpty(toUserId)) {
  413. page = tokenHistoryRepo.userHistoryTo(userId, toUserId, pageable);
  414. } else if (ObjectUtils.isNotEmpty(fromUserId)) {
  415. page = tokenHistoryRepo.userHistoryFrom(userId, fromUserId, pageable);
  416. } else {
  417. page = tokenHistoryRepo.userHistory(userId, pageable);
  418. }
  419. Set<String> tokenIds = page.stream().map(TokenHistory::getTokenId).collect(Collectors.toSet());
  420. List<Asset> assets = tokenIds.isEmpty() ? new ArrayList<>() : assetRepo.findByTokenIdIn(tokenIds);
  421. return page.map(tokenHistory -> {
  422. UserHistory userHistory = new UserHistory();
  423. BeanUtils.copyProperties(tokenHistory, userHistory);
  424. Optional<Asset> asset = assets.stream().filter(a -> a.getTokenId().equals(tokenHistory.getTokenId()))
  425. .findAny();
  426. userHistory.setAssetName(asset.map(Asset::getName).orElse(null));
  427. userHistory.setPic(asset.map(Asset::getPic).orElse(new ArrayList<>()));
  428. switch (tokenHistory.getOperation()) {
  429. case "出售":
  430. case "转让":
  431. userHistory.setDescription(tokenHistory.getToUserId().equals(userId) ? "作品交易——买入" : "作品交易——售出");
  432. break;
  433. case "空投":
  434. userHistory.setDescription("空投");
  435. break;
  436. case "转赠":
  437. userHistory.setDescription(tokenHistory.getToUserId().equals(userId) ? "他人赠送" : "作品赠送");
  438. break;
  439. }
  440. return userHistory;
  441. });
  442. }
  443. public Page<UserHistory> userHistory(Long userId, PageQuery pageQuery) {
  444. Page<TokenHistory> page = tokenHistoryRepo.findAll(((root, criteriaQuery, criteriaBuilder) -> {
  445. List<Predicate> and = JpaUtils
  446. .toPredicates(pageQuery, TokenHistory.class, root, criteriaQuery, criteriaBuilder);
  447. Map<String, Object> query = pageQuery.getQuery();
  448. if (ObjectUtils.isEmpty(query.get("toUserId")) && ObjectUtils.isEmpty(query.get("fromUserId"))) {
  449. and.add(criteriaBuilder.or(criteriaBuilder.equal(root.get("toUserId"), userId), criteriaBuilder
  450. .equal(root.get("fromUserId"), userId)));
  451. } else {
  452. if (ObjectUtils.isNotEmpty(query.get("toUserId"))) {
  453. and.add(criteriaBuilder.and(criteriaBuilder.equal(root.get("toUserId"), userId)));
  454. } else {
  455. and.add(criteriaBuilder.and(criteriaBuilder.equal(root.get("fromUserId"), userId)));
  456. }
  457. }
  458. return criteriaBuilder.and(and.toArray(new Predicate[0]));
  459. }), JpaUtils.toPageRequest(pageQuery));
  460. Set<String> tokenIds = page.stream().map(TokenHistory::getTokenId).collect(Collectors.toSet());
  461. List<Asset> assets = tokenIds.isEmpty() ? new ArrayList<>() : assetRepo.findByTokenIdIn(tokenIds);
  462. return page.map(tokenHistory -> {
  463. UserHistory userHistory = new UserHistory();
  464. BeanUtils.copyProperties(tokenHistory, userHistory);
  465. Optional<Asset> asset = assets.stream().filter(a -> a.getTokenId().equals(tokenHistory.getTokenId()))
  466. .findAny();
  467. userHistory.setAssetName(asset.map(Asset::getName).orElse(null));
  468. userHistory.setPic(asset.map(Asset::getPic).orElse(new ArrayList<>()));
  469. switch (tokenHistory.getOperation()) {
  470. case "出售":
  471. case "转让":
  472. userHistory.setDescription(tokenHistory.getToUserId().equals(userId) ? "作品交易——买入" : "作品交易——售出");
  473. break;
  474. case "空投":
  475. userHistory.setDescription("空投");
  476. break;
  477. case "转赠":
  478. userHistory.setDescription(tokenHistory.getToUserId().equals(userId) ? "他人赠送" : "作品赠送");
  479. break;
  480. }
  481. return userHistory;
  482. });
  483. }
  484. public String mint(LocalDateTime time) {
  485. if (time == null) {
  486. time = LocalDateTime.now();
  487. }
  488. for (Asset asset : assetRepo.toMint(time)) {
  489. rocketMQTemplate.syncSend(generalProperties.getMintTopic(), asset.getId());
  490. }
  491. return "ok";
  492. }
  493. @Cacheable(value = "userStat", key = "#userId")
  494. public Map<String, BigDecimal> breakdown(Long userId) {
  495. List<TokenHistory> page = tokenHistoryRepo.userHistory(userId);
  496. BigDecimal sale = page.stream()
  497. .filter(th -> th.getFromUserId().equals(userId) && ObjectUtils.isNotEmpty(th.getPrice()))
  498. .map(TokenHistory::getPrice)
  499. .reduce(BigDecimal.ZERO, BigDecimal::add);
  500. BigDecimal buy = page.stream()
  501. .filter(th -> th.getToUserId().equals(userId) && ObjectUtils.isNotEmpty(th.getPrice()))
  502. .map(TokenHistory::getPrice)
  503. .reduce(BigDecimal.ZERO, BigDecimal::add);
  504. Map<String, BigDecimal> map = new HashMap<>();
  505. map.put("sale", sale);
  506. map.put("buy", buy);
  507. return map;
  508. }
  509. public void transferCDN() throws ExecutionException, InterruptedException {
  510. ForkJoinPool customThreadPool = new ForkJoinPool(100);
  511. customThreadPool.submit(() -> {
  512. collectionRepo.selectResource().parallelStream().forEach(list -> {
  513. for (int i = 0; i < list.size(); i++) {
  514. list.set(i, replaceCDN(list.get(i)));
  515. }
  516. collectionRepo.updateCDN(Long.parseLong(list.get(0)),
  517. list.get(1),
  518. list.get(2),
  519. list.get(3),
  520. list.get(4),
  521. list.get(5));
  522. });
  523. assetRepo.selectResource().parallelStream().forEach(list -> {
  524. for (int i = 0; i < list.size(); i++) {
  525. list.set(i, replaceCDN(list.get(i)));
  526. }
  527. assetRepo.updateCDN(Long.parseLong(list.get(0)),
  528. list.get(1),
  529. list.get(2),
  530. list.get(3),
  531. list.get(4),
  532. list.get(5));
  533. });
  534. }).get();
  535. }
  536. public String replaceCDN(String url) {
  537. if (url == null) return null;
  538. return url.replaceAll("https://raex-meta\\.oss-cn-shenzhen\\.aliyuncs\\.com",
  539. "https://cdn.raex.vip");
  540. }
  541. // @Scheduled(cron = "0 0 0/1 * * ?")
  542. // public void offTheShelf() {
  543. // LocalDateTime lastTime = LocalDateTime.now().minusHours(120);
  544. // Set<Long> assetIds = collectionRepo
  545. // .findResaleCollectionPriceOver20K(BigDecimal
  546. // .valueOf(20000L), CollectionSource.TRANSFER, lastTime, true);
  547. // assetIds.forEach(this::cancelConsignmentBySystem);
  548. // }
  549. @Scheduled(cron = "0 0 0/1 * * ?")
  550. public void offTheShelfAll() {
  551. LocalDateTime lastTime = LocalDateTime.now().minusHours(240);
  552. Set<Long> assetIds = collectionRepo
  553. .findResaleCollectionPriceOver20K(BigDecimal
  554. .valueOf(0L), CollectionSource.TRANSFER, lastTime, true);
  555. assetIds.forEach(this::cancelConsignmentBySystem);
  556. }
  557. public void cancelConsignmentBySystem(Long id) {
  558. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  559. if (asset.getPublicCollectionId() != null) {
  560. List<Order> orders = orderRepo.findByCollectionId(asset.getPublicCollectionId());
  561. if (orders.stream().anyMatch(o -> o.getStatus() != OrderStatus.CANCELLED)) {
  562. throw new BusinessException("已有订单不可取消");
  563. }
  564. collectionRepo.findById(asset.getPublicCollectionId())
  565. .ifPresent(collection -> {
  566. collection.setSalable(false);
  567. collectionRepo.save(collection);
  568. });
  569. }
  570. asset.setConsignment(false);
  571. assetRepo.save(asset);
  572. }
  573. // @Cacheable(cacheNames = "fmaa", key = "#userId+'#'+#mintActivityId+'#'+#pageable.hashCode()")
  574. public PageWrapper<Asset> findMintActivityAssetsWrap(Long userId, Long mintActivityId, Pageable pageable) {
  575. return PageWrapper.of(findMintActivityAssets(userId, mintActivityId, pageable));
  576. }
  577. public Page<Asset> findMintActivityAssets(Long userId, Long mintActivityId, Pageable pageable) {
  578. MintActivity mintActivity = mintActivityRepo.findById(mintActivityId).orElse(null);
  579. if (mintActivity == null) return new PageImpl<>(Collections.emptyList());
  580. if (!mintActivity.isAudit()) {
  581. Set<Tag> tags = mintActivity.getRule().getTags();
  582. if (tags.isEmpty()) return new PageImpl<>(Collections.emptyList());
  583. return assetRepo.findAll((Specification<Asset>) (root, query, criteriaBuilder) ->
  584. query.distinct(true).where(criteriaBuilder.equal(root.get("userId"), userId),
  585. criteriaBuilder.equal(root.get("status"), AssetStatus.NORMAL),
  586. root.join("tags").get("id").in(tags.stream().map(Tag::getId).toArray()))
  587. .getRestriction(), pageable);
  588. } else {
  589. return assetRepo.findByUserIdAndStatusAndNameLike(userId, AssetStatus.NORMAL,
  590. "%" + mintActivity.getCollectionName() + "%", pageable);
  591. }
  592. }
  593. }