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

This commit is contained in:
puhui999
2023-07-24 15:14:16 +08:00
parent f23e1b857a
commit 8d13ecc9c8
10 changed files with 229 additions and 25 deletions

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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 同城配送
}

View File

@@ -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));
}
}

View File

@@ -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 用户编号