package com.izouma.nineth.service; 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.utils.JpaUtils; import com.izouma.nineth.utils.SecurityUtils; import com.izouma.nineth.utils.SnowflakeIdWorker; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; 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 java.math.BigDecimal; import java.time.LocalDateTime; import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.concurrent.TimeUnit; @Slf4j @Service @AllArgsConstructor public class AuctionOrderService { private AuctionOrderRepo auctionOrderRepo; private SysConfigService sysConfigService; private UserRepo userRepo; private AssetService assetService; private AuctionActivityRepo auctionActivityRepo; private AuctionRecordRepo auctionRecordRepo; private AssetRepo assetRepo; private UserAddressRepo userAddressRepo; private AuctionActivityService auctionActivityService; private RedisTemplate redisTemplate; private SnowflakeIdWorker snowflakeIdWorker; public Page all(PageQuery pageQuery) { return auctionOrderRepo .findAll(JpaUtils.toSpecification(pageQuery, AuctionOrder.class), JpaUtils.toPageRequest(pageQuery)); } 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("无拍卖信息")); String status = (String) redisTemplate.opsForValue().get(RedisKeys.AUCTION_STATUS + auctionId); if (status == null) status = auction.getStatus().toString(); 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.DEPOSIT.equals(type)) { return this.createDeposit(user, auction); } 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("超过支付时长"); } } UserAddress userAddress = null; if (addressId != null) { userAddress = userAddressRepo.findById(addressId).orElseThrow(new BusinessException("地址信息不存在")); } try { // auction.setStatus(AuctionStatus.PURCHASED); // auctionActivityRepo.save(auction); auctionActivityService.changeStatus(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.AUCTIONING); 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) { auctionActivityService.changeStatus(auctionId, AuctionStatus.ONGOING); throw e; } } public AuctionOrder createDeposit(User user, AuctionActivity auction) { if (user.getId().equals(auction.getSellerId())) { throw new BusinessException("不可自己出价自己的"); } //保证金 // 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) .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); } 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); // auctionActivityRepo.save(auction); auctionActivityService.changeStatus(order.getAuctionId(), AuctionStatus.FINISH); 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()); } //该出价记录表为竞得 AuctionRecord record = auctionRecordRepo.findById(order.getAuctionRecordId()) .orElseThrow(new BusinessException("无出价记录")); record.setPurchased(true); auctionRecordRepo.save(record); //退保证金 List orders = auctionOrderRepo.findAllByAuctionIdAndPaymentTypeAndStatus(order.getAuctionId(), AuctionPaymentType.DEPOSIT, AuctionOrderStatus.FINISH); orders.forEach(o -> { //退款(暂未写) o.setRefundTime(LocalDateTime.now()); o.setStatus(AuctionOrderStatus.REFUNDING); auctionOrderRepo.save(o); }); } public void cancel(AuctionOrder order) { if (!getOrderLock(order.getId())) { log.error("订单取消失败 {}, redis锁了", order.getId()); return; } 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))) { //超过支付时长 // auction.setStatus(AuctionStatus.PASS); // auctionActivityRepo.save(auction); auctionActivityService.changeStatus(order.getAuctionId(), AuctionStatus.PASS); //退其余保证金 List orders = auctionOrderRepo .findAllByAuctionIdAndPaymentTypeAndStatus(order.getAuctionId(), AuctionPaymentType.DEPOSIT, AuctionOrderStatus.FINISH); orders.stream() .filter(o -> !order.getUserId().equals(o.getUserId())) .forEach(o -> { //退款(暂未写) o.setRefundTime(LocalDateTime.now()); o.setStatus(AuctionOrderStatus.REFUNDING); auctionOrderRepo.save(o); }); } } else if (AuctionPaymentType.DEPOSIT.equals(order.getPaymentType())) { //删除出价记录 auctionRecordRepo.softDelete(order.getAuctionRecordId()); } else { //拍卖是否结束 if (LocalDateTime.now().isBefore(auction.getEndTime())) { //返回拍卖状态 // auction.setStatus(AuctionStatus.ONGOING); auctionActivityService.changeStatus(order.getAuctionId(), AuctionStatus.ONGOING); } else { // auction.setStatus(AuctionStatus.PASS); //退还保证金 List orders = auctionOrderRepo .findAllByAuctionIdAndPaymentTypeAndStatus(order.getAuctionId(), AuctionPaymentType.DEPOSIT, AuctionOrderStatus.FINISH); orders.forEach(o -> { //退款(暂未写) o.setRefundTime(LocalDateTime.now()); o.setStatus(AuctionOrderStatus.REFUNDING); auctionOrderRepo.save(o); }); } // auctionActivityRepo.save(auction); auctionActivityService.changeStatus(order.getAuctionId(), AuctionStatus.PASS); } if (AuctionSource.TRANSFER.equals(order.getSource())) { //改回资产状态 Asset asset = assetRepo.findById(auction.getAssetId()).orElseThrow(new BusinessException("资产不存在")); asset.setStatus(AssetStatus.NORMAL); assetRepo.save(asset); } 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()); } public boolean getOrderLock(Long orderId) { BoundValueOperations 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); } @Scheduled(cron = "0 0/30 * * * ?") public void passOverTimeAuction() { List purchased = auctionActivityRepo.findAllByStatus(AuctionStatus.PURCHASED); if (purchased != null) { purchased.forEach(act -> { List auctionOrders = auctionOrderRepo.findAllByAuctionIdAndPaymentTypeAndStatus(act .getId(), AuctionPaymentType.PURCHASE_PRICE, AuctionOrderStatus.NOT_PAID); auctionOrders.forEach(this::cancel); act.setStatus(AuctionStatus.PASS); auctionActivityRepo.save(act); }); } } /** * 发货 * * @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); } }