LotServiceImpl.java 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949
  1. package cn.hobbystocks.auc.service.impl;
  2. import cn.hobbystocks.auc.cache.CacheMap;
  3. import cn.hobbystocks.auc.common.constant.Constants;
  4. import cn.hobbystocks.auc.common.core.redis.Locker;
  5. import cn.hobbystocks.auc.common.core.redis.RedisCache;
  6. import cn.hobbystocks.auc.common.core.text.Convert;
  7. import cn.hobbystocks.auc.common.enums.LotStatusEnum;
  8. import cn.hobbystocks.auc.common.enums.PubStatusEnum;
  9. import cn.hobbystocks.auc.common.enums.RuleTypeEnum;
  10. import cn.hobbystocks.auc.common.exception.ServiceException;
  11. import cn.hobbystocks.auc.common.user.UserInfo;
  12. import cn.hobbystocks.auc.common.user.UserUtils;
  13. import cn.hobbystocks.auc.common.utils.CloneUtils;
  14. import cn.hobbystocks.auc.common.utils.DateUtils;
  15. import cn.hobbystocks.auc.common.utils.SensitiveDataUtils;
  16. import cn.hobbystocks.auc.common.utils.StringUtils;
  17. import cn.hobbystocks.auc.convert.LotConvert;
  18. import cn.hobbystocks.auc.domain.*;
  19. import cn.hobbystocks.auc.dto.LotExportDTO;
  20. import cn.hobbystocks.auc.event.ChangeEvent;
  21. import cn.hobbystocks.auc.event.EventPublisher;
  22. import cn.hobbystocks.auc.event.JPushEvent;
  23. import cn.hobbystocks.auc.event.StartBiddingEvent;
  24. import cn.hobbystocks.auc.handle.RuleHandlerHolder;
  25. import cn.hobbystocks.auc.handle.context.Live;
  26. import cn.hobbystocks.auc.handle.context.LiveContext;
  27. import cn.hobbystocks.auc.handle.context.tradition.TraditionRule;
  28. import cn.hobbystocks.auc.mapper.*;
  29. import cn.hobbystocks.auc.request.LotQueryRequest;
  30. import cn.hobbystocks.auc.request.LotRequest;
  31. import cn.hobbystocks.auc.response.LotDetailResponse;
  32. import cn.hobbystocks.auc.response.LotFansResponse;
  33. import cn.hobbystocks.auc.service.ILotService;
  34. import cn.hobbystocks.auc.task.DynamicTaskService;
  35. import cn.hobbystocks.auc.vo.LiveVO;
  36. import cn.hobbystocks.auc.vo.LotVO;
  37. import cn.hobbystocks.auc.vo.SelfVO;
  38. import com.alibaba.fastjson.JSON;
  39. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  40. import com.baomidou.mybatisplus.core.metadata.IPage;
  41. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  42. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  43. import com.google.common.collect.Lists;
  44. import lombok.extern.slf4j.Slf4j;
  45. import org.springframework.beans.BeanUtils;
  46. import org.springframework.beans.factory.annotation.Autowired;
  47. import org.springframework.stereotype.Service;
  48. import org.springframework.transaction.annotation.Isolation;
  49. import org.springframework.transaction.annotation.Transactional;
  50. import org.springframework.util.CollectionUtils;
  51. import java.math.BigDecimal;
  52. import java.util.*;
  53. import java.util.concurrent.TimeUnit;
  54. import java.util.concurrent.atomic.AtomicLong;
  55. import java.util.regex.Matcher;
  56. import java.util.regex.Pattern;
  57. import java.util.stream.Collectors;
  58. @Service
  59. @Slf4j
  60. public class LotServiceImpl extends ServiceImpl<LotMapper,Lot> implements ILotService
  61. {
  62. @Autowired
  63. private LotMapper lotMapper;
  64. @Autowired
  65. private BidMapper bidMapper;
  66. @Autowired
  67. private Locker locker;
  68. @Autowired
  69. private RuleHandlerHolder ruleHandlerHolder;
  70. @Autowired
  71. private RedisCache redisCache;
  72. @Autowired
  73. private AuctionMapper auctionMapper;
  74. @Autowired
  75. public EventPublisher eventPublisher;
  76. @Autowired(required = false)
  77. private CacheMap cacheMap;
  78. @Autowired
  79. private DynamicTaskService dynamicTaskService;
  80. @Autowired
  81. private LotServiceImpl _thiz;
  82. @Autowired
  83. private LotGroupMapper lotGroupMapper;
  84. @Autowired
  85. private LotFansMapper lotFansMapper;
  86. @Autowired
  87. private SpuCategoryMapper spuCategoryMapper;
  88. @Autowired
  89. private DepositOrderMapper depositOrderMapper;
  90. @Override
  91. public Lot selectLotById(Long id) {
  92. return lotMapper.selectLotById(id);
  93. }
  94. @Override
  95. public LotGroup selectLotGroupById(Long id) {
  96. return lotGroupMapper.selectLotGroupById(id);
  97. }
  98. @Override
  99. public List<Lot> selectLotList(Lot lot) {
  100. IPage<Lot> lotIPage=new Page<>(lot.getPageNum(),lot.getPageSize());
  101. return lotMapper.selectLotList(lotIPage,lot);
  102. }
  103. @Override
  104. public List<Lot> selectBiddingLotByAucId(Long aucId) {
  105. return lotMapper.selectNotEndLotList(aucId);
  106. }
  107. @Override
  108. public List<Lot> selectLotByGroupIds(Long[] groupIds) {
  109. return null;
  110. }
  111. @Override
  112. public List<LotGroup> selectLotGroupList(LotGroup lotGroup) {
  113. return lotGroupMapper.selectLotGroupList(lotGroup);
  114. }
  115. @Override
  116. public void cancelLotGroup(Long id) {
  117. LotGroup dbLotGroup = lotGroupMapper.selectLotGroupById(id);
  118. dbLotGroup.setStatus(Constants.GROUP_STATUS_WAITING);
  119. dbLotGroup.setPubStatus(Constants.PUB_STATUS_NO_PUBLISHED);
  120. lotGroupMapper.updateLotGroup(dbLotGroup);
  121. }
  122. @Override
  123. public List<Lot> selectLotByGroupId(Long id) {
  124. return lotMapper.selectLotByGroupId(id);
  125. }
  126. @Override
  127. public List<LotGroup> canCreateLive(Long merchantId) {
  128. return lotGroupMapper.canCreateLive(merchantId);
  129. }
  130. @Override
  131. public List<LotGroup> findPubbedLotGroupByIds(Long[] lotGroupIds,Long merchantId) {
  132. return lotGroupMapper.findPubbedLotGroupByLotIds(lotGroupIds,merchantId);
  133. }
  134. @Override
  135. @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
  136. public Long party(LotGroup lotGroup) {
  137. AtomicLong result = new AtomicLong(0);
  138. locker.tryLock(String.format(Constants.REDIS_GROUP_LOCK_LOT_TEMPLATE, lotGroup.getId()), () -> {
  139. LotGroup dbLotGroup = lotGroupMapper.selectLotGroupById(lotGroup.getId());
  140. Lot currLot = lotMapper.selectLotById(dbLotGroup.getNextLotId());
  141. Lot nextLot = new Lot();
  142. BeanUtils.copyProperties(dbLotGroup, nextLot, "id");
  143. nextLot.setStatus(Constants.LOT_STATUS_WAITING);
  144. nextLot.setStartTime(null);
  145. nextLot.setCreateTime(new Date());
  146. nextLot.setUpdateTime(null);
  147. nextLot.setPubTime(new Date());
  148. nextLot.setPubStatus(Constants.PUB_STATUS_PUBLISHED);
  149. nextLot.setGroupId(lotGroup.getId());
  150. nextLot.setAuctionId(2L);
  151. String name = currLot.getName();
  152. int i = name.lastIndexOf("#");
  153. if (i > 0 && i != name.length() -1) {
  154. String numStr = name.substring(i + 1);
  155. String nextNum = "";
  156. try {
  157. long l = Long.parseLong(numStr);
  158. nextNum = "#" + (l + 1);
  159. }catch (Exception ignored) {}
  160. nextLot.setName(nextLot.getName() + nextNum);
  161. }
  162. lotMapper.updateLot(
  163. Lot.builder()
  164. .id(currLot.getId())
  165. .startTime(new Date())
  166. .build());
  167. currLot.setStartTime(new Date());
  168. long l = Objects.isNull(dbLotGroup.getFinishNum()) ? 0 : dbLotGroup.getFinishNum();
  169. if (l >= dbLotGroup.getNum()) {
  170. throw new RuntimeException("商品库存已经用完");
  171. }else if (l<dbLotGroup.getNum()-1){
  172. lotMapper.insertLot(nextLot);
  173. }
  174. lotGroupMapper.updateLotGroup(LotGroup.builder()
  175. .id(lotGroup.getId())
  176. .lotId(currLot.getId())
  177. .nextLotId(nextLot.getId())
  178. .finishNum(Objects.isNull(dbLotGroup.getFinishNum()) ? 0 : dbLotGroup.getFinishNum() + 1)
  179. .status(Constants.LOT_STATUS_STARTING)
  180. .build()
  181. );
  182. pubLot(currLot);
  183. result.set(currLot.getId());
  184. eventPublisher.publishStartBiddingEvent(new StartBiddingEvent(dbLotGroup.getId()));
  185. }, true, "开启失败,请重试!");
  186. return result.get();
  187. }
  188. @Override
  189. public List<LotGroup> findStartingGroupByLotIds(Long[] lotIds) {
  190. return lotGroupMapper.findStartingGroupByIds(lotIds);
  191. }
  192. @Override
  193. @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
  194. public void updateExpire(LotFans fans) {
  195. lotFansMapper.updateLotFans(
  196. LotFans.builder()
  197. .id(fans.getId())
  198. .type("pay_expire")
  199. .expire(DateUtils.addDays(DateUtils.toDate(fans.getCreateTime()), 1))
  200. .build());
  201. lotMapper.updateLot(Lot.builder().id(fans.getLotId()).status(Constants.LOT_STATUS_PASS).build());
  202. }
  203. @Override
  204. @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
  205. public void updateSoldAndPaid(LotFans fans, Long groupId) {
  206. lotFansMapper.deleteLotFansById(fans.getId());
  207. lotMapper.updatePay(fans.getLotId(), 1);
  208. lotGroupMapper.addSold(groupId);
  209. }
  210. @Override
  211. @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
  212. public void handleEndLotGroup(LotGroup lotGroup, String status) {
  213. LotGroup dbLotGroup = lotGroupMapper.selectLotGroupById(lotGroup.getId());
  214. lotGroupMapper.updateLotGroupSold(dbLotGroup.getId());
  215. }
  216. @Override
  217. @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
  218. public int insertLot(Lot lot) {
  219. lot.setCreateTime(DateUtils.getNowDate());
  220. int result = lotMapper.insertLot(lot);
  221. dynamicTask(lot);
  222. return result;
  223. }
  224. @Override
  225. @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
  226. public int insertLotGroup(LotGroup lotGroup) {
  227. final int i = lotGroupMapper.insertLotGroup(lotGroup);
  228. Lot lot = new Lot();
  229. BeanUtils.copyProperties(lotGroup, lot, "id");
  230. lot.setStatus(Constants.LOT_STATUS_WAITING);
  231. lot.setStartTime(null);
  232. lot.setCreateTime(new Date());
  233. lot.setUpdateTime(null);
  234. lot.setPubTime(new Date());
  235. lot.setPubStatus(Constants.PUB_STATUS_PUBLISHED);
  236. lot.setGroupId(lotGroup.getId());
  237. lot.setNum(1L);
  238. lot.setName(lot.getName() + "#1");
  239. lotMapper.insertLot(lot);
  240. lotGroupMapper.updateLotGroup(LotGroup.builder().id(lotGroup.getId()).nextLotId(lot.getId()).build());
  241. return i;
  242. }
  243. @Override
  244. public int updateLot(Lot lot) {
  245. lot.setUpdateTime(DateUtils.getNowDate());
  246. return lotMapper.updateLot(lot);
  247. }
  248. @Override
  249. @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
  250. public int updateLotGroup(LotGroup lotGroup) {
  251. LotGroup dbLotGroup = lotGroupMapper.selectLotGroupById(lotGroup.getId());
  252. if (Objects.nonNull(lotGroup.getNum()) && dbLotGroup.getNum() > lotGroup.getNum()) {
  253. int count = lotMapper.selectLotByGroupId(lotGroup.getId()).size() - 1;
  254. if (count > lotGroup.getNum()) {
  255. throw new RuntimeException("数量不合法");
  256. }
  257. }
  258. lotGroup.setUpdateTime(DateUtils.getNowDate());
  259. int result = lotGroupMapper.updateLotGroup(lotGroup);
  260. dbLotGroup = lotGroupMapper.selectLotGroupById(lotGroup.getId());
  261. Lot lot = lotMapper.selectLotById(dbLotGroup.getNextLotId());
  262. if (Constants.LOT_STATUS_WAITING.equals(lot.getStatus())) {
  263. String name = lot.getName();
  264. String nextNum = "";
  265. int i = name.lastIndexOf("#");
  266. if (i > 0 && i != name.length() -1) {
  267. String numStr = name.substring(i + 1);
  268. try {
  269. long l = Long.parseLong(numStr);
  270. nextNum = "#" + l;
  271. }catch (Exception ignored) {}
  272. }
  273. BeanUtils.copyProperties(dbLotGroup, lot, "id","createTime", "name");
  274. lot.setStatus(Constants.LOT_STATUS_WAITING);
  275. lot.setStartTime(null);
  276. lot.setPubTime(new Date());
  277. lot.setPubStatus(Constants.PUB_STATUS_PUBLISHED);
  278. lot.setGroupId(lotGroup.getId());
  279. lot.setName(dbLotGroup.getName() + nextNum);
  280. updateLot(lot);
  281. }
  282. return result;
  283. }
  284. @Override
  285. @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
  286. public int updateLotGroup0(LotGroup lotGroup) {
  287. return lotGroupMapper.updateLotGroup(lotGroup);
  288. }
  289. @Override
  290. public int updateLotView(Lot lot) {
  291. lot.setUpdateTime(DateUtils.getNowDate());
  292. int result = lotMapper.updateLotView(lot);
  293. dynamicTask(lot);
  294. return result;
  295. }
  296. @Override
  297. @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
  298. public int updateLotEx(Lot lot) {
  299. lot.setUpdateTime(DateUtils.getNowDate());
  300. int result = lotMapper.updateLotEx(lot);
  301. dynamicTask(lot);
  302. return result;
  303. }
  304. @Override
  305. public List<Lot> selectBidding() {
  306. return lotMapper.selectBidding();
  307. }
  308. @Override
  309. public List<Lot> selectCancel() {
  310. return lotMapper.selectCancel();
  311. }
  312. @Override
  313. public int deleteLotByIds(String ids) {
  314. return lotMapper.deleteLotByIds(Convert.toLongArray(ids));
  315. }
  316. @Override
  317. public int deleteLotById(Long id) {
  318. return lotMapper.deleteLotById(id);
  319. }
  320. @Override
  321. @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
  322. public void pubLot(Lot lot) {
  323. locker.tryLock(String.format(Constants.REDIS_LOCK_LOT_TEMPLATE, lot.getId()), () ->{
  324. lot.setPubTime(new Date());
  325. lot.setPubStatus(Constants.PUB_STATUS_PUBLISHED);
  326. lot.setUpdateTime(new Date());
  327. lotMapper.updateLot(lot);
  328. //构建竞价拍卖上下文
  329. LiveContext liveContext = ruleHandlerHolder.pubLive(lot);
  330. if (liveContext.isLotUpdate()) {
  331. lotMapper.updateLot(lot);
  332. }
  333. redisCache.setCacheMapValue(String.format(Constants.REDIS_MAP_AUC_LOT_TEMPLATE, lot.getAuctionId()), lot.getId().toString(), liveContext.getLive());
  334. }, true);
  335. }
  336. @Override
  337. @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
  338. public void pubLots(Auction auction) {
  339. List<Lot> lots = lotMapper.selectLotByAucId(auction.getId());
  340. for (Lot lot : lots) {
  341. lot.setUpdateBy(auction.getUpdateBy());
  342. pubLot(lot);
  343. }
  344. }
  345. @Override
  346. @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
  347. public void insertLotAndPub(Lot lot) {
  348. insertLot(lot);
  349. pubLot(lot);
  350. }
  351. @Override
  352. @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
  353. public void updateLotAndPub(Lot lot) {
  354. updateLot(lot);
  355. pubLot(lot);
  356. }
  357. @Override
  358. public void removeLot(Lot lot) {
  359. Lot condition = new Lot();
  360. condition.setId(lot.getId());
  361. condition.setDelFlag(Constants.DEL_FLAG_DELETED);
  362. lotMapper.updateLot(condition);
  363. }
  364. @Override
  365. public void cancelLot(Lot lot) {
  366. lot.setUpdateTime(new Date());
  367. lot.setStatus(Constants.LOT_STATUS_CANCELLED);
  368. lot.setPubStatus(Constants.PUB_STATUS_NO_PUBLISHED);
  369. lotMapper.updateLot(lot);
  370. ruleHandlerHolder.cancelLot(lot);
  371. }
  372. @Override
  373. @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
  374. public void live(Live live) {
  375. live = redisCache.getCacheMapValue(String.format(Constants.REDIS_MAP_AUC_LOT_TEMPLATE, live.getLot().getAuctionId()), live.getLot().getId().toString());
  376. // 1秒的轮询 会存在失效数据 判断是否是失效数据
  377. if (Objects.isNull(live)) {
  378. //是失效数据
  379. return;
  380. }
  381. cacheMap.putLive(live);
  382. List<Bid> bidList = redisCache.getCacheList(String.format(Constants.REDIS_MAP_AUC_LOT_BID_TEMPLATE, live.getLot().getId()));
  383. // 创建当前 context
  384. LiveContext liveContext = LiveContext.builder()
  385. .live(live)
  386. .dbLot(Lot.builder()
  387. .id(live.getLot().getId())
  388. .auctionId(live.getLot().getAuctionId())
  389. .build())
  390. .bidList(bidList)
  391. .dbBid(getLastBid(bidList))
  392. .build();
  393. // 分析并设置当前状态
  394. ruleHandlerHolder.live(liveContext);
  395. // 更新当前状态 到数据库和缓存
  396. updateToRedisAndDb(liveContext);
  397. // 发送极光推送
  398. jPush(liveContext);
  399. }
  400. private void jPush(LiveContext liveContext) {
  401. Live live = liveContext.getLive();
  402. if (live.getCurrentEndTime() < (System.currentTimeMillis() + 1000 * 60 * 5)) {
  403. Long expire = redisCache.redisTemplate.getExpire(String.format(Constants.REDIS_LOCK_PUSH_LOT_TEMPLATE, live.getLot().getId()));
  404. if (Objects.isNull(expire) || expire < 0L) {
  405. eventPublisher.publishJPush(JPushEvent.builder().live(live).build());
  406. redisCache.setCacheObject(String.format(Constants.REDIS_LOCK_PUSH_LOT_TEMPLATE, live.getLot().getId()), "jPush", 10, TimeUnit.MINUTES);
  407. }
  408. }
  409. }
  410. private Bid getLastBid(List<Bid> bidList) {
  411. if (!CollectionUtils.isEmpty(bidList)) {
  412. return bidList.get(0);
  413. }
  414. return null;
  415. }
  416. private void updateToRedisAndDb(LiveContext liveContext) {
  417. Live live = liveContext.getLive();
  418. List<Bid> dataList = liveContext.getBidList();
  419. ChangeEvent changeEvent = new ChangeEvent(live);
  420. if (liveContext.isLotUpdate()) {
  421. liveContext.getDbLot().setUpdateTime(new Date());
  422. lotMapper.updateLot(liveContext.getDbLot());
  423. }
  424. if (liveContext.isBidUpdate()) {
  425. bidMapper.updateBid(liveContext.getDbBid());
  426. redisCache.setList(String.format(Constants.REDIS_MAP_AUC_LOT_BID_TEMPLATE, live.getLot().getId()), dataList);
  427. changeEvent.setBid(liveContext.getDbBid());
  428. }
  429. if (liveContext.isLiveUpdate()) {
  430. if (Constants.LOT_STATUS_SOLD.equals(live.getLot().getStatus()) ||
  431. Constants.LOT_STATUS_PASS.equals(live.getLot().getStatus())) {
  432. redisCache.delCacheMapValue(String.format(Constants.REDIS_MAP_AUC_LOT_TEMPLATE, live.getLot().getAuctionId()), live.getLot().getId().toString());
  433. redisCache.deleteObject(String.format(Constants.REDIS_MAP_AUC_LOT_BID_TEMPLATE, live.getLot().getId()));
  434. if (Constants.LOT_STATUS_SOLD.equals(live.getLot().getStatus())) {
  435. if(live.getBidId()==null){
  436. long bidid = bidMapper.getCurrentBid(live.getLot().getId());
  437. live.setBidId(bidid);
  438. }
  439. eventPublisher.publishSoldEvent(live, dataList);
  440. }
  441. eventPublisher.publishEndEvent(live);
  442. }else {
  443. redisCache.setCacheMapValue(String.format(Constants.REDIS_MAP_AUC_LOT_TEMPLATE, live.getLot().getAuctionId()), live.getLot().getId().toString(), live);
  444. }
  445. eventPublisher.publishChangeEvent(changeEvent);
  446. }
  447. }
  448. @Override
  449. public List<Lot> live(Long auctionId) {
  450. LambdaQueryWrapper<Lot> queryWrapper=new LambdaQueryWrapper<>();
  451. queryWrapper.eq(Lot::getAuctionId,auctionId).eq(Lot::getDelFlag,Constants.DEL_FLAG_NO_DELETE);
  452. return lotMapper.selectList(queryWrapper);
  453. }
  454. @Override
  455. public List<LiveVO> selfLive(SelfVO selfVO) {
  456. Map<String, Live> cacheLive = CloneUtils.clone(cacheMap.viewAuction(selfVO.getAuctionId()));
  457. return cacheLive.values().stream()
  458. .filter(live -> live.getAccountList().contains(UserUtils.getSimpleUserInfo().getId().toString()) &&
  459. Constants.LOT_STATUS_BIDDING.equals(live.getLot().getStatus()))
  460. .map(live -> {
  461. List<Bid> bidList = bidMapper.selectBidByLotId(live.getLot().getId());
  462. LiveVO liveVO = new LiveVO(live);
  463. if (!CollectionUtils.isEmpty(bidList)) {
  464. liveVO.setLastPriceTime(bidList.get(0).getCreateTime().getTime());
  465. }
  466. liveVO.setCurrUserBids(
  467. Objects.requireNonNull(bidList).stream()
  468. .filter(bid -> bid.getAccountId().equals(UserUtils.getSimpleUserInfo().getId().toString()))
  469. .collect(Collectors.toList())
  470. );
  471. return SensitiveDataUtils.handleViewDataSafe(liveVO);
  472. }).sorted(this::sort)
  473. .collect(Collectors.toList());
  474. }
  475. private int sort(LiveVO live1, LiveVO live2) {
  476. return (int)(live2.getLastPriceTime() - live1.getLastPriceTime());
  477. }
  478. @Override
  479. public List<LotVO> selfFinish(SelfVO selfVO) {
  480. return finishOrWin(selfVO, null);
  481. }
  482. @Override
  483. public List<LotVO> selfWin(SelfVO selfVO) {
  484. return finishOrWin(selfVO, 1);
  485. }
  486. @Override
  487. public List<LotVO> selfList(SelfVO selfVO) {
  488. UserInfo userInfo = UserUtils.getSimpleUserInfo();
  489. if (userInfo == null) {
  490. throw new ServiceException("请先登录");
  491. }
  492. String userId = userInfo.getId().toString();
  493. List<Lot> lotList = lotMapper.selectLotByAucId(selfVO.getAuctionId());
  494. List<DepositOrder> depositOrders = depositOrderMapper.selectList(new LambdaQueryWrapper<DepositOrder>()
  495. .eq(DepositOrder::getUserId, userInfo.getId())
  496. .eq(DepositOrder::getAuctionId, selfVO.getAuctionId())
  497. .in(DepositOrder::getStatus, 1));
  498. String type = StringUtils.isEmpty(selfVO.getType()) ? "all" : selfVO.getType();
  499. List<LotVO> result = new ArrayList<>();
  500. for (Lot lot : lotList) {
  501. List<Bid> userBids = bidMapper.selectBidList(Bid.builder()
  502. .lotId(lot.getId())
  503. .accountId(userId)
  504. .build());
  505. boolean hasUserBid = !CollectionUtils.isEmpty(userBids);
  506. boolean hasWinBid = hasUserBid && userBids.stream().anyMatch(bid -> Objects.equals(bid.getStatus(), 1));
  507. boolean hasDeposit = hasDepositQualification(lot, depositOrders);
  508. if (matchSelfScene(type, lot, hasDeposit, hasUserBid, hasWinBid)) {
  509. result.add(buildSelfLotVo(lot, userBids));
  510. }
  511. }
  512. sortSelfLots(type, result);
  513. return result;
  514. }
  515. private boolean hasDepositQualification(Lot lot, List<DepositOrder> depositOrders) {
  516. if (CollectionUtils.isEmpty(depositOrders)) {
  517. return false;
  518. }
  519. if (Objects.nonNull(lot.getDeposit())) {
  520. return depositOrders.stream().anyMatch(order -> Objects.equals("拍品", order.getDepositType())
  521. && Objects.equals(order.getLotId(), lot.getId()));
  522. }
  523. return depositOrders.stream().anyMatch(order -> Objects.equals("拍卖会", order.getDepositType())
  524. && Objects.equals(order.getAuctionId(), lot.getAuctionId()));
  525. }
  526. private boolean matchSelfScene(String type, Lot lot, boolean hasDeposit, boolean hasUserBid, boolean hasWinBid) {
  527. if ("all".equals(type)) {
  528. return isWaitingLot(lot, hasDeposit) || isLiveLot(lot, hasDeposit, hasUserBid) || isLoseLot(lot, hasUserBid, hasWinBid);
  529. }
  530. if ("waiting".equals(type)) {
  531. return isWaitingLot(lot, hasDeposit);
  532. }
  533. if ("live".equals(type)) {
  534. return isLiveLot(lot, hasDeposit, hasUserBid);
  535. }
  536. if ("lose".equals(type) || "pass".equals(type) || "finish".equals(type)) {
  537. return isLoseLot(lot, hasUserBid, hasWinBid);
  538. }
  539. return false;
  540. }
  541. private boolean isWaitingLot(Lot lot, boolean hasDeposit) {
  542. return Constants.LOT_STATUS_WAITING.equals(lot.getStatus()) && hasDeposit;
  543. }
  544. private boolean isLiveLot(Lot lot, boolean hasDeposit, boolean hasUserBid) {
  545. return (Constants.LOT_STATUS_STARTING.equals(lot.getStatus()) || Constants.LOT_STATUS_BIDDING.equals(lot.getStatus()))
  546. && (hasDeposit || hasUserBid);
  547. }
  548. private boolean isLoseLot(Lot lot, boolean hasUserBid, boolean hasWinBid) {
  549. if (!hasUserBid) {
  550. return false;
  551. }
  552. if (Constants.LOT_STATUS_PASS.equals(lot.getStatus())) {
  553. return true;
  554. }
  555. return Constants.LOT_STATUS_SOLD.equals(lot.getStatus()) && !hasWinBid;
  556. }
  557. private LotVO buildSelfLotVo(Lot lot, List<Bid> userBids) {
  558. LotVO lotVO = new LotVO();
  559. BeanUtils.copyProperties(lot, lotVO);
  560. lotVO.setBids(userBids);
  561. lotVO.setCurrentPrice(Objects.nonNull(lot.getLastPrice()) ? lot.getLastPrice() : null);
  562. return SensitiveDataUtils.handleViewDataSafe(lotVO);
  563. }
  564. private void sortSelfLots(String type, List<LotVO> lots) {
  565. if ("waiting".equals(type)) {
  566. lots.sort(Comparator.comparing(Lot::getStartTime, Comparator.nullsLast(Date::compareTo)));
  567. return;
  568. }
  569. if ("live".equals(type)) {
  570. lots.sort(Comparator.comparing(Lot::getLastPriceTime, Comparator.nullsLast(Date::compareTo)).reversed());
  571. return;
  572. }
  573. if ("lose".equals(type) || "pass".equals(type) || "finish".equals(type)) {
  574. lots.sort(Comparator.comparing(Lot::getRealEndTime, Comparator.nullsLast(Date::compareTo)).reversed());
  575. return;
  576. }
  577. List<LotVO> waiting = lots.stream().filter(lot -> Constants.LOT_STATUS_WAITING.equals(lot.getStatus()))
  578. .sorted(Comparator.comparing(Lot::getStartTime, Comparator.nullsLast(Date::compareTo)))
  579. .collect(Collectors.toList());
  580. List<LotVO> live = lots.stream()
  581. .filter(lot -> Constants.LOT_STATUS_STARTING.equals(lot.getStatus()) || Constants.LOT_STATUS_BIDDING.equals(lot.getStatus()))
  582. .sorted(Comparator.comparing(Lot::getLastPriceTime, Comparator.nullsLast(Date::compareTo)).reversed())
  583. .collect(Collectors.toList());
  584. List<LotVO> lose = lots.stream()
  585. .filter(lot -> Constants.LOT_STATUS_PASS.equals(lot.getStatus()) || Constants.LOT_STATUS_SOLD.equals(lot.getStatus()))
  586. .sorted(Comparator.comparing(Lot::getRealEndTime, Comparator.nullsLast(Date::compareTo)).reversed())
  587. .collect(Collectors.toList());
  588. lots.clear();
  589. lots.addAll(waiting);
  590. lots.addAll(live);
  591. lots.addAll(lose);
  592. }
  593. private List<LotVO> finishOrWin(SelfVO selfVO, Integer win) {
  594. String account = UserUtils.getSimpleUserInfo().getId().toString();
  595. List<LotVO> resultList = new ArrayList<>();
  596. List<Lot> lotList = lotMapper.selectLotByAucId(selfVO.getAuctionId());
  597. lotList.forEach(lot -> {
  598. if (Constants.LOT_STATUS_PASS.equals(lot.getStatus()) || Constants.LOT_STATUS_SOLD.equals(lot.getStatus())) {
  599. Bid condition = new Bid();
  600. condition.setLotId(lot.getId());
  601. condition.setAccountId(account);
  602. condition.setStatus(win);
  603. List<Bid> bids = bidMapper.selectBidList(condition);
  604. if (!CollectionUtils.isEmpty(bids)) {
  605. LotVO lotVO = new LotVO();
  606. BeanUtils.copyProperties(lot, lotVO);
  607. lotVO.setBids(bids);
  608. resultList.add(SensitiveDataUtils.handleViewDataSafe(lotVO));
  609. }
  610. }
  611. });
  612. return resultList;
  613. }
  614. @Override
  615. @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
  616. public void handleDelay(Long id) {
  617. Lot dbLot = lotMapper.selectLotById(id);
  618. Lot update = new Lot();
  619. BeanUtils.copyProperties(dbLot, update);
  620. update.setCreateBy("SYSTEM");
  621. update.setCreateTime(new Date());
  622. update.setPubStatus(Constants.PUB_STATUS_NO_PUBLISHED);
  623. update.setStatus(Constants.LOT_STATUS_WAITING);
  624. update.setDelFlag(Constants.DEL_FLAG_NO_DELETE);
  625. update.setStartTime(DateUtils.addDays(dbLot.getStartTime(), 1));
  626. update.setEndTime(DateUtils.addDays(dbLot.getEndTime(), 1));
  627. String name = update.getName();
  628. if (!StringUtils.isEmpty(name)) {
  629. Pattern pattern = Pattern.compile("第(\\d+)期");
  630. Matcher matcher = pattern.matcher(name);
  631. if (matcher.find()) {
  632. String group = matcher.group(0);
  633. String noStr = matcher.group(1);
  634. int no = (Integer.parseInt(noStr)) + 1;
  635. name = name.replace(group, "第" + no + "期");
  636. update.setName(name);
  637. }
  638. }
  639. lotMapper.insertLotClone(update);
  640. dbLot.setDelayPublish(null);
  641. lotMapper.updateLotView(dbLot);
  642. if (!Objects.equals(dbLot.getPubStatus(), Constants.PUB_STATUS_PUBLISHED)) {
  643. pubLot(dbLot);
  644. }
  645. dynamicTask(update);
  646. }
  647. @Override
  648. public void dynamicTasks() {
  649. lotMapper.dynamicLot().forEach(this::dynamicTask);
  650. }
  651. @Override
  652. public void dynamicTask(Lot lot) {
  653. //延时发布时间
  654. String delayPublish = lot.getDelayPublish();
  655. if (!StringUtils.isEmpty(delayPublish)) {
  656. dynamicTaskService.updateTask(lot.getId().toString(), DateUtils.getNextExecutionTime(delayPublish), () -> {
  657. _thiz.handleDelay(lot.getId());
  658. });
  659. }else {
  660. dynamicTaskService.removeTask(lot.getId().toString());
  661. }
  662. }
  663. @Override
  664. public List<Lot> selectLotListByAucId(Long aucId) {
  665. return getBaseMapper().selectLotByAucId(aucId);
  666. }
  667. @Override
  668. public void calculatorAndUpdateLotHot() {
  669. //查询正在竞价中的拍品
  670. List<Lot> lots = lotMapper.selectBidding();
  671. lots.forEach(lot -> {
  672. //计算出价权重
  673. double bidWeight = lot.getBidCount() * 0.5;
  674. //计算收藏权重
  675. LambdaQueryWrapper<LotFans> lotFansLambdaQueryWrapper = new LambdaQueryWrapper<>();
  676. Long lotFansCount = lotFansMapper.selectCount(lotFansLambdaQueryWrapper.eq(LotFans::getLotId, lot.getId()));
  677. double lotFansWeight = lotFansCount * 0.2;
  678. //计算时间权重,竞拍结束时间-当前时间=剩余时间
  679. Date endTime = lot.getEndTime();
  680. int timeWeight;
  681. Date date = new Date();
  682. Date date1 = DateUtils.addHours(date, 2);
  683. if (date1.after(endTime)){
  684. }
  685. //计算涨价幅度权重
  686. Long currentBid = bidMapper.getCurrentBid(lot.getId());
  687. });
  688. }
  689. @Override
  690. public List<Lot> selectBiddingLotList() {
  691. return getBaseMapper().selectBiddingLotList();
  692. }
  693. @Override
  694. public List<LotFansResponse> queryLotByCategory(LotQueryRequest request) {
  695. // 1. 构建最终要查询的分类ID
  696. List<Long> categoryIds = Lists.newArrayList();
  697. if (Objects.nonNull(request.getChildCategoryId())) {
  698. // 传了二级分类 → 直接用
  699. categoryIds.add(request.getChildCategoryId());
  700. } else if (Objects.nonNull(request.getMainCategoryId())) {
  701. // 只传一级 → 查询该一级下所有二级ID
  702. categoryIds = spuCategoryMapper.listChildByParentId(request.getMainCategoryId());
  703. }
  704. // 2. 没有分类 → 返回空
  705. if (CollectionUtils.isEmpty(categoryIds)) {
  706. return Lists.newArrayList();
  707. }
  708. // 3. 查询拍品ID
  709. List<Long> lotIds = spuCategoryMapper.listLotIdsByCategoryIds(categoryIds);
  710. if (CollectionUtils.isEmpty(lotIds)) {
  711. return Lists.newArrayList();
  712. }
  713. IPage<Lot> lotIPage =new Page<>(request.getPageNum(),request.getPageSize());
  714. Lot lot = new Lot();
  715. lot.setIds(lotIds);
  716. lot.setName(request.getName());
  717. lot.setPubStatus(request.getPubStatus());
  718. lot.setStatus(request.getStatus());
  719. lot.setStartTime(request.getStartTime());
  720. lot.setEndTime(request.getEndTime());
  721. lot.setRealEndTime(request.getRealEndTime());
  722. List<Lot> diamondPositions = baseMapper.selectLotList(lotIPage, lot);
  723. List<LotFansResponse> lotFansResponses = diamondPositions.stream().map(LotConvert.INSTANCE::toLotFansResponse).collect(Collectors.toList());
  724. return lotFansResponses;
  725. }
  726. @Override
  727. public LotDetailResponse queryLotDetail(Long lotId) {
  728. Lot lot = lotMapper.selectLotById(lotId);
  729. if (Objects.isNull(lot)) {
  730. throw new ServiceException("未找到拍品数据");
  731. }
  732. Auction auction = auctionMapper.selectAuctionById(lot.getAuctionId());
  733. if (Objects.isNull(auction)) {
  734. throw new ServiceException("未找到拍卖会相关数据");
  735. }
  736. TraditionRule rule = StringUtils.isEmpty(lot.getRuleContent())
  737. ? null
  738. : JSON.parseObject(lot.getRuleContent(), TraditionRule.class);
  739. BigDecimal startPrice = Objects.isNull(rule) ? null : rule.getStartPrice();
  740. BigDecimal markupAmount = Objects.isNull(rule) ? null : rule.getDefaultOnceAddPrice();
  741. Long depositAmount = lot.getDeposit();
  742. String depositType = "LOT";
  743. if (Objects.isNull(depositAmount)) {
  744. depositAmount = auction.getDeposit();
  745. depositType = "AUCTION";
  746. }
  747. LotDetailResponse response = new LotDetailResponse();
  748. response.setLotId(lot.getId());
  749. response.setAuctionId(lot.getAuctionId());
  750. response.setName(lot.getName());
  751. response.setAuctionName(auction.getName());
  752. response.setAuctionDesc(auction.getDescription());
  753. response.setRuleContent(lot.getRuleContent());
  754. response.setAuctionImgs(auction.getImgs());
  755. response.setImgs(lot.getImgs());
  756. response.setCarouselImgs(lot.getCarouselImgs());
  757. response.setStatus(lot.getStatus());
  758. Date startTime = toBeijingTime(lot.getStartTime());
  759. Date endTime = toBeijingTime(lot.getEndTime());
  760. Date realEndTime = toBeijingTime(lot.getRealEndTime());
  761. response.setStartTime(startTime);
  762. response.setEndTime(endTime);
  763. response.setRealEndTime(realEndTime);
  764. response.setStartPrice(startPrice);
  765. response.setCurrentPrice(Objects.nonNull(lot.getLastPrice()) ? lot.getLastPrice() : startPrice);
  766. response.setBidCount(lot.getBidCount());
  767. response.setDepositAmount(depositAmount);
  768. response.setDepositType(depositType);
  769. response.setServiceTariff(lot.getServiceTariff());
  770. response.setRuleType(lot.getRuleType());
  771. response.setMarkupAmount(markupAmount);
  772. response.setDetail(lot.getDetail());
  773. response.setProperties(lot.getProperties());
  774. response.setIsFans(Boolean.FALSE);
  775. response.setHasDeposit(Boolean.FALSE);
  776. response.setPayTimeLimit(lot.getPayTimeLimit());
  777. if (Objects.equals("Waiting", lot.getStatus()) && Objects.nonNull(startTime)) {
  778. response.setTimestamp(startTime.getTime() - System.currentTimeMillis());
  779. } else if (Objects.equals("Bidding", lot.getStatus()) && Objects.nonNull(endTime)) {
  780. response.setTimestamp(endTime.getTime() - System.currentTimeMillis());
  781. }
  782. //TODO 成交用户暂时无
  783. fillUserFlags(lot, response, depositType);
  784. return response;
  785. }
  786. private Date toBeijingTime(Date date) {
  787. return Objects.isNull(date) ? null : DateUtils.addHours(date, 8);
  788. }
  789. private void fillUserFlags(Lot lot, LotDetailResponse response, String depositType) {
  790. UserInfo userInfo = UserUtils.getSimpleUserInfo();
  791. if (Objects.isNull(userInfo)) {
  792. return;
  793. }
  794. Long favoriteCount = lotFansMapper.selectCount(new LambdaQueryWrapper<LotFans>()
  795. .eq(LotFans::getLotId, lot.getId())
  796. .eq(LotFans::getUserId, userInfo.getId().longValue())
  797. .eq(LotFans::getType, "user_like"));
  798. response.setIsFans(favoriteCount > 0);
  799. LambdaQueryWrapper<DepositOrder> depositQuery = new LambdaQueryWrapper<DepositOrder>()
  800. .eq(DepositOrder::getUserId, userInfo.getId())
  801. .in(DepositOrder::getStatus, 1);
  802. if (Objects.equals("LOT", depositType)) {
  803. depositQuery.eq(DepositOrder::getLotId, lot.getId())
  804. .eq(DepositOrder::getDepositType, "拍品");
  805. } else {
  806. depositQuery.eq(DepositOrder::getAuctionId, lot.getAuctionId())
  807. .eq(DepositOrder::getDepositType, "拍卖会");
  808. }
  809. Long depositCount = depositOrderMapper.selectCount(depositQuery);
  810. response.setHasDeposit(depositCount > 0);
  811. }
  812. @Override
  813. public List<LotExportDTO> exportLotList(LotRequest request) {
  814. if (Objects.isNull(request.getPageNum()) && Objects.isNull(request.getPageSize())) {
  815. request.setPageNum(1);
  816. request.setPageSize(1000);
  817. }
  818. request.setDelFlag(Constants.DEL_FLAG_NO_DELETE);
  819. IPage<Lot> lotIPage=new Page<>(request.getPageNum(),request.getPageSize());
  820. List<Lot> lotList = lotMapper.selectLotList(lotIPage, request);
  821. List<LotExportDTO> lotExportDTOS = lotList.stream().map(l -> {
  822. LotExportDTO lotExportDTO = new LotExportDTO();
  823. lotExportDTO.setId(String.valueOf(l.getId()));
  824. lotExportDTO.setGoodsId(l.getGoodsId());
  825. lotExportDTO.setGoodsName(l.getGoodsName());
  826. lotExportDTO.setGoodsType(l.getGoodsType());
  827. lotExportDTO.setSort(String.valueOf(l.getSort()));
  828. lotExportDTO.setBidCount(String.valueOf(l.getBidCount()));
  829. lotExportDTO.setPubStatus(PubStatusEnum.of(l.getPubStatus()).getDesc());
  830. lotExportDTO.setStatus(LotStatusEnum.of(l.getStatus()).getDesc());
  831. lotExportDTO.setCreateTime(DateUtils.parseDateToStr(l.getCreateTime()));
  832. lotExportDTO.setStartTime(DateUtils.parseDateToStr(l.getStartTime()));
  833. lotExportDTO.setEndTime(DateUtils.parseDateToStr(l.getEndTime()));
  834. lotExportDTO.setDeposit(String.valueOf(l.getDeposit() == null ? 0 : l.getDeposit()));
  835. lotExportDTO.setServiceTariff(String.valueOf(l.getServiceTariff()));
  836. lotExportDTO.setRuleType(RuleTypeEnum.getByCode(l.getRuleType()).getDesc());
  837. // 起拍价
  838. if (StringUtils.isNotEmpty(l.getRuleContent())) {
  839. TraditionRule traditionRule = JSON.parseObject(l.getRuleContent(), TraditionRule.class);
  840. lotExportDTO.setLastPrice(String.valueOf(traditionRule.getStartPrice()));
  841. }
  842. return lotExportDTO;
  843. }).collect(Collectors.toList());
  844. return lotExportDTOS;
  845. }
  846. @Override
  847. public List<Lot> selectLotListByLotIds(List<Long> lotIds) {
  848. return baseMapper.selectLotListByLotIds(lotIds);
  849. }
  850. }