我们从接口位置开始理解整体思想
这个代码应该很好理解,我们要知道,成功之后他返回的是from,咱们用网页打开它,就会来到对应的支付宝页面了
@ApiOperation("提交订单")
@PostMapping("confirm")
public void confirmOrder(@ApiParam("订单对象") @RequestBody ConfirmOrderRequest orderRequest, HttpServletResponse response) {
JsonData jsonData = orderService.confirmOrder(orderRequest);
if (jsonData.getCode() == 0) {
String client = orderRequest.getClientType();
String payType = orderRequest.getPayType();
//如果是支付宝网页支付,都是跳转网页,APP除外
if (payType.equalsIgnoreCase(ProductOrderPayTypeEnum.ALIPAY.name())) {
log.info("创建支付宝订单成功:{}", orderRequest.toString());
if (client.equalsIgnoreCase(ClientType.H5.name())) {
writeData(response, jsonData);
} else if (client.equalsIgnoreCase(ClientType.APP.name())) {
//APP SDK支付 TODO
}
} else if (payType.equalsIgnoreCase(ProductOrderPayTypeEnum.WECHAT.name())) {
//微信支付 TODO
}
} else {
log.error("创建订单失败{}", jsonData.toString());
}
}
更加复杂且精彩的要开始了!让我们走进订单的实现方法!首先我们得先看看我们的大致流程!
* * 防重提交
* * 用户微服务-确认收货地址
* * 商品微服务-获取最新购物项和价格
* * 订单验价
* * 优惠券微服务-获取优惠券
* * 验证价格
* * 锁定优惠券
* * 锁定商品库存
* * 创建订单对象
* * 创建子订单对象
* * 发送延迟消息-用于自动关单
* * 创建支付信息-对接三方支付
* * 防重提交
* * 用户微服务-确认收货地址
* * 商品微服务-获取最新购物项和价格
* * 订单验价
* * 优惠券微服务-获取优惠券
* * 验证价格
* * 锁定优惠券
* * 锁定商品库存
* * 创建订单对象
* * 创建子订单对象
* * 发送延迟消息-用于自动关单
* * 创建支付信息-对接三方支付
走进这个方法confirmOrder
@Override
public JsonData confirmOrder(ConfirmOrderRequest orderRequest) {
LoginUser loginUser = LoginInterceptor.threadLocal.get();
String orderOutTradeNo = CommonUtil.getStringNumRandom(32);
//获取收货地址详情
ProductOrderAddressVO addressVO = this.getUserAddress(orderRequest.getAddressId());
log.info("收货地址信息:{}",addressVO);
//获取用户加入购物车的商品
List<Long> productIdList = orderRequest.getProductIdList();
JsonData cartItemDate = productFeignService.confirmOrderCartItem(productIdList);
List<OrderItemVO> orderItemList = cartItemDate.getData(new TypeReference<List<OrderItemVO>>(){});
log.info("获取的商品:{}",orderItemList);
if(orderItemList == null){
//购物车商品不存在
throw new BizException(BizCodeEnum.ORDER_CONFIRM_CART_ITEM_NOT_EXIST);
}
//验证价格,减去商品优惠券
this.checkPrice(orderItemList,orderRequest);
//锁定优惠券
this.lockCouponRecords(orderRequest ,orderOutTradeNo );
//锁定库存
this.lockProductStocks(orderItemList,orderOutTradeNo);
//创建订单
ProductOrderDO productOrderDO = this.saveProductOrder(orderRequest,loginUser,orderOutTradeNo,addressVO);
//创建订单项
this.saveProductOrderItems(orderOutTradeNo,productOrderDO.getId(),orderItemList);
//发送延迟消息,用于自动关单
OrderMessage orderMessage = new OrderMessage();
orderMessage.setOutTradeNo(orderOutTradeNo);
rabbitTemplate.convertAndSend(rabbitMQConfig.getEventExchange(),rabbitMQConfig.getOrderCloseDelayRoutingKey(),orderMessage);
//创建支付
PayInfoVO payInfoVO = new PayInfoVO(orderOutTradeNo,
productOrderDO.getPayAmount(),orderRequest.getPayType(),
orderRequest.getClientType(), orderItemList.get(0).getProductTitle(),"",TimeConstant.ORDER_PAY_TIMEOUT_MILLS);
String payResult = payFactory.pay(payInfoVO);
if(StringUtils.isNotBlank(payResult)){
log.info("创建支付订单成功:payInfoVO={},payResult={}",payInfoVO,payResult);
return JsonData.buildSuccess(payResult);
}else {
log.error("创建支付订单失败:payInfoVO={},payResult={}",payInfoVO,payResult);
return JsonData.buildResult(BizCodeEnum.PAY_ORDER_FAIL);
}
} 接下来让我们走进更加细节方法中了解思路!
进入




通过feign调用对应的方法




相信看起来很简单,无非就是Redis中删掉购物车中对应的商品罢了




进入




注释已经很明白了!
/**
* 验证价格
* 1)统计全部商品的价格
* 2) 获取优惠券(判断是否满足优惠券的条件),总价再减去优惠券的价格 就是 最终的价格
*
* @param orderItemList
* @param orderRequest
*/
private void checkPrice(List<OrderItemVO> orderItemList, ConfirmOrderRequest orderRequest) {
//统计商品总价格
BigDecimal realPayAmount = new BigDecimal("0");
if (orderItemList != null) {
for(OrderItemVO orderItemVO : orderItemList){
BigDecimal itemRealPayAmount = orderItemVO.getTotalAmount();
realPayAmount = realPayAmount.add(itemRealPayAmount);
}
}
//获取优惠券,判断是否可以使用
CouponRecordVO couponRecordVO = getCartCouponRecord(orderRequest.getCouponRecordId());
//计算购物车价格,是否满足优惠券满减条件
if(couponRecordVO!=null){
//计算是否满足满减
if(realPayAmount.compareTo(couponRecordVO.getConditionPrice()) < 0){
throw new BizException(BizCodeEnum.ORDER_CONFIRM_COUPON_FAIL);
}
if(couponRecordVO.getPrice().compareTo(realPayAmount)>0){
realPayAmount = BigDecimal.ZERO;
}else {
realPayAmount = realPayAmount.subtract(couponRecordVO.getPrice());
}
}
if(realPayAmount.compareTo(orderRequest.getRealPayAmount()) !=0 ){
log.error("订单验价失败:{}",orderRequest);
throw new BizException(BizCodeEnum.ORDER_CONFIRM_PRICE_FAIL);
}
} 这两个其实是一样的,无非就是对商品进行锁定,用到的是mq,具体较复杂,这里就不细说了,主要是学习链路思想,rabbitMQ其实是没有延时队列这种说法的,但是却有死信队列,咱们可以通过死信队列来实现相应的延时队列,具体直接博主上面文章搜索也可以!




进入




/**
* 创建订单
* @param orderRequest
* @param loginUser
* @param orderOutTradeNo
* @param addressVO
*/
private ProductOrderDO saveProductOrder(ConfirmOrderRequest orderRequest, LoginUser loginUser, String orderOutTradeNo, ProductOrderAddressVO addressVO) {
ProductOrderDO productOrderDO = new ProductOrderDO();
productOrderDO.setUserId(loginUser.getId());
productOrderDO.setHeadImg(loginUser.getHeadImg());
productOrderDO.setNickname(loginUser.getName());
productOrderDO.setOutTradeNo(orderOutTradeNo);
productOrderDO.setCreateTime(new Date());
productOrderDO.setDel(0);
productOrderDO.setOrderType(ProductOrderTypeEnum.DAILY.name());
//实际支付的价格
productOrderDO.setPayAmount(orderRequest.getRealPayAmount());
//总价,未使用优惠券的价格
productOrderDO.setTotalAmount(orderRequest.getTotalAmount());
productOrderDO.setState(ProductOrderStateEnum.NEW.name());
productOrderDO.setPayType(ProductOrderPayTypeEnum.valueOf(orderRequest.getPayType()).name());
productOrderDO.setReceiverAddress(JSON.toJSONString(addressVO));
productOrderMapper.insert(productOrderDO);
return productOrderDO;
} 进入




/**
* 新增订单项
* @param orderOutTradeNo
* @param orderId
* @param orderItemList
*/
private void saveProductOrderItems(String orderOutTradeNo, Long orderId, List<OrderItemVO> orderItemList) {
List<ProductOrderItemDO> list = orderItemList.stream().map(
obj->{
ProductOrderItemDO itemDO = new ProductOrderItemDO();
itemDO.setBuyNum(obj.getBuyNum());
itemDO.setProductId(obj.getProductId());
itemDO.setProductImg(obj.getProductImg());
itemDO.setProductName(obj.getProductTitle());
itemDO.setOutTradeNo(orderOutTradeNo);
itemDO.setCreateTime(new Date());
//单价
itemDO.setAmount(obj.getAmount());
//总价
itemDO.setTotalAmount(obj.getTotalAmount());
itemDO.setProductOrderId(orderId);
return itemDO;
}
).collect(Collectors.toList());
orderItemMapper.insertBatch(list);
} 可以好好学习一下这种批量插入的方法
<!--批量插入-->
<insert id="insertBatch">
insert into product_order_item
(
<include refid="Base_Column_List_No_Id"/>
)
values
<foreach collection="orderItemList" item="item" index="index" separator=",">
(
#{item.productOrderId},
#{item.outTradeNo},
#{item.productId},
#{item.productName},
#{item.productImg},
#{item.buyNum},
#{item.createTime},
#{item.totalAmount},
#{item.amount}
)
</foreach>
</insert> 



进入




我们先来看构件的VO类:PayInfoVO
package net.xdclass.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PayInfoVO {
/**
* 订单号
*/
private String outTradeNo;
/**
* 订单总金额
*/
private BigDecimal payFee;
/**
* 支付类型 微信-支付宝-银行-其他
*/
private String payType;
/**
* 端类型 APP/H5/PC
*/
private String clientType;
/**
* 标题
*/
private String title;
/**
* 描述
*/
private String description;
/**
* 订单支付超时时间,毫秒
*/
private long orderPayTimeoutMills;
}
我们来看看简单的工厂模式——PayFactory
package net.xdclass.component;
import lombok.extern.slf4j.Slf4j;
import net.xdclass.enums.ProductOrderPayTypeEnum;
import net.xdclass.vo.PayInfoVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class PayFactory {
@Autowired
private AlipayStrategy alipayStrategy;
@Autowired
private WechatPayStrategy wechatPayStrategy;
/**
* 创建支付,简单工程模式
* @param payInfoVO
* @return
*/
public String pay(PayInfoVO payInfoVO){
String payType = payInfoVO.getPayType();
if(ProductOrderPayTypeEnum.ALIPAY.name().equalsIgnoreCase(payType)){
//支付宝支付
PayStrategyContext payStrategyContext = new PayStrategyContext(alipayStrategy);
return payStrategyContext.executeUnifiedorder(payInfoVO);
} else if(ProductOrderPayTypeEnum.WECHAT.name().equalsIgnoreCase(payType)){
//微信支付 暂未实现
PayStrategyContext payStrategyContext = new PayStrategyContext(wechatPayStrategy);
return payStrategyContext.executeUnifiedorder(payInfoVO);
}
return "";
}
/**
* 查询订单支付状态
*
* 支付成功返回非空,其他返回空
*
* @param payInfoVO
* @return
*/
public String queryPaySuccess(PayInfoVO payInfoVO){
String payType = payInfoVO.getPayType();
if(ProductOrderPayTypeEnum.ALIPAY.name().equalsIgnoreCase(payType)){
//支付宝支付
PayStrategyContext payStrategyContext = new PayStrategyContext(alipayStrategy);
return payStrategyContext.executeQueryPaySuccess(payInfoVO);
} else if(ProductOrderPayTypeEnum.WECHAT.name().equalsIgnoreCase(payType)){
//微信支付 暂未实现
PayStrategyContext payStrategyContext = new PayStrategyContext(wechatPayStrategy);
return payStrategyContext.executeQueryPaySuccess(payInfoVO);
}
return "";
}
}
这个位置也就是判断各种支付了,需要更深层次了解,可以直接搜索设计模式!




本文作者为DBC,转载请注明。