mirror of
https://gitee.com/hhyykk/ipms-sjy.git
synced 2025-08-17 03:31:54 +08:00
Merge remote-tracking branch 'origin/feature/mall_product' into feature/mall_product
# Conflicts: # yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderMapper.java
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.order.vo;
|
||||
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.framework.common.validation.Mobile;
|
||||
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
|
||||
@@ -67,7 +68,7 @@ public class AppTradeOrderSettlementReqVO {
|
||||
@JsonIgnore
|
||||
public boolean isValidActivityItems() {
|
||||
// 校验是否是活动订单
|
||||
if (seckillActivityId == null && combinationActivityId == null && combinationHeadId == null) {
|
||||
if (ObjUtil.isAllEmpty(seckillActivityId, combinationActivityId, combinationHeadId)) {
|
||||
return true;
|
||||
}
|
||||
// 校验订单项是否超出
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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()) {
|
||||
|
@@ -288,4 +288,11 @@ public class TradeOrderDO extends BaseDO {
|
||||
*/
|
||||
private Integer vipPrice;
|
||||
|
||||
/**
|
||||
* 秒杀活动编号
|
||||
*
|
||||
* 关联 SeckillActivityDO 的 id 字段
|
||||
*/
|
||||
private Long seckillActivityId;
|
||||
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -86,6 +86,12 @@ 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));
|
||||
}
|
||||
|
||||
default TradeOrderSummaryRespDTO selectSummaryByPayTimeBetween(LocalDateTime beginTime, LocalDateTime endTime) {
|
||||
return BeanUtil.copyProperties(CollUtil.get(selectMaps(MPJWrappers.<TradeOrderDO>lambdaJoin()
|
||||
.selectCount(TradeOrderDO::getId, TradeOrderSummaryRespDTO::getOrderPayCount)
|
||||
|
@@ -84,6 +84,15 @@ public interface TradeOrderQueryService {
|
||||
*/
|
||||
List<ExpressTrackRespDTO> getExpressTrackList(Long id);
|
||||
|
||||
/**
|
||||
* 【会员】在指定秒杀活动下,用户购买的商品数量
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param activityId 活动编号
|
||||
* @return 秒杀商品数量
|
||||
*/
|
||||
int getSeckillProductCount(Long userId, Long activityId);
|
||||
|
||||
// =================== Order Item ===================
|
||||
|
||||
/**
|
||||
|
@@ -14,6 +14,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;
|
||||
@@ -122,6 +123,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 分钟;主要考虑及时性要求不高,但是每次调用需要钱;
|
||||
/**
|
||||
* 获得订单的物流轨迹
|
||||
|
@@ -14,7 +14,7 @@ import javax.annotation.Resource;
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@Component
|
||||
public class TradeBargainHandler extends TradeOrderDefaultHandler {
|
||||
public class TradeBargainHandler implements TradeOrderHandler {
|
||||
|
||||
@Resource
|
||||
private BargainActivityApi bargainActivityApi;
|
||||
|
@@ -17,7 +17,7 @@ import javax.annotation.Resource;
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@Component
|
||||
public class TradeCombinationHandler extends TradeOrderDefaultHandler {
|
||||
public class TradeCombinationHandler implements TradeOrderHandler {
|
||||
|
||||
@Resource
|
||||
private CombinationRecordApi combinationRecordApi;
|
||||
|
@@ -1,34 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.service.order.handler;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.service.order.bo.TradeAfterOrderCreateReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.order.bo.TradeAfterPayOrderReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.order.bo.TradeBeforeOrderCreateReqBO;
|
||||
|
||||
/**
|
||||
* 订单活动特殊逻辑处理器 handler 默认抽象实现类
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
public abstract class TradeOrderDefaultHandler implements TradeOrderHandler {
|
||||
|
||||
@Override
|
||||
public void beforeOrderCreate(TradeBeforeOrderCreateReqBO reqBO) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterOrderCreate(TradeAfterOrderCreateReqBO reqBO) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPayOrder(TradeAfterPayOrderReqBO reqBO) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelOrder() {
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -17,25 +17,25 @@ public interface TradeOrderHandler {
|
||||
*
|
||||
* @param reqBO 请求
|
||||
*/
|
||||
void beforeOrderCreate(TradeBeforeOrderCreateReqBO reqBO);
|
||||
default void beforeOrderCreate(TradeBeforeOrderCreateReqBO reqBO) {}
|
||||
|
||||
/**
|
||||
* 订单创建后
|
||||
*
|
||||
* @param reqBO 请求
|
||||
*/
|
||||
void afterOrderCreate(TradeAfterOrderCreateReqBO reqBO);
|
||||
default void afterOrderCreate(TradeAfterOrderCreateReqBO reqBO) {}
|
||||
|
||||
/**
|
||||
* 支付订单后
|
||||
*
|
||||
* @param reqBO 请求
|
||||
*/
|
||||
void afterPayOrder(TradeAfterPayOrderReqBO reqBO);
|
||||
default void afterPayOrder(TradeAfterPayOrderReqBO reqBO) {}
|
||||
|
||||
/**
|
||||
* 订单取消
|
||||
*/
|
||||
void cancelOrder();
|
||||
default void cancelOrder() {}
|
||||
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@ import javax.annotation.Resource;
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@Component
|
||||
public class TradeSeckillHandler extends TradeOrderDefaultHandler {
|
||||
public class TradeSeckillHandler implements TradeOrderHandler {
|
||||
|
||||
@Resource
|
||||
private SeckillActivityApi seckillActivityApi;
|
||||
|
@@ -98,19 +98,9 @@ public class TradePriceCalculateRespBO {
|
||||
* VIP 减免金额,单位:分
|
||||
*/
|
||||
private Integer vipPrice;
|
||||
/**
|
||||
* 秒杀、拼团、砍价活动商品的总金额,单位:分
|
||||
*
|
||||
* 基于 {@link OrderItem#getActivityPrice()} ()} * {@link OrderItem#getCount()} 求和
|
||||
*/
|
||||
private Integer activityPrice;
|
||||
/**
|
||||
* 最终购买金额(总),单位:分
|
||||
*
|
||||
* ==========活动情况===========
|
||||
* = {@link #activityPrice}
|
||||
* + {@link #deliveryPrice}
|
||||
* ==========正常情况===========
|
||||
* = {@link #totalPrice}
|
||||
* - {@link #couponPrice}
|
||||
* - {@link #pointPrice}
|
||||
@@ -186,16 +176,9 @@ public class TradePriceCalculateRespBO {
|
||||
* VIP 减免金额,单位:分
|
||||
*/
|
||||
private Integer vipPrice;
|
||||
/**
|
||||
* 秒杀、拼团、砍价活动商品的金额,单位:分
|
||||
*/
|
||||
private Integer activityPrice;
|
||||
/**
|
||||
* 应付金额(总),单位:分
|
||||
* ==========活动情况===========
|
||||
* = {@link #activityPrice} * {@link #count}
|
||||
* + {@link #deliveryPrice}
|
||||
* ==========正常情况===========
|
||||
*
|
||||
* = {@link #price} * {@link #count}
|
||||
* - {@link #couponPrice}
|
||||
* - {@link #pointPrice}
|
||||
|
@@ -105,9 +105,6 @@ public class TradePriceCalculatorHelper {
|
||||
if (!item.getSelected()) {
|
||||
return;
|
||||
}
|
||||
// TODO puhui: 需要在这里计算活动的价格
|
||||
// ========== 一、活动情况 ==========
|
||||
// ========== 二、正常情况 ==========
|
||||
price.setTotalPrice(price.getTotalPrice() + item.getPrice() * item.getCount());
|
||||
price.setDiscountPrice(price.getDiscountPrice() + item.getDiscountPrice());
|
||||
price.setDeliveryPrice(price.getDeliveryPrice() + item.getDeliveryPrice());
|
||||
|
@@ -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() * orderItem.getCount();
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user