AssetService.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. package com.izouma.nineth.service;
  2. import com.izouma.nineth.TokenHistory;
  3. import com.izouma.nineth.domain.Collection;
  4. import com.izouma.nineth.domain.*;
  5. import com.izouma.nineth.dto.PageQuery;
  6. import com.izouma.nineth.dto.UserHistory;
  7. import com.izouma.nineth.enums.AssetStatus;
  8. import com.izouma.nineth.enums.CollectionSource;
  9. import com.izouma.nineth.enums.CollectionType;
  10. import com.izouma.nineth.enums.OrderStatus;
  11. import com.izouma.nineth.event.TransferAssetEvent;
  12. import com.izouma.nineth.exception.BusinessException;
  13. import com.izouma.nineth.lock.RedisLockable;
  14. import com.izouma.nineth.repo.*;
  15. import com.izouma.nineth.utils.JpaUtils;
  16. import com.izouma.nineth.utils.SecurityUtils;
  17. import com.izouma.nineth.utils.TokenUtils;
  18. import lombok.AllArgsConstructor;
  19. import lombok.extern.slf4j.Slf4j;
  20. import org.apache.commons.lang3.StringUtils;
  21. import org.springframework.beans.BeanUtils;
  22. import org.springframework.context.ApplicationContext;
  23. import org.springframework.data.domain.Page;
  24. import org.springframework.data.domain.Pageable;
  25. import org.springframework.scheduling.annotation.Async;
  26. import org.springframework.stereotype.Service;
  27. import java.math.BigDecimal;
  28. import java.time.LocalDateTime;
  29. import java.time.temporal.ChronoUnit;
  30. import java.util.*;
  31. import java.util.stream.Collectors;
  32. @Service
  33. @AllArgsConstructor
  34. @Slf4j
  35. public class AssetService {
  36. private AssetRepo assetRepo;
  37. private UserRepo userRepo;
  38. private CollectionRepo collectionRepo;
  39. private ApplicationContext applicationContext;
  40. private OrderRepo orderRepo;
  41. private TokenHistoryRepo tokenHistoryRepo;
  42. private AssetMintService assetMintService;
  43. private SysConfigService sysConfigService;
  44. public Page<Asset> all(PageQuery pageQuery) {
  45. return assetRepo.findAll(JpaUtils.toSpecification(pageQuery, Asset.class), JpaUtils.toPageRequest(pageQuery));
  46. }
  47. public Asset createAsset(Collection collection, User user, Long orderId, BigDecimal price, String type, Integer number) {
  48. Asset asset = Asset.create(collection, user);
  49. asset.setTokenId(TokenUtils.genTokenId());
  50. asset.setNumber(number);
  51. asset.setOrderId(orderId);
  52. asset.setPrice(price);
  53. assetRepo.save(asset);
  54. tokenHistoryRepo.save(TokenHistory.builder()
  55. .tokenId(asset.getTokenId())
  56. .fromUser(collection.getMinter())
  57. .fromUserId(collection.getMinterId())
  58. .fromAvatar(collection.getMinterAvatar())
  59. .toUser(user.getNickname())
  60. .toUserId(user.getId())
  61. .toAvatar(user.getAvatar())
  62. .operation(type)
  63. .price(price)
  64. .build());
  65. assetMintService.mint(asset);
  66. return asset;
  67. }
  68. public Asset createAsset(BlindBoxItem winItem, User user, Long orderId, BigDecimal price, String type, Integer number) {
  69. Asset asset = Asset.create(winItem, user);
  70. asset.setTokenId(TokenUtils.genTokenId());
  71. asset.setNumber(number);
  72. asset.setOrderId(orderId);
  73. asset.setPrice(price);
  74. assetRepo.saveAndFlush(asset);
  75. tokenHistoryRepo.save(TokenHistory.builder()
  76. .tokenId(asset.getTokenId())
  77. .fromUser(winItem.getMinter())
  78. .fromUserId(winItem.getMinterId())
  79. .fromAvatar(winItem.getMinterAvatar())
  80. .toUser(user.getNickname())
  81. .toUserId(user.getId())
  82. .toAvatar(user.getAvatar())
  83. .operation(type)
  84. .price(price)
  85. .build());
  86. assetMintService.mint(asset.getId(), user.getId());
  87. return asset;
  88. }
  89. public void publicShow(Long id) {
  90. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  91. if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  92. throw new BusinessException("此藏品不属于你");
  93. }
  94. if (asset.isPublicShow()) {
  95. return;
  96. }
  97. if (asset.getStatus() != AssetStatus.NORMAL) {
  98. throw new BusinessException("当前状态不可展示");
  99. }
  100. User owner = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在"));
  101. Collection collection = Collection.builder()
  102. .name(asset.getName())
  103. .pic(asset.getPic())
  104. .minter(asset.getMinter())
  105. .minterId(asset.getMinterId())
  106. .minterAvatar(asset.getMinterAvatar())
  107. .owner(owner.getNickname())
  108. .ownerId(owner.getId())
  109. .ownerAvatar(owner.getAvatar())
  110. .detail(asset.getDetail())
  111. .type(CollectionType.DEFAULT)
  112. .source(CollectionSource.TRANSFER)
  113. .sale(0)
  114. .stock(1)
  115. .total(1)
  116. .onShelf(true)
  117. .salable(false)
  118. .price(BigDecimal.valueOf(0))
  119. .properties(asset.getProperties())
  120. .canResale(asset.isCanResale())
  121. .royalties(asset.getRoyalties())
  122. .serviceCharge(asset.getServiceCharge())
  123. .assetId(id)
  124. .number(asset.getNumber())
  125. .projectId(asset.getProjectId())
  126. .build();
  127. collectionRepo.save(collection);
  128. asset.setPublicShow(true);
  129. asset.setPublicCollectionId(collection.getId());
  130. assetRepo.save(asset);
  131. }
  132. public void consignment(Long id, BigDecimal price) {
  133. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  134. if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  135. throw new BusinessException("此藏品不属于你");
  136. }
  137. int holdDays = sysConfigService.getInt("hold_days");
  138. if (ChronoUnit.DAYS.between(asset.getCreatedAt(), LocalDateTime.now()) < holdDays) {
  139. throw new BusinessException("需持有满" + holdDays + "天才能寄售上架");
  140. }
  141. User owner = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在"));
  142. if (StringUtils.isBlank(owner.getSettleAccountId())) {
  143. throw new BusinessException("请先绑定银行卡");
  144. }
  145. if (asset.isConsignment()) {
  146. throw new BusinessException("已寄售,请勿重新操作");
  147. }
  148. if (asset.getStatus() != AssetStatus.NORMAL) {
  149. throw new BusinessException("当前状态不可寄售");
  150. }
  151. if (asset.isPublicShow()) {
  152. cancelPublic(asset);
  153. }
  154. Collection collection = Collection.builder()
  155. .name(asset.getName())
  156. .pic(asset.getPic())
  157. .minter(asset.getMinter())
  158. .minterId(asset.getMinterId())
  159. .minterAvatar(asset.getMinterAvatar())
  160. .owner(owner.getNickname())
  161. .ownerId(owner.getId())
  162. .ownerAvatar(owner.getAvatar())
  163. .detail(asset.getDetail())
  164. .type(CollectionType.DEFAULT)
  165. .source(CollectionSource.TRANSFER)
  166. .sale(0)
  167. .stock(1)
  168. .total(1)
  169. .onShelf(true)
  170. .salable(true)
  171. .price(price)
  172. .properties(asset.getProperties())
  173. .canResale(asset.isCanResale())
  174. .royalties(asset.getRoyalties())
  175. .serviceCharge(asset.getServiceCharge())
  176. .assetId(id)
  177. .number(asset.getNumber())
  178. .projectId(asset.getProjectId())
  179. .build();
  180. collectionRepo.save(collection);
  181. asset.setPublicShow(true);
  182. asset.setConsignment(true);
  183. asset.setPublicCollectionId(collection.getId());
  184. asset.setSellPrice(price);
  185. assetRepo.save(asset);
  186. }
  187. public void cancelConsignment(Long id) {
  188. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  189. if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  190. throw new BusinessException("此藏品不属于你");
  191. }
  192. cancelConsignment(asset);
  193. }
  194. public void cancelConsignment(Asset asset) {
  195. if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  196. throw new BusinessException("此藏品不属于你");
  197. }
  198. if (asset.getPublicCollectionId() != null) {
  199. List<Order> orders = orderRepo.findByCollectionId(asset.getPublicCollectionId());
  200. if (orders.stream().anyMatch(o -> o.getStatus() != OrderStatus.CANCELLED)) {
  201. throw new BusinessException("已有订单不可取消");
  202. }
  203. collectionRepo.findById(asset.getPublicCollectionId())
  204. .ifPresent(collection -> {
  205. collection.setSalable(false);
  206. collectionRepo.save(collection);
  207. });
  208. }
  209. asset.setConsignment(false);
  210. assetRepo.save(asset);
  211. }
  212. public void cancelPublic(Long id) {
  213. Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  214. if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  215. throw new BusinessException("此藏品不属于你");
  216. }
  217. cancelPublic(asset);
  218. }
  219. public void cancelPublic(Asset asset) {
  220. if (!asset.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  221. throw new BusinessException("此藏品不属于你");
  222. }
  223. if (!asset.isPublicShow()) {
  224. return;
  225. }
  226. if (asset.isConsignment()) {
  227. cancelConsignment(asset);
  228. }
  229. Collection collection = collectionRepo.findById(asset.getPublicCollectionId())
  230. .orElseThrow(new BusinessException("无展示记录"));
  231. collectionRepo.delete(collection);
  232. asset.setPublicShow(false);
  233. asset.setPublicCollectionId(null);
  234. assetRepo.save(asset);
  235. }
  236. public void usePrivilege(Long assetId, Long privilegeId) {
  237. Asset asset = assetRepo.findById(assetId).orElseThrow(new BusinessException("无记录"));
  238. asset.getPrivileges().stream().filter(p -> p.getId().equals(privilegeId)).forEach(p -> {
  239. p.setOpened(true);
  240. p.setOpenTime(LocalDateTime.now());
  241. p.setOpenedBy(SecurityUtils.getAuthenticatedUser().getId());
  242. });
  243. assetRepo.save(asset);
  244. }
  245. @Async
  246. public void transfer(Asset asset, BigDecimal price, User toUser, String reason, Long orderId) {
  247. Asset newAsset = new Asset();
  248. BeanUtils.copyProperties(asset, newAsset);
  249. newAsset.setId(null);
  250. newAsset.setUserId(toUser.getId());
  251. newAsset.setOwner(toUser.getNickname());
  252. newAsset.setOwnerId(toUser.getId());
  253. newAsset.setOwnerAvatar(toUser.getAvatar());
  254. newAsset.setPublicShow(false);
  255. newAsset.setConsignment(false);
  256. newAsset.setPublicCollectionId(null);
  257. newAsset.setStatus(AssetStatus.NORMAL);
  258. newAsset.setPrice(price);
  259. newAsset.setSellPrice(null);
  260. newAsset.setOrderId(orderId);
  261. newAsset.setFromAssetId(asset.getId());
  262. assetRepo.save(newAsset);
  263. tokenHistoryRepo.save(TokenHistory.builder()
  264. .tokenId(asset.getTokenId())
  265. .fromUser(asset.getOwner())
  266. .fromUserId(asset.getOwnerId())
  267. .fromAvatar(asset.getOwnerAvatar())
  268. .toUser(toUser.getNickname())
  269. .toUserId(toUser.getId())
  270. .toAvatar(toUser.getAvatar())
  271. .operation(reason)
  272. .price("转赠".equals(reason) ? null : price)
  273. .build());
  274. asset.setPublicShow(false);
  275. asset.setConsignment(false);
  276. asset.setPublicCollectionId(null);
  277. asset.setStatus("转赠".equals(reason) ? AssetStatus.GIFTED : AssetStatus.TRANSFERRED);
  278. asset.setOwner(toUser.getNickname());
  279. asset.setOwnerId(toUser.getId());
  280. asset.setOwnerAvatar(toUser.getAvatar());
  281. assetRepo.save(asset);
  282. if (orderId != null) {
  283. applicationContext.publishEvent(new TransferAssetEvent(this, true, newAsset));
  284. }
  285. }
  286. public List<TokenHistory> tokenHistory(String tokenId, Long assetId) {
  287. if (tokenId == null) {
  288. if (assetId == null) return new ArrayList<>();
  289. tokenId = assetRepo.findById(assetId).map(Asset::getTokenId).orElse(null);
  290. }
  291. if (tokenId == null) return new ArrayList<>();
  292. return tokenHistoryRepo.findByTokenIdOrderByCreatedAtDesc(tokenId);
  293. }
  294. @RedisLockable(key = "#id", expiration = 60, isWaiting = true)
  295. public void testLock(String id, String i) throws InterruptedException {
  296. Thread.sleep(1000);
  297. log.info("" + i);
  298. }
  299. public void setHistory() {
  300. List<Asset> assets = assetRepo.findByCreatedAtBefore(LocalDateTime.of(2021, 11, 22, 23, 59, 59));
  301. assets.parallelStream().forEach(asset -> {
  302. try {
  303. User owner = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException(""));
  304. Order order = orderRepo.findById(asset.getOrderId()).orElseThrow(new BusinessException(""));
  305. TokenHistory t = TokenHistory.builder()
  306. .tokenId(asset.getTokenId())
  307. .fromUser(asset.getMinter())
  308. .fromUserId(asset.getMinterId())
  309. .fromAvatar(asset.getMinterAvatar())
  310. .toUser(owner.getNickname())
  311. .toUserId(owner.getId())
  312. .toAvatar(owner.getAvatar())
  313. .operation("出售")
  314. .price(order.getPrice())
  315. .build();
  316. t.setCreatedAt(asset.getCreatedAt());
  317. tokenHistoryRepo.save(t);
  318. } catch (Exception e) {
  319. }
  320. });
  321. }
  322. public Page<UserHistory> userHistory(Long userId, Pageable pageable) {
  323. Page<TokenHistory> page = tokenHistoryRepo.userHistory(userId, pageable);
  324. Set<String> tokenIds = page.stream().map(TokenHistory::getTokenId).collect(Collectors.toSet());
  325. List<Asset> assets = tokenIds.isEmpty() ? new ArrayList<>() : assetRepo.findByTokenIdIn(tokenIds);
  326. return page.map(tokenHistory -> {
  327. UserHistory userHistory = new UserHistory();
  328. BeanUtils.copyProperties(tokenHistory, userHistory);
  329. Optional<Asset> asset = assets.stream().filter(a -> a.getTokenId().equals(tokenHistory.getTokenId()))
  330. .findAny();
  331. userHistory.setAssetName(asset.map(Asset::getName).orElse(null));
  332. userHistory.setPic(asset.map(Asset::getPic).orElse(new ArrayList<>()));
  333. switch (tokenHistory.getOperation()) {
  334. case "出售":
  335. case "转让":
  336. userHistory.setDescription(tokenHistory.getToUserId().equals(userId) ? "作品交易——买入" : "作品交易——售出");
  337. break;
  338. case "空投":
  339. userHistory.setDescription("空投");
  340. break;
  341. case "转赠":
  342. userHistory.setDescription(tokenHistory.getToUserId().equals(userId) ? "他人赠送" : "作品赠送");
  343. break;
  344. }
  345. return userHistory;
  346. });
  347. }
  348. public Map<User, Integer> holdQuery(List<String> names, LocalDateTime startTime, LocalDateTime endTime) {
  349. Map<User, Integer> result = new HashMap<>();
  350. userRepo.findAll().stream().parallel().forEach(user -> {
  351. List<Asset> assets = assetRepo.findByUserId(user.getId());
  352. assets = assets.stream().filter(a -> names.stream().anyMatch(n -> n.equals(a.getName())))
  353. .collect(Collectors.toList());
  354. if (assets.size() < names.size()) {
  355. return;
  356. }
  357. assets = assets.stream().filter(a -> a.getCreatedAt().isBefore(startTime))
  358. .collect(Collectors.toList());
  359. if (assets.size() < names.size()) {
  360. return;
  361. }
  362. assets = assets.stream().filter(a -> {
  363. if (a.getStatus() != AssetStatus.GIFTED && a.getStatus() != AssetStatus.TRANSFERRED) {
  364. return true;
  365. } else {
  366. Asset a1 = assetRepo.findFirstByTokenIdAndCreatedAtAfterOrderByCreatedAt(a.getTokenId(), a.getCreatedAt());
  367. return a1 != null && a.getCreatedAt().isAfter(endTime);
  368. }
  369. })
  370. .collect(Collectors.toList());
  371. boolean flag = true;
  372. Map<String, Integer> map = new HashMap<>();
  373. for (String name : names) {
  374. int c = (int) assets.stream().filter(a -> name.equals(a.getName())).count();
  375. map.put(name, c);
  376. flag = flag && (c > 0);
  377. }
  378. if (flag) {
  379. result.put(user, map.values().stream().mapToInt(i -> i).min().orElse(0));
  380. }
  381. });
  382. return result;
  383. }
  384. }