| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- package com.izouma.nineth.service;
- import cn.hutool.core.collection.CollUtil;
- import com.izouma.nineth.config.RedisKeys;
- import com.izouma.nineth.domain.Asset;
- import com.izouma.nineth.domain.AuctionActivity;
- import com.izouma.nineth.domain.User;
- import com.izouma.nineth.dto.PageQuery;
- import com.izouma.nineth.dto.auction.AuctionInputDTO;
- import com.izouma.nineth.enums.*;
- import com.izouma.nineth.exception.BusinessException;
- import com.izouma.nineth.repo.*;
- import com.izouma.nineth.utils.JpaUtils;
- import com.izouma.nineth.utils.SecurityUtils;
- import lombok.AllArgsConstructor;
- import lombok.extern.slf4j.Slf4j;
- import org.apache.commons.lang3.ObjectUtils;
- import org.springframework.core.env.Environment;
- import org.springframework.data.domain.Page;
- import org.springframework.data.redis.core.BoundValueOperations;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.scheduling.TaskScheduler;
- import org.springframework.scheduling.annotation.Scheduled;
- import org.springframework.security.crypto.password.PasswordEncoder;
- import org.springframework.stereotype.Service;
- import javax.annotation.PostConstruct;
- import java.math.BigDecimal;
- import java.time.LocalDateTime;
- import java.time.ZoneId;
- import java.time.temporal.ChronoUnit;
- import java.util.*;
- import java.util.concurrent.ScheduledFuture;
- import java.util.concurrent.TimeUnit;
- @Slf4j
- @Service
- @AllArgsConstructor
- public class AuctionActivityService {
- private final AuctionActivityRepo auctionActivityRepo;
- private final AssetRepo assetRepo;
- private final UserRepo userRepo;
- private final PasswordEncoder passwordEncoder;
- private final RedisTemplate<String, Object> redisTemplate;
- private final CacheService cacheService;
- private final TaskScheduler taskScheduler;
- private final Environment env;
- private final SysConfigService sysConfigService;
- private final RockRecordService rockRecordService;
- private final Map<Long, ScheduledFuture<?>> tasks = new HashMap<>();
- @PostConstruct
- public void init() {
- if (Arrays.asList(env.getActiveProfiles()).contains("dev")) {
- return;
- }
- List<AuctionActivity> activities = auctionActivityRepo.findByStartTimeBeforeAndStatus(LocalDateTime.now(),
- AuctionStatus.NOTSTARTED);
- for (AuctionActivity activity : activities) {
- onShelfTask(activity);
- }
- }
- public Page<AuctionActivity> all(PageQuery pageQuery) {
- return auctionActivityRepo
- .findAll(JpaUtils.toSpecification(pageQuery, AuctionActivity.class), JpaUtils.toPageRequest(pageQuery));
- }
- public AuctionActivity createFromAsset(AuctionInputDTO dto) {
- Asset asset = assetRepo.findById(dto.getAssetId()).orElseThrow(new BusinessException("暂无"));
- //拍卖周期
- BigDecimal auctionCycle = sysConfigService.getBigDecimal("auction_cycle");
- AuctionActivity auctionActivity = new AuctionActivity();
- auctionActivity.setAuctionType(AuctionType.NFT);
- auctionActivity.setAssetId(dto.getAssetId());
- auctionActivity.setStatus(AuctionStatus.ONGOING);
- auctionActivity.setBids(0);
- auctionActivity.setCategory(asset.getCategory());
- auctionActivity.setEndTime(LocalDateTime.now()
- .plusHours(auctionCycle.multiply(new BigDecimal("24")).intValue()));
- auctionActivity.setDeposit(dto.getDeposit());
- if (Arrays.asList(env.getActiveProfiles()).contains("staging")) {
- auctionActivity.setEndTime(LocalDateTime.now().plusMinutes(8));
- }
- auctionActivity.setDetail(asset.getDetail());
- auctionActivity.setFixedPrice(dto.getFixedPrice());
- auctionActivity.setIncrement(dto.getIncrement());
- auctionActivity.setMinter(asset.getMinter());
- auctionActivity.setPic(asset.getPic());
- auctionActivity.setModel3d(asset.getModel3d());
- auctionActivity.setName(asset.getName());
- auctionActivity.setSeller(asset.getOwner());
- auctionActivity.setSellerId(asset.getOwnerId());
- auctionActivity.setStartTime(LocalDateTime.now());
- auctionActivity.setSource(AuctionSource.TRANSFER);
- //固定值 or 资产值
- auctionActivity.setServiceCharge(asset.getServiceCharge());
- auctionActivity.setRoyalties(asset.getRoyalties());
- auctionActivity.setStartingPrice(dto.getStartingPrice());
- auctionActivity.setHasFixedPrice(auctionActivity.getFixedPrice() != null);
- return save(auctionActivity, dto.getTradeCode());
- }
- public AuctionActivity save(AuctionActivity record, String tradeCode) {
- if (record.getSource().equals(AuctionSource.OFFICIAL)) {
- record.setStatus(AuctionStatus.NOTSTARTED);
- }
- if (record.getSource().equals(AuctionSource.TRANSFER) && !record.getSellerId().equals(9859L)) {
- User user = userRepo.findById(record.getSellerId()).orElseThrow(new BusinessException("无用户信息"));
- if (!passwordEncoder.matches(tradeCode, user.getTradeCode())) {
- throw new BusinessException("交易密码错误");
- }
- if (!AuthStatus.SUCCESS.equals(user.getAuthStatus())) {
- throw new BusinessException("未实名或实名未通过");
- }
- BigDecimal userBuy = rockRecordService.getRock(user.getId()).getRecord();
- BigDecimal num = sysConfigService.getBigDecimal("auction_lvzhoushi_num");
- if (userBuy.compareTo(num) < 0) {
- throw new BusinessException("绿洲石不足");
- }
- Asset asset = assetRepo.findById(record.getAssetId()).orElseThrow(new BusinessException("未找到该藏品"));
- if (!asset.getOwnerId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
- throw new BusinessException("非本人藏品,无法操作.");
- }
- int holdDays;
- if (ObjectUtils.isEmpty(asset.getHoldDays())) {
- holdDays = sysConfigService.getInt("hold_days");
- } else {
- holdDays = asset.getHoldDays();
- }
- if (holdDays == 0 && AssetSource.OFFICIAL.equals(asset.getSource())) {
- BigDecimal officialConsignment = sysConfigService.getBigDecimal("OFFICIAL_CONSIGNMENT");
- //天转小时
- int hour = officialConsignment.multiply(new BigDecimal("24")).intValue();
- if (ChronoUnit.HOURS.between(asset.getCreatedAt(), LocalDateTime.now()) < hour) {
- throw new BusinessException("需持有满" + hour + "小时后才能进行拍卖");
- }
- }
- if (ChronoUnit.DAYS.between(asset.getCreatedAt(), LocalDateTime.now()) < holdDays) {
- throw new BusinessException("需持有满" + holdDays + "天才能进行拍卖");
- }
- if (!asset.getStatus().equals(AssetStatus.NORMAL)) {
- throw new BusinessException("藏品状态异常,无法操作.");
- }
- if (asset.isPublicShow() || asset.isConsignment()) {
- // throw new BusinessException("藏品已寄售,取消寄售后再申请拍卖。");
- throw new BusinessException("请先下架藏品");
- }
- //是否二次拍卖
- List<AuctionActivity> activity = auctionActivityRepo.findByAssetId(asset.getId());
- if (CollUtil.isNotEmpty(activity)) {
- if (activity.stream().anyMatch(ac -> !AuctionStatus.PASS.equals(ac.getStatus()))) {
- throw new BusinessException("已有拍卖");
- }
- log.info("下架流拍拍卖:assetId-{}", asset.getId());
- auctionActivityRepo.updateOnShelf(asset.getId(), false);
- }
- asset.setStatus(AssetStatus.AUCTIONING);
- assetRepo.save(asset);
- }
- //上架
- record.setOnShelf(true);
- AuctionActivity saved = auctionActivityRepo.save(record);
- if (saved.getStatus().equals(AuctionStatus.NOTSTARTED)) {
- onShelfTask(saved);
- }
- return saved;
- }
- public synchronized String changeStatus(Long id, AuctionStatus status) {
- BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps(RedisKeys.AUCTION_STATUS + id);
- if (ops.get() == null) {
- Boolean success = ops.setIfAbsent(Optional.ofNullable(auctionActivityRepo.getStatus(id))
- .orElse(AuctionStatus.NOTSTARTED.toString()), 7, TimeUnit.DAYS);
- log.info("创建redis拍卖活动状态:{}", success);
- }
- String stock = (String) ops.getAndSet(status.toString());
- syncStatus(id);
- return stock;
- }
- // @Debounce(key = "#id", delay = 500)
- public void syncStatus(Long id) {
- String stock = (String) redisTemplate.opsForValue().get(RedisKeys.AUCTION_STATUS + id);
- if (stock != null) {
- log.info("同步拍卖活动状态信息{},{}", id, stock);
- auctionActivityRepo.updateStatus(id, AuctionStatus.valueOf(stock));
- cacheService.clearAuction(id);
- }
- }
- public synchronized String getStatus(Long id) {
- BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps(RedisKeys.AUCTION_STATUS + id);
- String stock = (String) ops.get();
- if (stock == null) {
- Boolean success = ops.setIfAbsent(Optional.ofNullable(auctionActivityRepo.getStatus(id))
- .orElse(AuctionStatus.NOTSTARTED.toString()), 7, TimeUnit.DAYS);
- log.info("创建redis拍卖活动状态:{}", success);
- return (String) ops.get();
- }
- return stock;
- }
- private void onShelfTask(AuctionActivity record) {
- ScheduledFuture<?> task = tasks.get(record.getId());
- if (task != null) {
- if (!task.cancel(true)) {
- return;
- }
- }
- if (record.getStatus().equals(AuctionStatus.NOTSTARTED)) {
- if (record.getStartTime().minusSeconds(2).isAfter(LocalDateTime.now())) {
- Date date = Date.from(record.getStartTime().atZone(ZoneId.systemDefault()).toInstant());
- ScheduledFuture<?> future = taskScheduler.schedule(() -> {
- // this.changeStatus(record.getId(), AuctionStatus.ONGOING);
- auctionActivityRepo.updateStatus(record.getId(), AuctionStatus.ONGOING);
- tasks.remove(record.getId());
- }, date);
- tasks.put(record.getId(), future);
- } else {
- // this.changeStatus(record.getId(), AuctionStatus.ONGOING);
- auctionActivityRepo.updateStatus(record.getId(), AuctionStatus.ONGOING);
- }
- }
- }
- public void offShelfTask(AuctionActivity record) {
- Long id = record.getId();
- ScheduledFuture<?> task = tasks.get(id);
- if (task != null) {
- if (!task.cancel(true)) {
- return;
- }
- }
- // AuctionActivity recordNew = auctionActivityRepo.findById(id)
- // .orElseThrow(new BusinessException("无数据"));
- if (record.getStatus().equals(AuctionStatus.ONGOING)) {
- if (record.getEndTime().minusSeconds(2).isAfter(LocalDateTime.now())) {
- Date date = Date.from(record.getEndTime().atZone(ZoneId.systemDefault()).toInstant());
- ScheduledFuture<?> future = taskScheduler.schedule(() -> {
- AuctionActivity recordNew1 = auctionActivityRepo.findById(id)
- .orElseThrow(new BusinessException("无数据"));
- if (ObjectUtils.isNotEmpty(recordNew1.getPurchasePrice())) {
- log.info("拍卖成交{}", recordNew1.getId());
- // this.changeStatus(recordNew1.getId(), AuctionStatus.PURCHASED);
- auctionActivityRepo.updateStatus(recordNew1.getId(), AuctionStatus.PURCHASED);
- } else {
- //没有成交价,无人出价过
- log.info("拍卖流拍Task-else{}", recordNew1.getId());
- auctionActivityRepo.scheduleOffShelf(recordNew1.getId(), AuctionStatus.PASS);
- if (AuctionSource.TRANSFER.equals(recordNew1.getSource())) {
- Asset asset = assetRepo.findById(recordNew1.getAssetId())
- .orElseThrow(new BusinessException("暂无"));
- asset.setStatus(AssetStatus.NORMAL);
- asset.setConsignment(false);
- asset.setPublicShow(false);
- assetRepo.save(asset);
- }
- }
- tasks.remove(id);
- }, date);
- tasks.put(id, future);
- } else {
- AuctionActivity recordNew1 = auctionActivityRepo.findById(id)
- .orElseThrow(new BusinessException("无数据"));
- if (ObjectUtils.isNotEmpty(recordNew1.getPurchasePrice())) {
- log.info("拍卖成交{}", id);
- auctionActivityRepo.scheduleOffShelf(id, AuctionStatus.PURCHASED);
- } else {
- log.info("拍卖流拍Task-else-else{}", id);
- auctionActivityRepo.scheduleOffShelf(id, AuctionStatus.PASS);
- if (AuctionSource.TRANSFER.equals(recordNew1.getSource())) {
- Asset asset = assetRepo.findById(recordNew1.getAssetId())
- .orElseThrow(new BusinessException("暂无"));
- asset.setStatus(AssetStatus.NORMAL);
- asset.setConsignment(false);
- asset.setPublicShow(false);
- assetRepo.save(asset);
- }
- }
- }
- }
- }
- /**
- * 定时下架拍卖
- * (每隔1分钟执行一次)
- */
- @Scheduled(cron = "0 */1 * * * ?")
- public void passAuction() {
- List<AuctionActivity> activities = auctionActivityRepo.findAllByStatus(AuctionStatus.ONGOING);
- activities.forEach(activity -> {
- if (activity.getEndTime().isBefore(LocalDateTime.now())) {
- if (ObjectUtils.isNotEmpty(activity.getPurchasePrice())) {
- log.info("拍卖成交{}", activity.getId());
- // this.changeStatus(activity.getId(), AuctionStatus.PURCHASED);
- auctionActivityRepo.updateStatus(activity.getId(), AuctionStatus.PURCHASED);
- } else {
- //没有成交价,无人出价过
- log.info("拍卖流拍Task-else-else{}", activity.getId());
- // this.changeStatus(activity.getId(), AuctionStatus.PASS);
- auctionActivityRepo.updateStatus(activity.getId(), AuctionStatus.PASS);
- if (AuctionSource.TRANSFER.equals(activity.getSource())) {
- Asset asset = assetRepo.findById(activity.getAssetId())
- .orElseThrow(new BusinessException("暂无"));
- asset.setStatus(AssetStatus.NORMAL);
- asset.setConsignment(false);
- asset.setPublicShow(false);
- assetRepo.save(asset);
- }
- }
- }
- });
- }
- }
|