Quellcode durchsuchen

fix:优化订单发货逻辑使其支持一单分发

puhui999 vor 1 Jahr
Ursprung
Commit
8d13ecc9c8
10 geänderte Dateien mit 229 neuen und 25 gelöschten Zeilen
  1. 8 0
      yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApi.java
  2. 5 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApiImpl.java
  3. 8 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityService.java
  4. 6 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityServiceImpl.java
  5. 7 4
      yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
  6. 7 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderDeliveryReqVO.java
  7. 13 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java
  8. 60 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDeliveryDO.java
  9. 23 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderDeliveryMapper.java
  10. 92 21
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java

+ 8 - 0
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApi.java

@@ -18,6 +18,14 @@ public interface CombinationApi {
      */
     void createRecord(@Valid CombinationRecordReqDTO reqDTO);
 
+    /**
+     * 获取开团记录状态
+     *
+     * @param userId  用户编号
+     * @param orderId 订单编号
+     */
+    Integer getRecordStatus(Long userId, Long orderId);
+
     /**
      * 更新开团记录状态
      *

+ 5 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApiImpl.java

@@ -22,6 +22,11 @@ public class CombinationApiImpl implements CombinationApi {
         activityService.createRecord(reqDTO);
     }
 
+    @Override
+    public Integer getRecordStatus(Long userId, Long orderId) {
+        return activityService.getRecordStatus(userId, orderId);
+    }
+
     @Override
     public void updateRecordStatus(Long userId, Long orderId, Integer status) {
         activityService.updateRecordStatusByUserIdAndOrderId(userId, orderId, status);

+ 8 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityService.java

@@ -109,4 +109,12 @@ public interface CombinationActivityService {
      */
     void createRecord(CombinationRecordReqDTO reqDTO);
 
+    /**
+     * 获得拼团状态
+     *
+     * @param userId  用户编号
+     * @param orderId 订单编号
+     * @return 拼团状态
+     */
+    Integer getRecordStatus(Long userId, Long orderId);
 }

+ 6 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityServiceImpl.java

@@ -248,4 +248,10 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
         recordMapper.insert(recordDO);
     }
 
+    @Override
+    public Integer getRecordStatus(Long userId, Long orderId) {
+        CombinationRecordDO recordDO = validateCombinationRecord(userId, orderId);
+        return recordDO.getStatus();
+    }
+
 }

+ 7 - 4
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java

@@ -28,8 +28,9 @@ public interface ErrorCodeConstants {
     ErrorCode ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED = new ErrorCode(1011000017, "交易订单发货失败,订单不是【待发货】状态");
     ErrorCode ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED = new ErrorCode(1011000018, "交易订单收货失败,订单不是【待收货】状态");
     ErrorCode ORDER_COMMENT_FAIL_STATUS_NOT_COMPLETED = new ErrorCode(1011000019, "创建交易订单项的评价失败,订单不是【已完成】状态");
-    ErrorCode ORDER_COMMENT_STATUS_NOT_FALSE = new ErrorCode(1011000019, "创建交易订单项的评价失败,订单已评价");
-    ErrorCode ORDER_DELIVERY_FAIL_REFUND_STATUS_NOT_NONE = new ErrorCode(1011000017, "交易订单发货失败,订单已退款或部分退款");
+    ErrorCode ORDER_COMMENT_STATUS_NOT_FALSE = new ErrorCode(1011000020, "创建交易订单项的评价失败,订单已评价");
+    ErrorCode ORDER_DELIVERY_FAIL_REFUND_STATUS_NOT_NONE = new ErrorCode(1011000021, "交易订单发货失败,订单已退款或部分退款");
+    ErrorCode ORDER_DELIVERY_FAIL_COMBINATION_RECORD_STATUS_NOT_SUCCESS = new ErrorCode(1011000022, "交易订单发货失败,拼团未成功");
 
     // ==========  After Sale 模块 1011000100 ==========
     ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1011000100, "售后单不存在");
@@ -66,8 +67,10 @@ public interface ErrorCodeConstants {
     ErrorCode EXPRESS_TEMPLATE_NOT_EXISTS = new ErrorCode(1011005001, "运费模板不存在");
 
     // ==========  物流 PICK_UP 模块 1011006000 ==========
-
     ErrorCode PICK_UP_STORE_NOT_EXISTS = new ErrorCode(1011006000, "自提门店不存在");
 
-
+    // ==========  物流 PICK_UP 模块 1011007000 ==========
+    ErrorCode ORDER_DELIVERY_FAILED_ITEMS_NOT_EMPTY = new ErrorCode(1011007000, "订单发货失败,请选择发货商品");
+    ErrorCode ORDER_DELIVERY_FAILED_ITEM_NOT_EXISTS = new ErrorCode(1011007001, "订单发货失败,所选发货商品不存在");
+    ErrorCode ORDER_DELIVERY_FAILED_ITEM_ALREADY_DELIVERY = new ErrorCode(1011007001, "订单发货失败,所选商品已发货");
 }

+ 7 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderDeliveryReqVO.java

@@ -6,6 +6,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
 import javax.validation.constraints.NotNull;
+import java.util.List;
 
 @Schema(description = "管理后台 - 订单发货 Request VO")
 @Data
@@ -26,6 +27,12 @@ public class TradeOrderDeliveryReqVO {
     @Schema(description = "发货物流单号", example = "SF123456789")
     private String logisticsNo;
 
+    // TODO 订单项商品单独发货
+
+    @Schema(description = "发货订单项", example = "[1,2,3]")
+    @NotNull(message = "发货订单项不能为空")
+    private List<Long> orderItemIds;
+
     // =============== 同城配送  ================
     // TODO
 

+ 13 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java

@@ -18,6 +18,7 @@ import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO;
 import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;
 import cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO;
 import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;
+import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDetailRespVO;
 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageItemRespVO;
 import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;
@@ -26,6 +27,7 @@ import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderI
 import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO;
 import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO;
 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
+import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDeliveryDO;
 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
@@ -334,4 +336,15 @@ public interface TradeOrderConvert {
     })
     CombinationRecordReqDTO convert(TradeOrderDO order, TradeOrderItemDO orderItem, AppTradeOrderCreateReqVO createReqVO, MemberUserRespDTO user);
 
+    TradeOrderDeliveryDO covert(Long orderId, Long orderItemId, Long userId, Integer deliveryType, Long logisticsId, String logisticsNo);
+
+    default List<TradeOrderDeliveryDO> covert(TradeOrderDO order, TradeOrderDeliveryReqVO deliveryReqVO) {
+        ArrayList<TradeOrderDeliveryDO> arrayList = new ArrayList<>();
+        deliveryReqVO.getOrderItemIds().forEach(item -> {
+            arrayList.add(covert(order.getId(), item, order.getUserId(), deliveryReqVO.getType(),
+                    deliveryReqVO.getLogisticsId(), deliveryReqVO.getLogisticsNo()));
+        });
+        return arrayList;
+    }
+
 }

+ 60 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDeliveryDO.java

@@ -0,0 +1,60 @@
+package cn.iocoder.yudao.module.trade.dal.dataobject.order;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+
+/**
+ * 交易订单发货记录 DO
+ *
+ * @author HUIHUI
+ */
+@TableName("trade_order_delivery")
+@KeySequence("trade_order_delivery_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class TradeOrderDeliveryDO extends BaseDO {
+
+    /**
+     * 订单发货记录 id
+     */
+    private Long id;
+    /**
+     * 订单 id
+     */
+    private Long orderId;
+    /**
+     * 订单项 id TODO 要不要一个发货记录可对应多个订单项?
+     */
+    private Long orderItemId;
+    /**
+     * 用户编号
+     *
+     * 关联 MemberUserDO 的 id 编号
+     */
+    private Long userId;
+    /**
+     * 配送方式
+     *
+     * 枚举 {@link DeliveryTypeEnum}
+     */
+    private Integer deliveryType;
+    /**
+     * 发货物流公司编号
+     */
+    private Long logisticsId;
+    /**
+     * 发货物流单号
+     */
+    private String logisticsNo;
+
+    // TODO 同城配送
+
+}

+ 23 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderDeliveryMapper.java

@@ -0,0 +1,23 @@
+package cn.iocoder.yudao.module.trade.dal.mysql.order;
+
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDeliveryDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 交易订单发货记录 Mapper
+ *
+ * @author HUIHUI
+ */
+@Mapper
+public interface TradeOrderDeliveryMapper extends BaseMapperX<TradeOrderDeliveryDO> {
+
+    default List<TradeOrderDeliveryDO> selsectListByOrderIdAndItemIds(Long orderId, List<Long> orderItemIds) {
+        return selectList(new LambdaQueryWrapperX<TradeOrderDeliveryDO>()
+                .eq(TradeOrderDeliveryDO::getOrderId, orderId).in(TradeOrderDeliveryDO::getOrderItemId, orderItemIds));
+    }
+
+}

+ 92 - 21
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java

@@ -38,7 +38,9 @@ import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
 import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO;
 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
+import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDeliveryDO;
 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
+import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderDeliveryMapper;
 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper;
 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
 import cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants;
@@ -107,6 +109,8 @@ public class TradeOrderServiceImpl implements TradeOrderService {
 
     @Resource
     private CombinationApi combinationApi;
+    @Resource
+    private TradeOrderDeliveryMapper orderDeliveryMapper;
     // =================== Order ===================
 
     @Override
@@ -243,7 +247,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
 
     /**
      * 执行创建完创建完订单后的逻辑
-     * <p>
+     *
      * 例如说:优惠劵的扣减、积分的扣减、支付单的创建等等
      *
      * @param userId          用户编号
@@ -256,7 +260,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
                                        TradePriceCalculateRespBO calculateRespBO) {
         // 下单时扣减商品库存
         productSkuApi.updateSkuStock(new ProductSkuUpdateStockReqDTO(TradeOrderConvert.INSTANCE.convertList(orderItems)));
-
+        // TODO puhui:扣减活动库存
         // 删除购物车商品 TODO 芋艿:待实现
 
         // 扣减积分,抵扣金额 TODO 芋艿:待实现
@@ -267,7 +271,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
                     .setOrderId(tradeOrderDO.getId()));
         }
 
-        // 生成预支付
+        // 生成预支付 TODO puhui: 活动支付金额怎么算?
         createPayOrder(tradeOrderDO, orderItems, calculateRespBO);
 
         // 增加订单日志 TODO 芋艿:待实现
@@ -313,7 +317,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
 
     /**
      * 校验交易订单满足被支付的条件
-     * <p>
+     *
      * 1. 交易订单未支付
      * 2. 支付单已支付
      *
@@ -367,29 +371,50 @@ public class TradeOrderServiceImpl implements TradeOrderService {
         return new KeyValue<>(order, payOrder);
     }
 
-    // TODO 芋艿:如果无需发货,需要怎么存储? fix:更改订单发货类型为无需发货更改发货类型为等待自提,等待用户自提后更改订单状态为已完成
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public void deliveryOrder(Long userId, TradeOrderDeliveryReqVO deliveryReqVO) {
         // 校验并获得交易订单(可发货)
         TradeOrderDO order = validateOrderDeliverable(deliveryReqVO.getId());
-        // TODO 判断发货类型,根据发货类型发货
-        DeliveryExpressDO deliveryExpress = deliveryExpressService.getDeliveryExpress(deliveryReqVO.getLogisticsId());
-        if (deliveryExpress == null) {
-            throw exception(EXPRESS_NOT_EXISTS);
+
+        TradeOrderDO tradeOrderDO = new TradeOrderDO();
+        List<TradeOrderDeliveryDO> deliveryDOs = new ArrayList<>();
+        /* TODO
+         * fix: 首先需要店铺设置配送方式如: 自提 、配送、物流-配送、物流-配送-自提、商家配送
+         * 1.如果店铺有设置配送方式用户只填写收货地址的情况下店家后台自己选择配送方式
+         * 2.如果店铺只支持到店自提那么下单后默认发货不需要物流
+         * 3.如果店铺支持 物流-配送-自提 的情况下后台不需要选择配送方式按前端用户选择的配送方式发货即可
+         */
+        // 判断发货类型
+        // 快递发货
+        if (ObjectUtil.equal(deliveryReqVO.getType(), DeliveryTypeEnum.EXPRESS.getMode())) {
+            deliveryDOs = express(order, deliveryReqVO);
+            tradeOrderDO.setLogisticsId(deliveryReqVO.getLogisticsId()).setLogisticsNo(deliveryReqVO.getLogisticsNo());
+        }
+        // 用户自提
+        if (ObjectUtil.equal(deliveryReqVO.getType(), DeliveryTypeEnum.PICK_UP.getMode())) {
+            deliveryDOs = pickUp(order, deliveryReqVO);
+            // 重置一下确保快递公司和快递单号为空
+            tradeOrderDO.setLogisticsId(null).setLogisticsNo("");
+        }
+        // TODO 芋艿:如果无需发货,需要怎么存储?
+        if (ObjectUtil.equal(deliveryReqVO.getType(), DeliveryTypeEnum.NULL.getMode())) {
+            // TODO 情况一:正常走发货逻辑和用户自提有点像 不同点:不需要自提门店只需要用户确认收货
+            // TODO 情况二:用户下单付款后直接确认收货或等待用户确认收货
         }
 
         // 更新 TradeOrderDO 状态为已发货,等待收货
-        int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(),
-                new TradeOrderDO().setStatus(TradeOrderStatusEnum.DELIVERED.getStatus())
-                        .setLogisticsId(deliveryReqVO.getLogisticsId()).setLogisticsNo(deliveryReqVO.getLogisticsNo())
-                        .setDeliveryStatus(TradeOrderDeliveryStatusEnum.DELIVERED.getStatus()).setDeliveryTime(LocalDateTime.now()));
+        tradeOrderDO.setStatus(TradeOrderStatusEnum.DELIVERED.getStatus())
+                .setDeliveryStatus(TradeOrderDeliveryStatusEnum.DELIVERED.getStatus()).setDeliveryTime(LocalDateTime.now());
+        int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(), tradeOrderDO);
         if (updateCount == 0) {
             throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED);
         }
-
+        // 发货成功记录发货表
+        orderDeliveryMapper.insertBatch(deliveryDOs);
         // TODO 芋艿:发送订单变化的消息
 
-        // TODO 芋艿:发送站内信 fix
+        // 发送站内信
         // 1、构造消息
         Map<String, Object> msgMap = new HashMap<>();
         msgMap.put("orderId", deliveryReqVO.getId());
@@ -400,16 +425,59 @@ public class TradeOrderServiceImpl implements TradeOrderService {
                         .setUserId(userId)
                         .setTemplateCode("order_delivery")
                         .setTemplateParams(msgMap));
-        // TODO 芋艿:OrderLog
 
-        // TODO 设计:like:是否要单独一个 delivery 发货单表??? fix: 确实单独一张发货表就能解决整单发货和单独发货的问题
-        // TODO 设计:niu:要不要支持一个订单下,多个 order item 单独发货,类似有赞
+        // TODO 芋艿:OrderLog
         // TODO 设计:lili:是不是发货后,才支持售后?
     }
 
+    private List<TradeOrderDeliveryDO> express(TradeOrderDO order, TradeOrderDeliveryReqVO deliveryReqVO) {
+        // 校验快递公司
+        DeliveryExpressDO deliveryExpress = deliveryExpressService.getDeliveryExpress(deliveryReqVO.getLogisticsId());
+        if (deliveryExpress == null) {
+            throw exception(EXPRESS_NOT_EXISTS);
+        }
+        // 校验发货商品
+        validateDeliveryOrderItem(order, deliveryReqVO);
+        // 创建发货记录
+        return TradeOrderConvert.INSTANCE.covert(order, deliveryReqVO);
+    }
+
+    private List<TradeOrderDeliveryDO> pickUp(TradeOrderDO order, TradeOrderDeliveryReqVO deliveryReqVO) {
+        // TODO 校验自提门店是否存在
+        // 重置一下确保快递公司和快递单号为空
+        deliveryReqVO.setLogisticsId(null);
+        deliveryReqVO.setLogisticsNo("");
+        // 校验发货商品
+        validateDeliveryOrderItem(order, deliveryReqVO);
+        // 创建发货记录
+        return TradeOrderConvert.INSTANCE.covert(order, deliveryReqVO);
+    }
+
+    private void validateDeliveryOrderItem(TradeOrderDO order, TradeOrderDeliveryReqVO deliveryReqVO) {
+        // TODO 设计:like:是否要单独一个 delivery 发货单表??? fix: 多商品可分开单独发货,添加 trade_order_delivery 交易订单发货日志表关联发货所选订单项设置物流单号
+        // TODO 设计:niu:要不要支持一个订单下,多个 order item 单独发货,类似有赞 fix
+        // 校验发货商品
+        if (CollUtil.isEmpty(deliveryReqVO.getOrderItemIds())) {
+            throw exception(ORDER_DELIVERY_FAILED_ITEMS_NOT_EMPTY);
+        }
+        // 校验发货商品是否存在
+        List<TradeOrderItemDO> orderItemDOs = tradeOrderItemMapper.selectListByOrderId(order.getId());
+        Set<Long> itemIds = convertSet(orderItemDOs, TradeOrderItemDO::getId);
+        if (!itemIds.containsAll(deliveryReqVO.getOrderItemIds())) {
+            throw exception(ORDER_DELIVERY_FAILED_ITEM_NOT_EXISTS);
+        }
+        // 校验所选订单项是否存在有已发货的
+        List<TradeOrderDeliveryDO> deliveryDOList = orderDeliveryMapper.selsectListByOrderIdAndItemIds(order.getId(), deliveryReqVO.getOrderItemIds());
+        if (CollUtil.isNotEmpty(deliveryDOList)) {
+            HashSet<Long> hashSet = CollUtil.newHashSet(deliveryReqVO.getOrderItemIds());
+            hashSet.retainAll(convertSet(deliveryDOList, TradeOrderDeliveryDO::getOrderItemId));
+            throw exception(ORDER_DELIVERY_FAILED_ITEM_ALREADY_DELIVERY);
+        }
+    }
+
     /**
      * 校验交易订单满足被发货的条件
-     * <p>
+     *
      * 1. 交易订单未发货
      *
      * @param id 交易订单编号
@@ -432,7 +500,10 @@ public class TradeOrderServiceImpl implements TradeOrderService {
         }
         // 校验订单拼团是否成功
         if (ObjectUtil.equal(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
-
+            // TODO 用户 ID 使用当前登录用户的还是订单保存的?
+            if (ObjectUtil.notEqual(combinationApi.getRecordStatus(order.getUserId(), order.getId()), CombinationRecordStatusEnum.SUCCESS.getStatus())) {
+                throw exception(ORDER_DELIVERY_FAIL_COMBINATION_RECORD_STATUS_NOT_SUCCESS);
+            }
         }
         // TODO puhui999: 校验订单砍价是否成功
         return order;
@@ -466,7 +537,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
 
     /**
      * 校验交易订单满足可售货的条件
-     * <p>
+     *
      * 1. 交易订单待收货
      *
      * @param userId 用户编号