!660 优化订单 OrderHandler 实现

Merge pull request !660 from 芋道源码/feature/mall_product_tmp
This commit is contained in:
芋道源码 2023-10-10 23:51:10 +00:00 committed by Gitee
commit 708af92a0f
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
14 changed files with 481 additions and 232 deletions

View File

@ -41,7 +41,7 @@ public interface SeckillProductMapper extends BaseMapperX<SeckillProductDO> {
Assert.isTrue(count > 0); Assert.isTrue(count > 0);
return update(null, new LambdaUpdateWrapper<SeckillProductDO>() return update(null, new LambdaUpdateWrapper<SeckillProductDO>()
.eq(SeckillProductDO::getId, id) .eq(SeckillProductDO::getId, id)
.gt(SeckillProductDO::getStock, count) .ge(SeckillProductDO::getStock, count)
.setSql("stock = stock - " + count)); .setSql("stock = stock - " + count));
} }

View File

@ -14,23 +14,12 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.module.member.api.address.AddressApi; import cn.iocoder.yudao.module.member.api.address.AddressApi;
import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
import cn.iocoder.yudao.module.member.api.level.MemberLevelApi;
import cn.iocoder.yudao.module.member.api.point.MemberPointApi;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum;
import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum;
import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi; import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi;
import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO; import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi;
import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO; import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderRemarkReqVO; import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderRemarkReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderUpdateAddressReqVO; import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderUpdateAddressReqVO;
@ -40,7 +29,6 @@ import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettle
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementRespVO; import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementRespVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO; import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO;
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert; import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.cart.CartDO; import cn.iocoder.yudao.module.trade.dal.dataobject.cart.CartDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO; 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.TradeOrderDO;
@ -48,15 +36,11 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper; 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.dal.mysql.order.TradeOrderMapper;
import cn.iocoder.yudao.module.trade.dal.redis.no.TradeNoRedisDAO; import cn.iocoder.yudao.module.trade.dal.redis.no.TradeNoRedisDAO;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
import cn.iocoder.yudao.module.trade.enums.order.*; import cn.iocoder.yudao.module.trade.enums.order.*;
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
import cn.iocoder.yudao.module.trade.framework.order.core.annotations.TradeOrderLog; import cn.iocoder.yudao.module.trade.framework.order.core.annotations.TradeOrderLog;
import cn.iocoder.yudao.module.trade.framework.order.core.utils.TradeOrderLogUtils; import cn.iocoder.yudao.module.trade.framework.order.core.utils.TradeOrderLogUtils;
import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageRecordService;
import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageUserService;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO;
import cn.iocoder.yudao.module.trade.service.cart.CartService; import cn.iocoder.yudao.module.trade.service.cart.CartService;
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
import cn.iocoder.yudao.module.trade.service.message.TradeMessageService; import cn.iocoder.yudao.module.trade.service.message.TradeMessageService;
@ -68,13 +52,15 @@ import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculatorHelper; import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculatorHelper;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.*; import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
@ -109,30 +95,12 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
private DeliveryExpressService deliveryExpressService; private DeliveryExpressService deliveryExpressService;
@Resource @Resource
private TradeMessageService tradeMessageService; private TradeMessageService tradeMessageService;
@Resource
private BrokerageUserService brokerageUserService;
@Resource
private BrokerageRecordService brokerageRecordService;
@Resource
private ProductSpuApi productSpuApi;
@Resource
private ProductSkuApi productSkuApi;
@Resource @Resource
private PayOrderApi payOrderApi; private PayOrderApi payOrderApi;
@Resource @Resource
private AddressApi addressApi; private AddressApi addressApi;
@Resource @Resource
private CouponApi couponApi;
@Resource
private CombinationRecordApi combinationRecordApi;
@Resource
private MemberUserApi memberUserApi;
@Resource
private MemberLevelApi memberLevelApi;
@Resource
private MemberPointApi memberPointApi;
@Resource
private ProductCommentApi productCommentApi; private ProductCommentApi productCommentApi;
@Resource @Resource
@ -199,7 +167,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
List<TradeOrderItemDO> orderItems = buildTradeOrderItems(order, calculateRespBO); List<TradeOrderItemDO> orderItems = buildTradeOrderItems(order, calculateRespBO);
// 2. 订单创建前的逻辑 // 2. 订单创建前的逻辑
beforeCreateTradeOrder(order, orderItems); tradeOrderHandlers.forEach(handler -> handler.beforeOrderCreate(order, orderItems));
// 3. 保存订单 // 3. 保存订单
tradeOrderMapper.insert(order); tradeOrderMapper.insert(order);
@ -234,11 +202,6 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
order.setReceiverName(createReqVO.getReceiverName()).setReceiverMobile(createReqVO.getReceiverMobile()); order.setReceiverName(createReqVO.getReceiverName()).setReceiverMobile(createReqVO.getReceiverMobile());
order.setPickUpVerifyCode(RandomUtil.randomNumbers(8)); // 随机一个核销码长度为 8 order.setPickUpVerifyCode(RandomUtil.randomNumbers(8)); // 随机一个核销码长度为 8
} }
// 设置订单推广人
BrokerageUserDO brokerageUser = brokerageUserService.getBrokerageUser(order.getUserId());
if (brokerageUser != null && brokerageUser.getBindUserId() != null) {
order.setBrokerageUserId(brokerageUser.getBindUserId());
}
return order; return order;
} }
@ -247,21 +210,6 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
return TradeOrderConvert.INSTANCE.convertList(tradeOrderDO, calculateRespBO); return TradeOrderConvert.INSTANCE.convertList(tradeOrderDO, calculateRespBO);
} }
/**
* 订单创建前执行前置逻辑
*
* @param order 订单
* @param orderItems 订单项
*/
private void beforeCreateTradeOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
// 1. 执行订单创建前置处理器
// TODO @puhui999这里有个纠结点handler 的定义是只处理指定类型的订单的拓展逻辑还是通用的 handler类似可以处理优惠劵等等
tradeOrderHandlers.forEach(handler -> handler.beforeOrderCreate(order, orderItems));
// 2. 下单时扣减商品库存
productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convertNegative(orderItems));
}
/** /**
* 订单创建后执行后置逻辑 * 订单创建后执行后置逻辑
* <p> * <p>
@ -276,27 +224,16 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
// 1. 执行订单创建后置处理器 // 1. 执行订单创建后置处理器
tradeOrderHandlers.forEach(handler -> handler.afterOrderCreate(order, orderItems)); tradeOrderHandlers.forEach(handler -> handler.afterOrderCreate(order, orderItems));
// 2. 有使用优惠券时更新 // 2. 删除购物车商品
// 不在前置扣减的原因是因为优惠劵要记录使用的订单号
if (order.getCouponId() != null) {
couponApi.useCoupon(new CouponUseReqDTO().setId(order.getCouponId()).setUserId(order.getUserId())
.setOrderId(order.getId()));
}
// 3. 扣减积分抵扣
// 不在前置扣减的原因是因为积分扣减时需要记录关联业务
reduceUserPoint(order.getUserId(), order.getUsePoint(), MemberPointBizTypeEnum.ORDER_USE, order.getId());
// 4. 删除购物车商品
Set<Long> cartIds = convertSet(createReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCartId); Set<Long> cartIds = convertSet(createReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCartId);
if (CollUtil.isNotEmpty(cartIds)) { if (CollUtil.isNotEmpty(cartIds)) {
cartService.deleteCart(order.getUserId(), cartIds); cartService.deleteCart(order.getUserId(), cartIds);
} }
// 5. 生成预支付 // 3. 生成预支付
createPayOrder(order, orderItems); createPayOrder(order, orderItems);
// 6. 插入订单日志 // 4. 插入订单日志
TradeOrderLogUtils.setOrderInfo(order.getId(), null, order.getStatus()); TradeOrderLogUtils.setOrderInfo(order.getId(), null, order.getStatus());
// TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来! // TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来!
@ -330,18 +267,11 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID); throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
} }
// 3订单支付成功后 // 3. 执行 TradeOrderHandler 的后置处理
List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id); List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id);
tradeOrderHandlers.forEach(handler -> handler.afterPayOrder(order, orderItems)); tradeOrderHandlers.forEach(handler -> handler.afterPayOrder(order, orderItems));
// 4.1 增加用户积分赠送 // 4. 记录订单日志
addUserPoint(order.getUserId(), order.getGivePoint(), MemberPointBizTypeEnum.ORDER_GIVE, order.getId());
// 4.2 增加用户经验
getSelf().addUserExperienceAsync(order.getUserId(), order.getPayPrice(), order.getId());
// 4.3 增加用户佣金
getSelf().addBrokerageAsync(order.getUserId(), order.getId());
// 5. 记录订单日志
TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.UNDELIVERED.getStatus()); TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.UNDELIVERED.getStatus());
TradeOrderLogUtils.setUserInfo(order.getUserId(), UserTypeEnum.MEMBER.getValue()); TradeOrderLogUtils.setUserInfo(order.getUserId(), UserTypeEnum.MEMBER.getValue());
} }
@ -434,8 +364,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
.put("logisticsNo", express != null ? deliveryReqVO.getLogisticsNo() : "").build()); .put("logisticsNo", express != null ? deliveryReqVO.getLogisticsNo() : "").build());
// 4. 发送站内信 // 4. 发送站内信
tradeMessageService.sendMessageWhenDeliveryOrder(new TradeOrderMessageWhenDeliveryOrderReqBO().setOrderId(order.getId()) tradeMessageService.sendMessageWhenDeliveryOrder(new TradeOrderMessageWhenDeliveryOrderReqBO()
.setUserId(order.getUserId()).setMessage(null)); .setOrderId(order.getId()).setUserId(order.getUserId()).setMessage(null));
} }
/** /**
@ -448,18 +378,13 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
*/ */
private TradeOrderDO validateOrderDeliverable(Long id) { private TradeOrderDO validateOrderDeliverable(Long id) {
TradeOrderDO order = validateOrderExists(id); TradeOrderDO order = validateOrderExists(id);
// 校验订单是否退款 // 1. 校验订单是否未发货
if (ObjectUtil.notEqual(TradeOrderRefundStatusEnum.NONE.getStatus(), order.getRefundStatus())) { if (ObjectUtil.notEqual(TradeOrderRefundStatusEnum.NONE.getStatus(), order.getRefundStatus())) {
throw exception(ORDER_DELIVERY_FAIL_REFUND_STATUS_NOT_NONE); throw exception(ORDER_DELIVERY_FAIL_REFUND_STATUS_NOT_NONE);
} }
// 订单类型拼团
if (Objects.equals(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
// 校验订单拼团是否成功
if (!combinationRecordApi.isCombinationRecordSuccess(order.getUserId(), order.getId())) {
throw exception(ORDER_DELIVERY_FAIL_COMBINATION_RECORD_STATUS_NOT_SUCCESS);
}
}
// 2. 执行 TradeOrderHandler 前置处理
tradeOrderHandlers.forEach(handler -> handler.beforeDeliveryOrder(order));
return order; return order;
} }
@ -616,30 +541,19 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
* @param cancelType 取消类型 * @param cancelType 取消类型
*/ */
private void cancelOrder0(TradeOrderDO order, TradeOrderCancelTypeEnum cancelType) { private void cancelOrder0(TradeOrderDO order, TradeOrderCancelTypeEnum cancelType) {
Long id = order.getId();
// 1. 更新 TradeOrderDO 状态为已取消 // 1. 更新 TradeOrderDO 状态为已取消
int updateCount = tradeOrderMapper.updateByIdAndStatus(id, order.getStatus(), int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(),
new TradeOrderDO().setStatus(TradeOrderStatusEnum.CANCELED.getStatus()) new TradeOrderDO().setStatus(TradeOrderStatusEnum.CANCELED.getStatus())
.setCancelType(cancelType.getType()).setCancelTime(LocalDateTime.now())); .setCancelType(cancelType.getType()).setCancelTime(LocalDateTime.now()));
if (updateCount == 0) { if (updateCount == 0) {
throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID); throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
} }
List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id); // 2. 执行 TradeOrderHandler 的后置处理
// 3. 回滚库存 List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(order.getId());
productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(orderItems)); tradeOrderHandlers.forEach(handler -> handler.afterCancelOrder(order, orderItems));
// 3.1 活动相关的回滚
tradeOrderHandlers.forEach(handler -> handler.cancelOrder(order, orderItems));
// 4. 回滚优惠券 // 3. 增加订单日志
if (order.getCouponId() != null && order.getCouponId() > 0) {
couponApi.returnUsedCoupon(order.getCouponId());
}
// 5. 回滚积分抵扣的
addUserPoint(order.getUserId(), order.getUsePoint(), MemberPointBizTypeEnum.ORDER_CANCEL, order.getId());
// 6. 增加订单日志
TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.CANCELED.getStatus()); TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.CANCELED.getStatus());
} }
@ -660,8 +574,9 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
.setStatus(TradeOrderStatusEnum.CANCELED.getStatus()) .setStatus(TradeOrderStatusEnum.CANCELED.getStatus())
.setCancelType(TradeOrderCancelTypeEnum.AFTER_SALE_CLOSE.getType()).setCancelTime(LocalDateTime.now())); .setCancelType(TradeOrderCancelTypeEnum.AFTER_SALE_CLOSE.getType()).setCancelTime(LocalDateTime.now()));
// 2. 退还优惠券 // 2. 执行 TradeOrderHandler 的后置处理
couponApi.returnUsedCoupon(order.getCouponId()); List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(order.getId());
tradeOrderHandlers.forEach(handler -> handler.afterCancelOrder(order, orderItems));
} }
@Override @Override
@ -789,13 +704,15 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void updateOrderItemWhenAfterSaleSuccess(Long id, Integer refundPrice) { public void updateOrderItemWhenAfterSaleSuccess(Long id, Integer refundPrice) {
// 1. 更新订单项 // 1.1 更新订单项
updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus(), null); TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus(), null);
// 1.2 执行 TradeOrderHandler 的后置处理
// 2.1 更新订单的退款金额积分
TradeOrderItemDO orderItem = tradeOrderItemMapper.selectById(id); TradeOrderItemDO orderItem = tradeOrderItemMapper.selectById(id);
TradeOrderDO order = tradeOrderMapper.selectById(orderItem.getOrderId()); TradeOrderDO order = tradeOrderMapper.selectById(orderItem.getOrderId());
tradeOrderHandlers.forEach(handler -> handler.afterCancelOrderItem(order, orderItem));
// 2.1 更新订单的退款金额积分
Integer orderRefundPrice = order.getRefundPrice() + refundPrice; Integer orderRefundPrice = order.getRefundPrice() + refundPrice;
Integer orderRefundPoint = order.getRefundPoint() + orderItem.getUsePoint(); Integer orderRefundPoint = order.getRefundPoint() + orderItem.getUsePoint();
Integer refundStatus = isAllOrderItemAfterSaleSuccess(order.getId()) ? Integer refundStatus = isAllOrderItemAfterSaleSuccess(order.getId()) ?
@ -806,23 +723,6 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
.setRefundPrice(orderRefundPrice).setRefundPoint(orderRefundPoint)); .setRefundPrice(orderRefundPrice).setRefundPoint(orderRefundPoint));
// 2.2 如果全部退款则进行取消订单 // 2.2 如果全部退款则进行取消订单
getSelf().cancelOrderByAfterSale(order, orderRefundPrice); getSelf().cancelOrderByAfterSale(order, orderRefundPrice);
// 3. 回滚库存
productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(Collections.singletonList(orderItem)));
// 3.1 活动相关的回滚
tradeOrderHandlers.forEach(handler -> handler.cancelOrder(order, Collections.singletonList(orderItem)));
// 4.1 回滚积分扣减用户积分赠送的
reduceUserPoint(order.getUserId(), orderItem.getGivePoint(), MemberPointBizTypeEnum.AFTER_SALE_DEDUCT_GIVE, orderItem.getAfterSaleId());
// 4.2 回滚积分增加用户积分返还抵扣
addUserPoint(order.getUserId(), orderItem.getUsePoint(), MemberPointBizTypeEnum.AFTER_SALE_REFUND_USED, orderItem.getAfterSaleId());
// 5. 回滚经验扣减用户经验
getSelf().reduceUserExperienceAsync(order.getUserId(), refundPrice, orderItem.getAfterSaleId());
// 6. 回滚佣金更新分佣记录为已失效
getSelf().cancelBrokerageAsync(order.getUserId(), id);
} }
@Override @Override
@ -839,6 +739,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
if (updateCount <= 0) { if (updateCount <= 0) {
throw exception(ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL); throw exception(ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL);
} }
} }
/** /**
@ -939,7 +840,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_COMMENT) @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_COMMENT)
public void createOrderItemCommentBySystemBySystem(TradeOrderDO order) { public void createOrderItemCommentBySystemBySystem(TradeOrderDO order) {
// 1. 查询未评论的订单项 // 1. 查询未评论的订单项
List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderIdAndCommentStatus(order.getId(), Boolean.FALSE); List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderIdAndCommentStatus(
order.getId(), Boolean.FALSE);
if (CollUtil.isEmpty(orderItems)) { if (CollUtil.isEmpty(orderItems)) {
return; return;
} }
@ -982,73 +884,6 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
// =================== 营销相关的操作 =================== // =================== 营销相关的操作 ===================
@Async
protected void addUserExperienceAsync(Long userId, Integer payPrice, Long orderId) {
int bizType = MemberExperienceBizTypeEnum.ORDER.getType();
memberLevelApi.addExperience(userId, payPrice, bizType, String.valueOf(orderId));
}
@Async
protected void reduceUserExperienceAsync(Long userId, Integer refundPrice, Long afterSaleId) {
int bizType = MemberExperienceBizTypeEnum.REFUND.getType();
memberLevelApi.addExperience(userId, -refundPrice, bizType, String.valueOf(afterSaleId));
}
/**
* 添加用户积分
* <p>
* 目前是支付成功后就会创建积分记录
* <p>
* 业内还有两种做法可以根据自己的业务调整
* 1. 确认收货后才创建积分记录
* 2. 支付 or 下单成功时创建积分记录冻结确认收货解冻或者 n 天后解冻
*
* @param userId 用户编号
* @param point 增加积分数量
* @param bizType 业务编号
* @param bizId 业务编号
*/
protected void addUserPoint(Long userId, Integer point, MemberPointBizTypeEnum bizType, Long bizId) {
if (point != null && point > 0) {
memberPointApi.addPoint(userId, point, bizType.getType(), String.valueOf(bizId));
}
}
protected void reduceUserPoint(Long userId, Integer point, MemberPointBizTypeEnum bizType, Long bizId) {
if (point != null && point > 0) {
memberPointApi.reducePoint(userId, point, bizType.getType(), String.valueOf(bizId));
}
}
/**
* 创建分销记录
* <p>
* 目前是支付成功后就会创建分销记录
* <p>
* 业内还有两种做法可以根据自己的业务调整
* 1. 确认收货后才创建分销记录
* 2. 支付 or 下单成功时创建分销记录冻结确认收货解冻或者 n 天后解冻
*
* @param userId 用户编号
* @param orderId 订单编号
*/
@Async
protected void addBrokerageAsync(Long userId, Long orderId) {
MemberUserRespDTO user = memberUserApi.getUser(userId);
Assert.notNull(user);
// 每一个订单项都会去生成分销记录
List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(orderId);
List<BrokerageAddReqBO> addList = convertList(orderItems,
item -> TradeOrderConvert.INSTANCE.convert(user, item,
productSpuApi.getSpu(item.getSpuId()), productSkuApi.getSku(item.getSkuId())));
brokerageRecordService.addBrokerage(userId, BrokerageRecordBizTypeEnum.ORDER, addList);
}
@Async
protected void cancelBrokerageAsync(Long userId, Long orderItemId) {
brokerageRecordService.cancelBrokerage(userId, BrokerageRecordBizTypeEnum.ORDER, String.valueOf(orderItemId));
}
/** /**
* 获得自身的代理对象解决 AOP 生效问题 * 获得自身的代理对象解决 AOP 生效问题
* *

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.trade.service.order.handler; package cn.iocoder.yudao.module.trade.service.order.handler;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.module.promotion.api.bargain.BargainActivityApi; import cn.iocoder.yudao.module.promotion.api.bargain.BargainActivityApi;
import cn.iocoder.yudao.module.promotion.api.bargain.BargainRecordApi; import cn.iocoder.yudao.module.promotion.api.bargain.BargainRecordApi;
@ -12,12 +13,12 @@ import javax.annotation.Resource;
import java.util.List; import java.util.List;
/** /**
* 砍价订单 handler 实现类 * 砍价订单 {@link TradeOrderHandler} 实现类
* *
* @author HUIHUI * @author HUIHUI
*/ */
@Component @Component
public class TradeBargainHandler implements TradeOrderHandler { public class TradeBargainOrderHandler implements TradeOrderHandler {
@Resource @Resource
private BargainActivityApi bargainActivityApi; private BargainActivityApi bargainActivityApi;
@ -26,7 +27,7 @@ public class TradeBargainHandler implements TradeOrderHandler {
@Override @Override
public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
if (TradeOrderTypeEnum.isBargain(order.getType())) { if (!TradeOrderTypeEnum.isBargain(order.getType())) {
return; return;
} }
// 明确校验一下 // 明确校验一下
@ -39,7 +40,7 @@ public class TradeBargainHandler implements TradeOrderHandler {
@Override @Override
public void afterOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { public void afterOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
if (TradeOrderTypeEnum.isBargain(order.getType())) { if (!TradeOrderTypeEnum.isBargain(order.getType())) {
return; return;
} }
// 明确校验一下 // 明确校验一下
@ -50,16 +51,28 @@ public class TradeBargainHandler implements TradeOrderHandler {
} }
@Override @Override
public void cancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { public void afterCancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
if (TradeOrderTypeEnum.isBargain(order.getType())) { if (!TradeOrderTypeEnum.isBargain(order.getType())) {
return; return;
} }
// 明确校验一下 // 明确校验一下
Assert.isTrue(orderItems.size() == 1, "砍价时,只允许选择一个商品"); Assert.isTrue(orderItems.size() == 1, "砍价时,只允许选择一个商品");
// 恢复砍价活动的库存 // 售后的订单项已经在 afterCancelOrderItem 回滚库存所以这里不需要重复回滚
bargainActivityApi.updateBargainActivityStock(order.getBargainActivityId(), orderItems = filterOrderItemListByNoneAfterSale(orderItems);
orderItems.get(0).getCount()); if (CollUtil.isEmpty(orderItems)) {
return;
}
afterCancelOrderItem(order, orderItems.get(0));
}
@Override
public void afterCancelOrderItem(TradeOrderDO order, TradeOrderItemDO orderItem) {
if (!TradeOrderTypeEnum.isBargain(order.getType())) {
return;
}
// 恢复增加砍价活动的库存
bargainActivityApi.updateBargainActivityStock(order.getBargainActivityId(), orderItem.getCount());
} }
} }

View File

@ -0,0 +1,118 @@
package cn.iocoder.yudao.module.trade.service.order.handler;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageRecordService;
import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageUserService;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
/**
* 订单分销的 {@link TradeOrderHandler} 实现类
*
* @author 芋道源码
*/
@Component
public class TradeBrokerageOrderHandler implements TradeOrderHandler {
@Resource
private MemberUserApi memberUserApi;
@Resource
private ProductSpuApi productSpuApi;
@Resource
private ProductSkuApi productSkuApi;
@Resource
private BrokerageRecordService brokerageRecordService;
@Resource
private BrokerageUserService brokerageUserService;
@Override
public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
// 设置订单推广人
BrokerageUserDO brokerageUser = brokerageUserService.getBrokerageUser(order.getUserId());
if (brokerageUser != null && brokerageUser.getBindUserId() != null) {
order.setBrokerageUserId(brokerageUser.getBindUserId());
}
}
@Override
public void afterPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
if (order.getBrokerageUserId() == null) {
return;
}
addBrokerage(order.getUserId(), orderItems);
}
@Override
public void afterCancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
// 如果是未支付的订单不会产生分销结果所以直接 return
if (!order.getPayStatus()) {
return;
}
if (order.getBrokerageUserId() == null) {
return;
}
// 售后的订单项已经在 afterCancelOrderItem 回滚库存所以这里不需要重复回滚
orderItems = filterOrderItemListByNoneAfterSale(orderItems);
if (CollUtil.isEmpty(orderItems)) {
return;
}
orderItems.forEach(orderItem -> afterCancelOrderItem(order, orderItem));
}
@Override
public void afterCancelOrderItem(TradeOrderDO order, TradeOrderItemDO orderItem) {
if (order.getBrokerageUserId() == null) {
return;
}
cancelBrokerage(order.getId(), orderItem.getOrderId());
}
/**
* 创建分销记录
* <p>
* 目前是支付成功后就会创建分销记录
* <p>
* 业内还有两种做法可以根据自己的业务调整
* 1. 确认收货后才创建分销记录
* 2. 支付 or 下单成功时创建分销记录冻结确认收货解冻或者 n 天后解冻
*
* @param userId 用户编号
* @param orderItems 订单项
*/
protected void addBrokerage(Long userId, List<TradeOrderItemDO> orderItems) {
MemberUserRespDTO user = memberUserApi.getUser(userId);
Assert.notNull(user);
ProductSpuRespDTO spu = productSpuApi.getSpu(orderItems.get(0).getSpuId());
Assert.notNull(spu);
ProductSkuRespDTO sku = productSkuApi.getSku(orderItems.get(0).getSkuId());
// 每一个订单项都会去生成分销记录
List<BrokerageAddReqBO> addList = convertList(orderItems,
item -> TradeOrderConvert.INSTANCE.convert(user, item, spu, sku));
brokerageRecordService.addBrokerage(userId, BrokerageRecordBizTypeEnum.ORDER, addList);
}
protected void cancelBrokerage(Long userId, Long orderItemId) {
brokerageRecordService.cancelBrokerage(userId, BrokerageRecordBizTypeEnum.ORDER, String.valueOf(orderItemId));
}
}

View File

@ -18,14 +18,15 @@ import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_CREATE_FAIL_EXIST_UNPAID; import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_CREATE_FAIL_EXIST_UNPAID;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_DELIVERY_FAIL_COMBINATION_RECORD_STATUS_NOT_SUCCESS;
/** /**
* 拼团订单 handler 接口实现类 * 拼团订单 {@link TradeOrderHandler} 实现类
* *
* @author HUIHUI * @author HUIHUI
*/ */
@Component @Component
public class TradeCombinationHandler implements TradeOrderHandler { public class TradeCombinationOrderHandler implements TradeOrderHandler {
@Resource @Resource
@Lazy @Lazy
@ -41,16 +42,16 @@ public class TradeCombinationHandler implements TradeOrderHandler {
@Override @Override
public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
// 如果不是拼团订单则结束 // 如果不是拼团订单则结束
if (TradeOrderTypeEnum.isCombination(order.getType())) { if (!TradeOrderTypeEnum.isCombination(order.getType())) {
return; return;
} }
Assert.isTrue(orderItems.size() == 1, "拼团时,只允许选择一个商品"); Assert.isTrue(orderItems.size() == 1, "拼团时,只允许选择一个商品");
// 校验是否满足拼团活动相关限制 // 1. 校验是否满足拼团活动相关限制
TradeOrderItemDO item = orderItems.get(0); TradeOrderItemDO item = orderItems.get(0);
combinationRecordApi.validateCombinationRecord(order.getUserId(), order.getCombinationActivityId(), combinationRecordApi.validateCombinationRecord(order.getUserId(), order.getCombinationActivityId(),
order.getCombinationHeadId(), item.getSkuId(), item.getCount()); order.getCombinationHeadId(), item.getSkuId(), item.getCount());
// 校验该用户是否存在未支付的拼团活动订单就是还没支付的时候重复下单了需要校验下不然的话一个拼团可以下多个单子了 // 2. 校验该用户是否存在未支付的拼团活动订单就是还没支付的时候重复下单了需要校验下不然的话一个拼团可以下多个单子了
TradeOrderDO activityOrder = orderQueryService.getActivityOrderByUserIdAndActivityIdAndStatus( TradeOrderDO activityOrder = orderQueryService.getActivityOrderByUserIdAndActivityIdAndStatus(
order.getUserId(), order.getCombinationActivityId(), TradeOrderStatusEnum.UNPAID.getStatus()); order.getUserId(), order.getCombinationActivityId(), TradeOrderStatusEnum.UNPAID.getStatus());
if (activityOrder != null) { if (activityOrder != null) {
@ -61,19 +62,32 @@ public class TradeCombinationHandler implements TradeOrderHandler {
@Override @Override
public void afterPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { public void afterPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
// 1.如果不是拼团订单则结束 // 1.如果不是拼团订单则结束
if (TradeOrderTypeEnum.isCombination(order.getType())) { if (!TradeOrderTypeEnum.isCombination(order.getType())) {
return; return;
} }
Assert.isTrue(orderItems.size() == 1, "拼团时,只允许选择一个商品"); Assert.isTrue(orderItems.size() == 1, "拼团时,只允许选择一个商品");
// 2.获取商品信息 // 2. 创建拼团记录
TradeOrderItemDO item = orderItems.get(0); TradeOrderItemDO item = orderItems.get(0);
// 2.1.创建拼团记录
KeyValue<Long, Long> recordIdAndHeadId = combinationRecordApi.createCombinationRecord( KeyValue<Long, Long> recordIdAndHeadId = combinationRecordApi.createCombinationRecord(
TradeOrderConvert.INSTANCE.convert(order, item)); TradeOrderConvert.INSTANCE.convert(order, item));
// 3.更新拼团相关信息到订单
// 3. 更新拼团相关信息到订单
// TODO 芋艿只需要更新 record
orderUpdateService.updateOrderCombinationInfo(order.getId(), order.getCombinationActivityId(), orderUpdateService.updateOrderCombinationInfo(order.getId(), order.getCombinationActivityId(),
recordIdAndHeadId.getKey(), recordIdAndHeadId.getValue()); recordIdAndHeadId.getKey(), recordIdAndHeadId.getValue());
} }
@Override
public void beforeDeliveryOrder(TradeOrderDO order) {
if (!TradeOrderTypeEnum.isCombination(order.getType())) {
return;
}
// 校验订单拼团是否成功
if (!combinationRecordApi.isCombinationRecordSuccess(order.getUserId(), order.getId())) {
throw exception(ORDER_DELIVERY_FAIL_COMBINATION_RECORD_STATUS_NOT_SUCCESS);
}
}
} }

View File

@ -0,0 +1,42 @@
package cn.iocoder.yudao.module.trade.service.order.handler;
import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
/**
* 优惠劵的 {@link TradeOrderHandler} 实现类
*
* @author 芋道源码
*/
@Component
public class TradeCouponOrderHandler implements TradeOrderHandler {
@Resource
private CouponApi couponApi;
@Override
public void afterOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
if (order.getCouponId() == null || order.getCouponId() <= 0) {
return;
}
// 不在前置扣减的原因是因为优惠劵要记录使用的订单号
couponApi.useCoupon(new CouponUseReqDTO().setId(order.getCouponId()).setUserId(order.getUserId())
.setOrderId(order.getId()));
}
@Override
public void afterCancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
if (order.getCouponId() == null || order.getCouponId() <= 0) {
return;
}
// 退回优惠劵
couponApi.returnUsedCoupon(order.getCouponId());
}
}

View File

@ -0,0 +1,120 @@
package cn.iocoder.yudao.module.trade.service.order.handler;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.module.member.api.level.MemberLevelApi;
import cn.iocoder.yudao.module.member.api.point.MemberPointApi;
import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum;
import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum;
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue;
/**
* 会员积分等级的 {@link TradeOrderHandler} 实现类
*
* @author owen
*/
@Component
public class TradeMemberPointOrderHandler implements TradeOrderHandler {
@Resource
private MemberPointApi memberPointApi;
@Resource
private MemberLevelApi memberLevelApi;
@Resource
private AfterSaleService afterSaleService;
@Override
public void afterOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
// 扣减用户积分订单抵扣不在前置扣减的原因是因为积分扣减时需要记录关联业务
reducePoint(order.getUserId(), order.getUsePoint(), MemberPointBizTypeEnum.ORDER_USE, order.getId());
}
@Override
public void afterPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
// 增加用户积分订单赠送
addPoint(order.getUserId(), order.getGivePoint(), MemberPointBizTypeEnum.ORDER_GIVE,
order.getId());
// 增加用户经验
memberLevelApi.addExperience(order.getUserId(), order.getPayPrice(),
MemberExperienceBizTypeEnum.ORDER_GIVE.getType(), String.valueOf(order.getId()));
}
@Override
public void afterCancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
// 售后的订单项已经在 afterCancelOrderItem 回滚库存所以这里不需要重复回滚
orderItems = filterOrderItemListByNoneAfterSale(orderItems);
if (CollUtil.isEmpty(orderItems)) {
return;
}
// 增加回滚用户积分订单抵扣
Integer usePoint = getSumValue(orderItems, TradeOrderItemDO::getUsePoint, Integer::sum);
addPoint(order.getUserId(), usePoint, MemberPointBizTypeEnum.ORDER_USE_CANCEL,
order.getId());
// 如下的返还需要经过支持也就是经历 afterPayOrder 流程
if (!order.getPayStatus()) {
return;
}
// 扣减回滚积分订单赠送
Integer givePoint = getSumValue(orderItems, TradeOrderItemDO::getGivePoint, Integer::sum);
reducePoint(order.getUserId(), givePoint, MemberPointBizTypeEnum.ORDER_GIVE_CANCEL,
order.getId());
// 扣减回滚用户经验
int payPrice = order.getPayPrice() - order.getRefundPrice();
memberLevelApi.addExperience(order.getUserId(), payPrice,
MemberExperienceBizTypeEnum.ORDER_GIVE_CANCEL.getType(), String.valueOf(order.getId()));
}
@Override
public void afterCancelOrderItem(TradeOrderDO order, TradeOrderItemDO orderItem) {
// 扣减回滚积分订单赠送
reducePoint(order.getUserId(), orderItem.getGivePoint(), MemberPointBizTypeEnum.ORDER_GIVE_CANCEL_ITEM,
orderItem.getId());
// 增加回滚积分订单抵扣
addPoint(order.getUserId(), orderItem.getUsePoint(), MemberPointBizTypeEnum.ORDER_USE_CANCEL_ITEM,
orderItem.getId());
// 扣减回滚用户经验
AfterSaleDO afterSale = afterSaleService.getAfterSale(orderItem.getAfterSaleId());
memberLevelApi.reduceExperience(order.getUserId(), afterSale.getRefundPrice(),
MemberExperienceBizTypeEnum.ORDER_GIVE_CANCEL_ITEM.getType(), String.valueOf(orderItem.getId()));
}
/**
* 添加用户积分
* <p>
* 目前是支付成功后就会创建积分记录
* <p>
* 业内还有两种做法可以根据自己的业务调整
* 1. 确认收货后才创建积分记录
* 2. 支付 or 下单成功时创建积分记录冻结确认收货解冻或者 n 天后解冻
*
* @param userId 用户编号
* @param point 增加积分数量
* @param bizType 业务编号
* @param bizId 业务编号
*/
protected void addPoint(Long userId, Integer point, MemberPointBizTypeEnum bizType, Long bizId) {
if (point != null && point > 0) {
memberPointApi.addPoint(userId, point, bizType.getType(), String.valueOf(bizId));
}
}
protected void reducePoint(Long userId, Integer point, MemberPointBizTypeEnum bizType, Long bizId) {
if (point != null && point > 0) {
memberPointApi.reducePoint(userId, point, bizType.getType(), String.valueOf(bizId));
}
}
}

View File

@ -1,7 +1,9 @@
package cn.iocoder.yudao.module.trade.service.order.handler; package cn.iocoder.yudao.module.trade.service.order.handler;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
import java.util.List; import java.util.List;
@ -35,16 +37,42 @@ public interface TradeOrderHandler {
* @param order 订单 * @param order 订单
* @param orderItems 订单项 * @param orderItems 订单项
*/ */
default void afterPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { default void afterPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {}
}
/** /**
* 订单取消 * 订单取消
* *
* @param order 订单 * @param order 订单
* @param orderItems 订单项 * @param orderItems 订单项
*/ */
default void cancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { default void afterCancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {}
/**
* 订单项取消后
*
* @param order 订单
* @param orderItem 订单项
*/
default void afterCancelOrderItem(TradeOrderDO order, TradeOrderItemDO orderItem) {}
/**
* 订单发货前
*
* @param order 订单
*/
default void beforeDeliveryOrder(TradeOrderDO order) {}
// ========== 公用方法 ==========
/**
* 过滤未售后的订单项列表
*
* @param orderItems 订单项列表
* @return 过滤后的订单项列表
*/
default List<TradeOrderItemDO> filterOrderItemListByNoneAfterSale(List<TradeOrderItemDO> orderItems) {
return CollectionUtils.filterList(orderItems,
item -> TradeOrderItemAfterSaleStatusEnum.isNone(item.getAfterSaleStatus()));
} }
} }

View File

@ -0,0 +1,46 @@
package cn.iocoder.yudao.module.trade.service.order.handler;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
import static java.util.Collections.singletonList;
/**
* 商品 SKU 库存的 {@link TradeOrderHandler} 实现类
*
* @author 芋道源码
*/
@Component
public class TradeProductSkuOrderHandler implements TradeOrderHandler {
@Resource
private ProductSkuApi productSkuApi;
@Override
public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convertNegative(orderItems));
}
@Override
public void afterCancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
// 售后的订单项已经在 afterCancelOrderItem 回滚库存所以这里不需要重复回滚
orderItems = filterOrderItemListByNoneAfterSale(orderItems);
if (CollUtil.isEmpty(orderItems)) {
return;
}
productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(orderItems));
}
@Override
public void afterCancelOrderItem(TradeOrderDO order, TradeOrderItemDO orderItem) {
productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(singletonList(orderItem)));
}
}

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.trade.service.order.handler; package cn.iocoder.yudao.module.trade.service.order.handler;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.module.promotion.api.seckill.SeckillActivityApi; import cn.iocoder.yudao.module.promotion.api.seckill.SeckillActivityApi;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
@ -11,19 +12,19 @@ import javax.annotation.Resource;
import java.util.List; import java.util.List;
/** /**
* 秒杀订单 handler 实现类 * 秒杀订单 {@link TradeOrderHandler} 实现类
* *
* @author HUIHUI * @author HUIHUI
*/ */
@Component @Component
public class TradeSeckillHandler implements TradeOrderHandler { public class TradeSeckillOrderHandler implements TradeOrderHandler {
@Resource @Resource
private SeckillActivityApi seckillActivityApi; private SeckillActivityApi seckillActivityApi;
@Override @Override
public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
if (TradeOrderTypeEnum.isSeckill(order.getType())) { if (!TradeOrderTypeEnum.isSeckill(order.getType())) {
return; return;
} }
// 明确校验一下 // 明确校验一下
@ -35,16 +36,29 @@ public class TradeSeckillHandler implements TradeOrderHandler {
} }
@Override @Override
public void cancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { public void afterCancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
if (TradeOrderTypeEnum.isSeckill(order.getType())) { if (!TradeOrderTypeEnum.isSeckill(order.getType())) {
return; return;
} }
// 明确校验一下 // 明确校验一下
Assert.isTrue(orderItems.size() == 1, "秒杀时,只允许选择一个商品"); Assert.isTrue(orderItems.size() == 1, "秒杀时,只允许选择一个商品");
// 售后的订单项已经在 afterCancelOrderItem 回滚库存所以这里不需要重复回滚
orderItems = filterOrderItemListByNoneAfterSale(orderItems);
if (CollUtil.isEmpty(orderItems)) {
return;
}
afterCancelOrderItem(order, orderItems.get(0));
}
@Override
public void afterCancelOrderItem(TradeOrderDO order, TradeOrderItemDO orderItem) {
if (!TradeOrderTypeEnum.isSeckill(order.getType())) {
return;
}
// 恢复秒杀活动的库存 // 恢复秒杀活动的库存
seckillActivityApi.updateSeckillStockIncr(order.getSeckillActivityId(), seckillActivityApi.updateSeckillStockIncr(order.getSeckillActivityId(),
orderItems.get(0).getSkuId(), orderItems.get(0).getCount()); orderItem.getSkuId(), orderItem.getCount());
} }
} }

View File

@ -28,4 +28,14 @@ public interface MemberLevelApi {
*/ */
void addExperience(Long userId, Integer experience, Integer bizType, String bizId); void addExperience(Long userId, Integer experience, Integer bizType, String bizId);
/**
* 扣减会员经验
*
* @param userId 会员ID
* @param experience 经验
* @param bizType 业务类型 {@link MemberExperienceBizTypeEnum}
* @param bizId 业务编号
*/
void reduceExperience(Long userId, Integer experience, Integer bizType, String bizId);
} }

View File

@ -20,10 +20,11 @@ public enum MemberExperienceBizTypeEnum {
*/ */
ADMIN(0, "管理员调整", "管理员调整获得 {} 经验", true), ADMIN(0, "管理员调整", "管理员调整获得 {} 经验", true),
INVITE_REGISTER(1, "邀新奖励", "邀请好友获得 {} 经验", true), INVITE_REGISTER(1, "邀新奖励", "邀请好友获得 {} 经验", true),
ORDER(2, "下单奖励", "下单获得 {} 经验", true),
REFUND(3, "退单扣除", "退单获得 {} 经验", false),
SIGN_IN(4, "签到奖励", "签到获得 {} 经验", true), SIGN_IN(4, "签到奖励", "签到获得 {} 经验", true),
LOTTERY(5, "抽奖奖励", "抽奖获得 {} 经验", true), LOTTERY(5, "抽奖奖励", "抽奖获得 {} 经验", true),
ORDER_GIVE(11, "下单奖励", "下单获得 {} 经验", true),
ORDER_GIVE_CANCEL(12, "下单奖励(整单取消)", "取消订单获得 {} 经验", false), // ORDER_GIVE 的取消
ORDER_GIVE_CANCEL_ITEM(13, "下单奖励(单个退款)", "退款订单获得 {} 经验", false), // ORDER_GIVE 的取消
; ;
/** /**

View File

@ -18,11 +18,14 @@ public enum MemberPointBizTypeEnum implements IntArrayValuable {
SIGN(1, "签到", "签到获得 {} 积分", true), SIGN(1, "签到", "签到获得 {} 积分", true),
ADMIN(2, "管理员修改", "管理员修改 {} 积分", true), ADMIN(2, "管理员修改", "管理员修改 {} 积分", true),
ORDER_GIVE(10, "订单奖励", "下单获得 {} 积分", true), // 支付订单时赠送积分
ORDER_CANCEL(11, "订单取消", "订单取消,退还 {} 积分", true), // 取消订单时退回积分 ORDER_USE(11, "订单积分抵扣", "下单使用 {} 积分", false), // 下单时扣减积分
ORDER_USE(12, "订单使用", "下单使用 {} 积分", false), // 下单时扣减积分 ORDER_USE_CANCEL(12, "订单积分抵扣(整单取消)", "订单取消,退还 {} 积分", true), // ORDER_USE 的取消
AFTER_SALE_REFUND_USED(13, "订单退款", "订单退款,退还 {} 积分", true), // 售后订单成功时退回积分对应 ORDER_USE 操作 ORDER_USE_CANCEL_ITEM(13, "订单积分抵扣(单个退款)", "订单退款,退还 {} 积分", true), // ORDER_USE 的取消
AFTER_SALE_DEDUCT_GIVE(14, "订单退款", "订单退款,扣除赠送的 {} 积分", false), // 售后订单成功时扣减积分对应 ORDER_GIVE 操作
ORDER_GIVE(21, "订单积分奖励", "下单获得 {} 积分", true), // 支付订单时赠送积分
ORDER_GIVE_CANCEL(22, "订单积分奖励(整单取消)", "订单取消,退还 {} 积分", false), // ORDER_GIVE 的取消
ORDER_GIVE_CANCEL_ITEM(23, "订单积分奖励(单个退款)", "订单退款,扣除赠送的 {} 积分", false) // ORDER_GIVE 的取消
; ;
/** /**

View File

@ -38,4 +38,9 @@ public class MemberLevelApiImpl implements MemberLevelApi {
memberLevelService.addExperience(userId, experience, bizTypeEnum, bizId); memberLevelService.addExperience(userId, experience, bizTypeEnum, bizId);
} }
@Override
public void reduceExperience(Long userId, Integer experience, Integer bizType, String bizId) {
addExperience(userId, -experience, bizType, bizId);
}
} }