AssetService.java 28 KB

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