Merge branch 'develop' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into master-jdk17

This commit is contained in:
YunaiV
2024-09-07 08:47:56 +08:00
71 changed files with 1624 additions and 918 deletions

View File

@@ -4,10 +4,10 @@ import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import jakarta.annotation.Resource;
import java.util.Collection;
import java.util.List;
@@ -36,8 +36,8 @@ public class TradeOrderApiImpl implements TradeOrderApi {
}
@Override
public void cancelPaidOrder(Long userId, Long orderId) {
tradeOrderUpdateService.cancelPaidOrder(userId, orderId);
public void cancelPaidOrder(Long userId, Long orderId, Integer cancelType) {
tradeOrderUpdateService.cancelPaidOrder(userId, orderId, cancelType);
}
}

View File

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.trade.dal.dataobject.order;
import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
@@ -12,10 +13,14 @@ import cn.iocoder.yudao.module.trade.enums.order.TradeOrderRefundStatusEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.*;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
/**
* 交易订单 DO
@@ -291,6 +296,24 @@ public class TradeOrderDO extends BaseDO {
*/
private Integer vipPrice;
/**
* 赠送的优惠劵
*
* key: 优惠劵模版编号
* value对应的优惠券数量
*
* 目的:用于订单支付后赠送优惠券
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private Map<Long, Integer> giveCouponTemplateCounts;
/**
* 赠送的优惠劵编号
*
* 目的:用于后续取消或者售后订单时,需要扣减赠送
*/
@TableField(typeHandler = LongListTypeHandler.class)
private List<Long> giveCouponIds;
/**
* 秒杀活动编号
*

View File

@@ -268,11 +268,6 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
return false;
}
// 校验分佣模式:仅可后台手动设置推广员
// if (BrokerageEnabledConditionEnum.ADMIN.getCondition().equals(tradeConfig.getBrokerageEnabledCondition())) {
// throw exception(BROKERAGE_BIND_CONDITION_ADMIN);
// }
// 校验分销关系绑定模式
if (BrokerageBindModeEnum.REGISTER.getMode().equals(tradeConfig.getBrokerageBindMode())) {
// 判断是否为新用户:注册时间在 30 秒内的,都算新用户

View File

@@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.trade.service.order;
import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
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.TradeOrderUpdateAddressReqVO;
@@ -10,9 +9,10 @@ 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.item.AppTradeOrderItemCommentCreateReqVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import jakarta.validation.constraints.NotNull;
import java.util.List;
/**
* 交易订单【写】Service 接口
*
@@ -187,13 +187,22 @@ public interface TradeOrderUpdateService {
*/
void updateOrderCombinationInfo(Long orderId, Long activityId, Long combinationRecordId, Long headId);
// TODO 芋艿:拼团取消,不调这个接口哈;
/**
* 取消支付订单
*
* @param userId 用户编号
* @param orderId 订单编号
* @param userId 用户编号
* @param orderId 订单编号
* @param cancelType 取消类型
*/
void cancelPaidOrder(Long userId, Long orderId);
void cancelPaidOrder(Long userId, Long orderId, Integer cancelType);
/**
* 更新下单赠送的优惠券编号到订单
*
* @param userId 用户编号
* @param orderId 订单编号
* @param giveCouponIds 赠送的优惠券编号列表
*/
void updateOrderGiveCouponIds(Long userId, Long orderId, List<Long> giveCouponIds);
}

View File

@@ -18,6 +18,8 @@ import cn.iocoder.yudao.module.member.api.address.dto.MemberAddressRespDTO;
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.PayOrderRespDTO;
import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi;
import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
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.dto.ProductCommentCreateReqDTO;
@@ -111,6 +113,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
private ProductCommentApi productCommentApi;
@Resource
public SocialClientApi socialClientApi;
@Resource
public PayRefundApi payRefundApi;
@Resource
private TradeOrderProperties tradeOrderProperties;
@@ -197,6 +201,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus());
order.setProductCount(getSumValue(calculateRespBO.getItems(), TradePriceCalculateRespBO.OrderItem::getCount, Integer::sum));
order.setUserIp(getClientIP()).setTerminal(getTerminal());
// 使用 + 赠送优惠券
order.setGiveCouponTemplateCounts(calculateRespBO.getGiveCouponTemplateCounts());
// 支付 + 退款信息
order.setAdjustPrice(0).setPayStatus(false);
order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()).setRefundPrice(0);
@@ -854,15 +860,46 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
@Override
@Transactional(rollbackFor = Exception.class)
public void cancelPaidOrder(Long userId, Long orderId) {
// TODO @puhui999需要校验状态已支付的情况下才可以。
public void cancelPaidOrder(Long userId, Long orderId, Integer cancelType) {
// 1.1 这里校验下 cancelType 只允许拼团关闭;
if (ObjUtil.notEqual(TradeOrderCancelTypeEnum.COMBINATION_CLOSE.getType(), cancelType)) {
return;
}
// 1.2 检验订单存在
TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(orderId, userId);
if (order == null) {
throw exception(ORDER_NOT_FOUND);
}
cancelOrder0(order, TradeOrderCancelTypeEnum.MEMBER_CANCEL);
// TODO @puhui999需要退款
// 1.3 校验订单是否支付
if (!order.getPayStatus()) {
throw exception(ORDER_CANCEL_PAID_FAIL, "已支付");
}
// 1.3 校验订单是否已退款
if (ObjUtil.equal(TradeOrderRefundStatusEnum.NONE.getStatus(), order.getRefundStatus())) {
throw exception(ORDER_CANCEL_PAID_FAIL, "未退款");
}
// 2.1 取消订单
cancelOrder0(order, TradeOrderCancelTypeEnum.COMBINATION_CLOSE);
// 2.2 创建退款单
payRefundApi.createRefund(new PayRefundCreateReqDTO()
.setAppKey(tradeOrderProperties.getPayAppKey()).setUserIp(getClientIP()) // 支付应用
.setMerchantOrderId(String.valueOf(order.getId())) // 支付单号
.setMerchantRefundId(String.valueOf(order.getId()))
.setReason(TradeOrderCancelTypeEnum.COMBINATION_CLOSE.getName()).setPrice(order.getPayPrice()));// 价格信息
}
@Override
public void updateOrderGiveCouponIds(Long userId, Long orderId, List<Long> giveCouponIds) {
// 1. 检验订单存在
TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(orderId, userId);
if (order == null) {
throw exception(ORDER_NOT_FOUND);
}
// 2. 更新订单赠送的优惠券编号列表
tradeOrderMapper.updateById(new TradeOrderDO().setId(orderId).setGiveCouponIds(giveCouponIds));
}
/**

View File

@@ -1,12 +1,16 @@
package cn.iocoder.yudao.module.trade.service.order.handler;
import cn.hutool.core.collection.CollUtil;
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 cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import jakarta.annotation.Resource;
import java.util.List;
/**
@@ -17,6 +21,12 @@ import java.util.List;
@Component
public class TradeCouponOrderHandler implements TradeOrderHandler {
@Resource
@Lazy // 延迟加载,避免循环依赖
private TradeOrderUpdateService orderUpdateService;
@Resource
private TradeOrderQueryService orderQueryService;
@Resource
private CouponApi couponApi;
@@ -31,12 +41,30 @@ public class TradeCouponOrderHandler implements TradeOrderHandler {
}
@Override
public void afterCancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
if (order.getCouponId() == null || order.getCouponId() <= 0) {
public void afterPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
if (CollUtil.isEmpty(order.getGiveCouponTemplateCounts())) {
return;
}
// 退回优惠
couponApi.returnUsedCoupon(order.getCouponId());
// 赠送优惠
List<Long> couponIds = couponApi.takeCouponsByAdmin(order.getGiveCouponTemplateCounts(), order.getUserId());
if (CollUtil.isEmpty(couponIds)) {
return;
}
orderUpdateService.updateOrderGiveCouponIds(order.getUserId(), order.getId(), couponIds);
}
@Override
public void afterCancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
// 情况一:退还订单使用的优惠券
if (order.getCouponId() != null && order.getCouponId() > 0) {
// 退回优惠劵
couponApi.returnUsedCoupon(order.getCouponId());
}
// 情况二:收回赠送的优惠券
if (CollUtil.isEmpty(order.getGiveCouponIds())) {
return;
}
couponApi.invalidateCouponsByAdmin(order.getGiveCouponIds(), order.getUserId());
}
}

View File

@@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* 价格计算 Response BO
@@ -67,6 +68,21 @@ public class TradePriceCalculateRespBO {
*/
private Long bargainActivityId;
/**
* 是否包邮
*/
private Boolean freeDelivery;
/**
* 赠送的优惠劵
*
* key: 优惠劵模版编号
* value对应的优惠券数量
*
* 目的:用于订单支付后赠送优惠券
*/
private Map<Long, Integer> giveCouponTemplateCounts;
/**
* 订单价格
*/
@@ -213,8 +229,19 @@ public class TradePriceCalculateRespBO {
*/
private Long categoryId;
// ========== 物流相关字段 =========
/**
* 运费模板 Id
* 配送方式数组
*
* 对应 DeliveryTypeEnum 枚举
*/
private List<Integer> deliveryTypes;
/**
* 物流配置模板编号
*
* 对应 TradeDeliveryExpressTemplateDO 的 id 编号
*/
private Long deliveryTemplateId;
@@ -234,7 +261,7 @@ public class TradePriceCalculateRespBO {
private List<ProductPropertyValueDetailRespDTO> properties;
/**
* 使用的积分
* 赠送的积分
*/
private Integer givePoint;

View File

@@ -25,6 +25,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_NO_MATCH_MIN_PRICE;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_NO_MATCH_SPU;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_COUPON_PRICE_TOO_MUCH;
/**
* 优惠劵的 {@link TradePriceCalculator} 实现类
@@ -65,8 +66,9 @@ public class TradeCouponPriceCalculator implements TradePriceCalculator {
// 3.1 计算可以优惠的金额
Integer couponPrice = getCouponPrice(coupon, totalPayPrice);
Assert.isTrue(couponPrice < totalPayPrice,
"优惠劵({}) 的优惠金额({}),不能大于订单总金额({})", coupon.getId(), couponPrice, totalPayPrice);
if (couponPrice <= totalPayPrice) {
throw exception(PRICE_CALCULATE_COUPON_PRICE_TOO_MUCH);
}
// 3.2 计算分摊的优惠金额
List<Integer> divideCouponPrices = TradePriceCalculatorHelper.dividePrice(orderItems, couponPrice);

View File

@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.member.api.address.MemberAddressApi;
import cn.iocoder.yudao.module.member.api.address.dto.MemberAddressRespDTO;
import cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO;
@@ -17,11 +18,11 @@ import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplate
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO.OrderItem;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import jakarta.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -55,7 +56,11 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
if (param.getDeliveryType() == null) {
return;
}
// TODO @puhui999需要校验是不是存在商品不能门店自提,或者不能快递发货的情况。就是说,配送方式不匹配哈
// 校验是不是存在商品不能门店自提,或者不能快递发货的情况。就是说,配送方式不匹配哈
if (CollectionUtils.anyMatch(result.getItems(), item -> !item.getDeliveryTypes().contains(param.getDeliveryType()))) {
throw exception(PRICE_CALCULATE_DELIVERY_PRICE_TYPE_ILLEGAL);
}
if (DeliveryTypeEnum.PICK_UP.getType().equals(param.getDeliveryType())) {
calculateByPickUp(param);
} else if (DeliveryTypeEnum.EXPRESS.getType().equals(param.getDeliveryType())) {
@@ -90,7 +95,12 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
return;
}
// 情况二:快递模版
// 情况二:活动包邮
if (Boolean.TRUE.equals(result.getFreeDelivery())) {
return;
}
// 情况三:快递模版
// 2.1 过滤出已选中的商品 SKU
List<OrderItem> selectedItem = filterList(result.getItems(), OrderItem::getSelected);
Set<Long> deliveryTemplateIds = convertSet(selectedItem, OrderItem::getDeliveryTemplateId);
@@ -124,7 +134,7 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
Map<Long, List<OrderItem>> template2ItemMap = convertMultiMap(selectedSkus, OrderItem::getDeliveryTemplateId);
// 依次计算快递运费
for (Map.Entry<Long, List<OrderItem>> entry : template2ItemMap.entrySet()) {
Long templateId = entry.getKey();
Long templateId = entry.getKey();
List<OrderItem> orderItems = entry.getValue();
DeliveryExpressTemplateRespBO templateBO = expressTemplateMap.get(templateId);
if (templateBO == null) {
@@ -144,8 +154,8 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
/**
* 按配送方式来计算运费
*
* @param orderItems SKU 商品项目
* @param chargeMode 配送计费方式
* @param orderItems SKU 商品项目
* @param chargeMode 配送计费方式
* @param templateCharge 快递运费配置
*/
private void calculateExpressFeeByChargeMode(List<OrderItem> orderItems, Integer chargeMode,

View File

@@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -31,8 +32,7 @@ public class TradePriceCalculatorHelper {
List<ProductSpuRespDTO> spuList, List<ProductSkuRespDTO> skuList) {
// 创建 PriceCalculateRespDTO 对象
TradePriceCalculateRespBO result = new TradePriceCalculateRespBO();
result.setType(getOrderType(param));
result.setPromotions(new ArrayList<>());
result.setType(getOrderType(param)).setPromotions(new ArrayList<>()).setGiveCouponTemplateCounts(new LinkedHashMap<>());
// 创建它的 OrderItem 属性
result.setItems(new ArrayList<>(param.getItems().size()));
@@ -60,7 +60,7 @@ public class TradePriceCalculatorHelper {
.setWeight(sku.getWeight()).setVolume(sku.getVolume());
// spu 信息
orderItem.setSpuName(spu.getName()).setCategoryId(spu.getCategoryId())
.setDeliveryTemplateId(spu.getDeliveryTemplateId())
.setDeliveryTypes(spu.getDeliveryTypes()).setDeliveryTemplateId(spu.getDeliveryTemplateId())
.setGivePoint(spu.getGiveIntegral()).setUsePoint(0);
if (StrUtil.isBlank(orderItem.getPicUrl())) {
orderItem.setPicUrl(spu.getPicUrl());

View File

@@ -3,23 +3,30 @@ package cn.iocoder.yudao.module.trade.service.price.calculator;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.module.promotion.api.reward.RewardActivityApi;
import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
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;
import jakarta.annotation.Resource;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import jakarta.annotation.Resource;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
import static cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculatorHelper.formatPrice;
// TODO @puhui999相关的单测建议改一改
/**
* 满减送活动的 {@link TradePriceCalculator} 实现类
*
@@ -52,7 +59,7 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
private void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result,
RewardActivityMatchRespDTO rewardActivity) {
// 1.1 获得满减送的订单项(商品)列表
List<TradePriceCalculateRespBO.OrderItem> orderItems = filterMatchCouponOrderItems(result, rewardActivity);
List<TradePriceCalculateRespBO.OrderItem> orderItems = filterMatchActivityOrderItems(result, rewardActivity);
if (CollUtil.isEmpty(orderItems)) {
return;
}
@@ -61,7 +68,7 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
if (rule == null) {
TradePriceCalculatorHelper.addNotMatchPromotion(result, orderItems,
rewardActivity.getId(), rewardActivity.getName(), PromotionTypeEnum.REWARD_ACTIVITY.getType(),
getRewardActivityNotMeetTip(rewardActivity));
getRewardActivityNotMeetTip(rewardActivity, orderItems));
return;
}
@@ -84,6 +91,36 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
TradePriceCalculatorHelper.recountPayPrice(orderItem);
}
TradePriceCalculatorHelper.recountAllPrice(result);
// 4.1 记录赠送的积分
if (rule.getPoint() != null && rule.getPoint() > 0) {
List<Integer> dividePoints = TradePriceCalculatorHelper.dividePrice(orderItems, rule.getPoint());
for (int i = 0; i < orderItems.size(); i++) {
// 商品可能赠送了积分,所以这里要加上
TradePriceCalculateRespBO.OrderItem orderItem = orderItems.get(i);
orderItem.setGivePoint(orderItem.getGivePoint() + dividePoints.get(i));
}
}
// 4.2 记录订单是否包邮
if (Boolean.TRUE.equals(rule.getFreeDelivery())) {
// 只要满足一个活动包邮那么这单就包邮
result.setFreeDelivery(true);
}
// 4.3 记录赠送的优惠券
if (CollUtil.isNotEmpty(rule.getGiveCouponTemplateCounts())) {
for (Map.Entry<Long, Integer> entry : rule.getGiveCouponTemplateCounts().entrySet()) {
Map<Long, Integer> giveCouponTemplateCounts = result.getGiveCouponTemplateCounts();
// TODO @puhui999是不是有一种可能性这个 key 没有,别的 key 有哈。
// TODO 这里还有一种简化的写法。就是下面,大概两行就可以啦
// result.getGiveCouponTemplateCounts().put(entry.getKey(),
// result.getGiveCouponTemplateCounts().getOrDefault(entry.getKey(), 0) + entry.getValue());
if (giveCouponTemplateCounts.get(entry.getKey()) == null) { // 情况一:还没有赠送的优惠券
result.setGiveCouponTemplateCounts(rule.getGiveCouponTemplateCounts());
} else { // 情况二:别的满减活动送过同类优惠券,则直接增加数量
giveCouponTemplateCounts.put(entry.getKey(), giveCouponTemplateCounts.get(entry.getKey()) + entry.getValue());
}
}
}
}
/**
@@ -93,10 +130,23 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
* @param rewardActivity 满减送活动
* @return 订单项(商品)列表
*/
private List<TradePriceCalculateRespBO.OrderItem> filterMatchCouponOrderItems(TradePriceCalculateRespBO result,
RewardActivityMatchRespDTO rewardActivity) {
return filterList(result.getItems(),
orderItem -> CollUtil.contains(rewardActivity.getSpuIds(), orderItem.getSpuId()));
private List<TradePriceCalculateRespBO.OrderItem> filterMatchActivityOrderItems(TradePriceCalculateRespBO result,
RewardActivityMatchRespDTO rewardActivity) {
// 情况一:全部商品都可以参与
if (PromotionProductScopeEnum.isAll(rewardActivity.getProductScope())) {
return result.getItems();
}
// 情况二:指定商品参与
if (PromotionProductScopeEnum.isSpu(rewardActivity.getProductScope())) {
return filterList(result.getItems(),
orderItem -> CollUtil.contains(rewardActivity.getProductScopeValues(), orderItem.getSpuId()));
}
// 情况三:指定商品类型参与
if (PromotionProductScopeEnum.isCategory(rewardActivity.getProductScope())) {
return filterList(result.getItems(),
orderItem -> CollUtil.contains(rewardActivity.getProductScopeValues(), orderItem.getCategoryId()));
}
return List.of();
}
/**
@@ -129,14 +179,30 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
}
/**
* 获得满减送活动匹配时的提示
* 获得满减送活动匹配时的提示
*
* @param rewardActivity 满减送活动
* @return 提示
*/
private String getRewardActivityNotMeetTip(RewardActivityMatchRespDTO rewardActivity) {
// TODO 芋艿:后面再想想;应该找第一个规则,算下还差多少即可。
return "TODO";
private String getRewardActivityNotMeetTip(RewardActivityMatchRespDTO rewardActivity,
List<TradePriceCalculateRespBO.OrderItem> orderItems) {
// 1. 计算数量和价格
Integer count = TradePriceCalculatorHelper.calculateTotalCount(orderItems);
Integer price = TradePriceCalculatorHelper.calculateTotalPayPrice(orderItems);
assert count != null && price != null;
// 2. 构建不满足时的提示信息:按最低档规则算
String meetTip = "满减送:购满 {} {},可以减 {} 元";
List<RewardActivityMatchRespDTO.Rule> rules = new ArrayList<>(rewardActivity.getRules());
rules.sort(Comparator.comparing(RewardActivityMatchRespDTO.Rule::getLimit)); // 按优惠门槛升序
RewardActivityMatchRespDTO.Rule rule = rules.get(0);
if (PromotionConditionTypeEnum.PRICE.getType().equals(rewardActivity.getConditionType())) {
return StrUtil.format(meetTip, rule.getLimit(), "", MoneyUtils.fenToYuanStr(rule.getDiscountPrice()));
}
if (PromotionConditionTypeEnum.COUNT.getType().equals(rewardActivity.getConditionType())) {
return StrUtil.format(meetTip, rule.getLimit(), "", MoneyUtils.fenToYuanStr(rule.getDiscountPrice()));
}
return StrUtil.EMPTY;
}
}

View File

@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.promotion.api.reward.RewardActivityApi;
import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
@@ -13,6 +14,7 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
@@ -47,7 +49,7 @@ public class TradeRewardActivityPriceCalculatorTest extends BaseMockitoUnitTest
TradePriceCalculateRespBO result = new TradePriceCalculateRespBO()
.setType(TradeOrderTypeEnum.NORMAL.getType())
.setPrice(new TradePriceCalculateRespBO.Price())
.setPromotions(new ArrayList<>())
.setPromotions(new ArrayList<>()).setGiveCouponTemplateCounts(new LinkedHashMap<>())
.setItems(asList(
new TradePriceCalculateRespBO.OrderItem().setSkuId(10L).setCount(2).setSelected(true)
.setPrice(100).setSpuId(1L),
@@ -60,16 +62,22 @@ public class TradeRewardActivityPriceCalculatorTest extends BaseMockitoUnitTest
TradePriceCalculatorHelper.recountPayPrice(result.getItems());
TradePriceCalculatorHelper.recountAllPrice(result);
// mock 方法(限时折扣 DiscountActivity 信息)
// mock 方法(满减送 RewardActivity 信息)
when(rewardActivityApi.getMatchRewardActivityList(eq(asSet(1L, 2L, 3L)))).thenReturn(asList(
randomPojo(RewardActivityMatchRespDTO.class, o -> o.setId(1000L).setName("活动 1000 号")
.setSpuIds(asList(1L, 2L)).setConditionType(PromotionConditionTypeEnum.PRICE.getType())
.setRules(singletonList(new RewardActivityMatchRespDTO.Rule().setLimit(200).setDiscountPrice(70)))),
.setConditionType(PromotionConditionTypeEnum.PRICE.getType())
.setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductScopeValues(asList(1L, 2L))
.setRules(singletonList(new RewardActivityMatchRespDTO.Rule().setLimit(20).setDiscountPrice(70)
.setFreeDelivery(false)))),
randomPojo(RewardActivityMatchRespDTO.class, o -> o.setId(2000L).setName("活动 2000 号")
.setSpuIds(singletonList(3L)).setConditionType(PromotionConditionTypeEnum.COUNT.getType())
.setRules(asList(new RewardActivityMatchRespDTO.Rule().setLimit(1).setDiscountPrice(10),
new RewardActivityMatchRespDTO.Rule().setLimit(2).setDiscountPrice(60), // 最大可满足,因为是 4 个
new RewardActivityMatchRespDTO.Rule().setLimit(10).setDiscountPrice(100))))
.setConditionType(PromotionConditionTypeEnum.COUNT.getType())
.setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductScopeValues(singletonList(3L))
.setRules(asList(new RewardActivityMatchRespDTO.Rule().setLimit(1).setDiscountPrice(10)
.setPoint(50).setFreeDelivery(false),
new RewardActivityMatchRespDTO.Rule().setLimit(2).setDiscountPrice(60)
.setPoint(100).setFreeDelivery(false), // 最大可满足,因为是 4 个
new RewardActivityMatchRespDTO.Rule().setLimit(10).setDiscountPrice(100)
.setFreeDelivery(false))))
));
// 调用
@@ -94,6 +102,7 @@ public class TradeRewardActivityPriceCalculatorTest extends BaseMockitoUnitTest
assertEquals(orderItem01.getCouponPrice(), 0);
assertEquals(orderItem01.getPointPrice(), 0);
assertEquals(orderItem01.getPayPrice(), 160);
assertEquals(orderItem01.getGivePoint(), 0);
// 断言SKU 2
TradePriceCalculateRespBO.OrderItem orderItem02 = result.getItems().get(1);
assertEquals(orderItem02.getSkuId(), 20L);
@@ -104,6 +113,7 @@ public class TradeRewardActivityPriceCalculatorTest extends BaseMockitoUnitTest
assertEquals(orderItem02.getCouponPrice(), 0);
assertEquals(orderItem02.getPointPrice(), 0);
assertEquals(orderItem02.getPayPrice(), 120);
assertEquals(orderItem02.getGivePoint(), 0);
// 断言SKU 3
TradePriceCalculateRespBO.OrderItem orderItem03 = result.getItems().get(2);
assertEquals(orderItem03.getSkuId(), 30L);
@@ -114,6 +124,7 @@ public class TradeRewardActivityPriceCalculatorTest extends BaseMockitoUnitTest
assertEquals(orderItem03.getCouponPrice(), 0);
assertEquals(orderItem03.getPointPrice(), 0);
assertEquals(orderItem03.getPayPrice(), 60);
assertEquals(orderItem03.getGivePoint(), 100);
// 断言Promotion 部分(第一个)
assertEquals(result.getPromotions().size(), 2);
TradePriceCalculateRespBO.Promotion promotion01 = result.getPromotions().get(0);
@@ -175,7 +186,7 @@ public class TradeRewardActivityPriceCalculatorTest extends BaseMockitoUnitTest
// mock 方法(限时折扣 DiscountActivity 信息)
when(rewardActivityApi.getMatchRewardActivityList(eq(asSet(1L, 2L)))).thenReturn(singletonList(
randomPojo(RewardActivityMatchRespDTO.class, o -> o.setId(1000L).setName("活动 1000 号")
.setSpuIds(asList(1L, 2L)).setConditionType(PromotionConditionTypeEnum.PRICE.getType())
.setProductScopeValues(asList(1L, 2L)).setConditionType(PromotionConditionTypeEnum.PRICE.getType())
.setRules(singletonList(new RewardActivityMatchRespDTO.Rule().setLimit(351).setDiscountPrice(70))))
));

View File

@@ -48,6 +48,7 @@ CREATE TABLE IF NOT EXISTS "trade_order"
"give_point" int NULL,
"refund_point" int NULL,
"vip_price" int NULL,
"give_coupons_map" varchar NULL,
"seckill_activity_id" long NULL,
"bargain_activity_id" long NULL,
"bargain_record_id" long NULL,