mirror of
				https://github.com/YunaiV/ruoyi-vue-pro.git
				synced 2025-11-01 03:28:41 +08:00 
			
		
		
		
	优化拼团活动的管理后台代码
This commit is contained in:
		| @ -1,8 +1,10 @@ | ||||
| package cn.iocoder.yudao.module.promotion.controller.admin.combination; | ||||
|  | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
| import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; | ||||
| import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; | ||||
| import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO; | ||||
| import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO; | ||||
| import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityRespVO; | ||||
| @ -21,7 +23,6 @@ import org.springframework.web.bind.annotation.*; | ||||
| import javax.annotation.Resource; | ||||
| import javax.validation.Valid; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
|  | ||||
| import static cn.hutool.core.collection.CollectionUtil.newArrayList; | ||||
| import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | ||||
| @ -37,7 +38,7 @@ public class CombinationActivityController { | ||||
|     private CombinationActivityService combinationActivityService; | ||||
|  | ||||
|     @Resource | ||||
|     private ProductSpuApi spuApi; | ||||
|     private ProductSpuApi productSpuApi; | ||||
|  | ||||
|     @PostMapping("/create") | ||||
|     @Operation(summary = "创建拼团活动") | ||||
| @ -80,12 +81,16 @@ public class CombinationActivityController { | ||||
|             @Valid CombinationActivityPageReqVO pageVO) { | ||||
|         // 查询拼团活动 | ||||
|         PageResult<CombinationActivityDO> pageResult = combinationActivityService.getCombinationActivityPage(pageVO); | ||||
|         if (CollUtil.isEmpty(pageResult.getList())) { | ||||
|             return success(PageResult.empty(pageResult.getTotal())); | ||||
|         } | ||||
|  | ||||
|         // 拼接数据 | ||||
|         Set<Long> activityIds = convertSet(pageResult.getList(), CombinationActivityDO::getId); | ||||
|         Set<Long> spuIds = convertSet(pageResult.getList(), CombinationActivityDO::getSpuId); | ||||
|         return success(CombinationActivityConvert.INSTANCE.convertPage(pageResult, | ||||
|                 combinationActivityService.getCombinationProductsByActivityIds(activityIds), | ||||
|                 spuApi.getSpuList(spuIds))); | ||||
|         List<CombinationProductDO> products = combinationActivityService.getCombinationProductsByActivityIds( | ||||
|                 convertSet(pageResult.getList(), CombinationActivityDO::getId)); | ||||
|         List<ProductSpuRespDTO> spus = productSpuApi.getSpuList( | ||||
|                 convertSet(pageResult.getList(), CombinationActivityDO::getSpuId)); | ||||
|         return success(CombinationActivityConvert.INSTANCE.convertPage(pageResult, products, spus)); | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -22,7 +22,7 @@ public class CombinationActivityBaseVO { | ||||
|     @NotNull(message = "拼团名称不能为空") | ||||
|     private String name; | ||||
|  | ||||
|     @Schema(description = "商品 SPU 编号,关联 ProductSpuDO 的 id", example = "[1,2,3]") | ||||
|     @Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||||
|     @NotNull(message = "拼团商品不能为空") | ||||
|     private Long spuId; | ||||
|  | ||||
| @ -48,7 +48,7 @@ public class CombinationActivityBaseVO { | ||||
|     @NotNull(message = "开团人数不能为空") | ||||
|     private Integer userSize; | ||||
|  | ||||
|     @Schema(description = "限制时长(小时)", requiredMode = Schema.RequiredMode.REQUIRED) | ||||
|     @Schema(description = "限制时长(小时)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | ||||
|     @NotNull(message = "限制时长不能为空") | ||||
|     private Integer limitDuration; | ||||
|  | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity; | ||||
|  | ||||
| import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductCreateReqVO; | ||||
| import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductBaseVO; | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| @ -17,6 +17,6 @@ public class CombinationActivityCreateReqVO extends CombinationActivityBaseVO { | ||||
|  | ||||
|     @Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED) | ||||
|     @Valid | ||||
|     private List<CombinationProductCreateReqVO> products; | ||||
|     private List<CombinationProductBaseVO> products; | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -20,15 +20,6 @@ public class CombinationActivityPageReqVO extends PageParam { | ||||
|     @Schema(description = "拼团名称", example = "赵六") | ||||
|     private String name; | ||||
|  | ||||
|     @Schema(description = "商品 SPU 编号关联 ProductSpuDO 的 id", example = "14016") | ||||
|     private Long spuId; | ||||
|  | ||||
|     @Schema(description = "总限购数量", example = "16218") | ||||
|     private Integer totalLimitCount; | ||||
|  | ||||
|     @Schema(description = "单次限购数量", example = "28265") | ||||
|     private Integer singleLimitCount; | ||||
|  | ||||
|     @Schema(description = "开始时间") | ||||
|     @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||||
|     private LocalDateTime[] startTime; | ||||
| @ -37,29 +28,7 @@ public class CombinationActivityPageReqVO extends PageParam { | ||||
|     @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||||
|     private LocalDateTime[] endTime; | ||||
|  | ||||
|     @Schema(description = "开团人数") | ||||
|     private Integer userSize; | ||||
|  | ||||
|     @Schema(description = "开团组数") | ||||
|     private Integer totalNum; | ||||
|  | ||||
|     @Schema(description = "成团组数") | ||||
|     private Integer successNum; | ||||
|  | ||||
|     @Schema(description = "参与人数", example = "25222") | ||||
|     private Integer orderUserCount; | ||||
|  | ||||
|     @Schema(description = "虚拟成团") | ||||
|     private Integer virtualGroup; | ||||
|  | ||||
|     @Schema(description = "活动状态:0开启 1关闭", example = "0") | ||||
|     private Integer status; | ||||
|  | ||||
|     @Schema(description = "限制时长(小时)") | ||||
|     private Integer limitDuration; | ||||
|  | ||||
|     @Schema(description = "创建时间") | ||||
|     @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||||
|     private LocalDateTime[] createTime; | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -17,40 +17,34 @@ import java.util.List; | ||||
| @ToString(callSuper = true) | ||||
| public class CombinationActivityRespVO extends CombinationActivityBaseVO { | ||||
|  | ||||
|     @Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "618大促") | ||||
|     @Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22901") | ||||
|     private Long id; | ||||
|  | ||||
|     @Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "618 大促") | ||||
|     private String spuName; | ||||
|  | ||||
|     @Schema(description = "商品主图", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.png") | ||||
|     private String picUrl; | ||||
|  | ||||
|     @Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22901") | ||||
|     private Long id; | ||||
|  | ||||
|     @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | ||||
|     private LocalDateTime createTime; | ||||
|  | ||||
|     @Schema(description = "开团人数", requiredMode = Schema.RequiredMode.REQUIRED) | ||||
|     @NotNull(message = "开团人数不能为空") | ||||
|     @Schema(description = "开团人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "666") | ||||
|     private Integer userSize; | ||||
|  | ||||
|     @Schema(description = "开团组数", requiredMode = Schema.RequiredMode.REQUIRED) | ||||
|     @NotNull(message = "开团组数不能为空") | ||||
|     private Integer totalNum; | ||||
|     @Schema(description = "开团组数", requiredMode = Schema.RequiredMode.REQUIRED, example = "33") | ||||
|     private Integer totalCount; | ||||
|  | ||||
|     @Schema(description = "成团组数", requiredMode = Schema.RequiredMode.REQUIRED) | ||||
|     @NotNull(message = "成团组数不能为空") | ||||
|     private Integer successNum; | ||||
|     @Schema(description = "成团组数", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") | ||||
|     private Integer successCount; | ||||
|  | ||||
|     @Schema(description = "虚拟成团", requiredMode = Schema.RequiredMode.REQUIRED) | ||||
|     @NotNull(message = "虚拟成团不能为空") | ||||
|     @Schema(description = "虚拟成团", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") | ||||
|     private Integer virtualGroup; | ||||
|  | ||||
|     @Schema(description = "活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") | ||||
|     @NotNull(message = "活动状态不能为空") | ||||
|     private Integer status; | ||||
|  | ||||
|     @Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED) | ||||
|     @Valid | ||||
|     private List<CombinationProductRespVO> products; | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity; | ||||
|  | ||||
| import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductUpdateReqVO; | ||||
| import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductBaseVO; | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| @ -22,6 +22,6 @@ public class CombinationActivityUpdateReqVO extends CombinationActivityBaseVO { | ||||
|  | ||||
|     @Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED) | ||||
|     @Valid | ||||
|     private List<CombinationProductUpdateReqVO> products; | ||||
|     private List<CombinationProductBaseVO> products; | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -21,7 +21,7 @@ public class CombinationProductBaseVO { | ||||
|     private Long skuId; | ||||
|  | ||||
|     @Schema(description = "拼团价格,单位分", requiredMode = Schema.RequiredMode.REQUIRED, example = "27682") | ||||
|     @NotNull(message = "拼团价格,单位分不能为空") | ||||
|     private Integer activePrice; | ||||
|     @NotNull(message = "拼团价格不能为空") | ||||
|     private Integer combinationPrice; | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -1,14 +0,0 @@ | ||||
| package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| import lombok.ToString; | ||||
|  | ||||
| @Schema(description = "管理后台 - 拼团商品创建 Request VO") | ||||
| @Data | ||||
| @EqualsAndHashCode(callSuper = true) | ||||
| @ToString(callSuper = true) | ||||
| public class CombinationProductCreateReqVO extends CombinationProductBaseVO { | ||||
|  | ||||
| } | ||||
| @ -1,14 +0,0 @@ | ||||
| package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| import lombok.ToString; | ||||
|  | ||||
| @Schema(description = "管理后台 - 拼团商品更新 Request VO") | ||||
| @Data | ||||
| @EqualsAndHashCode(callSuper = true) | ||||
| @ToString(callSuper = true) | ||||
| public class CombinationProductUpdateReqVO extends CombinationProductBaseVO { | ||||
|  | ||||
| } | ||||
| @ -9,7 +9,7 @@ import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.*; | ||||
| import cn.iocoder.yudao.module.promotion.convert.seckill.seckillactivity.SeckillActivityConvert; | ||||
| import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillActivityDO; | ||||
| import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillProductDO; | ||||
| import cn.iocoder.yudao.module.promotion.service.seckill.seckillactivity.SeckillActivityService; | ||||
| import cn.iocoder.yudao.module.promotion.service.seckill.SeckillActivityService; | ||||
| import io.swagger.v3.oas.annotations.Operation; | ||||
| import io.swagger.v3.oas.annotations.Parameter; | ||||
| import io.swagger.v3.oas.annotations.tags.Tag; | ||||
|  | ||||
| @ -6,7 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.*; | ||||
| import cn.iocoder.yudao.module.promotion.convert.seckill.seckillconfig.SeckillConfigConvert; | ||||
| import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillconfig.SeckillConfigDO; | ||||
| import cn.iocoder.yudao.module.promotion.service.seckill.seckillconfig.SeckillConfigService; | ||||
| import cn.iocoder.yudao.module.promotion.service.seckill.SeckillConfigService; | ||||
| import io.swagger.v3.oas.annotations.Operation; | ||||
| import io.swagger.v3.oas.annotations.Parameter; | ||||
| import io.swagger.v3.oas.annotations.tags.Tag; | ||||
|  | ||||
| @ -10,7 +10,6 @@ import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activit | ||||
| import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO; | ||||
| import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductBaseVO; | ||||
| import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductRespVO; | ||||
| import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductUpdateReqVO; | ||||
| import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO; | ||||
| import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO; | ||||
| import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO; | ||||
| @ -53,37 +52,34 @@ public interface CombinationActivityConvert { | ||||
|     default PageResult<CombinationActivityRespVO> convertPage(PageResult<CombinationActivityDO> page, | ||||
|                                                               List<CombinationProductDO> productList, | ||||
|                                                               List<ProductSpuRespDTO> spuList) { | ||||
|         Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId); | ||||
|         PageResult<CombinationActivityRespVO> pageResult = convertPage(page); | ||||
|         // 拼接商品 | ||||
|         Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId); | ||||
|         pageResult.getList().forEach(item -> { | ||||
|             MapUtils.findAndThen(spuMap, item.getSpuId(), spu -> { | ||||
|                 item.setSpuName(spu.getName()); | ||||
|                 item.setPicUrl(spu.getPicUrl()); | ||||
|             }); | ||||
|             item.setProducts(convertList2(productList)); | ||||
|             MapUtils.findAndThen(spuMap, item.getSpuId(), | ||||
|                     spu -> item.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl())); | ||||
|         }); | ||||
|         return pageResult; | ||||
|     } | ||||
|  | ||||
|     List<CombinationProductRespVO> convertList2(List<CombinationProductDO> productDOs); | ||||
|  | ||||
|     // TODO @puhui999:参数改成 activity、product 会不会干净一点哈 | ||||
|     @Mappings({ | ||||
|             @Mapping(target = "id", ignore = true), | ||||
|             @Mapping(target = "activityId", source = "activityDO.id"), | ||||
|             @Mapping(target = "spuId", source = "activityDO.spuId"), | ||||
|             @Mapping(target = "skuId", source = "vo.skuId"), | ||||
|             @Mapping(target = "activePrice", source = "vo.activePrice"), | ||||
|             @Mapping(target = "activityStartTime", source = "activityDO.startTime"), | ||||
|             @Mapping(target = "activityEndTime", source = "activityDO.endTime") | ||||
|     }) | ||||
|     CombinationProductDO convert(CombinationActivityDO activityDO, CombinationProductBaseVO vo); | ||||
|  | ||||
|     default List<CombinationProductDO> convertList(List<? extends CombinationProductBaseVO> products, CombinationActivityDO activityDO) { | ||||
|         return CollectionUtils.convertList(products, item -> convert(activityDO, item).setActivityStatus(activityDO.getStatus())); | ||||
|     } | ||||
|     @Mappings({ | ||||
|             @Mapping(target = "id", ignore = true), | ||||
|             @Mapping(target = "activityId", source = "activity.id"), | ||||
|             @Mapping(target = "spuId", source = "activity.spuId"), | ||||
|             @Mapping(target = "skuId", source = "product.skuId"), | ||||
|             @Mapping(target = "combinationPrice", source = "product.combinationPrice"), | ||||
|             @Mapping(target = "activityStartTime", source = "activity.startTime"), | ||||
|             @Mapping(target = "activityEndTime", source = "activity.endTime") | ||||
|     }) | ||||
|     CombinationProductDO convert(CombinationActivityDO activity, CombinationProductBaseVO product); | ||||
|  | ||||
|     default List<CombinationProductDO> convertList(List<CombinationProductUpdateReqVO> updateProductVOs, | ||||
|     default List<CombinationProductDO> convertList(List<CombinationProductBaseVO> updateProductVOs, | ||||
|                                                    List<CombinationProductDO> products, CombinationActivityDO activity) { | ||||
|         Map<Long, Long> productMap = convertMap(products, CombinationProductDO::getSkuId, CombinationProductDO::getId); | ||||
|         return CollectionUtils.convertList(updateProductVOs, updateProductVO -> convert(activity, updateProductVO) | ||||
|  | ||||
| @ -62,11 +62,11 @@ public class CombinationActivityDO extends BaseDO { | ||||
|     /** | ||||
|      * 开团组数 | ||||
|      */ | ||||
|     private Integer totalNum; | ||||
|     private Integer totalCount; | ||||
|     /** | ||||
|      * 成团组数 | ||||
|      */ | ||||
|     private Integer successNum; | ||||
|     private Integer successCount; | ||||
|     /** | ||||
|      * 参与人数 | ||||
|      */ | ||||
| @ -76,7 +76,7 @@ public class CombinationActivityDO extends BaseDO { | ||||
|      */ | ||||
|     private Integer virtualGroup; | ||||
|     /** | ||||
|      * 活动状态:0开启 1关闭 | ||||
|      * 活动状态 | ||||
|      * | ||||
|      * 枚举 {@link CommonStatusEnum} | ||||
|      */ | ||||
|  | ||||
| @ -40,6 +40,11 @@ public class CombinationProductDO extends BaseDO { | ||||
|      * 商品 SKU 编号 | ||||
|      */ | ||||
|     private Long skuId; | ||||
|     /** | ||||
|      * 拼团价格,单位分 | ||||
|      */ | ||||
|     private Integer combinationPrice; | ||||
|  | ||||
|     /** | ||||
|      * 拼团商品状态 | ||||
|      * | ||||
| @ -48,15 +53,15 @@ public class CombinationProductDO extends BaseDO { | ||||
|     private Integer activityStatus; | ||||
|     /** | ||||
|      * 活动开始时间点 | ||||
|      * | ||||
|      * 冗余 {@link CombinationActivityDO#getStartTime()} | ||||
|      */ | ||||
|     private LocalDateTime activityStartTime; | ||||
|     /** | ||||
|      * 活动结束时间点 | ||||
|      * | ||||
|      * 冗余 {@link CombinationActivityDO#getEndTime()} | ||||
|      */ | ||||
|     private LocalDateTime activityEndTime; | ||||
|     /** | ||||
|      * 拼团价格,单位分 | ||||
|      */ | ||||
|     private Integer activePrice; | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -20,6 +20,9 @@ public interface CombinationActivityMapper extends BaseMapperX<CombinationActivi | ||||
|     default PageResult<CombinationActivityDO> selectPage(CombinationActivityPageReqVO reqVO) { | ||||
|         return selectPage(reqVO, new LambdaQueryWrapperX<CombinationActivityDO>() | ||||
|                 .likeIfPresent(CombinationActivityDO::getName, reqVO.getName()) | ||||
|                 .betweenIfPresent(CombinationActivityDO::getStartTime, reqVO.getStartTime()) | ||||
|                 .betweenIfPresent(CombinationActivityDO::getEndTime, reqVO.getEndTime()) | ||||
|                 .eqIfPresent(CombinationActivityDO::getStatus, reqVO.getStatus()) | ||||
|                 .orderByDesc(CombinationActivityDO::getId)); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -26,7 +26,7 @@ public interface CombinationProductMapper extends BaseMapperX<CombinationProduct | ||||
|                 .eqIfPresent(CombinationProductDO::getActivityStatus, reqVO.getActivityStatus()) | ||||
|                 .betweenIfPresent(CombinationProductDO::getActivityStartTime, reqVO.getActivityStartTime()) | ||||
|                 .betweenIfPresent(CombinationProductDO::getActivityEndTime, reqVO.getActivityEndTime()) | ||||
|                 .eqIfPresent(CombinationProductDO::getActivePrice, reqVO.getActivePrice()) | ||||
|                 .eqIfPresent(CombinationProductDO::getCombinationPrice, reqVO.getActivePrice()) | ||||
|                 .betweenIfPresent(CombinationProductDO::getCreateTime, reqVO.getCreateTime()) | ||||
|                 .orderByDesc(CombinationProductDO::getId)); | ||||
|     } | ||||
|  | ||||
| @ -40,6 +40,14 @@ public interface CombinationActivityService { | ||||
|      */ | ||||
|     void deleteCombinationActivity(Long id); | ||||
|  | ||||
|     /** | ||||
|      * 校验拼团活动是否存在 | ||||
|      * | ||||
|      * @param id 编号 | ||||
|      * @return 拼团活动 | ||||
|      */ | ||||
|     CombinationActivityDO validateCombinationActivityExists(Long id); | ||||
|  | ||||
|     /** | ||||
|      * 获得拼团活动 | ||||
|      * | ||||
| @ -48,14 +56,6 @@ public interface CombinationActivityService { | ||||
|      */ | ||||
|     CombinationActivityDO getCombinationActivity(Long id); | ||||
|  | ||||
|     /** | ||||
|      * 获得拼团活动列表 | ||||
|      * | ||||
|      * @param ids 编号 | ||||
|      * @return 拼团活动列表 | ||||
|      */ | ||||
|     List<CombinationActivityDO> getCombinationActivityList(Collection<Long> ids); | ||||
|  | ||||
|     /** | ||||
|      * 获得拼团活动分页 | ||||
|      * | ||||
| @ -67,9 +67,9 @@ public interface CombinationActivityService { | ||||
|     /** | ||||
|      * 获得拼团活动商品列表 | ||||
|      * | ||||
|      * @param ids 拼团活动 ids | ||||
|      * @param activityIds 拼团活动 ids | ||||
|      * @return 拼团活动的商品列表 | ||||
|      */ | ||||
|     List<CombinationProductDO> getCombinationProductsByActivityIds(Collection<Long> ids); | ||||
|     List<CombinationProductDO> getCombinationProductsByActivityIds(Collection<Long> activityIds); | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,208 @@ | ||||
| package cn.iocoder.yudao.module.promotion.service.combination; | ||||
|  | ||||
| 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; | ||||
| import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; | ||||
| import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO; | ||||
| import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO; | ||||
| import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO; | ||||
| import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductBaseVO; | ||||
| import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert; | ||||
| import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO; | ||||
| import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO; | ||||
| import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationActivityMapper; | ||||
| import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationProductMapper; | ||||
| import org.springframework.stereotype.Service; | ||||
| import org.springframework.transaction.annotation.Transactional; | ||||
| import org.springframework.validation.annotation.Validated; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import java.util.Collection; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
|  | ||||
| import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||||
| import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; | ||||
| import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; | ||||
| import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS; | ||||
| import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS; | ||||
| import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; | ||||
| import static java.util.Collections.singletonList; | ||||
|  | ||||
| /** | ||||
|  * 拼团活动 Service 实现类 | ||||
|  * | ||||
|  * @author HUIHUI | ||||
|  */ | ||||
| @Service | ||||
| @Validated | ||||
| public class CombinationActivityServiceImpl implements CombinationActivityService { | ||||
|  | ||||
|     @Resource | ||||
|     private CombinationActivityMapper combinationActivityMapper; | ||||
|     @Resource | ||||
|     private CombinationProductMapper combinationProductMapper; | ||||
|  | ||||
|     @Resource | ||||
|     private ProductSpuApi productSpuApi; | ||||
|     @Resource | ||||
|     private ProductSkuApi productSkuApi; | ||||
|  | ||||
|     @Override | ||||
|     @Transactional(rollbackFor = Exception.class) | ||||
|     public Long createCombinationActivity(CombinationActivityCreateReqVO createReqVO) { | ||||
|         // 校验商品 SPU 是否存在是否参加的别的活动 | ||||
|         validateProductConflict(createReqVO.getSpuId(), null); | ||||
|         // 校验商品是否存在 | ||||
|         validateProductExists(createReqVO.getSpuId(), createReqVO.getProducts()); | ||||
|  | ||||
|         // 插入拼团活动 | ||||
|         CombinationActivityDO activity = CombinationActivityConvert.INSTANCE.convert(createReqVO) | ||||
|                 .setStatus(CommonStatusEnum.ENABLE.getStatus()) | ||||
|                 .setTotalCount(0).setSuccessCount(0).setOrderUserCount(0).setVirtualGroup(0); | ||||
|         combinationActivityMapper.insert(activity); | ||||
|         // 插入商品 | ||||
|         List<CombinationProductDO> products = CombinationActivityConvert.INSTANCE.convertList(createReqVO.getProducts(), activity); | ||||
|         combinationProductMapper.insertBatch(products); | ||||
|         // 返回 | ||||
|         return activity.getId(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 校验拼团商品参与的活动是否存在冲突 | ||||
|      * | ||||
|      * @param spuId 商品 SPU 编号 | ||||
|      * @param activityId 拼团活动编号 | ||||
|      */ | ||||
|     private void validateProductConflict(Long spuId, Long activityId) { | ||||
|         // 查询所有开启的拼团活动 | ||||
|         List<CombinationActivityDO> activityList = combinationActivityMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         if (activityId != null) { // 时排除自己 | ||||
|             activityList.removeIf(item -> ObjectUtil.equal(item.getId(), activityId)); | ||||
|         } | ||||
|         // 查找是否有其它活动,选择了该产品 | ||||
|         List<CombinationActivityDO> matchActivityList = filterList(activityList, activity -> ObjectUtil.equal(activity.getId(), spuId)); | ||||
|         if (CollUtil.isNotEmpty(matchActivityList)) { | ||||
|             throw exception(COMBINATION_ACTIVITY_SPU_CONFLICTS); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 校验拼团商品是否都存在 | ||||
|      * | ||||
|      * @param spuId 商品 SPU 编号 | ||||
|      * @param products 秒杀商品 | ||||
|      */ | ||||
|     private void validateProductExists(Long spuId, List<CombinationProductBaseVO> products) { | ||||
|         // 1. 校验商品 spu 是否存在 | ||||
|         ProductSpuRespDTO spu = productSpuApi.getSpu(spuId); | ||||
|         if (spu == null) { | ||||
|             throw exception(SPU_NOT_EXISTS); | ||||
|         } | ||||
|  | ||||
|         // 2. 校验商品 sku 都存在 | ||||
|         Map<Long, ProductSkuRespDTO> skuMap = convertMap(productSkuApi.getSkuListBySpuId(singletonList(spuId)), | ||||
|                 ProductSkuRespDTO::getId); | ||||
|         products.forEach(product -> { | ||||
|             if (!skuMap.containsKey(product.getSkuId())) { | ||||
|                 throw exception(SKU_NOT_EXISTS); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     @Transactional(rollbackFor = Exception.class) | ||||
|     public void updateCombinationActivity(CombinationActivityUpdateReqVO updateReqVO) { | ||||
|         // 校验存在 | ||||
|         CombinationActivityDO activityDO = validateCombinationActivityExists(updateReqVO.getId()); | ||||
|         // 校验状态 | ||||
|         if (ObjectUtil.equal(activityDO.getStatus(), CommonStatusEnum.DISABLE.getStatus())) { | ||||
|             throw exception(COMBINATION_ACTIVITY_STATUS_DISABLE); | ||||
|         } | ||||
|         // 校验商品冲突 | ||||
|         validateProductConflict(updateReqVO.getSpuId(), updateReqVO.getId()); | ||||
|         // 校验商品是否存在 | ||||
|         validateProductExists(updateReqVO.getSpuId(), updateReqVO.getProducts()); | ||||
|  | ||||
|         // 更新活动 | ||||
|         CombinationActivityDO updateObj = CombinationActivityConvert.INSTANCE.convert(updateReqVO); | ||||
|         combinationActivityMapper.updateById(updateObj); | ||||
|         // 更新商品 | ||||
|         updateCombinationProduct(updateObj, updateReqVO.getProducts()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 更新拼团商品 | ||||
|      * | ||||
|      * @param activity 拼团活动 | ||||
|      * @param products  该活动的最新商品配置 | ||||
|      */ | ||||
|     private void updateCombinationProduct(CombinationActivityDO activity, List<CombinationProductBaseVO> products) { | ||||
|         // 第一步,对比新老数据,获得添加、修改、删除的列表 | ||||
|         List<CombinationProductDO> newList = CombinationActivityConvert.INSTANCE.convertList(products, activity); | ||||
|         List<CombinationProductDO> oldList = combinationProductMapper.selectListByActivityIds(CollUtil.newArrayList(activity.getId())); | ||||
|         List<List<CombinationProductDO>> diffList = CollectionUtils.diffList(oldList, newList, (oldVal, newVal) -> { | ||||
|             boolean same = ObjectUtil.equal(oldVal.getSkuId(), newVal.getSkuId()); | ||||
|             if (same) { | ||||
|                 newVal.setId(oldVal.getId()); | ||||
|             } | ||||
|             return same; | ||||
|         }); | ||||
|  | ||||
|         // 第二步,批量添加、修改、删除 | ||||
|         if (CollUtil.isNotEmpty(diffList.get(0))) { | ||||
|             combinationProductMapper.insertBatch(diffList.get(0)); | ||||
|         } | ||||
|         if (CollUtil.isNotEmpty(diffList.get(1))) { | ||||
|             combinationProductMapper.updateBatch(diffList.get(1)); | ||||
|         } | ||||
|         if (CollUtil.isNotEmpty(diffList.get(2))) { | ||||
|             combinationProductMapper.deleteBatchIds(CollectionUtils.convertList(diffList.get(2), CombinationProductDO::getId)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     @Transactional(rollbackFor = Exception.class) | ||||
|     public void deleteCombinationActivity(Long id) { | ||||
|         // 校验存在 | ||||
|         CombinationActivityDO activityDO = validateCombinationActivityExists(id); | ||||
|         // 校验状态 | ||||
|         if (ObjectUtil.equal(activityDO.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { | ||||
|             throw exception(COMBINATION_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END); | ||||
|         } | ||||
|  | ||||
|         // 删除 | ||||
|         combinationActivityMapper.deleteById(id); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public CombinationActivityDO validateCombinationActivityExists(Long id) { | ||||
|         CombinationActivityDO activityDO = combinationActivityMapper.selectById(id); | ||||
|         if (activityDO == null) { | ||||
|             throw exception(COMBINATION_ACTIVITY_NOT_EXISTS); | ||||
|         } | ||||
|         return activityDO; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public CombinationActivityDO getCombinationActivity(Long id) { | ||||
|         return validateCombinationActivityExists(id); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public PageResult<CombinationActivityDO> getCombinationActivityPage(CombinationActivityPageReqVO pageReqVO) { | ||||
|         return combinationActivityMapper.selectPage(pageReqVO); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public List<CombinationProductDO> getCombinationProductsByActivityIds(Collection<Long> activityIds) { | ||||
|         return combinationProductMapper.selectListByActivityIds(activityIds); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -5,7 +5,7 @@ import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordUp | ||||
| import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO; | ||||
|  | ||||
| /** | ||||
|  * 商品活动记录 service | ||||
|  * 拼团记录 Service 接口 | ||||
|  * | ||||
|  * @author HUIHUI | ||||
|  */ | ||||
|  | ||||
| @ -0,0 +1,127 @@ | ||||
| package cn.iocoder.yudao.module.promotion.service.combination; | ||||
|  | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
| import cn.hutool.core.util.ObjectUtil; | ||||
| import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO; | ||||
| import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordUpdateStatusReqDTO; | ||||
| import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert; | ||||
| import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO; | ||||
| import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO; | ||||
| import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationRecordMapper; | ||||
| import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum; | ||||
| import org.springframework.stereotype.Service; | ||||
| import org.springframework.transaction.annotation.Transactional; | ||||
| import org.springframework.validation.annotation.Validated; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import java.util.List; | ||||
|  | ||||
| import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||||
| import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; | ||||
| import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COMBINATION_RECORD_USER_FULL; | ||||
|  | ||||
| // TODO 芋艿:等拼团记录做完,完整 review 下 | ||||
| /** | ||||
|  * 拼团记录 Service 实现类 | ||||
|  * | ||||
|  * @author HUIHUI | ||||
|  */ | ||||
| @Service | ||||
| @Validated | ||||
| public class CombinationRecordServiceImpl implements CombinationRecordService { | ||||
|  | ||||
|     @Resource | ||||
|     private CombinationActivityService combinationActivityService; | ||||
|  | ||||
|     @Resource | ||||
|     private CombinationRecordMapper recordMapper; | ||||
|  | ||||
|     @Override | ||||
|     public void updateCombinationRecordStatusByUserIdAndOrderId(CombinationRecordUpdateStatusReqDTO reqDTO) { | ||||
|         // 校验拼团是否存在 | ||||
|         CombinationRecordDO recordDO = validateCombinationRecord(reqDTO.getUserId(), reqDTO.getOrderId()); | ||||
|  | ||||
|         // 更新状态 | ||||
|         recordDO.setStatus(reqDTO.getStatus()); | ||||
|         recordMapper.updateById(recordDO); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     @Transactional(rollbackFor = Exception.class) | ||||
|     public void updateCombinationRecordStatusAndStartTimeByUserIdAndOrderId(CombinationRecordUpdateStatusReqDTO reqDTO) { | ||||
|         CombinationRecordDO recordDO = validateCombinationRecord(reqDTO.getUserId(), reqDTO.getOrderId()); | ||||
|         // 更新状态 | ||||
|         recordDO.setStatus(reqDTO.getStatus()); | ||||
|         // 更新开始时间 | ||||
|         recordDO.setStartTime(reqDTO.getStartTime()); | ||||
|         recordMapper.updateById(recordDO); | ||||
|  | ||||
|         // 更新拼团参入人数 | ||||
|         List<CombinationRecordDO> recordDOs = recordMapper.selectListByHeadIdAndStatus(recordDO.getHeadId(), reqDTO.getStatus()); | ||||
|         if (CollUtil.isNotEmpty(recordDOs)) { | ||||
|             recordDOs.forEach(item -> { | ||||
|                 item.setUserCount(recordDOs.size()); | ||||
|                 // 校验拼团是否满足要求 | ||||
|                 if (ObjectUtil.equal(recordDOs.size(), recordDO.getUserSize())) { | ||||
|                     item.setStatus(CombinationRecordStatusEnum.SUCCESS.getStatus()); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|         recordMapper.updateBatch(recordDOs); | ||||
|     } | ||||
|  | ||||
|     private CombinationRecordDO validateCombinationRecord(Long userId, Long orderId) { | ||||
|         // 校验拼团是否存在 | ||||
|         CombinationRecordDO recordDO = recordMapper.selectByUserIdAndOrderId(userId, orderId); | ||||
|         if (recordDO == null) { | ||||
|             throw exception(COMBINATION_RECORD_NOT_EXISTS); | ||||
|         } | ||||
|         return recordDO; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) { | ||||
|         // 1.1 校验拼团活动 | ||||
|         CombinationActivityDO activity = combinationActivityService.validateCombinationActivityExists(reqDTO.getActivityId()); | ||||
|         // 1.2 需要校验下,他当前是不是已经参加了该拼团; | ||||
|         CombinationRecordDO recordDO = recordMapper.selectByUserIdAndOrderId(reqDTO.getUserId(), reqDTO.getOrderId()); | ||||
|         if (recordDO != null) { | ||||
|             throw exception(COMBINATION_RECORD_EXISTS); | ||||
|         } | ||||
|         // 1.3 父拼团是否存在,是否已经满了 | ||||
|         if (reqDTO.getHeadId() != null) { | ||||
|             CombinationRecordDO recordDO1 = recordMapper.selectRecordByHeadId(reqDTO.getHeadId(), reqDTO.getActivityId(), CombinationRecordStatusEnum.IN_PROGRESS.getStatus()); | ||||
|             if (recordDO1 == null) { | ||||
|                 throw exception(COMBINATION_RECORD_HEAD_NOT_EXISTS); | ||||
|             } | ||||
|             // 校验拼团是否满足要求 | ||||
|             if (ObjectUtil.equal(recordDO1.getUserCount(), recordDO1.getUserSize())) { | ||||
|                 throw exception(COMBINATION_RECORD_USER_FULL); | ||||
|             } | ||||
|         } | ||||
|         // TODO @puhui999:应该还有一些校验,后续补噶;例如说,一个团,自己已经参与进去了,不能再参与进去; | ||||
|  | ||||
|         // 2. 创建拼团记录 | ||||
|         CombinationRecordDO record = CombinationActivityConvert.INSTANCE.convert(reqDTO); | ||||
|         record.setVirtualGroup(false); | ||||
|         // TODO @puhui999:过期时间,应该是 Date 哈; | ||||
|         record.setExpireTime(activity.getLimitDuration()); | ||||
|         record.setUserSize(activity.getUserSize()); | ||||
|         recordMapper.insert(record); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public CombinationRecordDO getCombinationRecord(Long userId, Long orderId) { | ||||
|         return validateCombinationRecord(userId, orderId); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * APP 端获取开团记录 | ||||
|      * | ||||
|      * @return 开团记录 | ||||
|      */ | ||||
|     public List<CombinationRecordDO> getRecordListByStatus(Integer status) { | ||||
|         return recordMapper.selectListByStatus(status); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -1,291 +0,0 @@ | ||||
| package cn.iocoder.yudao.module.promotion.service.combination; | ||||
|  | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
| import cn.hutool.core.collection.CollectionUtil; | ||||
| 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; | ||||
| import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; | ||||
| import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO; | ||||
| import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordUpdateStatusReqDTO; | ||||
| import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO; | ||||
| import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO; | ||||
| import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO; | ||||
| import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductCreateReqVO; | ||||
| import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductUpdateReqVO; | ||||
| import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert; | ||||
| import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO; | ||||
| import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO; | ||||
| import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO; | ||||
| import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationActivityMapper; | ||||
| import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationProductMapper; | ||||
| import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationRecordMapper; | ||||
| import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum; | ||||
| import org.springframework.stereotype.Service; | ||||
| import org.springframework.transaction.annotation.Transactional; | ||||
| import org.springframework.validation.annotation.Validated; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import java.util.Collection; | ||||
| import java.util.List; | ||||
|  | ||||
| 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.validateProductSkuAllExists; | ||||
|  | ||||
| /** | ||||
|  * 拼团活动 Service 实现类 | ||||
|  * | ||||
|  * @author HUIHUI | ||||
|  */ | ||||
| @Service | ||||
| @Validated | ||||
| public class CombinationServiceImpl implements CombinationActivityService, CombinationRecordService { | ||||
|  | ||||
|     @Resource | ||||
|     private CombinationActivityMapper combinationActivityMapper; | ||||
|     @Resource | ||||
|     private CombinationRecordMapper recordMapper; | ||||
|     @Resource | ||||
|     private CombinationProductMapper combinationProductMapper; | ||||
|     @Resource | ||||
|     private ProductSpuApi productSpuApi; | ||||
|     @Resource | ||||
|     private ProductSkuApi productSkuApi; | ||||
|  | ||||
|     @Override | ||||
|     @Transactional(rollbackFor = Exception.class) | ||||
|     public Long createCombinationActivity(CombinationActivityCreateReqVO createReqVO) { | ||||
|         // 校验商品 SPU 是否存在是否参加的别的活动 | ||||
|         validateProductCombinationConflict(createReqVO.getSpuId(), null); | ||||
|         // 获取所选 spu下的所有 sku | ||||
|         List<ProductSkuRespDTO> skus = productSkuApi.getSkuListBySpuId(CollectionUtil.newArrayList(createReqVO.getSpuId())); | ||||
|         // 校验商品 sku 是否存在 | ||||
|         validateProductSkuAllExists(skus, createReqVO.getProducts(), CombinationProductCreateReqVO::getSkuId); | ||||
|  | ||||
|         // TODO 艿艿 有个小问题:现在有活动时间和限制时长,活动时间的结束时间早于设置的限制时间怎么算状态比如: | ||||
|         //  活动时间 2023-08-05 15:00:00 - 2023-08-05 15:20:00 限制时长 2小时,那么活动时间结束就结束还是加时到满两小时 | ||||
|         // 插入拼团活动 | ||||
|         CombinationActivityDO activityDO = CombinationActivityConvert.INSTANCE.convert(createReqVO); | ||||
|         // TODO 营销相关属性初始化 拼团成功更新相关属性 | ||||
|         activityDO.setTotalNum(0); | ||||
|         activityDO.setSuccessNum(0); | ||||
|         activityDO.setOrderUserCount(0); | ||||
|         activityDO.setVirtualGroup(0); | ||||
|         activityDO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         combinationActivityMapper.insert(activityDO); | ||||
|         // 插入商品 | ||||
|         List<CombinationProductDO> productDOs = CombinationActivityConvert.INSTANCE.convertList(createReqVO.getProducts(), activityDO); | ||||
|         combinationProductMapper.insertBatch(productDOs); | ||||
|         // 返回 | ||||
|         return activityDO.getId(); | ||||
|     } | ||||
|  | ||||
|     private void validateProductCombinationConflict(Long spuId, Long activityId) { | ||||
|         // 校验商品 spu 是否存在 | ||||
|         List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(CollUtil.newArrayList(spuId)); | ||||
|         if (CollUtil.isEmpty(spuList)) { | ||||
|             throw exception(SPU_NOT_EXISTS); | ||||
|         } | ||||
|         // 查询所有开启的拼团活动 | ||||
|         List<CombinationActivityDO> activityDOs = combinationActivityMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         // 更新时排除自己 | ||||
|         if (activityId != null) { | ||||
|             activityDOs.removeIf(item -> ObjectUtil.equal(item.getId(), activityId)); | ||||
|         } | ||||
|         // 过滤出所有 spuIds 有交集的活动 | ||||
|         List<CombinationActivityDO> doList = CollectionUtils.convertList(activityDOs, c -> c, s -> ObjectUtil.equal(s.getId(), spuId)); | ||||
|         if (CollUtil.isNotEmpty(doList)) { | ||||
|             throw exception(COMBINATION_ACTIVITY_SPU_CONFLICTS); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     @Transactional(rollbackFor = Exception.class) | ||||
|     public void updateCombinationActivity(CombinationActivityUpdateReqVO updateReqVO) { | ||||
|         // 校验存在 | ||||
|         CombinationActivityDO activityDO = validateCombinationActivityExists(updateReqVO.getId()); | ||||
|         // 校验状态 | ||||
|         if (ObjectUtil.equal(activityDO.getStatus(), CommonStatusEnum.DISABLE.getStatus())) { | ||||
|             throw exception(COMBINATION_ACTIVITY_STATUS_DISABLE); | ||||
|         } | ||||
|         // 校验商品冲突 | ||||
|         validateProductCombinationConflict(updateReqVO.getSpuId(), updateReqVO.getId()); | ||||
|         // 获取所选 spu下的所有 sku | ||||
|         List<ProductSkuRespDTO> skus = productSkuApi.getSkuListBySpuId(CollectionUtil.newArrayList(updateReqVO.getSpuId())); | ||||
|         // 校验商品 sku 是否存在 | ||||
|         validateProductSkuAllExists(skus, updateReqVO.getProducts(), CombinationProductUpdateReqVO::getSkuId); | ||||
|  | ||||
|         // 更新 | ||||
|         CombinationActivityDO updateObj = CombinationActivityConvert.INSTANCE.convert(updateReqVO); | ||||
|         combinationActivityMapper.updateById(updateObj); | ||||
|         // 更新商品 | ||||
|         updateCombinationProduct(updateObj, updateReqVO.getProducts()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 更新拼团商品 | ||||
|      * | ||||
|      * @param activity 拼团活动 | ||||
|      * @param products  该活动的最新商品配置 | ||||
|      */ | ||||
|     private void updateCombinationProduct(CombinationActivityDO activity, List<CombinationProductUpdateReqVO> products) { | ||||
|         // 第一步,对比新老数据,获得添加、修改、删除的列表 | ||||
|         List<CombinationProductDO> newList = CombinationActivityConvert.INSTANCE.convertList(products, activity); | ||||
|         List<CombinationProductDO> oldList = combinationProductMapper.selectListByActivityIds(CollUtil.newArrayList(activity.getId())); | ||||
|         List<List<CombinationProductDO>> diffList = CollectionUtils.diffList(oldList, newList, (oldVal, newVal) -> { | ||||
|             boolean same = ObjectUtil.equal(oldVal.getSkuId(), newVal.getSkuId()); | ||||
|             if (same) { | ||||
|                 newVal.setId(oldVal.getId()); | ||||
|             } | ||||
|             return same; | ||||
|         }); | ||||
|  | ||||
|         // 第二步,批量添加、修改、删除 | ||||
|         if (CollUtil.isNotEmpty(diffList.get(0))) { | ||||
|             combinationProductMapper.insertBatch(diffList.get(0)); | ||||
|         } | ||||
|         if (CollUtil.isNotEmpty(diffList.get(1))) { | ||||
|             combinationProductMapper.updateBatch(diffList.get(1)); | ||||
|         } | ||||
|         if (CollUtil.isNotEmpty(diffList.get(2))) { | ||||
|             combinationProductMapper.deleteBatchIds(CollectionUtils.convertList(diffList.get(2), CombinationProductDO::getId)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     @Transactional(rollbackFor = Exception.class) | ||||
|     public void deleteCombinationActivity(Long id) { | ||||
|         // 校验存在 | ||||
|         CombinationActivityDO activityDO = validateCombinationActivityExists(id); | ||||
|         // 校验状态 | ||||
|         if (ObjectUtil.equal(activityDO.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { | ||||
|             throw exception(COMBINATION_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END); | ||||
|         } | ||||
|  | ||||
|         // 删除 | ||||
|         combinationActivityMapper.deleteById(id); | ||||
|     } | ||||
|  | ||||
|     private CombinationActivityDO validateCombinationActivityExists(Long id) { | ||||
|         CombinationActivityDO activityDO = combinationActivityMapper.selectById(id); | ||||
|         if (activityDO == null) { | ||||
|             throw exception(COMBINATION_ACTIVITY_NOT_EXISTS); | ||||
|         } | ||||
|         return activityDO; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public CombinationActivityDO getCombinationActivity(Long id) { | ||||
|         return validateCombinationActivityExists(id); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public List<CombinationActivityDO> getCombinationActivityList(Collection<Long> ids) { | ||||
|         return combinationActivityMapper.selectBatchIds(ids); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public PageResult<CombinationActivityDO> getCombinationActivityPage(CombinationActivityPageReqVO pageReqVO) { | ||||
|         return combinationActivityMapper.selectPage(pageReqVO); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public List<CombinationProductDO> getCombinationProductsByActivityIds(Collection<Long> ids) { | ||||
|         return combinationProductMapper.selectListByActivityIds(ids); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void updateCombinationRecordStatusByUserIdAndOrderId(CombinationRecordUpdateStatusReqDTO reqDTO) { | ||||
|         // 校验拼团是否存在 | ||||
|         CombinationRecordDO recordDO = validateCombinationRecord(reqDTO.getUserId(), reqDTO.getOrderId()); | ||||
|  | ||||
|         // 更新状态 | ||||
|         recordDO.setStatus(reqDTO.getStatus()); | ||||
|         recordMapper.updateById(recordDO); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     @Transactional(rollbackFor = Exception.class) | ||||
|     public void updateCombinationRecordStatusAndStartTimeByUserIdAndOrderId(CombinationRecordUpdateStatusReqDTO reqDTO) { | ||||
|         CombinationRecordDO recordDO = validateCombinationRecord(reqDTO.getUserId(), reqDTO.getOrderId()); | ||||
|         // 更新状态 | ||||
|         recordDO.setStatus(reqDTO.getStatus()); | ||||
|         // 更新开始时间 | ||||
|         recordDO.setStartTime(reqDTO.getStartTime()); | ||||
|         recordMapper.updateById(recordDO); | ||||
|  | ||||
|         // 更新拼团参入人数 | ||||
|         List<CombinationRecordDO> recordDOs = recordMapper.selectListByHeadIdAndStatus(recordDO.getHeadId(), reqDTO.getStatus()); | ||||
|         if (CollUtil.isNotEmpty(recordDOs)) { | ||||
|             recordDOs.forEach(item -> { | ||||
|                 item.setUserCount(recordDOs.size()); | ||||
|                 // 校验拼团是否满足要求 | ||||
|                 if (ObjectUtil.equal(recordDOs.size(), recordDO.getUserSize())) { | ||||
|                     item.setStatus(CombinationRecordStatusEnum.SUCCESS.getStatus()); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|         recordMapper.updateBatch(recordDOs); | ||||
|     } | ||||
|  | ||||
|     private CombinationRecordDO validateCombinationRecord(Long userId, Long orderId) { | ||||
|         // 校验拼团是否存在 | ||||
|         CombinationRecordDO recordDO = recordMapper.selectByUserIdAndOrderId(userId, orderId); | ||||
|         if (recordDO == null) { | ||||
|             throw exception(COMBINATION_RECORD_NOT_EXISTS); | ||||
|         } | ||||
|         return recordDO; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) { | ||||
|         // 1.1 校验拼团活动 | ||||
|         CombinationActivityDO activity = validateCombinationActivityExists(reqDTO.getActivityId()); | ||||
|         // 1.2 需要校验下,他当前是不是已经参加了该拼团; | ||||
|         CombinationRecordDO recordDO = recordMapper.selectByUserIdAndOrderId(reqDTO.getUserId(), reqDTO.getOrderId()); | ||||
|         if (recordDO != null) { | ||||
|             throw exception(COMBINATION_RECORD_EXISTS); | ||||
|         } | ||||
|         // 1.3 父拼团是否存在,是否已经满了 | ||||
|         if (reqDTO.getHeadId() != null) { | ||||
|             CombinationRecordDO recordDO1 = recordMapper.selectRecordByHeadId(reqDTO.getHeadId(), reqDTO.getActivityId(), CombinationRecordStatusEnum.IN_PROGRESS.getStatus()); | ||||
|             if (recordDO1 == null) { | ||||
|                 throw exception(COMBINATION_RECORD_HEAD_NOT_EXISTS); | ||||
|             } | ||||
|             // 校验拼团是否满足要求 | ||||
|             if (ObjectUtil.equal(recordDO1.getUserCount(), recordDO1.getUserSize())) { | ||||
|                 throw exception(COMBINATION_RECORD_USER_FULL); | ||||
|             } | ||||
|         } | ||||
|         // TODO @puhui999:应该还有一些校验,后续补噶;例如说,一个团,自己已经参与进去了,不能再参与进去; | ||||
|  | ||||
|         // 2. 创建拼团记录 | ||||
|         CombinationRecordDO record = CombinationActivityConvert.INSTANCE.convert(reqDTO); | ||||
|         record.setVirtualGroup(false); | ||||
|         // TODO @puhui999:过期时间,应该是 Date 哈; | ||||
|         record.setExpireTime(activity.getLimitDuration()); | ||||
|         record.setUserSize(activity.getUserSize()); | ||||
|         recordMapper.insert(record); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public CombinationRecordDO getCombinationRecord(Long userId, Long orderId) { | ||||
|         return validateCombinationRecord(userId, orderId); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * APP 端获取开团记录 | ||||
|      * | ||||
|      * @return 开团记录 | ||||
|      */ | ||||
|     public List<CombinationRecordDO> getRecordListByStatus(Integer status) { | ||||
|         return recordMapper.selectListByStatus(status); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -1,4 +1,4 @@ | ||||
| package cn.iocoder.yudao.module.promotion.service.seckill.seckillactivity; | ||||
| package cn.iocoder.yudao.module.promotion.service.seckill; | ||||
| 
 | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityCreateReqVO; | ||||
| @ -1,4 +1,4 @@ | ||||
| package cn.iocoder.yudao.module.promotion.service.seckill.seckillactivity; | ||||
| package cn.iocoder.yudao.module.promotion.service.seckill; | ||||
| 
 | ||||
| import cn.hutool.core.util.ObjectUtil; | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| @ -16,7 +16,6 @@ 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.service.seckill.seckillconfig.SeckillConfigService; | ||||
| import cn.iocoder.yudao.module.promotion.util.PromotionUtils; | ||||
| import org.springframework.stereotype.Service; | ||||
| import org.springframework.transaction.annotation.Transactional; | ||||
| @ -78,8 +77,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService { | ||||
|      * 校验秒杀商品参与的活动是否存在冲突 | ||||
|      * | ||||
|      * 1. 校验秒杀时段是否存在 | ||||
|      * 2. 校验商品 spu 是否存在 | ||||
|      * 3. 秒杀商品是否参加其它活动 | ||||
|      * 2. 秒杀商品是否参加其它活动 | ||||
|      * | ||||
|      * @param configIds 秒杀时段数组 | ||||
|      * @param spuId 商品 SPU 编号 | ||||
| @ -1,4 +1,4 @@ | ||||
| package cn.iocoder.yudao.module.promotion.service.seckill.seckillconfig; | ||||
| package cn.iocoder.yudao.module.promotion.service.seckill; | ||||
| 
 | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigCreateReqVO; | ||||
| @ -1,4 +1,4 @@ | ||||
| package cn.iocoder.yudao.module.promotion.service.seckill.seckillconfig; | ||||
| package cn.iocoder.yudao.module.promotion.service.seckill; | ||||
| 
 | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
| import cn.hutool.core.util.ObjectUtil; | ||||
| @ -24,15 +24,15 @@ import static org.junit.jupiter.api.Assertions.*; | ||||
|  | ||||
| // TODO 芋艿:等完成后,在补全单测 | ||||
| /** | ||||
|  * {@link CombinationServiceImpl} 的单元测试类 | ||||
|  * {@link CombinationActivityServiceImpl} 的单元测试类 | ||||
|  * | ||||
|  * @author HUIHUI | ||||
|  */ | ||||
| @Import(CombinationServiceImpl.class) | ||||
| @Import(CombinationActivityServiceImpl.class) | ||||
| public class CombinationActivityServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private CombinationServiceImpl combinationActivityService; | ||||
|     private CombinationActivityServiceImpl combinationActivityService; | ||||
|  | ||||
|     @Resource | ||||
|     private CombinationActivityMapper combinationActivityMapper; | ||||
| @ -112,8 +112,8 @@ public class CombinationActivityServiceImplTest extends BaseDbUnitTest { | ||||
|             o.setStartTime(null); | ||||
|             o.setEndTime(null); | ||||
|             o.setUserSize(null); | ||||
|             o.setTotalNum(null); | ||||
|             o.setSuccessNum(null); | ||||
|             o.setTotalCount(null); | ||||
|             o.setSuccessCount(null); | ||||
|             o.setOrderUserCount(null); | ||||
|             o.setVirtualGroup(null); | ||||
|             o.setStatus(null); | ||||
| @ -136,9 +136,9 @@ public class CombinationActivityServiceImplTest extends BaseDbUnitTest { | ||||
|         // 测试 userSize 不匹配 | ||||
|         combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setUserSize(null))); | ||||
|         // 测试 totalNum 不匹配 | ||||
|         combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setTotalNum(null))); | ||||
|         combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setTotalCount(null))); | ||||
|         // 测试 successNum 不匹配 | ||||
|         combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setSuccessNum(null))); | ||||
|         combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setSuccessCount(null))); | ||||
|         // 测试 orderUserCount 不匹配 | ||||
|         combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setOrderUserCount(null))); | ||||
|         // 测试 virtualGroup 不匹配 | ||||
| @ -186,8 +186,8 @@ public class CombinationActivityServiceImplTest extends BaseDbUnitTest { | ||||
|             o.setStartTime(null); | ||||
|             o.setEndTime(null); | ||||
|             o.setUserSize(null); | ||||
|             o.setTotalNum(null); | ||||
|             o.setSuccessNum(null); | ||||
|             o.setTotalCount(null); | ||||
|             o.setSuccessCount(null); | ||||
|             o.setOrderUserCount(null); | ||||
|             o.setVirtualGroup(null); | ||||
|             o.setStatus(null); | ||||
| @ -210,9 +210,9 @@ public class CombinationActivityServiceImplTest extends BaseDbUnitTest { | ||||
|         // 测试 userSize 不匹配 | ||||
|         combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setUserSize(null))); | ||||
|         // 测试 totalNum 不匹配 | ||||
|         combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setTotalNum(null))); | ||||
|         combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setTotalCount(null))); | ||||
|         // 测试 successNum 不匹配 | ||||
|         combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setSuccessNum(null))); | ||||
|         combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setSuccessCount(null))); | ||||
|         // 测试 orderUserCount 不匹配 | ||||
|         combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setOrderUserCount(null))); | ||||
|         // 测试 virtualGroup 不匹配 | ||||
|  | ||||
| @ -7,7 +7,7 @@ import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.Se | ||||
| import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityUpdateReqVO; | ||||
| import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillActivityDO; | ||||
| import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity.SeckillActivityMapper; | ||||
| import cn.iocoder.yudao.module.promotion.service.seckill.seckillactivity.SeckillActivityServiceImpl; | ||||
| import cn.iocoder.yudao.module.promotion.service.seckill.SeckillActivityServiceImpl; | ||||
| import org.junit.jupiter.api.Disabled; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.springframework.context.annotation.Import; | ||||
|  | ||||
| @ -5,7 +5,7 @@ import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.Seck | ||||
| import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigUpdateReqVO; | ||||
| import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillconfig.SeckillConfigDO; | ||||
| import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillconfig.SeckillConfigMapper; | ||||
| import cn.iocoder.yudao.module.promotion.service.seckill.seckillconfig.SeckillConfigServiceImpl; | ||||
| import cn.iocoder.yudao.module.promotion.service.seckill.SeckillConfigServiceImpl; | ||||
| import com.fasterxml.jackson.core.JsonProcessingException; | ||||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||||
| import org.junit.jupiter.api.Disabled; | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV