AuctionActivityService.java 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. package com.izouma.nineth.service;
  2. import com.izouma.nineth.annotations.Debounce;
  3. import com.izouma.nineth.config.RedisKeys;
  4. import com.izouma.nineth.domain.Asset;
  5. import com.izouma.nineth.domain.AuctionActivity;
  6. import com.izouma.nineth.domain.Collection;
  7. import com.izouma.nineth.domain.User;
  8. import com.izouma.nineth.dto.PageQuery;
  9. import com.izouma.nineth.dto.auction.AuctionInputDTO;
  10. import com.izouma.nineth.enums.AssetStatus;
  11. import com.izouma.nineth.enums.AuctionSource;
  12. import com.izouma.nineth.enums.AuctionStatus;
  13. import com.izouma.nineth.enums.AuctionType;
  14. import com.izouma.nineth.exception.BusinessException;
  15. import com.izouma.nineth.repo.AssetRepo;
  16. import com.izouma.nineth.repo.AuctionActivityRepo;
  17. import com.izouma.nineth.repo.UserRepo;
  18. import com.izouma.nineth.utils.JpaUtils;
  19. import com.izouma.nineth.utils.SecurityUtils;
  20. import lombok.AllArgsConstructor;
  21. import lombok.extern.slf4j.Slf4j;
  22. import org.springframework.core.env.Environment;
  23. import org.springframework.data.domain.Page;
  24. import org.springframework.data.redis.core.BoundValueOperations;
  25. import org.springframework.data.redis.core.RedisTemplate;
  26. import org.springframework.scheduling.TaskScheduler;
  27. import org.springframework.security.crypto.password.PasswordEncoder;
  28. import org.springframework.stereotype.Service;
  29. import org.springframework.web.bind.annotation.RequestBody;
  30. import java.math.BigDecimal;
  31. import java.time.LocalDateTime;
  32. import java.time.ZoneId;
  33. import java.util.*;
  34. import java.util.concurrent.ScheduledFuture;
  35. import java.util.concurrent.TimeUnit;
  36. @Slf4j
  37. @Service
  38. @AllArgsConstructor
  39. public class AuctionActivityService {
  40. private final AuctionActivityRepo auctionActivityRepo;
  41. private final AssetRepo assetRepo;
  42. private final UserRepo userRepo;
  43. private final PasswordEncoder passwordEncoder;
  44. private final RedisTemplate<String, Object> redisTemplate;
  45. private final CacheService cacheService;
  46. private final TaskScheduler taskScheduler;
  47. private final Environment env;
  48. private final Map<Long, ScheduledFuture<?>> tasks = new HashMap<>();
  49. public Page<AuctionActivity> all(PageQuery pageQuery) {
  50. return auctionActivityRepo
  51. .findAll(JpaUtils.toSpecification(pageQuery, AuctionActivity.class), JpaUtils.toPageRequest(pageQuery));
  52. }
  53. public AuctionActivity createFromAsset(AuctionInputDTO dto) {
  54. Asset asset = assetRepo.findById(dto.getAssetId()).orElseThrow(new BusinessException("暂无"));
  55. AuctionActivity auctionActivity = new AuctionActivity();
  56. auctionActivity.setAuctionType(AuctionType.NFT);
  57. auctionActivity.setAssetId(dto.getAssetId());
  58. auctionActivity.setStatus(AuctionStatus.ONGOING);
  59. auctionActivity.setBids(0);
  60. auctionActivity.setCategory(asset.getCategory());
  61. auctionActivity.setEndTime(LocalDateTime.now().plusDays(5));
  62. auctionActivity.setDeposit(dto.getDeposit());
  63. if (Arrays.asList(env.getActiveProfiles()).contains("staging")) {
  64. auctionActivity.setEndTime(LocalDateTime.now().plusMinutes(5));
  65. }
  66. auctionActivity.setDetail(asset.getDetail());
  67. auctionActivity.setFixedPrice(dto.getFixedPrice());
  68. auctionActivity.setIncrement(dto.getIncrement());
  69. auctionActivity.setMinter(asset.getMinter());
  70. auctionActivity.setPic(asset.getPic());
  71. auctionActivity.setModel3d(asset.getModel3d());
  72. auctionActivity.setName(asset.getName());
  73. auctionActivity.setSeller(asset.getOwner());
  74. auctionActivity.setSellerId(asset.getOwnerId());
  75. auctionActivity.setStartTime(LocalDateTime.now());
  76. auctionActivity.setSource(AuctionSource.TRANSFER);
  77. auctionActivity.setServiceCharge(5);
  78. auctionActivity.setRoyalties(10);
  79. auctionActivity.setStartingPrice(dto.getStartingPrice());
  80. if (auctionActivity.getFixedPrice() != null) {
  81. auctionActivity.setHasFixedPrice(true);
  82. } else {
  83. auctionActivity.setHasFixedPrice(false);
  84. }
  85. return save(auctionActivity, dto.getTradeCode());
  86. }
  87. public AuctionActivity save(AuctionActivity record, String tradeCode) {
  88. if (record.getSource().equals(AuctionSource.OFFICIAL)) {
  89. record.setStatus(AuctionStatus.NOTSTARTED);
  90. }
  91. if (record.getSource().equals(AuctionSource.TRANSFER) & !record.getSellerId().equals(9859L)) {
  92. User user = userRepo.findById(record.getSellerId()).orElseThrow(new BusinessException("无用户信息"));
  93. if (!passwordEncoder.matches(tradeCode, user.getTradeCode())) {
  94. throw new BusinessException("交易密码错误");
  95. }
  96. }
  97. if (record.getAuctionType().equals(AuctionType.NFT) & !record.getSellerId().equals(9859L)) {
  98. Asset asset = assetRepo.findById(record.getAssetId()).orElseThrow(new BusinessException("未找到该藏品"));
  99. if (!asset.getOwnerId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
  100. throw new BusinessException("非本人藏品,无法操作.");
  101. }
  102. if (!asset.getStatus().equals(AssetStatus.NORMAL)) {
  103. throw new BusinessException("藏品状态异常,无法操作.");
  104. }
  105. if (asset.isConsignment()) {
  106. throw new BusinessException("藏品已寄售,取消寄售后再申请拍卖。");
  107. }
  108. asset.setStatus(AssetStatus.AUCTIONING);
  109. assetRepo.save(asset);
  110. }
  111. AuctionActivity saved = auctionActivityRepo.save(record);
  112. if (saved.getStatus().equals(AuctionStatus.NOTSTARTED)) {
  113. onShelfTask(saved);
  114. } else if (saved.getStatus().equals(AuctionStatus.ONGOING)) {
  115. offShelfTask(saved);
  116. }
  117. return saved;
  118. }
  119. public synchronized String changeStatus(Long id, AuctionStatus status) {
  120. BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps(RedisKeys.AUCTION_STATUS + id);
  121. if (ops.get() == null) {
  122. Boolean success = ops.setIfAbsent(Optional.ofNullable(auctionActivityRepo.getStatus(id))
  123. .orElse(AuctionStatus.NOTSTARTED.toString()), 7, TimeUnit.DAYS);
  124. log.info("创建redis拍卖活动状态:{}", success);
  125. }
  126. String stock = (String) ops.getAndSet(status.toString());
  127. syncStatus(id);
  128. return stock;
  129. }
  130. @Debounce(key = "#id", delay = 500)
  131. public void syncStatus(Long id) {
  132. String stock = (String) redisTemplate.opsForValue().get(RedisKeys.AUCTION_STATUS + id);
  133. if (stock != null) {
  134. log.info("同步拍卖活动状态信息{}", id);
  135. auctionActivityRepo.updateStatus(id, AuctionStatus.valueOf(stock));
  136. cacheService.clearAuction(id);
  137. }
  138. }
  139. private void onShelfTask(AuctionActivity record) {
  140. ScheduledFuture<?> task = tasks.get(record.getId());
  141. if (task != null) {
  142. if (!task.cancel(true)) {
  143. return;
  144. }
  145. }
  146. if (record.getStatus().equals(AuctionStatus.NOTSTARTED)) {
  147. if (record.getStartTime().minusSeconds(2).isAfter(LocalDateTime.now())) {
  148. Date date = Date.from(record.getStartTime().atZone(ZoneId.systemDefault()).toInstant());
  149. ScheduledFuture<?> future = taskScheduler.schedule(() -> {
  150. auctionActivityRepo.scheduleOnShelf(record.getId(), AuctionStatus.ONGOING);
  151. tasks.remove(record.getId());
  152. offShelfTask(record);
  153. }, date);
  154. tasks.put(record.getId(), future);
  155. } else {
  156. auctionActivityRepo.scheduleOnShelf(record.getId(), AuctionStatus.ONGOING);
  157. offShelfTask(record);
  158. }
  159. }
  160. }
  161. private void offShelfTask(AuctionActivity record) {
  162. ScheduledFuture<?> task = tasks.get(record.getId());
  163. if (task != null) {
  164. if (!task.cancel(true)) {
  165. return;
  166. }
  167. }
  168. AuctionActivity recordNew1 = auctionActivityRepo.findById(record.getId())
  169. .orElseThrow(new BusinessException("无数据"));
  170. if (recordNew1.getStatus().equals(AuctionStatus.ONGOING)) {
  171. if (recordNew1.getEndTime().minusSeconds(2).isAfter(LocalDateTime.now())) {
  172. Date date = Date.from(record.getEndTime().atZone(ZoneId.systemDefault()).toInstant());
  173. ScheduledFuture<?> future = taskScheduler.schedule(() -> {
  174. AuctionActivity nowRecord = auctionActivityRepo.findById(record.getId())
  175. .orElseThrow(new BusinessException("无数据"));
  176. if (nowRecord.getPurchasePrice() != null) {
  177. auctionActivityRepo.scheduleOffShelf(nowRecord.getId(), AuctionStatus.PURCHASED);
  178. } else {
  179. auctionActivityRepo.scheduleOffShelf(nowRecord.getId(), AuctionStatus.PASS);
  180. if (record.getAuctionType().equals(AuctionType.NFT)) {
  181. Asset asset = assetRepo.findById(nowRecord.getAssetId())
  182. .orElseThrow(new BusinessException("暂无"));
  183. asset.setStatus(AssetStatus.NORMAL);
  184. asset.setConsignment(false);
  185. asset.setPublicShow(false);
  186. assetRepo.save(asset);
  187. }
  188. }
  189. tasks.remove(record.getId());
  190. }, date);
  191. tasks.put(record.getId(), future);
  192. } else {
  193. if (recordNew1.getPurchasePrice() != null) {
  194. auctionActivityRepo.scheduleOffShelf(recordNew1.getId(), AuctionStatus.PURCHASED);
  195. } else {
  196. auctionActivityRepo.scheduleOffShelf(recordNew1.getId(), AuctionStatus.PASS);
  197. if (record.getAuctionType().equals(AuctionType.NFT)) {
  198. Asset asset = assetRepo.findById(recordNew1.getAssetId())
  199. .orElseThrow(new BusinessException("暂无"));
  200. asset.setStatus(AssetStatus.NORMAL);
  201. asset.setConsignment(false);
  202. asset.setPublicShow(false);
  203. assetRepo.save(asset);
  204. }
  205. }
  206. }
  207. }
  208. }
  209. }