AssetService.java 69 KB

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