优化拼团活动的管理后台代码

This commit is contained in:
YunaiV 2023-08-13 00:06:46 +08:00
parent 6fdd4da0b3
commit ba31d5f6fb
28 changed files with 427 additions and 441 deletions

View File

@ -1,8 +1,10 @@
package cn.iocoder.yudao.module.promotion.controller.admin.combination; 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.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult; 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.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.CombinationActivityCreateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO; import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityRespVO; 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.annotation.Resource;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.List; import java.util.List;
import java.util.Set;
import static cn.hutool.core.collection.CollectionUtil.newArrayList; import static cn.hutool.core.collection.CollectionUtil.newArrayList;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@ -37,7 +38,7 @@ public class CombinationActivityController {
private CombinationActivityService combinationActivityService; private CombinationActivityService combinationActivityService;
@Resource @Resource
private ProductSpuApi spuApi; private ProductSpuApi productSpuApi;
@PostMapping("/create") @PostMapping("/create")
@Operation(summary = "创建拼团活动") @Operation(summary = "创建拼团活动")
@ -80,12 +81,16 @@ public class CombinationActivityController {
@Valid CombinationActivityPageReqVO pageVO) { @Valid CombinationActivityPageReqVO pageVO) {
// 查询拼团活动 // 查询拼团活动
PageResult<CombinationActivityDO> pageResult = combinationActivityService.getCombinationActivityPage(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); List<CombinationProductDO> products = combinationActivityService.getCombinationProductsByActivityIds(
Set<Long> spuIds = convertSet(pageResult.getList(), CombinationActivityDO::getSpuId); convertSet(pageResult.getList(), CombinationActivityDO::getId));
return success(CombinationActivityConvert.INSTANCE.convertPage(pageResult, List<ProductSpuRespDTO> spus = productSpuApi.getSpuList(
combinationActivityService.getCombinationProductsByActivityIds(activityIds), convertSet(pageResult.getList(), CombinationActivityDO::getSpuId));
spuApi.getSpuList(spuIds))); return success(CombinationActivityConvert.INSTANCE.convertPage(pageResult, products, spus));
} }
} }

View File

@ -22,7 +22,7 @@ public class CombinationActivityBaseVO {
@NotNull(message = "拼团名称不能为空") @NotNull(message = "拼团名称不能为空")
private String name; private String name;
@Schema(description = "商品 SPU 编号,关联 ProductSpuDO 的 id", example = "[1,2,3]") @Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "拼团商品不能为空") @NotNull(message = "拼团商品不能为空")
private Long spuId; private Long spuId;
@ -48,7 +48,7 @@ public class CombinationActivityBaseVO {
@NotNull(message = "开团人数不能为空") @NotNull(message = "开团人数不能为空")
private Integer userSize; private Integer userSize;
@Schema(description = "限制时长(小时)", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "限制时长(小时)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@NotNull(message = "限制时长不能为空") @NotNull(message = "限制时长不能为空")
private Integer limitDuration; private Integer limitDuration;

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity; 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 io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@ -17,6 +17,6 @@ public class CombinationActivityCreateReqVO extends CombinationActivityBaseVO {
@Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED)
@Valid @Valid
private List<CombinationProductCreateReqVO> products; private List<CombinationProductBaseVO> products;
} }

View File

@ -20,15 +20,6 @@ public class CombinationActivityPageReqVO extends PageParam {
@Schema(description = "拼团名称", example = "赵六") @Schema(description = "拼团名称", example = "赵六")
private String name; 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 = "开始时间") @Schema(description = "开始时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] startTime; private LocalDateTime[] startTime;
@ -37,29 +28,7 @@ public class CombinationActivityPageReqVO extends PageParam {
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] endTime; 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") @Schema(description = "活动状态0开启 1关闭", example = "0")
private Integer status; private Integer status;
@Schema(description = "限制时长(小时)")
private Integer limitDuration;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
} }

View File

@ -17,40 +17,34 @@ import java.util.List;
@ToString(callSuper = true) @ToString(callSuper = true)
public class CombinationActivityRespVO extends CombinationActivityBaseVO { 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; private String spuName;
@Schema(description = "商品主图", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.png") @Schema(description = "商品主图", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.png")
private String picUrl; private String picUrl;
@Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22901")
private Long id;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime; private LocalDateTime createTime;
@Schema(description = "开团人数", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "开团人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
@NotNull(message = "开团人数不能为空")
private Integer userSize; private Integer userSize;
@Schema(description = "开团组数", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "开团组数", requiredMode = Schema.RequiredMode.REQUIRED, example = "33")
@NotNull(message = "开团组数不能为空") private Integer totalCount;
private Integer totalNum;
@Schema(description = "成团组数", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "成团组数", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
@NotNull(message = "成团组数不能为空") private Integer successCount;
private Integer successNum;
@Schema(description = "虚拟成团", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "虚拟成团", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@NotNull(message = "虚拟成团不能为空")
private Integer virtualGroup; private Integer virtualGroup;
@Schema(description = "活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") @Schema(description = "活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
@NotNull(message = "活动状态不能为空")
private Integer status; private Integer status;
@Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED)
@Valid
private List<CombinationProductRespVO> products; private List<CombinationProductRespVO> products;
} }

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity; 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 io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@ -22,6 +22,6 @@ public class CombinationActivityUpdateReqVO extends CombinationActivityBaseVO {
@Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED)
@Valid @Valid
private List<CombinationProductUpdateReqVO> products; private List<CombinationProductBaseVO> products;
} }

View File

@ -21,7 +21,7 @@ public class CombinationProductBaseVO {
private Long skuId; private Long skuId;
@Schema(description = "拼团价格,单位分", requiredMode = Schema.RequiredMode.REQUIRED, example = "27682") @Schema(description = "拼团价格,单位分", requiredMode = Schema.RequiredMode.REQUIRED, example = "27682")
@NotNull(message = "拼团价格,单位分不能为空") @NotNull(message = "拼团价格不能为空")
private Integer activePrice; private Integer combinationPrice;
} }

View File

@ -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 {
}

View File

@ -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 {
}

View File

@ -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.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.SeckillActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillProductDO; 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.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;

View File

@ -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.controller.admin.seckill.vo.config.*;
import cn.iocoder.yudao.module.promotion.convert.seckill.seckillconfig.SeckillConfigConvert; 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.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.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;

View File

@ -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.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.CombinationProductBaseVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductRespVO; 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.CombinationActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO; 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.dataobject.combination.CombinationRecordDO;
@ -53,37 +52,34 @@ public interface CombinationActivityConvert {
default PageResult<CombinationActivityRespVO> convertPage(PageResult<CombinationActivityDO> page, default PageResult<CombinationActivityRespVO> convertPage(PageResult<CombinationActivityDO> page,
List<CombinationProductDO> productList, List<CombinationProductDO> productList,
List<ProductSpuRespDTO> spuList) { List<ProductSpuRespDTO> spuList) {
Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);
PageResult<CombinationActivityRespVO> pageResult = convertPage(page); PageResult<CombinationActivityRespVO> pageResult = convertPage(page);
// 拼接商品
Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);
pageResult.getList().forEach(item -> { pageResult.getList().forEach(item -> {
MapUtils.findAndThen(spuMap, item.getSpuId(), spu -> {
item.setSpuName(spu.getName());
item.setPicUrl(spu.getPicUrl());
});
item.setProducts(convertList2(productList)); item.setProducts(convertList2(productList));
MapUtils.findAndThen(spuMap, item.getSpuId(),
spu -> item.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl()));
}); });
return pageResult; return pageResult;
} }
List<CombinationProductRespVO> convertList2(List<CombinationProductDO> productDOs); List<CombinationProductRespVO> convertList2(List<CombinationProductDO> productDOs);
// TODO @puhui999参数改成 activityproduct 会不会干净一点哈
@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) { default List<CombinationProductDO> convertList(List<? extends CombinationProductBaseVO> products, CombinationActivityDO activityDO) {
return CollectionUtils.convertList(products, item -> convert(activityDO, item).setActivityStatus(activityDO.getStatus())); 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) { List<CombinationProductDO> products, CombinationActivityDO activity) {
Map<Long, Long> productMap = convertMap(products, CombinationProductDO::getSkuId, CombinationProductDO::getId); Map<Long, Long> productMap = convertMap(products, CombinationProductDO::getSkuId, CombinationProductDO::getId);
return CollectionUtils.convertList(updateProductVOs, updateProductVO -> convert(activity, updateProductVO) return CollectionUtils.convertList(updateProductVOs, updateProductVO -> convert(activity, updateProductVO)

View File

@ -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; private Integer virtualGroup;
/** /**
* 活动状态0开启 1关闭 * 活动状态
* *
* 枚举 {@link CommonStatusEnum} * 枚举 {@link CommonStatusEnum}
*/ */

View File

@ -40,6 +40,11 @@ public class CombinationProductDO extends BaseDO {
* 商品 SKU 编号 * 商品 SKU 编号
*/ */
private Long skuId; private Long skuId;
/**
* 拼团价格单位分
*/
private Integer combinationPrice;
/** /**
* 拼团商品状态 * 拼团商品状态
* *
@ -48,15 +53,15 @@ public class CombinationProductDO extends BaseDO {
private Integer activityStatus; private Integer activityStatus;
/** /**
* 活动开始时间点 * 活动开始时间点
*
* 冗余 {@link CombinationActivityDO#getStartTime()}
*/ */
private LocalDateTime activityStartTime; private LocalDateTime activityStartTime;
/** /**
* 活动结束时间点 * 活动结束时间点
*
* 冗余 {@link CombinationActivityDO#getEndTime()}
*/ */
private LocalDateTime activityEndTime; private LocalDateTime activityEndTime;
/**
* 拼团价格单位分
*/
private Integer activePrice;
} }

View File

@ -20,6 +20,9 @@ public interface CombinationActivityMapper extends BaseMapperX<CombinationActivi
default PageResult<CombinationActivityDO> selectPage(CombinationActivityPageReqVO reqVO) { default PageResult<CombinationActivityDO> selectPage(CombinationActivityPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<CombinationActivityDO>() return selectPage(reqVO, new LambdaQueryWrapperX<CombinationActivityDO>()
.likeIfPresent(CombinationActivityDO::getName, reqVO.getName()) .likeIfPresent(CombinationActivityDO::getName, reqVO.getName())
.betweenIfPresent(CombinationActivityDO::getStartTime, reqVO.getStartTime())
.betweenIfPresent(CombinationActivityDO::getEndTime, reqVO.getEndTime())
.eqIfPresent(CombinationActivityDO::getStatus, reqVO.getStatus())
.orderByDesc(CombinationActivityDO::getId)); .orderByDesc(CombinationActivityDO::getId));
} }

View File

@ -26,7 +26,7 @@ public interface CombinationProductMapper extends BaseMapperX<CombinationProduct
.eqIfPresent(CombinationProductDO::getActivityStatus, reqVO.getActivityStatus()) .eqIfPresent(CombinationProductDO::getActivityStatus, reqVO.getActivityStatus())
.betweenIfPresent(CombinationProductDO::getActivityStartTime, reqVO.getActivityStartTime()) .betweenIfPresent(CombinationProductDO::getActivityStartTime, reqVO.getActivityStartTime())
.betweenIfPresent(CombinationProductDO::getActivityEndTime, reqVO.getActivityEndTime()) .betweenIfPresent(CombinationProductDO::getActivityEndTime, reqVO.getActivityEndTime())
.eqIfPresent(CombinationProductDO::getActivePrice, reqVO.getActivePrice()) .eqIfPresent(CombinationProductDO::getCombinationPrice, reqVO.getActivePrice())
.betweenIfPresent(CombinationProductDO::getCreateTime, reqVO.getCreateTime()) .betweenIfPresent(CombinationProductDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(CombinationProductDO::getId)); .orderByDesc(CombinationProductDO::getId));
} }

View File

@ -40,6 +40,14 @@ public interface CombinationActivityService {
*/ */
void deleteCombinationActivity(Long id); void deleteCombinationActivity(Long id);
/**
* 校验拼团活动是否存在
*
* @param id 编号
* @return 拼团活动
*/
CombinationActivityDO validateCombinationActivityExists(Long id);
/** /**
* 获得拼团活动 * 获得拼团活动
* *
@ -48,14 +56,6 @@ public interface CombinationActivityService {
*/ */
CombinationActivityDO getCombinationActivity(Long id); 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 拼团活动的商品列表 * @return 拼团活动的商品列表
*/ */
List<CombinationProductDO> getCombinationProductsByActivityIds(Collection<Long> ids); List<CombinationProductDO> getCombinationProductsByActivityIds(Collection<Long> activityIds);
} }

View File

@ -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);
}
}

View File

@ -5,7 +5,7 @@ import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordUp
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO; import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
/** /**
* 商品活动记录 service * 拼团记录 Service 接口
* *
* @author HUIHUI * @author HUIHUI
*/ */

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityCreateReqVO; import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityCreateReqVO;

View File

@ -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.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 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.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.SeckillActivityMapper;
import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity.SeckillProductMapper; 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 cn.iocoder.yudao.module.promotion.util.PromotionUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -78,8 +77,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
* 校验秒杀商品参与的活动是否存在冲突 * 校验秒杀商品参与的活动是否存在冲突
* *
* 1. 校验秒杀时段是否存在 * 1. 校验秒杀时段是否存在
* 2. 校验商品 spu 是否存在 * 2. 秒杀商品是否参加其它活动
* 3. 秒杀商品是否参加其它活动
* *
* @param configIds 秒杀时段数组 * @param configIds 秒杀时段数组
* @param spuId 商品 SPU 编号 * @param spuId 商品 SPU 编号

View File

@ -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.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigCreateReqVO; import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigCreateReqVO;

View File

@ -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.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;

View File

@ -24,15 +24,15 @@ import static org.junit.jupiter.api.Assertions.*;
// TODO 芋艿等完成后在补全单测 // TODO 芋艿等完成后在补全单测
/** /**
* {@link CombinationServiceImpl} 的单元测试类 * {@link CombinationActivityServiceImpl} 的单元测试类
* *
* @author HUIHUI * @author HUIHUI
*/ */
@Import(CombinationServiceImpl.class) @Import(CombinationActivityServiceImpl.class)
public class CombinationActivityServiceImplTest extends BaseDbUnitTest { public class CombinationActivityServiceImplTest extends BaseDbUnitTest {
@Resource @Resource
private CombinationServiceImpl combinationActivityService; private CombinationActivityServiceImpl combinationActivityService;
@Resource @Resource
private CombinationActivityMapper combinationActivityMapper; private CombinationActivityMapper combinationActivityMapper;
@ -112,8 +112,8 @@ public class CombinationActivityServiceImplTest extends BaseDbUnitTest {
o.setStartTime(null); o.setStartTime(null);
o.setEndTime(null); o.setEndTime(null);
o.setUserSize(null); o.setUserSize(null);
o.setTotalNum(null); o.setTotalCount(null);
o.setSuccessNum(null); o.setSuccessCount(null);
o.setOrderUserCount(null); o.setOrderUserCount(null);
o.setVirtualGroup(null); o.setVirtualGroup(null);
o.setStatus(null); o.setStatus(null);
@ -136,9 +136,9 @@ public class CombinationActivityServiceImplTest extends BaseDbUnitTest {
// 测试 userSize 不匹配 // 测试 userSize 不匹配
combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setUserSize(null))); combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setUserSize(null)));
// 测试 totalNum 不匹配 // 测试 totalNum 不匹配
combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setTotalNum(null))); combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setTotalCount(null)));
// 测试 successNum 不匹配 // 测试 successNum 不匹配
combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setSuccessNum(null))); combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setSuccessCount(null)));
// 测试 orderUserCount 不匹配 // 测试 orderUserCount 不匹配
combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setOrderUserCount(null))); combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setOrderUserCount(null)));
// 测试 virtualGroup 不匹配 // 测试 virtualGroup 不匹配
@ -186,8 +186,8 @@ public class CombinationActivityServiceImplTest extends BaseDbUnitTest {
o.setStartTime(null); o.setStartTime(null);
o.setEndTime(null); o.setEndTime(null);
o.setUserSize(null); o.setUserSize(null);
o.setTotalNum(null); o.setTotalCount(null);
o.setSuccessNum(null); o.setSuccessCount(null);
o.setOrderUserCount(null); o.setOrderUserCount(null);
o.setVirtualGroup(null); o.setVirtualGroup(null);
o.setStatus(null); o.setStatus(null);
@ -210,9 +210,9 @@ public class CombinationActivityServiceImplTest extends BaseDbUnitTest {
// 测试 userSize 不匹配 // 测试 userSize 不匹配
combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setUserSize(null))); combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setUserSize(null)));
// 测试 totalNum 不匹配 // 测试 totalNum 不匹配
combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setTotalNum(null))); combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setTotalCount(null)));
// 测试 successNum 不匹配 // 测试 successNum 不匹配
combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setSuccessNum(null))); combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setSuccessCount(null)));
// 测试 orderUserCount 不匹配 // 测试 orderUserCount 不匹配
combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setOrderUserCount(null))); combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setOrderUserCount(null)));
// 测试 virtualGroup 不匹配 // 测试 virtualGroup 不匹配

View File

@ -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.controller.admin.seckill.vo.activity.SeckillActivityUpdateReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillActivityDO; 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.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.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;

View File

@ -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.controller.admin.seckill.vo.config.SeckillConfigUpdateReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillconfig.SeckillConfigDO; 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.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.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;