AssetService.java 34 KB

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