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

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

View File

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

View File

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

View File

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

View File

@ -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, "订单发货失败,所选商品已发货");
}

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") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 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 用户编号