AssetService.java 69 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449
  1. package com.izouma.nineth.service;
  2. import cn.hutool.core.collection.CollectionUtil;
  3. import com.alibaba.fastjson.JSONArray;
  4. import com.alibaba.fastjson.JSONObject;
  5. import com.google.common.collect.Lists;
  6. import com.google.common.hash.Hashing;
  7. import com.izouma.nineth.TokenHistory;
  8. import com.izouma.nineth.config.Constants;
  9. import com.izouma.nineth.config.GeneralProperties;
  10. import com.izouma.nineth.converter.LongArrayConverter;
  11. import com.izouma.nineth.domain.Collection;
  12. import com.izouma.nineth.domain.*;
  13. import com.izouma.nineth.dto.*;
  14. import com.izouma.nineth.enums.*;
  15. import com.izouma.nineth.exception.BusinessException;
  16. import com.izouma.nineth.repo.*;
  17. import com.izouma.nineth.repo.nftdomain.DomainAskRepo;
  18. import com.izouma.nineth.utils.JpaUtils;
  19. import com.izouma.nineth.utils.SecurityUtils;
  20. import com.izouma.nineth.utils.TokenUtils;
  21. import lombok.AllArgsConstructor;
  22. import lombok.extern.slf4j.Slf4j;
  23. import org.apache.commons.collections.CollectionUtils;
  24. import org.apache.commons.lang3.ObjectUtils;
  25. import org.apache.commons.lang3.RandomStringUtils;
  26. import org.apache.commons.lang3.StringUtils;
  27. import org.apache.rocketmq.spring.core.RocketMQTemplate;
  28. import org.springframework.beans.BeanUtils;
  29. import org.springframework.cache.annotation.Cacheable;
  30. import org.springframework.data.domain.Page;
  31. import org.springframework.data.domain.PageImpl;
  32. import org.springframework.data.domain.PageRequest;
  33. import org.springframework.data.domain.Pageable;
  34. import org.springframework.data.jpa.domain.Specification;
  35. import org.springframework.scheduling.annotation.Async;
  36. import org.springframework.scheduling.annotation.Scheduled;
  37. import org.springframework.security.crypto.password.PasswordEncoder;
  38. import org.springframework.stereotype.Service;
  39. import javax.persistence.criteria.Predicate;
  40. import javax.transaction.Transactional;
  41. import java.math.BigDecimal;
  42. import java.nio.charset.StandardCharsets;
  43. import java.time.Duration;
  44. import java.time.LocalDateTime;
  45. import java.time.format.DateTimeFormatter;
  46. import java.time.temporal.ChronoUnit;
  47. import java.util.*;
  48. import java.util.concurrent.ExecutionException;
  49. import java.util.concurrent.ForkJoinPool;
  50. import java.util.concurrent.atomic.AtomicInteger;
  51. import java.util.regex.Matcher;
  52. import java.util.regex.Pattern;
  53. import java.util.stream.Collectors;
  54. @Service
  55. @AllArgsConstructor
  56. @Slf4j
  57. public class AssetService {
  58. private AssetRepo assetRepo;
  59. private UserRepo userRepo;
  60. private CollectionRepo collectionRepo;
  61. private OrderRepo orderRepo;
  62. private TokenHistoryRepo tokenHistoryRepo;
  63. private SysConfigService sysConfigService;
  64. private RocketMQTemplate rocketMQTemplate;
  65. private GeneralProperties generalProperties;
  66. private ShowroomRepo showroomRepo;
  67. private ShowCollectionRepo showCollectionRepo;
  68. private CollectionPrivilegeRepo collectionPrivilegeRepo;
  69. private PasswordEncoder passwordEncoder;
  70. private MintActivityRepo mintActivityRepo;
  71. private DestroyRecordRepo destroyRecordRepo;
  72. private AirDropService airDropService;
  73. private HCChainService hcChainService;
  74. private RockRecordService rockRecordService;
  75. private RockRecordRepo rockRecordRepo;
  76. private AssetLockRepo assetLockRepo;
  77. private UserBalanceService userBalanceService;
  78. private PhotoAssetRepo photoAssetRepo;
  79. private NumberSeqRepo numberSeqRepo;
  80. private DomainAskRepo domainAskRepo;
  81. private DomainOrderRepo domainOrderRepo;
  82. public Page<Asset> all(PageQuery pageQuery) {
  83. Map<String, Object> query = pageQuery.getQuery();
  84. Specification<Asset> specification = JpaUtils.toSpecification(pageQuery, Asset.class);
  85. PageRequest pageRequest = JpaUtils.toPageRequest(pageQuery);
  86. if (query.containsKey("lock")) {
  87. LocalDateTime now = LocalDateTime.now();
  88. query.remove("lock");
  89. specification = specification.and((Specification<Asset>) (root, criteriaQuery, criteriaBuilder) -> {
  90. List<Predicate> and = new ArrayList<>();
  91. and.add(criteriaBuilder.greaterThan(root.get("lockTo"), now));
  92. return criteriaBuilder.and(and.toArray(new Predicate[0]));
  93. });
  94. }
  95. Page<Asset> all = assetRepo.findAll(specification, pageRequest);
  96. // Map<String, Object> query = pageQuery.getQuery();
  97. // if (query.containsKey("userId")) {
  98. // List<Long> orderId = orderRepo
  99. // .findAllByUserIdAndOpenedFalse(Convert.convert(Long.class, query.get("userId")));
  100. // return all.map(asset -> {
  101. // if (orderId.contains(asset.getOrderId())) {
  102. // asset.setOpened(false);
  103. // }
  104. // return asset;
  105. // });
  106. // }
  107. return new PageWrapper<>(all.getContent(), all.getPageable().getPageNumber(),
  108. all.getPageable().getPageSize(), all.getTotalElements()).toPage();
  109. }
  110. public List<AssetDTO> userSummary(PageQuery pageQuery) {
  111. List<AssetDTO> assetDTOs = new ArrayList<>();
  112. // 根据条件查询所有资产
  113. List<Asset> assets = assetRepo.findAll(JpaUtils.toSpecification(pageQuery, Asset.class));
  114. if (CollectionUtils.isEmpty(assets)) {
  115. return assetDTOs;
  116. }
  117. // 取出资产中未开启盲盒数据
  118. List<Asset> blindBoxClosedAssets = assets.stream()
  119. .filter(asset -> !asset.isOpened() && CollectionType.BLIND_BOX
  120. .equals(asset.getType()))
  121. .collect(Collectors.toList());
  122. if (CollectionUtils.isNotEmpty(blindBoxClosedAssets)) {
  123. blindBoxClosedAssets.forEach(asset -> {
  124. assetDTOs.add(AssetDTO.create(Lists.newArrayList(asset)));
  125. });
  126. // 移除资产中未开启盲盒数据
  127. assets.removeAll(blindBoxClosedAssets);
  128. }
  129. // 取出资产中所有未设置prefixName的值
  130. List<Asset> prefixNameIsNullAssets = assets.stream()
  131. .filter(asset -> StringUtils.isBlank(asset.getPrefixName()))
  132. .collect(Collectors.toList());
  133. if (CollectionUtils.isNotEmpty(prefixNameIsNullAssets)) {
  134. prefixNameIsNullAssets.forEach(asset -> {
  135. assetDTOs.add(AssetDTO.create(Lists.newArrayList(asset)));
  136. });
  137. assets.removeAll(prefixNameIsNullAssets);
  138. }
  139. if (CollectionUtils.isNotEmpty(assets)) {
  140. // 取出资产中所有prefixName
  141. List<String> prefixNames = assets.stream()
  142. .map(Asset::getPrefixName)
  143. .distinct()
  144. .collect(Collectors.toList());
  145. // 将资产中相同prefixName归类(除未开启盲盒和未设置prefixName)
  146. prefixNames.forEach(str -> {
  147. List<Asset> collect = assets.stream()
  148. .filter(asset -> str.equals(asset.getPrefixName()))
  149. .collect(Collectors.toList());
  150. assetDTOs.add(AssetDTO.create(collect));
  151. });
  152. }
  153. return assetDTOs;
  154. }
  155. public Asset createAsset(Collection collection, User user, Long orderId, BigDecimal price, String type,
  156. Integer number, boolean safeFlag) {
  157. if (collection.isMessNumber() && number != null) {
  158. number = getMessedNumber(collection.getId(), number, collection.getTotal());
  159. }
  160. Asset asset = Asset.create(collection, user);
  161. asset.setTokenId(TokenUtils.genTokenId());
  162. asset.setNumber(number);
  163. asset.setOasisId(collection.getOasisId());
  164. asset.setOrderId(orderId);
  165. asset.setPrice(price);
  166. asset.setPrefixName(collection.getPrefixName());
  167. asset.setTags(new HashSet<>());
  168. if (collection.getTags() != null) {
  169. asset.getTags().addAll(collection.getTags());
  170. }
  171. User fakeUser = null;
  172. if (safeFlag) {
  173. fakeUser = createFakeUser();
  174. asset.setOwner(fakeUser.getNickname());
  175. asset.setOwnerId(fakeUser.getId());
  176. asset.setOwnerAvatar(fakeUser.getAvatar());
  177. }
  178. assetRepo.saveAndFlush(asset);
  179. tokenHistoryRepo.save(TokenHistory.builder()
  180. .tokenId(asset.getTokenId())
  181. .fromUser(collection.getMinter())
  182. .fromUserId(collection.getMinterId())
  183. .fromAvatar(collection.getMinterAvatar())
  184. .toUser((safeFlag ? fakeUser : user).getNickname())
  185. .toUserId((safeFlag ? fakeUser : user).getId())
  186. .toAvatar((safeFlag ? fakeUser : user).getAvatar())
  187. .operation(type)
  188. .price(price)
  189. .companyId(asset.getCompanyId())
  190. .build());
  191. //绿洲石
  192. rockRecordService.addRock(user.getId(), price, "购买");
  193. rocketMQTemplate.syncSend(generalProperties.getMintTopic(), asset.getId());
  194. if (asset.getOasisId() != null & asset.getSource().equals(AssetSource.OFFICIAL)) {
  195. AirDrop airDrop = new AirDrop();
  196. airDrop.setName("建筑空投展厅");
  197. airDrop.setCollectionId(207012L);
  198. List<Long> userIds = new ArrayList<>();
  199. userIds.add(user.getId());
  200. List<Long> nums = new ArrayList<>();
  201. nums.add(1L);
  202. airDrop.setType(AirDropType.asset);
  203. List<DropTarget> dropTargets = new ArrayList<>();
  204. DropTarget dropTarget = new DropTarget();
  205. dropTarget.setNickname(user.getNickname());
  206. dropTarget.setNum(1);
  207. dropTarget.setPhone(user.getPhone());
  208. dropTarget.setUserId(user.getId());
  209. dropTargets.add(dropTarget);
  210. airDrop.setTargets(dropTargets);
  211. airDrop.setUserIds(userIds);
  212. airDrop.setNum(nums);
  213. airDropService.create(airDrop);
  214. }
  215. return asset;
  216. }
  217. public Asset createAsset(PhotoAsset photoAsset, User user, Long orderId, BigDecimal price, String type,
  218. Integer number, boolean safeFlag) {
  219. Asset asset = Asset.create(photoAsset, user);
  220. asset.setTokenId(TokenUtils.genTokenId());
  221. asset.setNumber(number);
  222. asset.setOrderId(orderId);
  223. asset.setPrice(price);
  224. asset.setPrefixName("星图");
  225. asset.setTags(new HashSet<>());
  226. User fakeUser = null;
  227. if (safeFlag) {
  228. fakeUser = createFakeUser();
  229. asset.setOwner(fakeUser.getNickname());
  230. asset.setOwnerId(fakeUser.getId());
  231. asset.setOwnerAvatar(fakeUser.getAvatar());
  232. }
  233. assetRepo.saveAndFlush(asset);
  234. tokenHistoryRepo.save(TokenHistory.builder()
  235. .tokenId(asset.getTokenId())
  236. .fromUser(photoAsset.getUserName())
  237. .fromUserId(photoAsset.getUserId())
  238. .fromAvatar(photoAsset.getUserAvatar())
  239. .toUser((safeFlag ? fakeUser : user).getNickname())
  240. .toUserId((safeFlag ? fakeUser : user).getId())
  241. .toAvatar((safeFlag ? fakeUser : user).getAvatar())
  242. .operation(type)
  243. .price(price)
  244. .companyId(asset.getCompanyId())
  245. .build());
  246. //绿洲石
  247. // rockRecordService.addRock(user.getId(), price, "购买");
  248. rocketMQTemplate.syncSend(generalProperties.getMintTopic(), asset.getId());
  249. return asset;
  250. }
  251. public Asset createAsset(DomainOrder domainOrder, User user, Long orderId, BigDecimal price, String type,
  252. Integer number, boolean safeFlag) {
  253. Asset asset = Asset.create(domainOrder, user);
  254. asset.setTokenId(TokenUtils.genTokenId());
  255. asset.setNumber(number);
  256. asset.setOrderId(orderId);
  257. asset.setPrice(price);
  258. asset.setStatus(AssetStatus.PENDING);
  259. if (StringUtils.isNumeric(domainOrder.getPicName())) {
  260. if (domainOrder.getPicName().length() < 6) {
  261. asset.setPrefixName("RIDN" + domainOrder.getPicName().length());
  262. } else {
  263. asset.setPrefixName("RIDN6");
  264. }
  265. } else {
  266. Pattern p;
  267. p = Pattern.compile("[\u4E00-\u9FA5|\\!|\\,|\\。|\\(|\\)|\\《|\\》|\\“|\\”|\\?|\\:|\\;|\\【|\\】]");
  268. Matcher m = p.matcher(domainOrder.getPicName());
  269. if (m.find()) {
  270. asset.setPrefixName("RIDC");
  271. } else {
  272. if (domainOrder.getPicName().length() < 6) {
  273. asset.setPrefixName("RID" + domainOrder.getPicName().length());
  274. } else {
  275. asset.setPrefixName("RID");
  276. }
  277. }
  278. }
  279. asset.setTags(new HashSet<>());
  280. User fakeUser = null;
  281. if (safeFlag) {
  282. fakeUser = createFakeUser();
  283. asset.setOwner(fakeUser.getNickname());
  284. asset.setOwnerId(fakeUser.getId());
  285. asset.setOwnerAvatar(fakeUser.getAvatar());
  286. }
  287. assetRepo.saveAndFlush(asset);
  288. tokenHistoryRepo.save(TokenHistory.builder()
  289. .tokenId(asset.getTokenId())
  290. .fromUser(domainOrder.getUserName())
  291. .fromUserId(domainOrder.getUserId())
  292. .fromAvatar(domainOrder.getUserAvatar())
  293. .toUser((safeFlag ? fakeUser : user).getNickname())
  294. .toUserId((safeFlag ? fakeUser : user).getId())
  295. .toAvatar((safeFlag ? fakeUser : user).getAvatar())
  296. .operation(type)
  297. .price(price)
  298. .companyId(asset.getCompanyId())
  299. .build());
  300. //绿洲石
  301. // rockRecordService.addRock(user.getId(), price, "购买");
  302. rocketMQTemplate.syncSend(generalProperties.getMintTopic(), asset.getId());
  303. return asset;
  304. }
  305. public Asset createAsset(TradeAuction tradeAuction, User user, Long orderId, BigDecimal price, String type,
  306. Integer number, boolean safeFlag) {
  307. Asset asset = Asset.create(tradeAuction, user);
  308. asset.setTokenId(TokenUtils.genTokenId());
  309. asset.setNumber(number);
  310. asset.setOrderId(orderId);
  311. asset.setPrice(price);
  312. asset.setPrefixName("易拍");
  313. asset.setTags(new HashSet<>());
  314. User fakeUser = null;
  315. if (safeFlag) {
  316. fakeUser = createFakeUser();
  317. asset.setOwner(fakeUser.getNickname());
  318. asset.setOwnerId(fakeUser.getId());
  319. asset.setOwnerAvatar(fakeUser.getAvatar());
  320. }
  321. assetRepo.saveAndFlush(asset);
  322. tokenHistoryRepo.save(TokenHistory.builder()
  323. .tokenId(asset.getTokenId())
  324. .fromUser(tradeAuction.getMinter())
  325. .fromUserId(Long.valueOf(tradeAuction.getMinterId()))
  326. .fromAvatar(tradeAuction.getMinterAvatar())
  327. .toUser((safeFlag ? fakeUser : user).getNickname())
  328. .toUserId((safeFlag ? fakeUser : user).getId())
  329. .toAvatar((safeFlag ? fakeUser : user).getAvatar())
  330. .operation(type)
  331. .price(null)
  332. .companyId(asset.getCompanyId())
  333. .build());
  334. //绿洲石
  335. // rockRecordService.addRock(user.getId(), price, "购买");
  336. rocketMQTemplate.syncSend(generalProperties.getMintTopic(), asset.getId());
  337. return asset;
  338. }
  339. public synchronized int getMessedNumber(Long collectionId, int number, int total) {
  340. NumberSeq numberSeq = numberSeqRepo.findById(collectionId).orElse(null);
  341. if (numberSeq == null || numberSeq.getTotal() != total) {
  342. numberSeq = new NumberSeq();
  343. numberSeq.setId(collectionId);
  344. numberSeq.setTotal(total);
  345. Map<Integer, Integer> map = new HashMap<>();
  346. for (int i = 0; i < total; i++) {
  347. map.put(i, Hashing.md5().hashString(collectionId + ":" + i, StandardCharsets.UTF_8).asInt());
  348. }
  349. numberSeq.setNumbers(map.entrySet().stream()
  350. .sorted(Map.Entry.comparingByValue())
  351. .map(Map.Entry::getKey)
  352. .collect(Collectors.toList()));
  353. numberSeqRepo.save(numberSeq);
  354. }
  355. return numberSeq.getNumbers().get(number);
  356. }
  357. public Asset createAsset(BlindBoxItem winItem, User user, Long orderId, BigDecimal price, String type,
  358. Integer number, Integer holdDays, boolean safeFlag) {
  359. Collection blindBox = collectionRepo.findDetailById(winItem.getBlindBoxId())
  360. .orElseThrow(new BusinessException("盲盒不存在"));
  361. Collection collection = collectionRepo.findDetailById(winItem.getCollectionId())
  362. .orElseThrow(new BusinessException("藏品不存在"));
  363. if (collection.isMessNumber() && number != null) {
  364. number = getMessedNumber(collection.getId(), number, collection.getTotal());
  365. }
  366. winItem.setCollection(collection);
  367. Asset asset = Asset.create(winItem, user, holdDays);
  368. if (collection.getType().equals(CollectionType.DOMAIN)) {
  369. asset.setType(CollectionType.DOMAIN);
  370. }
  371. asset.setTokenId(TokenUtils.genTokenId());
  372. asset.setNumber(number);
  373. asset.setOasisId(winItem.getOasisId());
  374. asset.setOrderId(orderId);
  375. asset.setPrice(price);
  376. asset.setPrefixName(collection.getPrefixName());
  377. asset.setEmpower(collection.getEmpower());
  378. asset.setTags(new HashSet<>());
  379. if (blindBox.getTags() != null) {
  380. asset.getTags().addAll(blindBox.getTags());
  381. }
  382. if (collection.getTags() != null) {
  383. asset.getTags().addAll(collection.getTags());
  384. }
  385. User fakeUser = null;
  386. if (safeFlag) {
  387. fakeUser = createFakeUser();
  388. asset.setOwner(fakeUser.getNickname());
  389. asset.setOwnerId(fakeUser.getId());
  390. asset.setOwnerAvatar(fakeUser.getAvatar());
  391. }
  392. assetRepo.saveAndFlush(asset);
  393. tokenHistoryRepo.save(TokenHistory.builder()
  394. .tokenId(asset.getTokenId())
  395. .fromUser(winItem.getMinter())
  396. .fromUserId(winItem.getMinterId())
  397. .fromAvatar(winItem.getMinterAvatar())
  398. .toUser((safeFlag ? fakeUser : user).getNickname())
  399. .toUserId((safeFlag ? fakeUser : user).getId())
  400. .toAvatar((safeFlag ? fakeUser : user).getAvatar())
  401. .operation(type)
  402. .price(price)
  403. .companyId(asset.getCompanyId())
  404. .build());
  405. //绿洲石
  406. rockRecordService.addRock(user.getId(), price, "购买");
  407. rocketMQTemplate.syncSend(generalProperties.getMintTopic(), asset.getId());
  408. return asset;
  409. }
  410. public void publicShow(Long id) {
  411. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  412. Set<DomainAskStatus> statuses = new HashSet<>();
  413. statuses.add(DomainAskStatus.ASKING);
  414. if (domainAskRepo
  415. .countByAssetIdAndOwnerIdAndStatusInAndDelFalse(asset.getId(), asset.getOwnerId(), statuses) > 0) {
  416. throw new BusinessException("已有叫价,不可上架");
  417. }
  418. if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  419. throw new BusinessException("此藏品不属于你");
  420. }
  421. if (asset.getLockTo() != null && asset.getLockTo().isAfter(LocalDateTime.now())) {
  422. throw new BusinessException("已锁仓,不能上架展示");
  423. }
  424. if (asset.isPublicShow()) {
  425. return;
  426. }
  427. if (asset.getStatus() != AssetStatus.NORMAL) {
  428. throw new BusinessException("当前状态不可展示");
  429. }
  430. User owner = asset.isSafeFlag() ?
  431. userRepo.findById(asset.getOwnerId()).orElseThrow(new BusinessException("用户不存在"))
  432. : userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在"));
  433. Collection collection = Collection.builder()
  434. .name(asset.getName())
  435. .pic(asset.getPic())
  436. .minter(asset.getMinter())
  437. .minterId(asset.getMinterId())
  438. .minterAvatar(asset.getMinterAvatar())
  439. .owner(owner.getNickname())
  440. .oasisId(asset.getOasisId())
  441. .ownerId(owner.getId())
  442. .ownerAvatar(owner.getAvatar())
  443. .detail(asset.getDetail())
  444. .type(CollectionType.DEFAULT)
  445. .source(CollectionSource.TRANSFER)
  446. .sale(0)
  447. .stock(1)
  448. .total(1)
  449. .onShelf(true)
  450. .salable(false)
  451. .price(BigDecimal.valueOf(0))
  452. .properties(asset.getProperties())
  453. .canResale(asset.isCanResale())
  454. .royalties(asset.getRoyalties())
  455. .serviceCharge(asset.getServiceCharge())
  456. .assetId(id)
  457. .number(asset.getNumber())
  458. .tags(new HashSet<>())
  459. .prefixName(asset.getPrefixName())
  460. .companyId(asset.getCompanyId())
  461. .build();
  462. if (asset.getTags() != null) {
  463. collection.getTags().addAll(asset.getTags());
  464. }
  465. if (asset.getType().equals(CollectionType.PICTURE)) {
  466. collection.setType(CollectionType.PICTURE);
  467. }
  468. if (asset.getType().equals(CollectionType.DOMAIN)) {
  469. collection.setType(CollectionType.DOMAIN);
  470. }
  471. collectionRepo.save(collection);
  472. asset.setPublicShow(true);
  473. asset.setPublicCollectionId(collection.getId());
  474. assetRepo.saveAndFlush(asset);
  475. }
  476. public synchronized void consignment(Long id, BigDecimal price, String tradeCode, boolean safeFlag) {
  477. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  478. Set<DomainAskStatus> statuses = new HashSet<>();
  479. statuses.add(DomainAskStatus.ASKING);
  480. if (domainAskRepo
  481. .countByAssetIdAndOwnerIdAndStatusInAndDelFalse(asset.getId(), asset.getOwnerId(), statuses) > 0) {
  482. throw new BusinessException("已有叫价,不可上架");
  483. }
  484. String mapString = sysConfigService.getString("collection_price");
  485. if (StringUtils.isNotBlank(mapString)) {
  486. JSONObject json = JSONObject.parseObject(mapString);
  487. String assetName = asset.getName();
  488. json.forEach((k, v) -> {
  489. if (assetName.contains(k)) {
  490. Map<String, Object> map = (Map<String, Object>) v;
  491. BigDecimal max = BigDecimal.valueOf(Double.parseDouble(map.get("maxPrice").toString()));
  492. BigDecimal min = BigDecimal.valueOf(Double.parseDouble(map.get("minPrice").toString()));
  493. if (price.compareTo(max) > 0) {
  494. throw new BusinessException("已经超过藏品最高限价" + max + "元");
  495. }
  496. if (price.compareTo(min) < 0) {
  497. throw new BusinessException("低于藏品最低限价" + min + "元");
  498. }
  499. }
  500. });
  501. }
  502. if (Objects.nonNull(asset.getType()) && asset.getType().equals(CollectionType.SHOWROOM)) {
  503. throw new BusinessException("展厅类型的藏品不能上架");
  504. }
  505. if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  506. throw new BusinessException("此藏品不属于你");
  507. }
  508. if (asset.getType().equals(CollectionType.PICTURE)) {
  509. throw new BusinessException("星图藏品不可寄售");
  510. }
  511. if (!sysConfigService.getBoolean("domain_transfer")) {
  512. throw new BusinessException("域名暂不可以寄售");
  513. }
  514. if (asset.getLockTo() != null && asset.getLockTo().isAfter(LocalDateTime.now())) {
  515. throw new BusinessException("已锁仓,不能寄售");
  516. }
  517. List<String> bannedAssets = Arrays.asList(sysConfigService.getString("older_collections").split(",").clone());
  518. bannedAssets.forEach(name -> {
  519. if (asset.getName().contains(name)) {
  520. throw new BusinessException("编年史藏品无法寄售");
  521. }
  522. });
  523. int holdDays = 0;
  524. if (!asset.getType().equals(CollectionType.DOMAIN)) {
  525. if (asset.getSource() == AssetSource.GIFT) {
  526. LocalDateTime localDateTime = asset.getCreatedAt();
  527. LocalDateTime gift_change_time = LocalDateTime
  528. .parse(sysConfigService.getString("gift_change_time"), DateTimeFormatter
  529. .ofPattern("yyyy-MM-dd HH:mm:ss"));
  530. if (localDateTime.compareTo(gift_change_time) < 0) {
  531. holdDays = 20;
  532. } else {
  533. holdDays = sysConfigService.getInt("gift_days");
  534. }
  535. } else {
  536. if (ObjectUtils.isEmpty(asset.getHoldDays())) {
  537. holdDays = sysConfigService.getInt("hold_days");
  538. } else {
  539. holdDays = asset.getHoldDays();
  540. }
  541. }
  542. } else {
  543. if (!ObjectUtils.isEmpty(asset.getHoldDays())) {
  544. holdDays = asset.getHoldDays();
  545. }
  546. }
  547. if (holdDays == 0 && AssetSource.OFFICIAL.equals(asset.getSource())) {
  548. BigDecimal officialConsignment = sysConfigService.getBigDecimal("OFFICIAL_CONSIGNMENT");
  549. //天转小时
  550. int hour = officialConsignment.multiply(new BigDecimal("24")).intValue();
  551. if (ChronoUnit.HOURS.between(asset.getCreatedAt(), LocalDateTime.now()) < hour) {
  552. throw new BusinessException("需持有满" + hour + "小时后才能寄售上架");
  553. }
  554. }
  555. if (ChronoUnit.DAYS.between(asset.getCreatedAt(), LocalDateTime.now()) < holdDays) {
  556. throw new BusinessException("需持有满" + holdDays + "天才能寄售上架");
  557. }
  558. User owner;
  559. if (safeFlag && !asset.isSafeFlag()) {
  560. owner = createFakeUser();
  561. asset.setOwner(owner.getNickname());
  562. asset.setOwnerId(owner.getId());
  563. asset.setOwnerAvatar(owner.getAvatar());
  564. asset.setSafeFlag(true);
  565. tokenHistoryRepo.findByTokenIdOrderByCreatedAtDesc(asset.getTokenId()).stream()
  566. .filter(t -> t.getToUserId().equals(asset.getUserId())).findFirst()
  567. .ifPresent(tokenHistory -> {
  568. tokenHistory.setToUserId(owner.getId());
  569. tokenHistory.setToUser(owner.getNickname());
  570. tokenHistory.setToAvatar(owner.getAvatar());
  571. tokenHistoryRepo.save(tokenHistory);
  572. });
  573. } else {
  574. owner = asset.isSafeFlag() ?
  575. userRepo.findById(asset.getOwnerId()).orElseThrow(new BusinessException("用户不存在"))
  576. : userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在"));
  577. }
  578. if (!passwordEncoder.matches(tradeCode, userRepo.findTradeCode(asset.getUserId()))) {
  579. throw new BusinessException("交易密码错误");
  580. }
  581. // if (StringUtils.isBlank(owner.getSettleAccountId())) {
  582. // throw new BusinessException("请先绑定银行卡");
  583. // }
  584. if (asset.isConsignment()) {
  585. throw new BusinessException("已寄售,请勿重新操作");
  586. }
  587. if (asset.getStatus() != AssetStatus.NORMAL) {
  588. throw new BusinessException("当前状态不可寄售");
  589. }
  590. if (asset.isPublicShow()) {
  591. cancelPublic(asset);
  592. }
  593. //寄售中的展厅需要先删除展厅
  594. if (CollectionType.SHOWROOM.equals(asset.getType())) {
  595. if (showroomRepo.findByAssetId(id).isPresent()) {
  596. throw new BusinessException("请先删除展厅");
  597. }
  598. }
  599. Collection collection = Collection.builder()
  600. .name(asset.getName())
  601. .pic(asset.getPic())
  602. .minter(asset.getMinter())
  603. .minterId(asset.getMinterId())
  604. .minterAvatar(asset.getMinterAvatar())
  605. .owner(owner.getNickname())
  606. .ownerId(owner.getId())
  607. .oasisId(asset.getOasisId())
  608. .ownerAvatar(owner.getAvatar())
  609. .detail(asset.getDetail())
  610. .type(CollectionType.DEFAULT)
  611. .source(CollectionSource.TRANSFER)
  612. .sale(0)
  613. .stock(1)
  614. .total(1)
  615. .onShelf(true)
  616. .salable(true)
  617. .price(price)
  618. .properties(asset.getProperties())
  619. .canResale(asset.isCanResale())
  620. .royalties(asset.getRoyalties())
  621. .serviceCharge(asset.getServiceCharge())
  622. .assetId(id)
  623. .number(asset.getNumber())
  624. .tags(new HashSet<>())
  625. .prefixName(asset.getPrefixName())
  626. .companyId(asset.getCompanyId())
  627. .build();
  628. if (asset.getType().equals(CollectionType.DOMAIN)) {
  629. collection.setType(CollectionType.DOMAIN);
  630. }
  631. if (asset.getTags() != null) {
  632. collection.getTags().addAll(asset.getTags());
  633. }
  634. collectionRepo.save(collection);
  635. asset.setPublicShow(true);
  636. asset.setConsignment(true);
  637. asset.setPublicCollectionId(collection.getId());
  638. asset.setSellPrice(price);
  639. assetRepo.saveAndFlush(asset);
  640. }
  641. public Asset cancelConsignmentAndStore(Long id) {
  642. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  643. if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  644. throw new BusinessException("此藏品不属于你");
  645. }
  646. if (asset.getStatus() != AssetStatus.NORMAL) {
  647. throw new BusinessException("当前状态不可寄售");
  648. }
  649. if (asset.getPublicCollectionId() != null) {
  650. List<Order> orders = orderRepo.findByCollectionId(asset.getPublicCollectionId());
  651. if (orders.stream().anyMatch(o -> o.getStatus() != OrderStatus.CANCELLED)) {
  652. throw new BusinessException("已有订单不可取消");
  653. }
  654. collectionRepo.findById(asset.getPublicCollectionId())
  655. .ifPresent(collection -> {
  656. collection.setSalable(false);
  657. collection.setOnShelf(false);
  658. collectionRepo.save(collection);
  659. });
  660. }
  661. asset.setConsignment(false);
  662. asset.setPublicShow(false);
  663. asset.setStatus(AssetStatus.DESTROYING);
  664. return assetRepo.saveAndFlush(asset);
  665. }
  666. public void batchCancelConsignment(String search) {
  667. List<Asset> onShelf = assetRepo.findOnShelfByNameLike("%" + search + "%");
  668. onShelf.forEach(this::cancelConsignmentBySystem);
  669. }
  670. public void cancelConsignment(Long id) {
  671. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  672. if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  673. throw new BusinessException("此藏品不属于你");
  674. }
  675. cancelConsignment(asset);
  676. }
  677. public void cancelConsignment(Asset asset) {
  678. if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  679. throw new BusinessException("此藏品不属于你");
  680. }
  681. if (asset.getPublicCollectionId() != null) {
  682. List<Order> orders = orderRepo.findByCollectionId(asset.getPublicCollectionId());
  683. if (orders.stream().anyMatch(o -> o.getStatus() != OrderStatus.CANCELLED)) {
  684. throw new BusinessException("已有订单不可取消");
  685. }
  686. collectionRepo.findById(asset.getPublicCollectionId())
  687. .ifPresent(collection -> {
  688. collection.setSalable(false);
  689. collectionRepo.save(collection);
  690. });
  691. }
  692. asset.setConsignment(false);
  693. assetRepo.saveAndFlush(asset);
  694. }
  695. public void cancelPublic(Long id) {
  696. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  697. if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  698. throw new BusinessException("此藏品不属于你");
  699. }
  700. cancelPublic(asset);
  701. }
  702. public void cancelPublic(Asset asset) {
  703. if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  704. throw new BusinessException("此藏品不属于你");
  705. }
  706. if (!asset.isPublicShow()) {
  707. return;
  708. }
  709. if (asset.isConsignment()) {
  710. cancelConsignment(asset);
  711. }
  712. Collection collection = collectionRepo.findById(asset.getPublicCollectionId())
  713. .orElseThrow(new BusinessException("无展示记录"));
  714. collectionRepo.delete(collection);
  715. // 如果展厅有此藏品
  716. showCollectionRepo.deleteAllByCollectionId(asset.getPublicCollectionId());
  717. asset.setPublicShow(false);
  718. asset.setPublicCollectionId(null);
  719. assetRepo.saveAndFlush(asset);
  720. }
  721. public void usePrivilege(Long assetId, Long privilegeId) {
  722. Asset asset = assetRepo.findById(assetId).orElseThrow(new BusinessException("无记录"));
  723. asset.getPrivileges().stream().filter(p -> p.getId().equals(privilegeId)).forEach(p -> {
  724. if (!p.getName().equals("铸造")) {
  725. p.setOpened(true);
  726. p.setOpenTime(LocalDateTime.now());
  727. p.setOpenedBy(SecurityUtils.getAuthenticatedUser().getId());
  728. }
  729. });
  730. assetRepo.saveAndFlush(asset);
  731. }
  732. public void transfer(Asset asset, BigDecimal price, User toUser, TransferReason reason, Long orderId) {
  733. transfer(asset, price, toUser, reason, orderId, false, asset.getTags());
  734. }
  735. private User createFakeUser() {
  736. String name = "0x" + RandomStringUtils.randomAlphabetic(8);
  737. return userRepo.save(User.builder()
  738. .username(name)
  739. .nickname(name)
  740. .avatar(Constants.DEFAULT_AVATAR)
  741. .isPublicShow(true)
  742. .build());
  743. }
  744. public void transfer(Asset asset, BigDecimal price, User toUser, TransferReason reason, Long orderId, boolean safeFlag, Set<Tag> tags) {
  745. Objects.requireNonNull(asset, "原藏品不能为空");
  746. Objects.requireNonNull(toUser, "转让人不能为空");
  747. Objects.requireNonNull(reason, "转让原因不能为空");
  748. try {
  749. if (asset.getType() == CollectionType.DOMAIN) {
  750. if (asset.getCollectionId() != null) {
  751. Optional<DomainOrder> domainOrder = domainOrderRepo.findById(asset.getCollectionId());
  752. domainOrder.ifPresent(domainOrder1 -> {
  753. domainOrder1.setOpenHyperLink(false);
  754. domainOrder1.setHyperLinkType(null);
  755. domainOrder1.setAddress(null);
  756. domainOrder1.setCurrentOwnerId(null);
  757. });
  758. }
  759. }
  760. } catch (Exception e) {
  761. log.error("超链报错:assetId" + asset.getId(), e);
  762. }
  763. User newOwner = toUser;
  764. if (safeFlag) {
  765. newOwner = createFakeUser();
  766. }
  767. Asset newAsset = new Asset();
  768. BeanUtils.copyProperties(asset, newAsset);
  769. newAsset.setId(null);
  770. newAsset.setUserId(toUser.getId());
  771. newAsset.setOwner(newOwner.getNickname());
  772. newAsset.setOwnerId(newOwner.getId());
  773. newAsset.setOwnerAvatar(newOwner.getAvatar());
  774. newAsset.setPublicShow(false);
  775. newAsset.setConsignment(false);
  776. newAsset.setPublicCollectionId(null);
  777. newAsset.setTags(new HashSet<>(tags));
  778. asset.setTags(new HashSet<>());
  779. newAsset.setStatus(AssetStatus.NORMAL);
  780. newAsset.setPrice(price);
  781. newAsset.setSellPrice(null);
  782. newAsset.setOrderId(orderId);
  783. newAsset.setOasisId(asset.getOasisId());
  784. newAsset.setFromAssetId(asset.getId());
  785. newAsset.setType(CollectionType.DEFAULT);
  786. if (asset.getType() == CollectionType.DOMAIN) {
  787. newAsset.setType(CollectionType.DOMAIN);
  788. }
  789. newAsset.setSource(TransferReason.GIFT == reason ? AssetSource.GIFT : AssetSource.TRANSFER);
  790. newAsset.setSafeFlag(safeFlag);
  791. newAsset.setHoldDays(asset.getOldHoldDays());
  792. if (asset.getType().equals(CollectionType.PICTURE)) {
  793. newAsset.setType(CollectionType.PICTURE);
  794. newAsset.setHoldDays(0);
  795. }
  796. Long savedId = assetRepo.saveAndFlush(newAsset).getId();
  797. if (asset.getType().equals(CollectionType.PICTURE)) {
  798. newAsset.setType(CollectionType.PICTURE);
  799. PhotoAsset photoAsset = photoAssetRepo.findFirstByCreateAssetId(asset.getId());
  800. photoAsset.setGifted(true);
  801. photoAsset.setOwnerId(toUser.getId());
  802. photoAsset.setCreateAssetId(savedId);
  803. photoAssetRepo.save(photoAsset);
  804. }
  805. TokenHistory tokenHistory = TokenHistory.builder()
  806. .tokenId(asset.getTokenId())
  807. .fromUser(asset.getOwner())
  808. .fromUserId(asset.getOwnerId())
  809. .fromAvatar(asset.getOwnerAvatar())
  810. .toUser(newOwner.getNickname())
  811. .toUserId(newOwner.getId())
  812. .toAvatar(newOwner.getAvatar())
  813. .operation(reason.getDescription())
  814. .price(TransferReason.GIFT == reason ? null : price)
  815. .companyId(asset.getCompanyId())
  816. .build();
  817. tokenHistoryRepo.save(tokenHistory);
  818. //购买者加绿洲石
  819. if (TransferReason.TRANSFER.equals(reason) || TransferReason.AUCTION.equals(reason)) {
  820. rockRecordService.addRock(newOwner.getId(), price, "购买");
  821. }
  822. asset.setPublicShow(false);
  823. asset.setConsignment(false);
  824. asset.setPublicCollectionId(null);
  825. switch (reason) {
  826. case GIFT:
  827. asset.setStatus(AssetStatus.GIFTED);
  828. break;
  829. case AUCTION:
  830. asset.setStatus(AssetStatus.AUCTIONED);
  831. break;
  832. case TRANSFER:
  833. asset.setStatus(AssetStatus.TRANSFERRED);
  834. break;
  835. case ASK:
  836. asset.setStatus(AssetStatus.TRANSFERRED);
  837. break;
  838. }
  839. asset.setOwner(newOwner.getNickname());
  840. asset.setOwnerId(newOwner.getId());
  841. asset.setOwnerAvatar(newOwner.getAvatar());
  842. assetRepo.saveAndFlush(asset);
  843. //vip权限转让
  844. CollectionPrivilege collectionPrivilege = collectionPrivilegeRepo.findByCollectionId(asset.getCollectionId());
  845. if (ObjectUtils.isNotEmpty(collectionPrivilege)) {
  846. if (collectionPrivilege.isVip()) {
  847. //更新vip信息
  848. userRepo.updateVipPurchase(toUser.getId(), 1);
  849. userRepo.updateVipPurchase(asset.getUserId(), 0);
  850. }
  851. }
  852. }
  853. public List<TokenHistory> tokenHistory(String tokenId, Long assetId) {
  854. if (tokenId == null) {
  855. if (assetId == null) return new ArrayList<>();
  856. tokenId = assetRepo.findById(assetId).map(Asset::getTokenId).orElse(null);
  857. }
  858. if (tokenId == null) return new ArrayList<>();
  859. return tokenHistoryRepo.findByTokenIdOrderByCreatedAtDesc(tokenId);
  860. }
  861. public Page<UserHistory> userHistory(Long userId, Long toUserId, Long fromUserId, Pageable pageable) {
  862. Page<TokenHistory> page;
  863. if (ObjectUtils.isNotEmpty(toUserId)) {
  864. page = tokenHistoryRepo.userHistoryTo(userId, toUserId, pageable);
  865. } else if (ObjectUtils.isNotEmpty(fromUserId)) {
  866. page = tokenHistoryRepo.userHistoryFrom(userId, fromUserId, pageable);
  867. } else {
  868. page = tokenHistoryRepo.userHistory(userId, pageable);
  869. }
  870. Set<String> tokenIds = page.stream().map(TokenHistory::getTokenId).collect(Collectors.toSet());
  871. List<Asset> assets = tokenIds.isEmpty() ? new ArrayList<>() : assetRepo.findByTokenIdIn(tokenIds);
  872. return page.map(tokenHistory -> {
  873. UserHistory userHistory = new UserHistory();
  874. BeanUtils.copyProperties(tokenHistory, userHistory);
  875. Optional<Asset> asset = assets.stream().filter(a -> a.getTokenId().equals(tokenHistory.getTokenId()))
  876. .findAny();
  877. userHistory.setAssetName(asset.map(Asset::getName).orElse(null));
  878. userHistory.setPic(asset.map(Asset::getPic).orElse(new ArrayList<>()));
  879. switch (tokenHistory.getOperation()) {
  880. case "出售":
  881. case "转让":
  882. userHistory.setDescription(tokenHistory.getToUserId().equals(userId) ? "作品交易——买入" : "作品交易——售出");
  883. break;
  884. case "转赠":
  885. userHistory.setDescription(tokenHistory.getToUserId().equals(userId) ? "他人赠送" : "作品赠送");
  886. break;
  887. default:
  888. userHistory.setDescription(tokenHistory.getOperation());
  889. }
  890. return userHistory;
  891. });
  892. }
  893. public Page<UserHistory> userHistory(Long userId, PageQuery pageQuery) {
  894. Page<TokenHistory> page = tokenHistoryRepo.findAll(((root, criteriaQuery, criteriaBuilder) -> {
  895. List<Predicate> and = JpaUtils
  896. .toPredicates(pageQuery, TokenHistory.class, root, criteriaQuery, criteriaBuilder);
  897. Map<String, Object> query = pageQuery.getQuery();
  898. if (ObjectUtils.isEmpty(query.get("toUserId")) && ObjectUtils.isEmpty(query.get("fromUserId"))) {
  899. and.add(criteriaBuilder.or(criteriaBuilder.equal(root.get("toUserId"), userId), criteriaBuilder
  900. .equal(root.get("fromUserId"), userId)));
  901. } else {
  902. if (ObjectUtils.isNotEmpty(query.get("toUserId"))) {
  903. and.add(criteriaBuilder.and(criteriaBuilder.equal(root.get("toUserId"), userId)));
  904. } else {
  905. and.add(criteriaBuilder.and(criteriaBuilder.equal(root.get("fromUserId"), userId)));
  906. }
  907. }
  908. return criteriaBuilder.and(and.toArray(new Predicate[0]));
  909. }), JpaUtils.toPageRequest(pageQuery));
  910. Set<String> tokenIds = page.stream().map(TokenHistory::getTokenId).collect(Collectors.toSet());
  911. List<Asset> assets = tokenIds.isEmpty() ? new ArrayList<>() : assetRepo.findByTokenIdIn(tokenIds);
  912. return page.map(tokenHistory -> {
  913. UserHistory userHistory = new UserHistory();
  914. BeanUtils.copyProperties(tokenHistory, userHistory);
  915. Optional<Asset> asset = assets.stream().filter(a -> a.getTokenId().equals(tokenHistory.getTokenId()))
  916. .findAny();
  917. userHistory.setAssetName(asset.map(Asset::getName).orElse(null));
  918. userHistory.setPic(asset.map(Asset::getPic).orElse(new ArrayList<>()));
  919. switch (tokenHistory.getOperation()) {
  920. case "出售":
  921. case "转让":
  922. userHistory.setDescription(tokenHistory.getToUserId().equals(userId) ? "作品交易——买入" : "作品交易——售出");
  923. break;
  924. case "转赠":
  925. userHistory.setDescription(tokenHistory.getToUserId().equals(userId) ? "他人赠送" : "作品赠送");
  926. break;
  927. default:
  928. userHistory.setDescription(tokenHistory.getOperation());
  929. }
  930. return userHistory;
  931. });
  932. }
  933. public String mint(LocalDateTime time) {
  934. if (time == null) {
  935. time = LocalDateTime.now();
  936. }
  937. for (Asset asset : assetRepo.toMint(time)) {
  938. rocketMQTemplate.syncSend(generalProperties.getMintTopic(), asset.getId());
  939. }
  940. return "ok";
  941. }
  942. @Cacheable(value = "userStat", key = "#userId")
  943. public Map<String, BigDecimal> breakdown(Long userId) {
  944. // List<TokenHistory> page = tokenHistoryRepo.userHistory(userId);
  945. // BigDecimal sale = page.stream()
  946. // .filter(th -> th.getFromUserId().equals(userId) && ObjectUtils.isNotEmpty(th.getPrice()))
  947. // .map(TokenHistory::getPrice)
  948. // .reduce(BigDecimal.ZERO, BigDecimal::add);
  949. // BigDecimal buy = page.stream()
  950. // .filter(th -> th.getToUserId().equals(userId) && ObjectUtils.isNotEmpty(th.getPrice()))
  951. // .map(TokenHistory::getPrice)
  952. // .reduce(BigDecimal.ZERO, BigDecimal::add);
  953. Map<String, BigDecimal> map = new HashMap<>();
  954. map.put("sale", tokenHistoryRepo.userSale(userId));
  955. map.put("buy", rockRecordService.getRock(userId).getRecord());
  956. return map;
  957. }
  958. public void transferCDN() throws ExecutionException, InterruptedException {
  959. ForkJoinPool customThreadPool = new ForkJoinPool(100);
  960. customThreadPool.submit(() -> {
  961. collectionRepo.selectResource().parallelStream().forEach(list -> {
  962. for (int i = 0; i < list.size(); i++) {
  963. list.set(i, replaceCDN(list.get(i)));
  964. }
  965. collectionRepo.updateCDN(Long.parseLong(list.get(0)),
  966. list.get(1),
  967. list.get(2),
  968. list.get(3),
  969. list.get(4),
  970. list.get(5));
  971. });
  972. assetRepo.selectResource().parallelStream().forEach(list -> {
  973. for (int i = 0; i < list.size(); i++) {
  974. list.set(i, replaceCDN(list.get(i)));
  975. }
  976. assetRepo.updateCDN(Long.parseLong(list.get(0)),
  977. list.get(1),
  978. list.get(2),
  979. list.get(3),
  980. list.get(4),
  981. list.get(5));
  982. });
  983. }).get();
  984. }
  985. public String replaceCDN(String url) {
  986. if (url == null) return null;
  987. return url.replaceAll("https://raex-meta\\.oss-cn-shenzhen\\.aliyuncs\\.com",
  988. "https://cdn.raex.vip");
  989. }
  990. // @Scheduled(cron = "0 0 0/1 * * ?")
  991. // public void offTheShelf() {
  992. // LocalDateTime lastTime = LocalDateTime.now().minusHours(120);
  993. // Set<Long> assetIds = collectionRepo
  994. // .findResaleCollectionPriceOver20K(BigDecimal
  995. // .valueOf(20000L), CollectionSource.TRANSFER, lastTime, true);
  996. // assetIds.forEach(this::cancelConsignmentBySystem);
  997. // }
  998. @Scheduled(cron = "0 0 0/1 * * ?")
  999. public void offTheShelfAll() {
  1000. LocalDateTime lastTime = LocalDateTime.now().minusDays(15);
  1001. LocalDateTime newRuleStartTime = LocalDateTime.of(2022, 12, 9, 17, 0, 0);
  1002. Set<Long> assetIds = collectionRepo
  1003. .findResaleCollectionOverTime(lastTime, newRuleStartTime);
  1004. LocalDateTime newRuleLastTime = LocalDateTime.now().minusDays(30);
  1005. assetIds.addAll(collectionRepo
  1006. .findResaleCollectionOverTimeV2(newRuleLastTime, newRuleStartTime));
  1007. assetIds.forEach(this::cancelConsignmentBySystem);
  1008. }
  1009. public void cancelConsignmentBySystem(Long id) {
  1010. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  1011. cancelConsignmentBySystem(asset);
  1012. }
  1013. public void cancelConsignmentBySystem(Asset asset) {
  1014. try {
  1015. if (asset.getPublicCollectionId() != null) {
  1016. List<Order> orders = orderRepo.findByCollectionId(asset.getPublicCollectionId());
  1017. if (orders.stream().anyMatch(o -> o.getStatus() != OrderStatus.CANCELLED)) {
  1018. throw new BusinessException("已有订单不可取消");
  1019. }
  1020. collectionRepo.findById(asset.getPublicCollectionId())
  1021. .ifPresent(collection -> {
  1022. collection.setSalable(false);
  1023. collection.setOnShelf(false);
  1024. collectionRepo.save(collection);
  1025. });
  1026. }
  1027. asset.setConsignment(false);
  1028. asset.setPublicShow(false);
  1029. assetRepo.saveAndFlush(asset);
  1030. } catch (Exception e) {
  1031. log.info("自动下架报错,assetId:" + asset.getId());
  1032. }
  1033. }
  1034. // @Cacheable(cacheNames = "fmaa", key = "#userId+'#'+#mintActivityId+'#'+#pageable.hashCode()")
  1035. public PageWrapper<Asset> findMintActivityAssetsWrap(Long userId, Long mintActivityId, Long companyId, Pageable pageable) {
  1036. return PageWrapper.of(findMintActivityAssets(userId, mintActivityId, companyId, pageable));
  1037. }
  1038. public Page<Asset> findMintActivityAssets(Long userId, Long mintActivityId, Long companyId, Pageable pageable) {
  1039. MintActivity mintActivity = mintActivityRepo.findById(mintActivityId).orElse(null);
  1040. if (mintActivity == null) return new PageImpl<>(Collections.emptyList());
  1041. return findMintActivityAssetsCommon(userId, companyId, new CommonMatchDTO(mintActivity.getRule(),
  1042. mintActivity.isAudit(), mintActivity.getCollectionName()), pageable);
  1043. }
  1044. public Page<Asset> findMintActivityAssetsCommon(Long userId, Long companyId, CommonMatchDTO commonMatchDTO, Pageable pageable) {
  1045. if (!commonMatchDTO.isAudit()) {
  1046. Set<Tag> tags = commonMatchDTO.getRule().getTags();
  1047. if (tags.isEmpty()) return new PageImpl<>(Collections.emptyList());
  1048. return assetRepo.findAll((Specification<Asset>) (root, query, criteriaBuilder) ->
  1049. query.distinct(true).where(
  1050. // where userId=some id
  1051. criteriaBuilder.equal(root.get("userId"), userId),
  1052. // and (lockTo is null or (lockTo is not null and lockTo < now))
  1053. criteriaBuilder.or(criteriaBuilder.isNull(root.get("lockTo")),
  1054. criteriaBuilder.and(criteriaBuilder.isNotNull(root.get("lockTo")),
  1055. criteriaBuilder.lessThan(root.get("lockTo"), LocalDateTime.now()))),
  1056. // and status = 'NORMAL'
  1057. criteriaBuilder.equal(root.get("status"), AssetStatus.NORMAL),
  1058. criteriaBuilder.equal(root.get("consignment"), false),
  1059. // and has some tagId
  1060. root.join("tags").get("id").in(tags.stream().map(Tag::getId).toArray()))
  1061. .getRestriction(), pageable);
  1062. } else {
  1063. return assetRepo
  1064. .findByUserIdAndStatusAndCompanyIdAndNameLikeAndConsignment(userId, AssetStatus.NORMAL, companyId,
  1065. "%" + commonMatchDTO.getCollectionName() + "%", pageable, false);
  1066. }
  1067. }
  1068. public void destroy(Long id, Long userId, String tradeCode, OperationSource source) {
  1069. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  1070. if (!asset.getUserId().equals(userId)) {
  1071. throw new BusinessException("此藏品不属于你");
  1072. }
  1073. if (asset.getStatus() != AssetStatus.NORMAL) {
  1074. throw new BusinessException("当前状态不可销毁");
  1075. }
  1076. if (asset.isPublicShow()) {
  1077. throw new BusinessException("请先取消公开展示");
  1078. // cancelPublic(asset);
  1079. }
  1080. User user = userRepo.findById(userId).orElseThrow(new BusinessException("用户不存在"));
  1081. if (StringUtils.isEmpty(user.getTradeCode())) {
  1082. throw new BusinessException("未设置交易密码");
  1083. }
  1084. if (!passwordEncoder.matches(tradeCode, user.getTradeCode())) {
  1085. throw new BusinessException("交易密码错误");
  1086. }
  1087. User toUser = userRepo.findById(Constants.BLACK_HOLE_USER_ID).orElseThrow(new BusinessException("无记录"));
  1088. TokenHistory tokenHistory = TokenHistory.builder()
  1089. .tokenId(asset.getTokenId())
  1090. .fromUser(asset.getOwner())
  1091. .fromUserId(asset.getOwnerId())
  1092. .fromAvatar(asset.getOwnerAvatar())
  1093. .toUser(toUser.getNickname())
  1094. .toUserId(toUser.getId())
  1095. .toAvatar(toUser.getAvatar())
  1096. .operation(TransferReason.DESTROY.getDescription())
  1097. .price(null)
  1098. .companyId(asset.getCompanyId())
  1099. .build();
  1100. tokenHistoryRepo.save(tokenHistory);
  1101. asset.setPublicShow(false);
  1102. asset.setConsignment(false);
  1103. asset.setPublicCollectionId(null);
  1104. asset.setStatus(AssetStatus.DESTROYED);
  1105. asset.setOwner(toUser.getNickname());
  1106. asset.setOwnerId(toUser.getId());
  1107. asset.setOwnerAvatar(toUser.getAvatar());
  1108. assetRepo.saveAndFlush(asset);
  1109. //积分记录
  1110. destroyRecordRepo.save(DestroyRecord.builder()
  1111. .userId(userId)
  1112. .assetId(asset.getId())
  1113. .name(asset.getName())
  1114. .pic(asset.getPic().get(0).getUrl())
  1115. .record(1)
  1116. .type(RecordType.OBTAIN)
  1117. .companyId(asset.getCompanyId())
  1118. .source(source)
  1119. .build());
  1120. //加积分
  1121. userRepo.addDestroyPoint(userId, 1);
  1122. }
  1123. @Transactional
  1124. public void metaDestroyWithoutTradeCode(MetaDestroyParam metaDestroyParam, Long userId, OperationSource source) {
  1125. List<Long> ids = metaDestroyParam.getIds();
  1126. String remark = metaDestroyParam.getRemark();
  1127. if (CollectionUtils.isEmpty(ids)) {
  1128. throw new BusinessException("要销毁的藏品id为空");
  1129. }
  1130. ids.forEach(id -> {
  1131. destroyWithoutTradeCode(id, userId, source, remark);
  1132. });
  1133. }
  1134. public void destroyWithoutTradeCode(Long id, Long userId, OperationSource source, String remark) {
  1135. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  1136. if (!asset.getUserId().equals(userId)) {
  1137. throw new BusinessException("此藏品不属于该用户");
  1138. }
  1139. if (OperationSource.META.equals(source)) {
  1140. if (asset.getStatus() != AssetStatus.NORMAL) {
  1141. throw new BusinessException("当前状态不可销毁");
  1142. }
  1143. } else {
  1144. if (asset.getStatus() != AssetStatus.DESTROYING) {
  1145. throw new BusinessException("当前状态不可销毁");
  1146. }
  1147. }
  1148. if (asset.isPublicShow()) {
  1149. throw new BusinessException("请先取消公开展示");
  1150. // cancelPublic(asset);
  1151. }
  1152. User toUser = userRepo.findById(Constants.BLACK_HOLE_USER_ID).orElseThrow(new BusinessException("无记录"));
  1153. TokenHistory tokenHistory = TokenHistory.builder()
  1154. .tokenId(asset.getTokenId())
  1155. .fromUser(asset.getOwner())
  1156. .fromUserId(asset.getOwnerId())
  1157. .fromAvatar(asset.getOwnerAvatar())
  1158. .toUser(toUser.getNickname())
  1159. .toUserId(toUser.getId())
  1160. .toAvatar(toUser.getAvatar())
  1161. .operation(TransferReason.DESTROY.getDescription())
  1162. .price(null)
  1163. .companyId(asset.getCompanyId())
  1164. .build();
  1165. tokenHistoryRepo.save(tokenHistory);
  1166. asset.setPublicShow(false);
  1167. asset.setConsignment(false);
  1168. asset.setPublicCollectionId(null);
  1169. asset.setStatus(AssetStatus.DESTROYED);
  1170. asset.setOwner(toUser.getNickname());
  1171. asset.setOwnerId(toUser.getId());
  1172. asset.setOwnerAvatar(toUser.getAvatar());
  1173. assetRepo.saveAndFlush(asset);
  1174. //积分记录
  1175. DestroyRecord destroyRecord = DestroyRecord.builder()
  1176. .userId(userId)
  1177. .assetId(asset.getId())
  1178. .name(asset.getName())
  1179. .record(1)
  1180. .type(RecordType.OBTAIN)
  1181. .companyId(asset.getCompanyId())
  1182. .source(source)
  1183. .remark(remark)
  1184. .build();
  1185. destroyRecord
  1186. .setPic("video/mp4".equals(asset.getPic().get(0).getType()) ? asset.getPic().get(0).getThumb() : asset
  1187. .getPic().get(0).getUrl());
  1188. destroyRecordRepo.save(destroyRecord);
  1189. //加积分
  1190. userRepo.addDestroyPoint(userId, 1);
  1191. }
  1192. public double getRoyalties(Long minterId, double royalties, Long userId) {
  1193. if (royalties == 2) {
  1194. return 2;
  1195. }
  1196. LongArrayConverter converter = new LongArrayConverter();
  1197. String discountMinter = sysConfigService.getString("discount_minter");
  1198. List<Long> minterIds = converter.convertToEntityAttribute(discountMinter);
  1199. if (minterIds.contains(minterId)) {
  1200. String discountCollection = sysConfigService.getString("discount_collection");
  1201. List<Long> collectionIds = converter.convertToEntityAttribute(discountCollection);
  1202. Long assetId = assetRepo.findDiscount(userId, collectionIds);
  1203. if (ObjectUtils.isNotEmpty(assetId)) {
  1204. return 2;
  1205. }
  1206. }
  1207. return royalties;
  1208. }
  1209. public double getServicecharge(double servicecharge, Long userId) {
  1210. // if (servicecharge == 3) {
  1211. // return 3;
  1212. // }
  1213. LongArrayConverter converter = new LongArrayConverter();
  1214. // String discountMinter = sysConfigService.getString("discount_minter");
  1215. // List<Long> minterIds = converter.convertToEntityAttribute(discountMinter);
  1216. // if (minterIds.contains(minterId)) {
  1217. String discountCollection = sysConfigService.getString("discount_charge");
  1218. List<Long> collectionIds = converter.convertToEntityAttribute(discountCollection);
  1219. Long assetId = assetRepo.findDiscount(userId, collectionIds);
  1220. if (ObjectUtils.isNotEmpty(assetId)) {
  1221. return 3;
  1222. }
  1223. // }
  1224. return servicecharge;
  1225. }
  1226. public double getDomainServiceCharge(Long userId) {
  1227. // if (servicecharge == 3) {
  1228. // return 3;
  1229. // }
  1230. // LongArrayConverter converter = new LongArrayConverter();
  1231. // String discountMinter = sysConfigService.getString("discount_minter");
  1232. // List<Long> minterIds = converter.convertToEntityAttribute(discountMinter);
  1233. // if (minterIds.contains(minterId)) {
  1234. String discountCollection = sysConfigService.getString("domain_discount");
  1235. JSONObject json = JSONObject.parseObject(discountCollection);
  1236. Set<String> keys = json.keySet();
  1237. List<Long> collectionIds = keys.stream().map(Long::parseLong).collect(Collectors.toList());
  1238. Set<Long> holdCollections = assetRepo.findDiscounts(userId, collectionIds);
  1239. double result = sysConfigService.getBigDecimal("domain_service_charge").doubleValue();
  1240. if (holdCollections.size() > 0) {
  1241. for (Long id : holdCollections) {
  1242. double discount = json.getDouble(id.toString());
  1243. if (discount < result) {
  1244. result = discount;
  1245. }
  1246. }
  1247. }
  1248. // }
  1249. return result;
  1250. }
  1251. @Async
  1252. public void hcChain() throws ExecutionException, InterruptedException {
  1253. new ForkJoinPool(1000).submit(() -> {
  1254. AtomicInteger num = new AtomicInteger();
  1255. assetRepo.findByStatus(AssetStatus.NORMAL).parallelStream()
  1256. .forEach(asset -> {
  1257. if (asset.getHcTxHash() == null) {
  1258. User user = userRepo.findById(asset.getUserId()).orElse(null);
  1259. if (user != null) {
  1260. if (user.getHcChainAddress() == null) {
  1261. user.setHcChainAddress(hcChainService.createAccount(asset.getUserId()));
  1262. }
  1263. NFT nft = hcChainService.mint(user.getHcChainAddress(), asset.getTokenId());
  1264. asset.setHcTokenId(nft.getTokenId());
  1265. asset.setHcTxHash(nft.getTxHash());
  1266. asset.setGasUsed(nft.getGasUsed());
  1267. assetRepo.saveAndFlush(asset);
  1268. }
  1269. }
  1270. log.info("hcChain:" + num.getAndIncrement());
  1271. });
  1272. }).get();
  1273. }
  1274. public void lockAsset(Long userId, Long assetId, Duration duration) {
  1275. User user = userRepo.findById(userId).orElseThrow(new BusinessException("用户不存在"));
  1276. Asset asset = assetRepo.findById(assetId).orElseThrow(new BusinessException("藏品不存在"));
  1277. if (!asset.getUserId().equals(userId)) {
  1278. throw new BusinessException("无权限");
  1279. }
  1280. if (asset.getLockTo() != null && asset.getLockTo().isAfter(LocalDateTime.now())) {
  1281. throw new BusinessException("已是锁仓状态");
  1282. }
  1283. if (asset.getType() == CollectionType.SHOWROOM) {
  1284. throw new BusinessException("展厅不可锁定");
  1285. }
  1286. if (asset.getStatus() != AssetStatus.NORMAL) {
  1287. throw new BusinessException("当前状态不可锁定");
  1288. }
  1289. if (asset.isPublicShow() || asset.isConsignment()) {
  1290. throw new BusinessException("请先取消展示和寄售");
  1291. }
  1292. if (duration.compareTo(Duration.parse("P1D")) < 0) {
  1293. throw new BusinessException("最小锁定1天");
  1294. }
  1295. asset.setLockAt(LocalDateTime.now());
  1296. asset.setLockTo(asset.getLockAt().plus(duration));
  1297. assetRepo.saveAndFlush(asset);
  1298. assetLockRepo.save(AssetLock.builder()
  1299. .userId(userId)
  1300. .phone(user.getPhone())
  1301. .nickname(user.getNickname())
  1302. .assetId(assetId)
  1303. .name(asset.getName())
  1304. .number(asset.getNumber())
  1305. .lockAt(asset.getLockAt())
  1306. .lockTo(asset.getLockTo())
  1307. .duration(duration)
  1308. .build());
  1309. }
  1310. public List<TransactionTopTenDTO> transactionTopTen() {
  1311. LocalDateTime localDateTime = LocalDateTime.now().plusDays(-7);
  1312. List<Map<String, String>> map = orderRepo.transactionTopTen(localDateTime);
  1313. JSONArray jsonArray = new JSONArray();
  1314. if (CollectionUtil.isEmpty(map)) {
  1315. return null;
  1316. }
  1317. jsonArray.addAll(map);
  1318. List<TransactionTopTenDTO> transactionTopTenDTOS = jsonArray.toJavaList(TransactionTopTenDTO.class);
  1319. if (CollectionUtil.isEmpty(transactionTopTenDTOS)) {
  1320. return null;
  1321. }
  1322. transactionTopTenDTOS.forEach(transactionTopTenDTO -> {
  1323. if (Objects.nonNull(transactionTopTenDTO.getId())) {
  1324. if (transactionTopTenDTO.getSource().equals(CollectionSource.OFFICIAL)) {
  1325. Collection collection = collectionRepo.findById(transactionTopTenDTO.getId()).orElse(null);
  1326. if (Objects.nonNull(collection)) {
  1327. transactionTopTenDTO.setPic(collection.getPic());
  1328. }
  1329. }
  1330. if (transactionTopTenDTO.getSource().equals(CollectionSource.TRANSFER)) {
  1331. Asset asset = assetRepo.findById(transactionTopTenDTO.getId()).orElse(null);
  1332. if (Objects.nonNull(asset)) {
  1333. transactionTopTenDTO.setPic(asset.getPic());
  1334. }
  1335. }
  1336. }
  1337. transactionTopTenDTO.setId(null);
  1338. transactionTopTenDTO.setSource(null);
  1339. });
  1340. return transactionTopTenDTOS;
  1341. }
  1342. public List<FuAssetDTO> queryFu() {
  1343. Long userId = SecurityUtils.getAuthenticatedUser().getId();
  1344. String prefixName = sysConfigService.getString("fu_prefixName");
  1345. if (StringUtils.isBlank(prefixName)) {
  1346. throw new BusinessException("缺少福分类配置");
  1347. }
  1348. String[] split = prefixName.split(",");
  1349. List<String> prefixNames = Arrays.asList(split);
  1350. return assetRepo.queryFu(userId, Constants.META_NORMAL_STATUS, prefixNames);
  1351. }
  1352. }