guomengjiao пре 1 месец
родитељ
комит
991a72c8c3

+ 0 - 12
ruoyi-admin/src/main/java/com/ruoyi/web/controller/shop/marketing/seckill/SeckillActivityController.java

@@ -116,16 +116,4 @@ public class SeckillActivityController extends BaseController {
         return toAjax(iSeckillActivityService.deleteWithValidByIds(Arrays.asList(ids), true) ? 1 : 0);
     }
 
-//    /**
-//     * 导出秒杀活动列表
-//     */
-//    @ApiOperation("导出秒杀活动列表")
-//    @SaCheckPermission("seckill:seckillActivity:export")
-//    @Log(title = "秒杀活动", businessType = BusinessType.EXPORT)
-//    @PostMapping("/export")
-//    public void export(@Validated SeckillActivityBo bo, HttpServletResponse response) {
-//        List<SeckillActivityVo> list = iSeckillActivityService.queryList(bo);
-//        ExcelUtil.exportExcel(list, "秒杀活动", SeckillActivityVo.class, response);
-//    }
-
 }

+ 0 - 12
ruoyi-admin/src/main/java/com/ruoyi/web/controller/shop/marketing/seckill/SeckillActivityProductController.java

@@ -96,16 +96,4 @@ public class SeckillActivityProductController extends BaseController {
         return toAjax(iSeckillActivityProductService.deleteWithValidByIds(Arrays.asList(ids), true) ? 1 : 0);
     }
 
-    /**
-     * 导出秒杀活动商品列表
-     */
-    @ApiOperation("导出秒杀活动商品列表")
-    @SaCheckPermission("seckill:seckillActivityProduct:export")
-    @Log(title = "秒杀活动商品", businessType = BusinessType.EXPORT)
-    @PostMapping("/export")
-    public void export(@Validated SeckillActivityProductBo bo, HttpServletResponse response) {
-        List<SeckillActivityProductVo> list = iSeckillActivityProductService.queryList(bo);
-        ExcelUtil.exportExcel(list, "秒杀活动商品", SeckillActivityProductVo.class, response);
-    }
-
 }

+ 30 - 0
ruoyi-shop/src/main/java/com/ruoyi/shop/marketing/seckill/domain/SeckillActivityProduct.java

@@ -1,13 +1,19 @@
 package com.ruoyi.shop.marketing.seckill.domain;
 
+import cn.hutool.extra.spring.SpringUtil;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableLogic;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.ruoyi.common.core.domain.BaseEntity;
+import com.ruoyi.common.enums.order.MarketingType;
+import com.ruoyi.common.filepathsplicing.FilePathValue;
 import com.ruoyi.shop.enums.PriceType;
+import com.ruoyi.shop.product.domain.vo.ProductSkuSetVo;
 import com.ruoyi.shop.product.domain.vo.SkuTableItem;
+import com.ruoyi.shop.product.service.IProductSkuSetService;
 import com.ruoyi.shop.typehandler.ListToSkuTableItemTypeHandler;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
 import java.math.BigDecimal;
@@ -109,4 +115,28 @@ public class SeckillActivityProduct extends BaseEntity {
     @TableLogic
     private Integer delFlag;
 
+
+    /**
+     * 在实体添加,修改之前调用,在销量增减时也会调用一次
+     */
+    public void computeResultSaleAndResidueStock() {
+        //输出销量 = 初始化销量+真实销量
+        this.resultSaleNum = this.initSaleNum + this.realSaleNum;
+        this.realStockTotal = this.residueStockTotal + this.realSaleNum;
+
+    }
+
+    @FilePathValue
+    @ApiModelProperty("商品规格")
+    @TableField(exist = false)
+    private List<ProductSkuSetVo> productSkuSetList;
+
+    /**
+     * 加载商品规格信息
+     */
+    public void loadProductSkuSetList() {
+        IProductSkuSetService service = SpringUtil.getBean("productSkuSetServiceImpl", IProductSkuSetService.class);
+        this.productSkuSetList = service.loadVoByBusinessIdAndProductIdAndActivityProductId(this.businessId, this.productId, this.id, MarketingType.GROUP);
+        this.residueStockTotal = this.productSkuSetList.stream().mapToInt(ProductSkuSetVo::getStock).sum();
+    }
 }

+ 0 - 3
ruoyi-shop/src/main/java/com/ruoyi/shop/marketing/seckill/domain/bo/SeckillActivityProductBo.java

@@ -108,7 +108,6 @@ public class SeckillActivityProductBo extends BaseEntity {
      * 投放的真实库存总数
      */
     @ApiModelProperty(value = "投放的真实库存总数", required = true)
-    @NotNull(message = "投放的真实库存总数不能为空", groups = { AddGroup.class, EditGroup.class })
     private Integer realStockTotal;
 
     /**
@@ -134,14 +133,12 @@ public class SeckillActivityProductBo extends BaseEntity {
      * 最小销售价
      */
     @ApiModelProperty(value = "最小销售价", required = true)
-    @NotNull(message = "最小销售价不能为空", groups = { AddGroup.class, EditGroup.class })
     private BigDecimal minSalePrice;
 
     /**
      * 最大销售价
      */
     @ApiModelProperty(value = "最大销售价", required = true)
-    @NotNull(message = "最大销售价不能为空", groups = { AddGroup.class, EditGroup.class })
     private BigDecimal maxSalePrice;
 
 

+ 4 - 1
ruoyi-shop/src/main/java/com/ruoyi/shop/marketing/seckill/exception/SeckillActivityProductExceptionEnum.java

@@ -15,7 +15,10 @@ public enum SeckillActivityProductExceptionEnum implements IIntegerEnum {
     SECKILL_ACTIVITY_PRODUCT_NOT_EXIST(540003, "秒杀活动商品不存在"),
     DISCOUNT_OUT_OF_RANGE(540004, "固定折扣不在范围"),
     FIXPRICE_OUT_OF_RANGE(540005, "固定减价不在范围"),
-    REAL_STOCK_TOTAL_LT_INIT_SALE_NUM(540006, "可售数量应大于初始化销量");
+    REAL_STOCK_TOTAL_LT_INIT_SALE_NUM(540006, "可售数量应大于初始化销量"),
+    ACTIVITY_PRICE_OUT_OF_RANGE(540007, "活动价格不在范围"),
+    ACTIVITY_PRICE_LT_SALE_PRICE(540008, "活动价格需要小于原价"),
+    SALE_EXCEPTION(540009, "可售数量与初始化销量之和不能小于已售数量");
 
     private Integer code;
 

+ 104 - 23
ruoyi-shop/src/main/java/com/ruoyi/shop/marketing/seckill/service/impl/SeckillActivityProductServiceImpl.java

@@ -1,6 +1,5 @@
 package com.ruoyi.shop.marketing.seckill.service.impl;
 
-import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -10,6 +9,9 @@ import com.ruoyi.common.core.domain.PageQuery;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.exception.ServiceException;
 import com.ruoyi.common.utils.BeanCopyUtils;
+import com.ruoyi.common.utils.MathUtils;
+import com.ruoyi.common.utils.redis.RedisUtils;
+import com.ruoyi.common.utils.redis.SemaphoreTool;
 import com.ruoyi.shop.enums.PriceType;
 import com.ruoyi.shop.marketing.seckill.domain.SeckillActivityProduct;
 import com.ruoyi.shop.marketing.seckill.domain.bo.SeckillActivityProductBo;
@@ -19,8 +21,9 @@ import com.ruoyi.shop.marketing.seckill.mapper.SeckillActivityProductMapper;
 import com.ruoyi.shop.marketing.seckill.service.ISeckillActivityProductService;
 import com.ruoyi.shop.marketing.seckill.service.ISeckillActivityService;
 import com.ruoyi.shop.product.domain.Product;
-import com.ruoyi.shop.product.domain.vo.SkuTableItem;
+import com.ruoyi.shop.product.domain.bo.ProductSkuSetBo;
 import com.ruoyi.shop.product.service.IProductService;
+import com.ruoyi.shop.product.service.IProductSkuSetService;
 import lombok.RequiredArgsConstructor;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
@@ -44,9 +47,13 @@ public class SeckillActivityProductServiceImpl implements ISeckillActivityProduc
 
     private final SeckillActivityProductMapper baseMapper;
     private final IProductService productService;
+    private final SemaphoreTool semaphoreTool;
     @Lazy
     @Resource
     private ISeckillActivityService seckillActivityService;
+    @Lazy
+    @Resource
+    private IProductSkuSetService productSkuSetService;
 
     /**
      * 查询秒杀活动商品分页
@@ -127,6 +134,65 @@ public class SeckillActivityProductServiceImpl implements ISeckillActivityProduc
     @Transactional(rollbackFor = Exception.class)
     @Override
     public Boolean insertByBo(SeckillActivityProductBo bo) {
+        Product product = verifyBo(bo);
+        //如果该秒杀活动上架了该商品,则是修改
+        SeckillActivityProduct seckillActivityProduct =  this.baseMapper.selectOne(
+            new LambdaQueryWrapper<SeckillActivityProduct>()
+                .eq(SeckillActivityProduct::getProductId, bo.getProductId())
+                .eq(SeckillActivityProduct::getSeckillId, bo.getSeckillId())
+                .last("limit 1")
+        );
+        if (seckillActivityProduct == null) {
+            seckillActivityProduct = new SeckillActivityProduct();
+        }
+        SeckillActivityProduct add = BeanCopyUtils.copy(bo, seckillActivityProduct);
+        add.setSkuTableValue(product.getSkuTable());
+        //可售数量+初始化销量>=已售数量
+        if ((add.getRealStockTotal() + add.getInitSaleNum()) < add.getResultSaleNum()) {
+            throw new ServiceException(SeckillActivityProductExceptionEnum.SALE_EXCEPTION);
+        }
+        //保存
+        baseMapper.insertOrUpdate(add);
+        //保存秒杀活动sku
+        List<ProductSkuSetBo> seckillSkuList = bo.getProductSkuSetList();
+        productSkuSetService.batchSaveBySeckill(add, product, seckillSkuList);
+        //更新秒杀活动商品的最大、最小价格
+        //最小价格
+        BigDecimal minPrice = seckillSkuList.stream().map(ProductSkuSetBo::getSalePrice).min(BigDecimal::compareTo).orElseGet(() -> BigDecimal.ZERO);
+        //最大价格
+        BigDecimal maxPrice = seckillSkuList.stream().map(ProductSkuSetBo::getSalePrice).max(BigDecimal::compareTo).orElseGet(() -> BigDecimal.ZERO);
+        add.setMinPrice(minPrice);
+        add.setMaxPrice(maxPrice);
+        BigDecimal minSalePrice = seckillSkuList.stream().map(ProductSkuSetBo::getOriginalSalePrice).min(BigDecimal::compareTo).orElseGet(() -> BigDecimal.ZERO);
+        //最大价格
+        BigDecimal maxSalePrice = seckillSkuList.stream().map(ProductSkuSetBo::getOriginalSalePrice).max(BigDecimal::compareTo).orElseGet(() -> BigDecimal.ZERO);
+        //最小销售价(原商品的最小销售价)
+        add.setMinSalePrice(minSalePrice);
+        //最大销售价
+        add.setMaxSalePrice(maxSalePrice);
+        add.loadProductSkuSetList();
+        //3.2计算销量和库存
+        add.computeResultSaleAndResidueStock();
+        baseMapper.updateById(add);
+        //创建信号量
+        createAndSetSemaphore(add);
+        return true;
+    }
+
+    private void createAndSetSemaphore(SeckillActivityProduct activityProduct) {
+        String key = getSemaphoreKey(activityProduct.getSeckillId(), activityProduct.getId(), activityProduct.getProductId());
+        Integer num = RedisUtils.getCacheObject(key);
+        if (num != null) {
+            RedisUtils.deleteObject(key);
+        }
+        semaphoreTool.setRSemaphorePermits(key, activityProduct.getResidueStockTotal());
+    }
+
+    private String getSemaphoreKey(Long activityId, Long activityProductId, Long productId) {
+        return "seckill:activityId:" + activityId + "activityProductId:" + activityProductId + "_productId:" + productId;
+    }
+
+    private Product verifyBo(SeckillActivityProductBo bo) {
         //1、校验商品是否是该商家的
         Product product = productService.getByBusinessIdAndId(bo.getBusinessId(), bo.getProductId());
         //2、校验秒杀活动是否是该商家的
@@ -149,27 +215,21 @@ public class SeckillActivityProductServiceImpl implements ISeckillActivityProduc
             throw new ServiceException(SeckillActivityProductExceptionEnum.REAL_STOCK_TOTAL_LT_INIT_SALE_NUM);
         }
         //4、校验活动价格
-        List<SkuTableItem> skuTableValue = bo.getSkuTableValue();
-        if (CollUtil.isNotEmpty(skuTableValue)) {
-            for (SkuTableItem sku : skuTableValue) {
-//                //活动价格不能小于0
-//                if (sku.getActivityPrice().compareTo(BigDecimal.ZERO) < 0) {
-//                    throw new RRException(MarketingException.ACTIVITY_PRICE_OUT_OF_RANGE);
-//                }
-//                //活动价格需要小于原价
-//                if (MathUtils.subtract(sku.getActivityPrice(), sku.getSalePrice())
-//                    .compareTo(BigDecimal.ZERO) >= 0) {
-//                    throw new RRException(MarketingException.ACTIVITY_PRICE_LT_SALE_PRICE);
-//                }
+        List<ProductSkuSetBo> productSkuSetList = bo.getProductSkuSetList();
+        if (CollUtil.isNotEmpty(productSkuSetList)) {
+            for (ProductSkuSetBo setBo : productSkuSetList) {
+                //活动价格不能小于0
+                if (setBo.getSalePrice().compareTo(BigDecimal.ZERO) < 0) {
+                    throw new ServiceException(SeckillActivityProductExceptionEnum.ACTIVITY_PRICE_OUT_OF_RANGE);
+                }
+                //活动价格需要小于原价
+                if (MathUtils.subtract(setBo.getSalePrice(), setBo.getOriginalSalePrice())
+                    .compareTo(BigDecimal.ZERO) >= 0) {
+                    throw new ServiceException(SeckillActivityProductExceptionEnum.ACTIVITY_PRICE_LT_SALE_PRICE);
+                }
             }
         }
-        SeckillActivityProduct add = BeanUtil.toBean(bo, SeckillActivityProduct.class);
-        validEntityBeforeSave(add);
-        boolean flag = baseMapper.insert(add) > 0;
-        if (flag) {
-            bo.setId(add.getId());
-        }
-        return flag;
+        return product;
     }
 
     /**
@@ -201,12 +261,33 @@ public class SeckillActivityProductServiceImpl implements ISeckillActivityProduc
      * @param ids 需要删除的秒杀活动商品主键
      * @return 结果
      */
+    @Transactional(rollbackFor = Exception.class)
     @Override
     public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
-        if(isValid){
+        if (isValid) {
             //TODO 做一些业务上的校验,判断是否需要校验
         }
-        return baseMapper.deleteBatchIds(ids) > 0;
+        for (Long id : ids) {
+            //秒杀活动只要开始了,就不能被删除
+//            SeckillActivityGoods goods = this.loadActivityGoodsById(id, true);
+//            SeckillActivity activity = seckillActivityService.getById(goods.getSeckillId());
+//            Date startDate = DateUtils.stringToDate(DateCustomUtils.dateToString(activity.getStartTime()) + " " + activity.getStartHour());
+//            Date endDate = DateUtils.stringToDate(DateCustomUtils.dateToString(activity.getEndTime()) + " " + activity.getEndHour());
+//            long timeMillis = System.currentTimeMillis();
+//            if (timeMillis > startDate.getTime() && timeMillis <= endDate.getTime()) {
+//                throw new RRException(MarketingException.SECKILL_ACTIVITY_START);
+//            }
+//
+//            //1、删除秒杀活动商品
+//            this.baseMapper.deleteById(id);
+//
+//            //2、删除秒杀活动商品sku
+//            seckillGoodsSkuService.deleteBySeckillGoodsId(id);
+//
+//            //删除秒杀的信号量
+//            redisUtils.delete(SeckillActivityGoodsService.getSemaphoreKey(goods.getSeckillId(), goods.getId(), goods.getGoodsId()));
+        }
+        return true;
     }
 
     @Override

+ 5 - 4
ruoyi-shop/src/main/java/com/ruoyi/shop/product/service/IProductSkuSetService.java

@@ -1,15 +1,15 @@
 package com.ruoyi.shop.product.service;
 
+import com.ruoyi.common.core.domain.PageQuery;
+import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.order.MarketingType;
 import com.ruoyi.shop.marketing.groupbuy.domain.GroupBuyActivityGoods;
+import com.ruoyi.shop.marketing.seckill.domain.SeckillActivityProduct;
 import com.ruoyi.shop.product.domain.Product;
 import com.ruoyi.shop.product.domain.ProductSkuSet;
-import com.ruoyi.shop.product.domain.vo.ProductSkuSetVo;
 import com.ruoyi.shop.product.domain.bo.ProductSkuSetBo;
-import com.ruoyi.common.core.page.TableDataInfo;
-import com.ruoyi.common.core.domain.PageQuery;
+import com.ruoyi.shop.product.domain.vo.ProductSkuSetVo;
 
-import javax.validation.constraints.NotNull;
 import java.util.Collection;
 import java.util.List;
 
@@ -143,4 +143,5 @@ public interface IProductSkuSetService {
     ProductSkuSetVo loadVoByBusinessIdAndProductIdAndActivityProductIdAndSkuHash( Long businessId, Long productId, String  skuHashCode,Long activityProductId, MarketingType marketingType);
 
 
+    void batchSaveBySeckill(SeckillActivityProduct add, Product product, List<ProductSkuSetBo> productSkuSetList);
 }

+ 46 - 0
ruoyi-shop/src/main/java/com/ruoyi/shop/product/service/impl/ProductSkuSetServiceImpl.java

@@ -16,6 +16,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.ruoyi.shop.marketing.groupbuy.domain.GroupBuyActivityGoods;
+import com.ruoyi.shop.marketing.seckill.domain.SeckillActivityProduct;
 import com.ruoyi.shop.product.domain.Product;
 import com.ruoyi.shop.product.domain.bo.ProductSkuSetStockBo;
 import com.ruoyi.shop.product.domain.vo.SkuItem;
@@ -295,4 +296,49 @@ public class ProductSkuSetServiceImpl implements IProductSkuSetService {
         return productSkuSetVo;
     }
 
+    @Override
+    public void batchSaveBySeckill(SeckillActivityProduct add, Product product, List<ProductSkuSetBo> productSkuSetList) {
+        //删除
+        this.baseMapper.delete(new LambdaQueryWrapper<ProductSkuSet>()
+            .eq(ProductSkuSet::getProductId, add.getProductId())
+            .eq(ProductSkuSet::getBusinessId, add.getBusinessId())
+            .eq(ProductSkuSet::getMarketingType, MarketingType.SECKILL)
+            .eq(ProductSkuSet::getActivityProductId, add.getId())
+        );
+        if(CollectionUtil.isEmpty(productSkuSetList))
+        {
+            return ;
+        }
+        for (ProductSkuSetBo productSkuSetBo : productSkuSetList){
+            productSkuSetBo.setMarketingType(MarketingType.SECKILL);
+            productSkuSetBo.setActivityId(add.getSeckillId());
+            productSkuSetBo.setActivityProductId(add.getId());
+            productSkuSetBo.setBusinessId(add.getBusinessId());
+            productSkuSetBo.setProductId(add.getProductId());
+            productSkuSetBo.setSkuHashCode("_");
+            productSkuSetBo.setSkuSetName("默认");
+            productSkuSetBo.setOriginalSalePrice(productSkuSetBo.getOriginalSalePrice());
+            productSkuSetBo.setCover(StringUtils.isBlank(productSkuSetBo.getCover())?product.getCoverImage():productSkuSetBo.getCover());
+            if(CollectionUtil.isNotEmpty(productSkuSetBo.getSkuSetValue()))
+            {
+                //安id升序排序
+                productSkuSetBo.getSkuSetValue().sort(Comparator.comparingLong(SkuItem::getId));
+                String skuHashCode = productSkuSetBo.getSkuSetValue().stream().map(SkuItem::getId).map(String::valueOf).collect(Collectors.joining("_"));
+                if(StringUtils.isNotBlank(skuHashCode))
+                {
+                    //头尾加下划线 "_"
+                    skuHashCode = "_"+skuHashCode+"_";
+                }
+                productSkuSetBo.setSkuHashCode(skuHashCode);
+                productSkuSetBo.setSkuSetName(productSkuSetBo.getSkuSetValue().stream().map(SkuItem::getName).collect(Collectors.joining(",")));
+            }
+            //保存
+            this.insertByBo(productSkuSetBo);
+
+            //保存库存
+            ProductSkuSetStockBo productSkuSetStockBo = BeanUtil.toBean(productSkuSetBo, ProductSkuSetStockBo.class);
+            iProductSkuSetStockService.saveStock(productSkuSetStockBo);
+        }
+    }
+
 }