因为Redis是单线程执行,所以Lua脚本的执行具有原子性
库存初始化:
-3:库存未初始化 大于等于0:剩余库存(新增之后剩余的库存)
String ADD_INVENTORY =
"if (redis.call('exists', KEYS[1]) == 1) then"
+ " local occStock = tonumber(redis.call('hget', KEYS[1], KEYS[2]));"
+ " if (occStock == nil) then"
+ " return tonumber(redis.call('get', KEYS[3]));"
+ " end;"
+ " if (redis.call('hdel', KEYS[1], KEYS[2]) == 1) then"
+ " return redis.call('incrBy', KEYS[3], occStock);"
+ " end;"
+ " return tonumber(redis.call('get', KEYS[3]));"
+ "end;"
+ "return -3;";
操作库存:
-5:库存操作失败
-4:代表库存传进来的值是负数(非法值)
-3:库存未初始化
-2:库存不足
-1:库存为0大于等于0:剩余库存(扣减之后剩余的库存)
String REDUCE_INVENTORY =
"if (redis.call('exists', KEYS[1]) == 1) then"
+ " local stock = tonumber(redis.call('get', KEYS[1]));"
+ " local num = tonumber(ARGV[1]);"
+ " if (num <= 0) then"
+ " return -4;"
+ " end;"
+ " if (stock <= 0) then"
+ " return -1;"
+ " end;"
+ " if (stock >= num) then"
+ " if (redis.call('hset', KEYS[2], KEYS[3], num) == 1) then"
+ " return redis.call('incrBy', KEYS[1], 0 - num);"
+ " else"
+ " return -5;"
+ " end;"
+ " end;"
+ " return -2;"
+ "end;"
+ "return -3;";
初始缓存库存:
public Boolean initCacheStock(Integer productId, Integer stockNum, StockCacheTypeEnum stockCacheTypeEnum) {
String stockKey= String.format(RedisKeyConstants.GOODS_CACHE_STOCK_KEY, productId);
switch (stockCacheTypeEnum) {
case INIT: {
boolean result = redisUtil.setIfAbsent(stockKey, stockNum);
if (result) {
log.info("初始化商品[{}]缓存库存成功, 数量为:{}", productId, redisUtil.get(stockKey));
return Boolean.TRUE;
}
log.info("商品[{}]已存在缓存库存", productId);
}
case RESET: {
boolean result = redisUtil.setIfPresent(stockKey, stockNum);
if (result) {
log.info("重置商品[{}]缓存库存成功, 数量为:{}", productId, redisUtil.get(stockKey));
return Boolean.TRUE;
}
String msg = StrUtil.format("重置商品[{}]缓存库存失败", productId);
log.info(msg);
throw new ShopRuntimeException(msg);
}
default:
throw new ShopRuntimeException("商品库存未知操作类型");
}
}
添加库存:
public boolean addCacheStock(Integer productId, String orderNo, Integer memberId) {
String stockKey= String.format(RedisKeyConstants.GOODS_CACHE_STOCK_KEY, productId);
String occupyStockKey= String.format(RedisKeyConstants.GOODS_CACHE_STOCK_OCCUPY_KEY, memberId);
Long result = redisTemplate.execute(addInventoryScript, CollUtil.toList(occupyStockKey, orderNo, stockKey));
if (Objects.isNull(result)) {
log.info("增加商品[{}]缓存库存失败", productId);
return Boolean.FALSE;
}
if (result >= 0) {
log.info("商品[{}] 剩余缓存库存:[{}]", productId, result);
return Boolean.TRUE;
}
String desc = StockCacheStatusEnum.getValueOfCode(result);
log.info("增加商品[{}]缓存库存失败: {}", productId, desc);
throw new ShopRuntimeException(desc);
}
扣减库存:
public Boolean reduceCacheStock(Integer productId, Integer stockNum, String orderNo, Integer memberId) {
String stockKey= String.format(RedisKeyConstants.GOODS_CACHE_STOCK_KEY, productId);
String occupyStockKey= String.format(RedisKeyConstants.GOODS_CACHE_STOCK_OCCUPY_KEY, memberId);
Long result = redisTemplate.execute(reduceInventoryScript, CollUtil.toList(stockKey, occupyStockKey, orderNo), stockNum);
if (result == null) {
log.info("扣减商品[{}]缓存库存失败", productId);
return Boolean.FALSE;
}
if (result >= 0) {
log.info("商品[{}] 本次扣减缓存库存:[{}], 剩余缓存库存:[{}]", productId, stockNum, result);
return Boolean.TRUE;
}
String desc = StockCacheStatusEnum.getValueOfCode(result);
log.info("扣减商品[{}]缓存库存失败: {}", productId, desc);
throw new ShopRuntimeException(desc);
}