AssetService.java 43 KB

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