package cn.hobbystocks.auc.service.impl; import cn.hobbystocks.auc.cache.CacheMap; import cn.hobbystocks.auc.common.constant.Constants; import cn.hobbystocks.auc.common.core.redis.Locker; import cn.hobbystocks.auc.common.core.redis.RedisCache; import cn.hobbystocks.auc.common.core.text.Convert; import cn.hobbystocks.auc.common.enums.LotStatusEnum; import cn.hobbystocks.auc.common.enums.PubStatusEnum; import cn.hobbystocks.auc.common.enums.RuleTypeEnum; import cn.hobbystocks.auc.common.exception.ServiceException; import cn.hobbystocks.auc.common.user.UserInfo; import cn.hobbystocks.auc.common.user.UserUtils; import cn.hobbystocks.auc.common.utils.CloneUtils; import cn.hobbystocks.auc.common.utils.DateUtils; import cn.hobbystocks.auc.common.utils.SensitiveDataUtils; import cn.hobbystocks.auc.common.utils.StringUtils; import cn.hobbystocks.auc.convert.LotConvert; import cn.hobbystocks.auc.domain.*; import cn.hobbystocks.auc.dto.LotExportDTO; import cn.hobbystocks.auc.event.ChangeEvent; import cn.hobbystocks.auc.event.EventPublisher; import cn.hobbystocks.auc.event.JPushEvent; import cn.hobbystocks.auc.event.StartBiddingEvent; import cn.hobbystocks.auc.handle.RuleHandlerHolder; import cn.hobbystocks.auc.handle.context.Live; import cn.hobbystocks.auc.handle.context.LiveContext; import cn.hobbystocks.auc.handle.context.tradition.TraditionRule; import cn.hobbystocks.auc.mapper.*; import cn.hobbystocks.auc.request.LotQueryRequest; import cn.hobbystocks.auc.request.LotRequest; import cn.hobbystocks.auc.response.LotDetailResponse; import cn.hobbystocks.auc.response.LotFansResponse; import cn.hobbystocks.auc.service.ILotService; import cn.hobbystocks.auc.task.DynamicTaskService; import cn.hobbystocks.auc.vo.LiveVO; import cn.hobbystocks.auc.vo.LotVO; import cn.hobbystocks.auc.vo.SelfVO; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.google.common.collect.Lists; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import java.math.BigDecimal; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @Service @Slf4j public class LotServiceImpl extends ServiceImpl implements ILotService { @Autowired private LotMapper lotMapper; @Autowired private BidMapper bidMapper; @Autowired private Locker locker; @Autowired private RuleHandlerHolder ruleHandlerHolder; @Autowired private RedisCache redisCache; @Autowired private AuctionMapper auctionMapper; @Autowired public EventPublisher eventPublisher; @Autowired(required = false) private CacheMap cacheMap; @Autowired private DynamicTaskService dynamicTaskService; @Autowired private LotServiceImpl _thiz; @Autowired private LotGroupMapper lotGroupMapper; @Autowired private LotFansMapper lotFansMapper; @Autowired private SpuCategoryMapper spuCategoryMapper; @Autowired private DepositOrderMapper depositOrderMapper; @Override public Lot selectLotById(Long id) { return lotMapper.selectLotById(id); } @Override public LotGroup selectLotGroupById(Long id) { return lotGroupMapper.selectLotGroupById(id); } @Override public List selectLotList(Lot lot) { IPage lotIPage=new Page<>(lot.getPageNum(),lot.getPageSize()); return lotMapper.selectLotList(lotIPage,lot); } @Override public List selectBiddingLotByAucId(Long aucId) { return lotMapper.selectNotEndLotList(aucId); } @Override public List selectLotByGroupIds(Long[] groupIds) { return null; } @Override public List selectLotGroupList(LotGroup lotGroup) { return lotGroupMapper.selectLotGroupList(lotGroup); } @Override public void cancelLotGroup(Long id) { LotGroup dbLotGroup = lotGroupMapper.selectLotGroupById(id); dbLotGroup.setStatus(Constants.GROUP_STATUS_WAITING); dbLotGroup.setPubStatus(Constants.PUB_STATUS_NO_PUBLISHED); lotGroupMapper.updateLotGroup(dbLotGroup); } @Override public List selectLotByGroupId(Long id) { return lotMapper.selectLotByGroupId(id); } @Override public List canCreateLive(Long merchantId) { return lotGroupMapper.canCreateLive(merchantId); } @Override public List findPubbedLotGroupByIds(Long[] lotGroupIds,Long merchantId) { return lotGroupMapper.findPubbedLotGroupByLotIds(lotGroupIds,merchantId); } @Override @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class) public Long party(LotGroup lotGroup) { AtomicLong result = new AtomicLong(0); locker.tryLock(String.format(Constants.REDIS_GROUP_LOCK_LOT_TEMPLATE, lotGroup.getId()), () -> { LotGroup dbLotGroup = lotGroupMapper.selectLotGroupById(lotGroup.getId()); Lot currLot = lotMapper.selectLotById(dbLotGroup.getNextLotId()); Lot nextLot = new Lot(); BeanUtils.copyProperties(dbLotGroup, nextLot, "id"); nextLot.setStatus(Constants.LOT_STATUS_WAITING); nextLot.setStartTime(null); nextLot.setCreateTime(new Date()); nextLot.setUpdateTime(null); nextLot.setPubTime(new Date()); nextLot.setPubStatus(Constants.PUB_STATUS_PUBLISHED); nextLot.setGroupId(lotGroup.getId()); nextLot.setAuctionId(2L); String name = currLot.getName(); int i = name.lastIndexOf("#"); if (i > 0 && i != name.length() -1) { String numStr = name.substring(i + 1); String nextNum = ""; try { long l = Long.parseLong(numStr); nextNum = "#" + (l + 1); }catch (Exception ignored) {} nextLot.setName(nextLot.getName() + nextNum); } lotMapper.updateLot( Lot.builder() .id(currLot.getId()) .startTime(new Date()) .build()); currLot.setStartTime(new Date()); long l = Objects.isNull(dbLotGroup.getFinishNum()) ? 0 : dbLotGroup.getFinishNum(); if (l >= dbLotGroup.getNum()) { throw new RuntimeException("商品库存已经用完"); }else if (l findStartingGroupByLotIds(Long[] lotIds) { return lotGroupMapper.findStartingGroupByIds(lotIds); } @Override @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class) public void updateExpire(LotFans fans) { lotFansMapper.updateLotFans( LotFans.builder() .id(fans.getId()) .type("pay_expire") .expire(DateUtils.addDays(DateUtils.toDate(fans.getCreateTime()), 1)) .build()); lotMapper.updateLot(Lot.builder().id(fans.getLotId()).status(Constants.LOT_STATUS_PASS).build()); } @Override @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class) public void updateSoldAndPaid(LotFans fans, Long groupId) { lotFansMapper.deleteLotFansById(fans.getId()); lotMapper.updatePay(fans.getLotId(), 1); lotGroupMapper.addSold(groupId); } @Override @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class) public void handleEndLotGroup(LotGroup lotGroup, String status) { LotGroup dbLotGroup = lotGroupMapper.selectLotGroupById(lotGroup.getId()); lotGroupMapper.updateLotGroupSold(dbLotGroup.getId()); } @Override @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class) public int insertLot(Lot lot) { lot.setCreateTime(DateUtils.getNowDate()); int result = lotMapper.insertLot(lot); dynamicTask(lot); return result; } @Override @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class) public int insertLotGroup(LotGroup lotGroup) { final int i = lotGroupMapper.insertLotGroup(lotGroup); Lot lot = new Lot(); BeanUtils.copyProperties(lotGroup, lot, "id"); lot.setStatus(Constants.LOT_STATUS_WAITING); lot.setStartTime(null); lot.setCreateTime(new Date()); lot.setUpdateTime(null); lot.setPubTime(new Date()); lot.setPubStatus(Constants.PUB_STATUS_PUBLISHED); lot.setGroupId(lotGroup.getId()); lot.setNum(1L); lot.setName(lot.getName() + "#1"); lotMapper.insertLot(lot); lotGroupMapper.updateLotGroup(LotGroup.builder().id(lotGroup.getId()).nextLotId(lot.getId()).build()); return i; } @Override public int updateLot(Lot lot) { lot.setUpdateTime(DateUtils.getNowDate()); return lotMapper.updateLot(lot); } @Override @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class) public int updateLotGroup(LotGroup lotGroup) { LotGroup dbLotGroup = lotGroupMapper.selectLotGroupById(lotGroup.getId()); if (Objects.nonNull(lotGroup.getNum()) && dbLotGroup.getNum() > lotGroup.getNum()) { int count = lotMapper.selectLotByGroupId(lotGroup.getId()).size() - 1; if (count > lotGroup.getNum()) { throw new RuntimeException("数量不合法"); } } lotGroup.setUpdateTime(DateUtils.getNowDate()); int result = lotGroupMapper.updateLotGroup(lotGroup); dbLotGroup = lotGroupMapper.selectLotGroupById(lotGroup.getId()); Lot lot = lotMapper.selectLotById(dbLotGroup.getNextLotId()); if (Constants.LOT_STATUS_WAITING.equals(lot.getStatus())) { String name = lot.getName(); String nextNum = ""; int i = name.lastIndexOf("#"); if (i > 0 && i != name.length() -1) { String numStr = name.substring(i + 1); try { long l = Long.parseLong(numStr); nextNum = "#" + l; }catch (Exception ignored) {} } BeanUtils.copyProperties(dbLotGroup, lot, "id","createTime", "name"); lot.setStatus(Constants.LOT_STATUS_WAITING); lot.setStartTime(null); lot.setPubTime(new Date()); lot.setPubStatus(Constants.PUB_STATUS_PUBLISHED); lot.setGroupId(lotGroup.getId()); lot.setName(dbLotGroup.getName() + nextNum); updateLot(lot); } return result; } @Override @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class) public int updateLotGroup0(LotGroup lotGroup) { return lotGroupMapper.updateLotGroup(lotGroup); } @Override public int updateLotView(Lot lot) { lot.setUpdateTime(DateUtils.getNowDate()); int result = lotMapper.updateLotView(lot); dynamicTask(lot); return result; } @Override @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class) public int updateLotEx(Lot lot) { lot.setUpdateTime(DateUtils.getNowDate()); int result = lotMapper.updateLotEx(lot); dynamicTask(lot); return result; } @Override public List selectBidding() { return lotMapper.selectBidding(); } @Override public List selectCancel() { return lotMapper.selectCancel(); } @Override public int deleteLotByIds(String ids) { return lotMapper.deleteLotByIds(Convert.toLongArray(ids)); } @Override public int deleteLotById(Long id) { return lotMapper.deleteLotById(id); } @Override @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class) public void pubLot(Lot lot) { locker.tryLock(String.format(Constants.REDIS_LOCK_LOT_TEMPLATE, lot.getId()), () ->{ lot.setPubTime(new Date()); lot.setPubStatus(Constants.PUB_STATUS_PUBLISHED); lot.setUpdateTime(new Date()); lotMapper.updateLot(lot); //构建竞价拍卖上下文 LiveContext liveContext = ruleHandlerHolder.pubLive(lot); if (liveContext.isLotUpdate()) { lotMapper.updateLot(lot); } redisCache.setCacheMapValue(String.format(Constants.REDIS_MAP_AUC_LOT_TEMPLATE, lot.getAuctionId()), lot.getId().toString(), liveContext.getLive()); }, true); } @Override @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class) public void pubLots(Auction auction) { List lots = lotMapper.selectLotByAucId(auction.getId()); for (Lot lot : lots) { lot.setUpdateBy(auction.getUpdateBy()); pubLot(lot); } } @Override @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class) public void insertLotAndPub(Lot lot) { insertLot(lot); pubLot(lot); } @Override @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class) public void updateLotAndPub(Lot lot) { updateLot(lot); pubLot(lot); } @Override public void removeLot(Lot lot) { Lot condition = new Lot(); condition.setId(lot.getId()); condition.setDelFlag(Constants.DEL_FLAG_DELETED); lotMapper.updateLot(condition); } @Override public void cancelLot(Lot lot) { lot.setUpdateTime(new Date()); lot.setStatus(Constants.LOT_STATUS_CANCELLED); lot.setPubStatus(Constants.PUB_STATUS_NO_PUBLISHED); lotMapper.updateLot(lot); ruleHandlerHolder.cancelLot(lot); } @Override @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class) public void live(Live live) { live = redisCache.getCacheMapValue(String.format(Constants.REDIS_MAP_AUC_LOT_TEMPLATE, live.getLot().getAuctionId()), live.getLot().getId().toString()); // 1秒的轮询 会存在失效数据 判断是否是失效数据 if (Objects.isNull(live)) { //是失效数据 return; } cacheMap.putLive(live); List bidList = redisCache.getCacheList(String.format(Constants.REDIS_MAP_AUC_LOT_BID_TEMPLATE, live.getLot().getId())); // 创建当前 context LiveContext liveContext = LiveContext.builder() .live(live) .dbLot(Lot.builder() .id(live.getLot().getId()) .auctionId(live.getLot().getAuctionId()) .build()) .bidList(bidList) .dbBid(getLastBid(bidList)) .build(); // 分析并设置当前状态 ruleHandlerHolder.live(liveContext); // 更新当前状态 到数据库和缓存 updateToRedisAndDb(liveContext); // 发送极光推送 jPush(liveContext); } private void jPush(LiveContext liveContext) { Live live = liveContext.getLive(); if (live.getCurrentEndTime() < (System.currentTimeMillis() + 1000 * 60 * 5)) { Long expire = redisCache.redisTemplate.getExpire(String.format(Constants.REDIS_LOCK_PUSH_LOT_TEMPLATE, live.getLot().getId())); if (Objects.isNull(expire) || expire < 0L) { eventPublisher.publishJPush(JPushEvent.builder().live(live).build()); redisCache.setCacheObject(String.format(Constants.REDIS_LOCK_PUSH_LOT_TEMPLATE, live.getLot().getId()), "jPush", 10, TimeUnit.MINUTES); } } } private Bid getLastBid(List bidList) { if (!CollectionUtils.isEmpty(bidList)) { return bidList.get(0); } return null; } private void updateToRedisAndDb(LiveContext liveContext) { Live live = liveContext.getLive(); List dataList = liveContext.getBidList(); ChangeEvent changeEvent = new ChangeEvent(live); if (liveContext.isLotUpdate()) { liveContext.getDbLot().setUpdateTime(new Date()); lotMapper.updateLot(liveContext.getDbLot()); } if (liveContext.isBidUpdate()) { bidMapper.updateBid(liveContext.getDbBid()); redisCache.setList(String.format(Constants.REDIS_MAP_AUC_LOT_BID_TEMPLATE, live.getLot().getId()), dataList); changeEvent.setBid(liveContext.getDbBid()); } Lot lot = live.getLot(); lot.setProperties(null); if (liveContext.isLiveUpdate()) { if (Constants.LOT_STATUS_SOLD.equals(live.getLot().getStatus()) || Constants.LOT_STATUS_PASS.equals(live.getLot().getStatus())) { redisCache.delCacheMapValue(String.format(Constants.REDIS_MAP_AUC_LOT_TEMPLATE, live.getLot().getAuctionId()), live.getLot().getId().toString()); redisCache.deleteObject(String.format(Constants.REDIS_MAP_AUC_LOT_BID_TEMPLATE, live.getLot().getId())); if (Constants.LOT_STATUS_SOLD.equals(live.getLot().getStatus())) { if(live.getBidId()==null){ long bidid = bidMapper.getCurrentBid(live.getLot().getId()); live.setBidId(bidid); } eventPublisher.publishSoldEvent(live, dataList); } eventPublisher.publishEndEvent(live); }else { redisCache.setCacheMapValue(String.format(Constants.REDIS_MAP_AUC_LOT_TEMPLATE, live.getLot().getAuctionId()), live.getLot().getId().toString(), live); } eventPublisher.publishChangeEvent(changeEvent); } } @Override public List live(Long auctionId) { LambdaQueryWrapper queryWrapper=new LambdaQueryWrapper<>(); queryWrapper.eq(Lot::getAuctionId,auctionId).eq(Lot::getDelFlag,Constants.DEL_FLAG_NO_DELETE); return lotMapper.selectList(queryWrapper); } @Override public List selfLive(SelfVO selfVO) { UserInfo userInfo = UserUtils.getSimpleUserInfo(); if (userInfo == null) { throw new ServiceException("璇峰厛鐧诲綍"); } List depositOrders = selectPaidDepositOrders(userInfo.getId()); List userBidList = bidMapper.selectBidList(Bid.builder() .accountId(userInfo.getId().toString()) .build()); List candidateLots = selectSelfCandidateLots(depositOrders, userBidList.stream() .map(Bid::getLotId) .filter(Objects::nonNull) .collect(Collectors.toSet())); Map cacheLive = new HashMap<>(); candidateLots.stream() .map(Lot::getAuctionId) .filter(Objects::nonNull) .distinct() .forEach(auctionId -> { Map auctionLive = CloneUtils.clone(cacheMap.viewAuction(auctionId)); if (!CollectionUtils.isEmpty(auctionLive)) { cacheLive.putAll(auctionLive); } }); return cacheLive.values().stream() .filter(live -> live.getAccountList().contains(UserUtils.getSimpleUserInfo().getId().toString()) && Constants.LOT_STATUS_BIDDING.equals(live.getLot().getStatus())) .map(live -> { List bidList = bidMapper.selectBidByLotId(live.getLot().getId()); LiveVO liveVO = new LiveVO(live); if (!CollectionUtils.isEmpty(bidList)) { liveVO.setLastPriceTime(bidList.get(0).getCreateTime().getTime()); } liveVO.setCurrUserBids( Objects.requireNonNull(bidList).stream() .filter(bid -> bid.getAccountId().equals(UserUtils.getSimpleUserInfo().getId().toString())) .collect(Collectors.toList()) ); return SensitiveDataUtils.handleViewDataSafe(liveVO); }).sorted(this::sort) .collect(Collectors.toList()); } private int sort(LiveVO live1, LiveVO live2) { return (int)(live2.getLastPriceTime() - live1.getLastPriceTime()); } @Override public List selfFinish(SelfVO selfVO) { return finishOrWin(null); } @Override public List selfWin(SelfVO selfVO) { return finishOrWin(1); } @Override public List selfList(SelfVO selfVO) { UserInfo userInfo = UserUtils.getSimpleUserInfo(); if (userInfo == null) { throw new ServiceException("请先登录"); } String userId = userInfo.getId().toString(); List depositOrders = selectPaidDepositOrders(userInfo.getId()); List userBidList = bidMapper.selectBidList(Bid.builder() .accountId(userId) .build()); Map> userBidMap = userBidList.stream() .filter(bid -> Objects.nonNull(bid.getLotId())) .collect(Collectors.groupingBy(Bid::getLotId)); List lotList = selectSelfCandidateLots(depositOrders, userBidMap.keySet()); String type = StringUtils.isEmpty(selfVO.getType()) ? "all" : selfVO.getType(); List result = new ArrayList<>(); for (Lot lot : lotList) { List userBids = userBidMap.getOrDefault(lot.getId(), Collections.emptyList()); boolean hasUserBid = !CollectionUtils.isEmpty(userBids); boolean hasWinBid = hasUserBid && userBids.stream().anyMatch(bid -> Objects.equals(bid.getStatus(), 1)); boolean hasDeposit = hasDepositQualification(lot, depositOrders); String selfStatus = resolveSelfStatus(lot, hasDeposit, hasUserBid, hasWinBid); if (matchSelfScene(type, selfStatus)) { result.add(buildSelfLotVo(lot, userBids, selfStatus)); } } sortSelfLots(type, result); return result; } private List selectPaidDepositOrders(Integer userId) { return depositOrderMapper.selectList(new LambdaQueryWrapper() .eq(DepositOrder::getUserId, userId) .eq(DepositOrder::getStatus, 1)); } private List selectSelfCandidateLots(List depositOrders, Collection bidLotIds) { Map lotMap = new LinkedHashMap<>(); if (!CollectionUtils.isEmpty(depositOrders)) { depositOrders.stream() .map(DepositOrder::getAuctionId) .filter(Objects::nonNull) .distinct() .forEach(auctionId -> addLots(lotMap, lotMapper.selectLotByAucId(auctionId))); } Set lotIds = new LinkedHashSet<>(); if (!CollectionUtils.isEmpty(bidLotIds)) { lotIds.addAll(bidLotIds); } if (!CollectionUtils.isEmpty(depositOrders)) { depositOrders.stream() .map(DepositOrder::getLotId) .filter(Objects::nonNull) .forEach(lotIds::add); } if (!CollectionUtils.isEmpty(lotIds)) { addLots(lotMap, lotMapper.selectLotListByLotIds(new ArrayList<>(lotIds))); } return new ArrayList<>(lotMap.values()); } private void addLots(Map lotMap, List lots) { if (CollectionUtils.isEmpty(lots)) { return; } lots.stream() .filter(lot -> Objects.nonNull(lot.getId())) .forEach(lot -> lotMap.put(lot.getId(), lot)); } private boolean hasDepositQualification(Lot lot, List depositOrders) { if (CollectionUtils.isEmpty(depositOrders)) { return false; } if (Objects.nonNull(lot.getDeposit())) { return depositOrders.stream().anyMatch(order -> Objects.equals("拍品", order.getDepositType()) && Objects.equals(order.getLotId(), lot.getId())); } return depositOrders.stream().anyMatch(order -> Objects.equals("拍卖会", order.getDepositType()) && Objects.equals(order.getAuctionId(), lot.getAuctionId())); } private String resolveSelfStatus(Lot lot, boolean hasDeposit, boolean hasUserBid, boolean hasWinBid) { if (isWaitingLot(lot, hasDeposit)) { return "waiting"; } if (isLiveLot(lot, hasDeposit, hasUserBid)) { return "live"; } if (isWinLot(lot, hasWinBid)) { return "win"; } if (isLoseLot(lot, hasUserBid, hasWinBid)) { return "lose"; } return null; } private boolean matchSelfScene(String type, String selfStatus) { if (StringUtils.isEmpty(selfStatus)) { return false; } if ("all".equals(type)) { return true; } if ("lose".equals(type) || "pass".equals(type) || "finish".equals(type)) { return "lose".equals(selfStatus); } if ("win".equals(type)) { return "win".equals(selfStatus); } return type.equals(selfStatus); } private boolean isWaitingLot(Lot lot, boolean hasDeposit) { return Constants.LOT_STATUS_WAITING.equals(lot.getStatus()) && hasDeposit; } private boolean isLiveLot(Lot lot, boolean hasDeposit, boolean hasUserBid) { return (Constants.LOT_STATUS_STARTING.equals(lot.getStatus()) || Constants.LOT_STATUS_BIDDING.equals(lot.getStatus())) && (hasDeposit || hasUserBid); } private boolean isWinLot(Lot lot, boolean hasWinBid) { return Constants.LOT_STATUS_SOLD.equals(lot.getStatus()) && hasWinBid; } private boolean isLoseLot(Lot lot, boolean hasUserBid, boolean hasWinBid) { if (!hasUserBid) { return false; } if (Constants.LOT_STATUS_PASS.equals(lot.getStatus())) { return true; } return Constants.LOT_STATUS_SOLD.equals(lot.getStatus()) && !hasWinBid; } private LotVO buildSelfLotVo(Lot lot, List userBids, String selfStatus) { LotVO lotVO = new LotVO(); BeanUtils.copyProperties(lot, lotVO); lotVO.setBids(userBids); TraditionRule rule = StringUtils.isEmpty(lot.getRuleContent()) ? null : JSON.parseObject(lot.getRuleContent(), TraditionRule.class); lotVO.setStartPrice(Objects.isNull(rule) ? null : rule.getStartPrice()); lotVO.setCurrentPrice(Objects.nonNull(lot.getLastPrice()) ? lot.getLastPrice() : null); lotVO.setSelfStatus(selfStatus); return SensitiveDataUtils.handleViewDataSafe(lotVO); } private void sortSelfLots(String type, List lots) { if ("waiting".equals(type)) { lots.sort(Comparator.comparing(Lot::getStartTime, Comparator.nullsLast(Date::compareTo))); return; } if ("live".equals(type)) { lots.sort(Comparator.comparing(Lot::getLastPriceTime, Comparator.nullsLast(Date::compareTo)).reversed()); return; } if ("lose".equals(type) || "pass".equals(type) || "finish".equals(type) || "win".equals(type)) { lots.sort(Comparator.comparing(Lot::getRealEndTime, Comparator.nullsLast(Date::compareTo)).reversed()); return; } List waiting = lots.stream().filter(lot -> "waiting".equals(lot.getSelfStatus())) .sorted(Comparator.comparing(Lot::getStartTime, Comparator.nullsLast(Date::compareTo))) .collect(Collectors.toList()); List live = lots.stream() .filter(lot -> "live".equals(lot.getSelfStatus())) .sorted(Comparator.comparing(Lot::getLastPriceTime, Comparator.nullsLast(Date::compareTo)).reversed()) .collect(Collectors.toList()); List lose = lots.stream() .filter(lot -> "lose".equals(lot.getSelfStatus()) || "win".equals(lot.getSelfStatus())) .sorted(Comparator.comparing(Lot::getRealEndTime, Comparator.nullsLast(Date::compareTo)).reversed()) .collect(Collectors.toList()); lots.clear(); lots.addAll(waiting); lots.addAll(live); lots.addAll(lose); } private List finishOrWin(Integer win) { String account = UserUtils.getSimpleUserInfo().getId().toString(); List resultList = new ArrayList<>(); List lotList = lotMapper.selectLotFinishOrWin(win, account); lotList.forEach(lot -> { if (Constants.LOT_STATUS_PASS.equals(lot.getStatus()) || Constants.LOT_STATUS_SOLD.equals(lot.getStatus())) { Bid condition = new Bid(); condition.setLotId(lot.getId()); condition.setAccountId(account); condition.setStatus(win); List bids = bidMapper.selectBidList(condition); if (!CollectionUtils.isEmpty(bids)) { LotVO lotVO = new LotVO(); BeanUtils.copyProperties(lot, lotVO); lotVO.setBids(bids); resultList.add(SensitiveDataUtils.handleViewDataSafe(lotVO)); } } }); return resultList; } @Override @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class) public void handleDelay(Long id) { Lot dbLot = lotMapper.selectLotById(id); Lot update = new Lot(); BeanUtils.copyProperties(dbLot, update); update.setCreateBy("SYSTEM"); update.setCreateTime(new Date()); update.setPubStatus(Constants.PUB_STATUS_NO_PUBLISHED); update.setStatus(Constants.LOT_STATUS_WAITING); update.setDelFlag(Constants.DEL_FLAG_NO_DELETE); update.setStartTime(DateUtils.addDays(dbLot.getStartTime(), 1)); update.setEndTime(DateUtils.addDays(dbLot.getEndTime(), 1)); String name = update.getName(); if (!StringUtils.isEmpty(name)) { Pattern pattern = Pattern.compile("第(\\d+)期"); Matcher matcher = pattern.matcher(name); if (matcher.find()) { String group = matcher.group(0); String noStr = matcher.group(1); int no = (Integer.parseInt(noStr)) + 1; name = name.replace(group, "第" + no + "期"); update.setName(name); } } lotMapper.insertLotClone(update); dbLot.setDelayPublish(null); lotMapper.updateLotView(dbLot); if (!Objects.equals(dbLot.getPubStatus(), Constants.PUB_STATUS_PUBLISHED)) { pubLot(dbLot); } dynamicTask(update); } @Override public void dynamicTasks() { lotMapper.dynamicLot().forEach(this::dynamicTask); } @Override public void dynamicTask(Lot lot) { //延时发布时间 String delayPublish = lot.getDelayPublish(); if (!StringUtils.isEmpty(delayPublish)) { dynamicTaskService.updateTask(lot.getId().toString(), DateUtils.getNextExecutionTime(delayPublish), () -> { _thiz.handleDelay(lot.getId()); }); }else { dynamicTaskService.removeTask(lot.getId().toString()); } } @Override public List selectLotListByAucId(Long aucId) { return getBaseMapper().selectLotByAucId(aucId); } @Override public void calculatorAndUpdateLotHot() { //查询正在竞价中的拍品 List lots = lotMapper.selectBidding(); lots.forEach(lot -> { //计算出价权重 double bidWeight = lot.getBidCount() * 0.5; //计算收藏权重 LambdaQueryWrapper lotFansLambdaQueryWrapper = new LambdaQueryWrapper<>(); Long lotFansCount = lotFansMapper.selectCount(lotFansLambdaQueryWrapper.eq(LotFans::getLotId, lot.getId())); double lotFansWeight = lotFansCount * 0.2; //计算时间权重,竞拍结束时间-当前时间=剩余时间 Date endTime = lot.getEndTime(); int timeWeight; Date date = new Date(); Date date1 = DateUtils.addHours(date, 2); if (date1.after(endTime)){ } //计算涨价幅度权重 Long currentBid = bidMapper.getCurrentBid(lot.getId()); }); } @Override public List selectBiddingLotList() { return getBaseMapper().selectBiddingLotList(); } @Override public List queryLotByCategory(LotQueryRequest request) { // 1. 构建最终要查询的分类ID List categoryIds = Lists.newArrayList(); if (Objects.nonNull(request.getChildCategoryId())) { // 传了二级分类 → 直接用 categoryIds.add(request.getChildCategoryId()); } else if (Objects.nonNull(request.getMainCategoryId())) { // 只传一级 → 查询该一级下所有二级ID categoryIds = spuCategoryMapper.listChildByParentId(request.getMainCategoryId()); } // 2. 没有分类 → 返回空 if (CollectionUtils.isEmpty(categoryIds)) { return Lists.newArrayList(); } // 3. 查询拍品ID List lotIds = spuCategoryMapper.listLotIdsByCategoryIds(categoryIds); if (CollectionUtils.isEmpty(lotIds)) { return Lists.newArrayList(); } IPage lotIPage =new Page<>(request.getPageNum(),request.getPageSize()); Lot lot = new Lot(); lot.setIds(lotIds); lot.setName(request.getName()); lot.setPubStatus(request.getPubStatus()); lot.setStatus(request.getStatus()); lot.setStartTime(request.getStartTime()); lot.setEndTime(request.getEndTime()); lot.setRealEndTime(request.getRealEndTime()); List diamondPositions = baseMapper.selectLotList(lotIPage, lot); List lotFansResponses = diamondPositions.stream().map(LotConvert.INSTANCE::toLotFansResponse).collect(Collectors.toList()); return lotFansResponses; } @Override public LotDetailResponse queryLotDetail(Long lotId) { Lot lot = lotMapper.selectLotById(lotId); if (Objects.isNull(lot)) { throw new ServiceException("未找到拍品数据"); } Auction auction = auctionMapper.selectAuctionById(lot.getAuctionId()); if (Objects.isNull(auction)) { throw new ServiceException("未找到拍卖会相关数据"); } TraditionRule rule = StringUtils.isEmpty(lot.getRuleContent()) ? null : JSON.parseObject(lot.getRuleContent(), TraditionRule.class); BigDecimal startPrice = Objects.isNull(rule) ? null : rule.getStartPrice(); BigDecimal markupAmount = Objects.isNull(rule) ? null : rule.getDefaultOnceAddPrice(); Long depositAmount = lot.getDeposit(); String depositType = "LOT"; if (Objects.isNull(depositAmount)) { depositAmount = auction.getDeposit(); depositType = "AUCTION"; } LotDetailResponse response = new LotDetailResponse(); response.setLotId(lot.getId()); response.setAuctionId(lot.getAuctionId()); response.setName(lot.getName()); response.setAuctionName(auction.getName()); response.setAuctionDesc(auction.getDescription()); response.setRuleContent(lot.getRuleContent()); response.setAuctionImgs(auction.getImgs()); response.setImgs(lot.getImgs()); response.setCarouselImgs(lot.getCarouselImgs()); response.setStatus(lot.getStatus()); Date startTime = toBeijingTime(lot.getStartTime()); Date endTime = toBeijingTime(lot.getEndTime()); Date realEndTime = toBeijingTime(lot.getRealEndTime()); response.setStartTime(startTime); response.setEndTime(endTime); response.setRealEndTime(realEndTime); response.setStartPrice(startPrice); response.setCurrentPrice(Objects.nonNull(lot.getLastPrice()) ? lot.getLastPrice() : startPrice); response.setBidCount(lot.getBidCount()); response.setDepositAmount(depositAmount); response.setDepositType(depositType); response.setServiceTariff(lot.getServiceTariff()); response.setRuleType(lot.getRuleType()); response.setMarkupAmount(markupAmount); response.setDetail(lot.getDetail()); response.setProperties(lot.getProperties()); response.setIsFans(Boolean.FALSE); response.setHasDeposit(Boolean.FALSE); response.setPayTimeLimit(lot.getPayTimeLimit()); if (Objects.equals("Waiting", lot.getStatus()) && Objects.nonNull(startTime)) { response.setTimestamp(startTime.getTime() - System.currentTimeMillis()); } else if (Objects.equals("Bidding", lot.getStatus()) && Objects.nonNull(endTime)) { response.setTimestamp(endTime.getTime() - System.currentTimeMillis()); } //TODO 成交用户暂时无 fillUserFlags(lot, response, depositType); return response; } private Date toBeijingTime(Date date) { return Objects.isNull(date) ? null : DateUtils.addHours(date, 8); } private void fillUserFlags(Lot lot, LotDetailResponse response, String depositType) { UserInfo userInfo = UserUtils.getSimpleUserInfo(); if (Objects.isNull(userInfo)) { return; } Long favoriteCount = lotFansMapper.selectCount(new LambdaQueryWrapper() .eq(LotFans::getLotId, lot.getId()) .eq(LotFans::getUserId, userInfo.getId().longValue()) .eq(LotFans::getType, "user_like")); response.setIsFans(favoriteCount > 0); LambdaQueryWrapper depositQuery = new LambdaQueryWrapper() .eq(DepositOrder::getUserId, userInfo.getId()) .in(DepositOrder::getStatus, 1); if (Objects.equals("LOT", depositType)) { depositQuery.eq(DepositOrder::getLotId, lot.getId()) .eq(DepositOrder::getDepositType, "拍品"); } else { depositQuery.eq(DepositOrder::getAuctionId, lot.getAuctionId()) .eq(DepositOrder::getDepositType, "拍卖会"); } Long depositCount = depositOrderMapper.selectCount(depositQuery); response.setHasDeposit(depositCount > 0); } @Override public List exportLotList(LotRequest request) { if (Objects.isNull(request.getPageNum()) && Objects.isNull(request.getPageSize())) { request.setPageNum(1); request.setPageSize(1000); } request.setDelFlag(Constants.DEL_FLAG_NO_DELETE); IPage lotIPage=new Page<>(request.getPageNum(),request.getPageSize()); List lotList = lotMapper.selectLotList(lotIPage, request); List lotExportDTOS = lotList.stream().map(l -> { LotExportDTO lotExportDTO = new LotExportDTO(); lotExportDTO.setId(String.valueOf(l.getId())); lotExportDTO.setGoodsId(l.getGoodsId()); lotExportDTO.setGoodsName(l.getGoodsName()); lotExportDTO.setGoodsType(l.getGoodsType()); lotExportDTO.setSort(String.valueOf(l.getSort())); lotExportDTO.setBidCount(String.valueOf(l.getBidCount())); lotExportDTO.setPubStatus(PubStatusEnum.of(l.getPubStatus()).getDesc()); lotExportDTO.setStatus(LotStatusEnum.of(l.getStatus()).getDesc()); lotExportDTO.setCreateTime(DateUtils.parseDateToStr(l.getCreateTime())); lotExportDTO.setStartTime(DateUtils.parseDateToStr(l.getStartTime())); lotExportDTO.setEndTime(DateUtils.parseDateToStr(l.getEndTime())); lotExportDTO.setDeposit(String.valueOf(l.getDeposit() == null ? 0 : l.getDeposit())); lotExportDTO.setServiceTariff(String.valueOf(l.getServiceTariff())); lotExportDTO.setRuleType(RuleTypeEnum.getByCode(l.getRuleType()).getDesc()); // 起拍价 if (StringUtils.isNotEmpty(l.getRuleContent())) { TraditionRule traditionRule = JSON.parseObject(l.getRuleContent(), TraditionRule.class); lotExportDTO.setLastPrice(String.valueOf(traditionRule.getStartPrice())); } return lotExportDTO; }).collect(Collectors.toList()); return lotExportDTOS; } @Override public List selectLotListByLotIds(List lotIds) { return baseMapper.selectLotListByLotIds(lotIds); } }