parent
a778c25075
commit
054569a3d3
@ -1,10 +0,0 @@ |
||||
package cn.iocoder.yudao.module.promotion.api.combination; |
||||
|
||||
/** |
||||
* 拼团活动 Api 接口 |
||||
* |
||||
* @author HUIHUI |
||||
*/ |
||||
public interface CombinationActivityApi { |
||||
|
||||
} |
@ -1,21 +0,0 @@ |
||||
package cn.iocoder.yudao.module.promotion.api.price; |
||||
|
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO; |
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO; |
||||
|
||||
/** |
||||
* 价格 API 接口 |
||||
* |
||||
* @author 芋道源码 |
||||
*/ |
||||
public interface PriceApi { |
||||
|
||||
/** |
||||
* 计算商品的价格 |
||||
* |
||||
* @param calculateReqDTO 价格请求 |
||||
* @return 价格相应 |
||||
*/ |
||||
PriceCalculateRespDTO calculatePrice(PriceCalculateReqDTO calculateReqDTO); |
||||
|
||||
} |
@ -1,35 +0,0 @@ |
||||
package cn.iocoder.yudao.module.promotion.api.price.dto; |
||||
|
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* 优惠劵的匹配信息 Response DTO |
||||
* |
||||
* why 放在 price 包下?主要获取的时候,需要涉及到较多的价格计算逻辑,放在 price 可以更好的复用逻辑 |
||||
* |
||||
* @author 芋道源码 |
||||
*/ |
||||
@Data |
||||
public class CouponMeetRespDTO { |
||||
|
||||
/** |
||||
* 优惠劵编号 |
||||
*/ |
||||
private Long id; |
||||
|
||||
// ========== 非优惠劵的基本信息字段 ==========
|
||||
/** |
||||
* 是否匹配 |
||||
*/ |
||||
private Boolean meet; |
||||
/** |
||||
* 不匹配的提示,即 {@link #meet} = true 才有值 |
||||
* |
||||
* 例如说: |
||||
* 1. 所结算商品没有符合条件的商品 |
||||
* 2. 差 XXX 元可用优惠劵 |
||||
* 3. 优惠劵未到使用时间 |
||||
*/ |
||||
private String meetTip; |
||||
|
||||
} |
@ -1,62 +0,0 @@ |
||||
package cn.iocoder.yudao.module.promotion.api.price.dto; |
||||
|
||||
import lombok.Data; |
||||
|
||||
import javax.validation.constraints.Min; |
||||
import javax.validation.constraints.NotNull; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* 价格计算 Request DTO |
||||
* |
||||
* @author 芋道源码 |
||||
*/ |
||||
@Data |
||||
@Deprecated |
||||
public class PriceCalculateReqDTO { |
||||
|
||||
/** |
||||
* 用户编号 |
||||
* |
||||
* 对应 MemberUserDO 的 id 编号 |
||||
*/ |
||||
private Long userId; |
||||
|
||||
/** |
||||
* 优惠劵编号 |
||||
*/ |
||||
private Long couponId; |
||||
|
||||
/** |
||||
* 收货地址编号 |
||||
*/ |
||||
private Long addressId; |
||||
|
||||
/** |
||||
* 商品 SKU 数组 |
||||
*/ |
||||
@NotNull(message = "商品数组不能为空") |
||||
private List<Item> items; |
||||
|
||||
/** |
||||
* 商品 SKU |
||||
*/ |
||||
@Data |
||||
public static class Item { |
||||
|
||||
/** |
||||
* SKU 编号 |
||||
*/ |
||||
@NotNull(message = "商品 SKU 编号不能为空") |
||||
private Long skuId; |
||||
|
||||
/** |
||||
* SKU 数量 |
||||
*/ |
||||
@NotNull(message = "商品 SKU 数量不能为空") |
||||
@Min(value = 0L, message = "商品 SKU 数量必须大于等于 0") // 可传递 0 数量,用于购物车未选中的情况
|
||||
private Integer count; |
||||
|
||||
} |
||||
|
||||
} |
@ -1,252 +0,0 @@ |
||||
package cn.iocoder.yudao.module.promotion.api.price.dto; |
||||
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; |
||||
import lombok.Data; |
||||
|
||||
import java.util.List; |
||||
|
||||
/** |
||||
* 价格计算 Response DTO |
||||
* |
||||
* 整体设计,参考 taobao 的技术文档: |
||||
* 1. <a href="https://developer.alibaba.com/docs/doc.htm?treeId=1&articleId=1029&docType=1">订单管理</a> |
||||
* 2. <a href="https://open.taobao.com/docV3.htm?docId=108471&docType=1">常用订单金额说明</a> |
||||
* |
||||
* 举个例子:<a href="https://img.alicdn.com/top/i1/LB1mALAi4HI8KJjy1zbXXaxdpXa">订单图</a> |
||||
* 输入: |
||||
* 1. 订单实付: trade.payment = 198.00;订单邮费:5 元; |
||||
* 2. 商品级优惠 圣诞价: 省 29.00 元 和 圣诞价:省 150.00 元; 订单级优惠,圣诞 2:省 5.00 元; |
||||
* 分摊: |
||||
* 1. 商品 1:原价 108 元,优惠 29 元,子订单实付 79 元,分摊主订单优惠 1.99 元; |
||||
* 2. 商品 2:原价 269 元,优惠 150 元,子订单实付 119 元,分摊主订单优惠 3.01 元; |
||||
* |
||||
* @author 芋道源码 |
||||
*/ |
||||
@Data |
||||
@Deprecated |
||||
public class PriceCalculateRespDTO { |
||||
|
||||
/** |
||||
* 订单 |
||||
*/ |
||||
private Order order; |
||||
|
||||
/** |
||||
* 营销活动数组 |
||||
* |
||||
* 只对应 {@link Order#items} 商品匹配的活动 |
||||
*/ |
||||
private List<Promotion> promotions; |
||||
|
||||
// TODO @芋艿:需要改造下,主要是价格字段
|
||||
/** |
||||
* 订单 |
||||
*/ |
||||
@Data |
||||
public static class Order { |
||||
|
||||
/** |
||||
* 商品原价(总),单位:分 |
||||
* |
||||
* 基于 {@link OrderItem#getOriginalPrice()} 求和 |
||||
* |
||||
* 对应 taobao 的 trade.total_fee 字段 |
||||
*/ |
||||
private Integer totalPrice; |
||||
/** |
||||
* 订单优惠(总),单位:分 |
||||
* |
||||
* 订单级优惠:对主订单的优惠,常见如:订单满 200 元减 10 元;订单满 80 包邮。 |
||||
* |
||||
* 对应 taobao 的 order.discount_fee 字段 |
||||
*/ |
||||
private Integer discountPrice; |
||||
/** |
||||
* 优惠劵减免金额(总),单位:分 |
||||
* |
||||
* 对应 taobao 的 trade.coupon_fee 字段 |
||||
*/ |
||||
private Integer couponPrice; |
||||
/** |
||||
* 积分减免金额(总),单位:分 |
||||
* |
||||
* 对应 taobao 的 trade.point_fee 字段 |
||||
*/ |
||||
private Integer pointPrice; |
||||
/** |
||||
* 运费金额,单位:分 |
||||
*/ |
||||
private Integer deliveryPrice; |
||||
/** |
||||
* 最终购买金额(总),单位:分 |
||||
* |
||||
* = {@link OrderItem#getPayPrice()} 求和 |
||||
* - {@link #couponPrice} |
||||
* - {@link #pointPrice} |
||||
* + {@link #deliveryPrice} |
||||
* - {@link #discountPrice} |
||||
*/ |
||||
private Integer payPrice; |
||||
/** |
||||
* 商品 SKU 数组 |
||||
*/ |
||||
private List<OrderItem> items; |
||||
|
||||
// ========== 营销基本信息 ==========
|
||||
/** |
||||
* 优惠劵编号 |
||||
*/ |
||||
private Long couponId; |
||||
|
||||
} |
||||
|
||||
/** |
||||
* 订单商品 SKU |
||||
*/ |
||||
@Data |
||||
public static class OrderItem { |
||||
|
||||
/** |
||||
* SPU 编号 |
||||
*/ |
||||
private Long spuId; |
||||
/** |
||||
* SKU 编号 |
||||
*/ |
||||
private Long skuId; |
||||
/** |
||||
* 购买数量 |
||||
*/ |
||||
private Integer count; |
||||
|
||||
/** |
||||
* 商品原价(总),单位:分 |
||||
* |
||||
* = {@link #originalUnitPrice} * {@link #getCount()} |
||||
*/ |
||||
private Integer originalPrice; |
||||
/** |
||||
* 商品原价(单),单位:分 |
||||
* |
||||
* 对应 ProductSkuDO 的 price 字段 |
||||
* 对应 taobao 的 order.price 字段 |
||||
*/ |
||||
private Integer originalUnitPrice; |
||||
/** |
||||
* 商品优惠(总),单位:分 |
||||
* |
||||
* 商品级优惠:对单个商品的,常见如:商品原价的 8 折;商品原价的减 50 元 |
||||
* |
||||
* 对应 taobao 的 order.discount_fee 字段 |
||||
*/ |
||||
private Integer discountPrice; |
||||
/** |
||||
* 子订单实付金额,不算主订单分摊金额,单位:分 |
||||
* |
||||
* = {@link #originalPrice} |
||||
* - {@link #discountPrice} |
||||
* |
||||
* 对应 taobao 的 order.payment 字段 |
||||
*/ |
||||
private Integer payPrice; |
||||
|
||||
/** |
||||
* 子订单分摊金额(总),单位:分 |
||||
* 需要分摊 {@link Order#discountPrice}、{@link Order#couponPrice}、{@link Order#pointPrice} |
||||
* |
||||
* 对应 taobao 的 order.part_mjz_discount 字段 |
||||
* 淘宝说明:子订单分摊优惠基础逻辑:一般正常优惠券和满减优惠按照子订单的金额进行分摊,特殊情况如果优惠券是指定商品使用的,只会分摊到对应商品子订单上不分摊。 |
||||
*/ |
||||
private Integer orderPartPrice; |
||||
/** |
||||
* 分摊后子订单实付金额(总),单位:分 |
||||
* |
||||
* = {@link #payPrice} |
||||
* - {@link #orderPartPrice} |
||||
* |
||||
* 对应 taobao 的 divide_order_fee 字段 |
||||
*/ |
||||
private Integer orderDividePrice; |
||||
|
||||
} |
||||
|
||||
/** |
||||
* 营销明细 |
||||
*/ |
||||
@Data |
||||
@Deprecated |
||||
public static class Promotion { |
||||
|
||||
/** |
||||
* 营销编号 |
||||
* |
||||
* 例如说:营销活动的编号、优惠劵的编号 |
||||
*/ |
||||
private Long id; |
||||
/** |
||||
* 营销名字 |
||||
*/ |
||||
private String name; |
||||
/** |
||||
* 营销类型 |
||||
* |
||||
* 枚举 {@link PromotionTypeEnum} |
||||
*/ |
||||
private Integer type; |
||||
/** |
||||
* 营销级别 |
||||
* |
||||
* 枚举 @link PromotionLevelEnum} TODO PromotionLevelEnum 没有这个枚举类 |
||||
*/ |
||||
private Integer level; |
||||
/** |
||||
* 计算时的原价(总),单位:分 |
||||
*/ |
||||
private Integer totalPrice; |
||||
/** |
||||
* 计算时的优惠(总),单位:分 |
||||
*/ |
||||
private Integer discountPrice; |
||||
/** |
||||
* 匹配的商品 SKU 数组 |
||||
*/ |
||||
private List<PromotionItem> items; |
||||
|
||||
// ========== 匹配情况 ==========
|
||||
|
||||
/** |
||||
* 是否满足优惠条件 |
||||
*/ |
||||
private Boolean match; |
||||
/** |
||||
* 满足条件的提示 |
||||
* |
||||
* 如果 {@link #match} = true 满足,则提示“圣诞价:省 150.00 元” |
||||
* 如果 {@link #match} = false 不满足,则提示“购满 85 元,可减 40 元” |
||||
*/ |
||||
private String description; |
||||
|
||||
} |
||||
|
||||
/** |
||||
* 营销匹配的商品 SKU |
||||
*/ |
||||
@Data |
||||
public static class PromotionItem { |
||||
|
||||
/** |
||||
* 商品 SKU 编号 |
||||
*/ |
||||
private Long skuId; |
||||
/** |
||||
* 计算时的原价(总),单位:分 |
||||
*/ |
||||
private Integer originalPrice; |
||||
/** |
||||
* 计算时的优惠(总),单位:分 |
||||
*/ |
||||
private Integer discountPrice; |
||||
|
||||
} |
||||
|
||||
} |
@ -1,13 +0,0 @@ |
||||
package cn.iocoder.yudao.module.promotion.api.combination; |
||||
|
||||
import org.springframework.stereotype.Service; |
||||
|
||||
/** |
||||
* 拼团活动 Api 接口实现类 |
||||
* |
||||
* @author HUIHUI |
||||
*/ |
||||
@Service |
||||
public class CombinationActivityApiImpl implements CombinationActivityApi { |
||||
|
||||
} |
@ -1,28 +0,0 @@ |
||||
package cn.iocoder.yudao.module.promotion.api.price; |
||||
|
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO; |
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO; |
||||
import cn.iocoder.yudao.module.promotion.service.price.PriceService; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
import javax.annotation.Resource; |
||||
|
||||
/** |
||||
* 价格 API 实现类 |
||||
* |
||||
* @author 芋道源码 |
||||
*/ |
||||
@Service |
||||
public class PriceApiImpl implements PriceApi { |
||||
|
||||
@Resource |
||||
private PriceService priceService; |
||||
|
||||
@Override |
||||
public PriceCalculateRespDTO calculatePrice(PriceCalculateReqDTO calculateReqDTO) { |
||||
//return priceService.calculatePrice(calculateReqDTO); TODO 没有 calculatePrice 这个方法
|
||||
|
||||
return null; |
||||
} |
||||
|
||||
} |
@ -1,49 +0,0 @@ |
||||
package cn.iocoder.yudao.module.promotion.convert.price; |
||||
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.CouponMeetRespDTO; |
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO; |
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO; |
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; |
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; |
||||
import org.mapstruct.Mapper; |
||||
import org.mapstruct.factory.Mappers; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
@Mapper |
||||
public interface PriceConvert { |
||||
|
||||
PriceConvert INSTANCE = Mappers.getMapper(PriceConvert.class); |
||||
|
||||
default PriceCalculateRespDTO convert(PriceCalculateReqDTO calculateReqDTO, List<ProductSkuRespDTO> skuList) { |
||||
// 创建 PriceCalculateRespDTO 对象
|
||||
PriceCalculateRespDTO priceCalculate = new PriceCalculateRespDTO(); |
||||
// 创建它的 Order 属性
|
||||
PriceCalculateRespDTO.Order order = new PriceCalculateRespDTO.Order().setTotalPrice(0).setDiscountPrice(0) |
||||
.setCouponPrice(0).setPointPrice(0).setDeliveryPrice(0).setPayPrice(0) |
||||
.setItems(new ArrayList<>()).setCouponId(calculateReqDTO.getCouponId()); |
||||
priceCalculate.setOrder(order).setPromotions(new ArrayList<>()); |
||||
// 创建它的 OrderItem 属性
|
||||
Map<Long, Integer> skuIdCountMap = CollectionUtils.convertMap(calculateReqDTO.getItems(), |
||||
PriceCalculateReqDTO.Item::getSkuId, PriceCalculateReqDTO.Item::getCount); |
||||
skuList.forEach(sku -> { |
||||
Integer count = skuIdCountMap.get(sku.getId()); |
||||
PriceCalculateRespDTO.OrderItem orderItem = new PriceCalculateRespDTO.OrderItem() |
||||
.setSpuId(sku.getSpuId()).setSkuId(sku.getId()).setCount(count) |
||||
.setOriginalUnitPrice(sku.getPrice()).setOriginalPrice(sku.getPrice() * count) |
||||
.setDiscountPrice(0).setOrderPartPrice(0); |
||||
orderItem.setPayPrice(orderItem.getOriginalPrice()).setOrderDividePrice(orderItem.getOriginalPrice()); |
||||
priceCalculate.getOrder().getItems().add(orderItem); |
||||
// 补充价格信息到 Order 中
|
||||
order.setTotalPrice(order.getTotalPrice() + orderItem.getOriginalPrice()) |
||||
.setPayPrice(order.getTotalPrice()); |
||||
}); |
||||
return priceCalculate; |
||||
} |
||||
|
||||
CouponMeetRespDTO convert(CouponDO coupon); |
||||
|
||||
} |
@ -1,23 +0,0 @@ |
||||
package cn.iocoder.yudao.module.promotion.service.price; |
||||
|
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.CouponMeetRespDTO; |
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO; |
||||
|
||||
import java.util.List; |
||||
|
||||
/** |
||||
* 价格计算 Service 接口 |
||||
* |
||||
* @author 芋道源码 |
||||
*/ |
||||
public interface PriceService { |
||||
|
||||
/** |
||||
* 获得优惠劵的匹配信息列表 |
||||
* |
||||
* @param calculateReqDTO 价格请求 |
||||
* @return 价格响应 |
||||
*/ |
||||
List<CouponMeetRespDTO> getMeetCouponList(PriceCalculateReqDTO calculateReqDTO); |
||||
|
||||
} |
@ -1,96 +0,0 @@ |
||||
package cn.iocoder.yudao.module.promotion.service.price; |
||||
|
||||
import cn.hutool.core.collection.CollUtil; |
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException; |
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.CouponMeetRespDTO; |
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO; |
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO; |
||||
import cn.iocoder.yudao.module.promotion.convert.price.PriceConvert; |
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; |
||||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum; |
||||
import cn.iocoder.yudao.module.promotion.service.coupon.CouponService; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.stereotype.Service; |
||||
import org.springframework.validation.annotation.Validated; |
||||
|
||||
import javax.annotation.Resource; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue; |
||||
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_VALID_TIME_NOT_NOW; |
||||
|
||||
/** |
||||
* 价格计算 Service 实现类 |
||||
* |
||||
* 优惠计算顺序:min(限时折扣, 会员折扣) > 满减送 > 优惠券。 |
||||
* 参考文档: |
||||
* 1. <a href="https://help.youzan.com/displaylist/detail_4_4-1-60384">有赞文档:限时折扣、满减送、优惠券哪个优先计算?</a> |
||||
* |
||||
* TODO 芋艿:进一步完善 |
||||
* 1. 限时折扣:指定金额、减免金额、折扣 |
||||
* 2. 满减送:循环、折扣 |
||||
* 3. 优惠劵:待定 |
||||
* |
||||
* @author 芋道源码 |
||||
*/ |
||||
@Service |
||||
@Validated |
||||
@Slf4j |
||||
public class PriceServiceImpl implements PriceService { |
||||
|
||||
@Resource |
||||
private CouponService couponService; |
||||
|
||||
@Override |
||||
public List<CouponMeetRespDTO> getMeetCouponList(PriceCalculateReqDTO calculateReqDTO) { |
||||
// 先计算一轮价格
|
||||
// PriceCalculateRespDTO priceCalculate = calculatePrice(calculateReqDTO);
|
||||
PriceCalculateRespDTO priceCalculate = null; |
||||
|
||||
// 获得用户的待使用优惠劵
|
||||
List<CouponDO> couponList = couponService.getCouponList(calculateReqDTO.getUserId(), CouponStatusEnum.UNUSED.getStatus()); |
||||
if (CollUtil.isEmpty(couponList)) { |
||||
return Collections.emptyList(); |
||||
} |
||||
|
||||
// 获得优惠劵的匹配信息
|
||||
return CollectionUtils.convertList(couponList, coupon -> { |
||||
CouponMeetRespDTO couponMeetRespDTO = PriceConvert.INSTANCE.convert(coupon); |
||||
try { |
||||
// 校验优惠劵
|
||||
couponService.validCoupon(coupon); |
||||
|
||||
// 获得匹配的商品 SKU 数组
|
||||
// TODO 芋艿:后续处理
|
||||
// List<PriceCalculateRespDTO.OrderItem> orderItems = getMatchCouponOrderItems(priceCalculate, coupon);
|
||||
List<PriceCalculateRespDTO.OrderItem> orderItems = null; |
||||
if (CollUtil.isEmpty(orderItems)) { |
||||
return couponMeetRespDTO.setMeet(false).setMeetTip("所结算商品没有符合条件的商品"); |
||||
} |
||||
|
||||
// 计算是否满足优惠劵的使用金额
|
||||
Integer originPrice = getSumValue(orderItems, PriceCalculateRespDTO.OrderItem::getOrderDividePrice, Integer::sum); |
||||
assert originPrice != null; |
||||
if (originPrice < coupon.getUsePrice()) { |
||||
return couponMeetRespDTO.setMeet(false) |
||||
// .setMeetTip(String.format("差 %s 元可用优惠劵", formatPrice(coupon.getUsePrice() - originPrice)));
|
||||
.setMeetTip("所结算的商品中未满足使用的金额"); |
||||
} |
||||
} catch (ServiceException serviceException) { |
||||
couponMeetRespDTO.setMeet(false); |
||||
if (serviceException.getCode().equals(COUPON_VALID_TIME_NOT_NOW.getCode())) { |
||||
couponMeetRespDTO.setMeetTip("优惠劵未到使用时间"); |
||||
} else { |
||||
log.error("[getMeetCouponList][calculateReqDTO({}) 获得优惠劵匹配信息异常]", calculateReqDTO, serviceException); |
||||
couponMeetRespDTO.setMeetTip("优惠劵不满足使用条件"); |
||||
} |
||||
return couponMeetRespDTO; |
||||
} |
||||
// 满足
|
||||
return couponMeetRespDTO.setMeet(true); |
||||
}); |
||||
} |
||||
|
||||
} |
@ -1,104 +0,0 @@ |
||||
package cn.iocoder.yudao.module.promotion.service.price; |
||||
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException; |
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; |
||||
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; |
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; |
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.CouponMeetRespDTO; |
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO; |
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; |
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum; |
||||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum; |
||||
import cn.iocoder.yudao.module.promotion.service.coupon.CouponService; |
||||
import cn.iocoder.yudao.module.promotion.service.reward.RewardActivityService; |
||||
import org.junit.jupiter.api.Test; |
||||
import org.mockito.InjectMocks; |
||||
import org.mockito.Mock; |
||||
|
||||
import java.util.List; |
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; |
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; |
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
||||
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_VALID_TIME_NOT_NOW; |
||||
import static java.util.Arrays.asList; |
||||
import static java.util.Collections.singletonList; |
||||
import static org.junit.jupiter.api.Assertions.*; |
||||
import static org.mockito.ArgumentMatchers.eq; |
||||
import static org.mockito.Mockito.doThrow; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
/** |
||||
* {@link PriceServiceImpl} 的单元测试 |
||||
* |
||||
* @author 芋道源码 |
||||
*/ |
||||
public class PriceServiceTest extends BaseMockitoUnitTest { |
||||
|
||||
@InjectMocks |
||||
private PriceServiceImpl priceService; |
||||
|
||||
@Mock |
||||
private RewardActivityService rewardActivityService; |
||||
@Mock |
||||
private CouponService couponService; |
||||
@Mock |
||||
private ProductSkuApi productSkuApi; |
||||
|
||||
/** |
||||
* 测试满减送活动,不匹配的情况 |
||||
*/ |
||||
@Test |
||||
public void testCalculatePrice_rewardActivityNotMeet() { |
||||
|
||||
} |
||||
|
||||
@Test |
||||
public void testGetMeetCouponList() { |
||||
// 准备参数
|
||||
PriceCalculateReqDTO calculateReqDTO = new PriceCalculateReqDTO().setUserId(1024L) |
||||
.setItems(singletonList(new PriceCalculateReqDTO.Item().setSkuId(10L).setCount(2))); |
||||
// mock 方法(商品 SKU 信息)
|
||||
ProductSkuRespDTO productSku = randomPojo(ProductSkuRespDTO.class, o -> o.setId(10L).setPrice(100)); |
||||
when(productSkuApi.getSkuList(eq(asSet(10L)))).thenReturn(singletonList(productSku)); |
||||
// mock 方法(情况一:优惠劵未到使用时间)
|
||||
CouponDO coupon01 = randomPojo(CouponDO.class); |
||||
doThrow(new ServiceException(COUPON_VALID_TIME_NOT_NOW)).when(couponService).validCoupon(coupon01); |
||||
// mock 方法(情况二:所结算商品没有符合条件的商品)
|
||||
CouponDO coupon02 = randomPojo(CouponDO.class); |
||||
// mock 方法(情况三:使用金额不足)
|
||||
CouponDO coupon03 = randomPojo(CouponDO.class, o -> o.setProductScope(PromotionProductScopeEnum.ALL.getScope()) |
||||
.setUsePrice(300)); |
||||
// mock 方法(情况五:满足条件)
|
||||
CouponDO coupon04 = randomPojo(CouponDO.class, o -> o.setProductScope(PromotionProductScopeEnum.ALL.getScope()) |
||||
.setUsePrice(190)); |
||||
// mock 方法(获得用户的待使用优惠劵)
|
||||
when(couponService.getCouponList(eq(1024L), eq(CouponStatusEnum.UNUSED.getStatus()))) |
||||
.thenReturn(asList(coupon01, coupon02, coupon03, coupon04)); |
||||
// 调用
|
||||
List<CouponMeetRespDTO> list = priceService.getMeetCouponList(calculateReqDTO); |
||||
// 断言
|
||||
assertEquals(list.size(), 4); |
||||
// 断言情况一:优惠劵未到使用时间
|
||||
CouponMeetRespDTO couponMeetRespDTO01 = list.get(0); |
||||
assertPojoEquals(couponMeetRespDTO01, coupon01); |
||||
assertFalse(couponMeetRespDTO01.getMeet()); |
||||
assertEquals(couponMeetRespDTO01.getMeetTip(), "优惠劵未到使用时间"); |
||||
// 断言情况二:所结算商品没有符合条件的商品
|
||||
CouponMeetRespDTO couponMeetRespDTO02 = list.get(1); |
||||
assertPojoEquals(couponMeetRespDTO02, coupon02); |
||||
assertFalse(couponMeetRespDTO02.getMeet()); |
||||
assertEquals(couponMeetRespDTO02.getMeetTip(), "所结算商品没有符合条件的商品"); |
||||
// 断言情况三:差 %s 元可用优惠劵
|
||||
CouponMeetRespDTO couponMeetRespDTO03 = list.get(2); |
||||
assertPojoEquals(couponMeetRespDTO03, coupon03); |
||||
assertFalse(couponMeetRespDTO03.getMeet()); |
||||
assertEquals(couponMeetRespDTO03.getMeetTip(), "所结算的商品中未满足使用的金额"); |
||||
// 断言情况四:满足条件
|
||||
CouponMeetRespDTO couponMeetRespDTO04 = list.get(3); |
||||
assertPojoEquals(couponMeetRespDTO04, coupon04); |
||||
assertTrue(couponMeetRespDTO04.getMeet()); |
||||
assertNull(couponMeetRespDTO04.getMeetTip()); |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue