it_lv hace 1 mes
padre
commit
279b9e706b
Se han modificado 1 ficheros con 238 adiciones y 312 borrados
  1. 238 312
      尚硅谷_尚品甄选_第6章_订单.md

+ 238 - 312
尚硅谷_尚品甄选_第6章_订单.md

@@ -659,7 +659,7 @@ public class OrderInfoController extends BaseController {
 
     @Operation(summary = "订单结算")
     @GetMapping("/trade")
-    public AjaxResult orderTradeData() {
+    public AjaxResult trade() {
         return success(orderInfoService.orderTradeData());
     }
 }
@@ -668,7 +668,7 @@ public class OrderInfoController extends BaseController {
 #### 1.5.3 IOrderInfoService
 
 ```java
-TradeVo orderTradeData();
+TradeVo trade();
 ```
 
 #### 1.5.4 OrderInfoServiceImpl
@@ -680,60 +680,64 @@ private RemoteCartService remoteCartService;
 @Autowired
 private RedisTemplate redisTemplate;
 
+/**
+ * 订单结算页相关参数封装
+ *
+ * @return
+ */
 @Override
-public TradeVo orderTradeData() {
-    // 获取当前登录用户的id
-    Long userId = SecurityContextHolder.getUserId();
+public TradeVo trade() {
+    //0.创建订单结算VO对象
+    TradeVo tradeVo = new TradeVo();
 
-    R<List<CartInfo>> cartInfoListResult = remoteCartService.getCartCheckedList(SecurityConstants.INNER);
-    if (R.FAIL == cartInfoListResult.getCode()) {
-        throw new ServiceException(cartInfoListResult.getMsg());
+    //1.远程调用购物车服务,获取选中的商品列表,封装订单明细列表
+    R<List<CartInfo>> r = cartService.getCartCheckedList(SecurityConstants.INNER);
+    //1.1 验证远程调用是否成功
+    if (R.FAIL == r.getCode()) {
+        throw new ServiceException("远程调用购物车服务失败,原因:" + r.getMsg());
     }
-    List<CartInfo> cartInfoList = cartInfoListResult.getData();
-    if (CollectionUtils.isEmpty(cartInfoList)) {
-        throw new ServiceException("购物车无选中商品");
+    //1.2 获取到选中购物车商品列表
+    List<CartInfo> cartInfoList = r.getData();
+    //1.3 将购物车商品列表转为订单明细列表
+    if (Collections.isEmpty(cartInfoList)) {
+        throw new ServiceException("没有需要结算商品");
     }
+    //1.4 将购物车商品转为订单明细
+    List<OrderItem> orderItemList = cartInfoList
+            .stream()
+            .map(cartInfo -> {
+                OrderItem orderItem = new OrderItem();
+                BeanUtils.copyProperties(cartInfo, orderItem);
+                return orderItem;
+            }).collect(Collectors.toList());
+    tradeVo.setOrderItemList(orderItemList);
 
-    //将集合泛型从购物车改为订单明细
-    List<OrderItem> orderItemList = null;
+    //2.计算订单总金额
     BigDecimal totalAmount = new BigDecimal(0);
-    if (!CollectionUtils.isEmpty(cartInfoList)) {
-        orderItemList = cartInfoList.stream().map(cartInfo -> {
-            OrderItem orderItem = new OrderItem();
-            BeanUtils.copyProperties(cartInfo, orderItem);
-            orderItem.setSkuNum(cartInfo.getSkuNum());
-            return orderItem;
-        }).collect(Collectors.toList());
-
-        //订单总金额
-        for (OrderItem orderItem : orderItemList) {
-            totalAmount = totalAmount.add(orderItem.getSkuPrice().multiply(new BigDecimal(orderItem.getSkuNum())));
-        }
+    for (OrderItem orderItem : orderItemList) {
+        totalAmount = totalAmount.add(orderItem.getSkuPrice().multiply(new BigDecimal(orderItem.getSkuNum())));
     }
+    tradeVo.setTotalAmount(totalAmount);
 
-    //渲染订单确认页面-生成用户流水号(防止页面重复提交和页面等待超时)
-    String tradeNo = this.generateTradeNo(userId);
 
-    TradeVo tradeVo = new TradeVo();
-    tradeVo.setTotalAmount(totalAmount);
-    tradeVo.setOrderItemList(orderItemList);
+    //3.生成本次订单流水号-防止重复点击提交订单,存入Redis中 有效期5分钟
+    String tradeNo = this.generateTradeNo();
     tradeVo.setTradeNo(tradeNo);
+
+    //4.封装结算对象VO响应
     return tradeVo;
 }
 
 /**
- * 渲染订单确认页面-生成用户流水号
+ * 生成当前用户结算订单流水号
  *
- * @param userId
  * @return
  */
-private String generateTradeNo(Long userId) {
-    //1.构建流水号Key
-    String userTradeKey = "user:tradeNo:" + userId;
-    //2.构建流水号value
-    String tradeNo = UUID.randomUUID().toString().replaceAll("-", "");
-    //3.将流水号存入Redis 暂存5分钟
-    redisTemplate.opsForValue().set(userTradeKey, tradeNo, 5, TimeUnit.MINUTES);
+@Override
+public String generateTradeNo() {
+    String tradeKey = "user:tradeNo:" + SecurityContextHolder.getUserId();
+    String tradeNo = UUID.randomUUID().toString();
+    redisTemplate.opsForValue().set(tradeKey, tradeNo, 5, TimeUnit.MINUTES);
     return tradeNo;
 }
 ```
@@ -776,9 +780,56 @@ post /order/orderInfo/submitOrder
 }
 ```
 
-### 2.2 获取商品最新价格接口
+### 2.2 批量查询商品实时价格
+
+获取最新商品sku价格与购物车价格比较,校验价格是否变化,价格变化就更新购物车价格
+
+#### 2.2.1 批量查询商品实时价格
+
+操作模块:`spzx-product`
+
+##### 1 ProductSkuController
+
+```java
+@Operation(summary = "批量获取商品sku最新价格信息")
+@InnerAuth
+@PostMapping(value = "/getSkuPriceList")
+public R<List<SkuPriceVo>> getSkuPriceList(@RequestBody List<Long> skuIdList)
+{
+    return R.ok(productSkuService.getSkuPriceList(skuIdList));
+}
+```
+
+##### 2 IProductService
+
+```java
+List<SkuPriceVo> getSkuPriceList(List<Long> skuIdList);
+```
+
+##### 3 ProductServiceImpl
+
+```java
+@Override
+public List<SkuPriceVo> getSkuPriceList(List<Long> skuIdList) {
+    List<ProductSku> productSkuList = baseMapper.selectList(
+        new LambdaQueryWrapper<ProductSku>()
+        .in(ProductSku::getId, skuIdList)
+        .select(ProductSku::getId, ProductSku::getSalePrice)
+    );
+    return productSkuList.stream().map(item -> {
+        SkuPriceVo skuPrice = new SkuPriceVo();
+        skuPrice.setSkuId(item.getId());
+        skuPrice.setSalePrice(item.getSalePrice());
+        return skuPrice;
+    }).collect(Collectors.toList());
+}
+```
+
+
 
-#### 2.2.1 远程调用接口开发
+### 2.3 获取商品最新价格接口
+
+#### 2.3.1 远程调用接口开发
 
 操作模块:spzx-product
 
@@ -794,7 +845,7 @@ public R<SkuPriceVo> getSkuPrice(@PathVariable("skuId") Long skuId)
 }
 ```
 
-#### 2.2.2 openFeign接口定义
+#### 2.3.2 openFeign接口定义
 
 操作模块:spzx-api-product
 
@@ -818,35 +869,35 @@ public R<SkuPriceVo> getSkuPrice(Long skuId, String source) {
 }
 ```
 
-### 2.3 更新购物车最新价格
+### 2.4 更新购物车最新价格
 
 操作模块:spzx-cart
 
-#### 2.3.1 远程调用接口开发
+#### 2.4.1 远程调用接口开发
 
 ##### 1 CartController
 
 ```java
 @Operation(summary="更新用户购物车列表价格")
 @InnerAuth
-@GetMapping("/updateCartPrice/{userId}")
-public R<Boolean> updateCartPrice(@PathVariable("userId") Long userId){
-    return R.ok(cartService.updateCartPrice(userId));
+@GetMapping("/updateCartPrice")
+public R<Boolean> updateCartPrice(){
+    return R.ok(cartService.updateCartPrice());
 }
 ```
 
 ##### 2 ICartService
 
 ```java
-Boolean updateCartPrice(Long userId);
+Boolean updateCartPrice();
 ```
 
 ##### 3 CartServiceImpl
 
 ```java
 @Override
-public Boolean updateCartPrice(Long userId) {
-    String cartKey = getCartKey(userId);
+public Boolean updateCartPrice() {
+    String cartKey = getCartKey(SecurityContextHolder.getUserId());
     BoundHashOperations<String, String, CartInfo> hashOperations = redisTemplate.boundHashOps(cartKey);
     List<CartInfo> cartCachInfoList = hashOperations.values();
     if (!CollectionUtils.isEmpty(cartCachInfoList)) {
@@ -870,7 +921,7 @@ public Boolean updateCartPrice(Long userId) {
 }
 ```
 
-#### 2.3.2 openFeign接口定义
+#### 2.4.2 openFeign接口定义
 
 操作模块:spzx-api-cart
 
@@ -879,7 +930,6 @@ public Boolean updateCartPrice(Long userId) {
 ```java
 @GetMapping("/updateCartPrice/{userId}")
 R<Boolean> updateCartPrice(
-    @PathVariable("userId") Long userId, 
     @RequestHeader(SecurityConstants.FROM_SOURCE) String source
 );
 ```
@@ -888,44 +938,44 @@ R<Boolean> updateCartPrice(
 
 ```java
 @Override
-public R<Boolean> updateCartPrice(Long userId, String source) {
+public R<Boolean> updateCartPrice(String source) {
     return R.fail("更新购物车价格失败:" + throwable.getMessage());
 }
 ```
 
 
 
-### 2.4 删除购物车选中商品
+### 2.5 删除购物车选中商品
 
 下单成功后,删除购物车选中的商品
 
 操作模块:spzx-cart
 
-#### 2.4.1 远程调用接口开发
+#### 2.5.1 远程调用接口开发
 
 ##### 1 CartController
 
 ```java
 @Operation(summary="删除用户购物车列表中选中商品列表")
 @InnerAuth
-@GetMapping("/deleteCartCheckedList/{userId}")
-public R<Boolean> deleteCartCheckedList(@PathVariable("userId") Long userId){
-    return R.ok(cartService.deleteCartCheckedList(userId));
+@GetMapping("/deleteCartCheckedList")
+public R<Boolean> deleteCartCheckedList(){
+    return R.ok(cartService.deleteCartCheckedList());
 }
 ```
 
 ##### 2 ICartService
 
 ```java
- Boolean deleteCartCheckedList(Long userId);
+ Boolean deleteCartCheckedList();
 ```
 
 ##### 3 CartServiceImpl
 
 ```java
 @Override
-public Boolean deleteCartCheckedList(Long userId) {
-    String cartKey = getCartKey(userId);
+public Boolean deleteCartCheckedList() {
+    String cartKey = getCartKey(SecurityContextHolder.getUserId());
     BoundHashOperations<String, String, CartInfo> hashOperations = redisTemplate.boundHashOps(cartKey);
     List<CartInfo> cartCachInfoList = hashOperations.values();
     if (!CollectionUtils.isEmpty(cartCachInfoList)) {
@@ -940,7 +990,7 @@ public Boolean deleteCartCheckedList(Long userId) {
 }
 ```
 
-#### 2.4.2 openFeign接口定义
+#### 2.5.2 openFeign接口定义
 
 操作模块:spzx-api-cart
 
@@ -949,7 +999,6 @@ public Boolean deleteCartCheckedList(Long userId) {
 ```java
 @GetMapping("/deleteCartCheckedList/{userId}")
 R<Boolean> deleteCartCheckedList(
-    @PathVariable("userId") Long userId, 
     @RequestHeader(SecurityConstants.FROM_SOURCE)String source
 );
 ```
@@ -958,16 +1007,16 @@ R<Boolean> deleteCartCheckedList(
 
 ```java
 @Override
-public R<Boolean> deleteCartCheckedList(Long userId, String source) {
+public R<Boolean> deleteCartCheckedList(String source) {
     return R.fail("删除用户购物车选中数据失败:" + throwable.getMessage());
 }
 ```
 
 
 
-### 2.5 获取用户地址信息(已定义)
+### 2.6 获取用户地址信息(已定义)
 
-#### 2.5.1 远程调用接口
+#### 2.6.1 远程调用接口
 
 操作模块:spzx-user
 
@@ -986,7 +1035,7 @@ public R<UserAddress> getUserAddress(@PathVariable("id") Long id)
 
 将spzx-user模块UserAddress实体类,移动到spzx-api-user模块
 
-#### 2.5.2 openFeign接口定义
+#### 2.6.2 openFeign接口定义
 
 操作模块:spzx-api-user
 
@@ -1047,81 +1096,11 @@ resources/META-INF.spring/org.springframework.boot.autoconfigure.AutoConfigurati
 com.spzx.user.api.factory.RemoteUserAddressFallbackFactory
 ```
 
-### 2.6 批量查询商品实时价格
-
-获取最新商品sku价格与购物车价格比较,校验价格是否变化,价格变化就更新购物车价格
-
-#### 2.6.1 批量查询商品实时价格
-
-操作模块:`spzx-product`
-
-##### 1 ProductSkuController
-
-```java
-@Operation(summary = "批量获取商品sku最新价格信息")
-@InnerAuth
-@PostMapping(value = "/getSkuPriceList")
-public R<List<SkuPriceVo>> getSkuPriceList(@RequestBody List<Long> skuIdList)
-{
-    return R.ok(productSkuService.getSkuPriceList(skuIdList));
-}
-```
-
-##### 2 IProductService
-
-```java
-List<SkuPriceVo> getSkuPriceList(List<Long> skuIdList);
-```
-
-##### 3 ProductServiceImpl
-
-```java
-@Override
-public List<SkuPriceVo> getSkuPriceList(List<Long> skuIdList) {
-    List<ProductSku> productSkuList = baseMapper.selectList(
-        new LambdaQueryWrapper<ProductSku>()
-        .in(ProductSku::getId, skuIdList)
-        .select(ProductSku::getId, ProductSku::getSalePrice)
-    );
-    return productSkuList.stream().map(item -> {
-        SkuPriceVo skuPrice = new SkuPriceVo();
-        skuPrice.setSkuId(item.getId());
-        skuPrice.setSalePrice(item.getSalePrice());
-        return skuPrice;
-    }).collect(Collectors.toList());
-}
-```
-
-#### 2.6.2 批量查询商品实时价格openFeign接口定义
-
-操作模块:`spzx-api-product`
-
-##### 1 RemoteProductService
-
-```java
-@PostMapping(value = "/productSku/getSkuPriceList")
-R<List<SkuPriceVo>> getSkuPriceList(
-    @RequestBody List<Long> skuIdList, 
-    @RequestHeader(SecurityConstants.FROM_SOURCE) String source
-);
-```
-
-##### 2 RemoteProductFallbackFactory
-
-```java
-@Override
-public R<List<SkuPriceVo>> getSkuPriceList(List<Long> skuIdList, String source) {
-    return R.fail("获取商品sku价格列表失败:" + throwable.getMessage());
-}
-```
-
-
-
-### 2.6 后端业务接口
+### 2.7 后端业务接口
 
 操作模块:spzx-order
 
-#### 2.6.1 OrderForm
+#### 2.7.1 OrderForm
 
 ```java
 package com.spzx.order.form;
@@ -1152,7 +1131,7 @@ public class OrderForm {
 }
 ```
 
-#### 2.6.2 OrderInfoController
+#### 2.7.2 OrderInfoController
 
 ```java
 @Operation(summary = "用户提交订单")
@@ -1162,13 +1141,13 @@ public AjaxResult submitOrder(@RequestBody OrderForm orderForm) {
 }
 ```
 
-#### 2.6.3 IOrderInfoService
+#### 2.7.3 IOrderInfoService
 
 ```java
 Long submitOrder(OrderForm orderForm);
 ```
 
-#### 2.6.4 OrderInfoServiceImpl
+#### 2.7.4 OrderInfoServiceImpl
 
 ```java
 @Autowired
@@ -1180,188 +1159,141 @@ private RemoteUserAddressService remoteUserAddressService;
 @Autowired
 private OrderLogMapper orderLogMapper;
 
-/**
- * 验证页面提交流水号是否有效
- *
- * @param userId
- * @param tradeNo
- * @return
- */
-private Boolean checkTradeNo(String userId, String tradeNo) {
-    String userTradeKey = "user:tradeNo:" + userId;
-    String redisTradeNo = (String) redisTemplate.opsForValue().get(userTradeKey);
-    return tradeNo.equals(redisTradeNo);
-}
-
 
 /**
- * 删除流水号
+ * 保存订单
  *
- * @param userId
+ * @param orderForm
+ * @return
  */
-private void deleteTradeNo(String userId) {
-    String userTradeKey = "user:tradeNo:" + userId;
-    redisTemplate.delete(userTradeKey);
-}
-
-@Transactional(rollbackFor = Exception.class)
 @Override
+@Transactional(rollbackFor = Exception.class)
 public Long submitOrder(OrderForm orderForm) {
-
-    //获取用户的userId
-    Long userId = SecurityContextHolder.getUserId();
-
-    //1 防止订单重复提交
-    //判断订单号是否存在:
-    // 如果存在则说明用户第一次提交此订单
-    // 如果五分中内提交了第二次订单,则订单号不存在,则说明用户已经提交过该订单,不能重复提交
-    // 如果五分中之后第一次提交,则订单号也不存在,说明操作超时
-    /*if(!this.checkTradeNo(userId.toString(), orderForm.getTradeNo())){
-            throw new ServiceException("请勿重复提交订单");
-        }
-        //删除redis中的订单号
-        this.deleteTradeNo(userId.toString());*/
-
-    //使用lua解决原子性的问题
-    String userTradeKey = "user:tradeNo:" + userId;
-    // 将键的值更为 processed  表示已处理  return 1 表示处理成功
-    // return -1  键存在但值不匹配,表示重复提交
-    // return 0   键不存在,订单过期
-    String scriptText = """
-        if redis.call('get', KEYS[1]) == ARGV[1] then
-            redis.call('set', KEYS[1], 'processed')
-            return 1
-        else
-           if redis.call('exists', KEYS[1]) == 1 then
-              redis.call('del',KEYS[1])
-              return -1
-           else
-              return 0
-           end
-       end
-       """;
-    DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
-    redisScript.setScriptText(scriptText);
-    redisScript.setResultType(Long.class);
-    Long flag = (Long)redisTemplate.execute(redisScript, Arrays.asList(userTradeKey), orderForm.getTradeNo());
-    if (flag == 0) {
-        throw new ServiceException("操作超时,请退回重试");
-    }
-    if(flag == -1){
-        throw new ServiceException("请勿重复提交订单");
+    //1.业务校验,验证流水号,防止订单重复提交或长时间停留订单结算页
+    String tradeKey = "user:tradeNo:" + SecurityContextHolder.getUserId();
+    //1.1 通过Lua脚本验证流水号 KEYS[1]:流水号Key  ARGV[1]:用户提交流水号
+    String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
+            "then\n" +
+            "    return redis.call(\"del\",KEYS[1])\n" +
+            "else\n" +
+            "    return 0\n" +
+            "end\n";
+    RedisScript<Boolean> booleanRedisScript = new DefaultRedisScript<>(script, Boolean.class);
+    Boolean flag = (Boolean) redisTemplate.execute(booleanRedisScript, Arrays.asList(tradeKey), orderForm.getTradeNo());
+    if (!flag) {
+        throw new ServiceException("流水号校验失败,请重新提交");
     }
 
-    try {
-        Thread.sleep(3000);
-    } catch (InterruptedException e) {
-        throw new RuntimeException(e);
-    }
+    //2.验证订单中明细商品价格是否变更
+    //2.1 根据订单明细列表中skuID列表,远程调用"商品服务"得到实时商品价格列表
+    List<Long> skuIdList = orderForm.getOrderItemList().stream()
+            .map(OrderItem::getSkuId)
+            .collect(Collectors.toList());
 
-    //2 获取最新价格并判断价格是否改变
-    //批量获取最新价格
-    List<OrderItem> orderItemList = orderForm.getOrderItemList();
-    List<Long> skuIdList = orderItemList.stream().map(orderItem -> orderItem.getSkuId()).collect(Collectors.toList());
-    R<List<SkuPriceVo>> skuPriceListResult =remoteProductService.getSkuPriceList(skuIdList, SecurityConstants.INNER);
-    if(R.FAIL == skuPriceListResult.getCode()){
-        throw new ServiceException(skuPriceListResult.getMsg());
+    R<List<SkuPriceVo>> r = remoteProductService.getSkuPriceList(skuIdList, SecurityConstants.INNER);
+    if (R.FAIL == r.getCode()) {
+        throw new ServiceException("远程调用商品服务失败,原因:" + r.getMsg());
     }
-    List<SkuPriceVo> skuPriceList = skuPriceListResult.getData();
-    //判断价格是否发生变化
-    String priceCheckResult = "";
-    Map<Long, BigDecimal> skuIdSalePriceMap = skuPriceList.stream().collect(Collectors.toMap(SkuPriceVo::getSkuId, SkuPriceVo::getSalePrice));
-    for (OrderItem orderItem : orderItemList) {
-        if(orderItem.getSkuPrice().compareTo(skuIdSalePriceMap.get(orderItem.getSkuId())) != 0){
-            //价格发生了变化
-            priceCheckResult += orderItem.getSkuName() + "\n";
+    List<SkuPriceVo> skuPriceVoList = r.getData();
+    Map<Long, BigDecimal> skuPriceMap =
+            skuPriceVoList.stream().collect(Collectors.toMap(SkuPriceVo::getSkuId, SkuPriceVo::getSalePrice));
+
+    //2.2 判断商品价格是否有变更,如果有变更
+    String priceErrorMsg = "";
+    for (OrderItem orderItem : orderForm.getOrderItemList()) {
+        if (orderItem.getSkuPrice().compareTo(skuPriceMap.get(orderItem.getSkuId())) != 0) {
+            priceErrorMsg += orderItem.getSkuName() + "\n";
         }
     }
-    if(!StringUtils.isEmpty(priceCheckResult)){
-
-        if(!orderForm.getIsBuy()){
-            //更新购物车中的价格
-            remoteCartService.updateCartPrice(userId, SecurityConstants.INNER);
+    if (StringUtils.isNotBlank(priceErrorMsg)) {
+        //2.2.1 如果是通过购物车车途径提交订单,则远程调用"购物车服务"更新购物车中商品价格
+        if (!orderForm.getIsBuy()) {
+            remoteCartService.updateCartPrice(SecurityConstants.INNER);
         }
-        throw new ServiceException(priceCheckResult + "以上商品价格发生变化,请确认");
-    }
 
-    //3 校验库存并锁定库存 TODO
+        //2.2.2 则提示价格有变更,重新结算,引导用户购物车页面
+        throw new ServiceException(priceErrorMsg + "以上商品价格有变动");
+    }
 
-    //4 下单
+    //4.如果商品价格未变更:保存订单及明细,日志
     Long orderId = this.saveOrder(orderForm);
 
-    //5 删除购物车选中商品
-    if(!orderForm.getIsBuy()){
-        remoteCartService.deleteCartCheckedList(userId, SecurityConstants.INNER);
+    //5.TODO 锁定商品库存,基于RabbitMQ异步锁定库存
+
+    //6.保存订单成功后, 如果是通过购物车车途径提交订单,清理用户购物车
+    if (!orderForm.getIsBuy()) {
+        remoteCartService.deleteCartCheckedList(SecurityConstants.INNER);
     }
+    //7.响应订单ID,对接支付页面
+    //TODO :RabbitMQ(死信、延迟插件)发送延迟消息 作用:自动将超时未支付订单关闭(延迟关单)
     return orderId;
 }
 
-private Long saveOrder(OrderForm orderForm) {
 
+
+/**
+ * 保存订单相关信息
+ *
+ * @param orderForm
+ * @return
+ */
+@Override
+public Long saveOrder(OrderForm orderForm) {
+    //1.保存订单信息
     Long userId = SecurityContextHolder.getUserId();
     String userName = SecurityContextHolder.getUserName();
-
-    // 保存订单记录:order_info
     OrderInfo orderInfo = new OrderInfo();
+    //1.1 用户信息
     orderInfo.setUserId(userId);
-    orderInfo.setOrderNo(orderForm.getTradeNo());//订单号
     orderInfo.setNickName(userName);
+    //1.2 生成订单编号 形式:日期+全局唯一ID(雪花算法)
+    String orderNo = DateUtils.dateTime() + IdWorker.getIdStr();
+    orderInfo.setOrderNo(orderNo);
+    //1.3 遍历订单明细列表计算订单总金额
+    BigDecimal totalAmount = orderForm.getOrderItemList().stream()
+            .map(OrderItem::getSkuPrice)
+            //从0开始累加结合中每个元素值
+            .reduce(BigDecimal.ZERO, BigDecimal::add);
+    orderInfo.setTotalAmount(totalAmount);
+    orderInfo.setOriginalTotalAmount(totalAmount);
+    //1.4 订单状态:待付款
+    orderInfo.setOrderStatus(0);
+    orderInfo.setCreateBy(userName);
     orderInfo.setRemark(orderForm.getRemark());
-    orderInfo.setFeightFee(orderForm.getFeightFee());
-
-    //远程调用获取用户地址信息
-    R<UserAddress> userAddressResult = remoteUserAddressService.getUserAddress(orderForm.getUserAddressId(), SecurityConstants.INNER);
-    if(R.FAIL == userAddressResult.getCode()){
-        throw new ServiceException(userAddressResult.getMsg());
+    //1.5 封装订单收件人信息 远程调用"用户服务"得到收件人信息
+    R<UserAddress> r = remoteUserAddressService.getUserAddress(orderForm.getUserAddressId(), SecurityConstants.INNER);
+    if (R.FAIL == r.getCode()) {
+        throw new ServiceException("远程调用用户服务失败,原因:" + r.getMsg());
     }
-    //获取用户地址信息
-    UserAddress userAddress = userAddressResult.getData();
-    //保存用户地址信息
+    UserAddress userAddress = r.getData();
     orderInfo.setReceiverName(userAddress.getName());
     orderInfo.setReceiverPhone(userAddress.getPhone());
     orderInfo.setReceiverTagName(userAddress.getTagName());
     orderInfo.setReceiverProvince(userAddress.getProvinceCode());
     orderInfo.setReceiverCity(userAddress.getCityCode());
     orderInfo.setReceiverDistrict(userAddress.getDistrictCode());
-    orderInfo.setReceiverAddress(userAddress.getFullAddress());
+    orderInfo.setReceiverAddress(userAddress.getAddress());
+    save(orderInfo);
+    Long orderId = orderInfo.getId();
 
-    //实时获取总价格
-    //计算orderItem中商品的总价格
+    //2.保存订单明细信息
     List<OrderItem> orderItemList = orderForm.getOrderItemList();
-    BigDecimal totalAmount = orderItemList
-        .stream()
-        .map(orderItem -> orderItem.getSkuPrice().multiply(new BigDecimal(orderItem.getSkuNum())))
-        .reduce(BigDecimal.ZERO, BigDecimal::add);
-    //设置商品总价格
-    orderInfo.setTotalAmount(totalAmount);
-    orderInfo.setCouponAmount(new BigDecimal(0));//优惠券价格
-    orderInfo.setOriginalTotalAmount(totalAmount);//原价
-    //订单状态
-    orderInfo.setOrderStatus(0);//待付款
-
-    orderInfo.setCreateBy(userName);
-    orderInfo.setUpdateBy(userName);
-    baseMapper.insert(orderInfo);
-
-    // 保存订单项记录:order_item
-    for (OrderItem orderItem : orderItemList) {
-        orderItem.setOrderId(orderInfo.getId());
-        orderItem.setCreateBy(userName);
-        orderItem.setUpdateBy(userName);
-        orderItemMapper.insert(orderItem);
+    if (!CollectionUtils.isEmpty(orderItemList)) {
+        for (OrderItem orderItem : orderItemList) {
+            orderItem.setOrderId(orderId);
+            orderItem.setCreateBy(userName);
+            orderItemMapper.insert(orderItem);
+        }
     }
-
-    // 保存订单日志记录:order_log
+    //3.保存订单操作日志
     OrderLog orderLog = new OrderLog();
-    orderLog.setOrderId(orderInfo.getId());
+    orderLog.setOrderId(orderId);
+    orderLog.setOperateUser(userId.toString());
     orderLog.setProcessStatus(0);
-    orderLog.setOperateUser(userName);
-    orderLog.setNote("提交订单");
+    orderLog.setNote(orderForm.getRemark());
     orderLog.setCreateBy(userName);
-    orderLog.setUpdateBy(userName);
     orderLogMapper.insert(orderLog);
-
-    return orderInfo.getId();
+    return orderId;
 }
 ```
 
@@ -1427,45 +1359,39 @@ TradeVo buy(Long skuId);
 #### 3.2.3、OrderInfoServiceImpl
 
 ```java
+/**
+ * 订单结算页面渲染
+ *
+ * @param skuId
+ * @return
+ */
 @Override
 public TradeVo buy(Long skuId) {
-
-    //获取商品SKU信息
-    R<ProductSku> productSkuResult = remoteProductService.getProductSku(skuId, SecurityConstants.INNER);
-    if(R.FAIL == productSkuResult.getCode()){
-        throw new ServiceException(productSkuResult.getMsg());
-    }
-    //获取实时价格
-    R<SkuPriceVo> skuPriceResult = remoteProductService.getSkuPrice(skuId, SecurityConstants.INNER);
-    if(R.FAIL == skuPriceResult.getCode()){
-        throw new ServiceException(skuPriceResult.getMsg());
+    //1.创建订单确认VO对象
+    TradeVo tradeVo = new TradeVo();
+    //2.封装订单明细
+    //2.1 远程调用"商品服务"查询商品信息
+    R<ProductSku> r = remoteProductService.getProductSku(skuId, SecurityConstants.INNER);
+    if (R.FAIL == r.getCode()) {
+        throw new ServiceException("远程调用商品服务失败,原因:" + r.getMsg());
     }
-
-    ProductSku productSku = productSkuResult.getData();
-    SkuPriceVo skuPrice = skuPriceResult.getData();
-
-    List<OrderItem> orderItemList = new ArrayList<>();
+    ProductSku productSku = r.getData();
+    //2.2 封装订单明细列表
     OrderItem orderItem = new OrderItem();
     orderItem.setSkuId(skuId);
     orderItem.setSkuName(productSku.getSkuName());
-    orderItem.setSkuNum(1);
-    orderItem.setSkuPrice(skuPrice.getSalePrice());//填充最新价格
     orderItem.setThumbImg(productSku.getThumbImg());
-
-    orderItemList.add(orderItem);
-
-
-    //订单总金额
-    BigDecimal totalAmount = skuPrice.getSalePrice();//填充最新价格
-
-    //渲染订单确认页面-生成用户流水号
-    String tradeNo = this.generateTradeNo(SecurityUtils.getUserId());
-
-    TradeVo tradeVo = new TradeVo();
-    tradeVo.setTotalAmount(totalAmount);
+    orderItem.setSkuPrice(productSku.getSalePrice());
+    orderItem.setSkuNum(1);
+    List<OrderItem> orderItemList = Arrays.asList(orderItem);
     tradeVo.setOrderItemList(orderItemList);
+    //3.封装订单总金额
+    tradeVo.setTotalAmount(orderItem.getSkuPrice());
+    //4.生成流水号
+    String tradeNo = this.generateTradeNo();
     tradeVo.setTradeNo(tradeNo);
-    tradeVo.setIsBuy(true); //立即购买
+    //5.设置立即购买为:true
+    tradeVo.setIsBuy(true);
     return tradeVo;
 }
 ```