AuctionOrderService.java 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. package com.izouma.nineth.service;
  2. import com.izouma.nineth.config.Constants;
  3. import com.izouma.nineth.config.RedisKeys;
  4. import com.izouma.nineth.domain.*;
  5. import com.izouma.nineth.dto.PageQuery;
  6. import com.izouma.nineth.enums.*;
  7. import com.izouma.nineth.exception.BusinessException;
  8. import com.izouma.nineth.repo.*;
  9. import com.izouma.nineth.service.sms.SmsService;
  10. import com.izouma.nineth.utils.JpaUtils;
  11. import com.izouma.nineth.utils.SecurityUtils;
  12. import com.izouma.nineth.utils.SnowflakeIdWorker;
  13. import lombok.extern.slf4j.Slf4j;
  14. import org.apache.commons.lang3.ObjectUtils;
  15. import org.apache.commons.lang3.StringUtils;
  16. import org.springframework.beans.factory.annotation.Autowired;
  17. import org.springframework.context.annotation.Lazy;
  18. import org.springframework.data.domain.Page;
  19. import org.springframework.data.redis.core.BoundValueOperations;
  20. import org.springframework.data.redis.core.RedisTemplate;
  21. import org.springframework.scheduling.annotation.Scheduled;
  22. import org.springframework.stereotype.Service;
  23. import javax.persistence.Transient;
  24. import java.math.BigDecimal;
  25. import java.math.RoundingMode;
  26. import java.time.LocalDateTime;
  27. import java.util.Arrays;
  28. import java.util.List;
  29. import java.util.Optional;
  30. import java.util.concurrent.TimeUnit;
  31. @Slf4j
  32. @Service
  33. public class AuctionOrderService {
  34. @Autowired
  35. private AuctionOrderRepo auctionOrderRepo;
  36. @Autowired
  37. private SysConfigService sysConfigService;
  38. @Autowired
  39. private UserRepo userRepo;
  40. @Autowired
  41. private AssetService assetService;
  42. @Autowired
  43. private AuctionActivityRepo auctionActivityRepo;
  44. @Autowired
  45. private AuctionRecordRepo auctionRecordRepo;
  46. @Autowired
  47. private AssetRepo assetRepo;
  48. @Autowired
  49. private UserAddressRepo userAddressRepo;
  50. @Autowired
  51. private AuctionActivityService auctionActivityService;
  52. @Autowired
  53. private RedisTemplate<String, Object> redisTemplate;
  54. @Autowired
  55. private SnowflakeIdWorker snowflakeIdWorker;
  56. @Autowired
  57. private AuctionPassRecordRepo auctionPassRecordRepo;
  58. @Lazy
  59. @Autowired
  60. private OrderPayService orderPayService;
  61. @Autowired
  62. private SmsService smsService;
  63. @Autowired
  64. private UserBalanceService userBalanceService;
  65. @Autowired
  66. private ShowroomService showroomService;
  67. @Autowired
  68. private CollectionRepo collectionRepo;
  69. @Autowired
  70. private ShowroomRepo showroomRepo;
  71. @Autowired
  72. private UserBalanceRepo userBalanceRepo;
  73. public Page<AuctionOrder> all(PageQuery pageQuery) {
  74. return auctionOrderRepo
  75. .findAll(JpaUtils.toSpecification(pageQuery, AuctionOrder.class), JpaUtils.toPageRequest(pageQuery));
  76. }
  77. public AuctionOrder create(Long userId, Long auctionId, Long addressId, Long auctionRecordId, AuctionPaymentType type) {
  78. User user = userRepo.findById(userId).orElseThrow(new BusinessException("无用户"));
  79. AuctionActivity auction = auctionActivityRepo.findById(auctionId)
  80. .orElseThrow(new BusinessException("无拍卖信息"));
  81. if (!auction.isOnShelf()) {
  82. throw new BusinessException("拍卖已结束");
  83. }
  84. String status = (String) redisTemplate.opsForValue().get(RedisKeys.AUCTION_STATUS + auctionId);
  85. if (status == null)
  86. status = auction.getStatus().toString();
  87. switch (AuctionStatus.valueOf(status)) {
  88. case NOTSTARTED:
  89. throw new BusinessException("拍卖还未开始");
  90. // case PURCHASED:
  91. // throw new BusinessException("拍卖成交中");
  92. case PASS:
  93. throw new BusinessException("已经流拍");
  94. case FINISH:
  95. throw new BusinessException("拍卖已结束");
  96. case FIXED_PRICE_PURCHASED:
  97. if (AuctionPaymentType.FIXED_PRICE.equals(type)) {
  98. throw new BusinessException("一口价成交中");
  99. }
  100. }
  101. if (user.getId().equals(auction.getSellerId())) {
  102. throw new BusinessException("不可自己出价自己");
  103. }
  104. if (AuctionPaymentType.PURCHASE_PRICE.equals(type)) {
  105. if (auction.getEndTime().isAfter(LocalDateTime.now())) {
  106. throw new BusinessException("拍卖还未结束");
  107. }
  108. AuctionRecord record = auctionRecordRepo.findTopByAuctionIdAndUserIdOrderByIdDesc(auctionId, userId);
  109. if (ObjectUtils.isEmpty(record) || !record.isPayDeposit()) {
  110. throw new BusinessException("未支付保证金");
  111. }
  112. if (record.getBidderPrice().compareTo(auction.getPurchasePrice()) != 0) {
  113. throw new BusinessException("与成交价不否");
  114. }
  115. int time = sysConfigService.getInt("auction_cancel_time");
  116. if (LocalDateTime.now().isAfter(auction.getEndTime().plusMinutes(time))) {
  117. throw new BusinessException("超过支付时长");
  118. }
  119. } else {
  120. if (auction.getEndTime().isBefore(LocalDateTime.now())) {
  121. throw new BusinessException("拍卖已结束");
  122. }
  123. if (AuctionPaymentType.DEPOSIT.equals(type)) {
  124. return this.createDeposit(user, auction);
  125. }
  126. }
  127. UserAddress userAddress = null;
  128. if (addressId != null) {
  129. userAddress = userAddressRepo.findById(addressId).orElseThrow(new BusinessException("地址信息不存在"));
  130. }
  131. try {
  132. auctionActivityService.changeStatus(auctionId, AuctionPaymentType.FIXED_PRICE
  133. .equals(type) ? AuctionStatus.FIXED_PRICE_PURCHASED : AuctionStatus.PURCHASED);
  134. if (AuctionSource.TRANSFER.equals(auction.getSource())) {
  135. Asset asset = assetRepo.findById(auction.getAssetId()).orElseThrow(new BusinessException("资产不存在"));
  136. asset.setStatus(AssetStatus.AUCTION_TRADING);
  137. assetRepo.save(asset);
  138. }
  139. BigDecimal price = AuctionPaymentType.FIXED_PRICE.equals(type) ? auction.getFixedPrice() : auction
  140. .getPurchasePrice();
  141. AuctionOrder order = AuctionOrder.builder()
  142. .id(snowflakeIdWorker.nextId())
  143. .auctionId(auction.getId())
  144. .userId(user.getId())
  145. .nickname(user.getNickname())
  146. .paymentType(type)
  147. .name(auction.getName())
  148. .pic(auction.getPic())
  149. .serviceCharge(auction.getServiceCharge())
  150. .royalties(auction.getRoyalties())
  151. .source(auction.getSource())
  152. .price(price)
  153. .totalPrice(price)
  154. .auctionRecordId(auctionRecordId)
  155. .status(AuctionOrderStatus.NOT_PAID)
  156. .contactName(Optional.ofNullable(userAddress).map(UserAddress::getName).orElse(null))
  157. .contactPhone(Optional.ofNullable(userAddress).map(UserAddress::getPhone).orElse(null))
  158. .address(Optional.ofNullable(userAddress).map(UserAddress::getDetail).orElse(null))
  159. .build();
  160. return auctionOrderRepo.save(order);
  161. } catch (Exception e) {
  162. auctionActivityService.changeStatus(auctionId, AuctionStatus.ONGOING);
  163. throw e;
  164. }
  165. }
  166. public AuctionOrder createDeposit(User user, AuctionActivity auction) {
  167. if (user.getId().equals(auction.getSellerId())) {
  168. throw new BusinessException("不可自己出价自己的");
  169. }
  170. //竞拍人绿魔卡余额限制
  171. UserBalance userBalance = userBalanceRepo.findByUserId(user.getId()).orElse(null);
  172. BigDecimal minAmount = sysConfigService.getBigDecimal("auction_min_amount");
  173. if (userBalance == null) {
  174. throw new BusinessException("请开通绿魔卡,并充值" + minAmount + "元");
  175. } else if (minAmount.compareTo(userBalance.getBalance()) > 0) {
  176. throw new BusinessException("绿魔卡余额不足" + minAmount + "元,请先充值");
  177. }
  178. //保证金
  179. // BigDecimal deposit = sysConfigService.getBigDecimal("deposit");
  180. AuctionOrder order = auctionOrderRepo
  181. .findByUserIdAndAuctionIdAndPaymentTypeAndStatusIn(user.getId(), auction.getId(),
  182. AuctionPaymentType.DEPOSIT, Arrays
  183. .asList(AuctionOrderStatus.NOT_PAID, AuctionOrderStatus.FINISH));
  184. if (ObjectUtils.isNotEmpty(order)) {
  185. if (AuctionOrderStatus.FINISH.equals(order.getStatus())) {
  186. throw new BusinessException("保证金已交过,无需再交");
  187. }
  188. throw new BusinessException("保证金未支付,取消后再重新出价");
  189. }
  190. AuctionRecord auctionRecord = AuctionRecord.builder()
  191. .auctionId(auction.getId())
  192. .type(AuctionRecordType.DEPOSIT)
  193. .bidderPrice(auction.getDeposit())
  194. .auctionPic(null)
  195. .userId(SecurityUtils.getAuthenticatedUser().getId())
  196. .avatar(SecurityUtils.getAuthenticatedUser().getAvatar())
  197. .name(auction.getName())
  198. .purchased(false)
  199. .auctionType(auction.getAuctionType())
  200. .build();
  201. AuctionRecord record = auctionRecordRepo.save(auctionRecord);
  202. order = AuctionOrder.builder()
  203. .id(snowflakeIdWorker.nextId())
  204. .auctionId(auction.getId())
  205. .userId(user.getId())
  206. .nickname(user.getNickname())
  207. .paymentType(AuctionPaymentType.DEPOSIT)
  208. .name(auction.getName())
  209. .pic(auction.getPic())
  210. .serviceCharge(auction.getServiceCharge())
  211. .royalties(auction.getRoyalties())
  212. .source(auction.getSource())
  213. .price(auction.getDeposit())
  214. .totalPrice(auction.getDeposit())
  215. .auctionRecordId(record.getId())
  216. .status(AuctionOrderStatus.NOT_PAID)
  217. .build();
  218. return auctionOrderRepo.save(order);
  219. }
  220. @Transient
  221. public void notify(Long id, PayMethod payMethod, String transactionId) {
  222. AuctionOrder order = auctionOrderRepo.findById(id).orElseThrow(new BusinessException("无记录"));
  223. if (!order.getStatus().equals(AuctionOrderStatus.NOT_PAID)) {
  224. throw new BusinessException("订单已处理");
  225. }
  226. AuctionActivity auction = auctionActivityRepo.findById(order.getAuctionId())
  227. .orElseThrow(new BusinessException("无拍卖活动"));
  228. if (auction.getAuctionType().equals(AuctionType.ENTITY)) {
  229. order.setStatus(AuctionOrderStatus.DELIVERY);
  230. } else {
  231. order.setStatus(AuctionOrderStatus.FINISH);
  232. }
  233. order.setPayMethod(payMethod);
  234. order.setTransactionId(transactionId);
  235. order.setPayTime(LocalDateTime.now());
  236. //存订单
  237. auctionOrderRepo.save(order);
  238. if (AuctionPaymentType.DEPOSIT.equals(order.getPaymentType())) {
  239. //改出价记录表
  240. AuctionRecord record = auctionRecordRepo.findById(order.getAuctionRecordId())
  241. .orElseThrow(new BusinessException("无出价记录"));
  242. record.setPayDeposit(true);
  243. auctionRecordRepo.save(record);
  244. return;
  245. }
  246. //此拍卖结束
  247. auctionActivityService.changeStatus(order.getAuctionId(), AuctionStatus.FINISH);
  248. //修改买家和成交价
  249. auction.setPurchaserId(order.getUserId());
  250. auction.setPurchasePrice(order.getTotalPrice());
  251. auctionActivityRepo.save(auction);
  252. if (AuctionSource.TRANSFER.equals(order.getSource())) {
  253. Asset asset = assetRepo.findById(auction.getAssetId()).orElseThrow(new BusinessException("资产不存在"));
  254. if (asset.isPublicShow()) {
  255. //取消公开展示
  256. assetService.cancelPublic(asset);
  257. }
  258. User user = userRepo.findById(order.getUserId()).orElseThrow(new BusinessException("无用户"));
  259. //转让流程
  260. assetService.transfer(asset, order.getTotalPrice(), user, TransferReason.AUCTION, order.getId());
  261. // 发送短信提醒用户转让成功
  262. if (asset.getUserId() != null) {
  263. smsService.sellOut(userRepo.findPhoneById(asset.getUserId()));
  264. }
  265. //用户冲余额
  266. BigDecimal amount = order.getTotalPrice()
  267. .multiply(BigDecimal.valueOf(100 - order.getRoyalties() - order.getServiceCharge()))
  268. .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
  269. userBalanceService.addBalance(asset.getOwnerId(), amount, id, BalanceType.AUCTION);
  270. }
  271. //改出价记录表为竞得(一口价无出价表)
  272. auctionRecordRepo.findById(order.getAuctionRecordId())
  273. .ifPresent(record -> {
  274. record.setPurchased(true);
  275. auctionRecordRepo.save(record);
  276. });
  277. //退保证金
  278. List<AuctionOrder> orders = auctionOrderRepo.findAllByAuctionIdAndPaymentTypeAndStatus(order.getAuctionId(),
  279. AuctionPaymentType.DEPOSIT, AuctionOrderStatus.FINISH);
  280. //退款
  281. orders.forEach(this::refund);
  282. }
  283. public void cancel(AuctionOrder order) {
  284. if (!getOrderLock(order.getId())) {
  285. log.error("订单取消失败 {}, redis锁了", order.getId());
  286. return;
  287. }
  288. boolean isRefund = false;
  289. try {
  290. AuctionActivity auction = auctionActivityRepo.findById(order.getAuctionId())
  291. .orElseThrow(new BusinessException("无记录"));
  292. if (AuctionPaymentType.PURCHASE_PRICE.equals(order.getPaymentType())) {
  293. //如果是拍卖,需获取取消订单的时长
  294. int time = sysConfigService.getInt("auction_cancel_time");
  295. if (LocalDateTime.now().isAfter(auction.getEndTime().plusMinutes(time))) {
  296. //超过支付时长
  297. log.info("取消订单流拍:{}", auction.getId());
  298. auctionActivityService.changeStatus(order.getAuctionId(), AuctionStatus.PASS);
  299. //添加到流拍记录表里
  300. auctionPassRecordRepo.save(AuctionPassRecord.builder()
  301. .auctionId(auction.getId())
  302. .userId(auction.getPurchaserId())
  303. .purchasePrice(auction.getPurchasePrice())
  304. .build());
  305. //流拍不退自己的保证金
  306. isRefund = true;
  307. if (AuctionSource.TRANSFER.equals(order.getSource())) {
  308. //改回资产状态
  309. Asset asset = assetRepo.findById(auction.getAssetId()).orElseThrow(new BusinessException("资产不存在"));
  310. asset.setStatus(AssetStatus.NORMAL);
  311. assetRepo.save(asset);
  312. }
  313. }
  314. } else if (AuctionPaymentType.DEPOSIT.equals(order.getPaymentType())) {
  315. //删除出价记录
  316. auctionRecordRepo.softDelete(order.getAuctionRecordId());
  317. } else {
  318. //拍卖是否结束
  319. if (LocalDateTime.now().isBefore(auction.getEndTime())) {
  320. //返回拍卖状态
  321. auctionActivityService.changeStatus(order.getAuctionId(), AuctionStatus.ONGOING);
  322. } else {
  323. //最后一个出价的人得
  324. auctionActivityService.changeStatus(order.getAuctionId(), AuctionStatus.PURCHASED);
  325. }
  326. }
  327. order.setStatus(AuctionOrderStatus.CANCELLED);
  328. order.setCancelTime(LocalDateTime.now());
  329. auctionOrderRepo.save(order);
  330. log.info("取消订单{}", order.getId());
  331. } catch (Exception e) {
  332. log.error("订单取消错误 orderId: " + order.getId(), e);
  333. }
  334. if (isRefund) {
  335. //退其余保证金
  336. List<AuctionOrder> orders = auctionOrderRepo
  337. .findAllByAuctionIdAndPaymentTypeAndStatus(order.getAuctionId(),
  338. AuctionPaymentType.DEPOSIT, AuctionOrderStatus.FINISH);
  339. //退款
  340. orders.stream()
  341. .filter(o -> !order.getUserId().equals(o.getUserId()))
  342. .forEach(this::refund);
  343. }
  344. releaseOrderLock(order.getId());
  345. }
  346. /**
  347. * 退款方法
  348. *
  349. * @param order 订单
  350. */
  351. private void refund(AuctionOrder order) {
  352. log.info("退款拍卖保证金订单{}", order.getId());
  353. PayMethod payMethod = order.getPayMethod();
  354. if (PayMethod.ALIPAY == payMethod) {
  355. if (StringUtils.length(order.getTransactionId()) == 28) {
  356. payMethod = PayMethod.HMPAY;
  357. } else if (StringUtils.length(order.getTransactionId()) == 30) {
  358. payMethod = PayMethod.SANDPAY;
  359. }
  360. }
  361. try {
  362. switch (payMethod) {
  363. case HMPAY:
  364. orderPayService.refund(order.getId().toString(), order.getTransactionId(), order.getTotalPrice(), Constants.PayChannel.HM);
  365. log.info("退款成功{}", order.getId());
  366. break;
  367. case SANDPAY:
  368. orderPayService.refund(order.getId().toString(), order.getTransactionId(), order.getTotalPrice(), Constants.PayChannel.SAND);
  369. log.info("退款成功{}", order.getId());
  370. break;
  371. case PAYEASE:
  372. orderPayService.refund(order.getId().toString(), order.getTransactionId(), order.getTotalPrice(), Constants.PayChannel.PE);
  373. log.info("退款成功{}", order.getId());
  374. break;
  375. }
  376. order.setRefundTime(LocalDateTime.now());
  377. order.setStatus(AuctionOrderStatus.REFUNDED);
  378. auctionOrderRepo.save(order);
  379. } catch (Exception e) {
  380. log.error("拍卖保证金订单退款失败 {} ", order.getId(), e);
  381. order.setRefundTime(LocalDateTime.now());
  382. order.setStatus(AuctionOrderStatus.REFUNDING);
  383. auctionOrderRepo.save(order);
  384. }
  385. }
  386. public boolean getOrderLock(Long orderId) {
  387. BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps(RedisKeys.AUCTION_ORDER_LOCK + orderId);
  388. Boolean flag = ops.setIfAbsent(1, 1, TimeUnit.DAYS);
  389. return Boolean.TRUE.equals(flag);
  390. }
  391. public void releaseOrderLock(Long orderId) {
  392. redisTemplate.delete(RedisKeys.AUCTION_ORDER_LOCK + orderId);
  393. }
  394. @Scheduled(cron = "0 0/10 * * * ?")
  395. public void passOverTimeAuction() {
  396. List<AuctionActivity> purchased = auctionActivityRepo.findAllByStatus(AuctionStatus.PURCHASED);
  397. if (purchased != null) {
  398. int time = sysConfigService.getInt("auction_cancel_time");
  399. purchased.forEach(act -> {
  400. if (LocalDateTime.now().isAfter(act.getEndTime().plusMinutes(time))) {
  401. List<AuctionOrder> auctionOrders = auctionOrderRepo.findAllByAuctionIdAndPaymentTypeAndStatus(act
  402. .getId(), AuctionPaymentType.PURCHASE_PRICE, AuctionOrderStatus.NOT_PAID);
  403. // if (CollUtil.isNotEmpty(auctionOrders)) {
  404. auctionOrders.forEach(this::cancel);
  405. // return;
  406. // }
  407. auctionActivityService.changeStatus(act.getId(), AuctionStatus.PASS);
  408. log.info("拍卖定时任务流拍{}", act.getId());
  409. if (AuctionSource.TRANSFER.equals(act.getSource())) {
  410. //改回资产状态
  411. Asset asset = assetRepo.findById(act.getAssetId()).orElseThrow(new BusinessException("资产不存在"));
  412. asset.setStatus(AssetStatus.NORMAL);
  413. assetRepo.save(asset);
  414. }
  415. //退其余保证金
  416. List<AuctionOrder> orders = auctionOrderRepo
  417. .findAllByAuctionIdAndPaymentTypeAndStatus(act.getId(),
  418. AuctionPaymentType.DEPOSIT, AuctionOrderStatus.FINISH);
  419. //退款
  420. orders.stream()
  421. .filter(o -> !act.getPurchaserId().equals(o.getUserId()))
  422. .forEach(this::refund);
  423. //添加到流拍记录表里
  424. auctionPassRecordRepo.save(AuctionPassRecord.builder()
  425. .auctionId(act.getId())
  426. .userId(act.getPurchaserId())
  427. .purchasePrice(act.getPurchasePrice())
  428. .build());
  429. }
  430. });
  431. }
  432. }
  433. /**
  434. * 发货
  435. *
  436. * @param id 编号
  437. * @param courierId 快递单号
  438. */
  439. public void dispatch(Long id, String courierId) {
  440. AuctionOrder auctionOrder = auctionOrderRepo.findById(id).orElseThrow(new BusinessException("铸造订单不存在"));
  441. auctionOrder.setStatus(AuctionOrderStatus.RECEIVE);
  442. auctionOrder.setCourierId(courierId);
  443. auctionOrderRepo.save(auctionOrder);
  444. }
  445. /**
  446. * 订单
  447. *
  448. * @param id 编号
  449. */
  450. public void finish(Long id) {
  451. AuctionOrder auctionOrder = auctionOrderRepo.findById(id).orElseThrow(new BusinessException("铸造订单不存在"));
  452. auctionOrder.setStatus(AuctionOrderStatus.FINISH);
  453. auctionOrderRepo.save(auctionOrder);
  454. }
  455. public void privilege(AuctionOrder order, User user) {
  456. if (showroomRepo.findByUserIdAndType(order.getUserId(), "AUCTION").isEmpty()) {
  457. //Bidder特殊拍卖展厅服务 创建一个bidder展厅藏品
  458. Long collectionId = (long) sysConfigService.getInt("bidder_collection_id");
  459. List<Asset> assets = assetRepo.findAllByUserIdAndCollectionIdAndStatus(order.getUserId(), collectionId, AssetStatus.NORMAL);
  460. Asset asset;
  461. if (assets.isEmpty()) {
  462. Collection collection = collectionRepo.findById(collectionId).orElseThrow(new BusinessException("无藏品"));
  463. if (!CollectionType.SHOWROOM.equals(collection.getType())) {
  464. throw new BusinessException("不是展厅藏品");
  465. }
  466. //创建资产
  467. asset = assetService.createAsset(collection, user, order.getId(), BigDecimal.ZERO, "拍卖赠送", null);
  468. } else {
  469. asset = assets.get(0);
  470. }
  471. //创建展厅
  472. showroomService.save(asset, "AUCTION");
  473. }
  474. //一个月的优先拍卖权
  475. PurchaserPrivilege.builder()
  476. .userId(order.getUserId())
  477. .title("元宇宙Bidder")
  478. .priority(true)
  479. .priorityExpireAt(LocalDateTime.now().plusMonths(1))
  480. .build();
  481. //前20名分钱
  482. BigDecimal totalPrice = order.getTotalPrice();
  483. //手续费
  484. int auctionServiceCharge = sysConfigService.getInt("auction_service_charge");
  485. BigDecimal serviceCharge = totalPrice.multiply(new BigDecimal(auctionServiceCharge))
  486. .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
  487. //奖励费用
  488. BigDecimal subtract = totalPrice.subtract(serviceCharge);
  489. int auctionReward = sysConfigService.getInt("auction_reward");
  490. BigDecimal reward = subtract.multiply(new BigDecimal(auctionReward)).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
  491. List<Long> records = auctionRecordRepo.findByAuctionId(order.getAuctionId(), 20);
  492. BigDecimal everyReward = reward.divide(new BigDecimal(records.size()), 2, RoundingMode.HALF_UP);
  493. //分奖励
  494. records.forEach(userId -> userBalanceService.addBalance(userId, everyReward, order.getId(), BalanceType.REWARD));
  495. //拍卖者所得
  496. BigDecimal amount = totalPrice.subtract(serviceCharge).subtract(reward);
  497. userBalanceService.addBalance(order.getUserId(), amount, order.getId(), BalanceType.AUCTION);
  498. }
  499. }