trade:价格计算时,接入优惠劵逻辑

This commit is contained in:
YunaiV
2023-10-04 00:30:03 +08:00
parent a1e9bedf34
commit 20f5834d9a
18 changed files with 191 additions and 102 deletions

View File

@@ -14,7 +14,7 @@ import java.util.List;
public class AppTradeOrderSettlementRespVO {
@Schema(description = "交易类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") // 对应 TradeOrderTypeEnum 枚举
private Integer type = 1; // TODO 芋艿:改成计算
private Integer type;
@Schema(description = "购物项数组", requiredMode = Schema.RequiredMode.REQUIRED)
private List<Item> items;
@@ -75,6 +75,9 @@ public class AppTradeOrderSettlementRespVO {
@Schema(description = "商品原价(总),单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "500")
private Integer totalPrice;
@Schema(description = "订单优惠(总),单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "66")
private Integer discountPrice;
@Schema(description = "运费金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "50")
private Integer deliveryPrice;

View File

@@ -211,7 +211,8 @@ public interface TradeOrderConvert {
.setCouponId(settlementReqVO.getCouponId()).setPointStatus(settlementReqVO.getPointStatus())
.setDeliveryType(settlementReqVO.getDeliveryType()).setAddressId(settlementReqVO.getAddressId())
.setPickUpStoreId(settlementReqVO.getPickUpStoreId())
.setItems(new ArrayList<>(settlementReqVO.getItems().size()));
.setItems(new ArrayList<>(settlementReqVO.getItems().size()))
.setSeckillActivityId(settlementReqVO.getSeckillActivityId());
// 商品项的构建
Map<Long, CartDO> cartMap = convertMap(cartList, CartDO::getId);
for (AppTradeOrderSettlementReqVO.Item item : settlementReqVO.getItems()) {

View File

@@ -288,4 +288,11 @@ public class TradeOrderDO extends BaseDO {
*/
private Integer vipPrice;
/**
* 秒杀活动编号
*
* 关联 SeckillActivityDO 的 id 字段
*/
private Long seckillActivityId;
}

View File

@@ -1,13 +1,19 @@
package cn.iocoder.yudao.module.trade.dal.mysql.order;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
@Mapper
public interface TradeOrderItemMapper extends BaseMapperX<TradeOrderItemDO> {
@@ -38,4 +44,13 @@ public interface TradeOrderItemMapper extends BaseMapperX<TradeOrderItemDO> {
.eq(TradeOrderItemDO::getCommentStatus, commentStatus));
}
default int selectProductSumByOrderId(@Param("orderIds") Set<Long> orderIds) {
// SQL sum 查询
List<Map<String, Object>> result = selectMaps(new QueryWrapper<TradeOrderItemDO>()
.select("SUM(count) AS sumCount")
.in("order_id", orderIds)); // 只计算选中的
// 获得数量
return CollUtil.getFirst(result) != null ? MapUtil.getInt(result.get(0), "sumCount") : 0;
}
}

View File

@@ -82,4 +82,10 @@ public interface TradeOrderMapper extends BaseMapperX<TradeOrderDO> {
.eq(TradeOrderDO::getCommentStatus, commentStatus));
}
default List<TradeOrderDO> selectListByUserIdAndSeckillActivityId(Long userId, Long seckillActivityId) {
return selectList(new LambdaUpdateWrapper<>(TradeOrderDO.class)
.eq(TradeOrderDO::getUserId, userId)
.eq(TradeOrderDO::getSeckillActivityId, seckillActivityId));
}
}

View File

@@ -82,6 +82,15 @@ public interface TradeOrderQueryService {
*/
List<ExpressTrackRespDTO> getExpressTrackList(Long id);
/**
* 【会员】在指定秒杀活动下,用户购买的商品数量
*
* @param userId 用户编号
* @param activityId 活动编号
* @return 秒杀商品数量
*/
int getSeckillProductCount(Long userId, Long activityId);
// =================== Order Item ===================
/**

View File

@@ -13,6 +13,7 @@ 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.mysql.order.TradeOrderItemMapper;
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClientFactory;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
@@ -120,6 +121,18 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
return getExpressTrackList(order);
}
@Override
public int getSeckillProductCount(Long userId, Long activityId) {
// 获得订单列表
List<TradeOrderDO> orders = tradeOrderMapper.selectListByUserIdAndSeckillActivityId(userId, activityId);
orders.removeIf(order -> TradeOrderStatusEnum.isCanceled(order.getStatus())); // 过滤掉【已取消】的订单
if (CollUtil.isEmpty(orders)) {
return 0;
}
// 获得订单项列表
return tradeOrderItemMapper.selectProductSumByOrderId(convertSet(orders, TradeOrderDO::getId));
}
// TODO @puhui999可以加个 spring 缓存30 分钟;主要考虑及时性要求不高,但是每次调用需要钱;
/**
* 获得订单的物流轨迹

View File

@@ -1,19 +1,22 @@
package cn.iocoder.yudao.module.trade.service.price.calculator;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.promotion.api.seckill.SeckillActivityApi;
import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillActivityProductRespDTO;
import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillValidateJoinRespDTO;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_SECKILL_TOTAL_LIMIT_COUNT;
// TODO huihui单测需要补充
/**
* 秒杀活动的 {@link TradePriceCalculator} 实现类
*
@@ -24,22 +27,45 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
public class TradeSeckillActivityPriceCalculator implements TradePriceCalculator {
@Resource
private SeckillActivityApi activityApi;
private SeckillActivityApi seckillActivityApi;
@Resource
private TradeOrderQueryService tradeOrderQueryService;
@Override
public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
// 1判断订单类型和是否具有秒杀活动编号
// 1. 判断订单类型和是否具有秒杀活动编号
if (param.getSeckillActivityId() == null) {
return;
}
// 2、获取秒杀活动商品信息
List<SeckillActivityProductRespDTO> productList = activityApi.getSeckillActivityProductList(param.getSeckillActivityId(), convertSet(param.getItems(),
TradePriceCalculateReqBO.Item::getSkuId));
Map<Long, SeckillActivityProductRespDTO> productMap = convertMap(productList, SeckillActivityProductRespDTO::getSkuId);
result.getItems().forEach(item -> {
SeckillActivityProductRespDTO product = productMap.get(item.getSkuId());
item.setActivityPrice(product.getSeckillPrice()); // 设置活动金额
});
Assert.isTrue(param.getItems().size() == 1, "秒杀时,只允许选择一个商品");
// 2. 校验是否可以参与秒杀
TradePriceCalculateRespBO.OrderItem orderItem = result.getItems().get(0);
SeckillValidateJoinRespDTO seckillActivity = validateJoinSeckill(
param.getUserId(), param.getSeckillActivityId(),
orderItem.getSkuId(), orderItem.getCount());
// 3.1 记录优惠明细
Integer discountPrice = orderItem.getPayPrice() - seckillActivity.getSeckillPrice();
TradePriceCalculatorHelper.addPromotion(result, orderItem,
param.getSeckillActivityId(), seckillActivity.getName(), PromotionTypeEnum.SECKILL_ACTIVITY.getType(),
StrUtil.format("秒杀活动:省 {} 元", TradePriceCalculatorHelper.formatPrice(discountPrice)),
discountPrice);
// 3.2 更新 SKU 优惠金额
orderItem.setDiscountPrice(orderItem.getDiscountPrice() + discountPrice);
TradePriceCalculatorHelper.recountPayPrice(orderItem);
TradePriceCalculatorHelper.recountAllPrice(result);
}
private SeckillValidateJoinRespDTO validateJoinSeckill(Long userId, Long activityId, Long skuId, Integer count) {
// 1. 校验是否可以参与秒杀
SeckillValidateJoinRespDTO seckillActivity = seckillActivityApi.validateJoinSeckill(activityId, skuId, count);
// 2. 校验总限购数量,目前只有 trade 有具体下单的数据,需要交给 trade 价格计算使用
int seckillProductCount = tradeOrderQueryService.getSeckillProductCount(userId, activityId);
if (seckillProductCount + count > seckillActivity.getTotalLimitCount()) {
throw exception(PRICE_CALCULATE_SECKILL_TOTAL_LIMIT_COUNT);
}
return seckillActivity;
}
}