package com.izouma.nineth.service; import com.izouma.nineth.annotations.RedisLock; 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 lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; 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.management.Query; import javax.transaction.Transactional; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.*; import java.util.concurrent.TimeUnit; @Service @AllArgsConstructor @Slf4j public class TradeAuctionOrderService { private TradeAuctionOrderRepo tradeAuctionOrderRepo; private TradeAuctionService tradeAuctionService; private TradeAuctionRepo tradeAuctionRepo; private UserRepo userRepo; private UserBalanceService userBalanceService; private SysConfigService sysConfigService; private CommissionRecordRepo commissionRecordRepo; private RedisTemplate redisTemplate; private AssetService assetService; private AssetRepo assetRepo; private AirDropService airDropService; private static int orderCancelInterval = 210; public Page all(PageQuery pageQuery) { return tradeAuctionOrderRepo.findAll(JpaUtils.toSpecification(pageQuery, TradeAuctionOrder.class), JpaUtils .toPageRequest(pageQuery)); } @RedisLock(value = "'createAuctionOrder::'+#auctionId", expire = 210, message = "当前拍卖活动已有订单,请等待订单取消或下一轮次") public TradeAuctionOrder create(Long auctionId, Long userId, BigDecimal price, AuctionPaymentType auctionPaymentType) { // if (tradeAuctionService.getStock(auctionId) < 1) { // throw new BusinessException("库存不足"); // } TradeAuction tradeAuction = tradeAuctionRepo.findById(auctionId).orElseThrow(new BusinessException("未找到易拍活动")); User user = userRepo.findById(userId).orElseThrow(new BusinessException("暂无用户")); if (tradeAuction.getStock() < 1) { throw new BusinessException("库存不足"); } if (tradeAuction.getCurrentOwnerId() != null & tradeAuction.getStatus() != TradeAuctionStatus.PURCHASED) { if (tradeAuction.getCurrentOwnerId().equals(userId)) { throw new BusinessException("不可竞价持有的易拍产品"); } } if (LocalDateTime.now().compareTo(tradeAuction.getCurrentStartTime()) < 0) { throw new BusinessException("未到竞价时间"); } if (!tradeAuction.getStatus().equals(TradeAuctionStatus.ONGOING) & !tradeAuction.getStatus() .equals(TradeAuctionStatus.PURCHASED)) { throw new BusinessException("易拍产品未处在竞价状态"); } if (auctionPaymentType.equals(AuctionPaymentType.DEPOSIT)) { if (price.compareTo(tradeAuction.getNextPrice()) != 0) { throw new BusinessException("购买资格已经没有"); } if (tradeAuction.getSale() >= 30) { throw new BusinessException("已无购买资格"); } } if (auctionPaymentType.equals(AuctionPaymentType.FIXED_PRICE)) { if (tradeAuction.getRecommendPrice().compareTo(price) != 0) { throw new BusinessException("提交价格与一口价价格不一致。"); } } tradeAuctionService.decreaseStock(auctionId, 1); BigDecimal serviceCharge = (BigDecimal.valueOf(3).multiply(tradeAuction.getNextPrice()) .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP)) .add(tradeAuction.getCurrentPrice().subtract(tradeAuction.getPrice())); if (auctionPaymentType.equals(AuctionPaymentType.PURCHASE_PRICE)) { price = tradeAuction.getPurchasedPrice().subtract(tradeAuction.getLastCommission()); } TradeAuctionOrder tradeAuctionOrder = TradeAuctionOrder.builder() .tradeAuctionId(tradeAuction.getId()) .earnedPrice(price.subtract(tradeAuction.getPrice())) .serviceCharge(serviceCharge) .name(tradeAuction.getName()) .nickname(user.getNickname()) .paymentType(auctionPaymentType) .userId(user.getId()) .originPrice(tradeAuction.getPrice()) .currentPrice(tradeAuction.getCurrentPrice()) .price(price) .paybackStatus(PaybackStatus.NOPASSED) .pic(tradeAuction.getPic()) .source(AuctionSource.OFFICIAL) .status(AuctionOrderStatus.NOT_PAID) .purchasedPrice(tradeAuction.getCurrentPrice()) .build(); if (user.getInviteType() != null) { if (user.getInviteType().equals(InviteType.AUCTION)) { tradeAuctionOrder.setInvitorPhone(user.getInvitorPhone()); } User parentUser = userRepo.findByPhoneAndDelFalse(user.getInvitorPhone()).orElse(null); if (parentUser != null) { if (parentUser.getInviteType() != null) { if (parentUser.getInviteType().equals(InviteType.AUCTION)) { tradeAuctionOrder.setInvitorParentPhone(parentUser.getInvitorPhone()); } } } } tradeAuctionOrder = tradeAuctionOrderRepo.save(tradeAuctionOrder); //下订单减库存 return tradeAuctionOrder; } // 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); // } @Transactional public void notify(Long orderId, String transactionId, PayMethod payMethod) { TradeAuctionOrder tradeAuctionOrder = tradeAuctionOrderRepo.findById(orderId) .orElseThrow(new BusinessException("未找到订单")); if (!tradeAuctionOrder.getStatus().equals(AuctionOrderStatus.NOT_PAID)) { throw new BusinessException("订单已经处理"); } tradeAuctionOrder.setPayTime(LocalDateTime.now()); tradeAuctionOrder.setPayMethod(payMethod); tradeAuctionOrder.setTransactionId(transactionId); tradeAuctionOrder.setStatus(AuctionOrderStatus.FINISH); TradeAuction tradeAuction = tradeAuctionRepo.findById(tradeAuctionOrder.getTradeAuctionId()) .orElseThrow(new BusinessException("未找到该易拍活动")); if (tradeAuction.getCurrentOrderId() != null & !tradeAuctionOrder.getPaymentType() .equals(AuctionPaymentType.PURCHASE_PRICE)) { //将上次订单设为待补偿状态 TradeAuctionOrder lastOrder = tradeAuctionOrderRepo.findById(tradeAuction.getCurrentOrderId()) .orElseThrow(new BusinessException("暂无订单")); lastOrder.setPaybackStatus(PaybackStatus.PASSED); tradeAuctionOrderRepo.save(lastOrder); } tradeAuction.setCurrentOrderId(orderId); if (tradeAuctionOrder.getPaymentType().equals(AuctionPaymentType.FIXED_PRICE)) { tradeAuction.setCurrentPrice(tradeAuctionOrder.getCurrentPrice()); tradeAuction.setCurrentOwner(tradeAuctionOrder.getNickname()); tradeAuction.setCurrentOwnerId(tradeAuctionOrder.getUserId()); tradeAuction.setStatus(TradeAuctionStatus.FIXED_PRICE_PURCHASED); tradeAuctionOrder.setStatus(AuctionOrderStatus.AIR_DROP); } else if (tradeAuctionOrder.getPaymentType().equals(AuctionPaymentType.DEPOSIT)) { if (tradeAuction.getSale() == 29) { tradeAuction.setSale(30L); tradeAuction.setCurrentPrice(tradeAuctionOrder.getPrice()); tradeAuction.setCurrentOwner(tradeAuctionOrder.getNickname()); tradeAuction.setCurrentOwnerId(tradeAuctionOrder.getUserId()); tradeAuction.setLastCommission(tradeAuctionOrder.getServiceCharge()); tradeAuction.setPurchasedPrice(tradeAuctionOrder.getPurchasedPrice()); tradeAuctionService.purchase(tradeAuction); return; } else { BigDecimal result = tradeAuctionOrder.getPrice() .multiply(BigDecimal.valueOf(tradeAuction.getIncreasePer())) .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP); tradeAuction.setNextPrice(result.add(tradeAuctionOrder.getPrice())); tradeAuction.setEarning(tradeAuction.getNextPrice().subtract(tradeAuction.getPrice())); BigDecimal serviceCharge = (BigDecimal.valueOf(0.03).multiply(tradeAuction.getNextPrice())) .add(tradeAuctionOrder.getPrice().subtract(tradeAuction.getPrice())); tradeAuction.setCommission(serviceCharge); tradeAuction.setCurrentPrice(tradeAuctionOrder.getPrice()); tradeAuction.setCurrentOwner(tradeAuctionOrder.getNickname()); tradeAuction.setCurrentOwnerId(tradeAuctionOrder.getUserId()); LocalDateTime now = LocalDateTime.now(); LocalDateTime eightToday = LocalDate.now().atTime(20, 0); LocalDateTime elevenToday = LocalDate.now().atTime(11, 0); if (now.compareTo(elevenToday) < 0) { tradeAuction.setCurrentStartTime(elevenToday); } else { if (now.compareTo(eightToday) < 0) { tradeAuction .setCurrentStartTime(eightToday); } else { tradeAuction .setCurrentStartTime(elevenToday.plusDays(1)); } } tradeAuction .setCurrentEndTime(tradeAuction.getCurrentStartTime().plusDays(1)); tradeAuction.setFixedPrice(tradeAuctionOrder.getPrice().multiply(BigDecimal.valueOf(150)) .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP)); tradeAuction.setStatus(TradeAuctionStatus.WAITING); } } else { tradeAuction.setCurrentPrice(tradeAuctionOrder.getCurrentPrice()); tradeAuction.setCurrentOwner(tradeAuctionOrder.getNickname()); tradeAuction.setCurrentOwnerId(tradeAuctionOrder.getUserId()); tradeAuction.setStatus(TradeAuctionStatus.FINISH); finishLastOrder(tradeAuction.getCurrentOrderId()); tradeAuctionOrder.setStatus(AuctionOrderStatus.AIR_DROP); } tradeAuction.setLastCommission(tradeAuctionOrder.getServiceCharge()); tradeAuctionOrder.setCurrentEndTime(tradeAuction.getCurrentEndTime()); tradeAuctionOrder.setCurrentPrice(tradeAuctionOrder.getPrice()); tradeAuction.setStock(0L); tradeAuction.setSale(tradeAuction.getSale() + 1); tradeAuctionOrder.setTradeAuctionStatus(tradeAuction.getStatus()); tradeAuctionOrderRepo.save(tradeAuctionOrder); tradeAuctionRepo.save(tradeAuction); tradeAuctionService.increaseSale(tradeAuction.getId(), 1); LocalDate now = LocalDate.now(); LocalDate start = LocalDate.of(2023, 1, 19); LocalDate end = LocalDate.of(2023, 1, 29); if (now.isBefore(end) & now.isAfter(start)) { checkHasAirdrop(tradeAuctionOrder.getUserId()); } } public void checkHasAirdrop(Long userId) { Long collectionId = Long.valueOf(sysConfigService.getString("tradeAcution_collectionId")); if (collectionId != null) { Asset asset = assetRepo .findFirstByCollectionIdAndUserIdAndSource(collectionId, userId, AssetSource.OFFICIAL); if (asset == null) { User user = userRepo.findById(userId).orElseThrow(new BusinessException("暂无该用户")); airDropService.create(AirDrop.builder() .name("春节参与易拍空投福卡") .remark("tradeAuction:" + userId) .type(AirDropType.asset) .userIds(Collections.singletonList(userId)) .collectionId(collectionId) .targets(Collections .singletonList(new DropTarget(user.getId(), user.getPhone(), user .getNickname(), 1))) .auto(true) .companyId(1L) .build()); } } } public void finishLastOrder(Long lastOrderId) { TradeAuctionOrder tradeAuctionOrder = tradeAuctionOrderRepo.findById(lastOrderId) .orElseThrow(new BusinessException("暂无订单")); tradeAuctionOrder.setStatus(AuctionOrderStatus.PAID); tradeAuctionOrder.setPaybackStatus(PaybackStatus.PAYED); tradeAuctionOrderRepo.save(tradeAuctionOrder); } @Scheduled(fixedRate = 6000) public void batchCancelledAuctionOrder() { List orders = tradeAuctionOrderRepo .findByStatusAndCreatedAtBeforeAndDelFalse(AuctionOrderStatus.NOT_PAID, LocalDateTime.now().minusSeconds(orderCancelInterval)); orders.parallelStream().forEach(o -> { try { TradeAuctionOrder order = tradeAuctionOrderRepo.findById(o.getId()) .orElseThrow(new BusinessException("订单不存在")); if (order.getStatus() == AuctionOrderStatus.NOT_PAID) { cancel(order); } } catch (Exception e) { log.error("取消易拍订单错误 " + o.getId(), e); } }); } public void cancel(TradeAuctionOrder order) { // if (!getOrderLock(order.getId())) { // log.error("订单取消失败 {}, redis锁了", order.getId()); // return; // } if (order.getStatus() != AuctionOrderStatus.NOT_PAID) { throw new BusinessException("当前订单状态无法取消[" + order.getStatus().name() + "]"); } order.setStatus(AuctionOrderStatus.CANCELLED); order.setCancelTime(LocalDateTime.now()); tradeAuctionService.increaseStock(order.getTradeAuctionId(), 1); tradeAuctionOrderRepo.save(order); // releaseOrderLock(order.getId()); } // @Scheduled(fixedRate = 30000) // public void batchAirdrop() { // List tradeAuctionOrders = tradeAuctionOrderRepo // .findByStatusAndDelFalse(AuctionOrderStatus.AIR_DROP); // tradeAuctionOrders.parallelStream().forEach(o -> { // try { // TradeAuctionOrder order = tradeAuctionOrderRepo.findById(o.getId()) // .orElseThrow(new BusinessException("订单不存在")); // TradeAuction tradeAuction = tradeAuctionRepo.findById(order.getTradeAuctionId()) // .orElseThrow(new BusinessException("未找到易拍活动")); // User owner = userRepo.findById(order.getUserId()) // .orElseThrow(new BusinessException("暂无用户")); // assetService.createAsset(tradeAuction, owner, order.getId(), tradeAuction // .getCurrentPrice(), "空投", 1, false); // order.setStatus(AuctionOrderStatus.FINISH); // tradeAuctionOrderRepo.save(order); // } catch (Exception e) { // log.error("取消易拍订单错误 " + o.getId(), e); // } // }); // } @Scheduled(fixedRate = 30000) public void batchPayEarning() { List tradeAuctionOrders = tradeAuctionOrderRepo.findByPaybackStatus(PaybackStatus.PASSED); tradeAuctionOrders.parallelStream().forEach(o -> { try { TradeAuctionOrder order = tradeAuctionOrderRepo.findById(o.getId()) .orElseThrow(new BusinessException("订单不存在")); if (order.getPaybackStatus() == PaybackStatus.PASSED) { payEarning(order); } } catch (Exception e) { log.error("取消易拍订单错误 " + o.getId(), e); } }); } private void payEarning(TradeAuctionOrder order) { if (!order.getPaymentType().equals(AuctionPaymentType.PURCHASE_PRICE)) { userBalanceService.addBalance(order.getUserId(), order.getEarnedPrice(), order.getId(), BalanceType.BONUS); order.setPaybackStatus(PaybackStatus.PAYED); tradeAuctionOrderRepo.save(order); } } public void commission(TradeAuctionOrder order, BigDecimal price) { List officialPhones = Arrays.asList(sysConfigService.getString("ta_phone").split(",")); if (order.getInvitorPhone() != null) { if (officialPhones.contains(order.getInvitorPhone())) { userRepo.findByPhoneAndDelFalse(order.getInvitorPhone()).ifPresent(user -> { BigDecimal shareRatio = sysConfigService.getBigDecimal("auction_commission_rate"); if (StringUtils.isNotBlank(user.getSettleAccountId()) && shareRatio != null && shareRatio.compareTo(BigDecimal.ZERO) > 0) { commissionRecordRepo.save(CommissionRecord.builder() .orderId(order.getId()) .collectionId(order.getTradeAuctionId()) .name(order.getName()) .totalPrice(price) .nickname(user.getNickname()) .userId(user.getId()) .shareRatio(user.getShareRatio()) .phone(user.getPhone()) .shareAmount(price.multiply(shareRatio) .divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP)) .type(InviteType.AUCTION) .build()); userBalanceService.addBalance(user.getId(), price.multiply(shareRatio) .divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP), order .getId(), BalanceType.COMMISSION); } }); } } if (order.getInvitorParentPhone() != null) { if (officialPhones.contains(order.getInvitorParentPhone())) { userRepo.findByPhoneAndDelFalse(order.getInvitorParentPhone()).ifPresent(user -> { BigDecimal shareRatio = sysConfigService.getBigDecimal("auction_commission_step"); if (StringUtils.isNotBlank(user.getSettleAccountId()) && shareRatio != null && shareRatio.compareTo(BigDecimal.ZERO) > 0) { commissionRecordRepo.save(CommissionRecord.builder() .orderId(order.getId()) .collectionId(order.getTradeAuctionId()) .name(order.getName()) .totalPrice(price) .nickname(user.getNickname()) .userId(user.getId()) .shareRatio(user.getShareRatio()) .phone(user.getPhone()) .shareAmount(price.multiply(shareRatio) .divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP)) .type(InviteType.AUCTION) .build()); userBalanceService.addBalance(user.getId(), price.multiply(shareRatio) .divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP), order .getId(), BalanceType.COMMISSION); } }); } } } public Map statistic(Long tradeAuctionId) { Map result = new HashMap<>(); if (tradeAuctionId == null) { //总流水 BigDecimal deposit = tradeAuctionOrderRepo.sumDepositAmount(); BigDecimal purchased = tradeAuctionOrderRepo.sumPurchasedAmount(); BigDecimal fixed = tradeAuctionOrderRepo.sumFixedAmount(); BigDecimal comi = tradeAuctionOrderRepo.sumComi(); BigDecimal commi = (BigDecimal.valueOf(3).multiply(comi) .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP)); if (fixed == null) { fixed = BigDecimal.ZERO; } BigDecimal payed = deposit.add(purchased).add(fixed); result.put("payed", payed.setScale(1, RoundingMode.DOWN)); //总手续费 result.put("comi", commi.setScale(1, RoundingMode.DOWN)); //用户收益 BigDecimal earning = tradeAuctionOrderRepo.earningPrice(); BigDecimal serviceCharge = tradeAuctionOrderRepo.serviceCharge(); BigDecimal userEarning = earning.subtract(serviceCharge); result.put("userEarning", userEarning.setScale(1, RoundingMode.DOWN)); //平台收益 BigDecimal platEarning = payed.subtract(earning); result.put("platEarning", platEarning.setScale(1, RoundingMode.DOWN)); } else { //总流水 BigDecimal deposit = tradeAuctionOrderRepo.sumDepositAmountById(tradeAuctionId); if (deposit == null) { deposit = BigDecimal.ZERO; } BigDecimal purchased = tradeAuctionOrderRepo.sumPurchasedAmountById(tradeAuctionId); if (purchased == null) { purchased = BigDecimal.ZERO; } BigDecimal fixed = tradeAuctionOrderRepo.sumFixedAmountById(tradeAuctionId); if (fixed == null) { fixed = BigDecimal.ZERO; } BigDecimal comi = tradeAuctionOrderRepo.sumComiById(tradeAuctionId); BigDecimal commi = BigDecimal.ZERO; if (comi != null) { commi = (BigDecimal.valueOf(3).multiply(comi) .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP)); } BigDecimal payed = deposit.add(purchased).add(fixed); result.put("payed", payed.setScale(1, RoundingMode.DOWN)); //总手续费 result.put("comi", commi); //用户收益 BigDecimal earning = tradeAuctionOrderRepo.earningPriceById(tradeAuctionId); if (earning == null) { earning = BigDecimal.ZERO; } BigDecimal serviceCharge = tradeAuctionOrderRepo.serviceChargeById(tradeAuctionId); if (serviceCharge == null) { serviceCharge = BigDecimal.ZERO; } BigDecimal userEarning = earning.subtract(serviceCharge); result.put("userEarning", userEarning.setScale(1, RoundingMode.DOWN)); //平台收益 BigDecimal platEarning = payed.subtract(earning); result.put("platEarning", platEarning.setScale(1, RoundingMode.DOWN)); } return result; } }