Explorar o código

【功能完善】商城: APP 完善积分商城活动下单

puhui999 hai 8 meses
pai
achega
67809be46d
Modificáronse 11 ficheiros con 223 adicións e 3 borrados
  1. 17 0
      yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/point/PointActivityApi.java
  2. 10 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/point/PointActivityApiImpl.java
  3. 31 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/point/PointActivityMapper.java
  4. 29 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/point/PointProductMapper.java
  5. 18 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityService.java
  6. 37 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityServiceImpl.java
  7. 1 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java
  8. 4 0
      yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderTypeEnum.java
  9. 7 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java
  10. 64 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradePointOrderHandler.java
  11. 5 2
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointActivityPriceCalculator.java

+ 17 - 0
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/point/PointActivityApi.java

@@ -21,5 +21,22 @@ public interface PointActivityApi {
      */
     PointValidateJoinRespDTO validateJoinPointActivity(Long activityId, Long skuId, Integer count);
 
+    /**
+     * 更新积分商城商品库存(减少)
+     *
+     * @param id    活动编号
+     * @param skuId sku 编号
+     * @param count 数量(正数)
+     */
+    void updatePointStockDecr(Long id, Long skuId, Integer count);
+
+    /**
+     * 更新积分商城商品库存(增加)
+     *
+     * @param id    活动编号
+     * @param skuId sku 编号
+     * @param count 数量(正数)
+     */
+    void updatePointStockIncr(Long id, Long skuId, Integer count);
 
 }

+ 10 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/point/PointActivityApiImpl.java

@@ -23,4 +23,14 @@ public class PointActivityApiImpl implements PointActivityApi {
         return pointActivityService.validateJoinPointActivity(activityId, skuId, count);
     }
 
+    @Override
+    public void updatePointStockDecr(Long id, Long skuId, Integer count) {
+        pointActivityService.updatePointStockDecr(id, skuId, count);
+    }
+
+    @Override
+    public void updatePointStockIncr(Long id, Long skuId, Integer count) {
+        pointActivityService.updatePointStockIncr(id, skuId, count);
+    }
+
 }

+ 31 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/point/PointActivityMapper.java

@@ -1,10 +1,12 @@
 package cn.iocoder.yudao.module.promotion.dal.mysql.point;
 
+import cn.hutool.core.lang.Assert;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivityPageReqVO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointActivityDO;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import org.apache.ibatis.annotations.Mapper;
 
 /**
@@ -25,4 +27,33 @@ public interface PointActivityMapper extends BaseMapperX<PointActivityDO> {
                 .orderByDesc(PointActivityDO::getId));
     }
 
+    /**
+     * 更新活动库存(减少)
+     *
+     * @param id    活动编号
+     * @param count 扣减的库存数量(正数)
+     * @return 影响的行数
+     */
+    default int updateStockDecr(Long id, int count) {
+        Assert.isTrue(count > 0);
+        return update(null, new LambdaUpdateWrapper<PointActivityDO>()
+                .eq(PointActivityDO::getId, id)
+                .ge(PointActivityDO::getStock, count)
+                .setSql("stock = stock - " + count));
+    }
+
+    /**
+     * 更新活动库存(增加)
+     *
+     * @param id    活动编号
+     * @param count 增加的库存数量(正数)
+     * @return 影响的行数
+     */
+    default int updateStockIncr(Long id, int count) {
+        Assert.isTrue(count > 0);
+        return update(null, new LambdaUpdateWrapper<PointActivityDO>()
+                .eq(PointActivityDO::getId, id)
+                .setSql("stock = stock + " + count));
+    }
+
 }

+ 29 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/point/PointProductMapper.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.promotion.dal.mysql.point;
 
+import cn.hutool.core.lang.Assert;
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointProductDO;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@@ -34,4 +35,32 @@ public interface PointProductMapper extends BaseMapperX<PointProductDO> {
                 PointProductDO::getSkuId, skuId);
     }
 
+    /**
+     * 更新活动库存(减少)
+     *
+     * @param id    活动编号
+     * @param count 扣减的库存数量(减少库存)
+     * @return 影响的行数
+     */
+    default int updateStockDecr(Long id, int count) {
+        Assert.isTrue(count > 0);
+        return update(null, new LambdaUpdateWrapper<PointProductDO>()
+                .eq(PointProductDO::getId, id)
+                .ge(PointProductDO::getStock, count)
+                .setSql("stock = stock - " + count));
+    }
+
+    /**
+     * 更新活动库存(增加)
+     *
+     * @param id    活动编号
+     * @param count 需要增加的库存(增加库存)
+     * @return 影响的行数
+     */
+    default int updateStockIncr(Long id, int count) {
+        Assert.isTrue(count > 0);
+        return update(null, new LambdaUpdateWrapper<PointProductDO>()
+                .eq(PointProductDO::getId, id)
+                .setSql("stock = stock + " + count));
+    }
 }

+ 18 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityService.java

@@ -33,6 +33,24 @@ public interface PointActivityService {
      */
     void updatePointActivity(@Valid PointActivitySaveReqVO updateReqVO);
 
+    /**
+     * 更新积分商城商品库存(减少)
+     *
+     * @param id    活动编号
+     * @param skuId sku 编号
+     * @param count 数量(正数)
+     */
+    void updatePointStockDecr(Long id, Long skuId, Integer count);
+
+    /**
+     * 更新积分商城商品库存(增加)
+     *
+     * @param id    活动编号
+     * @param skuId sku 编号
+     * @param count 数量(正数)
+     */
+    void updatePointStockIncr(Long id, Long skuId, Integer count);
+
     /**
      * 关闭积分商城活动
      *

+ 37 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityServiceImpl.java

@@ -103,6 +103,43 @@ public class PointActivityServiceImpl implements PointActivityService {
         updateSeckillProduct(updateObj, updateReqVO.getProducts());
     }
 
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updatePointStockDecr(Long id, Long skuId, Integer count) {
+        // 1.1 校验活动库存是否充足
+        PointActivityDO activity = validatePointActivityExists(id);
+        if (count > activity.getStock()) {
+            throw exception(POINT_ACTIVITY_UPDATE_STOCK_FAIL);
+        }
+        // 1.2 校验商品库存是否充足
+        PointProductDO product = pointProductMapper.selectListByActivityIdAndSkuId(id, skuId);
+        if (product == null || count > product.getStock()) {
+            throw exception(POINT_ACTIVITY_UPDATE_STOCK_FAIL);
+        }
+
+        // 2.1 更新活动商品库存
+        int updateCount = pointProductMapper.updateStockDecr(product.getId(), count);
+        if (updateCount == 0) {
+            throw exception(POINT_ACTIVITY_UPDATE_STOCK_FAIL);
+        }
+
+        // 2.2 更新活动库存
+        updateCount = pointActivityMapper.updateStockDecr(id, count);
+        if (updateCount == 0) {
+            throw exception(POINT_ACTIVITY_UPDATE_STOCK_FAIL);
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updatePointStockIncr(Long id, Long skuId, Integer count) {
+        PointProductDO product = pointProductMapper.selectListByActivityIdAndSkuId(id, skuId);
+        // 更新活动商品库存
+        pointProductMapper.updateStockIncr(product.getId(), count);
+        // 更新活动库存
+        pointActivityMapper.updateStockIncr(id, count);
+    }
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void closePointActivity(Long id) {

+ 1 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java

@@ -160,7 +160,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
     public void updateSeckillStockDecr(Long id, Long skuId, Integer count) {
         // 1.1 校验活动库存是否充足
         SeckillActivityDO seckillActivity = validateSeckillActivityExists(id);
-        if (count > seckillActivity.getTotalStock()) {
+        if (count > seckillActivity.getStock()) {
             throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);
         }
         // 1.2 校验商品库存是否充足

+ 4 - 0
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderTypeEnum.java

@@ -55,4 +55,8 @@ public enum TradeOrderTypeEnum implements IntArrayValuable {
         return ObjectUtil.equal(type, COMBINATION.getType());
     }
 
+    public static boolean isPoint(Integer type) {
+        return ObjectUtil.equal(type, POINT.getType());
+    }
+
 }

+ 7 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java

@@ -353,4 +353,11 @@ public class TradeOrderDO extends BaseDO {
      */
     private Long combinationRecordId;
 
+    /**
+     * 积分商城活动的编号
+     *
+     * 关联 PointActivityDO 的 id 字段
+     */
+    private Long pointActivityId;
+
 }

+ 64 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradePointOrderHandler.java

@@ -0,0 +1,64 @@
+package cn.iocoder.yudao.module.trade.service.order.handler;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.lang.Assert;
+import cn.iocoder.yudao.module.promotion.api.point.PointActivityApi;
+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.TradeOrderTypeEnum;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 积分商城活动订单的 {@link TradeOrderHandler} 实现类
+ *
+ * @author HUIHUI
+ */
+@Component
+public class TradePointOrderHandler implements TradeOrderHandler {
+
+    @Resource
+    private PointActivityApi pointActivityApi;
+
+    @Override
+    public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
+        if (!TradeOrderTypeEnum.isPoint(order.getType())) {
+            return;
+        }
+        // 明确校验一下
+        Assert.isTrue(orderItems.size() == 1, "积分商城活动兑换商品兑换时,只允许选择一个商品");
+
+        // 扣减积分商城活动的库存
+        pointActivityApi.updatePointStockDecr(order.getPointActivityId(),
+                orderItems.get(0).getSkuId(), orderItems.get(0).getCount());
+    }
+
+    @Override
+    public void afterCancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
+        if (!TradeOrderTypeEnum.isPoint(order.getType())) {
+            return;
+        }
+        // 明确校验一下
+        Assert.isTrue(orderItems.size() == 1, "积分商城活动兑换商品兑换时,只允许选择一个商品");
+
+        // 售后的订单项,已经在 afterCancelOrderItem 回滚库存,所以这里不需要重复回滚
+        orderItems = filterOrderItemListByNoneAfterSale(orderItems);
+        if (CollUtil.isEmpty(orderItems)) {
+            return;
+        }
+        afterCancelOrderItem(order, orderItems.get(0));
+    }
+
+    @Override
+    public void afterCancelOrderItem(TradeOrderDO order, TradeOrderItemDO orderItem) {
+        if (!TradeOrderTypeEnum.isPoint(order.getType())) {
+            return;
+        }
+        // 恢复积分商城活动的库存
+        pointActivityApi.updatePointStockIncr(order.getPointActivityId(),
+                orderItem.getSkuId(), orderItem.getCount());
+    }
+
+}

+ 5 - 2
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointActivityPriceCalculator.java

@@ -63,15 +63,18 @@ public class TradePointActivityPriceCalculator implements TradePriceCalculator {
         // 3.1 记录优惠明细
         int discountPrice = orderItem.getPayPrice(); // 情况一:单使用积分兑换
         Assert.isTrue(activity.getPoint() >= 1, "积分商城商品兑换积分必须大于 1");
-        result.setUsePoint(activity.getPoint());
+        result.setUsePoint(activity.getPoint() * orderItem.getCount());
+        orderItem.setUsePoint(activity.getPoint() * orderItem.getCount());
         if (activity.getPrice() != null && activity.getPrice() > 0) { // 情况二:积分 + 金额
             discountPrice = orderItem.getPayPrice() - activity.getPrice() * orderItem.getCount();
         }
+        // 3.2 记录优惠明细
         TradePriceCalculatorHelper.addPromotion(result, orderItem,
                 param.getPointActivityId(), "积分商城活动", PromotionTypeEnum.POINT.getType(),
                 StrUtil.format("积分商城活动:省 {} 元", TradePriceCalculatorHelper.formatPrice(discountPrice)),
                 discountPrice);
-        // 3.2 更新 SKU 优惠金额
+
+        // 3.3 更新 SKU 优惠金额
         orderItem.setDiscountPrice(orderItem.getDiscountPrice() + discountPrice);
         TradePriceCalculatorHelper.recountPayPrice(orderItem);
         TradePriceCalculatorHelper.recountAllPrice(result);