it_lv 2 tygodni temu
rodzic
commit
21cf6cb5e9
1 zmienionych plików z 247 dodań i 298 usunięć
  1. 247 298
      第7章 订单.md

+ 247 - 298
第7章 订单.md

@@ -8,8 +8,10 @@
   - 购买项目-VIP(允许重复购买)
   - 购买项目-专辑(只能购买一次)
   - 购买项目-声音(只能购买一次)
+  - 结算商品代码优化-设计模式(策略模式+工厂模式)
 - 提交订单(余额付款)-->确保余额安全性
   - 分布式事务解决方案-Seata
+  - 虚拟物品发货代码优化-设计模式(策略模式+工厂模式)
 - 我的订单
 - 订单延迟关单
   - RabbitMQ提供延迟任务
@@ -285,19 +287,16 @@ public class OrderInfoApiController {
 
 
     /**
-     * 用户选择不同虚拟商品进行下单结算,封装渲染订单结算页所需要参数
+     * 三种商品(VIP会员、专辑、声音)订单结算,渲染订单结算页面
      *
-     * @param tradeVo
-     * @return OrderInfoVo订单VO对象,将来订单提交时,需要将订单VO对象提交到服务端
+     * @param tradeVo (购买项目类型、购买项目ID、声音数量)
+     * @return 订单VO信息
      */
     @GuiGuLogin
-    @Operation(summary = "用户选择不同虚拟商品进行下单结算,封装渲染订单结算页所需要参数")
+    @Operation(summary = "三种商品(VIP会员、专辑、声音)订单结算")
     @PostMapping("/orderInfo/trade")
-    public Result<OrderInfoVo> tradeData(@Valid @RequestBody TradeVo tradeVo) {
-        //1.获取用户ID
-        Long userId = AuthContextHolder.getUserId();
-        //2.封装订单VO对象
-        OrderInfoVo orderInfoVo = orderInfoService.tradeData(userId, tradeVo);
+    public Result<OrderInfoVo> trade(@RequestBody TradeVo tradeVo) {
+        OrderInfoVo orderInfoVo = orderInfoService.trade(tradeVo);
         return Result.ok(orderInfoVo);
     }
 
@@ -318,14 +317,13 @@ public interface OrderInfoService extends IService<OrderInfo> {
 
 
     /**
-     * 订单确认数据汇总
-     * @param userId 用户ID
-     * @param tradeVo 选择购买商品交易vo对象
-     * @return
+     * 三种商品(VIP会员、专辑、声音)订单结算,渲染订单结算页面
+     *
+     * @param tradeVo (购买项目类型、购买项目ID、声音数量)
+     * @return 订单VO信息
      */
-    OrderInfoVo tradeData(Long userId, TradeVo tradeVo);
+    OrderInfoVo trade(TradeVo tradeVo);
 }
-
 ```
 
 ```java
@@ -378,56 +376,55 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
 
 
     /**
-     * 订单确认数据汇总
+     * 三种商品(VIP会员、专辑、声音)订单结算,渲染订单结算页面
      *
-     * @param userId  用户ID
-     * @param tradeVo 选择购买商品交易vo对象
-     * @return
+     * @param tradeVo (购买项目类型、购买项目ID、声音数量)
+     * @return 订单VO信息
      */
     @Override
-    public OrderInfoVo tradeData(Long userId, TradeVo tradeVo) {
-        //1.初始化OrderInfoVo
+    public OrderInfoVo trade(TradeVo tradeVo) {
+        //0.创建订单VO对象
         OrderInfoVo orderInfoVo = new OrderInfoVo();
-        //1.1. 初始化相关金额:0.00 此时金额必须是字符串"0.00"
+        Long userId = AuthContextHolder.getUserId();
+        //1.初始化订单VO中价格、订单明细列表、订单减免明细列表
+        //1.1 声明三个初始价格:原价、减免金额、订单金额
         BigDecimal originalAmount = new BigDecimal("0.00");
         BigDecimal orderAmount = new BigDecimal("0.00");
         BigDecimal derateAmount = new BigDecimal("0.00");
-
-        //1.2. 初始化商品及优惠列表
+        //1.2 声明封装订单明细集合
         List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
+        //1.3 声明封装订单减免明细集合
         List<OrderDerateVo> orderDerateVoList = new ArrayList<>();
 
-        //付款项目类型: 1001-专辑 1002-声音 1003-vip会员
+        //获取购买项目类型 1001-专辑 1002-声音 1003-vip会员
         String itemType = tradeVo.getItemType();
-        //2.处理购买商品类型为:VIP套餐
+        //2.处理购买项目类型:VIP套餐
         if (SystemConstant.ORDER_ITEM_TYPE_VIP.equals(itemType)) {
-            //2.1 远程调用“用户”服务获取套餐信息
+            //2.1 远程调用用户服务,得到购买VIP会员套餐信息
             VipServiceConfig vipServiceConfig = userFeignClient.getVipServiceConfig(tradeVo.getItemId()).getData();
-            Assert.notNull(vipServiceConfig, "选择套餐:{}有误", tradeVo.getItemId());
-            //2.2 封装价格:原价 减免价 定价价
+            Assert.notNull(vipServiceConfig, "会员套餐{}不存在", tradeVo.getItemId());
+            //2.2 计算商品价格:订单价=原价-减免金额
             originalAmount = vipServiceConfig.getPrice();
             orderAmount = vipServiceConfig.getDiscountPrice();
-            if (orderAmount.compareTo(originalAmount) != 0) {
-                //减免价格=原价-订单价
+            if (originalAmount.compareTo(orderAmount) == 1) {
                 derateAmount = originalAmount.subtract(orderAmount);
             }
-            //2.3 封装订单中商品明细列表
+            //2.3 将得到VIP套餐作为订单明细,封装成订单明细VO对象,加入到订单明细列表
             OrderDetailVo orderDetailVo = new OrderDetailVo();
-            orderDetailVo.setItemId(tradeVo.getItemId());
-            orderDetailVo.setItemName("会员:" + vipServiceConfig.getName());
-            orderDetailVo.setItemPrice(vipServiceConfig.getPrice());
+            orderDetailVo.setItemId(vipServiceConfig.getId());
+            orderDetailVo.setItemName("会员套餐:" + vipServiceConfig.getName());
             orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl());
+            orderDetailVo.setItemPrice(originalAmount);
             orderDetailVoList.add(orderDetailVo);
-            //2.4 封装商品优惠列表
-            if (orderAmount.compareTo(originalAmount) != 0) {
+
+            //2.4 如有优惠金额,封装订单减免vo对象,加入到订单减免列表
+            if (originalAmount.compareTo(orderAmount) == 1) {
                 OrderDerateVo orderDerateVo = new OrderDerateVo();
                 orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT);
                 orderDerateVo.setDerateAmount(derateAmount);
-                orderDerateVo.setRemarks("会员限时优惠:" + derateAmount);
+                orderDerateVo.setRemarks("套餐限时减免:" + derateAmount);
                 orderDerateVoList.add(orderDerateVo);
             }
-
-
         } else if (SystemConstant.ORDER_ITEM_TYPE_ALBUM.equals(itemType)) {
             //3.TODO 处理购买商品类型为:专辑
            
@@ -435,29 +432,38 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
             //4.TODO 处理购买商品类型为:声音
 
         }
-        //5.封装共有订单VO信息:价格、商品及优惠 TODO 、杂项信息(购买项目类型、流水号、时间戳签名)
+        //5.封装订单VO信息共有的信息,价格赋值、商品明细列表赋值、订单减免明细列表赋值、流水号机制、签名机制
+        //5.1 封装第一类信息:三个价格(原价、订单价、减免价)
         orderInfoVo.setOriginalAmount(originalAmount);
         orderInfoVo.setOrderAmount(orderAmount);
         orderInfoVo.setDerateAmount(derateAmount);
+
+        //5.2 封装第二类信息:商品明细列表、优惠列表
         orderInfoVo.setOrderDetailVoList(orderDetailVoList);
         orderInfoVo.setOrderDerateVoList(orderDerateVoList);
-        orderInfoVo.setItemType(itemType);
 
-        //5.1 为本次订单产生唯一流水号且存入Redis,将来在提交订单验证流水号
-        String tradeNo = IdUtil.randomUUID();
+        //5.3 封装第三类信息:购买项目类型、流水号机制(本次订单流水号)、签名机制(时间戳+签名值)
+        //5.3.1 购买项目类型
+        orderInfoVo.setItemType(itemType);
+        //5.3.2 流水号机制第一步:生成本次订单流水号,解决回退、重复点击提交造成订单重复提交问题
+        //5.3.2.1. 构建当前用户本次订单流水号Key
         String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
-        redisTemplate.opsForValue().set(tradeKey, tradeNo, RedisConstant.ORDER_TRADE_EXPIRE, TimeUnit.MINUTES);
+        //5.3.2.2. 构建当前用户本次订单流水号UUID值
+        String tradeNo = IdUtil.randomUUID();
+        //5.3.2.3. 写入Redis有效时间5分钟
+        redisTemplate.opsForValue().set(tradeKey, tradeNo, 5, TimeUnit.MINUTES);
         orderInfoVo.setTradeNo(tradeNo);
 
-        //5.2 生成时间戳及订单签名
-        orderInfoVo.setTimestamp(System.currentTimeMillis());
-        //5.2.1 将订单VO转为Map TODO:将付款方式payWay空值属性排除掉 不需要参与签名计算
+        //5.3.3 签名机制
+        //5.3.3.1. 生成本次订单时间戳
+        long timeMillis = System.currentTimeMillis();
+        orderInfoVo.setTimestamp(timeMillis);
+        //5.3.3.2 将订单VO转为Map,生成本次订单签名值 TODO 由于付款方式属性payWay为null不需要参与签名
         Map<String, Object> orderInfoMap = BeanUtil.beanToMap(orderInfoVo, false, true);
-        //5.2.2 调用工具类生成签名值
         String sign = SignHelper.getSign(orderInfoMap);
+        //5.3.3.3. 封装Vo中签名值
         orderInfoVo.setSign(sign);
-
-        //6.响应订单VO对象
+        //6.响应订单vo,客户端完成订单页面渲染
         return orderInfoVo;
     }
 }
@@ -511,16 +517,18 @@ public class FeignInterceptor implements RequestInterceptor {
 
 ```java
 /**
- * 查询当前用户是否购买指定专辑
+ * 判断当前用户是否购买过指定专辑
+ *
  * @param albumId
- * @return true:已购买 false:未购买
+ * @return
  */
 @GuiGuLogin
-@Operation(summary = "查询当前用户是否购买指定专辑")
+@Operation(summary = "判断当前用户是否购买过指定专辑")
 @GetMapping("/userInfo/isPaidAlbum/{albumId}")
-public Result<Boolean> isPaidAlbum(@PathVariable Long albumId){
-    Boolean isPaid = userInfoService.isPaidAlbum(albumId);
-    return Result.ok(isPaid);
+public Result<Boolean> isPaidAlbum(@PathVariable Long albumId) {
+    Long userId = AuthContextHolder.getUserId();
+    Boolean isPaidAlbum = userInfoService.isPaidAlbum(userId, albumId);
+    return Result.ok(isPaidAlbum);
 }
 ```
 
@@ -539,18 +547,19 @@ Boolean isPaidAlbum(Long albumId);
 
 ```java
 /**
- * 查询当前用户是否购买指定专辑
+ * 判断当前用户是否购买过指定专辑
  *
  * @param albumId
  * @return
  */
 @Override
-public Boolean isPaidAlbum(Long albumId) {
-    Long userId = AuthContextHolder.getUserId();
-    LambdaQueryWrapper<UserPaidAlbum> queryWrapper = new LambdaQueryWrapper<>();
-    queryWrapper.eq(UserPaidAlbum::getUserId, userId);
-    queryWrapper.eq(UserPaidAlbum::getAlbumId, albumId);
-    return userPaidAlbumMapper.selectCount(queryWrapper) > 0;
+public Boolean isPaidAlbum(Long userId, Long albumId) {
+    Long count = userPaidAlbumMapper.selectCount(
+            new LambdaQueryWrapper<UserPaidAlbum>()
+                    .eq(UserPaidAlbum::getUserId, userId)
+                    .eq(UserPaidAlbum::getAlbumId, albumId)
+    );
+    return count > 0;
 }
 ```
 
@@ -558,9 +567,10 @@ public Boolean isPaidAlbum(Long albumId) {
 
 ```java
 /**
- * 查询当前用户是否购买指定专辑
+ * 判断当前用户是否购买过指定专辑
+ *
  * @param albumId
- * @return true:已购买 false:未购买
+ * @return
  */
 @GetMapping("/userInfo/isPaidAlbum/{albumId}")
 public Result<Boolean> isPaidAlbum(@PathVariable Long albumId);
@@ -593,57 +603,58 @@ public Result<Boolean> isPaidAlbum(Long albumId) {
  */
 @Override
 public OrderInfoVo tradeData(Long userId, TradeVo tradeVo) {
-    //1.初始化OrderInfoVo
+    //0.创建订单VO对象
     OrderInfoVo orderInfoVo = new OrderInfoVo();
-    //1.1. 初始化相关金额:0.00 此时金额必须是字符串"0.00"
-    BigDecimal originalAmount = new BigDecimal("0.00");
-    BigDecimal orderAmount = new BigDecimal("0.00");
-    BigDecimal derateAmount = new BigDecimal("0.00");
+    //...省略已写代码
 
-    //1.2. 初始化商品及优惠列表
-    List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
-    List<OrderDerateVo> orderDerateVoList = new ArrayList<>();
-
-    //付款项目类型: 1001-专辑 1002-声音 1003-vip会员
+    //获取购买项目类型 1001-专辑 1002-声音 1003-vip会员
     String itemType = tradeVo.getItemType();
+    
     //2.处理购买商品类型为:VIP套餐
     if (SystemConstant.ORDER_ITEM_TYPE_VIP.equals(itemType)) {
         //2.1...省略代码
 
     } else if (SystemConstant.ORDER_ITEM_TYPE_ALBUM.equals(itemType)) {
-        //3. 处理购买商品类型为:专辑
-        //3.1 远程调用用户服务是否已购专辑
+		//3. 处理购买项目类型:专辑
+        //3.1 远程调用用户服务,判断用户是否已购买过专辑,如果已买则响应已购买
         Long albumId = tradeVo.getItemId();
-        Boolean isPaid = userFeignClient.isPaidAlbum(albumId).getData();
-        if (isPaid) {
-            throw new RuntimeException("用户已购买专辑:" + albumId);
+        Boolean flag = userFeignClient.isPaidAlbum(albumId).getData();
+        if (flag) {
+            throw new GuiguException(500, "用户已购买专辑");
         }
-        //3.2 远程调用"专辑"服务获取专辑信息
+
+        //3.2 远程调用专辑服务,获取专辑价格以及相关折扣(普通、VIP折扣)
         AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(albumId).getData();
-        Assert.notNull(albumInfo, "专辑:{}不存在", albumId);
+        Assert.notNull(albumInfo, "专辑{}不存在", albumId);
+        BigDecimal price = albumInfo.getPrice();
+        BigDecimal discount = albumInfo.getDiscount();
+        BigDecimal vipDiscount = albumInfo.getVipDiscount();
 
-        //3.3 远程调用用户服务获取用户身份,封装"商品"相关价格
-        UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(userId).getData();
+        //3.3 远程调用用户服务,判断当前用户是否是会员
         Boolean isVIP = false;
-        if (1 == userInfoVo.getIsVip().intValue() && userInfoVo.getVipExpireTime().after(new Date())) {
+
+        UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(userId).getData();
+        Assert.notNull(userInfoVo, "用户{}不存在", userId);
+        if (userInfoVo.getIsVip().intValue() == 1 && userInfoVo.getVipExpireTime().after(new Date())) {
             isVIP = true;
         }
-        originalAmount = albumInfo.getPrice();
-        orderAmount = originalAmount;
-        //减免价=原价*折扣
-        if (!isVIP && albumInfo.getDiscount().intValue() != -1) {
-            //普通用户 且 专辑有普通用户折扣
-            orderAmount = originalAmount.multiply(albumInfo.getDiscount()).divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
-        }
 
-        if (isVIP && albumInfo.getVipDiscount().intValue() != -1) {
-            //VIP用户 且 专辑有VIP用户折扣
-            orderAmount = originalAmount.multiply(albumInfo.getVipDiscount()).divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
+        //3.4 基于用户角色专辑折扣计算专辑价格(原价、订单价、减免价格)
+        originalAmount = price;
+        orderAmount = originalAmount;
+        if (!isVIP && discount.doubleValue() != -1) {
+            //专辑价格 普通用户折扣  订单价格=原价*折扣
+            orderAmount =
+                    originalAmount.multiply(discount).divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
         }
-        if (orderAmount.compareTo(originalAmount) != 0) {
-            derateAmount = originalAmount.subtract(orderAmount);
+        if (isVIP && vipDiscount.doubleValue() != -1) {
+            //专辑价格 VIP会员折扣  订单价格=原价*折扣
+            orderAmount =
+                    originalAmount.multiply(vipDiscount).divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
         }
-        //3.4 封装"商品"列表及商品优惠列表
+        //计算专辑价格减免金额 = 原价-订单价格
+        derateAmount = originalAmount.subtract(orderAmount);
+        //3.5 将专辑信息作为订单明细,封装成订单明细VO对象,加入到订单明细列表
         OrderDetailVo orderDetailVo = new OrderDetailVo();
         orderDetailVo.setItemId(albumId);
         orderDetailVo.setItemName("专辑:" + albumInfo.getAlbumTitle());
@@ -651,40 +662,19 @@ public OrderInfoVo tradeData(Long userId, TradeVo tradeVo) {
         orderDetailVo.setItemPrice(originalAmount);
         orderDetailVoList.add(orderDetailVo);
 
-        if (orderAmount.compareTo(originalAmount) != 0) {
+        //3.6 如有优惠金额,封装订单减免vo对象,加入到订单减免列表
+        if (originalAmount.compareTo(orderAmount) == 1) {
             OrderDerateVo orderDerateVo = new OrderDerateVo();
             orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_ALBUM_DISCOUNT);
             orderDerateVo.setDerateAmount(derateAmount);
-            orderDerateVo.setRemarks("专辑限时优惠:" + derateAmount);
+            orderDerateVo.setRemarks("专辑折扣减免:" + derateAmount);
             orderDerateVoList.add(orderDerateVo);
         }
     } else if (SystemConstant.ORDER_ITEM_TYPE_TRACK.equals(itemType)) {
         //4.TODO 处理购买商品类型为:声音
 
     }
-    //5.封装共有订单VO信息:价格、商品及优惠 TODO 、杂项信息(购买项目类型、流水号、时间戳签名)
-    orderInfoVo.setOriginalAmount(originalAmount);
-    orderInfoVo.setOrderAmount(orderAmount);
-    orderInfoVo.setDerateAmount(derateAmount);
-    orderInfoVo.setOrderDetailVoList(orderDetailVoList);
-    orderInfoVo.setOrderDerateVoList(orderDerateVoList);
-    orderInfoVo.setItemType(itemType);
-
-    //5.1 为本次订单产生唯一流水号且存入Redis,将来在提交订单验证流水号
-    String tradeNo = IdUtil.randomUUID();
-    String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
-    redisTemplate.opsForValue().set(tradeKey, tradeNo, RedisConstant.ORDER_TRADE_EXPIRE, TimeUnit.MINUTES);
-    orderInfoVo.setTradeNo(tradeNo);
-
-    //5.2 生成时间戳及订单签名
-    orderInfoVo.setTimestamp(System.currentTimeMillis());
-    //5.2.1 将订单VO转为Map TODO:将付款方式payWay空值属性排除掉 不需要参与签名计算
-    Map<String, Object> orderInfoMap = BeanUtil.beanToMap(orderInfoVo, false, true);
-    //5.2.2 调用工具类生成签名值
-    String sign = SignHelper.getSign(orderInfoMap);
-    orderInfoVo.setSign(sign);
-
-    //6.响应订单VO对象
+    //5.省略已写代码
     return orderInfoVo;
 }
 ```
@@ -706,18 +696,16 @@ public OrderInfoVo tradeData(Long userId, TradeVo tradeVo) {
 ```java
 /**
  * 查询当前用户某个专辑下已购声音ID列表
- * @param albumId 专辑ID
- * @return 已购声音ID列表
+ * @param albumId
+ * @return
  */
 @GuiGuLogin
 @Operation(summary = "查询当前用户某个专辑下已购声音ID列表")
 @GetMapping("/userInfo/findUserPaidTrackList/{albumId}")
-public Result<List<Long>> getUserPaidTrackIdList(@PathVariable Long albumId){
-    //1.获取当前登录用户ID
+public Result<List<Long>> findUserPaidTrackList(@PathVariable Long albumId) {
     Long userId = AuthContextHolder.getUserId();
-    //2.调用业务逻辑获取已购声音ID列表
-    List<Long> trackIdList = userInfoService.getUserPaidTrackIdList(userId, albumId);
-    return Result.ok(trackIdList);
+    List<Long> list = userInfoService.findUserPaidTrackList(userId, albumId);
+    return Result.ok(list);
 }
 ```
 
@@ -726,10 +714,10 @@ public Result<List<Long>> getUserPaidTrackIdList(@PathVariable Long albumId){
 ```java
 /**
  * 查询当前用户某个专辑下已购声音ID列表
- * @param albumId 专辑ID
- * @return 已购声音ID列表
+ * @param albumId
+ * @return
  */
-List<Long> getUserPaidTrackIdList(Long userId, Long albumId);
+List<Long> findUserPaidTrackList(Long userId, Long albumId);
 ```
 
 ##### **UserInfoServiceImpl**实现类
@@ -737,19 +725,23 @@ List<Long> getUserPaidTrackIdList(Long userId, Long albumId);
 ```java
 /**
  * 查询当前用户某个专辑下已购声音ID列表
- * @param albumId 专辑ID
- * @return 已购声音ID列表
+ *
+ * @param albumId
+ * @return
  */
 @Override
-public List<Long> getUserPaidTrackIdList(Long userId, Long albumId) {
-    LambdaQueryWrapper<UserPaidTrack> queryWrapper = new LambdaQueryWrapper<>();
-    queryWrapper.eq(UserPaidTrack::getUserId, userId);
-    queryWrapper.eq(UserPaidTrack::getAlbumId, albumId);
-    //只需要获取已购声音ID
-    queryWrapper.select(UserPaidTrack::getTrackId);
-    List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(queryWrapper);
-    if(CollectionUtil.isNotEmpty(userPaidTrackList)){
-        return userPaidTrackList.stream().map(UserPaidTrack::getTrackId).collect(Collectors.toList());
+public List<Long> findUserPaidTrackList(Long userId, Long albumId) {
+    List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(
+            new LambdaQueryWrapper<UserPaidTrack>()
+                    .eq(UserPaidTrack::getUserId, userId)
+                    .eq(UserPaidTrack::getAlbumId, albumId)
+                    .select(UserPaidTrack::getTrackId)
+    );
+    if (CollUtil.isNotEmpty(userPaidTrackList)) {
+        List<Long> userPaidTrackIdList = userPaidTrackList.stream()
+                .map(UserPaidTrack::getTrackId)
+                .collect(Collectors.toList());
+        return userPaidTrackIdList;
     }
     return null;
 }
@@ -760,19 +752,19 @@ public List<Long> getUserPaidTrackIdList(Long userId, Long albumId) {
 ```java
 /**
  * 查询当前用户某个专辑下已购声音ID列表
- * @param albumId 专辑ID
- * @return 已购声音ID列表
+ * @param albumId
+ * @return
  */
 @GetMapping("/userInfo/findUserPaidTrackList/{albumId}")
-public Result<List<Long>> getUserPaidTrackIdList(@PathVariable Long albumId);
+public Result<List<Long>> findUserPaidTrackList(@PathVariable Long albumId);
 ```
 
 **UserDegradeFeignClient**熔断类
 
 ```java
 @Override
-public Result<List<Long>> getUserPaidTrackIdList(Long albumId) {
-    log.error("[用户服务]远程调用getuserPaidTrackIdList执行服务降级");
+public Result<List<Long>> findUserPaidTrackList(Long albumId) {
+    log.error("[用户服务]远程调用findUserPaidTrackList执行服务降级");
     return null;
 }
 ```
@@ -820,12 +812,12 @@ public Result<List<Map<String, Object>>> getUserTrackPaidList(@PathVariable Long
 
 ```java
 /**
- * 基于当前选择购买起始声音得到分集购买列表
+ * 基于当前用户未购买声音数量动态渲染分集购买列表
  * @param userId 用户ID
- * @param trackId 选择购买声音ID(起始计算参照)
+ * @param trackId 选择购买声音ID,作为起始标准计算未购买声音数量
  * @return
  */
-List<Map<String, Object>> getUserTrackPaidList(Long userId, Long trackId);
+List<Map<String, Object>> findUserTrackPaidList(Long userId, Long trackId);
 ```
 
 ##### AlbumInfoServiceImpl实现类
@@ -841,61 +833,58 @@ List<Map<String, Object>> getUserTrackPaidList(Long userId, Long trackId);
 
 ```java
 /**
- * 基于当前选择购买起始声音得到分集购买列表
+ * 基于当前用户未购买声音数量动态渲染分集购买列表
  *
  * @param userId  用户ID
- * @param trackId 选择购买声音ID(起始计算参照)
- * @return [{name:"本集",price:1,trackCount:1}]
+ * @param trackId 选择购买声音ID,作为起始标准计算未购买声音数量
+ * @return [{name:"本集",price:0.1,trackCount:1},{name:"后10集",price:1,trackCount:10}..,{name:"全集",price:3.8,trackCount:38}]
  */
 @Override
-public List<Map<String, Object>> getUserTrackPaidList(Long userId, Long trackId) {
-    //1.创建分集购买列表
-    List<Map<String, Object>> list = new ArrayList<>();
-
-    //2.根据声音ID查询到“起始”待购声音记录,得到当前声音徐浩、所属专辑ID
+public List<Map<String, Object>> findUserTrackPaidList(Long userId, Long trackId) {
+    //1.根据选择购买声音ID查询声音记录,得到声音序号,得到专辑ID
     TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
     Integer orderNum = trackInfo.getOrderNum();
     Long albumId = trackInfo.getAlbumId();
-
-    //3.根据"起始"声音序号获取所有待购买声音列表(可能包含用户已购声音)
-    LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<>();
-    queryWrapper.eq(TrackInfo::getAlbumId, albumId);
-    queryWrapper.ge(TrackInfo::getOrderNum, orderNum);
-    queryWrapper.select(TrackInfo::getId);
-    List<TrackInfo> trackInfoList = trackInfoMapper.selectList(queryWrapper);
-
-    //4.远程调用"用户服务"获取已购买声音ID列表
-    List<Long> userPaidTrackIdList = userFeignClient.getUserPaidTrackIdList(albumId).getData();
-
-    //5.如果存在已购声音ID,将已购声音ID排除掉得到真正未购买声音列表
-    if (CollectionUtil.isNotEmpty(userPaidTrackIdList)) {
-        trackInfoList = trackInfoList.stream()
-                .filter(track -> !userPaidTrackIdList.contains(track.getId())).collect(Collectors.toList());
+    //2.根据"起始声音"需要+专辑ID查询声音表得到“未购买声音列表”,这里可能包含已购买声音
+    List<TrackInfo> waitBuyTrackList = trackInfoMapper.selectList(
+            new LambdaQueryWrapper<TrackInfo>()
+                    .eq(TrackInfo::getAlbumId, albumId)
+                    .ge(TrackInfo::getOrderNum, orderNum)
+                    .select(TrackInfo::getId)
+    );
+
+    //3.远程调用用户服务,得到用户已购声音ID列表,如果有值将已购买声音ID排除掉
+    List<Long> userPaidTrackIdList = userFeignClient.findUserPaidTrackList(albumId).getData();
+    if (CollUtil.isNotEmpty(userPaidTrackIdList)) {
+        waitBuyTrackList =
+                waitBuyTrackList.stream()
+                        .filter(t -> !userPaidTrackIdList.contains(t.getId()))
+                        .collect(Collectors.toList());
     }
 
-    //6.根据未购买声音列表长度动态构建分集购买列表
-    //6.0 根据专辑ID查询专辑价格=声音价格
-    BigDecimal price = albumInfoMapper.selectById(albumId).getPrice();
-    //6.1 构建"本集"分集购买对象
+    //4.基于未购买声音数量,动态封装分集购买列表
+    int size = waitBuyTrackList.size();
+    List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
+    //4.0 查询专辑记录得到单集价格
+    AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
+    BigDecimal price = albumInfo.getPrice();
+    ///4.1 创建"本集"分集购买对象
     Map<String, Object> currMap = new HashMap<>();
     currMap.put("name", "本集");
     currMap.put("price", price);
     currMap.put("trackCount", 1);
     list.add(currMap);
-    //6.2 根据未购买声音数量循环最多构建6个分集购买对象(50集)
-    int size = trackInfoList.size();
-    //例如:size=12 展示 后10集,全集     size=35 展示 后10集 后20集 后30集 全集
-    for (int i = 10; i <= 50; i += 10) {
-        if (size > i) {
-            Map<String, Object> map = new HashMap<>();
+    //4.2 创建"后i*10集"分集购买对象
+    for (int i = 10; i < 50; i += 10) {
+        Map<String, Object> map = new HashMap<>();
+        if (i < size) {
             map.put("name", "后" + i + "集");
-            map.put("price", price.multiply(BigDecimal.valueOf(i)));
+            map.put("price", price.multiply(new BigDecimal(i)));
             map.put("trackCount", i);
             list.add(map);
         } else {
-            Map<String, Object> map = new HashMap<>();
-            map.put("name", "全集(" + size + "集)");
-            map.put("price", price.multiply(BigDecimal.valueOf(size)));
+            map.put("name", "全集(后" + size + "集)");
+            map.put("price", price.multiply(new BigDecimal(size)));
             map.put("trackCount", size);
             list.add(map);
             break;
@@ -915,20 +904,17 @@ public List<Map<String, Object>> getUserTrackPaidList(Long userId, Long trackId)
 
 ```java
 /**
- * 查询当前用户未购买声音列表
- *
- * @param trackId    点击付费标识声音ID,将该声音作为起始标准
- * @param trackCount 购买数量
- * @return 待购声音列表
+ *  查询用户未购买声音列表
+ * @param trackId 选择购买声音ID,作为起始标准计算未购买声音数量
+ * @param trackCount 限制查询未购买声音数量
+ * @return
  */
 @GuiGuLogin
-@Operation(summary = "查询当前用户未购买声音列表")
+@Operation(summary = "查询用户未购买声音列表")
 @GetMapping("/trackInfo/findPaidTrackInfoList/{trackId}/{trackCount}")
-public Result<List<TrackInfo>> getWaitBuyTrackList(@PathVariable Long trackId, @PathVariable Integer trackCount) {
-    //1.从ThreadLocal中获取当前登录用户ID
+public Result<List<TrackInfo>> findWaitBuyTrackInfoList(@PathVariable Long trackId, @PathVariable Integer trackCount) {
     Long userId = AuthContextHolder.getUserId();
-    //2.调用业务逻辑获取待购声音列表
-    List<TrackInfo> list = albumInfoService.getWaitBuyTrackList(userId, trackId, trackCount);
+    List<TrackInfo> list = trackInfoService.findWaitBuyTrackInfoList(userId, trackId, trackCount);
     return Result.ok(list);
 }
 ```
@@ -937,13 +923,12 @@ public Result<List<TrackInfo>> getWaitBuyTrackList(@PathVariable Long trackId, @
 
 ```java
 /**
- * 查询当前用户未购买声音列表
- * @param userId   用户ID
- * @param trackId 点击付费标识声音ID,将该声音作为起始标准
- * @param trackCount 购买数量
- * @return 待购声音列表
+ *  查询用户未购买声音列表
+ * @param trackId 选择购买声音ID,作为起始标准计算未购买声音数量
+ * @param trackCount 限制查询未购买声音数量
+ * @return
  */
-List<TrackInfo> getWaitBuyTrackList(Long userId, Long trackId, Integer trackCount);
+List<TrackInfo> findWaitBuyTrackInfoList(Long userId, Long trackId, Integer trackCount);
 ```
 
 #### 实现类
@@ -957,41 +942,34 @@ List<TrackInfo> getWaitBuyTrackList(Long userId, Long trackId, Integer trackCoun
 
 ```java
 /**
- * 查询当前用户未购买声音列表
+ * 查询用户未购买声音列表
  *
- * @param userId     用户ID
- * @param trackId    点击付费标识声音ID,将该声音作为起始标准
- * @param trackCount 购买数量
- * @return 待购声音列表
+ * @param trackId    选择购买声音ID,作为起始标准计算未购买声音数量
+ * @param trackCount 限制查询未购买声音数量
+ * @return
  */
 @Override
-public List<TrackInfo> getWaitBuyTrackList(Long userId, Long trackId, Integer trackCount) {
-    //1.根据声音ID查询到“起始”待购声音记录,得到当前声音序号、所属专辑ID
+public List<TrackInfo> findWaitBuyTrackInfoList(Long userId, Long trackId, Integer trackCount) {
+    //1.根据声音ID查询到“起始”待购声音记录-得到专辑ID及序号
     TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
     Long albumId = trackInfo.getAlbumId();
     Integer orderNum = trackInfo.getOrderNum();
 
     //2.远程调用用户服务获取已购买声音ID列表
-    List<Long> userPaidTrackIdList = userFeignClient.getUserPaidTrackIdList(albumId).getData();
-
-    //3.获取待购声音列表 查询条件:专辑ID,序号,已购声音ID 。排序字段:序号  。返回数量:购买数量
-    LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<>();
-    //3.1 等值查询专辑标题
-    queryWrapper.eq(TrackInfo::getAlbumId, albumId);
-    //3.2 大于等于当前选购声音序号
-    queryWrapper.ge(TrackInfo::getOrderNum, orderNum);
-    if (CollectionUtil.isNotEmpty(userPaidTrackIdList)) {
-        //3.3 如果存在已购买声音,去除已购买声音ID
-        queryWrapper.notIn(TrackInfo::getId, userPaidTrackIdList);
+    List<Long> userPaidTrackList = userFeignClient.findUserPaidTrackList(albumId).getData();
+
+    //3.构建查询条件查询未购买声音列表
+    LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<TrackInfo>()
+            .eq(TrackInfo::getAlbumId, albumId)
+            .ge(TrackInfo::getOrderNum, orderNum)
+            .orderByAsc(TrackInfo::getOrderNum)
+            .select(TrackInfo::getId, TrackInfo::getTrackTitle, TrackInfo::getCoverUrl, TrackInfo::getAlbumId)
+            .last("limit " + trackCount);
+    if (CollUtil.isNotEmpty(userPaidTrackList)) {
+        queryWrapper.notIn(TrackInfo::getId, userPaidTrackList);
     }
-    //3.4 按照声音序号升序
-    queryWrapper.orderByAsc(TrackInfo::getOrderNum);
-    //3.5 按用户选择购买数量获取声音列表
-    queryWrapper.last("LIMIT " + trackCount);
-    //3.6 执行查询列:多查询了专辑ID,需要在订单服务进行算价格需要获取专辑中价格
-    queryWrapper.select(TrackInfo::getId, TrackInfo::getTrackTitle, TrackInfo::getCoverUrl, TrackInfo::getAlbumId);
-    List<TrackInfo> list = trackInfoMapper.selectList(queryWrapper);
-    return list;
+    List<TrackInfo> trackInfoList = trackInfoMapper.selectList(queryWrapper);
+    return trackInfoList;
 }
 ```
 
@@ -999,22 +977,21 @@ public List<TrackInfo> getWaitBuyTrackList(Long userId, Long trackId, Integer tr
 
 ```java
 /**
- * 查询当前用户未购买声音列表
- *
- * @param trackId    点击付费标识声音ID,将该声音作为起始标准
- * @param trackCount 购买数量
- * @return 待购声音列表
+ *  查询用户未购买声音列表
+ * @param trackId 选择购买声音ID,作为起始标准计算未购买声音数量
+ * @param trackCount 限制查询未购买声音数量
+ * @return
  */
 @GetMapping("/trackInfo/findPaidTrackInfoList/{trackId}/{trackCount}")
-public Result<List<TrackInfo>> getWaitBuyTrackList(@PathVariable Long trackId, @PathVariable Integer trackCount);
+public Result<List<TrackInfo>> findWaitBuyTrackInfoList(@PathVariable Long trackId, @PathVariable Integer trackCount);
 ```
 
 熔断类:
 
 ```java
 @Override
-public Result<List<TrackInfo>> getWaitBuyTrackList(Long trackId, Integer trackCount) {
-    log.error("[专辑服务]远程调用getWaitBuyTrackList执行服务降级");
+public Result<List<TrackInfo>> findWaitBuyTrackInfoList(Long trackId, Integer trackCount) {
+    log.error("【专辑服务】提供findWaitBuyTrackInfoList远程调用失败");
     return null;
 }
 ```
@@ -1023,85 +1000,57 @@ public Result<List<TrackInfo>> getWaitBuyTrackList(Long trackId, Integer trackCo
 
 ```java
 /**
- * 订单确认数据汇总
+ * 三种商品(VIP会员、专辑、声音)订单结算,渲染订单结算页面
  *
- * @param userId  用户ID
- * @param tradeVo 选择购买商品交易vo对象
- * @return
+ * @param tradeVo (购买项目类型、购买项目ID、声音数量)
+ * @return 订单VO信息
  */
 @Override
-public OrderInfoVo tradeData(Long userId, TradeVo tradeVo) {
-    //1.初始化OrderInfoVo
-    OrderInfoVo orderInfoVo = new OrderInfoVo();
-    //1.1. 初始化相关金额:0.00 此时金额必须是字符串"0.00"
-    BigDecimal originalAmount = new BigDecimal("0.00");
-    BigDecimal orderAmount = new BigDecimal("0.00");
-    BigDecimal derateAmount = new BigDecimal("0.00");
-
-    //1.2. 初始化商品及优惠列表
-    List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
-    List<OrderDerateVo> orderDerateVoList = new ArrayList<>();
-
-    //付款项目类型: 1001-专辑 1002-声音 1003-vip会员
+public OrderInfoVo trade(TradeVo tradeVo) {
+    //0.创建订单VO对象
+    //...省略已有代码
+    //获取购买项目类型 1001-专辑 1002-声音 1003-vip会员
     String itemType = tradeVo.getItemType();
-    //2.处理购买商品类型为:VIP套餐
+    //2.处理购买项目类型:VIP套餐
     if (SystemConstant.ORDER_ITEM_TYPE_VIP.equals(itemType)) {
-        //2.1 ...省略
+        //...省略已有代码
     } else if (SystemConstant.ORDER_ITEM_TYPE_ALBUM.equals(itemType)) {
-        //3. 处理购买商品类型为:专辑 ...省略
+        //3. 处理购买项目类型:专辑
+        //...省略已有代码
     } else if (SystemConstant.ORDER_ITEM_TYPE_TRACK.equals(itemType)) {
-        //4.TODO 处理购买商品类型为:声音
-        Long trackId = tradeVo.getItemId();
-        Integer trackCount = tradeVo.getTrackCount();
-        //4.1 远程调用"专辑服务"获取待购买声音列表
-        List<TrackInfo> waitBuyTrackList = albumFeignClient.getWaitBuyTrackList(trackId, trackCount).getData();
-        Assert.notNull(waitBuyTrackList, "不存在待结算声音");
+        //4.处理购买项目类型:声音
 
-        //4.2 远程调用专辑服务得到专辑信息-得到声音单价
-        Long albumId = waitBuyTrackList.get(0).getAlbumId();
-        AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(albumId).getData();
-        Assert.notNull(albumInfo, "专辑:{}不存在", albumId);
+        //4.1 远程调用专辑服务获取待购买声音列表
+        Long trackId = tradeVo.getItemId();
+        List<TrackInfo> trackInfoList = albumFeignClient.findWaitBuyTrackInfoList(trackId, tradeVo.getTrackCount()).getData();
+        Assert.notNull(trackInfoList, "没有需要结算声音");
 
-        //4.3 封装"商品"相关价格 声音单价*列表长度 注意:声音不支持折扣
+        //4.2 远程调用专辑服务获取声音单价-声音不支持折扣
+        AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(trackInfoList.get(0).getAlbumId()).getData();
+        Assert.notNull(albumInfo, "专辑{}不存在", trackInfoList.get(0).getAlbumId());
         BigDecimal price = albumInfo.getPrice();
-        originalAmount = price.multiply(BigDecimal.valueOf(waitBuyTrackList.size()));
+
+        //4.3 封装相关价格
+        originalAmount = price.multiply(new BigDecimal(trackInfoList.size()));
         orderAmount = originalAmount;
 
-        //4.4 封装"商品"列表 将集合泛型从TrackInfo转为OrderDetailVo
-        orderDetailVoList = waitBuyTrackList
+        //4.4 将待购买声音作为订单明细,封装成订单明细VO对象,加入到订单明细列表
+        orderDetailVoList = trackInfoList
                 .stream()
                 .map(trackInfo -> {
                     OrderDetailVo orderDetailVo = new OrderDetailVo();
                     orderDetailVo.setItemId(trackInfo.getId());
-                    orderDetailVo.setItemName(trackInfo.getTrackTitle());
+                    orderDetailVo.setItemName("声音:" + trackInfo.getTrackTitle());
                     orderDetailVo.setItemUrl(trackInfo.getCoverUrl());
                     orderDetailVo.setItemPrice(price);
                     return orderDetailVo;
                 }).collect(Collectors.toList());
+
     }
-    //5.封装共有订单VO信息:价格、商品及优惠 TODO 、杂项信息(购买项目类型、流水号、时间戳签名)
-    orderInfoVo.setOriginalAmount(originalAmount);
-    orderInfoVo.setOrderAmount(orderAmount);
-    orderInfoVo.setDerateAmount(derateAmount);
-    orderInfoVo.setOrderDetailVoList(orderDetailVoList);
-    orderInfoVo.setOrderDerateVoList(orderDerateVoList);
-    orderInfoVo.setItemType(itemType);
-
-    //5.1 为本次订单产生唯一流水号且存入Redis,将来在提交订单验证流水号
-    String tradeNo = IdUtil.randomUUID();
-    String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
-    redisTemplate.opsForValue().set(tradeKey, tradeNo, RedisConstant.ORDER_TRADE_EXPIRE, TimeUnit.MINUTES);
-    orderInfoVo.setTradeNo(tradeNo);
-
-    //5.2 生成时间戳及订单签名
-    orderInfoVo.setTimestamp(System.currentTimeMillis());
-    //5.2.1 将订单VO转为Map TODO:将付款方式payWay空值属性排除掉 不需要参与签名计算
-    Map<String, Object> orderInfoMap = BeanUtil.beanToMap(orderInfoVo, false, true);
-    //5.2.2 调用工具类生成签名值
-    String sign = SignHelper.getSign(orderInfoMap);
-    orderInfoVo.setSign(sign);
-
-    //6.响应订单VO对象
+
+    //5.封装订单VO信息共有的信息,价格赋值、商品明细列表赋值、订单减免明细列表赋值、流水号机制、签名机制
+    //...省略已有代码
+    //6.响应订单vo,客户端完成订单页面渲染
     return orderInfoVo;
 }
 ```