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;
}
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;
}
}
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);
}
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;
}
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|打开库存分桶失败|");
}
}
}
Aggregations