我们从接口位置开始理解整体思想
这个代码应该很好理解,我们要知道,成功之后他返回的是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,转载请注明。