AuctionActivityService.java 11 KB

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