定时订单消息关单功能开发

This commit is contained in:
jieyuu 2024-09-23 21:53:49 +08:00
parent 64efdd1f5e
commit 4c80b3e53f
7 changed files with 156 additions and 26 deletions

View File

@ -0,0 +1,18 @@
package net.jieyuu.model;
import lombok.Data;
@Data
public class OrderMessage {
/**
* 消息id
*/
private Long messageId;
/**
* 订单号
*/
private String outTradeNo;
}

View File

@ -2,6 +2,7 @@ package net.jieyuu.mapper;
import net.jieyuu.model.ProductOrderDO; import net.jieyuu.model.ProductOrderDO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
/** /**
* <p> * <p>
@ -13,4 +14,5 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
*/ */
public interface ProductOrderMapper extends BaseMapper<ProductOrderDO> { public interface ProductOrderMapper extends BaseMapper<ProductOrderDO> {
void updateOrderPayState(@Param("outTradeNo") String outTradeNo, @Param("newState") String newState, @Param("oldState") String oldState);
} }

View File

@ -0,0 +1,50 @@
package net.jieyuu.mq;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import net.jieyuu.model.OrderMessage;
import net.jieyuu.service.ProductOrderService;
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.Component;
import java.io.IOException;
@Slf4j
@Component
@RabbitListener(queues = "${mqconfig.order_close_queue}")
public class ProductOrderMQListener {
@Autowired
private ProductOrderService productOrderService;
/**
* 消息重复消费幂等性保障
*
* @param orderMessage
* @param message
* @param channel
* @throws IOException
*/
@RabbitHandler
public void closeProductOrder(OrderMessage orderMessage, Message message, Channel channel) throws IOException {
log.info("监听到消息:closeProductOrder:{} ", orderMessage);
long msgTag = message.getMessageProperties().getDeliveryTag();
try {
boolean flag = productOrderService.closeProductOrder(orderMessage);
if (flag) {// 关单成功
channel.basicAck(msgTag, false);
} else {// 失败
channel.basicAck(msgTag, true);// 重新入队
}
} catch (IOException e) {
log.error("定时关单失败:{}", orderMessage);
channel.basicReject(msgTag, true);
}
}
}

View File

@ -1,5 +1,6 @@
package net.jieyuu.service; package net.jieyuu.service;
import net.jieyuu.model.OrderMessage;
import net.jieyuu.model.ProductOrderDO; import net.jieyuu.model.ProductOrderDO;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import net.jieyuu.request.ConfirmOrderRequest; import net.jieyuu.request.ConfirmOrderRequest;
@ -31,4 +32,11 @@ public interface ProductOrderService extends IService<ProductOrderDO> {
* @return * @return
*/ */
String queryProductOrderState(String outTradeNo); String queryProductOrderState(String outTradeNo);
/**
* 队列监听定时关单接口
*
* @param orderMessage
*/
boolean closeProductOrder(OrderMessage orderMessage);
} }

View File

@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference; import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.jieyuu.config.RabbitMQConfig;
import net.jieyuu.enums.*; import net.jieyuu.enums.*;
import net.jieyuu.exception.BizException; import net.jieyuu.exception.BizException;
import net.jieyuu.feign.CouponFeignService; import net.jieyuu.feign.CouponFeignService;
@ -11,11 +12,8 @@ import net.jieyuu.feign.ProductFeignService;
import net.jieyuu.feign.UserFeignService; import net.jieyuu.feign.UserFeignService;
import net.jieyuu.interceptor.LoginInterceptor; import net.jieyuu.interceptor.LoginInterceptor;
import net.jieyuu.mapper.ProductOrderItemMapper; import net.jieyuu.mapper.ProductOrderItemMapper;
import net.jieyuu.model.LoginUser; import net.jieyuu.model.*;
import net.jieyuu.model.OrderItemVO;
import net.jieyuu.model.ProductOrderDO;
import net.jieyuu.mapper.ProductOrderMapper; import net.jieyuu.mapper.ProductOrderMapper;
import net.jieyuu.model.ProductOrderItemDO;
import net.jieyuu.request.ConfirmOrderRequest; import net.jieyuu.request.ConfirmOrderRequest;
import net.jieyuu.request.LockCouponRecordRequest; import net.jieyuu.request.LockCouponRecordRequest;
import net.jieyuu.request.LockProductRequest; import net.jieyuu.request.LockProductRequest;
@ -26,6 +24,8 @@ import net.jieyuu.utils.CommonUtil;
import net.jieyuu.utils.JsonData; import net.jieyuu.utils.JsonData;
import net.jieyuu.vo.CouponRecordVO; import net.jieyuu.vo.CouponRecordVO;
import net.jieyuu.vo.ProductOrderAddressVO; import net.jieyuu.vo.ProductOrderAddressVO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -62,6 +62,12 @@ public class ProductOrderServiceImpl extends ServiceImpl<ProductOrderMapper, Pro
@Autowired @Autowired
private ProductOrderItemMapper productOrderItemMapper; private ProductOrderItemMapper productOrderItemMapper;
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private RabbitMQConfig rabbitMQConfig;
/** /**
* * 防重提交 * * 防重提交
* * 用户微服务-确认收货地址 * * 用户微服务-确认收货地址
@ -110,12 +116,17 @@ public class ProductOrderServiceImpl extends ServiceImpl<ProductOrderMapper, Pro
ProductOrderDO productOrderDO = this.saveProductOrder(orderRequest, loginUser, orderOutTradeNo, addressVO); ProductOrderDO productOrderDO = this.saveProductOrder(orderRequest, loginUser, orderOutTradeNo, addressVO);
//创建订单项 //创建订单项
this.saveproductOrderItems(orderOutTradeNo, productOrderDO.getId(), orderItemList); this.saveProductOrderItems(orderOutTradeNo, productOrderDO.getId(), orderItemList);
// 发送延迟消息用于自动关单 todo
// 发送延迟消息用于自动关单
OrderMessage orderMessage = new OrderMessage();
orderMessage.setOutTradeNo(orderOutTradeNo);
orderMessage.setMessageId(productOrderDO.getId());
rabbitTemplate.convertAndSend(rabbitMQConfig.getEventExchange(), rabbitMQConfig.getOrderCloseDelayRoutingKey(), orderMessage);
//创建支付 todo //创建支付 todo
return null; return null;
} }
@ -126,7 +137,7 @@ public class ProductOrderServiceImpl extends ServiceImpl<ProductOrderMapper, Pro
* @param orderId * @param orderId
* @param orderItemList * @param orderItemList
*/ */
private void saveproductOrderItems(String orderOutTradeNo, Long orderId, List<OrderItemVO> orderItemList) { private void saveProductOrderItems(String orderOutTradeNo, Long orderId, List<OrderItemVO> orderItemList) {
List<ProductOrderItemDO> orderItemDOList = orderItemList.stream().map(obj -> { List<ProductOrderItemDO> orderItemDOList = orderItemList.stream().map(obj -> {
ProductOrderItemDO itemDO = new ProductOrderItemDO(); ProductOrderItemDO itemDO = new ProductOrderItemDO();
@ -361,4 +372,35 @@ public class ProductOrderServiceImpl extends ServiceImpl<ProductOrderMapper, Pro
return productOrderDO.getState(); return productOrderDO.getState();
} }
} }
@Override
public boolean closeProductOrder(OrderMessage orderMessage) {
ProductOrderDO productOrderDO = productOrderMapper.selectOne(new QueryWrapper<ProductOrderDO>().eq("out_trade_no", orderMessage.getOutTradeNo()));
if (productOrderDO == null) {
log.warn("直接确认消息,订单不存在:{}", orderMessage);
return true;
}
if (productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.PAY.name())) {// 成功消费
log.info("直接确认消息,订单支付成功:{}", orderMessage);
return true;
}
// 向第三方支付查询支付情况 todo
String payResult = "";
// 订单结果为空支付不成功取消订单
if (StringUtils.isBlank(payResult)) {
productOrderMapper.updateOrderPayState(orderMessage.getOutTradeNo(), ProductOrderStateEnum.CANCEL.name(), ProductOrderStateEnum.NEW.name());
log.info("结果为空,则未支付成功,本地取消订单:{}", orderMessage);
return true;
} else {
// 将订单状态改为已经支付
log.warn("支付成功,主动修改订单状态为已支付,造成该元婴的情况可能是支付通道回调有问题:{}", orderMessage);
productOrderMapper.updateOrderPayState(orderMessage.getOutTradeNo(), ProductOrderStateEnum.PAY.name(), ProductOrderStateEnum.NEW.name());
return true;
}
}
} }

View File

@ -4,25 +4,35 @@
<!-- 通用查询映射结果 --> <!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="net.jieyuu.model.ProductOrderDO"> <resultMap id="BaseResultMap" type="net.jieyuu.model.ProductOrderDO">
<id column="id" property="id" /> <id column="id" property="id"/>
<result column="out_trade_no" property="outTradeNo" /> <result column="out_trade_no" property="outTradeNo"/>
<result column="state" property="state" /> <result column="state" property="state"/>
<result column="create_time" property="createTime" /> <result column="create_time" property="createTime"/>
<result column="total_amount" property="totalAmount" /> <result column="total_amount" property="totalAmount"/>
<result column="pay_amount" property="payAmount" /> <result column="pay_amount" property="payAmount"/>
<result column="pay_type" property="payType" /> <result column="pay_type" property="payType"/>
<result column="nickname" property="nickname" /> <result column="nickname" property="nickname"/>
<result column="head_img" property="headImg" /> <result column="head_img" property="headImg"/>
<result column="user_id" property="userId" /> <result column="user_id" property="userId"/>
<result column="del" property="del" /> <result column="del" property="del"/>
<result column="update_time" property="updateTime" /> <result column="update_time" property="updateTime"/>
<result column="order_type" property="orderType" /> <result column="order_type" property="orderType"/>
<result column="receiver_address" property="receiverAddress" /> <result column="receiver_address" property="receiverAddress"/>
</resultMap> </resultMap>
<!-- 通用查询结果列 --> <!-- 通用查询结果列 -->
<sql id="Base_Column_List"> <sql id="Base_Column_List">
id, out_trade_no, state, create_time, total_amount, pay_amount, pay_type, nickname, head_img, user_id, del, update_time, order_type, receiver_address id
, out_trade_no, state, create_time, total_amount, pay_amount, pay_type, nickname, head_img, user_id, del, update_time, order_type, receiver_address
</sql> </sql>
<!-- 更新订单状态 乐观锁使用 -->
<update id="updateOrderPayState">
update product_order
set `state` = #{newState}
where out_trade_no = #{outTradeNo}
and `state` = #{oldState}
</update>
</mapper> </mapper>

View File

@ -3,6 +3,7 @@ package biz;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.jieyuu.OrderApplication; import net.jieyuu.OrderApplication;
import net.jieyuu.model.CouponRecordMessage; import net.jieyuu.model.CouponRecordMessage;
import net.jieyuu.model.OrderMessage;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.core.RabbitTemplate;
@ -21,14 +22,13 @@ public class DemoApplicationTests {
@Test @Test
public void send() { public void send() {
rabbitTemplate.convertAndSend("order.event.exchange", "order.close.delay.routing.key", "this is a new order"); rabbitTemplate.convertAndSend("order.event.exchange", "order.close.delay.routing.key", "this is a new order");
} }
@Test @Test
public void testCouponRecordRelease() { public void testCouponRecordRelease() {
CouponRecordMessage message = new CouponRecordMessage(); OrderMessage message = new OrderMessage();
message.setOutTradeNo("123456abc"); message.setOutTradeNo("123456abc");
message.setTaskId(1l); message.setMessageId(1l);
rabbitTemplate.convertAndSend("order.event.exchange", "order.close.delay.routing.key", message); rabbitTemplate.convertAndSend("order.event.exchange", "order.close.delay.routing.key", message);
} }