Search in sources :

Example 1 with Bucket

use of com.actionworks.flashsale.domain.model.Bucket in project flash-sale by ThoughtsBeta.

the class BucketsDomainServiceImpl method arrangeBuckets.

@Override
public boolean arrangeBuckets(Long itemId, List<Bucket> buckets) {
    logger.info("arrangeBuckets|编排库存分桶|{},{}", itemId, JSON.toJSONString(buckets));
    if (itemId == null || itemId <= 0 || CollectionUtils.isEmpty(buckets)) {
        logger.info("arrangeBuckets|库存分桶参数错误|{}", itemId);
        throw new DomainException(PARAMS_INVALID);
    }
    Optional<Bucket> primaryBucketOptional = buckets.stream().filter(Bucket::isPrimaryBucket).findFirst();
    if (!primaryBucketOptional.isPresent()) {
        throw new DomainException(PRIMARY_BUCKET_IS_MISSING);
    }
    if (buckets.stream().filter(Bucket::isPrimaryBucket).count() > 1) {
        throw new DomainException(MULTI_PRIMARY_BUCKETS_FOUND_BUT_EXPECT_ONE);
    }
    buckets.forEach(stockBucket -> {
        if (stockBucket.getTotalStocksAmount() == null || stockBucket.getTotalStocksAmount() < 0) {
            throw new DomainException(TOTAL_STOCKS_AMOUNT_INVALID);
        }
        if (stockBucket.getAvailableStocksAmount() == null || stockBucket.getAvailableStocksAmount() <= 0) {
            throw new DomainException(AVAILABLE_STOCKS_AMOUNT_INVALID);
        }
        if (!stockBucket.getAvailableStocksAmount().equals(stockBucket.getTotalStocksAmount()) && stockBucket.isSubBucket()) {
            throw new DomainException(AVAILABLE_STOCKS_AMOUNT_NOT_EQUALS_TO_TOTAL_STOCKS_AMOUNT);
        }
        if (!itemId.equals(stockBucket.getItemId())) {
            throw new DomainException(STOCK_BUCKET_ITEM_INVALID);
        }
    });
    boolean success = bucketsRepository.submitBuckets(itemId, buckets);
    if (!success) {
        return false;
    }
    StockBucketEvent stockBucketEvent = new StockBucketEvent();
    stockBucketEvent.setEventType(StockBucketEventType.ARRANGED);
    stockBucketEvent.setItemId(itemId);
    domainEventPublisher.publish(stockBucketEvent);
    logger.info("arrangeBuckets|编排库存分桶已完成|{}", itemId);
    return true;
}
Also used : DomainException(com.actionworks.flashsale.domain.exception.DomainException) Bucket(com.actionworks.flashsale.domain.model.Bucket) StockBucketEvent(com.actionworks.flashsale.domain.event.StockBucketEvent)

Example 2 with Bucket

use of com.actionworks.flashsale.domain.model.Bucket in project flash-sale by ThoughtsBeta.

the class BucketsCacheService method alignItemStocks.

@Override
public boolean alignItemStocks(Long itemId) {
    if (itemId == null) {
        logger.info("alignItemStocks|参数为空");
        return false;
    }
    String stockBucketCacheInitLockKey = getStockBucketCacheInitLockKey(itemId);
    DistributedLock lock = distributedLockFactoryService.getDistributedLock(stockBucketCacheInitLockKey);
    try {
        boolean isLockSuccess = lock.tryLock(5, 5, TimeUnit.SECONDS);
        if (!isLockSuccess) {
            logger.info("alignItemStocks|校准库存时获取锁失败|{}", itemId);
            return false;
        }
        List<Bucket> buckets = bucketsDomainService.getBucketsByItem(itemId);
        if (CollectionUtils.isEmpty(buckets)) {
            logger.info("alignItemStocks|秒杀品未设置库存|{}", itemId);
            return false;
        }
        buckets.forEach(stockBucket -> {
            String key1StockBucketCacheKey = getBucketAvailableStocksCacheKey(stockBucket.getItemId(), stockBucket.getSerialNo());
            String key2StockBucketsSuspendKey = getItemStockBucketsSuspendKey(stockBucket.getItemId());
            String key3ItemStocksCacheAlignKey = getItemStocksCacheAlignKey(stockBucket.getItemId());
            String key4ItemStockBucketsQuantityCacheKey = getItemStockBucketsQuantityCacheKey(stockBucket.getItemId());
            List<String> keys = Lists.newArrayList(key1StockBucketCacheKey, key2StockBucketsSuspendKey, key3ItemStocksCacheAlignKey, key4ItemStockBucketsQuantityCacheKey);
            DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(INIT_OR_ALIGN_ITEM_STOCK_LUA, Long.class);
            Long result = redisCacheService.getRedisTemplate().execute(redisScript, keys, stockBucket.getAvailableStocksAmount(), buckets.size());
            if (result == null) {
                logger.info("alignItemStocks|分桶库存校准失败|{},{}", itemId, stockBucketCacheInitLockKey);
                return;
            }
            if (result == -998) {
                logger.info("alignItemStocks|库存维护中,已暂停服务|{},{},{}", result, itemId, stockBucketCacheInitLockKey);
                return;
            }
            if (result == -997) {
                logger.info("alignItemStocks|库存数据校准对齐中|{},{},{}", result, itemId, stockBucketCacheInitLockKey);
                return;
            }
            if (result == 1) {
                logger.info("alignItemStocks|分桶库存校准完成|{},{},{}", result, itemId, stockBucketCacheInitLockKey);
            }
        });
        logger.info("alignItemStocks|分桶库存校准全部完成|{},{}", itemId, stockBucketCacheInitLockKey);
        return true;
    } catch (Exception e) {
        logger.error("alignItemStocks|秒杀品库存初始化错误|{},{}", itemId, stockBucketCacheInitLockKey, e);
        return false;
    }
}
Also used : DistributedLock(com.actionworks.flashsale.lock.DistributedLock) Bucket(com.actionworks.flashsale.domain.model.Bucket) DefaultRedisScript(org.springframework.data.redis.core.script.DefaultRedisScript)

Example 3 with Bucket

use of com.actionworks.flashsale.domain.model.Bucket in project flash-sale by ThoughtsBeta.

the class DefaultBucketsArrangementService method rearrangeStockBucketsBasedIncrementalMode.

/**
 * 根据增量库存重新分桶
 */
private void rearrangeStockBucketsBasedIncrementalMode(Long itemId, Integer incrementalStocksAmount, Integer bucketsAmount, List<Bucket> buckets) {
    Optional<Bucket> primaryStockBucketOptional = buckets.stream().filter(Bucket::isPrimaryBucket).findFirst();
    if (!primaryStockBucketOptional.isPresent()) {
        return;
    }
    // 回收分桶库存
    int remainAvailableStocks = buckets.stream().mapToInt(Bucket::getAvailableStocksAmount).sum();
    Integer totalAvailableStocksAmount = remainAvailableStocks + incrementalStocksAmount;
    int presentAvailableStocks = remainAvailableStocks + incrementalStocksAmount;
    if (presentAvailableStocks < 0) {
        throw new StockBucketException("可用库存不足!");
    }
    Bucket primaryBucket = primaryStockBucketOptional.get();
    primaryBucket.increaseTotalStocksAmount(incrementalStocksAmount);
    List<Bucket> presentBuckets = buildBuckets(itemId, totalAvailableStocksAmount, bucketsAmount, primaryBucket);
    submitBucketsToArrange(itemId, presentBuckets);
}
Also used : Bucket(com.actionworks.flashsale.domain.model.Bucket) StockBucketException(com.actionworks.flashsale.app.exception.StockBucketException)

Example 4 with Bucket

use of com.actionworks.flashsale.domain.model.Bucket in project flash-sale by ThoughtsBeta.

the class DefaultBucketsArrangementService method buildBuckets.

private List<Bucket> buildBuckets(Long itemId, Integer availableStocksAmount, Integer bucketsQuantity, Bucket primaryBucket) {
    if (itemId == null || availableStocksAmount == null || bucketsQuantity == null || bucketsQuantity <= 0) {
        throw new StockBucketException("构建分桶时参数错误");
    }
    List<Bucket> buckets = new ArrayList<>();
    int averageStocksInEachBucket = availableStocksAmount / bucketsQuantity;
    int pieceStocks = availableStocksAmount % bucketsQuantity;
    for (int i = 0; i < bucketsQuantity; i++) {
        if (i == 0) {
            if (primaryBucket == null) {
                primaryBucket = new Bucket();
            }
            primaryBucket.setSerialNo(i);
            primaryBucket.setAvailableStocksAmount(averageStocksInEachBucket);
            primaryBucket.setStatus(BucketStatus.ENABLED.getCode());
            buckets.add(primaryBucket);
            continue;
        }
        Bucket subBucket = new Bucket().setStatus(BucketStatus.ENABLED.getCode()).setSerialNo(i).setItemId(itemId);
        if (i < bucketsQuantity - 1) {
            subBucket.setTotalStocksAmount(averageStocksInEachBucket);
            subBucket.setAvailableStocksAmount(averageStocksInEachBucket);
        }
        if (i == bucketsQuantity - 1) {
            Integer restAvailableStocksAmount = averageStocksInEachBucket + pieceStocks;
            subBucket.setTotalStocksAmount(restAvailableStocksAmount);
            subBucket.setAvailableStocksAmount(restAvailableStocksAmount);
        }
        buckets.add(subBucket);
    }
    return buckets;
}
Also used : Bucket(com.actionworks.flashsale.domain.model.Bucket) ArrayList(java.util.ArrayList) StockBucketException(com.actionworks.flashsale.app.exception.StockBucketException)

Example 5 with Bucket

use of com.actionworks.flashsale.domain.model.Bucket in project flash-sale by ThoughtsBeta.

the class DefaultBucketsArrangementService method arrangeStockBuckets.

@Transactional
@Override
public void arrangeStockBuckets(Long itemId, Integer stocksAmount, Integer bucketsQuantity, Integer assignmentMode) {
    logger.info("arrangeBuckets|准备库存分桶|{},{},{}", itemId, stocksAmount, bucketsQuantity);
    if (itemId == null || stocksAmount == null || stocksAmount < 0 || bucketsQuantity == null || bucketsQuantity <= 0) {
        throw new StockBucketException("参数错误");
    }
    DistributedLock lock = lockFactoryService.getDistributedLock(ITEM_STOCK_BUCKETS_SUSPEND_KEY + itemId);
    try {
        boolean isLockSuccess = lock.tryLock(5, 5, TimeUnit.SECONDS);
        if (!isLockSuccess) {
            logger.info("arrangeBuckets|库存分桶时获取锁失败|{}", itemId);
            return;
        }
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        try {
            boolean success = bucketsDomainService.suspendBuckets(itemId);
            if (!success) {
                logger.info("arrangeBuckets|关闭库存分桶失败|{}", itemId);
                throw new StockBucketException("关闭库存分桶失败");
            }
            dataSourceTransactionManager.commit(transactionStatus);
        } catch (Exception e) {
            logger.info("arrangeBuckets|关闭分桶失败回滚中|{}", itemId, e);
            dataSourceTransactionManager.rollback(transactionStatus);
        }
        List<Bucket> buckets = bucketsDomainService.getBucketsByItem(itemId);
        if (buckets.size() == 0) {
            initStockBuckets(itemId, stocksAmount, bucketsQuantity);
            return;
        }
        if (ArrangementMode.isTotalAmountMode(assignmentMode)) {
            arrangeStockBucketsBasedTotalMode(itemId, stocksAmount, bucketsQuantity, buckets);
        }
        if (ArrangementMode.isIncrementalAmountMode(assignmentMode)) {
            rearrangeStockBucketsBasedIncrementalMode(itemId, stocksAmount, bucketsQuantity, buckets);
        }
    } catch (Exception e) {
        logger.error("arrangeBuckets|库存分桶错误|", e);
        throw new StockBucketException("库存分桶错误");
    } finally {
        lock.unlock();
        boolean success = bucketsDomainService.resumeBuckets(itemId);
        if (!success) {
            logger.error("arrangeBuckets|打开库存分桶失败|");
        }
    }
}
Also used : DistributedLock(com.actionworks.flashsale.lock.DistributedLock) Bucket(com.actionworks.flashsale.domain.model.Bucket) TransactionStatus(org.springframework.transaction.TransactionStatus) StockBucketException(com.actionworks.flashsale.app.exception.StockBucketException) StockBucketException(com.actionworks.flashsale.app.exception.StockBucketException) Transactional(org.springframework.transaction.annotation.Transactional)

Aggregations

Bucket (com.actionworks.flashsale.domain.model.Bucket)9 StockBucketException (com.actionworks.flashsale.app.exception.StockBucketException)5 DistributedLock (com.actionworks.flashsale.lock.DistributedLock)2 StockBucketDTO (com.actionworks.flashsale.app.model.dto.StockBucketDTO)1 StockBucketSummaryDTO (com.actionworks.flashsale.app.model.dto.StockBucketSummaryDTO)1 StockBucketEvent (com.actionworks.flashsale.domain.event.StockBucketEvent)1 DomainException (com.actionworks.flashsale.domain.exception.DomainException)1 ArrayList (java.util.ArrayList)1 DefaultRedisScript (org.springframework.data.redis.core.script.DefaultRedisScript)1 TransactionStatus (org.springframework.transaction.TransactionStatus)1 Transactional (org.springframework.transaction.annotation.Transactional)1