河北省住房建设厅网站首页粉色帝国网站
- 作者: 五速梦信息网
- 时间: 2026年03月21日 10:55
当前位置: 首页 > news >正文
河北省住房建设厅网站首页,粉色帝国网站,WordPress打开速度不稳定,wordpress专业主题订单服务的问题及解决方案问题1#xff1a;Feign远程调用时丢失请求头编辑出现这个Feign远程调用时丢失请求头的问题是因为Feign在远程调用的时候会创建一个新的请求#xff0c;但是这个新的请求里啥都没有#xff0c;没有cookie值#xff0c;而这个cookie值里有成功登录后… 订单服务的问题及解决方案问题1Feign远程调用时丢失请求头编辑出现这个Feign远程调用时丢失请求头的问题是因为Feign在远程调用的时候会创建一个新的请求但是这个新的请求里啥都没有没有cookie值而这个cookie值里有成功登录后的信息所以由于新请求中没有cookie值就会被购物车服务的登录拦截器给拦截了package com.saodai.saodaimall.order.config;import feign.RequestInterceptor; import feign.RequestTemplate; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;/** *feign拦截器功能用于解决feign远程调用时请求头丢失的问题 在配置类里写的拦截器是不需要手动注册到springMVC中因为用注解他会自动注册 /Configuration public class GuliFeignConfig {Bean(requestInterceptor)public RequestInterceptor requestInterceptor() {RequestInterceptor requestInterceptor new RequestInterceptor() {Overridepublic void apply(RequestTemplate template) {//1、使用RequestContextHolder拿到刚进来的请求数据ServletRequestAttributes requestAttributes (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();if (requestAttributes ! null) {//老请求HttpServletRequest request requestAttributes.getRequest();if (request ! null) {//2、同步请求头的数据主要是cookie//获取老请求的cookie值String cookie request.getHeader(Cookie);//把老请求的cookie值放到新请求上来进行一个同步template表示新请求template.header(Cookie, cookie);}}}};return requestInterceptor;}}加个RequestInterceptor拦截器重写apply方法把老请求的cookie值设置到新请求中去这样就解决了这个问题问题2Feign异步情况丢失上下文问题编辑编辑导致Feign异步情况丢失上下文问题是因为Feign在远程调用服务的时候数据都是放在TreadLocalRequestContextHolder获取的请求信息而RequestContextHolder是用TreadLocal做的中的TreadLocal中同一个线程共享数据是没有什么问题的也就是原先同步的时候是没有问题但是由于为了提高效率开了异步任务自然异步任务的线程跟原来的线程不是同一个线程就会出现丢失上下的情况 Overridepublic OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {//获取当前线程请求头信息包括上下文RequestAttributes requestAttributes RequestContextHolder.getRequestAttributes();/开启第一个异步任务/CompletableFutureVoid addressFuture CompletableFuture.runAsync(() - {//把当前线程请求头信息包括上下文设置为异步线程中的请求头信息包括上下文RequestContextHolder.setRequestAttributes(requestAttributes);//异步任务}, threadPoolExecutor);} }解决办法就是把原来线程的的上下文设置到每一个异步线程的上下文中即可问题3解库存的分布式事务问题问题就是提交订单submitOrder这个方法的所有代码实现的业务都要保持原子性如果出现任何异常都要进行数据回滚例如这个方法中是先保存订单和订单项的信息到数据库对应的表格中然后再调用远程服务去锁库存如果锁库存成功后再执行下面的代码出现异常或者电脑嗝屁了这类情况那前面保存好的订单和订单项就应该数据回滚也就是删除刚才录入的数据同时库存服务也应该解库存。一般情况下只需要在submitOrder方法上加一个Transactional注解就可以了但是这个注解只能回滚本地的服务不能回滚库存服务中的锁库存也就是这个注解可以实现出现异常后删除刚才录入数据库的订单和订单项的数据但是没办法把锁库存的数量给改回原来的数量因为锁库存是库存服务的不是订单服务的所以没法靠Transactional注解来实现回滚解决办法这里采用的是通过RabbitMQ的延时队列来实现保持数据的最终一致性也就是保持数据库的数据最后的一致性而不是像Transactional注解一样立马就回滚立马保持数据的一致性这里可以理解成Transactional 是出现异常后飞快的把数据恢复到原来的样子而保持数据的最终一致性是过了一段时间后才恢复到原来的样子那RabbitMQ的延时队列是怎样来实现数据的最终一致性的呢编辑简单说下其实就是用户下订单后就给延时队列发送消息如果这个订单的状态在指定的时间过后还是待付款就自动取消这个订单取消了这个订单后就立马发消息给队列去解库存可以看出这里就不在乎你到底是异常导致的没支付成功还是用户没付款导致的再或者是电脑嗝屁了无论是哪种情况都不管我只管这个订单有没有在指定的时间内把订单的状态改成已支付状态只要是过了指定的时间订单的状态还是待付款的话那我就给你回滚 数据不但之前已经存到数据库的订单和订单项数据我都给你删了而且库存也给你解了就是这么霸气!从而这样来保持数据库的最终一致性具体实现1、在MyRabbitMQConfig配置类中创建队列、交换机、队列和交换机的绑定关系订单服务和库存服务都是创建一个主题交换机两个队列一个队列是用来存放消息的另外一个队列是用来存放死信的也就是死了的消息这里并没有直接处理掉而是放到这个队列里整体思路首先生产者发送一个消息给topic交换机order-event-exchange交换机根据路由键order.create.order路由到延时队列order.delay.queue然后消息在延时队列里等待指定的时间当时间过期后还没有被消费就会被当成死信然后把这个消息通过交换机order-event-exchange识别他的新的路由键order.release.order路由到新的队列order.release.order.queuepackage com.saodai.saodaimall.order.config;import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.Exchange; import org.springframework.amqp.core.Queue; import org.springframework.amqp.core.TopicExchange; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;import java.util.HashMap;/ RabbitMQ配置类* 整体思路* 首先生产者发送一个消息给topic交换机order-event-exchange交换机根据路由键order.create.order路由到延时队列order.delay.queue* 然后消息在延时队列里等待指定的时间当时间过期后还没有被消费就会被当成死信然后把这个消息通过交换机order-event-exchange识别他的新的* 路由键order.release.order路由到新的队列order.release.order.queue** 这里只有一个交换机两个队列一个队列是用来存放消息的另外一个队列是用来存放死信的也就是死了的消息这里并没有直接处理掉而是放到这个队列里/Configuration public class MyRabbitMQConfig {/创建延时队列延时队列是通过参数来设置的 arguments.put(x-dead-letter-exchange, order-event-exchange);前面的固定的前缀表示这个队列延时后的消息* return*/ Beanpublic Queue orderDelayQueue() {//用map构造参数HashMapString, Object arguments new HashMap();//指定延时后的消息的交换机x-dead-letter-exchange是固定的前缀order-event-exchange是自定义的交换机arguments.put(x-dead-letter-exchange, order-event-exchange);//死信路由也就是消息如果超时了就会被当作死信然后通过路由键order.release.order路由到指定的消息队列arguments.put(x-dead-letter-routing-key, order.release.order);//设置消息过期时间arguments.put(x-message-ttl, 60000); // 消息过期时间 1分钟/Queue(String name, 队列名字boolean durable, 是否持久化boolean exclusive, 是否排他boolean autoDelete, 是否自动删除MapString, Object arguments) 参数/Queue queue new Queue(order.delay.queue, true, false, false, arguments);return queue;}/* 死信队列也就是到了这个队列的都是要死的消息** return/Beanpublic Queue orderReleaseQueue() {Queue queue new Queue(order.release.order.queue, true, false, false);return queue;}/** TopicExchange创建主题类型的交换机 return/Beanpublic Exchange orderEventExchange() {/** String name, boolean durable,* boolean autoDelete,* MapString, Object arguments* /return new TopicExchange(order-event-exchange, true, false);}/** 创建交换机和队列的捆绑关系延时队列捆绑* return/Beanpublic Binding orderCreateBinding() {/** String destination, 目的地队列名或者交换机名字 DestinationType destinationType, 目的地类型Queue、Exhcange* String exchange,* String routingKey,* MapString, Object arguments* /return new Binding(order.delay.queue,Binding.DestinationType.QUEUE,order-event-exchange,order.create.order,null);}/** 创建交换机和队列的捆绑关系死信队列捆绑* return/Beanpublic Binding orderReleaseBinding() {return new Binding(order.release.order.queue,Binding.DestinationType.QUEUE,order-event-exchange,order.release.order,null);}/** 订单释放直接和库存释放进行绑定* return/Beanpublic Binding orderReleaseOtherBinding() {return new Binding(stock.release.stock.queue,Binding.DestinationType.QUEUE,order-event-exchange,order.release.other.#,null);}/** 商品秒杀队列* return/Beanpublic Queue orderSecKillOrrderQueue() {Queue queue new Queue(order.seckill.order.queue, true, false, false);return queue;}Beanpublic Binding orderSecKillOrrderQueueBinding() {//String destination, DestinationType destinationType, String exchange, String routingKey,// MapString, Object argumentsBinding binding new Binding(order.seckill.order.queue,Binding.DestinationType.QUEUE,order-event-exchange,order.seckill.order,null);return binding;}}/****创建延时队列 return*/ Beanpublic Queue orderDelayQueue() {//用map构造参数HashMapString, Object arguments new HashMap();//指定延时后的消息的交换机x-dead-letter-exchange是固定的前缀order-event-exchange是自定义的交换机arguments.put(x-dead-letter-exchange, order-event-exchange);//死信路由也就是消息如果超时了就会被当作死信然后通过路由键order.release.order路由到指定的消息队列arguments.put(x-dead-letter-routing-key, order.release.order);//设置消息过期时间arguments.put(x-message-ttl, 60000); // 消息过期时间 1分钟/Queue(String name, 队列名字boolean durable, 是否持久化boolean exclusive, 是否排他boolean autoDelete, 是否自动删除MapString, Object arguments) 参数/Queue queue new Queue(order.delay.queue, true, false, false, arguments);return queue;}创建特殊的延时队列只需要传入一个map类型的参数进去就可以让普通队列成为一个延时队列 //指定延时后的消息的交换机x-dead-letter-exchange是固定的前缀order-event-exchange是自定义的交换机 arguments.put(x-dead-letter-exchange, order-event-exchange); //死信路由也就是消息如果超时了就会被当作死信然后通过路由键order.release.order路由到指定的消息队列 arguments.put(x-dead-letter-routing-key, order.release.order); //设置消息过期时间 arguments.put(x-message-ttl, 60000); // 消息过期时间 1分钟x-dead-letter-exchange这个key是rabbitMQ封装好了的固定的前缀表示这个队列延时后的消息指定使用order-event-exchange这个交换机这个交换机来把延时的消息进行传输x-dead-letter-routing-key也是rabbitMQ封装好了的固定的前缀表示这个队列延时后的消息使用的新路由键是order.release.orderx-message-ttl也是rabbitMQ封装好了的固定的前缀表示设置延时队列的延时时间是多少也就是上面的三个key值都是封装好的固定前缀后面的值才是自定义的package com.saodai.saodaimall.ware.config;import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.Exchange; import org.springframework.amqp.core.Queue; import org.springframework.amqp.core.TopicExchange; import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.amqp.support.converter.MessageConverter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;import java.util.HashMap;/*** RabbitMQ配置类一个交换机两个队列两个绑定跟订单服务的基本一样详细介绍看订单服务的队列/ Configuration public class MyRabbitMQConfig {/* 使用JSON序列化机制进行消息转换* return/Beanpublic MessageConverter messageConverter() {return new Jackson2JsonMessageConverter();}/** RabbitMQ要第一次连接上发现没有队列或者交换机才会创建所以如果没有下面的代码运行会发现官网中并没有创建交换机和队列* 下面代码就是模拟监听这样就可以连接上RabbitMQ然后可以创建交换机和队列* 但是后面要注释掉自动解锁库存时这里也会监听队列导致多一个消费者所以要注释掉/ // RabbitListener(queues stock.release.stock.queue) // public void handle(Message message) { // // }/** 库存服务默认的交换机* return/Beanpublic Exchange stockEventExchange() {//String name, boolean durable, boolean autoDelete, MapString, Object argumentsTopicExchange topicExchange new TopicExchange(stock-event-exchange, true, false);return topicExchange;}/** 普通队列* return/Beanpublic Queue stockReleaseStockQueue() {//String name, boolean durable, boolean exclusive, boolean autoDelete, MapString, Object argumentsQueue queue new Queue(stock.release.stock.queue, true, false, false);return queue;}/** 延迟队列* return/Beanpublic Queue stockDelay() {HashMapString, Object arguments new HashMap();arguments.put(x-dead-letter-exchange, stock-event-exchange);arguments.put(x-dead-letter-routing-key, stock.release);// 消息过期时间 2分钟arguments.put(x-message-ttl, 120000);Queue queue new Queue(stock.delay.queue, true, false, false,arguments);return queue;}/** 交换机与普通队列绑定* return/Beanpublic Binding stockLocked() {//String destination, DestinationType destinationType, String exchange, String routingKey,// MapString, Object argumentsBinding binding new Binding(stock.release.stock.queue,Binding.DestinationType.QUEUE,stock-event-exchange,stock.release.#,null);return binding;}/** 交换机与延迟队列绑定* return/Beanpublic Binding stockLockedBinding() {return new Binding(stock.delay.queue,Binding.DestinationType.QUEUE,stock-event-exchange,stock.locked,null);}}2、理解订单服务和库存服务的RabbitMQ队列的图编辑1订单服务使用RabbitMQ的整个过程1、订单创建成功后发送消息给topic主题交换机order-event-exchange交换机根据order.create.order路由键把消息路由到order.delay.queue延时队列订单创建成功是指OrderServiceImpl类中的submitOrder方法执行成功后给order.delay.queue队列发送消息rabbitTemplate.convertAndSend(order-event-exchange,order.create.order,order.getOrder());2、消息在order.delay.queue延时队列里等待指定的时间当时间过期后还没有被消费就会被当成死信然后把这个消息通过order-event-exchange交换机的新的路由键order.release.order路由到order.release.order.queue队列过期后的路由键和交换机设置是由订单服务的MyRabbitMQConfig配置的注意这里order.delay.queue队列只是作为延时队列来使用的正常情况是会有队列的监听器来监听这个队列的消息然后消费掉但是在这个场景中是没有消费者来消费这个队列的消息的因为这个队列只需要延时就可以了并不需要消费者这个队列的消息等待指定的时间后就会被送到order.release.order.queue队列里从而达到延时队列的效果 //交换机x-dead-letter-exchange是固定的order-event-exchange是自定义的交换机 arguments.put(x-dead-letter-exchange, order-event-exchange); //死信路由也就是消息如果超时了就会被当作死信然后通过路由键order.release.order路由到指定的消息队列 arguments.put(x-dead-letter-routing-key, order.release.order); //设置消息过期时间 arguments.put(x-message-ttl, 60000); // 消息过期时间 1分钟3、设置一个监听器用来消费order.release.order.queue队列的消息这个队列的消息都是已经超时了的消息也就是模拟用户生成订单后没有支付的订单所以要写个监听器来取消之前生成的订单package com.saodai.saodaimall.order.listener;import com.rabbitmq.client.Channel; import com.saodai.saodaimall.order.entity.OrderEntity; import com.saodai.saodaimall.order.service.OrderService; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;import java.io.IOException;/** 订单监听器监听的是队列order.release.order.queue定时关闭订单* 但凡被这个监听器监听到的消息都是过期的死信/ RabbitListener(queues order.release.order.queue) Service public class OrderCloseListener {Autowiredprivate OrderService orderService;RabbitHandlerpublic void listener(OrderEntity orderEntity, Channel channel, Message message) throws IOException {System.out.println(收到过期的订单信息准备关闭订单 orderEntity.getOrderSn());try {//关闭订单orderService.closeOrder(orderEntity);//消费者的手动ack确认这条消息被成功消费了channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);} catch (Exception e) {channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);}}}/* 关闭订单这个方法由OrderCloseListener监听器调用* 这个方法被调用说明这个订单已经过了指定的时间还没有付款* 所谓的关闭订单其实就是修改订单的状态修改成已取消就行了* param orderEntity 前面生成订单时发送给RabbitMQ队列的消息orderEntity/Overridepublic void closeOrder(OrderEntity orderEntity) {//关闭订单之前先查询一下数据库判断此订单状态是否已支付OrderEntity orderInfo this.getOne(new QueryWrapperOrderEntity().eq(order_sn,orderEntity.getOrderSn()));// CREATE_NEW(0,待付款)说明这个订单已经过了指定的时间还没有付款if (orderInfo.getStatus().equals(OrderStatusEnum.CREATE_NEW.getCode())) {//如果是待付款状态就可以进行关单OrderEntity orderUpdate new OrderEntity();orderUpdate.setId(orderInfo.getId());//把待付款修改成已取消的状态即可CANCLED(4,已取消)orderUpdate.setStatus(OrderStatusEnum.CANCLED.getCode());this.updateById(orderUpdate);/**这里要考虑一个情况这个特殊情况是需要下面的额外处理 防止订单服务卡顿导致订单状态消息一直改不了也就是上面的代码因为卡顿导致没有执行解库存服务先执行查订单状态发现不是取消状态然后什么都不处理* 导致卡顿的订单永远都不能解锁库存* 所以订单释放直接和库存释放进行绑定/// 发送消息给MQOrderTo orderTo new OrderTo();BeanUtils.copyProperties(orderInfo, orderTo);try {//订单释放直接和库存释放进行绑定/** 订单取消后立马发消息给交换机交换机把这个消息通过路由键order.release.other发到队列stock.release.stock.queue* 这个路由设置是由MyRabbitMQConfig中的orderReleaseOtherBinding方法进行绑定的*/rabbitTemplate.convertAndSend(order-event-exchange, order.release.other, orderTo);} catch (Exception e) {//TODO 定期扫描数据库重新发送失败的消息}}}关闭订单之前先查询一下数据库判断此订单状态是否已支付关闭订单其实就是修改订单的状态修改成已取消这里要考虑一个情况这个特殊情况是需要额外的处理的按理来说是订单服务的取消订单操作是在解库存操作的前面的也就是一般先会取消订单操作后再去解库存操作但是如果取消订单操作因为网络卡顿导致解库存操作先执行的话就会出现下面的情况解库存的实现逻辑又是先来看看订单的状态是不是已取消如果是已取消才会去解库存否则就不会执行解库存操作了上面的情况就会出现解库存操作来看订单状态的时候发现订单状态是待支付不是已取消状态 所以就不执行解库存操作由于解库存操作只会来查看一次所以就会导致卡顿的订单永远都不能解锁库存解决办法订单取消后立马发消息给order-event-exchange交换机交换机把这个消息通过路由键order.release.other发到stock.release.stock.queue队列这个队列其中有个监听方法就是来监听这个消息的只要监听到这个消息就会立马执行解库存rabbitTemplate.convertAndSend(order-event-exchange, order.release.other, orderTo);package com.saodai.common.to;import lombok.Data;import java.math.BigDecimal; import java.util.Date;/** *订单类 /Data public class OrderTo {private Long id;/** member_id/private Long memberId;/** 订单号/private String orderSn;/** 使用的优惠券/private Long couponId;/** create_time/private Date createTime;/** 用户名/private String memberUsername;/** 订单总额/private BigDecimal totalAmount;/** 应付总额/private BigDecimal payAmount;/** 运费金额/private BigDecimal freightAmount;/** 促销优化金额促销价、满减、阶梯价/private BigDecimal promotionAmount;/** 积分抵扣金额/private BigDecimal integrationAmount;/** 优惠券抵扣金额/private BigDecimal couponAmount;/** 后台调整订单使用的折扣金额/private BigDecimal discountAmount;/** 支付方式【1-支付宝2-微信3-银联 4-货到付款】/private Integer payType;/** 订单来源[0-PC订单1-app订单]/private Integer sourceType;/** 订单状态【0-待付款1-待发货2-已发货3-已完成4-已关闭5-无效订单】/private Integer status;/** 物流公司(配送方式)/private String deliveryCompany;/** 物流单号/private String deliverySn;/** 自动确认时间天/private Integer autoConfirmDay;/** 可以获得的积分/private Integer integration;/** 可以获得的成长值/private Integer growth;/** 发票类型[0-不开发票1-电子发票2-纸质发票]/private Integer billType;/** 发票抬头/private String billHeader;/** 发票内容/private String billContent;/** 收票人电话/private String billReceiverPhone;/** 收票人邮箱/private String billReceiverEmail;/** 收货人姓名/private String receiverName;/** 收货人电话/private String receiverPhone;/** 收货人邮编/private String receiverPostCode;/** 省份/直辖市/private String receiverProvince;/** 城市/private String receiverCity;/** 区/private String receiverRegion;/** 详细地址/private String receiverDetailAddress;/** 订单备注/private String note;/** 确认收货状态[0-未确认1-已确认]/private Integer confirmStatus;/** 删除状态【0-未删除1-已删除】/private Integer deleteStatus;/** 下单时使用的积分/private Integer useIntegration;/** 支付时间/private Date paymentTime;/** 发货时间/private Date deliveryTime;/** 确认收货时间/private Date receiveTime;/** 评价时间/private Date commentTime;/** 修改时间/private Date modifyTime;}2库存服务使用RabbitMQ的整个过程锁库存成功后就会发消息给stock-event-exchange交换机交换机根据路由键stock.locked把消息路由到stock.delay.queue延时队列跟上面一样这个延时队列的消息不会被消费掉时间过期后就把消息根据路由键stock.release路由到stock.release.stock.queue队列然后这个消息队列的消息是被一个专门解库存的监听器来监听注意这里有两种解库存的监听方法一个是自动解库存的监听一个是订单服务的订单取消后立马解库存的监听rabbitTemplate.convertAndSend(stock-event-exchange,stock.locked,lockedTo);package com.saodai.saodaimall.ware.listener;import com.rabbitmq.client.Channel; import com.saodai.common.to.OrderTo; import com.saodai.common.to.mq.StockLockedTo; import com.saodai.saodaimall.ware.service.WareSkuService; import lombok.extern.slf4j.Slf4j; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;import java.io.IOException;/** RabbitMQ的监听器* 这里有两个监听方法这两个监听识别的依据是看传入的是StockLockedTo还是OrderTo* 一个是监听的库存自动解锁* 一个是监听订单取消后库存解锁/Slf4j RabbitListener(queues stock.release.stock.queue) Service public class StockReleaseListener {Autowiredprivate WareSkuService wareSkuService;/**** 监听库存自动解锁/RabbitHandlerpublic void handleStockLockedRelease(StockLockedTo to, Message message, Channel channel) throws IOException {log.info(***收到解锁库存的信息);try {System.out.println(收到解锁库存的信息);//当前消息是否被第二次及以后重新派发过来了// Boolean redelivered message.getMessageProperties().getRedelivered();//解锁库存wareSkuService.unlockStock(to);// 手动删除消息channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);} catch (Exception e) {// 解锁失败 将消息重新放回队列让别人消费channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);}}/* 防止订单服务卡顿导致订单状态消息一直改不了库存优先到期查订单状态新建什么都不处理导致卡顿的订单永远都不能解锁库存 订单释放直接和库存释放进行绑定* param orderTo* param message* param channel* throws IOException/RabbitHandlerpublic void handleOrderCloseRelease(OrderTo orderTo, Message message, Channel channel) throws IOException {log.info(***收到订单关闭准备解锁库存的信息*);try {wareSkuService.unlockStock(orderTo);// 手动删除消息channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);} catch (Exception e) {// 解锁失败 将消息重新放回队列让别人消费channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);}}}package com.saodai.common.to.mq;import lombok.Data;/ 发送到mq消息队列的to/Data public class StockLockedTo {/ 库存工作单的id /private Long id;/ 工作单详情的所有信息 StockDetailTo对象内容就是上面的WareOrderTaskDetailEntity /private StockDetailTo detailTo; }解锁库存的思路首先查询数据库的库存详细工作单表看看有没有成功锁定库存如果成功锁库存了会有对应的一条记录如果没有那就说明库存没有锁成功那自然就不需要解锁了库存详细工作单表有这条记录那就证明库存锁定成功了具体需不需要解库存还要先看订单状态先查询有没有这个订单没有这个订单必须解锁库存可能出现因为有异常造成的数据回滚导致订单不存在的情况但是库存锁成功了有这个订单不一定解锁库存要根据订单的状态来决定是否解库存订单状态是已取消状态说明是用户没有支付订单过期了那就必须解锁库存订单状态是已支付状态说明是用户支付成功了那就不能解锁库存除了判断上面的情况还有考虑当前库存详细工作单的状态只有满足订单状态是已取消状态并且是已锁定的状态那才可以解库存已锁定解锁库存已解锁 不能再解锁 /* (这个方法是由StockReleaseListener监听器调用的)* 锁库存失败后的自动解锁也就是回溯* param to/Overridepublic void unlockStock(StockLockedTo to) {//获取库存详细工作单类StockDetailTo detail to.getDetailTo();//库存详细工作单的idLong detailId detail.getId();//WareOrderTaskDetailEntity是库存详细工作单类WareOrderTaskDetailEntity taskDetailInfo wareOrderTaskDetailService.getById(detailId);if (taskDetailInfo ! null) {//查出wms_ware_order_task工作单的信息Long id to.getId();//订单锁库存工作单(获取哪个订单要锁库存)WareOrderTaskEntity orderTaskInfo wareOrderTaskService.getById(id);//获取订单号查询订单状态String orderSn orderTaskInfo.getOrderSn();//远程查询订单信息R orderData orderFeignService.getOrderStatus(orderSn);if (orderData.getCode() 0) {//订单数据返回成功OrderVo orderInfo orderData.getData(data, new TypeReferenceOrderVo() {});/** CREATE_NEW(0,待付款),* PAYED(1,已付款),* SENDED(2,已发货),* RECIEVED(3,已完成),* CANCLED(4,已取消),* SERVICING(5,售后中),* SERVICED(6,售后完成);///订单不存在因为有异常造成的数据回滚导致订单不存在或者订单状态是取消状态orderInfo.getStatus() 4才可解库存if (orderInfo null || orderInfo.getStatus() 4) {//当前库存工作单详情状态1已锁定只有当前库存工作单详情状态未解锁才可以解锁if (taskDetailInfo.getLockStatus() 1) {//调用真正接库存的方法unLockStockunLockStock(detail.getSkuId(),detail.getWareId(),detail.getSkuNum(),detailId);}}} else {//消息拒绝以后重新放在队列里面让别人继续消费解锁//远程调用服务失败throw new RuntimeException(远程调用服务失败);}} else {//无需解锁}}/** 真正解锁库存的方法自动解库存* param skuId 需要解锁库存的商品id* param wareId 需要解锁库存的库存仓库id* param num 需要解锁库存的商品数量* param taskDetailId 库存工作单详情id/public void unLockStock(Long skuId,Long wareId,Integer num,Long taskDetailId) {//库存解锁其实就是修改wms_ware_sku表中的stock_locked的值之前锁库存锁了多少个就减去多少个wareSkuDao.unLockStock(skuId,wareId,num);//更新工作单的状态WareOrderTaskDetailEntity taskDetailEntity new WareOrderTaskDetailEntity();taskDetailEntity.setId(taskDetailId);//setLockStatus(2)表示变为已解锁1表示已锁定2表示已解锁3表示减扣taskDetailEntity.setLockStatus(2);wareOrderTaskDetailService.updateById(taskDetailEntity);}/** 订单取消了就立马解库存* 防止订单服务卡顿导致订单状态消息一直改不了库存优先到期查订单状态新建什么都不处理* 导致卡顿的订单永远都不能解锁库存* param orderTo/Transactional(rollbackFor Exception.class)Overridepublic void unlockStock(OrderTo orderTo) {String orderSn orderTo.getOrderSn();//查一下最新的库存解锁状态防止重复解锁库存WareOrderTaskEntity orderTaskEntity wareOrderTaskService.getOrderTaskByOrderSn(orderSn);//按照工作单的id找到所有 没有解锁的库存进行解锁lock_status1表示已锁定库存Long id orderTaskEntity.getId();ListWareOrderTaskDetailEntity list wareOrderTaskDetailService.list(new QueryWrapperWareOrderTaskDetailEntity().eq(task_id, id).eq(lock_status, 1));for (WareOrderTaskDetailEntity taskDetailEntity : list) {//解锁库存unLockStock(taskDetailEntity.getSkuId(),taskDetailEntity.getWareId(),taskDetailEntity.getSkuNum(),taskDetailEntity.getId());}}自动解库存/** (这个方法是由StockReleaseListener监听器调用的)* 锁库存失败后的自动解锁也就是回溯* param to/Overridepublic void unlockStock(StockLockedTo to) {//获取库存详细工作单类StockDetailTo detail to.getDetailTo();//库存详细工作单的idLong detailId detail.getId();//WareOrderTaskDetailEntity是库存详细工作单类WareOrderTaskDetailEntity taskDetailInfo wareOrderTaskDetailService.getById(detailId);if (taskDetailInfo ! null) {//查出wms_ware_order_task工作单的信息Long id to.getId();//订单锁库存工作单(获取哪个订单要锁库存)WareOrderTaskEntity orderTaskInfo wareOrderTaskService.getById(id);//获取订单号查询订单状态String orderSn orderTaskInfo.getOrderSn();//远程查询订单信息R orderData orderFeignService.getOrderStatus(orderSn);if (orderData.getCode() 0) {//订单数据返回成功OrderVo orderInfo orderData.getData(data, new TypeReferenceOrderVo() {});/** CREATE_NEW(0,待付款),* PAYED(1,已付款),* SENDED(2,已发货),* RECIEVED(3,已完成),* CANCLED(4,已取消),* SERVICING(5,售后中),* SERVICED(6,售后完成);///订单不存在因为有异常造成的数据回滚导致订单不存在或者订单状态是取消状态orderInfo.getStatus() 4才可解库存if (orderInfo null || orderInfo.getStatus() 4) {//当前库存工作单详情状态1已锁定只有当前库存工作单详情状态未解锁才可以解锁if (taskDetailInfo.getLockStatus() 1) {//调用真正接库存的方法unLockStockunLockStock(detail.getSkuId(),detail.getWareId(),detail.getSkuNum(),detailId);}}} else {//消息拒绝以后重新放在队列里面让别人继续消费解锁//远程调用服务失败throw new RuntimeException(远程调用服务失败);}} else {//无需解锁}}/** 真正解锁库存的方法自动解库存* param skuId 需要解锁库存的商品id* param wareId 需要解锁库存的库存仓库id* param num 需要解锁库存的商品数量* param taskDetailId 库存工作单详情id/public void unLockStock(Long skuId,Long wareId,Integer num,Long taskDetailId) {//库存解锁其实就是修改wms_ware_sku表中的stock_locked的值之前锁库存锁了多少个就减去多少个wareSkuDao.unLockStock(skuId,wareId,num);//更新工作单的状态WareOrderTaskDetailEntity taskDetailEntity new WareOrderTaskDetailEntity();taskDetailEntity.setId(taskDetailId);//setLockStatus(2)表示变为已解锁1表示已锁定2表示已解锁3表示减扣taskDetailEntity.setLockStatus(2);wareOrderTaskDetailService.updateById(taskDetailEntity);}自动解库存的具体实现流程获取库存详细工作单的id package com.saodai.common.to.mq;import lombok.Data;/** 发送到mq消息队列的to/Data public class StockLockedTo {/ 库存工作单的id /private Long id;/ 工作单详情的所有信息 /private StockDetailTo detailTo; }package com.saodai.common.to.mq;import lombok.Data;/* 其实就是库存工作单详情实体类具体给订单的哪个商品锁库存/Data public class StockDetailTo {private Long id;/* sku_id/private Long skuId;/** sku_name/private String skuName;/** 购买个数/private Integer skuNum;/** 工作单id/private Long taskId;/** 仓库id/private Long wareId;/** 锁定状态/private Integer lockStatus;}查询数据库有没有这个库存详细工作单类package com.saodai.saodaimall.ware.entity;import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor;import java.io.Serializable;/** 库存工作单详情具体给订单的哪个商品锁库存/NoArgsConstructor AllArgsConstructor Builder Data TableName(wms_ware_order_task_detail) public class WareOrderTaskDetailEntity implements Serializable {private static final long serialVersionUID 1L;/** id/TableIdprivate Long id;/** sku_id/private Long skuId;/** sku_name/private String skuName;/** 购买个数/private Integer skuNum;/** 工作单id/private Long taskId;/** 仓库id/private Long wareId;/** 锁定状态/private Integer lockStatus;}查询订单锁库存工作单(获取哪个订单要锁库存)package com.saodai.saodaimall.ware.entity;import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data;import java.io.Serializable; import java.util.Date;/** 订单锁库存工作单(表示我准备要给哪个订单锁库存了)/ Data TableName(wms_ware_order_task) public class WareOrderTaskEntity implements Serializable {private static final long serialVersionUID 1L;/** id/TableIdprivate Long id;/** order_id/private Long orderId;/** order_sn/private String orderSn;/** 收货人/private String consignee;/** 收货人电话/private String consigneeTel;/** 配送地址/private String deliveryAddress;/** 订单备注/private String orderComment;/** 付款方式【 1:在线付款 2:货到付款】/private Integer paymentWay;/** 任务状态/private Integer taskStatus;/** 订单描述/private String orderBody;/** 物流单号/private String trackingNo;/** create_time/private Date createTime;/** 仓库id/private Long wareId;/** 工作单备注/private String taskComment;}根据订单号远程查询订单package com.saodai.saodaimall.ware.vo;import lombok.Data;import java.math.BigDecimal; import java.util.Date;Data public class OrderVo {private Long id;/** member_id/private Long memberId;/** 订单号/private String orderSn;/** 使用的优惠券/private Long couponId;/** create_time/private Date createTime;/** 用户名/private String memberUsername;/** 订单总额/private BigDecimal totalAmount;/** 应付总额/private BigDecimal payAmount;/** 运费金额/private BigDecimal freightAmount;/** 促销优化金额促销价、满减、阶梯价/private BigDecimal promotionAmount;/** 积分抵扣金额/private BigDecimal integrationAmount;/** 优惠券抵扣金额/private BigDecimal couponAmount;/** 后台调整订单使用的折扣金额/private BigDecimal discountAmount;/** 支付方式【1-支付宝2-微信3-银联 4-货到付款】/private Integer payType;/** 订单来源[0-PC订单1-app订单]/private Integer sourceType;/** 订单状态【0-待付款1-待发货2-已发货3-已完成4-已关闭5-无效订单】/private Integer status;/** 物流公司(配送方式)/private String deliveryCompany;/** 物流单号/private String deliverySn;/** 自动确认时间天/private Integer autoConfirmDay;/** 可以获得的积分/private Integer integration;/** 可以获得的成长值/private Integer growth;/** 发票类型[0-不开发票1-电子发票2-纸质发票]/private Integer billType;/** 发票抬头/private String billHeader;/** 发票内容/private String billContent;/** 收票人电话/private String billReceiverPhone;/** 收票人邮箱/private String billReceiverEmail;/** 收货人姓名/private String receiverName;/** 收货人电话/private String receiverPhone;/** 收货人邮编/private String receiverPostCode;/** 省份/直辖市/private String receiverProvince;/** 城市/private String receiverCity;/** 区/private String receiverRegion;/** 详细地址/private String receiverDetailAddress;/** 订单备注/private String note;/** 确认收货状态[0-未确认1-已确认]/private Integer confirmStatus;/** 删除状态【0-未删除1-已删除】/private Integer deleteStatus;/** 下单时使用的积分/private Integer useIntegration;/** 支付时间/private Date paymentTime;/** 发货时间/private Date deliveryTime;/** 确认收货时间/private Date receiveTime;/** 评价时间/private Date commentTime;/** 修改时间/private Date modifyTime;}进行双重判断先判断订单不存在因为有异常造成的数据回滚导致订单不存在或者订单状态是取消状态在判断当前库存工作单详情状态是不是11表示已锁定只有当前库存工作单详情状态未解锁才可以解锁调用unLockStock方法实现真正的解库存自动解库存更新库存的数量还原更新工作单的状态为已解锁 /** 真正解锁库存的方法自动解库存* param skuId 需要解锁库存的商品id* param wareId 需要解锁库存的库存仓库id* param num 需要解锁库存的商品数量* param taskDetailId 库存工作单详情id/public void unLockStock(Long skuId,Long wareId,Integer num,Long taskDetailId) {//库存解锁其实就是修改wms_ware_sku表中的stock_locked的值之前锁库存锁了多少个就减去多少个wareSkuDao.unLockStock(skuId,wareId,num);//更新工作单的状态WareOrderTaskDetailEntity taskDetailEntity new WareOrderTaskDetailEntity();taskDetailEntity.setId(taskDetailId);//setLockStatus(2)表示变为已解锁1表示已锁定2表示已解锁3表示减扣taskDetailEntity.setLockStatus(2);wareOrderTaskDetailService.updateById(taskDetailEntity);} !– 解锁库存– update idunLockStockUPDATE wms_ware_skuSET stock_locked stock_locked - #{num}WHEREsku_id ${skuId}AND ware_id #{wareId} /update手动解库存订单服务的订单取消后立马解库存的具体逻辑首先通过订单号查询订单锁库存工作单通过订单锁库存工作单的id去库存详细工作单去找对应的锁库存的记录看有没有记录并且锁库存的状态是已锁定的状态防止多次重复解库存其中库存详细工作单中的工作id的值就是订单锁库存工作单的id的值最后调用真正的解库存方法来解库存 /** 真正解锁库存的方法自动解库存* param skuId 需要解锁库存的商品id* param wareId 需要解锁库存的库存仓库id* param num 需要解锁库存的商品数量* param taskDetailId 库存工作单详情id/public void unLockStock(Long skuId,Long wareId,Integer num,Long taskDetailId) {//库存解锁其实就是修改wms_ware_sku表中的stock_locked的值之前锁库存锁了多少个就减去多少个wareSkuDao.unLockStock(skuId,wareId,num);//更新工作单的状态WareOrderTaskDetailEntity taskDetailEntity new WareOrderTaskDetailEntity();taskDetailEntity.setId(taskDetailId);//setLockStatus(2)表示变为已解锁1表示已锁定2表示已解锁3表示减扣taskDetailEntity.setLockStatus(2);wareOrderTaskDetailService.updateById(taskDetailEntity);}/** 订单取消了就立马解库存* 防止订单服务卡顿导致订单状态消息一直改不了库存优先到期查订单状态新建什么都不处理* 导致卡顿的订单永远都不能解锁库存* param orderTo*/Transactional(rollbackFor Exception.class)Overridepublic void unlockStock(OrderTo orderTo) {String orderSn orderTo.getOrderSn();//查一下最新的库存解锁状态防止重复解锁库存WareOrderTaskEntity orderTaskEntity wareOrderTaskService.getOrderTaskByOrderSn(orderSn);//按照工作单的id找到所有 没有解锁的库存进行解锁lock_status1表示已锁定库存Long id orderTaskEntity.getId();ListWareOrderTaskDetailEntity list wareOrderTaskDetailService.list(new QueryWrapperWareOrderTaskDetailEntity().eq(task_id, id).eq(lock_status, 1));for (WareOrderTaskDetailEntity taskDetailEntity : list) {//解锁库存unLockStock(taskDetailEntity.getSkuId(),taskDetailEntity.getWareId(),taskDetailEntity.getSkuNum(),taskDetailEntity.getId());}}
- 上一篇: 河北省住房和城乡建设部网站首页屏蔽阿里云网站
- 下一篇: 河北省做网站哪家公司好网站开发求职信
相关文章
-
河北省住房和城乡建设部网站首页屏蔽阿里云网站
河北省住房和城乡建设部网站首页屏蔽阿里云网站
- 技术栈
- 2026年03月21日
-
河北省住房城乡建设局网站网站建设与推广策划案案例
河北省住房城乡建设局网站网站建设与推广策划案案例
- 技术栈
- 2026年03月21日
-
河北省建设注册中心网站首页wordpress 主题 激活
河北省建设注册中心网站首页wordpress 主题 激活
- 技术栈
- 2026年03月21日
-
河北省做网站哪家公司好网站开发求职信
河北省做网站哪家公司好网站开发求职信
- 技术栈
- 2026年03月21日
-
河北手机版建站系统开发网站建设需要的条件
河北手机版建站系统开发网站建设需要的条件
- 技术栈
- 2026年03月21日
-
河北提供网站制作公司哪家专业沈阳微网站制作
河北提供网站制作公司哪家专业沈阳微网站制作
- 技术栈
- 2026年03月21日
