AuctionOrderService.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. package com.izouma.nineth.service;
  2. import com.izouma.nineth.config.RedisKeys;
  3. import com.izouma.nineth.domain.*;
  4. import com.izouma.nineth.dto.PageQuery;
  5. import com.izouma.nineth.enums.*;
  6. import com.izouma.nineth.exception.BusinessException;
  7. import com.izouma.nineth.repo.*;
  8. import com.izouma.nineth.utils.JpaUtils;
  9. import com.izouma.nineth.utils.SecurityUtils;
  10. import com.izouma.nineth.utils.SnowflakeIdWorker;
  11. import lombok.AllArgsConstructor;
  12. import lombok.extern.slf4j.Slf4j;
  13. import org.apache.commons.lang3.ObjectUtils;
  14. import org.springframework.data.domain.Page;
  15. import org.springframework.data.redis.core.BoundValueOperations;
  16. import org.springframework.data.redis.core.RedisTemplate;
  17. import org.springframework.scheduling.annotation.Scheduled;
  18. import org.springframework.stereotype.Service;
  19. import java.math.BigDecimal;
  20. import java.time.LocalDateTime;
  21. import java.util.Arrays;
  22. import java.util.List;
  23. import java.util.Optional;
  24. import java.util.concurrent.TimeUnit;
  25. @Slf4j
  26. @Service
  27. @AllArgsConstructor
  28. public class AuctionOrderService {
  29. private AuctionOrderRepo auctionOrderRepo;
  30. private SysConfigService sysConfigService;
  31. private UserRepo userRepo;
  32. private AssetService assetService;
  33. private AuctionActivityRepo auctionActivityRepo;
  34. private AuctionRecordRepo auctionRecordRepo;
  35. private AssetRepo assetRepo;
  36. private UserAddressRepo userAddressRepo;
  37. private AuctionActivityService auctionActivityService;
  38. private RedisTemplate<String, Object> redisTemplate;
  39. private SnowflakeIdWorker snowflakeIdWorker;
  40. public Page<AuctionOrder> all(PageQuery pageQuery) {
  41. return auctionOrderRepo
  42. .findAll(JpaUtils.toSpecification(pageQuery, AuctionOrder.class), JpaUtils.toPageRequest(pageQuery));
  43. }
  44. public AuctionOrder create(Long userId, Long auctionId, Long addressId, Long auctionRecordId, AuctionPaymentType type) {
  45. User user = userRepo.findById(userId).orElseThrow(new BusinessException("无用户"));
  46. AuctionActivity auction = auctionActivityRepo.findById(auctionId)
  47. .orElseThrow(new BusinessException("无拍卖信息"));
  48. String status = (String) redisTemplate.opsForValue().get(RedisKeys.AUCTION_STATUS + auctionId);
  49. if (status == null)
  50. status = auction.getStatus().toString();
  51. switch (AuctionStatus.valueOf(status)) {
  52. case NOTSTARTED:
  53. throw new BusinessException("拍卖还未开始");
  54. // case PURCHASED:
  55. // throw new BusinessException("拍卖成交中");
  56. case PASS:
  57. throw new BusinessException("已经流拍");
  58. case FINISH:
  59. throw new BusinessException("拍卖已结束");
  60. case FIXED_PRICE_PURCHASED:
  61. if (AuctionPaymentType.FIXED_PRICE.equals(type)) {
  62. throw new BusinessException("一口价成交中");
  63. }
  64. }
  65. if (user.getId().equals(auction.getSellerId())) {
  66. throw new BusinessException("不可自己出价自己的");
  67. }
  68. if (AuctionPaymentType.DEPOSIT.equals(type)) {
  69. return this.createDeposit(user, auction);
  70. }
  71. if (AuctionPaymentType.PURCHASE_PRICE.equals(type)) {
  72. if (auction.getEndTime().isAfter(LocalDateTime.now())) {
  73. throw new BusinessException("拍卖还未结束");
  74. }
  75. AuctionRecord record = auctionRecordRepo.findTopByAuctionIdAndUserIdOrderByIdDesc(auctionId, userId);
  76. if (ObjectUtils.isEmpty(record) || !record.isPayDeposit()) {
  77. throw new BusinessException("未支付保证金");
  78. }
  79. if (record.getBidderPrice().compareTo(auction.getPurchasePrice()) != 0) {
  80. throw new BusinessException("与成交价不否");
  81. }
  82. int time = sysConfigService.getInt("auction_cancel_time");
  83. if (LocalDateTime.now().isAfter(auction.getEndTime().plusMinutes(time))) {
  84. throw new BusinessException("超过支付时长");
  85. }
  86. }
  87. UserAddress userAddress = null;
  88. if (addressId != null) {
  89. userAddress = userAddressRepo.findById(addressId).orElseThrow(new BusinessException("地址信息不存在"));
  90. }
  91. try {
  92. // auction.setStatus(AuctionStatus.PURCHASED);
  93. // auctionActivityRepo.save(auction);
  94. auctionActivityService.changeStatus(auctionId, AuctionPaymentType.FIXED_PRICE
  95. .equals(type) ? AuctionStatus.FIXED_PRICE_PURCHASED : AuctionStatus.PURCHASED);
  96. if (AuctionSource.TRANSFER.equals(auction.getSource())) {
  97. Asset asset = assetRepo.findById(auction.getAssetId()).orElseThrow(new BusinessException("资产不存在"));
  98. asset.setStatus(AssetStatus.AUCTIONING);
  99. assetRepo.save(asset);
  100. }
  101. BigDecimal price = AuctionPaymentType.FIXED_PRICE.equals(type) ? auction.getFixedPrice() : auction
  102. .getPurchasePrice();
  103. AuctionOrder order = AuctionOrder.builder()
  104. .id(snowflakeIdWorker.nextId())
  105. .auctionId(auction.getId())
  106. .userId(user.getId())
  107. .nickname(user.getNickname())
  108. .paymentType(type)
  109. .name(auction.getName())
  110. .pic(auction.getPic())
  111. .serviceCharge(auction.getServiceCharge())
  112. .royalties(auction.getRoyalties())
  113. .source(auction.getSource())
  114. .price(price)
  115. .totalPrice(price)
  116. .auctionRecordId(auctionRecordId)
  117. .status(AuctionOrderStatus.NOT_PAID)
  118. .contactName(Optional.ofNullable(userAddress).map(UserAddress::getName).orElse(null))
  119. .contactPhone(Optional.ofNullable(userAddress).map(UserAddress::getPhone).orElse(null))
  120. .address(Optional.ofNullable(userAddress).map(UserAddress::getDetail).orElse(null))
  121. .build();
  122. return auctionOrderRepo.save(order);
  123. } catch (Exception e) {
  124. auctionActivityService.changeStatus(auctionId, AuctionStatus.ONGOING);
  125. throw e;
  126. }
  127. }
  128. public AuctionOrder createDeposit(User user, AuctionActivity auction) {
  129. if (user.getId().equals(auction.getSellerId())) {
  130. throw new BusinessException("不可自己出价自己的");
  131. }
  132. //保证金
  133. // BigDecimal deposit = sysConfigService.getBigDecimal("deposit");
  134. AuctionOrder order = auctionOrderRepo
  135. .findByUserIdAndAuctionIdAndPaymentTypeAndStatusIn(user.getId(), auction.getId(),
  136. AuctionPaymentType.DEPOSIT, Arrays
  137. .asList(AuctionOrderStatus.NOT_PAID, AuctionOrderStatus.FINISH));
  138. if (ObjectUtils.isNotEmpty(order)) {
  139. if (AuctionOrderStatus.FINISH.equals(order.getStatus())) {
  140. throw new BusinessException("保证金已交过,无需再交");
  141. }
  142. throw new BusinessException("保证金未支付,取消后再重新出价");
  143. }
  144. AuctionRecord auctionRecord = AuctionRecord.builder()
  145. .auctionId(auction.getId())
  146. .type(AuctionRecordType.DEPOSIT)
  147. .bidderPrice(auction.getDeposit())
  148. .auctionPic(null)
  149. .userId(SecurityUtils.getAuthenticatedUser().getId())
  150. .avatar(SecurityUtils.getAuthenticatedUser().getAvatar())
  151. .name(auction.getName())
  152. .purchased(false)
  153. .build();
  154. AuctionRecord record = auctionRecordRepo.save(auctionRecord);
  155. order = AuctionOrder.builder()
  156. .id(snowflakeIdWorker.nextId())
  157. .auctionId(auction.getId())
  158. .userId(user.getId())
  159. .nickname(user.getNickname())
  160. .paymentType(AuctionPaymentType.DEPOSIT)
  161. .name(auction.getName())
  162. .pic(auction.getPic())
  163. .serviceCharge(auction.getServiceCharge())
  164. .royalties(auction.getRoyalties())
  165. .source(auction.getSource())
  166. .price(auction.getDeposit())
  167. .totalPrice(auction.getDeposit())
  168. .auctionRecordId(record.getId())
  169. .status(AuctionOrderStatus.NOT_PAID)
  170. .build();
  171. return auctionOrderRepo.save(order);
  172. }
  173. public void notify(Long id, PayMethod payMethod, String transactionId) {
  174. AuctionOrder order = auctionOrderRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  175. if (!order.getStatus().equals(AuctionOrderStatus.NOT_PAID)) {
  176. throw new BusinessException("订单已处理");
  177. }
  178. AuctionActivity auction = auctionActivityRepo.findById(order.getAuctionId())
  179. .orElseThrow(new BusinessException("无拍卖活动"));
  180. if (auction.getAuctionType().equals(AuctionType.ENTITY)) {
  181. order.setStatus(AuctionOrderStatus.DELIVERY);
  182. } else {
  183. order.setStatus(AuctionOrderStatus.FINISH);
  184. }
  185. order.setPayMethod(payMethod);
  186. order.setTransactionId(transactionId);
  187. order.setPayTime(LocalDateTime.now());
  188. //存订单
  189. auctionOrderRepo.save(order);
  190. if (AuctionPaymentType.DEPOSIT.equals(order.getPaymentType())) {
  191. //改出价记录表
  192. AuctionRecord record = auctionRecordRepo.findById(order.getAuctionRecordId())
  193. .orElseThrow(new BusinessException("无出价记录"));
  194. record.setPayDeposit(true);
  195. auctionRecordRepo.save(record);
  196. return;
  197. }
  198. //此拍卖结束
  199. // auction.setStatus(AuctionStatus.FINISH);
  200. // auctionActivityRepo.save(auction);
  201. auctionActivityService.changeStatus(order.getAuctionId(), AuctionStatus.FINISH);
  202. if (AuctionSource.TRANSFER.equals(order.getSource())) {
  203. Asset asset = assetRepo.findById(auction.getAssetId()).orElseThrow(new BusinessException("资产不存在"));
  204. if (asset.isPublicShow()) {
  205. //取消公开展示
  206. assetService.cancelPublic(asset);
  207. }
  208. User user = userRepo.findById(order.getUserId()).orElseThrow(new BusinessException("无用户"));
  209. //转让流程
  210. assetService.transfer(asset, order.getTotalPrice(), user, TransferReason.AUCTION, order.getId());
  211. }
  212. //该出价记录表为竞得
  213. AuctionRecord record = auctionRecordRepo.findById(order.getAuctionRecordId())
  214. .orElseThrow(new BusinessException("无出价记录"));
  215. record.setPurchased(true);
  216. auctionRecordRepo.save(record);
  217. //退保证金
  218. List<AuctionOrder> orders = auctionOrderRepo.findAllByAuctionIdAndPaymentTypeAndStatus(order.getAuctionId(),
  219. AuctionPaymentType.DEPOSIT, AuctionOrderStatus.FINISH);
  220. orders.forEach(o -> {
  221. //退款(暂未写)
  222. o.setRefundTime(LocalDateTime.now());
  223. o.setStatus(AuctionOrderStatus.REFUNDING);
  224. auctionOrderRepo.save(o);
  225. });
  226. }
  227. public void cancel(AuctionOrder order) {
  228. if (!getOrderLock(order.getId())) {
  229. log.error("订单取消失败 {}, redis锁了", order.getId());
  230. return;
  231. }
  232. try {
  233. AuctionActivity auction = auctionActivityRepo.findById(order.getAuctionId())
  234. .orElseThrow(new BusinessException("无记录"));
  235. if (AuctionPaymentType.PURCHASE_PRICE.equals(order.getPaymentType())) {
  236. //如果是拍卖,需获取取消订单的时长
  237. int time = sysConfigService.getInt("auction_cancel_time");
  238. if (LocalDateTime.now().isAfter(auction.getEndTime().plusMinutes(time))) {
  239. //超过支付时长
  240. // auction.setStatus(AuctionStatus.PASS);
  241. // auctionActivityRepo.save(auction);
  242. auctionActivityService.changeStatus(order.getAuctionId(), AuctionStatus.PASS);
  243. //退其余保证金
  244. List<AuctionOrder> orders = auctionOrderRepo
  245. .findAllByAuctionIdAndPaymentTypeAndStatus(order.getAuctionId(),
  246. AuctionPaymentType.DEPOSIT, AuctionOrderStatus.FINISH);
  247. orders.stream()
  248. .filter(o -> !order.getUserId().equals(o.getUserId()))
  249. .forEach(o -> {
  250. //退款(暂未写)
  251. o.setRefundTime(LocalDateTime.now());
  252. o.setStatus(AuctionOrderStatus.REFUNDING);
  253. auctionOrderRepo.save(o);
  254. });
  255. }
  256. } else if (AuctionPaymentType.DEPOSIT.equals(order.getPaymentType())) {
  257. //删除出价记录
  258. auctionRecordRepo.softDelete(order.getAuctionRecordId());
  259. } else {
  260. //拍卖是否结束
  261. if (LocalDateTime.now().isBefore(auction.getEndTime())) {
  262. //返回拍卖状态
  263. // auction.setStatus(AuctionStatus.ONGOING);
  264. auctionActivityService.changeStatus(order.getAuctionId(), AuctionStatus.ONGOING);
  265. } else {
  266. // auction.setStatus(AuctionStatus.PASS);
  267. //退还保证金
  268. List<AuctionOrder> orders = auctionOrderRepo
  269. .findAllByAuctionIdAndPaymentTypeAndStatus(order.getAuctionId(),
  270. AuctionPaymentType.DEPOSIT, AuctionOrderStatus.FINISH);
  271. orders.forEach(o -> {
  272. //退款(暂未写)
  273. o.setRefundTime(LocalDateTime.now());
  274. o.setStatus(AuctionOrderStatus.REFUNDING);
  275. auctionOrderRepo.save(o);
  276. });
  277. }
  278. // auctionActivityRepo.save(auction);
  279. auctionActivityService.changeStatus(order.getAuctionId(), AuctionStatus.PASS);
  280. }
  281. if (AuctionSource.TRANSFER.equals(order.getSource())) {
  282. //改回资产状态
  283. Asset asset = assetRepo.findById(auction.getAssetId()).orElseThrow(new BusinessException("资产不存在"));
  284. asset.setStatus(AssetStatus.NORMAL);
  285. assetRepo.save(asset);
  286. }
  287. order.setStatus(AuctionOrderStatus.CANCELLED);
  288. order.setCancelTime(LocalDateTime.now());
  289. auctionOrderRepo.save(order);
  290. log.info("取消订单{}", order.getId());
  291. } catch (Exception e) {
  292. log.error("订单取消错误 orderId: " + order.getId(), e);
  293. }
  294. releaseOrderLock(order.getId());
  295. }
  296. public boolean getOrderLock(Long orderId) {
  297. BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps(RedisKeys.AUCTION_ORDER_LOCK + orderId);
  298. Boolean flag = ops.setIfAbsent(1, 1, TimeUnit.DAYS);
  299. return Boolean.TRUE.equals(flag);
  300. }
  301. public void releaseOrderLock(Long orderId) {
  302. redisTemplate.delete(RedisKeys.AUCTION_ORDER_LOCK + orderId);
  303. }
  304. @Scheduled(cron = "0 0/30 * * * ?")
  305. public void passOverTimeAuction() {
  306. List<AuctionActivity> purchased = auctionActivityRepo.findAllByStatus(AuctionStatus.PURCHASED);
  307. if (purchased != null) {
  308. purchased.forEach(act -> {
  309. List<AuctionOrder> auctionOrders = auctionOrderRepo.findAllByAuctionIdAndPaymentTypeAndStatus(act
  310. .getId(), AuctionPaymentType.PURCHASE_PRICE, AuctionOrderStatus.NOT_PAID);
  311. auctionOrders.forEach(this::cancel);
  312. act.setStatus(AuctionStatus.PASS);
  313. auctionActivityRepo.save(act);
  314. });
  315. }
  316. }
  317. /**
  318. * 发货
  319. *
  320. * @param id 编号
  321. * @param courierId 快递单号
  322. */
  323. public void dispatch(Long id, String courierId) {
  324. AuctionOrder auctionOrder = auctionOrderRepo.findById(id).orElseThrow(new BusinessException("铸造订单不存在"));
  325. auctionOrder.setStatus(AuctionOrderStatus.RECEIVE);
  326. auctionOrder.setCourierId(courierId);
  327. auctionOrderRepo.save(auctionOrder);
  328. }
  329. /**
  330. * 订单
  331. *
  332. * @param id 编号
  333. */
  334. public void finish(Long id) {
  335. AuctionOrder auctionOrder = auctionOrderRepo.findById(id).orElseThrow(new BusinessException("铸造订单不存在"));
  336. auctionOrder.setStatus(AuctionOrderStatus.FINISH);
  337. auctionOrderRepo.save(auctionOrder);
  338. }
  339. }