一、支付成功处理
1.1 更改订单状态
订单支付成功后,我们已经更改了订单支付记录状态,接下来我还有更改订单状态,因为他们是不同的微服务模块,所以我们采用消息队列的方式,保证数据最终一致性;
1.1.1 在MqConst常量类添加变量
/* 订单支付 */ public static final String EXCHANGE_DIRECT_PAYMENT_PAY = “exchange.direct.payment.pay”; public static final String ROUTING_PAYMENT_PAY = “payment.pay”; //队列 public static final String QUEUE_PAYMENT_PAY = “queue.payment.pay”; /* 减库存 */public static final String EXCHANGEDIRECT_WARE_STOCK = “exchange.direct.ware.stock”;public static final String ROUTING_WARE_STOCK = “ware.stock”;//队列public static final String QUEUE_WARE_STOCK = “queue.ware.stock”; / 减库存成功,更新订单状态 /_public static final String EXCHANGE_DIRECT_WARE_ORDER = “exchange.direct.ware.order”;public static final String ROUTING_WARE_ORDER = “ware.order”;//队列public static final String QUEUE_WARE_ORDER = “queue.ware.order”**; |
---|
1.1.2 在service-payment 中添加依赖和配置
<dependency> <groupId>com.atguigu.gmall</groupId> <artifactId>rabbit-util</artifactId> <version>1.0</version></dependency> |
---|
1.1.3 支付成功发送消息
1.1.4 service-order模块接收消息
1.2 订单模块发送减库存通知
订单模块除了接收到请求改变单据状态,还要发送库存系统
查看看《库存管理系统接口手册》中【减库存的消息队列消费端接口】中的描述,组织相应的消息数据进行传递。
1.2.1 OrderService接口
/ 发送消息给库存! @param **orderId __ */ void sendOrderStatus(Long orderId);/ 将orderInfo变为map集合 @param **orderInfo */ Map initWareOrder(OrderInfo orderInfo); |
---|
1.2.2 编写实现类
@Override public void sendOrderStatus(Long orderId) { this.updateOrderStatus(orderId, ProcessStatus.NOTIFIED_WARE); String wareJson = initWareOrder(orderId); rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_WARE_STOCK, MqConst.ROUTING_WARE_STOCK, wareJson); }// 根据orderId 获取json 字符串 private String initWareOrder(Long orderId) { // 通过orderId 获取orderInfo _OrderInfo orderInfo = getOrderInfo(orderId); // 将orderInfo中部分数据转换为Map Map map = initWareOrder(orderInfo); return JSON._toJSONString(map); } // 将orderInfo中部分数据转换为Map public Map initWareOrder(OrderInfo orderInfo) { HashMap map.put(“orderId”, orderInfo.getId()); map.put(“consignee”, orderInfo.getConsignee()); map.put(“consigneeTel”, orderInfo.getConsigneeTel()); map.put(“orderComment”, orderInfo.getOrderComment()); map.put(“orderBody”, orderInfo.getTradeBody()); map.put(“deliveryAddress”, orderInfo.getDeliveryAddress()); map.put(“paymentWay”, “2”); map.put(“wareId”, orderInfo.getWareId());_// 仓库Id ,减库存拆单时需要使用! / details:[{skuId:101,skuNum:1,skuName: ’小米手64G’}, {skuId:201,skuNum:1,skuName:’索尼耳机’}] / _ArrayList |
---|
1.3 消费减库存结果
给仓库系统发送减库存消息后,还要接受减库存成功或者失败的消息。
同样根据《库存管理系统接口手册》中【商品减库结果消息】的说明完成。消费该消息的消息队列监听程序。
接受到消息后主要做的工作就是更新订单状态。
在订单项目中OrderReceiver
/ 扣减库存成功,更新订单状态 @param **msgJson __ @throws IOException / @RabbitListener(bindings = @QueueBinding( value = @Queue(value = MqConst.QUEUE_WARE_ORDER, durable = “true”), exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_WARE_ORDER), key = {MqConst.ROUTING_WARE_ORDER} )) public void updateOrderStatus(String msgJson, Message message, Channel channel) throws IOException { if (!StringUtils.isEmpty(msgJson)){ Map String orderId = (String)map.get(“orderId”); String status = (String)map.get(“status”); if (“DEDUCTED”.equals(status)){ // 减库存成功! 修改订单状态为已支付 orderService.updateOrderStatus(Long.parseLong(orderId), ProcessStatus.WAITING_DELEVER); }else { / 减库存失败!远程调用其他仓库查看是否有库存! true: orderService.sendOrderStatus(orderId); orderService.updateOrderStatus(orderId, ProcessStatus.NOTIFIED_WARE); false: 1. 补货 | 2. 人工客服。 / orderService.updateOrderStatus(Long.parseLong(orderId), ProcessStatus.STOCK_EXCEPTION); } } channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); } |
---|
1.4 拆单接口
1.4.1 库存系统配置拆单回调接口
application-dev.ymlorder: split: url: http://localhost:8204/api/order/orderSplit |
---|
1.4.2 订单实现拆单接口
List |
---|
1.4.3 拆单接口实现类
@Override @Transactional public List ArrayList / 1. 先获取到原始订单 107 2. 将wareSkuMap 转换为我们能操作的对象 [{“wareId”:”1”,”skuIds”:[“2”,”10”]},{“wareId”:”2”,”skuIds”:[“3”]}] 方案一:class Param{ private String wareId; private List } 方案二:看做一个Map mpa.put(“wareId”,value); map.put(“skuIds”,value) 3. 创建一个新的子订单 108 109 。。。 4. 给子订单赋值 5. 保存子订单到数据库 6. 修改原始订单的状态 7. 测试 _OrderInfo orderInfoOrigin = getOrderInfo(orderId); List(wareSkuMap, Map.class); if (maps != null) { for (Map map : maps) { String wareId = (String) map.get(“wareId”); List OrderInfo subOrderInfo = new OrderInfo(); // 属性拷贝 _BeanUtils._copyProperties(orderInfoOrigin, subOrderInfo); // 防止主键冲突 _subOrderInfo.setId(null); subOrderInfo.setParentOrderId(orderId); // 赋值仓库Id subOrderInfo.setWareId(wareId); // 计算子订单的金额: 必须有订单明细 // 获取到子订单明细 // 声明一个集合来存储子订单明细 ArrayList List if (orderDetailList != null && orderDetailList.size() > 0) { for (OrderDetail orderDetail : orderDetailList) { // 获取子订单明细的商品Id for (String skuId : skuIds) { if (Long._parseLong(skuId) == orderDetail.getSkuId().longValue()) { // 将订单明细添加到集合 _orderDetails.add(orderDetail); } } } } subOrderInfo.setOrderDetailList(orderDetails); // 计算总金额 subOrderInfo.sumTotalAmount(); // 保存子订单 saveOrderInfo(subOrderInfo); // 将子订单添加到集合中! orderInfoArrayList.add(subOrderInfo); } } // 修改原始订单的状态 updateOrderStatus(orderId, ProcessStatus.**_SPLIT); return **orderInfoArrayList; } |
---|
1.4.4 拆单接口控制器
/ 拆单业务 @param **request __ @return ** **/ @RequestMapping(“orderSplit”) public String orderSplit(HttpServletRequest request){ String orderId = request.getParameter(“orderId”); _String wareSkuMap = request.getParameter(“wareSkuMap”); // 拆单:获取到的子订单集合 List // 声明一个存储map的集合 _ArrayList// 生成子订单集合 for (OrderInfo orderInfo : subOrderInfoList) { Map map = orderService.initWareOrder(orderInfo); // 添加到集合中! mapArrayList.add(map); } return JSON._toJSONString(mapArrayList); } |
---|
二、取消订单业务补充
2.1 在MqConst中添加常量
/* 关闭交易 */ public static final String EXCHANGE_DIRECT_PAYMENT_CLOSE = “exchange.direct.payment.close”; public static final String ROUTING_PAYMENT_CLOSE = “payment.close”; //队列 public static final String QUEUE_PAYMENT_CLOSE = “queue.payment.close”; |
---|
2.2 在取消订单实现类中发送消息关闭交易
更改接口
@Override public void execExpiredOrder(Long orderId) { // orderInfo _updateOrderStatus(orderId, ProcessStatus.**_CLOSED); rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE, MqConst.ROUTING_PAYMENT_CLOSE**, orderId); } |
---|
2.3 service-payment模块接收消息
2.3.1 编写消费者
package com.atguigu.gmall.payment.receiver; @Component public class PaymentReceiver { @Autowired private PaymentService paymentService; @SneakyThrows @RabbitListener(bindings = @QueueBinding( value = @Queue(value = MqConst.QUEUE_PAYMENT_CLOSE,durable = “true”), exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE), key = {MqConst.ROUTING_PAYMENT_CLOSE} )) public void closePayment(Long orderId , Message message, Channel channel){ if (null != orderId){ // 关闭交易 paymentService.closePayment(orderId); } _// 手动ack _channel.basicAck(message.getMessageProperties().getDeliveryTag(),false); } } |
---|
2.3.2 编写关闭交易记录接口与实现类
PaymentService/ 关闭过期交易记录 @param **orderId __ */ void closePayment(Long orderId); |
---|
@Override public void closePayment(Long orderId) { // 设置关闭交易记录的条件 118 _QueryWrapper paymentInfoQueryWrapper.eq(“order_id”,orderId); if (null == count || count.intValue()==0) return; // 在关闭支付宝交易之前。还需要关闭paymentInfo PaymentInfo paymentInfo = new PaymentInfo(); paymentInfo.setPaymentStatus(PaymentStatus.**_ClOSED.name()); paymentInfoMapper**.update(paymentInfo,paymentInfoQueryWrapper); } |
2.4 支付宝关闭交易
2.4.1 编写接口
AlipayService接口/ 关闭交易 * @param **orderId __ _ @return ** **/ _Boolean closePay(Long orderId); |
---|
2.4.2 编写实现类
@SneakyThrows @Override public Boolean closePay(Long orderId) { OrderInfo orderInfo = orderFeignClient.getOrderInfo(orderId); _AlipayTradeCloseRequest request = new AlipayTradeCloseRequest(); HashMap map.put(“operator_id”,“YX01”); request.setBizContent(JSON._toJSONString(map)); AlipayTradeCloseResponse response = alipayClient.execute(request); if(response.isSuccess()){ System.out.println(“调用成功”); return true; } else { System.out.println(“调用失败”); return false; } } |
---|
2.4.3 编写控制器
AlipayController http://localhost:8205/api/payment/alipay/closePay/25// 根据订单Id关闭订单 @GetMapping(“closePay/{orderId}”) @ResponseBody public Boolean closePay(@PathVariable Long orderId){ Boolean aBoolean = alipayService.closePay(orderId); return aBoolean; } |
---|
2.5 查询支付交易记录
2.5.1 编写接口
AlipayService / 根据订单查询是否支付成功! @param **orderId __ _ @return ** **/ _Boolean checkPayment(Long orderId); |
---|
2.5.2 编写实现类
@SneakyThrows @Override public Boolean checkPayment(Long orderId) { // 根据订单Id 查询订单信息 _OrderInfo orderInfo = orderFeignClient.getOrderInfo(orderId); AlipayTradeQueryRequest request = new AlipayTradeQueryRequest(); HashMap map.put(“out_trade_no”,orderInfo.getOutTradeNo()); AlipayTradeQueryResponse response = alipayClient.execute(request); if(response.isSuccess()){ return true; } else { return false; } } |
---|
2.5.3 编写控制器
http://localhost:8205/api/payment/alipay/checkPayment/30
// 查看是否有交易记录 @RequestMapping(“checkPayment/{orderId}”) @ResponseBody public Boolean checkPayment(@PathVariable Long orderId){ // 调用退款接口 boolean flag = alipayService.checkPayment(orderId); return flag; } |
---|
2.6 整合关闭过期订单
2.6.1 在AlipayController 添加查询PaymentInfo 数据接口
@GetMapping(“getPaymentInfo/{outTradeNo}”) @ResponseBody public PaymentInfo getPaymentInfo(@PathVariable String outTradeNo){ PaymentInfo paymentInfo = paymentService.getPaymentInfo(outTradeNo, PaymentType.ALIPAY.name()); if (null!=paymentInfo){ return paymentInfo; } return null; } |
---|
2.6.2 创建service-payment-client
PaymentFeignClient 接口package com.atguigu.gmall.payment.client;@FeignClient(value = “service-payment”,fallback = PaymentDegradeFeignClient.class) public interface PaymentFeignClient { @GetMapping(“api/payment/alipay/closePay/{orderId}”) Boolean closePay(@PathVariable Long orderId); @GetMapping(“api/payment/alipay/checkPayment/{orderId}”) Boolean checkPayment(@PathVariable Long orderId); @GetMapping(“api/payment/alipay/getPaymentInfo/{outTradeNo}”) PaymentInfo getPaymentInfo(@PathVariable String outTradeNo); } |
---|
PaymentDegradeFeignClient实现类@Component public class PaymentDegradeFeignClient implements PaymentFeignClient { @Override public Boolean closePay(Long orderId) { return null; } @Override public Boolean checkPayment(Long orderId) { return null; } @Override public PaymentInfo getPaymentInfo(String outTradeNo) { return null; } } |
2.6.3 在订单service-order项目中添加依赖
<dependency> <groupId>com.atguigu.gmall</groupId> <artifactId>service-payment-client</artifactId> <version>1.0</version> </dependency> |
---|
2.6.4 OrderReceiver 整合代码
2.6.4.1 关闭订单流程图:
2.6.4.2 代码实现
接口:OrderService/ 更新过期订单 @param **orderId __ *@param flag __ */ void execExpiredOrder(Long orderId,String flag); |
---|
@Override public void execExpiredOrder(Long orderId,String flag) { // 调用方法 状态 _updateOrderStatus(orderId,ProcessStatus.**_CLOSED); if (“2”.equals(flag)){ // 发送消息队列,关闭支付宝的交易记录。 rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE,MqConst.ROUTING_PAYMENT_CLOSE**,orderId); } } |
| @Autowired
private RabbitService rabbitService;
@Autowired
private PaymentFeignClient paymentFeignClient;
// 监听消息@SneakyThrows
@RabbitListener(queues = MqConst.**_QUEUE_ORDER_CANCEL)public void orderCancel(Long orderId, Message message, Channel channel){
try {
// 判断订单id 是否存在! if (orderId!=null){
_// 根据订单Id 查询订单对象 _OrderInfo orderInfo = orderService.getById(orderId);
// 判断 if(orderInfo!=null && “UNPAID”.equals(orderInfo.getOrderStatus()) && “UNPAID”.equals(orderInfo.getProcessStatus())){
_// 关闭过期订单! 还需要关闭对应的 paymentInfo ,还有alipay.
// orderService.execExpiredOrder(orderId);
// 查询paymentInfo 是否存在! _PaymentInfo paymentInfo = paymentFeignClient.getPaymentInfo(orderInfo.getOutTradeNo());
// 判断 用户点击了扫码支付 if(paymentInfo!=null && “UNPAID”**.equals(paymentInfo.getPaymentStatus())){
_// 查看是否有交易记录! _Boolean flag = **paymentFeignClient**.checkPayment(orderId);<br /> _// 判断 _**if **(flag){<br /> _// flag = true , 有交易记录 // 调用关闭接口! 扫码未支付这样才能关闭成功! _Boolean result = **paymentFeignClient**.closePay(orderId);<br /> _// 判断 _**if **(result){<br /> _// result = true; 关闭成功!未付款!需要关闭orderInfo, paymentInfo,Alipay<br /> _**orderService**.execExpiredOrder(orderId,**"2"**);<br /> }**else **{<br /> _// result = false; 表示付款! // 说明已经付款了! 正常付款成功都会走异步通知! _}<br /> }**else **{<br /> _// 没有交易记录,不需要关闭支付! 需要关闭orderInfo, paymentInfo<br /> _**orderService**.execExpiredOrder(orderId,**"2"**);<br /> }
}**else **{<br /> _// 只关闭订单orderInfo! _**orderService**.execExpiredOrder(orderId,**"1"**);<br /> }<br /> }<br /> }<br /> <br /> } **catch **(Exception e) {<br /> _// 写入日志...<br /> _e.printStackTrace();<br /> }<br /> _// 手动确认 _channel.basicAck(message.getMessageProperties().getDeliveryTag(),**false**);<br />} |
| —- |