AssetService.java 68 KB

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