尚品汇商城

一、支付成功处理

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 支付成功发送消息

paySuccess
16 支付之后业务处理 - 图1

1.1.4 service-order模块接收消息

OrderReceiver类添加方法
16 支付之后业务处理 - 图2

1.2 订单模块发送减库存通知

订单模块除了接收到请求改变单据状态,还要发送库存系统
查看看《库存管理系统接口手册》中【减库存的消息队列消费端接口】中的描述,组织相应的消息数据进行传递。
16 支付之后业务处理 - 图3

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 = new 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 mapArrayList = new ArrayList<>();
List orderDetailList = orderInfo.getOrderDetailList();
for (OrderDetail orderDetail : orderDetailList) {
HashMap orderDetailMap = new HashMap<>();
orderDetailMap.put(“skuId”, orderDetail.getSkuId());
orderDetailMap.put(“skuNum”, orderDetail.getSkuNum());
orderDetailMap.put(“skuName”, orderDetail.getSkuName());
mapArrayList.add(orderDetailMap);
}
map.put(“details”, mapArrayList);
return map;
}

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 map = JSON.parseObject(msgJson, Map.class);
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 orderSplit(Long orderId, String wareSkuMap);

1.4.3 拆单接口实现类

@Override
@Transactional
public List orderSplit(Long orderId, String wareSkuMap) {
ArrayList orderInfoArrayList = new ArrayList<>();
/
1. 先获取到原始订单 107
2. 将wareSkuMap 转换为我们能操作的对象 [{“wareId”:”1”,”skuIds”:[“2”,”10”]},{“wareId”:”2”,”skuIds”:[“3”]}]
方案一:class Param{
private String wareId;
private List skuIds;
}
方案二:看做一个Map mpa.put(“wareId”,value); map.put(“skuIds”,value)

3. 创建一个新的子订单 108 109 。。。 4. 给子订单赋值 5. 保存子订单到数据库 6. 修改原始订单的状态 7. 测试
/
_OrderInfo orderInfoOrigin = getOrderInfo(orderId);
List maps = JSON._parseArray
(wareSkuMap, Map.class);
if (maps != null) {
for (Map map : maps) {
String wareId = (String) map.get(“wareId”);

List skuIds = (List) map.get(“skuIds”);

OrderInfo subOrderInfo = new OrderInfo();
// 属性拷贝
_BeanUtils._copyProperties
(orderInfoOrigin, subOrderInfo);
// 防止主键冲突
_subOrderInfo.setId(null);
subOrderInfo.setParentOrderId(orderId);
// 赋值仓库Id
subOrderInfo.setWareId(wareId);

// 计算子订单的金额: 必须有订单明细
// 获取到子订单明细
// 声明一个集合来存储子订单明细
ArrayList orderDetails = new ArrayList<>();

List orderDetailList = orderInfoOrigin.getOrderDetailList();
// 表示主主订单明细中获取到子订单的明细
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 subOrderInfoList = orderService.orderSplit(Long._parseLong(orderId),wareSkuMap);
// 声明一个存储map的集合
_ArrayList mapArrayList = new 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 = new QueryWrapper<>();
paymentInfoQueryWrapper.eq(“order_id”,orderId);
// 如果当前的交易记录不存在,则不更新交易记录 Integer count = paymentInfoMapper.selectCount(paymentInfoQueryWrapper);
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 = new HashMap<>();
// map.put(“tradeno”,paymentInfo.getTradeNo()); // 从paymentInfo 中获取! _map.put(“out_trade_no”,orderInfo.getOutTradeNo());
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 = new HashMap<>();
map.put(“out_trade_no”,orderInfo.getOutTradeNo());
// 根据outtrade_no 查询交易记录 _request.setBizContent(JSON._toJSONString(map));
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 关闭订单流程图:

16 支付之后业务处理 - 图4

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())){

  1. _// 查看是否有交易记录! _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 /> }
  2. }**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 />} |

| —- |