mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-22 15:21:53 +08:00
trade:优化下单逻辑的实现
This commit is contained in:
parent
c766f7daa5
commit
1b477aaa0d
@ -39,7 +39,7 @@ public interface ProductSkuApi {
|
||||
List<ProductSkuRespDTO> getSkuListBySpuId(Collection<Long> spuIds);
|
||||
|
||||
/**
|
||||
* 更新 SKU 库存
|
||||
* 更新 SKU 库存(增加 or 减少)
|
||||
*
|
||||
* @param updateStockReqDTO 更新请求
|
||||
*/
|
||||
|
@ -58,8 +58,9 @@ public interface ErrorCodeConstants {
|
||||
|
||||
// ========== Price 相关 1011003000 ============
|
||||
ErrorCode PRICE_CALCULATE_PAY_PRICE_ILLEGAL = new ErrorCode(1011003000, "支付价格计算异常,原因:价格小于等于 0");
|
||||
ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_USER_ADDR_IS_EMPTY = new ErrorCode(1011003001, "计算快递运费异常,收件人地址编号为空");
|
||||
ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_USER_ADDRESS_IS_EMPTY = new ErrorCode(1011003001, "计算快递运费异常,收件人地址编号为空");
|
||||
ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND = new ErrorCode(1011003002, "计算快递运费异常,找不到对应的运费模板");
|
||||
ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_PICK_UP_STORE_IS_EMPTY = new ErrorCode(1011003003, "计算快递运费异常,自提点为空");
|
||||
|
||||
// ========== 物流 Express 模块 1011004000 ==========
|
||||
ErrorCode EXPRESS_NOT_EXISTS = new ErrorCode(1011004000, "快递公司不存在");
|
||||
|
@ -15,7 +15,6 @@ import java.util.Arrays;
|
||||
@AllArgsConstructor
|
||||
public enum DeliveryTypeEnum implements IntArrayValuable {
|
||||
|
||||
NULL(0, "无需物流"),
|
||||
EXPRESS(1, "快递发货"),
|
||||
PICK_UP(2, "用户自提"),;
|
||||
|
||||
|
@ -15,7 +15,8 @@ Authorization: Bearer {{appToken}}
|
||||
tenant-id: {{appTenentId}}
|
||||
|
||||
{
|
||||
"type": 0,
|
||||
"pointStatus": true,
|
||||
"deliveryType": 1,
|
||||
"addressId": 21,
|
||||
"items": [
|
||||
{
|
||||
|
@ -1,6 +1,5 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.order;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
|
||||
@ -12,11 +11,8 @@ import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
|
||||
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.TradeOrderItemDO;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderOperateTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
|
||||
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.utils.TradeOrderLogUtils;
|
||||
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
|
||||
import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
|
||||
import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;
|
||||
@ -65,10 +61,7 @@ public class AppTradeOrderController {
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建订单")
|
||||
@PreAuthenticated
|
||||
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.TEST)
|
||||
public CommonResult<AppTradeOrderCreateRespVO> createOrder(@RequestBody AppTradeOrderCreateReqVO createReqVO) {
|
||||
TradeOrderLogUtils.setOrderInfo(10L, 1, 2,
|
||||
MapUtil.<String, Object>builder().put("nickname", "小明").put("thing", "种土豆").build());
|
||||
public CommonResult<AppTradeOrderCreateRespVO> createOrder(@Valid @RequestBody AppTradeOrderCreateReqVO createReqVO) {
|
||||
TradeOrderDO order = tradeOrderUpdateService.createOrder(getLoginUserId(), getClientIP(), createReqVO);
|
||||
return success(new AppTradeOrderCreateRespVO().setId(order.getId()).setPayOrderId(order.getPayOrderId()));
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.order.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.AssertTrue;
|
||||
|
||||
@Schema(description = "用户 App - 交易订单创建 Request VO")
|
||||
@Data
|
||||
public class AppTradeOrderCreateReqVO extends AppTradeOrderSettlementReqVO {
|
||||
@ -10,4 +13,10 @@ public class AppTradeOrderCreateReqVO extends AppTradeOrderSettlementReqVO {
|
||||
@Schema(description = "备注", example = "这个是我的订单哟")
|
||||
private String remark;
|
||||
|
||||
@AssertTrue(message = "配送方式不能为空")
|
||||
@JsonIgnore
|
||||
public boolean isDeliveryTypeNotNull() {
|
||||
return getDeliveryType() != null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ public class AppTradeOrderSettlementReqVO {
|
||||
private Boolean pointStatus;
|
||||
|
||||
// ========== 配送相关相关字段 ==========
|
||||
@Schema(description = "配送方式", required = true, example = "1")
|
||||
@Schema(description = "配送方式", example = "1")
|
||||
@InEnum(value = DeliveryTypeEnum.class, message = "配送方式不正确")
|
||||
private Integer deliveryType;
|
||||
|
||||
@ -62,6 +62,8 @@ public class AppTradeOrderSettlementReqVO {
|
||||
@Schema(description = "砍价活动编号", example = "123")
|
||||
private Long bargainActivityId;
|
||||
|
||||
// TODO @puhui999:可以写个参数校验,如果 seckillActivityId 或 combinationActivityId 或 combinationHeadId 的情况,items 应该只有一个
|
||||
|
||||
@Data
|
||||
@Schema(description = "用户 App - 商品项")
|
||||
@Valid
|
||||
|
@ -56,6 +56,7 @@ public interface TradeOrderConvert {
|
||||
|
||||
@Mappings({
|
||||
@Mapping(target = "id", ignore = true),
|
||||
@Mapping(source = "userId", target = "userId"),
|
||||
@Mapping(source = "createReqVO.couponId", target = "couponId"),
|
||||
@Mapping(target = "remark", ignore = true),
|
||||
@Mapping(source = "createReqVO.remark", target = "userRemark"),
|
||||
@ -89,22 +90,15 @@ public interface TradeOrderConvert {
|
||||
TradeOrderItemDO convert(TradePriceCalculateRespBO.OrderItem item);
|
||||
|
||||
default ProductSkuUpdateStockReqDTO convert(List<TradeOrderItemDO> list) {
|
||||
return new ProductSkuUpdateStockReqDTO(TradeOrderConvert.INSTANCE.convertList(list));
|
||||
}
|
||||
|
||||
default ProductSkuUpdateStockReqDTO convertNegative(List<TradeOrderItemDO> list) {
|
||||
List<ProductSkuUpdateStockReqDTO.Item> items = TradeOrderConvert.INSTANCE.convertList(list);
|
||||
items.forEach(item -> item.setIncrCount(-item.getIncrCount()));
|
||||
List<ProductSkuUpdateStockReqDTO.Item> items = CollectionUtils.convertList(list, item ->
|
||||
new ProductSkuUpdateStockReqDTO.Item().setId(item.getSkuId()).setIncrCount(-item.getCount()));
|
||||
return new ProductSkuUpdateStockReqDTO(items);
|
||||
}
|
||||
default ProductSkuUpdateStockReqDTO convertNegative(List<AppTradeOrderSettlementReqVO.Item> list) {
|
||||
List<ProductSkuUpdateStockReqDTO.Item> items = CollectionUtils.convertList(list, item ->
|
||||
new ProductSkuUpdateStockReqDTO.Item().setId(item.getSkuId()).setIncrCount(-item.getCount()));
|
||||
return new ProductSkuUpdateStockReqDTO(items);
|
||||
}
|
||||
|
||||
List<ProductSkuUpdateStockReqDTO.Item> convertList(List<TradeOrderItemDO> list);
|
||||
|
||||
@Mappings({
|
||||
@Mapping(source = "skuId", target = "id"),
|
||||
@Mapping(source = "count", target = "incrCount"),
|
||||
})
|
||||
ProductSkuUpdateStockReqDTO.Item convert(TradeOrderItemDO bean);
|
||||
|
||||
default PayOrderCreateReqDTO convert(TradeOrderDO order, List<TradeOrderItemDO> orderItems,
|
||||
TradePriceCalculateRespBO calculateRespBO, TradeOrderProperties orderProperties) {
|
||||
@ -218,8 +212,9 @@ public interface TradeOrderConvert {
|
||||
|
||||
default TradePriceCalculateReqBO convert(Long userId, AppTradeOrderSettlementReqVO settlementReqVO,
|
||||
List<CartDO> cartList) {
|
||||
TradePriceCalculateReqBO reqBO = new TradePriceCalculateReqBO();
|
||||
reqBO.setUserId(userId).setCouponId(settlementReqVO.getCouponId()).setAddressId(settlementReqVO.getAddressId())
|
||||
TradePriceCalculateReqBO reqBO = new TradePriceCalculateReqBO().setUserId(userId)
|
||||
.setCouponId(settlementReqVO.getCouponId()).setPointStatus(settlementReqVO.getPointStatus())
|
||||
.setDeliveryType(settlementReqVO.getDeliveryType()).setAddressId(settlementReqVO.getAddressId())
|
||||
.setItems(new ArrayList<>(settlementReqVO.getItems().size()));
|
||||
// 商品项的构建
|
||||
Map<Long, CartDO> cartMap = convertMap(cartList, CartDO::getId);
|
||||
|
@ -251,12 +251,19 @@ public class TradeOrderDO extends BaseDO {
|
||||
* 对应 taobao 的 trade.coupon_fee 字段
|
||||
*/
|
||||
private Integer couponPrice;
|
||||
// TODO 芋艿:需要记录使用的积分;
|
||||
/**
|
||||
* 使用的积分
|
||||
*/
|
||||
private Integer usePoint;
|
||||
/**
|
||||
* 积分抵扣的金额,单位:分
|
||||
*
|
||||
* 对应 taobao 的 trade.point_fee 字段
|
||||
*/
|
||||
private Integer pointPrice;
|
||||
// /**
|
||||
// * 奖励的积分 TODO 疯狂:可以使用这个字段哈;
|
||||
// */
|
||||
// private Integer rewardPoint;
|
||||
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ 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.order.*;
|
||||
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.service.brokerage.bo.BrokerageAddReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.brokerage.record.BrokerageRecordService;
|
||||
import cn.iocoder.yudao.module.trade.service.cart.CartService;
|
||||
@ -182,32 +183,24 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CREATE)
|
||||
public TradeOrderDO createOrder(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO) {
|
||||
// 1、执行订单创建前置处理器
|
||||
// TODO @puhui999:最好也抽个 beforeOrderCreate 方法;不要 BO 各自处理参数岂不美哉?
|
||||
TradeBeforeOrderCreateReqBO beforeOrderCreateReqBO = TradeOrderConvert.INSTANCE.convert(createReqVO);
|
||||
beforeOrderCreateReqBO.setOrderType(validateActivity(createReqVO));
|
||||
beforeOrderCreateReqBO.setUserId(userId);
|
||||
beforeOrderCreateReqBO.setCount(getSumValue(createReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCount, Integer::sum));
|
||||
// TODO @puhui999:这里有个纠结点;handler 的定义是只处理指定类型的订单的拓展逻辑;还是通用的 handler,类似可以处理优惠劵等等
|
||||
tradeOrderHandlers.forEach(handler -> handler.beforeOrderCreate(beforeOrderCreateReqBO));
|
||||
|
||||
// 2. 价格计算
|
||||
// 0. 价格计算
|
||||
TradePriceCalculateRespBO calculateRespBO = calculatePrice(userId, createReqVO);
|
||||
|
||||
// 1. 订单创建前的逻辑
|
||||
beforeCreateTradeOrder(userId, createReqVO, calculateRespBO);
|
||||
|
||||
// 2.1 插入 TradeOrderDO 订单
|
||||
TradeOrderDO order = createTradeOrder(userId, userIp, createReqVO, calculateRespBO);
|
||||
// 2.2 插入 TradeOrderItemDO 订单项
|
||||
List<TradeOrderItemDO> orderItems = createTradeOrderItems(order, calculateRespBO);
|
||||
|
||||
// 3. 订单创建完后的逻辑
|
||||
// 3. 订单创建后的逻辑
|
||||
afterCreateTradeOrder(userId, createReqVO, order, orderItems, calculateRespBO);
|
||||
|
||||
// TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来!
|
||||
return order;
|
||||
}
|
||||
|
||||
|
||||
// TODO @puhui999:订单超时,自动取消;
|
||||
|
||||
/**
|
||||
@ -234,9 +227,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
address = validateAddress(userId, createReqVO.getAddressId());
|
||||
}
|
||||
TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(userId, clientIp, createReqVO, calculateRespBO, address);
|
||||
String no = orderNoRedisDAO.generate(TradeOrderNoRedisDAO.TRADE_ORDER_NO_PREFIX);
|
||||
order.setType(validateActivity(createReqVO));
|
||||
order.setNo(no);
|
||||
order.setType(calculateRespBO.getType());
|
||||
order.setNo(orderNoRedisDAO.generate(TradeOrderNoRedisDAO.TRADE_ORDER_NO_PREFIX));
|
||||
order.setStatus(TradeOrderStatusEnum.UNPAID.getStatus());
|
||||
order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus());
|
||||
order.setProductCount(getSumValue(calculateRespBO.getItems(), TradePriceCalculateRespBO.OrderItem::getCount, Integer::sum));
|
||||
@ -252,71 +244,80 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
return order;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验活动,并返回订单类型
|
||||
*
|
||||
* @param createReqVO 请求参数
|
||||
* @return 订单类型
|
||||
*/
|
||||
private Integer validateActivity(AppTradeOrderCreateReqVO createReqVO) {
|
||||
if (createReqVO.getSeckillActivityId() != null) {
|
||||
return TradeOrderTypeEnum.SECKILL.getType();
|
||||
}
|
||||
if (createReqVO.getCombinationActivityId() != null) {
|
||||
return TradeOrderTypeEnum.COMBINATION.getType();
|
||||
}
|
||||
// TODO 砍价敬请期待
|
||||
return TradeOrderTypeEnum.NORMAL.getType();
|
||||
}
|
||||
|
||||
private List<TradeOrderItemDO> createTradeOrderItems(TradeOrderDO tradeOrderDO, TradePriceCalculateRespBO calculateRespBO) {
|
||||
private List<TradeOrderItemDO> createTradeOrderItems(TradeOrderDO tradeOrderDO,
|
||||
TradePriceCalculateRespBO calculateRespBO) {
|
||||
List<TradeOrderItemDO> orderItems = TradeOrderConvert.INSTANCE.convertList(tradeOrderDO, calculateRespBO);
|
||||
tradeOrderItemMapper.insertBatch(orderItems);
|
||||
return orderItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行创建完创建完订单后的逻辑
|
||||
* 订单创建前,执行前置逻辑
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param createReqVO 创建订单请求
|
||||
* @param calculateRespBO 订单价格计算结果
|
||||
*/
|
||||
private void beforeCreateTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO,
|
||||
TradePriceCalculateRespBO calculateRespBO) {
|
||||
// 1. 执行订单创建前置处理器
|
||||
TradeBeforeOrderCreateReqBO beforeOrderCreateReqBO = TradeOrderConvert.INSTANCE.convert(createReqVO);
|
||||
beforeOrderCreateReqBO.setOrderType(calculateRespBO.getType());
|
||||
beforeOrderCreateReqBO.setUserId(userId);
|
||||
beforeOrderCreateReqBO.setCount(getSumValue(createReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCount, Integer::sum));
|
||||
// TODO @puhui999:这里有个纠结点;handler 的定义是只处理指定类型的订单的拓展逻辑;还是通用的 handler,类似可以处理优惠劵等等
|
||||
tradeOrderHandlers.forEach(handler -> handler.beforeOrderCreate(beforeOrderCreateReqBO));
|
||||
|
||||
// 2. 下单时扣减商品库存
|
||||
productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convertNegative(createReqVO.getItems()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单创建后,执行后置逻辑
|
||||
*
|
||||
* 例如说:优惠劵的扣减、积分的扣减、支付单的创建等等
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param createReqVO 创建订单请求
|
||||
* @param tradeOrderDO 交易订单
|
||||
* @param order 交易订单
|
||||
* @param calculateRespBO 订单价格计算结果
|
||||
*/
|
||||
private void afterCreateTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO,
|
||||
TradeOrderDO tradeOrderDO, List<TradeOrderItemDO> orderItems,
|
||||
TradeOrderDO order, List<TradeOrderItemDO> orderItems,
|
||||
TradePriceCalculateRespBO calculateRespBO) {
|
||||
// 执行订单创建后置处理器
|
||||
tradeOrderHandlers.forEach(handler -> handler.afterOrderCreate(TradeOrderConvert.INSTANCE.convert(userId, createReqVO, tradeOrderDO, orderItems.get(0))));
|
||||
// 1. 执行订单创建后置处理器
|
||||
// TODO @puhui999:从通用性来说,应该不用 orderItems.get(0)
|
||||
tradeOrderHandlers.forEach(handler -> handler.afterOrderCreate(
|
||||
TradeOrderConvert.INSTANCE.convert(userId, createReqVO, order, orderItems.get(0))));
|
||||
|
||||
// 扣减积分 TODO 芋艿:待实现,需要前置;
|
||||
// 这个是不是应该放到支付成功之后?如果支付后的话,可能积分可以重复使用哈。资源类,都要预扣
|
||||
|
||||
// 有使用优惠券时更新 TODO 芋艿:需要前置;
|
||||
// 2. 有使用优惠券时更新
|
||||
// 不在前置扣减的原因,是因为优惠劵要记录使用的订单号
|
||||
if (createReqVO.getCouponId() != null) {
|
||||
couponApi.useCoupon(new CouponUseReqDTO().setId(createReqVO.getCouponId()).setUserId(userId)
|
||||
.setOrderId(tradeOrderDO.getId()));
|
||||
.setOrderId(order.getId()));
|
||||
}
|
||||
|
||||
// 下单时扣减商品库存
|
||||
productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convertNegative(orderItems));
|
||||
// 3. 扣减积分
|
||||
// 不在前置扣减的原因,是因为积分扣减时,需要记录关联业务
|
||||
if (order.getUsePoint() != null && order.getUsePoint() > 0) {
|
||||
memberPointApi.reducePoint(userId, calculateRespBO.getUsePoint(),
|
||||
MemberPointBizTypeEnum.ORDER_USE.getType(), String.valueOf(order.getId()));
|
||||
}
|
||||
|
||||
// 删除购物车商品
|
||||
// 4. 删除购物车商品
|
||||
Set<Long> cartIds = convertSet(createReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCartId);
|
||||
if (CollUtil.isNotEmpty(cartIds)) {
|
||||
cartService.deleteCart(userId, cartIds);
|
||||
}
|
||||
|
||||
// 生成预支付
|
||||
createPayOrder(tradeOrderDO, orderItems, calculateRespBO);
|
||||
// 5. 生成预支付
|
||||
createPayOrder(order, orderItems, calculateRespBO);
|
||||
|
||||
// 增加订单日志 TODO 芋艿:待实现
|
||||
// TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来!
|
||||
}
|
||||
|
||||
|
||||
private void createPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems, TradePriceCalculateRespBO calculateRespBO) {
|
||||
private void createPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems,
|
||||
TradePriceCalculateRespBO calculateRespBO) {
|
||||
// 创建支付单,用于后续的支付
|
||||
PayOrderCreateReqDTO payOrderCreateReqDTO = TradeOrderConvert.INSTANCE.convert(
|
||||
order, orderItems, calculateRespBO, tradeOrderProperties);
|
||||
@ -344,6 +345,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
}
|
||||
// 校验活动
|
||||
// 1、拼团活动
|
||||
// TODO @puhui999:这块也抽象到 handler 里
|
||||
if (Objects.equals(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
|
||||
// 更新拼团状态 TODO puhui999:订单支付失败或订单支付过期删除这条拼团记录
|
||||
combinationRecordApi.updateRecordStatusToInProgress(order.getUserId(), order.getId(), LocalDateTime.now());
|
||||
@ -741,6 +743,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
couponApi.returnUsedCoupon(order.getCouponId());
|
||||
|
||||
// 4.回滚积分:积分是支付成功后才增加的吧? 回复:每个项目不同,目前看下来,确认收货貌似更合适,我再看看其它项目的业务选择;
|
||||
// TODO @疯狂:有赞是可配置(支付 or 确认收货),我们按照支付好列;然后这里的退积分,指的是下单时的积分抵扣。
|
||||
|
||||
// TODO 芋艿:OrderLog
|
||||
|
||||
@ -773,17 +776,18 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
|
||||
@Async
|
||||
protected void addUserPointAsync(Long userId, Integer payPrice, Long orderId) {
|
||||
int bizType = MemberPointBizTypeEnum.ORDER_BUY.getType();
|
||||
// TODO @疯狂:具体多少积分,需要分成 2 不分:1. 支付金额;2. 商品金额
|
||||
int bizType = MemberPointBizTypeEnum.ORDER_REWARD.getType();
|
||||
memberPointApi.addPoint(userId, payPrice, bizType, String.valueOf(orderId));
|
||||
}
|
||||
|
||||
@Async
|
||||
protected void reduceUserPointAsync(Long userId, Integer refundPrice, Long afterSaleId) {
|
||||
// TODO @疯狂:退款时,按照金额比例,退还积分;https://help.youzan.com/displaylist/detail_4_4-1-49185
|
||||
int bizType = MemberPointBizTypeEnum.ORDER_CANCEL.getType();
|
||||
memberPointApi.addPoint(userId, -refundPrice, bizType, String.valueOf(afterSaleId));
|
||||
}
|
||||
|
||||
|
||||
@Async
|
||||
protected void addBrokerageAsync(Long userId, Long orderId) {
|
||||
MemberUserRespDTO user = memberUserApi.getUser(userId);
|
||||
|
@ -37,6 +37,11 @@ public class TradeCombinationHandler implements TradeOrderHandler {
|
||||
|
||||
@Override
|
||||
public void afterOrderCreate(TradeAfterOrderCreateReqBO reqBO) {
|
||||
// TODO @puhui999:需要判断下;
|
||||
if (true) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建砍价记录
|
||||
combinationRecordApi.createCombinationRecord(TradeOrderConvert.INSTANCE.convert(reqBO));
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package cn.iocoder.yudao.module.trade.service.price.bo;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.Valid;
|
||||
@ -17,13 +16,6 @@ import java.util.List;
|
||||
@Data
|
||||
public class TradePriceCalculateReqBO {
|
||||
|
||||
/**
|
||||
* 订单类型
|
||||
*
|
||||
* 枚举 {@link TradeOrderTypeEnum}
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
*
|
||||
@ -37,13 +29,12 @@ public class TradePriceCalculateReqBO {
|
||||
* 对应 CouponDO 的 id 编号
|
||||
*/
|
||||
private Long couponId;
|
||||
|
||||
// TODO @疯狂:需要增加一个 PriceCalculator 实现积分扣减的计算;写回到 TradePriceCalculateRespBO 的 usePoint
|
||||
/**
|
||||
* 收货地址编号
|
||||
*
|
||||
* 对应 MemberAddressDO 的 id 编号
|
||||
* 是否使用积分
|
||||
*/
|
||||
private Long addressId;
|
||||
@NotNull(message = "是否使用积分不能为空")
|
||||
private Boolean pointStatus;
|
||||
|
||||
/**
|
||||
* 配送方式
|
||||
@ -51,6 +42,18 @@ public class TradePriceCalculateReqBO {
|
||||
* 枚举 {@link DeliveryTypeEnum}
|
||||
*/
|
||||
private Integer deliveryType;
|
||||
/**
|
||||
* 收货地址编号
|
||||
*
|
||||
* 对应 MemberAddressDO 的 id 编号
|
||||
*/
|
||||
private Long addressId;
|
||||
/**
|
||||
* 自提门店编号
|
||||
*
|
||||
* 对应 PickUpStoreDO 的 id 编号
|
||||
*/
|
||||
private Long pickUpStoreId;
|
||||
|
||||
/**
|
||||
* 商品 SKU 数组
|
||||
@ -58,6 +61,30 @@ public class TradePriceCalculateReqBO {
|
||||
@NotNull(message = "商品数组不能为空")
|
||||
private List<Item> items;
|
||||
|
||||
// ========== 秒杀活动相关字段 ==========
|
||||
/**
|
||||
* 秒杀活动编号
|
||||
*/
|
||||
private Long seckillActivityId;
|
||||
|
||||
// ========== 拼团活动相关字段 ==========
|
||||
// TODO @puhui999:是不是拼团记录的编号哈?
|
||||
/**
|
||||
* 拼团活动编号
|
||||
*/
|
||||
private Long combinationActivityId;
|
||||
|
||||
/**
|
||||
* 拼团团长编号
|
||||
*/
|
||||
private Long combinationHeadId;
|
||||
|
||||
// ========== 砍价活动相关字段 ==========
|
||||
/**
|
||||
* 砍价活动编号
|
||||
*/
|
||||
private Long bargainActivityId;
|
||||
|
||||
/**
|
||||
* 商品 SKU
|
||||
*/
|
||||
|
@ -48,6 +48,11 @@ public class TradePriceCalculateRespBO {
|
||||
*/
|
||||
private Long couponId;
|
||||
|
||||
/**
|
||||
* 使用的积分
|
||||
*/
|
||||
private Integer usePoint;
|
||||
|
||||
/**
|
||||
* 订单价格
|
||||
*/
|
||||
|
@ -2,11 +2,14 @@ package cn.iocoder.yudao.module.trade.service.price.calculator;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.module.member.api.address.AddressApi;
|
||||
import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO;
|
||||
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressTemplateService;
|
||||
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryPickUpStoreService;
|
||||
import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateRespBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
@ -22,8 +25,7 @@ import java.util.Set;
|
||||
|
||||
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.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_DELIVERY_PRICE_USER_ADDR_IS_EMPTY;
|
||||
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND;
|
||||
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
* 运费的 {@link TradePriceCalculator} 实现类
|
||||
@ -37,20 +39,41 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
|
||||
|
||||
@Resource
|
||||
private AddressApi addressApi;
|
||||
|
||||
@Resource
|
||||
private DeliveryPickUpStoreService deliveryPickUpStoreService;
|
||||
@Resource
|
||||
private DeliveryExpressTemplateService deliveryExpressTemplateService;
|
||||
|
||||
@Override
|
||||
public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
|
||||
// TODO @芋艿:如果门店自提,需要校验是否开启;
|
||||
// 1.1 判断配送方式
|
||||
if (param.getDeliveryType() == null || DeliveryTypeEnum.PICK_UP.getMode().equals(param.getDeliveryType())) {
|
||||
if (param.getDeliveryType() == null) {
|
||||
return;
|
||||
}
|
||||
if (param.getAddressId() == null) {
|
||||
throw exception(PRICE_CALCULATE_DELIVERY_PRICE_USER_ADDR_IS_EMPTY);
|
||||
if (DeliveryTypeEnum.PICK_UP.getMode().equals(param.getDeliveryType())) {
|
||||
calculateByPickUp(param, result);
|
||||
} else if (DeliveryTypeEnum.EXPRESS.getMode().equals(param.getDeliveryType())) {
|
||||
calculateExpress(param, result);
|
||||
}
|
||||
}
|
||||
|
||||
private void calculateByPickUp(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
|
||||
if (param.getPickUpStoreId() == null) {
|
||||
throw exception(PRICE_CALCULATE_DELIVERY_PRICE_PICK_UP_STORE_IS_EMPTY);
|
||||
}
|
||||
DeliveryPickUpStoreDO pickUpStore = deliveryPickUpStoreService.getDeliveryPickUpStore(param.getPickUpStoreId());
|
||||
if (pickUpStore == null || CommonStatusEnum.DISABLE.getStatus().equals(pickUpStore.getStatus())) {
|
||||
throw exception(PICK_UP_STORE_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
// ========= 快递发货 ==========
|
||||
|
||||
private void calculateExpress(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
|
||||
// 1. 得到收件地址区域
|
||||
if (param.getAddressId() == null) {
|
||||
throw exception(PRICE_CALCULATE_DELIVERY_PRICE_USER_ADDRESS_IS_EMPTY);
|
||||
}
|
||||
// 1.2 得到收件地址区域
|
||||
AddressRespDTO address = addressApi.getAddress(param.getAddressId(), param.getUserId());
|
||||
Assert.notNull(address, "收件人({})的地址,不能为空", param.getUserId());
|
||||
|
||||
@ -89,8 +112,12 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
|
||||
for (OrderItem orderItem : orderItems) {
|
||||
totalCount += orderItem.getCount();
|
||||
totalPrice += orderItem.getPayPrice();
|
||||
totalWeight += totalWeight + orderItem.getWeight() * orderItem.getCount();
|
||||
totalVolume += totalVolume + orderItem.getVolume() * orderItem.getCount();
|
||||
if (orderItem.getWeight() != null) {
|
||||
totalWeight += totalWeight + orderItem.getWeight() * orderItem.getCount();
|
||||
}
|
||||
if (orderItem.getVolume() != null) {
|
||||
totalVolume += totalVolume + orderItem.getVolume() * orderItem.getCount();
|
||||
}
|
||||
}
|
||||
// 优先判断是否包邮. 如果包邮不计算快递运费
|
||||
if (isExpressFree(templateBO.getChargeMode(), totalCount, totalWeight,
|
||||
|
@ -0,0 +1,29 @@
|
||||
package cn.iocoder.yudao.module.trade.service.price.calculator;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 使用积分的 {@link TradePriceCalculator} 实现类
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@Component
|
||||
@Order(TradePriceCalculator.ORDER_POINT_USE)
|
||||
@Slf4j
|
||||
public class TradePointUsePriceCalculator implements TradePriceCalculator {
|
||||
|
||||
@Override
|
||||
public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
|
||||
// TODO 疯狂:待实现,嘿嘿;
|
||||
if (param.getPointStatus()) {
|
||||
result.setUsePoint(10);
|
||||
} else {
|
||||
result.setUsePoint(0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -6,6 +6,9 @@ import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
/**
|
||||
* 价格计算的计算器接口
|
||||
*
|
||||
* 优惠计算顺序:
|
||||
* 1. <a href="https://help.youzan.com/displaylist/detail_4_4-1-53316">积分抵现、会员价、优惠券、粉丝专享价、满减送哪个优先计算?</>
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface TradePriceCalculator {
|
||||
@ -13,12 +16,13 @@ public interface TradePriceCalculator {
|
||||
int ORDER_DISCOUNT_ACTIVITY = 10;
|
||||
int ORDER_REWARD_ACTIVITY = 20;
|
||||
int ORDER_COUPON = 30;
|
||||
int ORDER_POINT_USE = 40;
|
||||
/**
|
||||
* 快递运费的计算
|
||||
*
|
||||
* 放在各种营销活动、优惠劵后面 TODO
|
||||
*/
|
||||
int ORDER_DELIVERY = 40;
|
||||
int ORDER_DELIVERY = 50;
|
||||
|
||||
void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result);
|
||||
|
||||
|
@ -4,6 +4,7 @@ import cn.hutool.core.lang.Assert;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
|
||||
@ -28,7 +29,7 @@ public class TradePriceCalculatorHelper {
|
||||
List<ProductSpuRespDTO> spuList, List<ProductSkuRespDTO> skuList) {
|
||||
// 创建 PriceCalculateRespDTO 对象
|
||||
TradePriceCalculateRespBO result = new TradePriceCalculateRespBO();
|
||||
result.setType(param.getType());
|
||||
result.setType(getOrderType(param));
|
||||
result.setPromotions(new ArrayList<>());
|
||||
|
||||
// 创建它的 OrderItem 属性
|
||||
@ -69,6 +70,23 @@ public class TradePriceCalculatorHelper {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算订单类型
|
||||
*
|
||||
* @param param 计算参数
|
||||
* @return 订单类型
|
||||
*/
|
||||
private static Integer getOrderType(TradePriceCalculateReqBO param) {
|
||||
if (param.getSeckillActivityId() != null) {
|
||||
return TradeOrderTypeEnum.SECKILL.getType();
|
||||
}
|
||||
if (param.getCombinationActivityId() != null) {
|
||||
return TradeOrderTypeEnum.COMBINATION.getType();
|
||||
}
|
||||
// TODO 砍价敬请期待
|
||||
return TradeOrderTypeEnum.NORMAL.getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于订单项,重新计算 price 总价
|
||||
*
|
||||
|
@ -45,7 +45,7 @@ public class TradePriceServiceImplTest extends BaseMockitoUnitTest {
|
||||
public void testCalculatePrice() {
|
||||
// 准备参数
|
||||
TradePriceCalculateReqBO calculateReqBO = new TradePriceCalculateReqBO()
|
||||
.setType(TradeOrderTypeEnum.NORMAL.getType()).setUserId(10L)
|
||||
.setUserId(10L)
|
||||
.setCouponId(20L).setAddressId(30L)
|
||||
.setItems(Arrays.asList(
|
||||
new TradePriceCalculateReqBO.Item().setSkuId(100L).setCount(1).setSelected(true),
|
||||
|
@ -21,6 +21,13 @@
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 参数校验 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.member.api.point;
|
||||
|
||||
import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
|
||||
/**
|
||||
* 用户积分的 API 接口
|
||||
*
|
||||
@ -17,6 +19,18 @@ public interface MemberPointApi {
|
||||
* @param bizType 业务类型 {@link MemberPointBizTypeEnum}
|
||||
* @param bizId 业务编号
|
||||
*/
|
||||
void addPoint(Long userId, Integer point, Integer bizType, String bizId);
|
||||
void addPoint(Long userId, @Min(value = 1L, message = "积分必须是正数") Integer point,
|
||||
Integer bizType, String bizId);
|
||||
|
||||
/**
|
||||
* 减少用户积分
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param point 积分
|
||||
* @param bizType 业务类型 {@link MemberPointBizTypeEnum}
|
||||
* @param bizId 业务编号
|
||||
*/
|
||||
void reducePoint(Long userId, @Min(value = 1L, message = "积分必须是正数") Integer point,
|
||||
Integer bizType, String bizId);
|
||||
|
||||
}
|
||||
|
@ -17,8 +17,10 @@ import java.util.Objects;
|
||||
public enum MemberPointBizTypeEnum implements IntArrayValuable {
|
||||
|
||||
SIGN(1, "签到", "签到获得 {} 积分", true),
|
||||
ORDER_BUY(10, "订单消费", "下单获得 {} 积分", true),
|
||||
ORDER_CANCEL(11, "订单取消", "退单获得 {} 积分", false); // 退回积分
|
||||
ORDER_REWARD(10, "订单奖励", "下单获得 {} 积分", true),
|
||||
ORDER_CANCEL(11, "订单取消", "退单获得 {} 积分", false), // 退回积分
|
||||
ORDER_USE(12, "订单使用", "下单使用 {} 积分", false), // 扣减积分
|
||||
;
|
||||
|
||||
/**
|
||||
* 类型
|
||||
|
@ -31,4 +31,13 @@ public class MemberPointApiImpl implements MemberPointApi {
|
||||
memberPointRecordService.createPointRecord(userId, point, bizTypeEnum, bizId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reducePoint(Long userId, Integer point, Integer bizType, String bizId) {
|
||||
MemberPointBizTypeEnum bizTypeEnum = MemberPointBizTypeEnum.getByType(bizType);
|
||||
if (bizTypeEnum == null) {
|
||||
throw exception(POINT_RECORD_BIZ_NOT_SUPPORT);
|
||||
}
|
||||
memberPointRecordService.createPointRecord(userId, point, bizTypeEnum, bizId);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user