|  | @@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
 | 
	
		
			
				|  |  |  import cn.hutool.core.util.ObjectUtil;
 | 
	
		
			
				|  |  |  import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 | 
	
		
			
				|  |  |  import cn.iocoder.yudao.framework.common.pojo.PageResult;
 | 
	
		
			
				|  |  | +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 | 
	
		
			
				|  |  |  import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
 | 
	
		
			
				|  |  |  import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
 | 
	
		
			
				|  |  |  import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
 | 
	
	
		
			
				|  | @@ -18,23 +19,21 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.
 | 
	
		
			
				|  |  |  import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillProductDO;
 | 
	
		
			
				|  |  |  import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity.SeckillActivityMapper;
 | 
	
		
			
				|  |  |  import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity.SeckillProductMapper;
 | 
	
		
			
				|  |  | -import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum;
 | 
	
		
			
				|  |  |  import cn.iocoder.yudao.module.promotion.service.seckill.seckillconfig.SeckillConfigService;
 | 
	
		
			
				|  |  |  import cn.iocoder.yudao.module.promotion.util.PromotionUtils;
 | 
	
		
			
				|  |  |  import org.springframework.stereotype.Service;
 | 
	
		
			
				|  |  | +import org.springframework.transaction.annotation.Transactional;
 | 
	
		
			
				|  |  |  import org.springframework.validation.annotation.Validated;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import javax.annotation.Resource;
 | 
	
		
			
				|  |  | -import java.util.ArrayList;
 | 
	
		
			
				|  |  |  import java.util.Collection;
 | 
	
		
			
				|  |  |  import java.util.List;
 | 
	
		
			
				|  |  | -import java.util.stream.Collectors;
 | 
	
		
			
				|  |  | +import java.util.Set;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 | 
	
		
			
				|  |  |  import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS;
 | 
	
		
			
				|  |  |  import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
 | 
	
		
			
				|  |  |  import static cn.iocoder.yudao.module.promotion.util.PromotionUtils.validateProductSkuExistence;
 | 
	
		
			
				|  |  | -import static java.util.Arrays.asList;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /**
 | 
	
		
			
				|  |  |   * 秒杀活动 Service 实现类
 | 
	
	
		
			
				|  | @@ -57,17 +56,19 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
 | 
	
		
			
				|  |  |      private ProductSkuApi productSkuApi;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @Override
 | 
	
		
			
				|  |  | +    @Transactional(rollbackFor = Exception.class)
 | 
	
		
			
				|  |  |      public Long createSeckillActivity(SeckillActivityCreateReqVO createReqVO) {
 | 
	
		
			
				|  |  |          // 校验商品秒秒杀时段是否冲突
 | 
	
		
			
				|  |  | -        validateProductSpuSeckillConflict(createReqVO.getConfigIds(), createReqVO.getSpuIds());
 | 
	
		
			
				|  |  | +        validateProductSpuSeckillConflict(createReqVO.getConfigIds(), createReqVO.getSpuId(), null);
 | 
	
		
			
				|  |  |          // 获取所选 spu下的所有 sku
 | 
	
		
			
				|  |  | -        List<ProductSkuRespDTO> skus = productSkuApi.getSkuListBySpuId(createReqVO.getSpuIds());
 | 
	
		
			
				|  |  | +        List<ProductSkuRespDTO> skus = productSkuApi.getSkuListBySpuId(CollUtil.newArrayList(createReqVO.getSpuId()));
 | 
	
		
			
				|  |  |          // 校验商品 sku 是否存在
 | 
	
		
			
				|  |  |          validateProductSkuExistence(skus, createReqVO.getProducts(), SeckillProductCreateReqVO::getSkuId);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          // 插入秒杀活动
 | 
	
		
			
				|  |  |          SeckillActivityDO activity = SeckillActivityConvert.INSTANCE.convert(createReqVO)
 | 
	
		
			
				|  |  | -                .setStatus(PromotionUtils.calculateActivityStatus(createReqVO.getEndTime()));
 | 
	
		
			
				|  |  | +                .setStatus(PromotionUtils.calculateActivityStatus(createReqVO.getEndTime()))
 | 
	
		
			
				|  |  | +                .setTotalStock(CollectionUtils.getSumValue(createReqVO.getProducts(), SeckillProductCreateReqVO::getStock, Integer::sum));
 | 
	
		
			
				|  |  |          seckillActivityMapper.insert(activity);
 | 
	
		
			
				|  |  |          // 插入商品
 | 
	
		
			
				|  |  |          List<SeckillProductDO> product = SeckillActivityConvert.INSTANCE.convertList(activity, createReqVO.getProducts());
 | 
	
	
		
			
				|  | @@ -75,119 +76,124 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
 | 
	
		
			
				|  |  |          return activity.getId();
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    private void validateProductSpuSeckillConflict(List<Long> configIds, List<Long> spuIds) {
 | 
	
		
			
				|  |  | +    private void validateProductSpuSeckillConflict(List<Long> configIds, Long spuId, Long activityId) {
 | 
	
		
			
				|  |  |          // 校验秒杀时段是否存在
 | 
	
		
			
				|  |  |          seckillConfigService.validateSeckillConfigExists(configIds);
 | 
	
		
			
				|  |  |          // 校验商品 spu 是否存在
 | 
	
		
			
				|  |  | -        List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(spuIds);
 | 
	
		
			
				|  |  | -        if (ObjectUtil.notEqual(spuIds.size(), spuList.size())) {
 | 
	
		
			
				|  |  | +        List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(CollUtil.newArrayList(spuId));
 | 
	
		
			
				|  |  | +        if (CollUtil.isEmpty(spuList)) {
 | 
	
		
			
				|  |  |              throw exception(SPU_NOT_EXISTS);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          // 查询所有开启的秒杀活动
 | 
	
		
			
				|  |  |          List<SeckillActivityDO> activityDOs = seckillActivityMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus());
 | 
	
		
			
				|  |  | -        // 过滤出所有 spuIds 有交集的活动
 | 
	
		
			
				|  |  | -        List<SeckillActivityDO> doList = activityDOs.stream().filter(s -> {
 | 
	
		
			
				|  |  | -            // 判断 spu 是否有交集
 | 
	
		
			
				|  |  | -            ArrayList<Long> spuIdsClone = CollUtil.newArrayList(s.getSpuIds());
 | 
	
		
			
				|  |  | -            spuIdsClone.retainAll(spuIds);
 | 
	
		
			
				|  |  | -            if (CollUtil.isEmpty(spuIdsClone)) {
 | 
	
		
			
				|  |  | -                return false;
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | +        if (activityId != null) {
 | 
	
		
			
				|  |  | +            // 更新时移除本活动
 | 
	
		
			
				|  |  | +            activityDOs.removeIf(item -> ObjectUtil.equal(item.getId(), activityId));
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        // 过滤出所有 spuId 有交集的活动
 | 
	
		
			
				|  |  | +        List<SeckillActivityDO> activityDOs1 = CollectionUtils.convertList(activityDOs, c -> c, s -> ObjectUtil.equal(s.getSpuId(), spuId));
 | 
	
		
			
				|  |  | +        if (CollUtil.isNotEmpty(activityDOs1)) {
 | 
	
		
			
				|  |  | +            throw exception(SECKILL_ACTIVITY_SPU_CONFLICTS);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        List<SeckillActivityDO> activityDOs2 = CollectionUtils.convertList(activityDOs, c -> c, s -> {
 | 
	
		
			
				|  |  |              // 判断秒杀时段是否有交集
 | 
	
		
			
				|  |  |              List<Long> configIdsClone = CollUtil.newArrayList(s.getConfigIds());
 | 
	
		
			
				|  |  |              configIdsClone.retainAll(configIds);
 | 
	
		
			
				|  |  |              return CollUtil.isNotEmpty(configIdsClone);
 | 
	
		
			
				|  |  | -        }).collect(Collectors.toList());
 | 
	
		
			
				|  |  | -        if (CollUtil.isNotEmpty(doList)) {
 | 
	
		
			
				|  |  | -            throw exception(SECKILL_ACTIVITY_SPU_CONFLICTS);
 | 
	
		
			
				|  |  | +        });
 | 
	
		
			
				|  |  | +        if (CollUtil.isNotEmpty(activityDOs2)) {
 | 
	
		
			
				|  |  | +            throw exception(SECKILL_TIME_CONFLICTS);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @Override
 | 
	
		
			
				|  |  | +    @Transactional(rollbackFor = Exception.class)
 | 
	
		
			
				|  |  |      public void updateSeckillActivity(SeckillActivityUpdateReqVO updateReqVO) {
 | 
	
		
			
				|  |  |          // 校验存在
 | 
	
		
			
				|  |  |          SeckillActivityDO seckillActivity = validateSeckillActivityExists(updateReqVO.getId());
 | 
	
		
			
				|  |  | -        if (CommonStatusEnum.ENABLE.getStatus().equals(seckillActivity.getStatus())) {
 | 
	
		
			
				|  |  | +        if (CommonStatusEnum.DISABLE.getStatus().equals(seckillActivity.getStatus())) {
 | 
	
		
			
				|  |  |              throw exception(SECKILL_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          // 校验商品是否冲突
 | 
	
		
			
				|  |  | -        validateProductSpuSeckillConflict(updateReqVO.getConfigIds(), updateReqVO.getSpuIds());
 | 
	
		
			
				|  |  | +        validateProductSpuSeckillConflict(updateReqVO.getConfigIds(), updateReqVO.getSpuId(), updateReqVO.getId());
 | 
	
		
			
				|  |  |          // 获取所选 spu下的所有 sku
 | 
	
		
			
				|  |  | -        List<ProductSkuRespDTO> skus = productSkuApi.getSkuListBySpuId(updateReqVO.getSpuIds());
 | 
	
		
			
				|  |  | +        List<ProductSkuRespDTO> skus = productSkuApi.getSkuListBySpuId(CollUtil.newArrayList(updateReqVO.getSpuId()));
 | 
	
		
			
				|  |  |          // 校验商品 sku 是否存在
 | 
	
		
			
				|  |  |          validateProductSkuExistence(skus, updateReqVO.getProducts(), SeckillProductUpdateReqVO::getSkuId);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          // 更新活动
 | 
	
		
			
				|  |  |          SeckillActivityDO updateObj = SeckillActivityConvert.INSTANCE.convert(updateReqVO)
 | 
	
		
			
				|  |  | -                .setStatus(PromotionUtils.calculateActivityStatus(updateReqVO.getEndTime()));
 | 
	
		
			
				|  |  | +                .setStatus(PromotionUtils.calculateActivityStatus(updateReqVO.getEndTime()))
 | 
	
		
			
				|  |  | +                .setTotalStock(CollectionUtils.getSumValue(updateReqVO.getProducts(), SeckillProductUpdateReqVO::getStock, Integer::sum));
 | 
	
		
			
				|  |  |          seckillActivityMapper.updateById(updateObj);
 | 
	
		
			
				|  |  |          // 更新商品
 | 
	
		
			
				|  |  | -        //updateSeckillProduct(updateReqVO);
 | 
	
		
			
				|  |  | +        updateSeckillProduct(updateObj, updateReqVO.getProducts());
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      /**
 | 
	
		
			
				|  |  |       * 更新秒杀商品
 | 
	
		
			
				|  |  | -     * 后台查出的数据和前台查出的数据进行遍历,
 | 
	
		
			
				|  |  | -     * 1. 对前台数据进行遍历:如果不存在于后台的 sku 中需要新增
 | 
	
		
			
				|  |  | -     * 2. 对后台数据进行遍历:如果不存在于前台的 sku 中需要删除
 | 
	
		
			
				|  |  | -     * 3. 最后对当前活动商品全部更新,更新秒杀时段id列表
 | 
	
		
			
				|  |  |       *
 | 
	
		
			
				|  |  | -     * @param updateReqVO 更新的请求VO
 | 
	
		
			
				|  |  | +     * @param updateObj DO
 | 
	
		
			
				|  |  | +     * @param products  商品配置
 | 
	
		
			
				|  |  |       */
 | 
	
		
			
				|  |  | -    private void updateSeckillProduct(SeckillActivityUpdateReqVO updateReqVO) {
 | 
	
		
			
				|  |  | -        // TODO puhui999:要不这里简单一点;删除原本的,插入新增的;不做的这么细致
 | 
	
		
			
				|  |  | -        // TODO puhui999:后续完善
 | 
	
		
			
				|  |  | -        //List<SeckillProductDO> seckillProductDOs = seckillProductMapper.selectListByActivityId(updateReqVO.getId());
 | 
	
		
			
				|  |  | -        //List<SeckillProductUpdateReqVO> products = updateReqVO.getProducts();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        ////计算需要删除的数据
 | 
	
		
			
				|  |  | -        //List<Long> deleteIds = CollectionUtils.convertList(seckillProductDOs, SeckillProductDO::getId,
 | 
	
		
			
				|  |  | -        //        seckillProductDO -> products.stream()
 | 
	
		
			
				|  |  | -        //                .noneMatch(product -> SeckillActivityConvert.INSTANCE.isEquals(seckillProductDO, product)));
 | 
	
		
			
				|  |  | -        //if (CollUtil.isNotEmpty(deleteIds)) {
 | 
	
		
			
				|  |  | -        //    seckillProductMapper.deleteBatchIds(deleteIds);
 | 
	
		
			
				|  |  | -        //}
 | 
	
		
			
				|  |  | -        //
 | 
	
		
			
				|  |  | -        //// 计算需要新增的数据
 | 
	
		
			
				|  |  | -        //List<SeckillProductDO> newSeckillProductDOs = CollectionUtils.convertList(products,
 | 
	
		
			
				|  |  | -        //        product -> SeckillActivityConvert.INSTANCE.convert(product).setActivityId(updateReqVO.getId()));
 | 
	
		
			
				|  |  | -        //newSeckillProductDOs.removeIf(product -> seckillProductDOs.stream()
 | 
	
		
			
				|  |  | -        //        .anyMatch(seckillProduct -> SeckillActivityConvert.INSTANCE.isEquals(seckillProduct, product)));
 | 
	
		
			
				|  |  | -        //if (CollUtil.isNotEmpty(newSeckillProductDOs)) {
 | 
	
		
			
				|  |  | -        //    seckillProductMapper.insertBatch(newSeckillProductDOs);
 | 
	
		
			
				|  |  | -        //}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        //全量更新当前活动商品的秒杀时段id列表(timeIds)
 | 
	
		
			
				|  |  | -        seckillProductMapper.updateTimeIdsByActivityId(updateReqVO.getId(), updateReqVO.getConfigIds());
 | 
	
		
			
				|  |  | +    private void updateSeckillProduct(SeckillActivityDO updateObj, List<SeckillProductUpdateReqVO> products) {
 | 
	
		
			
				|  |  | +        List<SeckillProductDO> seckillProductDOs = seckillProductMapper.selectListByActivityId(updateObj.getId());
 | 
	
		
			
				|  |  | +        // 数据库中的活动商品
 | 
	
		
			
				|  |  | +        Set<Long> convertSet = CollectionUtils.convertSet(seckillProductDOs, SeckillProductDO::getSkuId);
 | 
	
		
			
				|  |  | +        // 前端传过来的活动商品
 | 
	
		
			
				|  |  | +        Set<Long> convertSet1 = CollectionUtils.convertSet(products, SeckillProductUpdateReqVO::getSkuId);
 | 
	
		
			
				|  |  | +        // 删除后台存在的前端不存在的商品
 | 
	
		
			
				|  |  | +        List<Long> d = CollectionUtils.filterList(convertSet, item -> !convertSet1.contains(item));
 | 
	
		
			
				|  |  | +        if (CollUtil.isNotEmpty(d)) {
 | 
	
		
			
				|  |  | +            seckillProductMapper.deleteBatchIds(d);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        // 前端存在的后端不存在的商品
 | 
	
		
			
				|  |  | +        List<Long> c = CollectionUtils.filterList(convertSet1, item -> !convertSet.contains(item));
 | 
	
		
			
				|  |  | +        if (CollUtil.isNotEmpty(c)) {
 | 
	
		
			
				|  |  | +            List<SeckillProductUpdateReqVO> vos = CollectionUtils.filterList(products, item -> c.contains(item.getSkuId()));
 | 
	
		
			
				|  |  | +            List<SeckillProductDO> productDOs = SeckillActivityConvert.INSTANCE.convertList(updateObj, vos);
 | 
	
		
			
				|  |  | +            seckillProductMapper.insertBatch(productDOs);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        // 更新已存在的商品
 | 
	
		
			
				|  |  | +        List<Long> u = CollectionUtils.filterList(convertSet1, convertSet::contains);
 | 
	
		
			
				|  |  | +        if (CollUtil.isNotEmpty(u)) {
 | 
	
		
			
				|  |  | +            List<SeckillProductUpdateReqVO> vos = CollectionUtils.filterList(products, item -> u.contains(item.getSkuId()));
 | 
	
		
			
				|  |  | +            List<SeckillProductDO> productDOs = SeckillActivityConvert.INSTANCE.convertList1(updateObj, vos, seckillProductDOs);
 | 
	
		
			
				|  |  | +            seckillProductMapper.insertBatch(productDOs);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @Override
 | 
	
		
			
				|  |  | +    @Transactional(rollbackFor = Exception.class)
 | 
	
		
			
				|  |  |      public void closeSeckillActivity(Long id) {
 | 
	
		
			
				|  |  | +        // TODO 待验证没使用过
 | 
	
		
			
				|  |  |          // 校验存在
 | 
	
		
			
				|  |  | -        SeckillActivityDO seckillActivity = this.validateSeckillActivityExists(id);
 | 
	
		
			
				|  |  | -        if (PromotionActivityStatusEnum.CLOSE.getStatus().equals(seckillActivity.getStatus())) {
 | 
	
		
			
				|  |  | +        SeckillActivityDO seckillActivity = validateSeckillActivityExists(id);
 | 
	
		
			
				|  |  | +        if (CommonStatusEnum.DISABLE.getStatus().equals(seckillActivity.getStatus())) {
 | 
	
		
			
				|  |  |              throw exception(SECKILL_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -        if (PromotionActivityStatusEnum.END.getStatus().equals(seckillActivity.getStatus())) {
 | 
	
		
			
				|  |  | -            throw exception(SECKILL_ACTIVITY_CLOSE_FAIL_STATUS_END);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          // 更新
 | 
	
		
			
				|  |  | -        SeckillActivityDO updateObj = new SeckillActivityDO().setId(id).setStatus(PromotionActivityStatusEnum.CLOSE.getStatus());
 | 
	
		
			
				|  |  | +        SeckillActivityDO updateObj = new SeckillActivityDO().setId(id).setStatus(CommonStatusEnum.DISABLE.getStatus());
 | 
	
		
			
				|  |  |          seckillActivityMapper.updateById(updateObj);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @Override
 | 
	
		
			
				|  |  | +    @Transactional(rollbackFor = Exception.class)
 | 
	
		
			
				|  |  |      public void deleteSeckillActivity(Long id) {
 | 
	
		
			
				|  |  |          // 校验存在
 | 
	
		
			
				|  |  |          SeckillActivityDO seckillActivity = this.validateSeckillActivityExists(id);
 | 
	
		
			
				|  |  | -        List<Integer> statuses = asList(PromotionActivityStatusEnum.CLOSE.getStatus(), PromotionActivityStatusEnum.END.getStatus());
 | 
	
		
			
				|  |  | -        if (!statuses.contains(seckillActivity.getStatus())) {
 | 
	
		
			
				|  |  | +        if (CommonStatusEnum.ENABLE.getStatus().equals(seckillActivity.getStatus())) {
 | 
	
		
			
				|  |  |              throw exception(SECKILL_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        // 删除
 | 
	
		
			
				|  |  | +        // 删除活动
 | 
	
		
			
				|  |  |          seckillActivityMapper.deleteById(id);
 | 
	
		
			
				|  |  | +        // 删除活动商品
 | 
	
		
			
				|  |  | +        List<SeckillProductDO> productDOs = seckillProductMapper.selectListByActivityId(id);
 | 
	
		
			
				|  |  | +        Set<Long> convertSet = CollectionUtils.convertSet(productDOs, SeckillProductDO::getSkuId);
 | 
	
		
			
				|  |  | +        seckillProductMapper.deleteBatchIds(convertSet);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      private SeckillActivityDO validateSeckillActivityExists(Long id) {
 |