mirror of
https://gitee.com/youlaitech/youlai-mall.git
synced 2024-12-23 13:03:43 +08:00
feat:订单redis+lua防止订单重复提交,保证同一时刻多次点击幂等
This commit is contained in:
parent
8ff6362660
commit
503fba04e7
@ -51,7 +51,7 @@ public class OmsOrder extends BaseEntity {
|
|||||||
/**
|
/**
|
||||||
* 会员id
|
* 会员id
|
||||||
*/
|
*/
|
||||||
private Long userId;
|
private Long memberId;
|
||||||
/**
|
/**
|
||||||
* 使用的优惠券
|
* 使用的优惠券
|
||||||
*/
|
*/
|
||||||
|
@ -5,6 +5,7 @@ import lombok.Data;
|
|||||||
|
|
||||||
import javax.validation.constraints.NotBlank;
|
import javax.validation.constraints.NotBlank;
|
||||||
import javax.validation.constraints.Size;
|
import javax.validation.constraints.Size;
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -21,9 +22,8 @@ public class OrderSubmitDTO {
|
|||||||
|
|
||||||
private List<OrderItemVO> orderItems;
|
private List<OrderItemVO> orderItems;
|
||||||
|
|
||||||
private Long payAmount;
|
// 验价前台传值
|
||||||
|
private Long totalPrice;
|
||||||
private String couponId;
|
|
||||||
|
|
||||||
@NotBlank(message = "请选择收货地址")
|
@NotBlank(message = "请选择收货地址")
|
||||||
private String addressId;
|
private String addressId;
|
||||||
@ -31,4 +31,9 @@ public class OrderSubmitDTO {
|
|||||||
@Size(max = 500, message = "订单备注长度不能超过500")
|
@Size(max = 500, message = "订单备注长度不能超过500")
|
||||||
private String remark;
|
private String remark;
|
||||||
|
|
||||||
|
|
||||||
|
private Long payAmount;
|
||||||
|
|
||||||
|
private String couponId;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ public class CartVO implements Serializable {
|
|||||||
|
|
||||||
private Integer count;
|
private Integer count;
|
||||||
|
|
||||||
private Long price;
|
private Long price; // 加入购物车价格,因会变动,不能作为订单计算因子,订单验价时需重新获取商品价格即可
|
||||||
|
|
||||||
private Long coupon;
|
private Long coupon;
|
||||||
|
|
||||||
|
@ -12,4 +12,16 @@ public interface OmsConstants {
|
|||||||
|
|
||||||
String BUSINESS_NO_PREFIX = "businessno:";
|
String BUSINESS_NO_PREFIX = "businessno:";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 释放锁lua脚本
|
||||||
|
*/
|
||||||
|
String RELEASE_LOCK_LUA_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 释放锁成功返回值
|
||||||
|
*/
|
||||||
|
Long RELEASE_LOCK_SUCCESS_RESULT = 1L;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import com.youlai.mall.oms.pojo.domain.OmsOrderItem;
|
|||||||
import com.youlai.mall.oms.service.IOrderItemService;
|
import com.youlai.mall.oms.service.IOrderItemService;
|
||||||
import com.youlai.mall.oms.service.IOrderService;
|
import com.youlai.mall.oms.service.IOrderService;
|
||||||
import com.youlai.mall.pms.api.app.PmsSkuFeignService;
|
import com.youlai.mall.pms.api.app.PmsSkuFeignService;
|
||||||
import com.youlai.mall.pms.pojo.dto.InventoryDTO;
|
import com.youlai.mall.pms.pojo.dto.SkuLockDTO;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.amqp.core.Message;
|
import org.springframework.amqp.core.Message;
|
||||||
@ -32,7 +32,7 @@ public class RabbitMQListener {
|
|||||||
|
|
||||||
IOrderItemService orderItemService;
|
IOrderItemService orderItemService;
|
||||||
|
|
||||||
PmsSkuFeignService inventoryFeignService;
|
PmsSkuFeignService skuFeignService;
|
||||||
|
|
||||||
RabbitTemplate rabbitTemplate;
|
RabbitTemplate rabbitTemplate;
|
||||||
|
|
||||||
@ -47,12 +47,12 @@ public class RabbitMQListener {
|
|||||||
// 如果关单成功,发送消息释放库存
|
// 如果关单成功,发送消息释放库存
|
||||||
// rabbitTemplate.convertAndSend("product_event_change", "stock:unlock", orderSn);
|
// rabbitTemplate.convertAndSend("product_event_change", "stock:unlock", orderSn);
|
||||||
List<OmsOrderItem> orderItems = orderItemService.getByOrderId(orderId);
|
List<OmsOrderItem> orderItems = orderItemService.getByOrderId(orderId);
|
||||||
List<InventoryDTO> inventoryList = orderItems.stream().map(orderItem -> InventoryDTO.builder()
|
List<SkuLockDTO> stockLick = orderItems.stream().map(orderItem -> SkuLockDTO.builder()
|
||||||
.skuId(orderItem.getSkuId())
|
.skuId(orderItem.getSkuId())
|
||||||
.count(orderItem.getSkuQuantity())
|
.count(orderItem.getSkuQuantity())
|
||||||
.build())
|
.build())
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
inventoryFeignService.unlockStock(inventoryList);
|
skuFeignService.unlockStock(stockLick);
|
||||||
} else {
|
} else {
|
||||||
// 如果关单失败,则订单可能已经被处理,直接手动ACK确认消息
|
// 如果关单失败,则订单可能已经被处理,直接手动ACK确认消息
|
||||||
channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
|
channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
|
||||||
|
@ -14,7 +14,7 @@ import com.youlai.mall.oms.service.IOrderItemService;
|
|||||||
import com.youlai.mall.oms.service.IOrderPayService;
|
import com.youlai.mall.oms.service.IOrderPayService;
|
||||||
import com.youlai.mall.oms.service.IOrderService;
|
import com.youlai.mall.oms.service.IOrderService;
|
||||||
import com.youlai.mall.pms.api.app.PmsSkuFeignService;
|
import com.youlai.mall.pms.api.app.PmsSkuFeignService;
|
||||||
import com.youlai.mall.pms.pojo.dto.InventoryDTO;
|
import com.youlai.mall.pms.pojo.dto.SkuLockDTO;
|
||||||
import com.youlai.mall.ums.api.UmsMemberFeignService;
|
import com.youlai.mall.ums.api.UmsMemberFeignService;
|
||||||
import io.seata.spring.annotation.GlobalTransactional;
|
import io.seata.spring.annotation.GlobalTransactional;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
@ -36,7 +36,7 @@ public class OrderPayServiceImpl extends ServiceImpl<OrderPayMapper, OmsOrderPay
|
|||||||
|
|
||||||
private IOrderItemService orderItemService;
|
private IOrderItemService orderItemService;
|
||||||
|
|
||||||
private PmsSkuFeignService inventoryFeignService;
|
private PmsSkuFeignService skuFeignService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@GlobalTransactional(rollbackFor = Exception.class)
|
@GlobalTransactional(rollbackFor = Exception.class)
|
||||||
@ -67,13 +67,13 @@ public class OrderPayServiceImpl extends ServiceImpl<OrderPayMapper, OmsOrderPay
|
|||||||
|
|
||||||
// 扣减库存
|
// 扣减库存
|
||||||
List<OmsOrderItem> orderItems = orderItemService.getByOrderId(orderId);
|
List<OmsOrderItem> orderItems = orderItemService.getByOrderId(orderId);
|
||||||
List<InventoryDTO> inventoryList = orderItems.stream().map(orderItem -> InventoryDTO.builder()
|
List<SkuLockDTO> stockLick = orderItems.stream().map(orderItem -> SkuLockDTO.builder()
|
||||||
.skuId(orderItem.getSkuId())
|
.skuId(orderItem.getSkuId())
|
||||||
.count(orderItem.getSkuQuantity())
|
.count(orderItem.getSkuQuantity())
|
||||||
.build())
|
.build())
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
inventoryFeignService.deductStock(inventoryList);
|
skuFeignService.deductStock(stockLick);
|
||||||
|
|
||||||
// 添加订单支付记录
|
// 添加订单支付记录
|
||||||
OmsOrderPay orderPay = OmsOrderPay.builder()
|
OmsOrderPay orderPay = OmsOrderPay.builder()
|
||||||
|
@ -14,7 +14,6 @@ import com.youlai.common.result.ResultCode;
|
|||||||
import com.youlai.common.web.exception.BizException;
|
import com.youlai.common.web.exception.BizException;
|
||||||
import com.youlai.common.web.util.BeanMapperUtils;
|
import com.youlai.common.web.util.BeanMapperUtils;
|
||||||
import com.youlai.common.web.util.RequestUtils;
|
import com.youlai.common.web.util.RequestUtils;
|
||||||
import com.youlai.mall.oms.constant.OmsConstants;
|
|
||||||
import com.youlai.mall.oms.enums.OrderStatusEnum;
|
import com.youlai.mall.oms.enums.OrderStatusEnum;
|
||||||
import com.youlai.mall.oms.enums.OrderTypeEnum;
|
import com.youlai.mall.oms.enums.OrderTypeEnum;
|
||||||
import com.youlai.mall.oms.mapper.OrderMapper;
|
import com.youlai.mall.oms.mapper.OrderMapper;
|
||||||
@ -28,7 +27,7 @@ import com.youlai.mall.oms.pojo.vo.*;
|
|||||||
import com.youlai.mall.oms.service.*;
|
import com.youlai.mall.oms.service.*;
|
||||||
import com.youlai.mall.pms.api.app.PmsSkuFeignService;
|
import com.youlai.mall.pms.api.app.PmsSkuFeignService;
|
||||||
import com.youlai.mall.pms.pojo.domain.PmsSku;
|
import com.youlai.mall.pms.pojo.domain.PmsSku;
|
||||||
import com.youlai.mall.pms.pojo.dto.InventoryDTO;
|
import com.youlai.mall.pms.pojo.dto.SkuLockDTO;
|
||||||
import com.youlai.mall.pms.pojo.dto.SkuDTO;
|
import com.youlai.mall.pms.pojo.dto.SkuDTO;
|
||||||
import com.youlai.mall.ums.api.UmsAddressFeignService;
|
import com.youlai.mall.ums.api.UmsAddressFeignService;
|
||||||
import com.youlai.mall.ums.api.UmsMemberFeignService;
|
import com.youlai.mall.ums.api.UmsMemberFeignService;
|
||||||
@ -38,20 +37,20 @@ import lombok.AllArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||||
import org.springframework.core.task.AsyncTaskExecutor;
|
import org.springframework.core.task.AsyncTaskExecutor;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
import org.springframework.data.redis.core.script.DefaultRedisScript;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.web.context.request.RequestAttributes;
|
import org.springframework.web.context.request.RequestAttributes;
|
||||||
import org.springframework.web.context.request.RequestContextHolder;
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static com.youlai.mall.oms.constant.OmsConstants.*;
|
||||||
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@ -77,7 +76,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
|
|||||||
|
|
||||||
private RabbitTemplate rabbitTemplate;
|
private RabbitTemplate rabbitTemplate;
|
||||||
|
|
||||||
private RedisTemplate redisTemplate;
|
private StringRedisTemplate redisTemplate;
|
||||||
|
|
||||||
private ThreadPoolExecutor threadPoolExecutor;
|
private ThreadPoolExecutor threadPoolExecutor;
|
||||||
|
|
||||||
@ -126,7 +125,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
|
|||||||
CompletableFuture<Void> orderTokenCompletableFuture = CompletableFuture.runAsync(() -> {
|
CompletableFuture<Void> orderTokenCompletableFuture = CompletableFuture.runAsync(() -> {
|
||||||
String orderToken = IdUtil.randomUUID();
|
String orderToken = IdUtil.randomUUID();
|
||||||
orderConfirmVO.setOrderToken(orderToken);
|
orderConfirmVO.setOrderToken(orderToken);
|
||||||
redisTemplate.opsForValue().set(OmsConstants.ORDER_TOKEN_PREFIX + orderToken, orderToken);
|
redisTemplate.opsForValue().set(ORDER_TOKEN_PREFIX + orderToken, orderToken);
|
||||||
}, threadPoolExecutor);
|
}, threadPoolExecutor);
|
||||||
|
|
||||||
CompletableFuture.allOf(orderItemsCompletableFuture, addressesCompletableFuture, orderTokenCompletableFuture);
|
CompletableFuture.allOf(orderItemsCompletableFuture, addressesCompletableFuture, orderTokenCompletableFuture);
|
||||||
@ -137,178 +136,76 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
|
|||||||
@GlobalTransactional
|
@GlobalTransactional
|
||||||
public OrderSubmitVO submit(OrderSubmitDTO submitDTO) {
|
public OrderSubmitVO submit(OrderSubmitDTO submitDTO) {
|
||||||
|
|
||||||
submitDTO.
|
// 订单重复提交校验
|
||||||
|
String orderToken = submitDTO.getOrderToken();
|
||||||
|
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(RELEASE_LOCK_LUA_SCRIPT, Long.class);
|
||||||
|
Long result = this.redisTemplate.execute(redisScript, Collections.singletonList(ORDER_TOKEN_PREFIX + orderToken), orderToken);
|
||||||
|
|
||||||
|
if (ObjectUtil.equals(result, RELEASE_LOCK_SUCCESS_RESULT)) {
|
||||||
|
throw new BizException("订单不可重复提交");
|
||||||
|
|
||||||
log.info("开始创建订单:{}", submitInfoDTO);
|
|
||||||
threadLocal.set(submitInfoDTO);
|
|
||||||
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
|
|
||||||
|
|
||||||
OrderBO orderBO = new OrderBO();
|
|
||||||
CompletableFuture<Void> orderFuture;
|
|
||||||
CompletableFuture<Void> orderItemFuture;
|
|
||||||
CompletableFuture<Void> orderDeliveryFuture;
|
|
||||||
|
|
||||||
// 创建订单任务
|
|
||||||
{
|
|
||||||
orderFuture = CompletableFuture.runAsync(() -> {
|
|
||||||
RequestContextHolder.setRequestAttributes(attributes);
|
|
||||||
threadLocal.set(submitInfoDTO);
|
|
||||||
OrderSubmitDTO submitInfo = threadLocal.get();
|
|
||||||
log.info("订单提交信息:{}", submitInfo);
|
|
||||||
OmsOrder order = new OmsOrder();
|
|
||||||
order.setOrderSn(IdWorker.getTimeId())
|
|
||||||
.setRemark(submitInfo.getRemark())
|
|
||||||
.setStatus(OrderStatusEnum.PENDING_PAYMENT.getCode())
|
|
||||||
.setSourceType(OrderTypeEnum.APP.getCode())
|
|
||||||
.setUserId(RequestUtils.getUserId());
|
|
||||||
log.debug("完善后的订单信息:{}", order.toString());
|
|
||||||
orderBO.setOrder(order);
|
|
||||||
|
|
||||||
}, executor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建订单商品任务
|
List<OrderItemVO> orderItems = submitDTO.getOrderItems();
|
||||||
{
|
if (CollectionUtil.isEmpty(orderItems)) {
|
||||||
orderItemFuture = CompletableFuture.runAsync(() -> {
|
throw new BizException("请选择商品再提交");
|
||||||
RequestContextHolder.setRequestAttributes(attributes);
|
|
||||||
threadLocal.set(submitInfoDTO);
|
|
||||||
OrderSubmitDTO submitInfo = threadLocal.get();
|
|
||||||
List<OmsOrderItem> orderItems;
|
|
||||||
if (submitInfoDTO.getSkuId() != null) { // 直接下单
|
|
||||||
orderItems = new ArrayList<>();
|
|
||||||
orderItems.add(OmsOrderItem.builder().skuId(submitInfo.getSkuId()).skuQuantity(submitInfo.getSkuNum()).build());
|
|
||||||
} else { // 购物车下单
|
|
||||||
CartVO cart = cartService.getCart();
|
|
||||||
orderItems = cart.getItems().stream().map(cartItem -> OmsOrderItem.builder().skuId(cartItem.getSkuId())
|
|
||||||
.skuQuantity(cartItem.getCount())
|
|
||||||
.build()
|
|
||||||
).collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Long> skuIds = orderItems.stream().map(item -> item.getSkuId())
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
List<SkuDTO> skuList = inventoryFeignService.listBySkuIds(skuIds).getData();
|
|
||||||
if (CollectionUtil.isEmpty(skuList)) {
|
|
||||||
throw new BizException("订单商品库存为空");
|
|
||||||
}
|
|
||||||
for (OmsOrderItem orderItem : orderItems) {
|
|
||||||
skuList.stream().filter(sku -> sku.getId().equals(orderItem.getSkuId())).findFirst()
|
|
||||||
.ifPresent(skuItem -> {
|
|
||||||
BeanUtil.copyProperties(skuItem, orderItem);
|
|
||||||
orderItem.setSkuPrice(skuItem.getPrice());
|
|
||||||
orderItem.setSkuTotalPrice(orderItem.getSkuPrice() * orderItem.getSkuQuantity());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
orderBO.setOrderItems(orderItems);
|
|
||||||
}, executor);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建发货信息任务
|
|
||||||
{
|
|
||||||
orderDeliveryFuture = CompletableFuture.runAsync(() -> {
|
|
||||||
RequestContextHolder.setRequestAttributes(attributes);
|
|
||||||
threadLocal.set(submitInfoDTO);
|
|
||||||
String addressId = threadLocal.get().getAddressId();
|
|
||||||
UmsAddressDTO userAddress = memberFeignService.getAddressById(addressId).getData();
|
|
||||||
if (userAddress == null) {
|
|
||||||
throw new BizException("会员地址不存在");
|
|
||||||
}
|
|
||||||
OmsOrderDelivery orderDelivery = OmsOrderDelivery.builder()
|
|
||||||
.receiverName(userAddress.getName())
|
|
||||||
.receiverPhone(userAddress.getMobile())
|
|
||||||
.receiverPostCode(userAddress.getZipCode())
|
|
||||||
.receiverProvince(userAddress.getProvince())
|
|
||||||
.receiverCity(userAddress.getCity())
|
|
||||||
.receiverRegion(userAddress.getArea())
|
|
||||||
.receiverDetailAddress(userAddress.getAddress())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
orderBO.setOrderDelivery(orderDelivery);
|
|
||||||
}, executor);
|
|
||||||
}
|
|
||||||
|
|
||||||
CompletableFuture<Void> future = CompletableFuture.allOf(orderFuture, orderItemFuture, orderDeliveryFuture);
|
|
||||||
future.get();
|
|
||||||
|
|
||||||
// 订单验价
|
// 订单验价
|
||||||
{
|
Long currentTotalPrice = orderItems.stream().map(item -> {
|
||||||
OmsOrder order = orderBO.getOrder();
|
PmsSku sku = skuFeignService.getSkuById(item.getSkuId()).getData();
|
||||||
List<OmsOrderItem> orderItems = orderBO.getOrderItems();
|
if (sku != null) {
|
||||||
|
return sku.getPrice() * item.getCount();
|
||||||
|
}
|
||||||
|
return 0l;
|
||||||
|
}).reduce(0l, Long::sum);
|
||||||
|
|
||||||
log.info("计算订单价格:order:{},orderItems:{}", order, orderItems);
|
if (currentTotalPrice.compareTo(submitDTO.getTotalPrice()) != 0) {
|
||||||
|
throw new BizException("页面已过期,请重新刷新页面再提交");
|
||||||
if (order == null || CollectionUtil.isEmpty(orderItems)) {
|
|
||||||
throw new BizException("订单或订单商品列表为空,订单创建失败");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Long totalAmount = orderItems.stream().mapToLong(OmsOrderItem::getSkuTotalPrice).sum();
|
// 校验库存是否足够和锁库存
|
||||||
int totalQuantity = orderItems.stream().mapToInt(OmsOrderItem::getSkuQuantity).sum();
|
List<SkuLockDTO> skuLockList = orderItems.stream()
|
||||||
Long payAmount = totalAmount;
|
.map(item -> SkuLockDTO.builder().skuId(item.getSkuId())
|
||||||
if (order.getCouponAmount() != null) {
|
.count(item.getCount())
|
||||||
payAmount -= order.getCouponAmount();
|
.orderToken(orderToken)
|
||||||
}
|
|
||||||
if (order.getFreightAmount() != null) {
|
|
||||||
payAmount -= order.getFreightAmount();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
OrderSubmitDTO orderSubmitInfo = threadLocal.get();
|
|
||||||
int compare = Long.compare(orderSubmitInfo.getPayAmount().longValue(), payAmount.longValue());
|
|
||||||
if (compare != 0) {
|
|
||||||
throw new BizException("订单价格变化,请重新提交");
|
|
||||||
}
|
|
||||||
|
|
||||||
order.setTotalAmount(totalAmount);
|
|
||||||
order.setTotalQuantity(totalQuantity);
|
|
||||||
order.setPayAmount(payAmount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 锁定库存
|
|
||||||
{
|
|
||||||
List<OmsOrderItem> orderItems = orderBO.getOrderItems();
|
|
||||||
List<InventoryDTO> items = orderItems.stream().map(orderItem -> InventoryDTO.builder()
|
|
||||||
.skuId(orderItem.getSkuId())
|
|
||||||
.count(orderItem.getSkuQuantity())
|
|
||||||
.build())
|
.build())
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
Result result = inventoryFeignService.lockStock(items);
|
Result lockResult = skuFeignService.lockStock(skuLockList);
|
||||||
if (!StrUtil.equals(result.getCode(), ResultCode.SUCCESS.getCode())) {
|
|
||||||
throw new BizException("下单失败,锁定库存错误");
|
if (!Result.success().getCode().equals(lockResult.getCode())) {
|
||||||
}
|
throw new BizException(Result.failed().getMsg());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存订单
|
// 保存订单
|
||||||
OmsOrder order = orderBO.getOrder();
|
OmsOrder order = new OmsOrder();
|
||||||
|
order.setOrderSn(IdWorker.getTimeId())
|
||||||
|
.setStatus(OrderStatusEnum.PENDING_PAYMENT.getCode())
|
||||||
|
.setSourceType(OrderTypeEnum.APP.getCode())
|
||||||
|
.setMemberId(RequestUtils.getUserId())
|
||||||
|
.setRemark(submitDTO.getRemark());
|
||||||
this.save(order);
|
this.save(order);
|
||||||
Long orderId = order.getId();
|
|
||||||
|
|
||||||
// 保存订单商品
|
// 保存订单商品
|
||||||
List<OmsOrderItem> orderItems = orderBO.getOrderItems();
|
List<OmsOrderItem> orderItemList = orderItems.stream().map(item -> OmsOrderItem.builder()
|
||||||
orderItems.forEach(item -> item.setOrderId(orderId));
|
.orderId(order.getId())
|
||||||
orderItemService.saveBatch(orderItems);
|
.skuId(item.getSkuId())
|
||||||
|
.skuPrice(item.getPrice())
|
||||||
// 保存发货信息
|
.skuPic(item.getSkuPic())
|
||||||
OmsOrderDelivery orderDelivery = orderBO.getOrderDelivery();
|
.skuQuantity(item.getCount())
|
||||||
orderDelivery.setOrderId(orderId);
|
.build()).collect(Collectors.toList());
|
||||||
orderDeliveryService.save(orderDelivery);
|
orderItemService.saveBatch(orderItemList);
|
||||||
|
|
||||||
// 删除购物车中已购买的商品
|
|
||||||
if (ObjectUtil.isNull(submitInfoDTO.getSkuId())) {
|
|
||||||
cartService.deleteSelectedItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将订单放入延时队列,超时未支付系统自动关单
|
// 将订单放入延时队列,超时未支付系统自动关单
|
||||||
rabbitTemplate.convertAndSend("order-exchange", "order:create", orderId);
|
rabbitTemplate.convertAndSend("order-exchange", "order:create", orderToken);
|
||||||
|
|
||||||
|
// 记录发货 TODO
|
||||||
|
|
||||||
|
OrderSubmitVO submitVO = new OrderSubmitVO();
|
||||||
|
submitVO.setId(order.getId());
|
||||||
|
submitVO.setOrderSn(order.getOrderSn());
|
||||||
|
return submitVO;
|
||||||
|
|
||||||
OrderSubmitVO result = new OrderSubmitVO();
|
|
||||||
result.setId(orderId);
|
|
||||||
result.setOrderSn(order.getOrderSn());
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -388,7 +285,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
|
|||||||
Long userId = RequestUtils.getUserId();
|
Long userId = RequestUtils.getUserId();
|
||||||
OmsOrder order = this.getOne(new LambdaQueryWrapper<OmsOrder>()
|
OmsOrder order = this.getOne(new LambdaQueryWrapper<OmsOrder>()
|
||||||
.eq(OmsOrder::getId, id)
|
.eq(OmsOrder::getId, id)
|
||||||
.eq(OmsOrder::getUserId, userId));
|
.eq(OmsOrder::getMemberId, userId));
|
||||||
if (order == null) {
|
if (order == null) {
|
||||||
throw new BizException("订单不存在,订单ID非法");
|
throw new BizException("订单不存在,订单ID非法");
|
||||||
}
|
}
|
||||||
@ -396,24 +293,5 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取订单商品 1. 直接购买 2. 购物车结算
|
|
||||||
*/
|
|
||||||
private List<OrderItemVO> getOrderItems(Long skuId, Integer count) {
|
|
||||||
if (skuId != null) { // 直接购买
|
|
||||||
OrderItemVO itemVO = OrderItemVO.builder()
|
|
||||||
.skuId(skuId)
|
|
||||||
.count(count)
|
|
||||||
.build();
|
|
||||||
return Arrays.asList(itemVO);
|
|
||||||
}
|
|
||||||
// 购物车结算,从购物车获取商品
|
|
||||||
CartVO cart = cartService.getCart();
|
|
||||||
List<OrderItemVO> items = cart.getItems().stream()
|
|
||||||
.filter(CartItemVO::isChecked)
|
|
||||||
.map(item -> OrderItemVO.builder().skuId(item.getSkuId()).count(item.getCount()).build())
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,8 @@ import org.springframework.web.bind.annotation.PathVariable;
|
|||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
|
||||||
@FeignClient(value = "mall-pms",contextId = "AdminInventoryFeignService")
|
@FeignClient(value = "mall-pms",contextId = "AdminskuFeignService")
|
||||||
public interface InventoryFeignService {
|
public interface SkuFeignService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 扣减库存
|
* 扣减库存
|
@ -3,7 +3,7 @@ package com.youlai.mall.pms.api.app;
|
|||||||
import com.youlai.common.result.Result;
|
import com.youlai.common.result.Result;
|
||||||
import com.youlai.mall.pms.pojo.domain.PmsSku;
|
import com.youlai.mall.pms.pojo.domain.PmsSku;
|
||||||
import com.youlai.mall.pms.pojo.dto.SkuDTO;
|
import com.youlai.mall.pms.pojo.dto.SkuDTO;
|
||||||
import com.youlai.mall.pms.pojo.dto.InventoryDTO;
|
import com.youlai.mall.pms.pojo.dto.SkuLockDTO;
|
||||||
import org.springframework.cloud.openfeign.FeignClient;
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
@ -28,17 +28,17 @@ public interface PmsSkuFeignService {
|
|||||||
* 锁定库存
|
* 锁定库存
|
||||||
*/
|
*/
|
||||||
@PutMapping("/api.app/v1/skus/batch/lock_stock")
|
@PutMapping("/api.app/v1/skus/batch/lock_stock")
|
||||||
Result lockStock(@RequestBody List<InventoryDTO> list);
|
Result lockStock(@RequestBody List<SkuLockDTO> list);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解锁库存
|
* 解锁库存
|
||||||
*/
|
*/
|
||||||
@PutMapping("/api.app/v1/skus/batch/unlock_stock")
|
@PutMapping("/api.app/v1/skus/batch/unlock_stock")
|
||||||
Result<Boolean> unlockStock(@RequestBody List<InventoryDTO> list);
|
Result<Boolean> unlockStock(@RequestBody List<SkuLockDTO> list);
|
||||||
|
|
||||||
|
|
||||||
@PutMapping("/api.app/v1/skus/batch/deduct_stock")
|
@PutMapping("/api.app/v1/skus/batch/deduct_stock")
|
||||||
Result deductStock(@RequestBody List<InventoryDTO> list);
|
Result deductStock(@RequestBody List<SkuLockDTO> list);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package com.youlai.mall.pms.pojo.dto;
|
package com.youlai.mall.pms.pojo.dto;
|
||||||
|
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@ -15,12 +14,14 @@ import lombok.NoArgsConstructor;
|
|||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class InventoryDTO {
|
public class SkuLockDTO {
|
||||||
|
|
||||||
@ApiModelProperty("库存ID")
|
|
||||||
private Long skuId;
|
private Long skuId;
|
||||||
|
|
||||||
@ApiModelProperty("数量")
|
|
||||||
private Integer count;
|
private Integer count;
|
||||||
|
|
||||||
|
private String orderToken;
|
||||||
|
|
||||||
|
private Boolean locked;
|
||||||
|
|
||||||
}
|
}
|
@ -6,6 +6,6 @@ package com.youlai.mall.pms.common.constant;
|
|||||||
*/
|
*/
|
||||||
public interface PmsConstants {
|
public interface PmsConstants {
|
||||||
|
|
||||||
String PRODUCT_INVENTORY_PREFIX = "pms:stock:";
|
String PMS_STOCK_LOCK_PREFIX = "pms:stock:lock:";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ public class SkuController {
|
|||||||
@PutMapping("/{id}/stock")
|
@PutMapping("/{id}/stock")
|
||||||
public Result updateStock(@PathVariable Long id, @RequestParam Integer num) {
|
public Result updateStock(@PathVariable Long id, @RequestParam Integer num) {
|
||||||
PmsSku sku = iPmsSkuService.getById(id);
|
PmsSku sku = iPmsSkuService.getById(id);
|
||||||
sku.setInventory(sku.getInventory() + num);
|
sku.setStock(sku.getStock() + num);
|
||||||
boolean result = iPmsSkuService.updateById(sku);
|
boolean result = iPmsSkuService.updateById(sku);
|
||||||
return Result.judge(result);
|
return Result.judge(result);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ package com.youlai.mall.pms.controller.app;
|
|||||||
import com.youlai.common.result.Result;
|
import com.youlai.common.result.Result;
|
||||||
import com.youlai.mall.pms.pojo.domain.PmsSku;
|
import com.youlai.mall.pms.pojo.domain.PmsSku;
|
||||||
import com.youlai.mall.pms.pojo.dto.SkuDTO;
|
import com.youlai.mall.pms.pojo.dto.SkuDTO;
|
||||||
import com.youlai.mall.pms.pojo.dto.InventoryDTO;
|
import com.youlai.mall.pms.pojo.dto.SkuLockDTO;
|
||||||
import com.youlai.mall.pms.service.IPmsSkuService;
|
import com.youlai.mall.pms.service.IPmsSkuService;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiImplicitParam;
|
import io.swagger.annotations.ApiImplicitParam;
|
||||||
@ -41,7 +41,7 @@ public class SkuController {
|
|||||||
@ApiOperation(value = "锁定库存", httpMethod = "PUT")
|
@ApiOperation(value = "锁定库存", httpMethod = "PUT")
|
||||||
@ApiImplicitParam(name = "list", value = "锁定库存", required = true, paramType = "body", dataType = "InventoryNumDTO")
|
@ApiImplicitParam(name = "list", value = "锁定库存", required = true, paramType = "body", dataType = "InventoryNumDTO")
|
||||||
@PutMapping("/batch/lock_stock")
|
@PutMapping("/batch/lock_stock")
|
||||||
public Result<Boolean> lockStock(@RequestBody List<InventoryDTO> list) {
|
public Result<Boolean> lockStock(@RequestBody List<SkuLockDTO> list) {
|
||||||
boolean result = iPmsSkuService.lockStock(list);
|
boolean result = iPmsSkuService.lockStock(list);
|
||||||
return Result.judge(result);
|
return Result.judge(result);
|
||||||
}
|
}
|
||||||
@ -50,7 +50,7 @@ public class SkuController {
|
|||||||
@ApiOperation(value = "解锁库存", httpMethod = "PUT")
|
@ApiOperation(value = "解锁库存", httpMethod = "PUT")
|
||||||
@ApiImplicitParam(name = "list", value = "释放库存", required = true, paramType = "body", dataType = "InventoryNumDTO")
|
@ApiImplicitParam(name = "list", value = "释放库存", required = true, paramType = "body", dataType = "InventoryNumDTO")
|
||||||
@PutMapping("/batch/unlock_stock")
|
@PutMapping("/batch/unlock_stock")
|
||||||
public Result<Boolean> unlockStock(@RequestBody List<InventoryDTO> list) {
|
public Result<Boolean> unlockStock(@RequestBody List<SkuLockDTO> list) {
|
||||||
boolean result = iPmsSkuService.unlockStock(list);
|
boolean result = iPmsSkuService.unlockStock(list);
|
||||||
return Result.judge(result);
|
return Result.judge(result);
|
||||||
}
|
}
|
||||||
@ -58,7 +58,7 @@ public class SkuController {
|
|||||||
@ApiOperation(value = "扣减库存", httpMethod = "PUT")
|
@ApiOperation(value = "扣减库存", httpMethod = "PUT")
|
||||||
@ApiImplicitParam(name = "list", value = "释放库存", required = true, paramType = "body", dataType = "InventoryNumDTO")
|
@ApiImplicitParam(name = "list", value = "释放库存", required = true, paramType = "body", dataType = "InventoryNumDTO")
|
||||||
@PutMapping("/batch/deduct_stock")
|
@PutMapping("/batch/deduct_stock")
|
||||||
public Result<Boolean> deductStock(@RequestBody List<InventoryDTO> list) {
|
public Result<Boolean> deductStock(@RequestBody List<SkuLockDTO> list) {
|
||||||
boolean result = iPmsSkuService.deductStock(list);
|
boolean result = iPmsSkuService.deductStock(list);
|
||||||
return Result.judge(result);
|
return Result.judge(result);
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import org.springframework.stereotype.Component;
|
|||||||
* @date 2021-03-16
|
* @date 2021-03-16
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class InventoryListener {
|
public class PmsListener {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
@ -3,7 +3,7 @@ package com.youlai.mall.pms.service;
|
|||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
import com.youlai.mall.pms.pojo.domain.PmsSku;
|
import com.youlai.mall.pms.pojo.domain.PmsSku;
|
||||||
import com.youlai.mall.pms.pojo.dto.SkuDTO;
|
import com.youlai.mall.pms.pojo.dto.SkuDTO;
|
||||||
import com.youlai.mall.pms.pojo.dto.InventoryDTO;
|
import com.youlai.mall.pms.pojo.dto.SkuLockDTO;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -14,14 +14,14 @@ public interface IPmsSkuService extends IService<PmsSku> {
|
|||||||
* @param list
|
* @param list
|
||||||
* @return 库存锁定结果
|
* @return 库存锁定结果
|
||||||
*/
|
*/
|
||||||
boolean lockStock(List<InventoryDTO> list);
|
boolean lockStock(List<SkuLockDTO> list);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解锁库存
|
* 解锁库存
|
||||||
* @param list
|
* @param list
|
||||||
* @return 解锁库存结果
|
* @return 解锁库存结果
|
||||||
*/
|
*/
|
||||||
boolean unlockStock(List<InventoryDTO> list);
|
boolean unlockStock(List<SkuLockDTO> list);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,5 +39,5 @@ public interface IPmsSkuService extends IService<PmsSku> {
|
|||||||
*/
|
*/
|
||||||
List<SkuDTO> listBySkuIds(List<Long> ids);
|
List<SkuDTO> listBySkuIds(List<Long> ids);
|
||||||
|
|
||||||
boolean deductStock(List<InventoryDTO> list);
|
boolean deductStock(List<SkuLockDTO> list);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package com.youlai.mall.pms.service.impl;
|
package com.youlai.mall.pms.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
import cn.hutool.core.convert.Convert;
|
import cn.hutool.core.convert.Convert;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
@ -9,15 +11,18 @@ import com.youlai.mall.pms.common.constant.PmsConstants;
|
|||||||
import com.youlai.mall.pms.mapper.PmsSkuMapper;
|
import com.youlai.mall.pms.mapper.PmsSkuMapper;
|
||||||
import com.youlai.mall.pms.pojo.domain.PmsSku;
|
import com.youlai.mall.pms.pojo.domain.PmsSku;
|
||||||
import com.youlai.mall.pms.pojo.dto.SkuDTO;
|
import com.youlai.mall.pms.pojo.dto.SkuDTO;
|
||||||
import com.youlai.mall.pms.pojo.dto.InventoryDTO;
|
import com.youlai.mall.pms.pojo.dto.SkuLockDTO;
|
||||||
import com.youlai.mall.pms.service.IPmsSkuService;
|
import com.youlai.mall.pms.service.IPmsSkuService;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static com.youlai.mall.pms.common.constant.PmsConstants.PMS_STOCK_LOCK_PREFIX;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@ -27,31 +32,52 @@ public class PmsSkuServiceImpl extends ServiceImpl<PmsSkuMapper, PmsSku> impleme
|
|||||||
|
|
||||||
private RedisTemplate redisTemplate;
|
private RedisTemplate redisTemplate;
|
||||||
|
|
||||||
@Override
|
private StringRedisTemplate stringRedisTemplate;
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
public boolean lockStock(List<InventoryDTO> inventories) {
|
|
||||||
log.info("锁定库存: {}", inventories);
|
|
||||||
|
|
||||||
inventories.forEach(item -> {
|
@Override
|
||||||
|
public boolean lockStock(List<SkuLockDTO> skuLockList) {
|
||||||
|
|
||||||
|
if (CollectionUtil.isNotEmpty(skuLockList)) {
|
||||||
|
throw new BizException("锁定商品为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 锁定商品
|
||||||
|
skuLockList.forEach(item -> {
|
||||||
boolean result = this.update(new LambdaUpdateWrapper<PmsSku>()
|
boolean result = this.update(new LambdaUpdateWrapper<PmsSku>()
|
||||||
|
.setSql("locked_stock = locked_stock + " + item.getCount())
|
||||||
.eq(PmsSku::getId, item.getSkuId())
|
.eq(PmsSku::getId, item.getSkuId())
|
||||||
.apply("stock >= locked_inventory + {0}", item.getCount())
|
.apply("stock >= locked_stock + {0}", item.getCount())
|
||||||
.setSql("locked_inventory = locked_inventory + " + item.getCount())
|
|
||||||
);
|
);
|
||||||
if (!result) {
|
if (result) {
|
||||||
throw new BizException("锁定库存失败,库存ID:" + item.getSkuId() + ",数量:" + item.getCount());
|
item.setLocked(true);
|
||||||
|
} else {
|
||||||
|
item.setLocked(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 锁定失败的商品集合
|
||||||
|
List<SkuLockDTO> unlockSkus = skuLockList.stream().filter(item -> !item.getLocked()).collect(Collectors.toList());
|
||||||
|
if (CollectionUtil.isNotEmpty(unlockSkus)) {
|
||||||
|
// 恢复已被锁定的库存
|
||||||
|
List<SkuLockDTO> lockSkus = skuLockList.stream().filter(SkuLockDTO::getLocked).collect(Collectors.toList());
|
||||||
|
this.unlockStock(lockSkus);
|
||||||
|
|
||||||
|
List<Long> ids = unlockSkus.stream().map(SkuLockDTO::getSkuId).collect(Collectors.toList());
|
||||||
|
throw new BizException("商品" + ids.toString() + "库存不足");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将锁定的商品保存至Redis中
|
||||||
|
String orderToken = skuLockList.get(0).getOrderToken();
|
||||||
|
stringRedisTemplate.opsForValue().set(PMS_STOCK_LOCK_PREFIX+orderToken, JSONUtil.toJsonStr(skuLockList));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean unlockStock(List<InventoryDTO> inventories) {
|
public boolean unlockStock(List<SkuLockDTO> skuList) {
|
||||||
inventories.forEach(item -> {
|
skuList.forEach(item -> {
|
||||||
boolean result = this.update(new LambdaUpdateWrapper<PmsSku>()
|
boolean result = this.update(new LambdaUpdateWrapper<PmsSku>()
|
||||||
.eq(PmsSku::getId, item.getSkuId())
|
.eq(PmsSku::getId, item.getSkuId())
|
||||||
.setSql("locked_inventory = locked_inventory - " + item.getCount())
|
.setSql("locked_stock = locked_stock - " + item.getCount())
|
||||||
);
|
);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
throw new BizException("解锁库存失败,库存ID:" + item.getSkuId() + ",数量:" + item.getCount());
|
throw new BizException("解锁库存失败,库存ID:" + item.getSkuId() + ",数量:" + item.getCount());
|
||||||
@ -62,11 +88,11 @@ public class PmsSkuServiceImpl extends ServiceImpl<PmsSkuMapper, PmsSku> impleme
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean deductStock(List<InventoryDTO> inventories) {
|
public boolean deductStock(List<SkuLockDTO> inventories) {
|
||||||
inventories.forEach(item -> {
|
inventories.forEach(item -> {
|
||||||
boolean result = this.update(new LambdaUpdateWrapper<PmsSku>()
|
boolean result = this.update(new LambdaUpdateWrapper<PmsSku>()
|
||||||
.eq(PmsSku::getId, item.getSkuId())
|
.eq(PmsSku::getId, item.getSkuId())
|
||||||
.setSql("locked_inventory = locked_inventory - " + item.getCount())
|
.setSql("locked_stock = locked_stock - " + item.getCount())
|
||||||
.setSql("stock = stock - " + item.getCount())
|
.setSql("stock = stock - " + item.getCount())
|
||||||
);
|
);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
@ -89,7 +115,7 @@ public class PmsSkuServiceImpl extends ServiceImpl<PmsSkuMapper, PmsSku> impleme
|
|||||||
public Integer getStockById(Long id) {
|
public Integer getStockById(Long id) {
|
||||||
Integer stock = 0;
|
Integer stock = 0;
|
||||||
// 读->缓存
|
// 读->缓存
|
||||||
Object cacheVal = redisTemplate.opsForValue().get(PmsConstants.PRODUCT_INVENTORY_PREFIX + id);
|
Object cacheVal = redisTemplate.opsForValue().get(PMS_STOCK_LOCK_PREFIX + id);
|
||||||
if (cacheVal != null) {
|
if (cacheVal != null) {
|
||||||
stock = Convert.toInt(cacheVal);
|
stock = Convert.toInt(cacheVal);
|
||||||
return stock;
|
return stock;
|
||||||
@ -98,12 +124,12 @@ public class PmsSkuServiceImpl extends ServiceImpl<PmsSkuMapper, PmsSku> impleme
|
|||||||
// 读->数据库
|
// 读->数据库
|
||||||
PmsSku pmsSku = this.getOne(new LambdaQueryWrapper<PmsSku>()
|
PmsSku pmsSku = this.getOne(new LambdaQueryWrapper<PmsSku>()
|
||||||
.eq(PmsSku::getId, id)
|
.eq(PmsSku::getId, id)
|
||||||
.select(PmsSku::getInventory));
|
.select(PmsSku::getStock));
|
||||||
|
|
||||||
if (pmsSku != null) {
|
if (pmsSku != null) {
|
||||||
stock = pmsSku.getInventory();
|
stock = pmsSku.getStock();
|
||||||
// 写->缓存
|
// 写->缓存
|
||||||
redisTemplate.opsForValue().set(PmsConstants.PRODUCT_INVENTORY_PREFIX + id, stock);
|
redisTemplate.opsForValue().set(PmsConstants.PMS_STOCK_LOCK_PREFIX + id, stock);
|
||||||
}
|
}
|
||||||
|
|
||||||
return stock;
|
return stock;
|
||||||
|
Loading…
Reference in New Issue
Block a user