@Override
@Transactional
public boolean decreaseStock(Integer itemId, Integer amount) throws BusinessException {
//int affectedRow = itemStockDOMapper.decreaseStock(itemId,amount);
long result = redisTemplate.opsForValue().increment("promo_item_stock_"+itemId,amount.intValue() * -1);
if(result >= 0){
//更新库存成功
boolean mqResult = mqProducer.asyncReduceStock(itemId, amount);
if(!mqResult){
redisTemplate.opsForValue().increment("promo_item_stock_"+itemId,amount.intValue());
return false;
}
return true;
}else{
//更新库存失败
redisTemplate.opsForValue().increment("promo_item_stock_"+itemId,amount.intValue());
return false;
}
/**
* @author Ni Qingzhan
* @create 2020-07-26-22:17
*/
@Component
public class MqProducer {
private DefaultMQProducer producer;
@Value("${mq.nameserver.addr}")
private String nameAddr;
@Value("${mq.topicname}")
private String topicName;
@PostConstruct
public void init() throws MQClientException {
//做mq producer的初始化
producer = new DefaultMQProducer("producer_group");
producer.setNamesrvAddr(nameAddr);
producer.start();
}
//同步库存扣减消息
public boolean asyncReduceStock(Integer itemId, Integer amount) {
Map<String, Object> bodyMap = new HashMap<>();
bodyMap.put("itemId", itemId);
bodyMap.put("amount", amount);
Message message = new Message(topicName, "increase", JSON.toJSON(bodyMap).toString().getBytes(Charset.forName("UTF-8")));
try {
producer.send(message);
} catch (MQClientException e) {
e.printStackTrace();
return false;
} catch (RemotingException e) {
e.printStackTrace();
return false;
} catch (MQBrokerException e) {
e.printStackTrace();return false;
} catch (InterruptedException e) {
e.printStackTrace();return false;
}
return true;
}
}
/**
* @author Ni Qingzhan
* @create 2020-07-26-22:17
*/
@Component
public class MqConsumer {
private DefaultMQPushConsumer consumer;
@Value("${mq.nameserver.addr}")
private String nameAddr;
@Value("${mq.topicname}")
private String topicName;
@Autowired
private ItemStockDOMapper itemStockDOMapper;
@PostConstruct
public void init() throws MQClientException {
//做mq Comsumer的初始化
consumer = new DefaultMQPushConsumer("stock_consumer_group");
consumer.setNamesrvAddr(nameAddr);
consumer.subscribe(topicName, "*");
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
Message msg = msgs.get(0);
String jsonString = new String(msg.getBody());
Map<String, Object> map = JSON.parseObject(jsonString, Map.class);
Integer itemId = (Integer)map.get("itemId");
Integer amount = (Integer)map.get("amount");
//实现库存真正减去
itemStockDOMapper.decreaseStock(itemId, amount);
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
}
}
@Override
@Transactional
public OrderModel createOrder(Integer userId, Integer itemId, Integer promoId, Integer amount) throws BusinessException {
//1.校验下单状态,下单的商品是否存在,用户是否合法,购买数量是否正确
//ItemModel itemModel = itemService.getItemById(itemId);
ItemModel itemModel = itemService.getItemByIdInCache(itemId);
if(itemModel == null){
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"商品信息不存在");
}
UserModel userModel = userService.getUserByIdInCache(userId);
if(userModel == null){
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"用户信息不存在");
}
if(amount <= 0 || amount > 99){
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"数量信息不正确");
}
//校验活动信息
if(promoId != null){
//(1)校验对应活动是否存在这个适用商品
if(promoId.intValue() != itemModel.getPromoModel().getId()){
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"活动信息不正确");
//(2)校验活动是否正在进行中
}else if(itemModel.getPromoModel().getStatus().intValue() != 2) {
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"活动信息还未开始");
}
}
//2.落单减库存
boolean result = itemService.decreaseStock(itemId,amount);
if(!result){
throw new BusinessException(EmBusinessError.STOCK_NOT_ENOUGH);
}
//3.订单入库
OrderModel orderModel = new OrderModel();
orderModel.setUserId(userId);
orderModel.setItemId(itemId);
orderModel.setAmount(amount);
if(promoId != null){
orderModel.setItemPrice(itemModel.getPromoModel().getPromoItemPrice());
}else{
orderModel.setItemPrice(itemModel.getPrice());
}
orderModel.setPromoId(promoId);
orderModel.setOrderPrice(orderModel.getItemPrice().multiply(new BigDecimal(amount)));
//生成交易流水号,订单号
orderModel.setId(generateOrderNo());
OrderDO orderDO = convertFromOrderModel(orderModel);
orderDOMapper.insertSelective(orderDO);
//加上商品的销量
itemService.increaseSales(itemId,amount);
//4.返回前端
return orderModel;
}
问题:
若减库存成功,但是订单入库失败?发生少卖
先减redis库存,订单入库,最后发送异步消息。。。。但是当异步成功,但是commit失败,也会出现问题。所以能不能等事务成功后异步消息。
可以的
ransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
//异步更新库存
try {
boolean mqResult = itemService.asyncDecreaseStock(itemId, amount);
} catch (BusinessException e) {
e.printStackTrace();
}
}
});
但是在数据库提交之后和异步消息之间发生问题? 中间发生断档,异步消息发送失败。
发送事务性消息:
private TransactionMQProducer transactionMQProducer;
操作流水
![]()
库存售罄
库存售馨标识
售馨后不去操作后续流程
售馨后通知各系统售馨
在redis中设置响应的key。
本文探讨了在库存管理系统中如何处理库存减少与订单创建之间的事务一致性问题,特别是在使用消息队列的情况下。通过分析不同场景,如库存减少成功但订单创建失败的情况,提出了解决方案,包括使用事务性消息来确保在库存操作和数据库操作之间的原子性和一致性。

1069

被折叠的 条评论
为什么被折叠?



