当前位置: 首页 > news >正文

义乌公司做网站wordpress页面怎么添加

义乌公司做网站,wordpress页面怎么添加,现在做网站怎么赚钱,深圳做网站佰达科技二十七目录 1. 延迟关闭订单 1.1 订单延时关闭功能技术选型 1.1.1 定时任务 1.1.2 RabbitMQ 1.1.3 Redis 过期监听 1.1.4 Redisson 1.1.5 RocketMQ 1.2 RocketMQ订单延时关闭发送方实现 1.3 RocketMQ订单延时关闭的消费方实现 1. 延迟关闭订单 用户发起订单后#xff0c;如…目录 1. 延迟关闭订单 1.1 订单延时关闭功能技术选型 1.1.1 定时任务 1.1.2 RabbitMQ 1.1.3 Redis 过期监听 1.1.4 Redisson 1.1.5 RocketMQ 1.2 RocketMQ订单延时关闭发送方实现 1.3 RocketMQ订单延时关闭的消费方实现 1. 延迟关闭订单 用户发起订单后如果长时间未支付需要将订单关闭也就是大家常说的延时关闭订单。12306的PC端的延迟时间是10分钟: 1.1 订单延时关闭功能技术选型 1.1.1 定时任务 通过定时任务是一种常见的订单延迟关闭解决方案。 可以通过调度平台来实现定时任务的执行具体任务是根据订单创建时间扫描所有到期的订单并执行关闭订单的操作。 大家常用的定时任务调度平台有以下这些 https://github.com/xuxueli/xxl-job https://github.com/PowerJob/PowerJob https://github.com/apache/shardingsphere-elasticjob 这种方案的优点在于简单易实现但是该方案也存在一些问题 延迟时间不精确使用定时任务执行订单关闭逻辑无法保证订单在十分钟后准确地关闭。如果任务执行器在关闭订单的具体时间点出现问题可能导致订单关闭的时间延后。 不适合高并发场景定时任务执行的频率通常是固定的无法根据实际订单的情况来灵活调整。在高并发场景下可能导致大量的定时任务同时执行造成系统负载过大。 分库分表问题拿 12306 来说订单表按照用户标识和订单号进行了分库分表那这样的话和上面说的根据订单创建时间去扫描一批订单进行关闭自然就行不通。因为根据创建时间查询没有携带分片键存在读扩散问题。 通常最不推荐的方式是使用定时任务来实现订单关闭。 1.1.2 RabbitMQ RabbitMQ 是一个功能强大的消息中间件通过使用 RabbitMQ 的延时消息特性我们可以轻松实现订单十分钟延时关闭功能。首先我们需要在 RabbitMQ 服务器上启用延时特性通常通过安装 rabbitmq_delayed_message_exchange 插件来支持延时消息功能。 接下来我们创建两个队列订单队列和死信队列。订单队列用于存储需要延时关闭的订单消息而死信队列则用于存储延时时间到达后的订单消息。在创建订单队列时我们要为队列配置延时特性指定订单消息的延时时间比如十分钟。这样当有新的订单需要延时关闭时我们只需要将订单消息发送到订单队列并设置消息的延时时间。 在订单队列中设置死信交换机和死信队列当订单消息的延时时间到达后消息会自动转发到死信队列从而触发关闭订单的操作。在死信队列中我们可以监听消息并执行关闭订单的逻辑。为了确保消息的可靠性可以在关闭订单操作前添加适当的幂等性措施这样即使消息重复处理也不会对系统产生影响。 通过以上步骤我们就成功实现了订单的十分钟延时关闭功能。当有新的订单需要延时关闭时将订单消息发送到订单队列并设置延时时间。在延时时间到达后订单消息会自动进入死信队列从而触发关闭订单的操作。这种方式既简单又可靠保证了系统的稳定性和可用性。 从整体来说 RabbitMQ 实现延时关闭订单功能是比较合适的但也存在几个问题 延时精度RabbitMQ 的延时消息特性是基于消息的 TTLTime-To-Live来实现的因此消息的延时时间并不是完全准确的可能会有一定的误差。在处理订单十分钟延时关闭时可能会有一些订单的关闭时间略晚于预期时间。 高并发问题如果系统中有大量的订单需要延时关闭而订单关闭操作非常复杂耗时可能会导致消息队列中的消息堆积。这样就可能导致延时关闭操作无法及时处理影响订单的实际关闭时间。 重复消息问题由于网络原因或其他不可预知的因素可能会导致消息重复发送到订单队列。如果没有处理好消息的幂等性可能会导致订单重复关闭的问题从而造成数据不一致或其他异常情况。 可靠性问题RabbitMQ 是一个消息中间件它是一个独立的系统。如果 RabbitMQ 本身出现故障或宕机可能会导致订单延时关闭功能失效。因此在使用 RabbitMQ 实现延时关闭功能时需要考虑如何保证 RabbitMQ 的高可用性和稳定性。 延时精度和高并发属于一类问题取决于客户端的消费能力。重复消费问题是所有消息中间件都需要解决需要通过消息表等幂等解决方案解决。比较难搞定的是可用性问题RabbitMQ 在可用性方面较弱部分场景下会存在单点故障问题。 1.1.3 Redis 过期监听 可以借助 Redis 的过期消息监听机制实现延时关闭功能。 首先在订单创建时将订单信息存储到 Redis并设置过期时间为十分钟。同时在 Redis 中存储一个过期消息监听的键值对键为订单号值为待处理订单的标识。 其次编写一个消息监听器持续监听 Redis 的过期事件。监听器使用 Redis 的 PSUBSCRIBE 命令订阅过期事件并在监听到过期事件时触发相应的处理逻辑。 当订单过期时间到达时Redis 会自动触发过期事件消息监听器捕获到该事件并获取到过期的订单号。接着监听器执行订单关闭的逻辑如更新订单状态为关闭状态释放相关资源等实现订单的十分钟延时关闭功能。 需要注意的是消息监听器应该是一个长期运行的任务确保持续监听 Redis 的过期事件。为了保证系统的稳定性和可靠性可以在实现订单关闭逻辑时添加容错机制以应对 Redis 可能发生故障或重启的情况。 Redis 过期消息也存在几个问题 不够精确Redis 的过期时间是通过定时器实现的可能存在一定的误差导致订单的关闭时间不是精确的十分钟。这对于某些对时间要求较高的场景可能不适用。 Redis 宕机如果 Redis 宕机或重启那些已经设置了过期时间但还未过期的订单信息将会丢失导致这部分订单无法正确关闭。需要考虑如何处理这种异常情况。 可靠性依赖 Redis 的过期时间来实现订单关闭功能需要确保 Redis 的高可用性和稳定性。如果 Redis 发生故障或网络问题可能导致订单关闭功能失效。 版本问题Redis 5.0 之前是不保证延迟消息持久化的如果客户端消费过程中宕机或者重启这个消息不会重复投递。5.0 之后推出了 Stream 功能有了持久化等比较完善的延迟消息功能。 1.1.4 Redisson 通过 Redisson 的 RDelayedQueue 功能可以实现订单十分钟延时关闭的功能。 首先我们需要创建一个 RDelayedQueue 对象用于存放需要延时关闭的订单信息。当用户创建订单时我们将订单信息添加到 RDelayedQueue 中并设置订单的延时时间为十分钟。 Redisson 提供了监听功能可以实现对 RDelayedQueue 中订单信息的监听。一旦订单到达设定的延时时间Redisson 会触发监听事件。在监听到订单的延时事件后我们可以编写相应的处理逻辑即关闭对应的订单。 在处理订单关闭时我们可以根据订单号或订单创建时间等信息来找到对应的订单进行关闭操作。 不过这种方式也不推荐使用基本上 Redis 过期监听消息存在的问题RDelayedQueue 也都会有因为 RDelayedQueue 本质上也是依赖 Redis 实现。 1.1.5 RocketMQ 在订单生成时我们将订单关闭消息发送到 RocketMQ并设置消息的延迟时间为十分钟。RocketMQ 支持设置消息的延迟时间可以通过设置消息的 delayLevel 来指定延迟级别每个级别对应一种延迟时间。这样订单关闭消息将在十分钟后自动被消费者接收到。 需要注意RocketMQ 5.0 之后已经支持了自定义时间的延迟而不仅是延迟级别范围内的时间。 为了处理订单关闭消息我们需要在消费者端创建一个消息监听器。当消息监听器接收到订单关闭消息时触发订单关闭操作将订单状态设置为关闭状态。 需要注意的是RocketMQ 的消息传递机制保证了消息的可靠性传递因此消息可能会进行多次重试。为了确保订单关闭操作的幂等性即多次执行不会产生副作用我们需要在订单关闭逻辑中进行幂等性的处理。 铁路 12306 系统中使用 RocketMQ 作为延时关闭订单的技术实现从整体考虑处理能力以及稳定性相对来说较为合适。 1.2 RocketMQ订单延时关闭发送方实现 在12306中,当订单添加完成之后, 在OrderService方,会延迟10分钟,发送一个关闭订单的消息给到MQ, 而关闭订单的消费方订阅这个消息,进行未及时支付的订单进行取消操作. 1) 我们创建一个延迟关闭订单的事件实体类 DelayCloseOrderEvent ​ package com.fs.fs12306.biz.orderservice.mq.event; ​ import com.fs.fs12306.biz.orderservice.dto.req.TicketOrderItemCreateReqDTO; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; ​ import java.util.List; ​ /*** 延迟关闭订单事件*/ Data Builder NoArgsConstructor AllArgsConstructor public class DelayCloseOrderEvent { ​/*** 车次 ID*/private String trainId; ​/*** 出发站点*/private String departure; ​/*** 到达站点*/private String arrival; ​/*** 订单号*/private String orderSn; ​/*** 乘车人购票信息*/private ListTicketOrderItemCreateReqDTO trainPurchaseTicketResults; } ​ 2) 消息发送事件基础扩充属性实体 BaseSendExtendDTO ​ package com.fs.fs12306.biz.orderservice.mq.produce; ​ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; ​ /*** 消息发送事件基础扩充属性实体**/ Data NoArgsConstructor AllArgsConstructor Builder public final class BaseSendExtendDTO { ​/*** 事件名称*/private String eventName; ​/*** 主题*/private String topic; ​/*** 标签*/private String tag; ​/*** 业务标识*/private String keys; ​/*** 发送消息超时时间*/private Long sentTimeout; ​/*** 延迟消息*/private Integer delayLevel; } ​ 3) 把发送消息封装成一个模版类AbstractCommonSendProduceTemplate,目的是为了提供一个抽象的公共类方便在项目中发送RocketMQ消息。 ​ package com.fs.fs12306.biz.orderservice.mq.produce; ​ import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson2.JSON; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.messaging.Message; ​ import java.util.Optional; ​ /*** RocketMQ 抽象公共发送消息组件**/ Slf4j RequiredArgsConstructor public abstract class AbstractCommonSendProduceTemplateT { ​private final RocketMQTemplate rocketMQTemplate; ​/*** 构建消息发送事件基础扩充属性实体* param messageSendEvent 消息发送事件* return 扩充属性实体*/protected abstract BaseSendExtendDTO buildBaseSendExtendParam(T messageSendEvent); ​/*** 构建消息基本参数请求头、Keys...** param messageSendEvent 消息发送事件* param requestParam     扩充属性实体* return 消息基本参数*/protected abstract Message? buildMessage(T messageSendEvent, BaseSendExtendDTO requestParam); ​/*** 消息事件通用发送** param messageSendEvent 消息发送事件* return 消息发送返回结果*/public SendResult sendMessage(T messageSendEvent) {//构建基础扩充属性实体BaseSendExtendDTO baseSendExtendDTO buildBaseSendExtendParam(messageSendEvent);SendResult sendResult;try {//构建消息的topic和tag,格式: topic:tagStringBuilder destinationBuilder StrUtil.builder().append(baseSendExtendDTO.getTopic());if (StrUtil.isNotBlank(baseSendExtendDTO.getTag())) {destinationBuilder.append(:).append(baseSendExtendDTO.getTag());}//同步发送消息sendResult rocketMQTemplate.syncSend(destinationBuilder.toString(),buildMessage(messageSendEvent, baseSendExtendDTO),baseSendExtendDTO.getSentTimeout(),Optional.ofNullable(baseSendExtendDTO.getDelayLevel()).orElse(0));log.info([{}] 消息发送结果{}消息ID{}消息Keys{}, baseSendExtendDTO.getEventName(), sendResult.getSendStatus(), sendResult.getMsgId(), baseSendExtendDTO.getKeys());} catch (Throwable ex) {log.error([{}] 消息发送失败消息体{}, baseSendExtendDTO.getEventName(), JSON.toJSONString(messageSendEvent), ex);throw ex;}return sendResult;} } ​ AbstractCommonSendProduceTemplate定义了一个抽象的RocketMQ消息发送模板具体的实现需要子类根据具体需求进行。通过这种方式可以在不同的场景下复用消息发送的逻辑提高代码的可维护性和可扩展性。 4) 编写延迟关闭订单生产者 DelayCloseOrderSendProduce 让延迟关闭订单生产者继承AbstractCommonSendProduceTemplate类,复用消息发送的逻辑 package com.fs.fs12306.biz.orderservice.mq.produce; ​ import cn.hutool.core.util.StrUtil; import com.fs.fs12306.biz.orderservice.common.constant.OrderRocketMQConstant; import com.fs.fs12306.biz.orderservice.mq.domain.MessageWrapper; import com.fs.fs12306.biz.orderservice.mq.event.DelayCloseOrderEvent; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.common.message.MessageConst; import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.stereotype.Component; ​ import java.util.UUID; ​ /*** 延迟关闭订单生产者**/ Slf4j Component public class DelayCloseOrderSendProduce extends AbstractCommonSendProduceTemplateDelayCloseOrderEvent { ​private final ConfigurableEnvironment environment; ​public DelayCloseOrderSendProduce(Autowired RocketMQTemplate rocketMQTemplate, Autowired ConfigurableEnvironment environment) {super(rocketMQTemplate);this.environment environment;} ​Overrideprotected BaseSendExtendDTO buildBaseSendExtendParam(DelayCloseOrderEvent messageSendEvent) {return BaseSendExtendDTO.builder().eventName(延迟关闭订单).keys(messageSendEvent.getOrderSn()).topic(environment.resolvePlaceholders(OrderRocketMQConstant.ORDER_DELAY_CLOSE_TOPIC_KEY)).tag(environment.resolvePlaceholders(OrderRocketMQConstant.ORDER_DELAY_CLOSE_TAG_KEY)).sentTimeout(2000L)// RocketMQ 延迟消息级别 1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h.delayLevel(14).build();} ​Overrideprotected Message? buildMessage(DelayCloseOrderEvent messageSendEvent, BaseSendExtendDTO requestParam) {String keys StrUtil.isEmpty(requestParam.getKeys()) ? UUID.randomUUID().toString() : requestParam.getKeys();return MessageBuilder.withPayload(new MessageWrapper(requestParam.getKeys(), messageSendEvent)).setHeader(MessageConst.PROPERTY_KEYS, keys).setHeader(MessageConst.PROPERTY_TAGS, requestParam.getTag()).build();} } 对于消息的负载信息,我们进行再次封装: MessageWrapper ​ package com.fs.fs12306.biz.orderservice.mq.domain; ​ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import lombok.NonNull; import lombok.RequiredArgsConstructor; ​ import java.io.Serializable; import java.util.UUID; ​ /*** 消息体包装器**/ Data Builder NoArgsConstructor(force true) AllArgsConstructor RequiredArgsConstructor public final class MessageWrapperT implements Serializable { ​private static final long serialVersionUID 1L; ​/*** 消息发送 Keys*/NonNullprivate String keys; ​/*** 消息体*/NonNullprivate T message; ​/*** 唯一标识用于客户端幂等验证*/private String uuid UUID.randomUUID().toString(); ​/*** 消息发送时间*/private Long timestamp System.currentTimeMillis(); } ​ 在往数据库插入订单相关的数据之后,我们就使用延时关闭订单的生产者延迟发送一个消息,从而实现延时关闭订单的功能 try {// 发送 RocketMQ 延时消息指定时间后取消订单DelayCloseOrderEvent delayCloseOrderEvent DelayCloseOrderEvent.builder().trainId(String.valueOf(requestParam.getTrainId())).departure(requestParam.getDeparture()).arrival(requestParam.getArrival()).orderSn(orderSn).trainPurchaseTicketResults(requestParam.getTicketOrderItems()).build();SendResult sendResult delayCloseOrderSendProduce.sendMessage(delayCloseOrderEvent);if (!Objects.equals(sendResult.getSendStatus(), SendStatus.SEND_OK)) {throw new ServiceException(投递延迟关闭订单消息队列失败);}} catch (Throwable ex) {log.error(延迟关闭订单消息队列发送错误请求参数{}, JSON.toJSONString(requestParam), ex);throw ex;} 1.3 RocketMQ订单延时关闭的消费方实现 12306中的关闭订单的消费方是在TicketService实现的.可能会有一个疑问为什么延迟关闭订单发送端在订单服务消费延迟关闭订单消息却是在购票服务 如果消息的消费在订单服务中进行那么订单服务就需要调用购票服务来进行座位的释放或订单状态的更新。这样就形成了一个远程循环依赖订单服务依赖于购票服务同时购票服务又依赖于订单服务。这对于微服务涉及中是需要尽可能避免的。所以最终将消费延迟关闭消息放在了购票服务。 在ticketService服务中,也定义一个延时关闭订单的事件实体类, 与发送方的延时关闭订单事件的实体类是一样, ​ package com.fs.fs12306.biz.ticketservice.mq.event; ​ import com.fs.fs12306.biz.ticketservice.service.handler.ticket.dto.TrainPurchaseTicketRespDTO; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; ​ import java.util.List; ​ /*** 延迟关闭订单事件**/ Data Builder NoArgsConstructor AllArgsConstructor public class DelayCloseOrderEvent { ​/*** 车次 ID*/private String trainId; ​/*** 出发站点*/private String departure; ​/*** 到达站点*/private String arrival; ​/*** 订单号*/private String orderSn; ​/*** 乘车人购票信息*/private ListTrainPurchaseTicketRespDTO trainPurchaseTicketResults; } ​ 封装消息体包装器MessageWrapper ​ package com.fs.fs12306.biz.ticketservice.mq.domain; ​ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import lombok.NonNull; import lombok.RequiredArgsConstructor; ​ import java.io.Serializable; import java.util.UUID; ​ /*** 消息体包装器**/ Data Builder NoArgsConstructor(force true) AllArgsConstructor RequiredArgsConstructor public final class MessageWrapperT implements Serializable { ​private static final long serialVersionUID 1L; ​/*** 消息发送 Keys*/NonNullprivate String keys; ​/*** 消息体*/NonNullprivate T message; ​/*** 唯一标识用于客户端幂等验证*/private String uuid UUID.randomUUID().toString(); ​/*** 消息发送时间*/private Long timestamp System.currentTimeMillis(); } ​ 这里有一个可以优化的地方,我们发现DelayCloseOrderEvent,MessageWrapper在OrderService和ticketService重复编写了, 可以把DelayCloseOrderEvent类抽取出来,放到一个公共模块中. 编写延时关闭订单的消费者DelayCloseOrderConsumer ​ package com.fs.fs12306.biz.ticketservice.mq.consumer; ​ import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSON; import com.fs.fs12306.biz.ticketservice.common.constant.TicketRocketMQConstant; import com.fs.fs12306.biz.ticketservice.dto.domain.RouteDTO; import com.fs.fs12306.biz.ticketservice.dto.req.CancelTicketOrderReqDTO; import com.fs.fs12306.biz.ticketservice.mq.domain.MessageWrapper; import com.fs.fs12306.biz.ticketservice.mq.event.DelayCloseOrderEvent; import com.fs.fs12306.biz.ticketservice.remote.TicketOrderRemoteService; import com.fs.fs12306.biz.ticketservice.remote.dto.TicketOrderDetailRespDTO; import com.fs.fs12306.biz.ticketservice.remote.dto.TicketOrderPassengerDetailRespDTO; import com.fs.fs12306.biz.ticketservice.service.SeatService; import com.fs.fs12306.biz.ticketservice.service.TrainStationService; import com.fs.fs12306.biz.ticketservice.service.handler.ticket.dto.TrainPurchaseTicketRespDTO; import com.fs.fs12306.biz.ticketservice.service.handler.ticket.tokenbucket.TicketAvailabilityTokenBucket; import com.fs.fs12306.frameworks.starter.cache.DistributedCache; import com.fs.fs12306.frameworks.starter.common.util.BeanUtil; import com.fs.fs12306.frameworks.starter.convention.result.Result; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; import org.apache.rocketmq.spring.core.RocketMQListener; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; ​ import java.util.List; import java.util.Map; import java.util.stream.Collectors; ​ import static com.fs.fs12306.biz.ticketservice.common.constant.RedisKeyConstant.TRAIN_STATION_REMAINING_TICKET; ​ /*** 延迟关闭订单消费者**/ Slf4j Component RequiredArgsConstructor RocketMQMessageListener(topic TicketRocketMQConstant.ORDER_DELAY_CLOSE_TOPIC_KEY,selectorExpression TicketRocketMQConstant.ORDER_DELAY_CLOSE_TAG_KEY,consumerGroup TicketRocketMQConstant.TICKET_DELAY_CLOSE_CG_KEY ) public final class DelayCloseOrderConsumer implements RocketMQListenerMessageWrapperDelayCloseOrderEvent { ​private final SeatService seatService;private final TicketOrderRemoteService ticketOrderRemoteService;private final TrainStationService trainStationService;private final DistributedCache distributedCache;private final TicketAvailabilityTokenBucket ticketAvailabilityTokenBucket; ​Value(${ticket.availability.cache-update.type:})private String ticketAvailabilityCacheUpdateType; ​Overridepublic void onMessage(MessageWrapperDelayCloseOrderEvent delayCloseOrderEventMessageWrapper) {log.info([延迟关闭订单] 开始消费{}, JSON.toJSONString(delayCloseOrderEventMessageWrapper));DelayCloseOrderEvent delayCloseOrderEvent delayCloseOrderEventMessageWrapper.getMessage();String orderSn delayCloseOrderEvent.getOrderSn();ResultBoolean closedTickOrder;try {//远程调用OrderService的关闭订单的接口closedTickOrder ticketOrderRemoteService.closeTickOrder(new CancelTicketOrderReqDTO(orderSn));} catch (Throwable ex) {log.error([延迟关闭订单] 订单号{} 远程调用订单服务失败, orderSn, ex);throw ex;}if (closedTickOrder.isSuccess() !StrUtil.equals(ticketAvailabilityCacheUpdateType, binlog)) {if (!closedTickOrder.getData()) {log.info([延迟关闭订单] 订单号{} 用户已支付订单, orderSn);return;}String trainId delayCloseOrderEvent.getTrainId();String departure delayCloseOrderEvent.getDeparture();String arrival delayCloseOrderEvent.getArrival();ListTrainPurchaseTicketRespDTO trainPurchaseTicketResults delayCloseOrderEvent.getTrainPurchaseTicketResults();try {//释放锁定的座位seatService.unlock(trainId, departure, arrival, trainPurchaseTicketResults);} catch (Throwable ex) {log.error([延迟关闭订单] 订单号{} 回滚列车DB座位状态失败, orderSn, ex);throw ex;}try {StringRedisTemplate stringRedisTemplate (StringRedisTemplate) distributedCache.getInstance();MapInteger, ListTrainPurchaseTicketRespDTO seatTypeMap trainPurchaseTicketResults.stream().collect(Collectors.groupingBy(TrainPurchaseTicketRespDTO::getSeatType));ListRouteDTO routeDTOList trainStationService.listTakeoutTrainStationRoute(trainId, departure, arrival);//缓存中的余票加routeDTOList.forEach(each - {String keySuffix StrUtil.join(_, trainId, each.getStartStation(), each.getEndStation());seatTypeMap.forEach((seatType, trainPurchaseTicketRespDTOList) - {stringRedisTemplate.opsForHash().increment(TRAIN_STATION_REMAINING_TICKET keySuffix, String.valueOf(seatType), trainPurchaseTicketRespDTOList.size());});});TicketOrderDetailRespDTO ticketOrderDetail BeanUtil.convert(delayCloseOrderEvent, TicketOrderDetailRespDTO.class);ticketOrderDetail.setPassengerDetails(BeanUtil.convert(delayCloseOrderEvent.getTrainPurchaseTicketResults(), TicketOrderPassengerDetailRespDTO.class));ticketAvailabilityTokenBucket.rollbackInBucket(ticketOrderDetail);} catch (Throwable ex) {log.error([延迟关闭订单] 订单号{} 回滚列车Cache余票失败, orderSn, ex);throw ex;}}} } ​ OrderService的feign client接口 ​ package com.fs.fs12306.biz.ticketservice.remote; ​ import com.fs.fs12306.biz.ticketservice.dto.req.CancelTicketOrderReqDTO; import com.fs.fs12306.biz.ticketservice.dto.req.TicketOrderItemQueryReqDTO; import com.fs.fs12306.biz.ticketservice.remote.dto.TicketOrderCreateRemoteReqDTO; import com.fs.fs12306.biz.ticketservice.remote.dto.TicketOrderDetailRespDTO; import com.fs.fs12306.biz.ticketservice.remote.dto.TicketOrderPassengerDetailRespDTO; import com.fs.fs12306.frameworks.starter.convention.result.Result; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.SpringQueryMap; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import java.util.List; ​ /*** 车票订单远程服务调用**/ FeignClient(value fs12306-order-service, url ${aggregation.remote-url:}) public interface TicketOrderRemoteService {/*** 创建车票订单** param requestParam 创建车票订单请求参数* return 订单号*/PostMapping(/api/order-service/order/ticket/create)ResultString createTicketOrder(RequestBody TicketOrderCreateRemoteReqDTO requestParam); ​/*** 车票订单关闭** param requestParam 车票订单关闭入参* return 关闭订单返回结果*/PostMapping(/api/order-service/order/ticket/close)ResultBoolean closeTickOrder(RequestBody CancelTicketOrderReqDTO requestParam); ​/*** 车票订单取消** param requestParam 车票订单取消入参* return 订单取消返回结果*/PostMapping(/api/order-service/order/ticket/cancel)ResultVoid cancelTicketOrder(RequestBody CancelTicketOrderReqDTO requestParam); } ​ 接下来需要在OrderService服务中,实现对订单关闭的业务实现 ​ package com.fs.fs12306.biz.orderservice.controller; ​ import cn.crane4j.annotation.AutoOperate; import com.fs.fs12306.biz.orderservice.dto.req.*; import com.fs.fs12306.biz.orderservice.dto.resp.TicketOrderDetailRespDTO; import com.fs.fs12306.biz.orderservice.dto.resp.TicketOrderDetailSelfRespDTO; import com.fs.fs12306.biz.orderservice.dto.resp.TicketOrderPassengerDetailRespDTO; import com.fs.fs12306.biz.orderservice.service.OrderItemService; import com.fs.fs12306.biz.orderservice.service.OrderService; import com.fs.fs12306.frameworks.starter.convention.page.PageResponse; import com.fs.fs12306.frameworks.starter.convention.result.Result; import com.fs.fs12306.frameworks.starter.web.Results; import lombok.RequiredArgsConstructor; ​ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; ​ import java.util.List; ​ /*** 车票订单接口控制层**/ RestController RequiredArgsConstructor public class TicketOrderController { ​private final OrderService orderService;private final OrderItemService orderItemService; ​/*** 根据订单号查询车票订单*/GetMapping(/api/order-service/order/ticket/query)public ResultTicketOrderDetailRespDTO queryTicketOrderByOrderSn(RequestParam(value orderSn) String orderSn) {return Results.success(orderService.queryTicketOrderByOrderSn(orderSn));} ​/*** 根据子订单记录id查询车票子订单详情*/GetMapping(/api/order-service/order/item/ticket/query)public ResultListTicketOrderPassengerDetailRespDTO queryTicketItemOrderById(TicketOrderItemQueryReqDTO requestParam) {return Results.success(orderItemService.queryTicketItemOrderById(requestParam));} ​/*** 分页查询车票订单*/AutoOperate(type TicketOrderDetailRespDTO.class, on data.records)GetMapping(/api/order-service/order/ticket/page)public ResultPageResponseTicketOrderDetailRespDTO pageTicketOrder(TicketOrderPageQueryReqDTO requestParam) {return Results.success(orderService.pageTicketOrder(requestParam));} ​/*** 分页查询本人车票订单*/GetMapping(/api/order-service/order/ticket/self/page)public ResultPageResponseTicketOrderDetailSelfRespDTO pageSelfTicketOrder(TicketOrderSelfPageQueryReqDTO requestParam) {return Results.success(orderService.pageSelfTicketOrder(requestParam));} ​/*** 车票订单创建*/PostMapping(/api/order-service/order/ticket/create)public ResultString createTicketOrder(RequestBody TicketOrderCreateReqDTO requestParam) {return Results.success(orderService.createTicketOrder(requestParam));} ​/*** 车票订单关闭*/PostMapping(/api/order-service/order/ticket/close)public ResultBoolean closeTickOrder(RequestBody CancelTicketOrderReqDTO requestParam) {return Results.success(orderService.closeTickOrder(requestParam));} ​/*** 车票订单取消*/PostMapping(/api/order-service/order/ticket/cancel)public ResultBoolean cancelTickOrder(RequestBody CancelTicketOrderReqDTO requestParam) {return Results.success(orderService.cancelTickOrder(requestParam));} } ​ 关闭订单的业务实现: OrderService接口定义关闭订单的方法:closeTickOrder /*** 关闭火车票订单** param requestParam 关闭火车票订单入参*/ boolean closeTickOrder(CancelTicketOrderReqDTO requestParam); OrderServiceImpl类的具体closeTickOrder方法的实现如下:Overridepublic boolean closeTickOrder(CancelTicketOrderReqDTO requestParam) {String orderSn requestParam.getOrderSn();LambdaQueryWrapperOrderDO queryWrapper Wrappers.lambdaQuery(OrderDO.class).eq(OrderDO::getOrderSn, orderSn).select(OrderDO::getStatus);OrderDO orderDO orderMapper.selectOne(queryWrapper);if (Objects.isNull(orderDO) || orderDO.getStatus() ! OrderStatusEnum.PENDING_PAYMENT.getStatus()) {return false;}// 原则上订单关闭和订单取消这两个方法可以复用为了区分未来考虑到的场景这里对方法进行拆分但复用逻辑return cancelTickOrder(requestParam);} ​ Overridepublic boolean cancelTickOrder(CancelTicketOrderReqDTO requestParam) {String orderSn requestParam.getOrderSn();LambdaQueryWrapperOrderDO queryWrapper Wrappers.lambdaQuery(OrderDO.class).eq(OrderDO::getOrderSn, orderSn);OrderDO orderDO orderMapper.selectOne(queryWrapper);if (orderDO null) {throw new ServiceException(OrderCanalErrorCodeEnum.ORDER_CANAL_UNKNOWN_ERROR);} else if (orderDO.getStatus() ! OrderStatusEnum.PENDING_PAYMENT.getStatus()) {throw new ServiceException(OrderCanalErrorCodeEnum.ORDER_CANAL_STATUS_ERROR);}RLock lock redissonClient.getLock(StrBuilder.create(order:canal:order_sn_).append(orderSn).toString());if (!lock.tryLock()) {throw new ClientException(OrderCanalErrorCodeEnum.ORDER_CANAL_REPETITION_ERROR);}try {OrderDO updateOrderDO new OrderDO();updateOrderDO.setStatus(OrderStatusEnum.CLOSED.getStatus());updateOrderDO.setOrderSn(orderSn);LambdaUpdateWrapperOrderDO updateWrapper Wrappers.lambdaUpdate(OrderDO.class).eq(OrderDO::getOrderSn, orderSn);int updateResult orderMapper.update(updateOrderDO, updateWrapper);if (updateResult 0) {throw new ServiceException(OrderCanalErrorCodeEnum.ORDER_CANAL_ERROR);}OrderItemDO updateOrderItemDO new OrderItemDO();updateOrderItemDO.setStatus(OrderItemStatusEnum.CLOSED.getStatus());updateOrderItemDO.setOrderSn(orderSn);LambdaUpdateWrapperOrderItemDO updateItemWrapper Wrappers.lambdaUpdate(OrderItemDO.class).eq(OrderItemDO::getOrderSn, orderSn);int updateItemResult orderItemMapper.update(updateOrderItemDO, updateItemWrapper);if (updateItemResult 0) {throw new ServiceException(OrderCanalErrorCodeEnum.ORDER_CANAL_ERROR);}} finally {lock.unlock();}return true;}
http://www.hkea.cn/news/14280586/

相关文章:

  • 全国互联网安全管理服务平台网站优化 情况
  • 建一个网站问谁免费 网站
  • 2008 iis asp配置网站网站制作需要学什么语言
  • 哈尔滨做网站的公司怎么做合买彩票网站
  • 建设网站架构服务器需要多少钱免费申请网站com域名
  • 网络设计的安全性原则主要是指杭州seo排名
  • 临邑建设局网站怎么查看网站快照
  • 招聘门户网站有哪些物流企业网站建设步骤
  • 网站前端开发培训网络建设公司前景
  • 设计师如何注册个人网站代工平台
  • 网站备案的程序安徽科技网站建设
  • 北京一个公司做网站认证保定企业网站开发
  • 北京大兴做环保备案网站非常好的资讯网站设计
  • 如何小企业网站建设工程建设安全管理
  • 海洋网络做网站不负责seo推广策略
  • g4560做网站服务器阿里云商标注册入口
  • 给网站设置关键词做语文高考题网站
  • 桂城网站制作上海装修公司排名榜单出炉
  • 自己的网站源代码一片空白门户网站开发难点
  • 网站搭建代理网络营销策划方案简介
  • 如何免费网站建设看片应该搜什么关键词哪些词
  • 怎么用dw英文版做网站做促销的网站
  • 大连网站开发公司力推选仟亿科技网络服务器租用价格
  • 宁波网站推广渠道网站建设费长期待摊费用
  • 台州网站建设平台wordpress需要ftp
  • 教育网站赏析网络推广理实一体化软件
  • wordpress 仿站vip百度网址ip
  • 手机膜 东莞网站建设用自己点电脑做电影网站
  • 建设学校网站前的需求分析广告制作宣传
  • 装修行业在什么网站上做推广好阿里云是做网站的吗