| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462 |
- package com.izouma.nineth.service;
- 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 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
- 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 AuctionActivityService auctionActivityService;
- @Autowired
- private RedisTemplate<String, Object> redisTemplate;
- @Autowired
- private SnowflakeIdWorker snowflakeIdWorker;
- @Autowired
- private AuctionPassRecordRepo auctionPassRecordRepo;
- @Lazy
- @Autowired
- private OrderPayService orderPayService;
- @Autowired
- private SmsService smsService;
- public Page<AuctionOrder> 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("无拍卖信息"));
- if (!auction.isOnShelf()) {
- throw 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 {
- 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.AUCTION_TRADING);
- assetRepo.save(asset);
- // 发送短信提醒用户转让成功
- if (asset.getUserId() != null) {
- smsService.sellOut(userRepo.findPhoneById(asset.getUserId()));
- }
- }
- 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)
- .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);
- }
- 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;
- }
- //此拍卖结束
- 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());
- }
- //改出价记录表为竞得(一口价无出价表)
- auctionRecordRepo.findById(order.getAuctionRecordId())
- .ifPresent(record -> {
- 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))) {
- //超过支付时长
- auctionActivityService.changeStatus(order.getAuctionId(), AuctionStatus.PASS);
- //添加到流拍记录表里
- auctionPassRecordRepo.save(AuctionPassRecord.builder()
- .auctionId(auction.getId())
- .userId(auction.getPurchaserId())
- .purchasePrice(auction.getPurchasePrice())
- .build());
- //流拍不退自己的保证金
- isRefund = true;
- }
- } else if (AuctionPaymentType.DEPOSIT.equals(order.getPaymentType())) {
- //删除出价记录
- auctionRecordRepo.softDelete(order.getAuctionRecordId());
- } else {
- //拍卖是否结束
- if (LocalDateTime.now().isBefore(auction.getEndTime())) {
- //返回拍卖状态
- auctionActivityService.changeStatus(order.getAuctionId(), AuctionStatus.ONGOING);
- } else {
- //最后一个出价的人得
- auctionActivityService.changeStatus(order.getAuctionId(), AuctionStatus.PURCHASED);
- }
- }
- 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);
- }
- 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;
- }
- 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);
- }
- @Scheduled(cron = "0 0/30 * * * ?")
- 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::cancel);
- auctionActivityService.changeStatus(act.getId(), AuctionStatus.PASS);
- log.info("拍卖定时任务流拍{}", act.getId());
- //退其余保证金
- 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);
- }
- }
|