|
|
@@ -0,0 +1,607 @@
|
|
|
+package com.izouma.nineth.service;
|
|
|
+
|
|
|
+import com.izouma.nineth.annotations.RedisLock;
|
|
|
+import com.izouma.nineth.config.Constants;
|
|
|
+import com.izouma.nineth.config.RedisKeys;
|
|
|
+import com.izouma.nineth.domain.*;
|
|
|
+import com.izouma.nineth.dto.PageQuery;
|
|
|
+import com.izouma.nineth.enums.*;
|
|
|
+import com.izouma.nineth.exception.BusinessException;
|
|
|
+import com.izouma.nineth.repo.*;
|
|
|
+import com.izouma.nineth.service.sms.SmsService;
|
|
|
+import com.izouma.nineth.utils.JpaUtils;
|
|
|
+import com.izouma.nineth.utils.SecurityUtils;
|
|
|
+import com.izouma.nineth.utils.SnowflakeIdWorker;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.commons.lang3.ObjectUtils;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.context.annotation.Lazy;
|
|
|
+import org.springframework.data.domain.Page;
|
|
|
+import org.springframework.data.redis.core.BoundValueOperations;
|
|
|
+import org.springframework.data.redis.core.RedisTemplate;
|
|
|
+import org.springframework.scheduling.annotation.Scheduled;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import javax.persistence.Transient;
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.math.RoundingMode;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Optional;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
+
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+public class AuctionOrderService {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private AuctionOrderRepo auctionOrderRepo;
|
|
|
+ @Autowired
|
|
|
+ private SysConfigService sysConfigService;
|
|
|
+ @Autowired
|
|
|
+ private UserRepo userRepo;
|
|
|
+ @Autowired
|
|
|
+ private AssetService assetService;
|
|
|
+ @Autowired
|
|
|
+ private AuctionActivityRepo auctionActivityRepo;
|
|
|
+ @Autowired
|
|
|
+ private AuctionRecordRepo auctionRecordRepo;
|
|
|
+ @Autowired
|
|
|
+ private AssetRepo assetRepo;
|
|
|
+ @Autowired
|
|
|
+ private UserAddressRepo userAddressRepo;
|
|
|
+ @Autowired
|
|
|
+ private RedisTemplate<String, Object> redisTemplate;
|
|
|
+ @Autowired
|
|
|
+ private SnowflakeIdWorker snowflakeIdWorker;
|
|
|
+ @Autowired
|
|
|
+ private AuctionPassRecordRepo auctionPassRecordRepo;
|
|
|
+ @Lazy
|
|
|
+ @Autowired
|
|
|
+ private OrderPayService orderPayService;
|
|
|
+ @Autowired
|
|
|
+ private SmsService smsService;
|
|
|
+ @Autowired
|
|
|
+ private UserBalanceService userBalanceService;
|
|
|
+ @Autowired
|
|
|
+ private ShowroomService showroomService;
|
|
|
+ @Autowired
|
|
|
+ private CollectionRepo collectionRepo;
|
|
|
+ @Autowired
|
|
|
+ private ShowroomRepo showroomRepo;
|
|
|
+ @Autowired
|
|
|
+ private UserBalanceRepo userBalanceRepo;
|
|
|
+
|
|
|
+ public Page<AuctionOrder> all(PageQuery pageQuery) {
|
|
|
+ return auctionOrderRepo
|
|
|
+ .findAll(JpaUtils.toSpecification(pageQuery, AuctionOrder.class), JpaUtils.toPageRequest(pageQuery));
|
|
|
+ }
|
|
|
+
|
|
|
+ @RedisLock("'createAuctionOrder::'+#auctionId")
|
|
|
+ public AuctionOrder create(Long userId, Long auctionId, Long addressId, Long auctionRecordId, AuctionPaymentType type) {
|
|
|
+ User user = userRepo.findById(userId).orElseThrow(new BusinessException("无用户"));
|
|
|
+
|
|
|
+ AuctionActivity auction = auctionActivityRepo.findById(auctionId)
|
|
|
+ .orElseThrow(new BusinessException("无拍卖信息"));
|
|
|
+
|
|
|
+ if (!auction.isOnShelf()) {
|
|
|
+ throw new BusinessException("拍卖已结束");
|
|
|
+ }
|
|
|
+
|
|
|
+ String status = auctionActivityRepo.getStatus(auctionId);
|
|
|
+ switch (AuctionStatus.valueOf(status)) {
|
|
|
+ case NOTSTARTED:
|
|
|
+ throw new BusinessException("拍卖还未开始");
|
|
|
+// case PURCHASED:
|
|
|
+// throw new BusinessException("拍卖成交中");
|
|
|
+ case PASS:
|
|
|
+ throw new BusinessException("已经流拍");
|
|
|
+ case FINISH:
|
|
|
+ throw new BusinessException("拍卖已结束");
|
|
|
+ case FIXED_PRICE_PURCHASED:
|
|
|
+ if (AuctionPaymentType.FIXED_PRICE.equals(type)) {
|
|
|
+ throw new BusinessException("一口价成交中");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (user.getId().equals(auction.getSellerId())) {
|
|
|
+ throw new BusinessException("不可自己出价自己");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (AuctionPaymentType.PURCHASE_PRICE.equals(type)) {
|
|
|
+ if (auction.getEndTime().isAfter(LocalDateTime.now())) {
|
|
|
+ throw new BusinessException("拍卖还未结束");
|
|
|
+ }
|
|
|
+ AuctionRecord record = auctionRecordRepo.findTopByAuctionIdAndUserIdOrderByIdDesc(auctionId, userId);
|
|
|
+ if (ObjectUtils.isEmpty(record) || !record.isPayDeposit()) {
|
|
|
+ throw new BusinessException("未支付保证金");
|
|
|
+ }
|
|
|
+ if (record.getBidderPrice().compareTo(auction.getPurchasePrice()) != 0) {
|
|
|
+ throw new BusinessException("与成交价不否");
|
|
|
+ }
|
|
|
+ int time = sysConfigService.getInt("auction_cancel_time");
|
|
|
+ if (LocalDateTime.now().isAfter(auction.getEndTime().plusMinutes(time))) {
|
|
|
+ throw new BusinessException("超过支付时长");
|
|
|
+ }
|
|
|
+
|
|
|
+// AuctionOrder order = auctionOrderRepo
|
|
|
+// .findByUserIdAndAuctionIdAndPaymentTypeAndStatusIn(user.getId(), auction.getId(),
|
|
|
+// AuctionPaymentType.PURCHASE_PRICE, Arrays
|
|
|
+// .asList(AuctionOrderStatus.NOT_PAID, AuctionOrderStatus.FINISH));
|
|
|
+// if (ObjectUtils.isNotEmpty(order)) {
|
|
|
+// if (AuctionOrderStatus.FINISH.equals(order.getStatus())) {
|
|
|
+// throw new BusinessException("成交金已交过,无需再交");
|
|
|
+// }
|
|
|
+// throw new BusinessException("成交金未支付,取消后再支付");
|
|
|
+// }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ if (auction.getEndTime().isBefore(LocalDateTime.now())) {
|
|
|
+ throw new BusinessException("拍卖已结束");
|
|
|
+ }
|
|
|
+ if (AuctionPaymentType.DEPOSIT.equals(type)) {
|
|
|
+ return this.createDeposit(user, auction);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ UserAddress userAddress = null;
|
|
|
+ if (addressId != null) {
|
|
|
+ userAddress = userAddressRepo.findById(addressId).orElseThrow(new BusinessException("地址信息不存在"));
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+
|
|
|
+ auctionActivityRepo.updateStatus(auctionId, AuctionPaymentType.FIXED_PRICE
|
|
|
+ .equals(type) ? AuctionStatus.FIXED_PRICE_PURCHASED : AuctionStatus.PURCHASED);
|
|
|
+
|
|
|
+ if (AuctionSource.TRANSFER.equals(auction.getSource())) {
|
|
|
+ Asset asset = assetRepo.findById(auction.getAssetId()).orElseThrow(new BusinessException("资产不存在"));
|
|
|
+ asset.setStatus(AssetStatus.AUCTION_TRADING);
|
|
|
+ assetRepo.save(asset);
|
|
|
+ }
|
|
|
+
|
|
|
+ BigDecimal price = AuctionPaymentType.FIXED_PRICE.equals(type) ? auction.getFixedPrice() : auction
|
|
|
+ .getPurchasePrice();
|
|
|
+
|
|
|
+ AuctionOrder order = AuctionOrder.builder()
|
|
|
+ .id(snowflakeIdWorker.nextId())
|
|
|
+ .auctionId(auction.getId())
|
|
|
+ .userId(user.getId())
|
|
|
+ .nickname(user.getNickname())
|
|
|
+ .paymentType(type)
|
|
|
+ .name(auction.getName())
|
|
|
+ .pic(auction.getPic())
|
|
|
+ .serviceCharge(auction.getServiceCharge())
|
|
|
+ .royalties(auction.getRoyalties())
|
|
|
+ .source(auction.getSource())
|
|
|
+ .price(price)
|
|
|
+ .totalPrice(price)
|
|
|
+ .auctionRecordId(auctionRecordId)
|
|
|
+ .status(AuctionOrderStatus.NOT_PAID)
|
|
|
+ .contactName(Optional.ofNullable(userAddress).map(UserAddress::getName).orElse(null))
|
|
|
+ .contactPhone(Optional.ofNullable(userAddress).map(UserAddress::getPhone).orElse(null))
|
|
|
+ .address(Optional.ofNullable(userAddress).map(UserAddress::getDetail).orElse(null))
|
|
|
+ .build();
|
|
|
+
|
|
|
+ return auctionOrderRepo.save(order);
|
|
|
+ } catch (Exception e) {
|
|
|
+ auctionActivityRepo.updateStatus(auctionId, AuctionStatus.ONGOING);
|
|
|
+ throw e;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public AuctionOrder createDeposit(User user, AuctionActivity auction) {
|
|
|
+ if (user.getId().equals(auction.getSellerId())) {
|
|
|
+ throw new BusinessException("不可自己出价自己的");
|
|
|
+ }
|
|
|
+
|
|
|
+ //竞拍人绿魔卡余额限制
|
|
|
+ BigDecimal minAmount = sysConfigService.getBigDecimal("auction_min_amount");
|
|
|
+ if (!user.isWalletEnabled()) {
|
|
|
+ throw new BusinessException("请开通绿魔卡,并充值" + minAmount + "元");
|
|
|
+ } else {
|
|
|
+ UserBalance userBalance = userBalanceRepo.findByUserId(user.getId()).orElse(new UserBalance(user.getId()));
|
|
|
+ if (minAmount.compareTo(userBalance.getBalance()) > 0) {
|
|
|
+ throw new BusinessException("绿魔卡余额不足" + minAmount + "元,请先充值");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //保证金
|
|
|
+// BigDecimal deposit = sysConfigService.getBigDecimal("deposit");
|
|
|
+ AuctionOrder order = auctionOrderRepo
|
|
|
+ .findByUserIdAndAuctionIdAndPaymentTypeAndStatusIn(user.getId(), auction.getId(),
|
|
|
+ AuctionPaymentType.DEPOSIT, Arrays
|
|
|
+ .asList(AuctionOrderStatus.NOT_PAID, AuctionOrderStatus.FINISH));
|
|
|
+ if (ObjectUtils.isNotEmpty(order)) {
|
|
|
+ if (AuctionOrderStatus.FINISH.equals(order.getStatus())) {
|
|
|
+ throw new BusinessException("保证金已交过,无需再交");
|
|
|
+ }
|
|
|
+ throw new BusinessException("保证金未支付,取消后再重新出价");
|
|
|
+ }
|
|
|
+
|
|
|
+ AuctionRecord auctionRecord = AuctionRecord.builder()
|
|
|
+ .auctionId(auction.getId())
|
|
|
+ .type(AuctionRecordType.DEPOSIT)
|
|
|
+ .bidderPrice(auction.getDeposit())
|
|
|
+ .auctionPic(null)
|
|
|
+ .userId(SecurityUtils.getAuthenticatedUser().getId())
|
|
|
+ .avatar(SecurityUtils.getAuthenticatedUser().getAvatar())
|
|
|
+ .name(auction.getName())
|
|
|
+ .purchased(false)
|
|
|
+ .auctionType(auction.getAuctionType())
|
|
|
+ .build();
|
|
|
+ AuctionRecord record = auctionRecordRepo.save(auctionRecord);
|
|
|
+
|
|
|
+ order = AuctionOrder.builder()
|
|
|
+ .id(snowflakeIdWorker.nextId())
|
|
|
+ .auctionId(auction.getId())
|
|
|
+ .userId(user.getId())
|
|
|
+ .nickname(user.getNickname())
|
|
|
+ .paymentType(AuctionPaymentType.DEPOSIT)
|
|
|
+ .name(auction.getName())
|
|
|
+ .pic(auction.getPic())
|
|
|
+ .serviceCharge(auction.getServiceCharge())
|
|
|
+ .royalties(auction.getRoyalties())
|
|
|
+ .source(auction.getSource())
|
|
|
+ .price(auction.getDeposit())
|
|
|
+ .totalPrice(auction.getDeposit())
|
|
|
+ .auctionRecordId(record.getId())
|
|
|
+ .status(AuctionOrderStatus.NOT_PAID)
|
|
|
+ .build();
|
|
|
+
|
|
|
+
|
|
|
+ return auctionOrderRepo.save(order);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ @Transient
|
|
|
+ public void notify(Long id, PayMethod payMethod, String transactionId) {
|
|
|
+ AuctionOrder order = auctionOrderRepo.findById(id).orElseThrow(new BusinessException("无记录"));
|
|
|
+ if (!order.getStatus().equals(AuctionOrderStatus.NOT_PAID)) {
|
|
|
+ throw new BusinessException("订单已处理");
|
|
|
+ }
|
|
|
+ AuctionActivity auction = auctionActivityRepo.findById(order.getAuctionId())
|
|
|
+ .orElseThrow(new BusinessException("无拍卖活动"));
|
|
|
+
|
|
|
+ if (auction.getAuctionType().equals(AuctionType.ENTITY)) {
|
|
|
+ order.setStatus(AuctionOrderStatus.DELIVERY);
|
|
|
+ } else {
|
|
|
+ order.setStatus(AuctionOrderStatus.FINISH);
|
|
|
+ }
|
|
|
+ order.setPayMethod(payMethod);
|
|
|
+ order.setTransactionId(transactionId);
|
|
|
+ order.setPayTime(LocalDateTime.now());
|
|
|
+ //存订单
|
|
|
+ auctionOrderRepo.save(order);
|
|
|
+
|
|
|
+ if (AuctionPaymentType.DEPOSIT.equals(order.getPaymentType())) {
|
|
|
+ //改出价记录表
|
|
|
+ AuctionRecord record = auctionRecordRepo.findById(order.getAuctionRecordId())
|
|
|
+ .orElseThrow(new BusinessException("无出价记录"));
|
|
|
+ record.setPayDeposit(true);
|
|
|
+ auctionRecordRepo.save(record);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ //此拍卖结束
|
|
|
+ //修改买家和成交价
|
|
|
+ auction.setStatus(AuctionStatus.FINISH);
|
|
|
+ auction.setPurchaserId(order.getUserId());
|
|
|
+ auction.setPurchasePrice(order.getTotalPrice());
|
|
|
+ auctionActivityRepo.save(auction);
|
|
|
+ log.info("拍卖结束:{}", order.getAuctionId());
|
|
|
+
|
|
|
+ if (AuctionSource.TRANSFER.equals(order.getSource())) {
|
|
|
+ Asset asset = assetRepo.findById(auction.getAssetId()).orElseThrow(new BusinessException("资产不存在"));
|
|
|
+ if (asset.isPublicShow()) {
|
|
|
+ //取消公开展示
|
|
|
+ assetService.cancelPublic(asset);
|
|
|
+ }
|
|
|
+ User user = userRepo.findById(order.getUserId()).orElseThrow(new BusinessException("无用户"));
|
|
|
+ //转让流程
|
|
|
+ assetService.transfer(asset, order.getTotalPrice(), user, TransferReason.AUCTION, order.getId());
|
|
|
+
|
|
|
+ // 发送短信提醒用户转让成功
|
|
|
+ if (asset.getUserId() != null) {
|
|
|
+ smsService.sellOut(userRepo.findPhoneById(asset.getUserId()));
|
|
|
+ }
|
|
|
+
|
|
|
+ //用户冲余额
|
|
|
+ BigDecimal amount = order.getTotalPrice()
|
|
|
+ .multiply(BigDecimal.valueOf(100 - order.getRoyalties() - order.getServiceCharge()))
|
|
|
+ .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
|
|
|
+
|
|
|
+ userBalanceService.addBalance(asset.getUserId(), amount, id, BalanceType.AUCTION);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ //改出价记录表为竞得(一口价无出价表)
|
|
|
+ if (ObjectUtils.isNotEmpty(order.getAuctionRecordId())) {
|
|
|
+ AuctionRecord record = auctionRecordRepo.findById(order.getAuctionRecordId())
|
|
|
+ .orElseThrow(new BusinessException("无出价记录"));
|
|
|
+ record.setPurchased(true);
|
|
|
+ auctionRecordRepo.save(record);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ //退保证金
|
|
|
+ List<AuctionOrder> orders = auctionOrderRepo.findAllByAuctionIdAndPaymentTypeAndStatus(order.getAuctionId(),
|
|
|
+ AuctionPaymentType.DEPOSIT, AuctionOrderStatus.FINISH);
|
|
|
+ //退款
|
|
|
+ orders.forEach(this::refund);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void cancel(AuctionOrder order) {
|
|
|
+ if (!getOrderLock(order.getId())) {
|
|
|
+ log.error("订单取消失败 {}, redis锁了", order.getId());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ boolean isRefund = false;
|
|
|
+ try {
|
|
|
+ AuctionActivity auction = auctionActivityRepo.findById(order.getAuctionId())
|
|
|
+ .orElseThrow(new BusinessException("无记录"));
|
|
|
+
|
|
|
+ if (AuctionPaymentType.PURCHASE_PRICE.equals(order.getPaymentType())) {
|
|
|
+ //如果是拍卖,需获取取消订单的时长
|
|
|
+ int time = sysConfigService.getInt("auction_cancel_time");
|
|
|
+ if (LocalDateTime.now().isAfter(auction.getEndTime().plusMinutes(time))) {
|
|
|
+ //超过支付时长
|
|
|
+ log.info("取消订单流拍:{}", auction.getId());
|
|
|
+ auctionActivityRepo.updateStatus(order.getAuctionId(), AuctionStatus.PASS);
|
|
|
+ //添加到流拍记录表里
|
|
|
+ auctionPassRecordRepo.save(AuctionPassRecord.builder()
|
|
|
+ .auctionId(auction.getId())
|
|
|
+ .userId(auction.getPurchaserId())
|
|
|
+ .purchasePrice(auction.getPurchasePrice())
|
|
|
+ .build());
|
|
|
+ //流拍不退自己的保证金
|
|
|
+ isRefund = true;
|
|
|
+
|
|
|
+ if (AuctionSource.TRANSFER.equals(order.getSource())) {
|
|
|
+ //改回资产状态
|
|
|
+ Asset asset = assetRepo.findById(auction.getAssetId())
|
|
|
+ .orElseThrow(new BusinessException("资产不存在"));
|
|
|
+ asset.setStatus(AssetStatus.NORMAL);
|
|
|
+ assetRepo.save(asset);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ } else if (AuctionPaymentType.DEPOSIT.equals(order.getPaymentType())) {
|
|
|
+ //删除出价记录
|
|
|
+ auctionRecordRepo.softDelete(order.getAuctionRecordId());
|
|
|
+ } else {
|
|
|
+ //拍卖是否结束
|
|
|
+ if (LocalDateTime.now().isBefore(auction.getEndTime())) {
|
|
|
+ //返回拍卖状态
|
|
|
+ auctionActivityRepo.updateStatus(order.getAuctionId(), AuctionStatus.ONGOING);
|
|
|
+ } else {
|
|
|
+ //最后一个出价的人得
|
|
|
+ auctionActivityRepo.updateStatus(order.getAuctionId(), AuctionStatus.PURCHASED);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ order.setStatus(AuctionOrderStatus.CANCELLED);
|
|
|
+ order.setCancelTime(LocalDateTime.now());
|
|
|
+ auctionOrderRepo.save(order);
|
|
|
+ log.info("取消订单{}", order.getId());
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("订单取消错误 orderId: " + order.getId(), e);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isRefund) {
|
|
|
+ //退其余保证金
|
|
|
+ List<AuctionOrder> orders = auctionOrderRepo
|
|
|
+ .findAllByAuctionIdAndPaymentTypeAndStatus(order.getAuctionId(),
|
|
|
+ AuctionPaymentType.DEPOSIT, AuctionOrderStatus.FINISH);
|
|
|
+ //退款
|
|
|
+ orders.stream()
|
|
|
+ .filter(o -> !order.getUserId().equals(o.getUserId()))
|
|
|
+ .forEach(this::refund);
|
|
|
+ }
|
|
|
+ releaseOrderLock(order.getId());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 退款方法
|
|
|
+ *
|
|
|
+ * @param order 订单
|
|
|
+ */
|
|
|
+ private void refund(AuctionOrder order) {
|
|
|
+ log.info("退款拍卖保证金订单{}", order.getId());
|
|
|
+ PayMethod payMethod = order.getPayMethod();
|
|
|
+ if (PayMethod.ALIPAY == payMethod) {
|
|
|
+ if (StringUtils.length(order.getTransactionId()) == 28) {
|
|
|
+ payMethod = PayMethod.HMPAY;
|
|
|
+ } else if (StringUtils.length(order.getTransactionId()) == 30) {
|
|
|
+ payMethod = PayMethod.SANDPAY;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ switch (payMethod) {
|
|
|
+ case HMPAY:
|
|
|
+ orderPayService.refund(order.getId()
|
|
|
+ .toString(), order.getTransactionId(), order.getTotalPrice(), Constants.PayChannel.HM);
|
|
|
+ log.info("退款成功{}", order.getId());
|
|
|
+ break;
|
|
|
+ case SANDPAY:
|
|
|
+ orderPayService.refund(order.getId()
|
|
|
+ .toString(), order.getTransactionId(), order.getTotalPrice(), Constants.PayChannel.SAND);
|
|
|
+ log.info("退款成功{}", order.getId());
|
|
|
+ break;
|
|
|
+ case PAYEASE:
|
|
|
+ orderPayService.refund(order.getId()
|
|
|
+ .toString(), order.getTransactionId(), order.getTotalPrice(), Constants.PayChannel.PE);
|
|
|
+ log.info("退款成功{}", order.getId());
|
|
|
+ break;
|
|
|
+ case BALANCE:
|
|
|
+ userBalanceService.addBalance(order.getUserId(), order.getTotalPrice(), order.getId(), BalanceType.AUCTION_RETURN);
|
|
|
+ log.info("退款成功{}", order.getId());
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ order.setRefundTime(LocalDateTime.now());
|
|
|
+ order.setStatus(AuctionOrderStatus.REFUNDED);
|
|
|
+ auctionOrderRepo.save(order);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("拍卖保证金订单退款失败 {} ", order.getId(), e);
|
|
|
+ order.setRefundTime(LocalDateTime.now());
|
|
|
+ order.setStatus(AuctionOrderStatus.REFUNDING);
|
|
|
+ auctionOrderRepo.save(order);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean getOrderLock(Long orderId) {
|
|
|
+ BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps(RedisKeys.AUCTION_ORDER_LOCK + orderId);
|
|
|
+ Boolean flag = ops.setIfAbsent(1, 1, TimeUnit.DAYS);
|
|
|
+ return Boolean.TRUE.equals(flag);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void releaseOrderLock(Long orderId) {
|
|
|
+ redisTemplate.delete(RedisKeys.AUCTION_ORDER_LOCK + orderId);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void passCancel(AuctionOrder order) {
|
|
|
+ if (!getOrderLock(order.getId())) {
|
|
|
+ log.error("订单取消失败 {}, redis锁了", order.getId());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ order.setStatus(AuctionOrderStatus.CANCELLED);
|
|
|
+ order.setCancelTime(LocalDateTime.now());
|
|
|
+ auctionOrderRepo.save(order);
|
|
|
+ log.info("取消订单{}", order.getId());
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("订单取消错误 orderId: " + order.getId(), e);
|
|
|
+ }
|
|
|
+ releaseOrderLock(order.getId());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Scheduled(cron = "0 0/1 * * * ?")
|
|
|
+ public void passOverTimeAuction() {
|
|
|
+ List<AuctionActivity> purchased = auctionActivityRepo.findAllByStatus(AuctionStatus.PURCHASED);
|
|
|
+ if (purchased != null) {
|
|
|
+ int time = sysConfigService.getInt("auction_cancel_time");
|
|
|
+ purchased.forEach(act -> {
|
|
|
+ if (LocalDateTime.now().isAfter(act.getEndTime().plusMinutes(time))) {
|
|
|
+ List<AuctionOrder> auctionOrders = auctionOrderRepo.findAllByAuctionIdAndPaymentTypeAndStatus(act
|
|
|
+ .getId(), AuctionPaymentType.PURCHASE_PRICE, AuctionOrderStatus.NOT_PAID);
|
|
|
+ //创建了订单
|
|
|
+ auctionOrders.forEach(this::passCancel);
|
|
|
+
|
|
|
+ act.setStatus(AuctionStatus.PASS);
|
|
|
+ auctionActivityRepo.save(act);
|
|
|
+ log.info("拍卖定时任务流拍{}", act.getId());
|
|
|
+
|
|
|
+ if (AuctionSource.TRANSFER.equals(act.getSource())) {
|
|
|
+ //改回资产状态
|
|
|
+ Asset asset = assetRepo.findById(act.getAssetId())
|
|
|
+ .orElseThrow(new BusinessException("资产不存在"));
|
|
|
+ asset.setStatus(AssetStatus.NORMAL);
|
|
|
+ assetRepo.save(asset);
|
|
|
+ }
|
|
|
+
|
|
|
+ //退其余保证金
|
|
|
+ List<AuctionOrder> orders = auctionOrderRepo
|
|
|
+ .findAllByAuctionIdAndPaymentTypeAndStatus(act.getId(),
|
|
|
+ AuctionPaymentType.DEPOSIT, AuctionOrderStatus.FINISH);
|
|
|
+ //退款
|
|
|
+ orders.stream()
|
|
|
+ .filter(o -> !act.getPurchaserId().equals(o.getUserId()))
|
|
|
+ .forEach(this::refund);
|
|
|
+
|
|
|
+ //添加到流拍记录表里
|
|
|
+ auctionPassRecordRepo.save(AuctionPassRecord.builder()
|
|
|
+ .auctionId(act.getId())
|
|
|
+ .userId(act.getPurchaserId())
|
|
|
+ .purchasePrice(act.getPurchasePrice())
|
|
|
+ .build());
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 发货
|
|
|
+ *
|
|
|
+ * @param id 编号
|
|
|
+ * @param courierId 快递单号
|
|
|
+ */
|
|
|
+ public void dispatch(Long id, String courierId) {
|
|
|
+ AuctionOrder auctionOrder = auctionOrderRepo.findById(id).orElseThrow(new BusinessException("铸造订单不存在"));
|
|
|
+ auctionOrder.setStatus(AuctionOrderStatus.RECEIVE);
|
|
|
+ auctionOrder.setCourierId(courierId);
|
|
|
+ auctionOrderRepo.save(auctionOrder);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 订单
|
|
|
+ *
|
|
|
+ * @param id 编号
|
|
|
+ */
|
|
|
+ public void finish(Long id) {
|
|
|
+ AuctionOrder auctionOrder = auctionOrderRepo.findById(id).orElseThrow(new BusinessException("铸造订单不存在"));
|
|
|
+ auctionOrder.setStatus(AuctionOrderStatus.FINISH);
|
|
|
+ auctionOrderRepo.save(auctionOrder);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void privilege(AuctionOrder order, User user) {
|
|
|
+ if (showroomRepo.findByUserIdAndType(order.getUserId(), "AUCTION").isEmpty()) {
|
|
|
+ //Bidder特殊拍卖展厅服务 创建一个bidder展厅藏品
|
|
|
+ Long collectionId = (long) sysConfigService.getInt("bidder_collection_id");
|
|
|
+ List<Asset> assets = assetRepo.findAllByUserIdAndCollectionIdAndStatus(order.getUserId(), collectionId, AssetStatus.NORMAL);
|
|
|
+ Asset asset;
|
|
|
+ if (assets.isEmpty()) {
|
|
|
+ Collection collection = collectionRepo.findById(collectionId).orElseThrow(new BusinessException("无藏品"));
|
|
|
+ if (!CollectionType.SHOWROOM.equals(collection.getType())) {
|
|
|
+ throw new BusinessException("不是展厅藏品");
|
|
|
+ }
|
|
|
+ //创建资产
|
|
|
+ asset = assetService.createAsset(collection, user, order.getId(), BigDecimal.ZERO, "拍卖赠送",
|
|
|
+ null, false);
|
|
|
+ } else {
|
|
|
+ asset = assets.get(0);
|
|
|
+ }
|
|
|
+ //创建展厅
|
|
|
+ showroomService.save(asset, "AUCTION");
|
|
|
+ }
|
|
|
+
|
|
|
+ //一个月的优先拍卖权
|
|
|
+ PurchaserPrivilege.builder()
|
|
|
+ .userId(order.getUserId())
|
|
|
+ .title("元宇宙Bidder")
|
|
|
+ .priority(true)
|
|
|
+ .priorityExpireAt(LocalDateTime.now().plusMonths(1))
|
|
|
+ .build();
|
|
|
+
|
|
|
+
|
|
|
+ //前20名分钱
|
|
|
+ BigDecimal totalPrice = order.getTotalPrice();
|
|
|
+ //手续费
|
|
|
+ int auctionServiceCharge = sysConfigService.getInt("auction_service_charge");
|
|
|
+ BigDecimal serviceCharge = totalPrice.multiply(new BigDecimal(auctionServiceCharge))
|
|
|
+ .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
|
|
|
+
|
|
|
+ //奖励费用
|
|
|
+ BigDecimal subtract = totalPrice.subtract(serviceCharge);
|
|
|
+ int auctionReward = sysConfigService.getInt("auction_reward");
|
|
|
+ BigDecimal reward = subtract.multiply(new BigDecimal(auctionReward))
|
|
|
+ .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
|
|
|
+ List<Long> records = auctionRecordRepo.findByAuctionId(order.getAuctionId(), 20);
|
|
|
+ BigDecimal everyReward = reward.divide(new BigDecimal(records.size()), 2, RoundingMode.HALF_UP);
|
|
|
+ //分奖励
|
|
|
+ records.forEach(userId -> userBalanceService.addBalance(userId, everyReward, order.getId(), BalanceType.REWARD));
|
|
|
+
|
|
|
+
|
|
|
+ //拍卖者所得
|
|
|
+ BigDecimal amount = totalPrice.subtract(serviceCharge).subtract(reward);
|
|
|
+ userBalanceService.addBalance(order.getUserId(), amount, order.getId(), BalanceType.AUCTION);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|