AssetService.java 63 KB

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