diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java index 62e0e4441..3473852f7 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java @@ -4,6 +4,7 @@ import cn.hutool.core.date.LocalDateTimeUtil; import java.time.Duration; import java.time.LocalDateTime; +import java.time.LocalTime; /** * 时间工具类,用于 {@link java.time.LocalDateTime} @@ -50,7 +51,7 @@ public class LocalDateTimeUtils { * 判断当前时间是否在该时间范围内 * * @param startTime 开始时间 - * @param endTime 结束时间 + * @param endTime 结束时间 * @return 是否 */ public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime) { @@ -60,4 +61,23 @@ public class LocalDateTimeUtils { return LocalDateTimeUtil.isIn(LocalDateTime.now(), startTime, endTime); } + /** + * 检查时间重叠 不包含日期 + * + * @param startTime1 需要校验的开始时间 + * @param endTime1 需要校验的结束时间 + * @param startTime2 校验所需的开始时间 + * @param endTime2 校验所需的结束时间 + * @return 是否重叠 + */ + public static boolean checkTimeOverlap(LocalTime startTime1, LocalTime endTime1, LocalTime startTime2, LocalTime endTime2) { + // 判断时间是否重叠 + // 开始时间在已配置时段的结束时间之前 且 结束时间在已配置时段的开始时间之后 [] + return startTime1.isBefore(endTime2) && endTime1.isAfter(startTime2) + // 开始时间在已配置时段的开始时间之前 且 结束时间在已配置时段的开始时间之后 (] 或 () + || startTime1.isBefore(startTime2) && endTime1.isAfter(startTime2) + // 开始时间在已配置时段的结束时间之前 且 结束时间在已配值时段的结束时间之后 [) 或 () + || startTime1.isBefore(endTime2) && endTime1.isAfter(endTime2); + } + } diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApi.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApi.java index 21fcf7d96..2ed262cde 100644 --- a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApi.java +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApi.java @@ -30,14 +30,13 @@ public interface ProductSkuApi { */ List getSkuList(Collection ids); - // TODO puhui999:入参用 Collection 更通用 /** * 批量查询 SKU 数组 * * @param spuIds SPU 编号列表 * @return SKU 数组 */ - List getSkuListBySpuId(List spuIds); + List getSkuListBySpuId(Collection spuIds); /** * 更新 SKU 库存 diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ErrorCodeConstants.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ErrorCodeConstants.java index d97a2613f..4c31a8baa 100644 --- a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ErrorCodeConstants.java +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ErrorCodeConstants.java @@ -46,7 +46,7 @@ public interface ErrorCodeConstants { // ========== 商品 评价 1008007000 ========== ErrorCode COMMENT_NOT_EXISTS = new ErrorCode(1008007000, "商品 评价 不存在"); - ErrorCode ORDER_SPU_COMMENT_EXISTS = new ErrorCode(1008007001, "订单 商品评价 已存在"); + ErrorCode ORDER_SKU_COMMENT_EXISTS = new ErrorCode(1008007001, "订单 商品评价 已存在"); ErrorCode COMMENT_ERROR_OPT = new ErrorCode(1008007002, "商品评价非法操作"); ErrorCode COMMENT_ADDITIONAL_EXISTS = new ErrorCode(1008007003, "商品追加评价已存在"); diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApiImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApiImpl.java index 4a8425f61..bd0258efa 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApiImpl.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApiImpl.java @@ -43,7 +43,7 @@ public class ProductSkuApiImpl implements ProductSkuApi { } @Override - public List getSkuListBySpuId(List spuIds) { + public List getSkuListBySpuId(Collection spuIds) { if (CollUtil.isEmpty(spuIds)) { return Collections.emptyList(); } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentBaseVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentBaseVO.java index c2e791a44..24d6a5456 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentBaseVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentBaseVO.java @@ -26,23 +26,10 @@ public class ProductCommentBaseVO { @NotNull(message = "评价人头像不能为空") private String userAvatar; - // TODO @puhui:spuId、spuName 是不是只有 ProductCommentRespVO 有呀。 - @Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉丝滑透气小短袖") - @NotNull(message = "商品 SPU 编号不能为空") - private Long spuId; - - @Schema(description = "商品 SPU 名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") - @NotNull(message = "商品 SPU 名称不能为空") - private String spuName; - @Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @NotNull(message = "商品 SKU 编号不能为空") private Long skuId; - @Schema(description = "评分星级 1-5 分", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") - @NotNull(message = "评分星级不能为空") - private Integer scores; - @Schema(description = "描述星级 1-5 分", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") @NotNull(message = "描述星级不能为空") private Integer descriptionScores; diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentPageReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentPageReqVO.java index f6e913fea..3791f572e 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentPageReqVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentPageReqVO.java @@ -35,9 +35,8 @@ public class ProductCommentPageReqVO extends PageParam { @InEnum(ProductCommentScoresEnum.class) private Integer scores; - // TODO @puhui999:replyStatus 哈 @Schema(description = "商家是否回复", example = "true") - private Boolean replied; + private Boolean replyStatus; @Schema(description = "创建时间") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentRespVO.java index a75e9ec0c..9205e34cc 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentRespVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/vo/ProductCommentRespVO.java @@ -5,6 +5,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; +import javax.validation.constraints.NotNull; import java.time.LocalDateTime; @Schema(description = "管理后台 - 商品评价 Response VO") @@ -40,4 +41,15 @@ public class ProductCommentRespVO extends ProductCommentBaseVO { @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) private LocalDateTime createTime; + @Schema(description = "评分星级 1-5 分", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") + private Integer scores; + + @Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉丝滑透气小短袖") + @NotNull(message = "商品 SPU 编号不能为空") + private Long spuId; + + @Schema(description = "商品 SPU 名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") + @NotNull(message = "商品 SPU 名称不能为空") + private String spuName; + } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java index 0df57ca4f..e1c70f01c 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java @@ -21,6 +21,7 @@ import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; import java.io.IOException; +import java.util.Collection; import java.util.List; import java.util.Map; @@ -29,11 +30,6 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS; -/** - * 商品 SPU 相关接口 - * - * @author HUIHUI - */ @Tag(name = "管理后台 - 商品 SPU") @RestController @RequestMapping("/product/spu") @@ -100,6 +96,15 @@ public class ProductSpuController { return success(ProductSpuConvert.INSTANCE.convertList02(list)); } + @GetMapping("/list") + @Operation(summary = "获得商品 SPU 详情列表") + @Parameter(name = "spuIds", description = "spu 编号列表", required = true, example = "[1,2,3]") + @PreAuthorize("@ss.hasPermission('product:spu:query')") + public CommonResult> getSpuList(@RequestParam("spuIds") Collection spuIds) { + return success(ProductSpuConvert.INSTANCE.convertForSpuDetailRespListVO( + productSpuService.getSpuList(spuIds), productSkuService.getSkuListBySpuId(spuIds))); + } + @GetMapping("/page") @Operation(summary = "获得商品 SPU 分页") @PreAuthorize("@ss.hasPermission('product:spu:query')") diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuCreateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuCreateReqVO.java index b2fef56e2..baf6adfab 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuCreateReqVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuCreateReqVO.java @@ -9,11 +9,6 @@ import lombok.ToString; import javax.validation.Valid; import java.util.List; -/** - * 商品 SPU 创建 Request VO - * - * @author HUIHUI - */ @Schema(description = "管理后台 - 商品 SPU 创建 Request VO") @Data @EqualsAndHashCode(callSuper = true) diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuDetailRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuDetailRespVO.java index eff229c9d..1be96632d 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuDetailRespVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuDetailRespVO.java @@ -8,12 +8,6 @@ import lombok.ToString; import java.util.List; -/** - * 商品 SPU 详细 Response VO - * 包括关联的 SKU 等信息 - * - * @author HUIHUI - */ @Schema(description = "管理后台 - 商品 SPU 详细 Response VO") @Data @EqualsAndHashCode(callSuper = true) diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuExportReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuExportReqVO.java index f1da656d1..aaeca8622 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuExportReqVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuExportReqVO.java @@ -10,11 +10,6 @@ import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; -/** - * 商品Spu导出 Request VO,参数和 ProductSpuPageReqVO 是一致的 - * - * @author HUIHUI - */ @Schema(description = "管理后台 - 商品Spu导出 Request VO,参数和 ProductSpuPageReqVO 是一致的") @Data @NoArgsConstructor diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuPageReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuPageReqVO.java index d428164dd..81cff4210 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuPageReqVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuPageReqVO.java @@ -11,11 +11,6 @@ import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; -/** - * 商品 SPU 分页 Request VO - * - * @author HUIHUI - */ @Schema(description = "管理后台 - 商品 SPU 分页 Request VO") @Data @EqualsAndHashCode(callSuper = true) diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuRespVO.java index 346614ad0..bd35d5b9a 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuRespVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuRespVO.java @@ -7,11 +7,6 @@ import lombok.ToString; import java.time.LocalDateTime; -/** - * 商品 SPU Response VO - * - * @author HUIHUI - */ @Schema(description = "管理后台 - 商品 SPU Response VO") @Data @EqualsAndHashCode(callSuper = true) diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuSimpleRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuSimpleRespVO.java index 1c943a912..7d9d0d05d 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuSimpleRespVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuSimpleRespVO.java @@ -4,11 +4,6 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.ToString; -/** - * 商品 SPU 精简 Response VO - * TODO 商品 SPU 精简 VO 暂时没有使用到,用到的时候再按需添加\修改属性 - * @author HUIHUI - */ @Schema(description = "管理后台 - 商品 SPU 精简 Response VO") @Data @ToString(callSuper = true) diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuUpdateReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuUpdateReqVO.java index c7c424fa0..bb69eb4cf 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuUpdateReqVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuUpdateReqVO.java @@ -12,11 +12,6 @@ import javax.validation.Valid; import javax.validation.constraints.NotNull; import java.util.List; -/** - * 商品 SPU 更新 Request VO - * - * @author HUIHUI - */ @Schema(description = "管理后台 - 商品 SPU 更新 Request VO") @Data @EqualsAndHashCode(callSuper = true) diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuUpdateStatusReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuUpdateStatusReqVO.java index 57bda636c..e36e68466 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuUpdateStatusReqVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuUpdateStatusReqVO.java @@ -7,11 +7,6 @@ import lombok.Data; import javax.validation.constraints.NotNull; -/** - * 商品 SPU Status 更新 Request VO - * - * @author HUIHUI - */ @Schema(description = "管理后台 - 商品 SPU Status 更新 Request VO") @Data public class ProductSpuUpdateStatusReqVO{ diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/comment/AppProductCommentController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/comment/AppProductCommentController.java index 1bcd4a138..3b79a2855 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/comment/AppProductCommentController.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/comment/AppProductCommentController.java @@ -1,18 +1,18 @@ package cn.iocoder.yudao.module.product.controller.app.comment; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentPageReqVO; import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentStatisticsRespVO; import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppProductCommentRespVO; -import cn.iocoder.yudao.module.product.controller.app.property.vo.value.AppProductPropertyValueDetailRespVO; import cn.iocoder.yudao.module.product.convert.comment.ProductCommentConvert; import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO; import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; import cn.iocoder.yudao.module.product.service.comment.ProductCommentService; import cn.iocoder.yudao.module.product.service.sku.ProductSkuService; +import com.google.common.collect.Maps; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; @@ -26,11 +26,9 @@ import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import javax.validation.Valid; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @@ -61,26 +59,14 @@ public class AppProductCommentController { @GetMapping("/page") @Operation(summary = "获得商品评价分页") public CommonResult> getCommentPage(@Valid AppCommentPageReqVO pageVO) { - PageResult page = productCommentService.getCommentPage(pageVO, Boolean.TRUE); - // TODO @puhui CollUtils 有简化 convertmap 和 list 的方法 - Set skuIds = page.getList().stream().map(AppProductCommentRespVO::getSkuId).collect(Collectors.toSet()); + PageResult commentDOPage = productCommentService.getCommentPage(pageVO, Boolean.TRUE); + Set skuIds = CollectionUtils.convertSet(commentDOPage.getList(), ProductCommentDO::getSkuId); List skuList = productSkuService.getSkuList(skuIds); - Map skuDOMap = new HashMap<>(skuIds.size()); + Map skuDOMap = Maps.newLinkedHashMapWithExpectedSize(skuIds.size()); if (CollUtil.isNotEmpty(skuList)) { - skuDOMap.putAll(skuList.stream().collect(Collectors.toMap(ProductSkuDO::getId, c -> c))); + skuDOMap.putAll(CollectionUtils.convertMap(skuList, ProductSkuDO::getId, c -> c)); } - // TODO @puihui999:下面也可以放到 convert 里哈 - page.getList().forEach(item -> { - // 判断用户是否选择匿名 - if (ObjectUtil.equal(item.getAnonymous(), true)) { - item.setUserNickname(ProductCommentDO.NICKNAME_ANONYMOUS); - } - ProductSkuDO productSkuDO = skuDOMap.get(item.getSkuId()); - if (productSkuDO != null) { - List skuProperties = ProductCommentConvert.INSTANCE.convertList01(productSkuDO.getProperties()); - item.setSkuProperties(skuProperties); - } - }); + PageResult page = ProductCommentConvert.INSTANCE.convertPage02(commentDOPage, skuDOMap); return success(page); } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/comment/vo/AppProductCommentRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/comment/vo/AppProductCommentRespVO.java index d13a2b0ac..2ea3af496 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/comment/vo/AppProductCommentRespVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/comment/vo/AppProductCommentRespVO.java @@ -10,11 +10,6 @@ import javax.validation.constraints.Size; import java.time.LocalDateTime; import java.util.List; -/** - * 用户 App - 商品评价详情 Response VO - * - * @author HUIHUI - */ @Schema(description = "用户 App - 商品评价详情 Response VO") @Data @ToString(callSuper = true) diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/comment/ProductCommentConvert.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/comment/ProductCommentConvert.java index 170a3f0f4..92c661881 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/comment/ProductCommentConvert.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/comment/ProductCommentConvert.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.product.convert.comment; +import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO; @@ -19,6 +20,7 @@ import org.mapstruct.factory.Mappers; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.List; +import java.util.Map; /** * 商品评价 Convert @@ -32,10 +34,12 @@ public interface ProductCommentConvert { ProductCommentRespVO convert(ProductCommentDO bean); - // TODO @puhui999:这里貌似字段对上,就不用 mapping 了;可以测试下看看哈 - @Mapping(target = "goodCount", source = "goodCount") - @Mapping(target = "mediocreCount", source = "mediocreCount") - @Mapping(target = "negativeCount", source = "negativeCount") + @Named("calculateOverallScore") + default double calculateOverallScore(long goodCount, long mediocreCount, long negativeCount) { + return (goodCount * 5 + mediocreCount * 3 + negativeCount) / (double) (goodCount + mediocreCount + negativeCount); + } + + @Mapping(target = "scores", expression = "java(calculateOverallScore(goodCount, mediocreCount, negativeCount))") AppCommentStatisticsRespVO convert(Long goodCount, Long mediocreCount, Long negativeCount); List convertList(List list); @@ -44,7 +48,23 @@ public interface ProductCommentConvert { PageResult convertPage(PageResult page); - PageResult convertPage02(PageResult pageResult); + PageResult convertPage01(PageResult pageResult); + + default PageResult convertPage02(PageResult pageResult, Map skuDOMap) { + PageResult page = convertPage01(pageResult); + page.getList().forEach(item -> { + // 判断用户是否选择匿名 + if (ObjectUtil.equal(item.getAnonymous(), true)) { + item.setUserNickname(ProductCommentDO.NICKNAME_ANONYMOUS); + } + ProductSkuDO productSkuDO = skuDOMap.get(item.getSkuId()); + if (productSkuDO != null) { + List skuProperties = ProductCommentConvert.INSTANCE.convertList01(productSkuDO.getProperties()); + item.setSkuProperties(skuProperties); + } + }); + return page; + } /** * 计算综合评分 diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java index aaad240a4..833ed0722 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.product.convert.spu; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils; import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; @@ -18,6 +19,7 @@ import org.mapstruct.factory.Mappers; import java.util.ArrayList; import java.util.List; +import java.util.Map; import static cn.hutool.core.util.ObjectUtil.defaultIfNull; @@ -107,4 +109,15 @@ public interface ProductSpuConvert { return detailRespVO; } + default List convertForSpuDetailRespListVO(List spus, List skus) { + ArrayList vos = new ArrayList<>(); + Map> skuMultiMap = CollectionUtils.convertMultiMap(skus, ProductSkuDO::getSpuId); + spus.forEach(spu -> { + ProductSpuDetailRespVO detailRespVO = convert03(spu); + detailRespVO.setSkus(ProductSkuConvert.INSTANCE.convertList(skuMultiMap.get(spu.getId()))); + vos.add(detailRespVO); + }); + return vos; + } + } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/comment/ProductCommentMapper.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/comment/ProductCommentMapper.java index 1426073a5..48b651a55 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/comment/ProductCommentMapper.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/comment/ProductCommentMapper.java @@ -51,11 +51,11 @@ public interface ProductCommentMapper extends BaseMapperX { return selectPage(reqVO, queryWrapper); } - default ProductCommentDO selectByUserIdAndOrderItemIdAndSpuId(Long userId, Long orderItemId, Long spuId) { + default ProductCommentDO selectByUserIdAndOrderItemIdAndSpuId(Long userId, Long orderItemId, Long skuId) { return selectOne(new LambdaQueryWrapperX() .eq(ProductCommentDO::getUserId, userId) .eq(ProductCommentDO::getOrderItemId, orderItemId) - .eq(ProductCommentDO::getSpuId, spuId)); + .eq(ProductCommentDO::getSpuId, skuId)); } default Long selectCountBySpuId(Long spuId, Boolean visible, Integer type) { diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentService.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentService.java index 7f615677c..2c2b35197 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentService.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentService.java @@ -54,7 +54,7 @@ public interface ProductCommentService { * @param visible 是否可见 * @return 商品评价分页 */ - PageResult getCommentPage(AppCommentPageReqVO pageVO, Boolean visible); + PageResult getCommentPage(AppCommentPageReqVO pageVO, Boolean visible); /** * 创建商品评论 diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentServiceImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentServiceImpl.java index b0822e96c..3409344d1 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentServiceImpl.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentServiceImpl.java @@ -81,7 +81,7 @@ public class ProductCommentServiceImpl implements ProductCommentService { @Transactional(rollbackFor = Exception.class) public void createComment(ProductCommentCreateReqVO createReqVO) { // 校验评论 - validateComment(createReqVO.getSpuId(), createReqVO.getUserId(), createReqVO.getOrderItemId()); + validateComment(createReqVO.getSkuId(), createReqVO.getUserId(), createReqVO.getOrderItemId()); ProductCommentDO commentDO = ProductCommentConvert.INSTANCE.convert(createReqVO); productCommentMapper.insert(commentDO); @@ -108,11 +108,11 @@ public class ProductCommentServiceImpl implements ProductCommentService { return commentDO.getId(); } - private void validateComment(Long spuId, Long userId, Long orderItemId) { + private void validateComment(Long skuId, Long userId, Long orderItemId) { // 判断当前订单的当前商品用户是否评价过 - ProductCommentDO exist = productCommentMapper.selectByUserIdAndOrderItemIdAndSpuId(userId, orderItemId, spuId); + ProductCommentDO exist = productCommentMapper.selectByUserIdAndOrderItemIdAndSpuId(userId, orderItemId, skuId); if (null != exist) { - throw exception(ORDER_SPU_COMMENT_EXISTS); + throw exception(ORDER_SKU_COMMENT_EXISTS); } } @@ -141,23 +141,17 @@ public class ProductCommentServiceImpl implements ProductCommentService { productCommentMapper.selectCountBySpuId(spuId, visible, AppCommentPageReqVO.MEDIOCRE_COMMENT), // 查询商品 id = spuId 的所有差评数量 productCommentMapper.selectCountBySpuId(spuId, visible, AppCommentPageReqVO.NEGATIVE_COMMENT) - ).setScores(3.0); // TODO @puhui999:这里要实现下;; + ); } @Override public List getCommentList(Long spuId, Integer count) { - // 校验商品 spu 是否存在 - // TODO @puhui 这里校验可以去掉哈。 - ProductSpuDO spuDO = validateSpu(spuId); - return ProductCommentConvert.INSTANCE.convertList02(productCommentMapper.selectCommentList(spuDO.getId(), count).getList()); + return ProductCommentConvert.INSTANCE.convertList02(productCommentMapper.selectCommentList(spuId, count).getList()); } - // TODO @puhui 可以放到 controller 去 convert 哈 @Override - public PageResult getCommentPage(AppCommentPageReqVO pageVO, Boolean visible) { - // TODO @puhui 可以放到 controller 去 convert 哈 - return ProductCommentConvert.INSTANCE.convertPage02( - productCommentMapper.selectPage(pageVO, visible)); + public PageResult getCommentPage(AppCommentPageReqVO pageVO, Boolean visible) { + return productCommentMapper.selectPage(pageVO, visible); } @Override diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuService.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuService.java index 4c106d1bc..fbc9830bf 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuService.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuService.java @@ -90,7 +90,7 @@ public interface ProductSkuService { * @param spuIds spu 编码集合 * @return 商品 sku 集合 */ - List getSkuListBySpuId(List spuIds); + List getSkuListBySpuId(Collection spuIds); /** * 通过 spuId 删除 sku 信息 diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java index 3f77fbe8d..7c6c5030e 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java @@ -148,7 +148,7 @@ public class ProductSkuServiceImpl implements ProductSkuService { } @Override - public List getSkuListBySpuId(List spuIds) { + public List getSkuListBySpuId(Collection spuIds) { return productSkuMapper.selectListBySpuId(spuIds); } diff --git a/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentServiceImplTest.java b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentServiceImplTest.java index 8d3a3d5b8..2f55af837 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentServiceImplTest.java +++ b/yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentServiceImplTest.java @@ -10,7 +10,6 @@ import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommen import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentUpdateVisibleReqVO; import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentPageReqVO; import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentStatisticsRespVO; -import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppProductCommentRespVO; import cn.iocoder.yudao.module.product.convert.comment.ProductCommentConvert; import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO; import cn.iocoder.yudao.module.product.dal.mysql.comment.ProductCommentMapper; @@ -128,7 +127,7 @@ public class ProductCommentServiceImplTest extends BaseDbUnitTest { productCommentPageReqVO.setSpuId(spuId); productCommentPageReqVO.setSpuName("感冒药"); productCommentPageReqVO.setScores(ProductCommentScoresEnum.FOUR.getScores()); - productCommentPageReqVO.setReplied(Boolean.TRUE); + productCommentPageReqVO.setReplyStatus(Boolean.TRUE); PageResult commentPage = productCommentService.getCommentPage(productCommentPageReqVO); PageResult result = ProductCommentConvert.INSTANCE.convertPage(productCommentMapper.selectPage(productCommentPageReqVO)); @@ -138,15 +137,15 @@ public class ProductCommentServiceImplTest extends BaseDbUnitTest { assertEquals(8, all.getTotal()); // 测试获取所有商品分页评论数据 - PageResult result1 = productCommentService.getCommentPage(new AppCommentPageReqVO(), Boolean.TRUE); + PageResult result1 = productCommentService.getCommentPage(new AppCommentPageReqVO(), Boolean.TRUE); assertEquals(7, result1.getTotal()); // 测试获取所有商品分页中评数据 - PageResult result2 = productCommentService.getCommentPage(new AppCommentPageReqVO().setType(AppCommentPageReqVO.MEDIOCRE_COMMENT), Boolean.TRUE); + PageResult result2 = productCommentService.getCommentPage(new AppCommentPageReqVO().setType(AppCommentPageReqVO.MEDIOCRE_COMMENT), Boolean.TRUE); assertEquals(2, result2.getTotal()); // 测试获取指定 spuId 商品分页中评数据 - PageResult result3 = productCommentService.getCommentPage(new AppCommentPageReqVO().setSpuId(spuId).setType(AppCommentPageReqVO.MEDIOCRE_COMMENT), Boolean.TRUE); + PageResult result3 = productCommentService.getCommentPage(new AppCommentPageReqVO().setSpuId(spuId).setType(AppCommentPageReqVO.MEDIOCRE_COMMENT), Boolean.TRUE); assertEquals(2, result3.getTotal()); // 测试分页 tab count diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApi.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApi.java new file mode 100644 index 000000000..486409320 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApi.java @@ -0,0 +1,49 @@ +package cn.iocoder.yudao.module.promotion.api.combination; + +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO; + +import javax.validation.Valid; + +/** + * 拼团活动 API 接口 + * + * @author HUIHUI + */ +public interface CombinationApi { + + /** + * 创建开团记录 + * + * @param reqDTO 请求 DTO + */ + void createRecord(@Valid CombinationRecordReqDTO reqDTO); + + /** + * 获取开团记录状态 + * + * @param userId 用户编号 + * @param orderId 订单编号 + */ + boolean validateRecordStatusIsSuccess(Long userId, Long orderId); + + + /** + * 更新开团记录状态 + * + * @param userId 用户编号 + * @param orderId 订单编号 + * @param status 状态值 + */ + void updateRecordStatus(Long userId, Long orderId, Integer status); + + /** + * 更新开团记录状态和开始时间 + * + * @param userId 用户编号 + * @param orderId 订单编号 + * @param status 状态值 + * @return + */ + void updateRecordStatusAndStartTime(Long userId, Long orderId, Integer status); + +} diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationRecordReqDTO.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationRecordReqDTO.java new file mode 100644 index 000000000..a5d1048d9 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationRecordReqDTO.java @@ -0,0 +1,77 @@ +package cn.iocoder.yudao.module.promotion.api.combination.dto; + +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +/** + * 拼团记录 Request DTO + * + * @author HUIHUI + */ +@Data +public class CombinationRecordReqDTO { + + /** + * 拼团活动编号 + */ + @NotNull(message = "拼团活动编号不能为空") + private Long activityId; + /** + * spu 编号 + */ + @NotNull(message = "spu 编号不能为空") + private Long spuId; + /** + * sku 编号 + */ + @NotNull(message = "sku 编号不能为空") + private Long skuId; + /** + * 用户编号 + */ + @NotNull(message = "用户编号不能为空") + private Long userId; + /** + * 订单编号 + */ + @NotNull(message = "订单编号不能为空") + private Long orderId; + /** + * 团长编号 + */ + @NotNull(message = "团长编号不能为空") + private Long headId; + /** + * 商品名字 + */ + @NotEmpty(message = "商品名字不能为空") + private String spuName; + /** + * 商品图片 + */ + @NotEmpty(message = "商品图片不能为空") + private String picUrl; + /** + * 拼团商品单价 + */ + @NotNull(message = "拼团商品单价不能为空") + private Integer combinationPrice; + /** + * 用户昵称 + */ + @NotEmpty(message = "用户昵称不能为空") + private String nickname; + /** + * 用户头像 + */ + @NotEmpty(message = "用户头像不能为空") + private String avatar; + /** + * 开团状态:正在开团 拼团成功 拼团失败 TODO 等待支付 + */ + @NotNull(message = "开团状态不能为空") + private Integer status; + +} diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java index 945709697..6a646a11d 100644 --- a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java +++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode; /** * Promotion 错误码枚举类 - * + *

* promotion 系统,使用 1-013-000-000 段 */ public interface ErrorCodeConstants { @@ -58,5 +58,12 @@ public interface ErrorCodeConstants { ErrorCode SECKILL_TIME_CONFLICTS = new ErrorCode(1013009001, "秒杀时段冲突"); ErrorCode SECKILL_TIME_EQUAL = new ErrorCode(1013009002, "秒杀时段开始时间和结束时间不能相等"); ErrorCode SECKILL_START_TIME_BEFORE_END_TIME = new ErrorCode(1013009003, "秒杀时段开始时间不能在结束时间之后"); + ErrorCode SECKILL_TIME_DISABLE = new ErrorCode(1013009004, "秒杀时段已关闭"); + // ========== 拼团活动 1013010000 ========== + ErrorCode COMBINATION_ACTIVITY_NOT_EXISTS = new ErrorCode(1013010000, "拼团活动不存在"); + ErrorCode COMBINATION_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013010001, "存在商品参加了其它拼团活动"); + ErrorCode COMBINATION_ACTIVITY_STATUS_DISABLE = new ErrorCode(1013010002, "拼团活动已关闭不能修改"); + ErrorCode COMBINATION_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013010003, "拼团活动未关闭或未结束,不能删除"); + ErrorCode COMBINATION_RECORD_NOT_EXISTS = new ErrorCode(1013010004, "拼团不存在"); } diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/combination/CombinationRecordStatusEnum.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/combination/CombinationRecordStatusEnum.java new file mode 100644 index 000000000..cc475133e --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/combination/CombinationRecordStatusEnum.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.promotion.enums.combination; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 拼团状态枚举 + * + * @author HUIHUI + */ +@AllArgsConstructor +@Getter +public enum CombinationRecordStatusEnum implements IntArrayValuable { + NOT_PAY(0, "未付款"), + ONGOING(1, "进行中"), + SUCCESS(2, "拼团成功"), + FAILED(3, "拼团失败"); + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CombinationRecordStatusEnum::getStatus).toArray(); + + /** + * 状态值 + */ + private final Integer status; + /** + * 状态名 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApiImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApiImpl.java new file mode 100644 index 000000000..2386f9ed6 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApiImpl.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.promotion.api.combination; + +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO; +import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.time.LocalDateTime; + +/** + * 拼团活动 API 实现类 + * + * @author HUIHUI + */ +@Service +public class CombinationApiImpl implements CombinationApi { + @Resource + private CombinationActivityService activityService; + + @Override + public void createRecord(CombinationRecordReqDTO reqDTO) { + activityService.createRecord(reqDTO); + } + + @Override + public boolean validateRecordStatusIsSuccess(Long userId, Long orderId) { + return activityService.validateRecordStatusIsSuccess(userId, orderId); + } + + @Override + public void updateRecordStatus(Long userId, Long orderId, Integer status) { + activityService.updateRecordStatusByUserIdAndOrderId(userId, orderId, status); + } + + @Override + public void updateRecordStatusAndStartTime(Long userId, Long orderId, Integer status) { + activityService.updateRecordStatusAndStartTimeByUserIdAndOrderId(userId, orderId, status, LocalDateTime.now()); + } + + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/CombinationActivityController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/CombinationActivityController.java new file mode 100644 index 000000000..883ff2ffc --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/CombinationActivityController.java @@ -0,0 +1,112 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.combination; + +import cn.hutool.core.collection.CollectionUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +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.*; +import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert; +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationActivityDO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationProductDO; +import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + +@Tag(name = "管理后台 - 拼团活动") +@RestController +@RequestMapping("/promotion/combination-activity") +@Validated +public class CombinationActivityController { + + @Resource + private CombinationActivityService combinationActivityService; + @Resource + private ProductSpuApi spuApi; + + @PostMapping("/create") + @Operation(summary = "创建拼团活动") + @PreAuthorize("@ss.hasPermission('promotion:combination-activity:create')") + public CommonResult createCombinationActivity(@Valid @RequestBody CombinationActivityCreateReqVO createReqVO) { + return success(combinationActivityService.createCombinationActivity(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新拼团活动") + @PreAuthorize("@ss.hasPermission('promotion:combination-activity:update')") + public CommonResult updateCombinationActivity(@Valid @RequestBody CombinationActivityUpdateReqVO updateReqVO) { + combinationActivityService.updateCombinationActivity(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除拼团活动") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('promotion:combination-activity:delete')") + public CommonResult deleteCombinationActivity(@RequestParam("id") Long id) { + combinationActivityService.deleteCombinationActivity(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得拼团活动") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('promotion:combination-activity:query')") + public CommonResult getCombinationActivity(@RequestParam("id") Long id) { + CombinationActivityDO combinationActivity = combinationActivityService.getCombinationActivity(id); + List productDOs = combinationActivityService.getProductsByActivityIds(CollectionUtil.newArrayList(id)); + return success(CombinationActivityConvert.INSTANCE.convert(combinationActivity, productDOs)); + } + + @GetMapping("/list") + @Operation(summary = "获得拼团活动列表") + @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048") + @PreAuthorize("@ss.hasPermission('promotion:combination-activity:query')") + public CommonResult> getCombinationActivityList(@RequestParam("ids") Collection ids) { + List list = combinationActivityService.getCombinationActivityList(ids); + return success(CombinationActivityConvert.INSTANCE.convertList(list)); + } + + @GetMapping("/page") + @Operation(summary = "获得拼团活动分页") + @PreAuthorize("@ss.hasPermission('promotion:combination-activity:query')") + public CommonResult> getCombinationActivityPage(@Valid CombinationActivityPageReqVO pageVO) { + PageResult pageResult = combinationActivityService.getCombinationActivityPage(pageVO); + Set aIds = CollectionUtils.convertSet(pageResult.getList(), CombinationActivityDO::getId); + List productDOs = combinationActivityService.getProductsByActivityIds(aIds); + Set spuIds = CollectionUtils.convertSet(pageResult.getList(), CombinationActivityDO::getSpuId); + List spuList = spuApi.getSpuList(spuIds); + return success(CombinationActivityConvert.INSTANCE.convertPage(pageResult, productDOs, spuList)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出拼团活动 Excel") + @PreAuthorize("@ss.hasPermission('promotion:combination-activity:export')") + @OperateLog(type = EXPORT) + public void exportCombinationActivityExcel(@Valid CombinationActivityExportReqVO exportReqVO, + HttpServletResponse response) throws IOException { + List list = combinationActivityService.getCombinationActivityList(exportReqVO); + // 导出 Excel + List datas = CombinationActivityConvert.INSTANCE.convertList02(list); + ExcelUtils.write(response, "拼团活动.xls", "数据", CombinationActivityExcelVO.class, datas); + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityBaseVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityBaseVO.java new file mode 100644 index 000000000..daebc9905 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityBaseVO.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +/** + * 拼团活动 Base VO,提供给添加、修改、详细的子 VO 使用 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 + */ +@Data +public class CombinationActivityBaseVO { + + @Schema(description = "拼团名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "越拼越省钱") + @NotNull(message = "拼团名称不能为空") + private String name; + + @Schema(description = "商品 SPU 编号关联 ProductSpuDO 的 id", example = "[1,2,3]") + @NotNull(message = "拼团商品不能为空") + private Long spuId; + + @Schema(description = "总限购数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "16218") + @NotNull(message = "总限购数量不能为空") + private Integer totalLimitCount; + + @Schema(description = "单次限购数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "28265") + @NotNull(message = "单次限购数量不能为空") + private Integer singleLimitCount; + + @Schema(description = "活动时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "[2022-07-01 00:00:00,2022-07-01 23:59:59]") + @NotNull(message = "活动时间不能为空") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] activityTime; + + @Schema(description = "开团人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "25222") + @NotNull(message = "开团人数不能为空") + private Integer userSize; + + @Schema(description = "限制时长(小时)", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "限制时长(小时)不能为空") + private Integer limitDuration; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityCreateReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityCreateReqVO.java new file mode 100644 index 000000000..ea7879e4e --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityCreateReqVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity; + +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductCreateReqVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.Valid; +import java.util.List; + +@Schema(description = "管理后台 - 拼团活动创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CombinationActivityCreateReqVO extends CombinationActivityBaseVO { + + @Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED) + @Valid + private List products; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityExcelVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityExcelVO.java new file mode 100644 index 000000000..4ca035c9d --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityExcelVO.java @@ -0,0 +1,65 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Data; + +import java.time.LocalDateTime; + + +/** + * 拼团活动 Excel VO + * + * @author HUIHUI + */ +@Data +public class CombinationActivityExcelVO { + + @ExcelProperty("活动编号") + private Long id; + + @ExcelProperty("拼团名称") + private String name; + + @ExcelProperty("商品 SPU 编号关联 ProductSpuDO 的 id") + private Long spuId; + + @ExcelProperty("总限购数量") + private Integer totalLimitCount; + + @ExcelProperty("单次限购数量") + private Integer singleLimitCount; + + @ExcelProperty("开始时间") + private LocalDateTime startTime; + + @ExcelProperty("结束时间") + private LocalDateTime endTime; + + @ExcelProperty("开团人数") + private Integer userSize; + + @ExcelProperty("开团组数") + private Integer totalNum; + + @ExcelProperty("成团组数") + private Integer successNum; + + @ExcelProperty("参与人数") + private Integer orderUserCount; + + @ExcelProperty("虚拟成团") + private Integer virtualGroup; + + @ExcelProperty(value = "活动状态:0开启 1关闭", converter = DictConvert.class) + @DictFormat("common_status") // TODO 代码优化:建议设置到对应的 XXXDictTypeConstants 枚举类中 + private Integer status; + + @ExcelProperty("限制时长(小时)") + private Integer limitDuration; + + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityExportReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityExportReqVO.java new file mode 100644 index 000000000..99f73cfa2 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityExportReqVO.java @@ -0,0 +1,60 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 拼团活动 Excel 导出 Request VO,参数和 CombinationActivityPageReqVO 是一致的") +@Data +public class CombinationActivityExportReqVO { + + @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; + + @Schema(description = "结束时间") + @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; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityPageReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityPageReqVO.java new file mode 100644 index 000000000..bcd06fdc8 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityPageReqVO.java @@ -0,0 +1,65 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 拼团活动分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +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; + + @Schema(description = "结束时间") + @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; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityRespVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityRespVO.java new file mode 100644 index 000000000..106021264 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityRespVO.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity; + +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductRespVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - 拼团活动 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CombinationActivityRespVO extends CombinationActivityBaseVO { + + @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 = "开团人数不能为空") + private Integer userSize; + + @Schema(description = "开团组数", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "开团组数不能为空") + private Integer totalNum; + + @Schema(description = "成团组数", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "成团组数不能为空") + private Integer successNum; + + @Schema(description = "虚拟成团", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "虚拟成团不能为空") + private Integer virtualGroup; + + @Schema(description = "活动状态:0开启 1关闭", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + @NotNull(message = "活动状态不能为空") + private Integer status; + + @Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED) + @Valid + private List products; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityUpdateReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityUpdateReqVO.java new file mode 100644 index 000000000..c4f8cf0c3 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityUpdateReqVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity; + +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductUpdateReqVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.util.List; + +@Schema(description = "管理后台 - 拼团活动更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CombinationActivityUpdateReqVO extends CombinationActivityBaseVO { + + @Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22901") + @NotNull(message = "活动编号不能为空") + private Long id; + + @Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED) + @Valid + private List products; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductBaseVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductBaseVO.java new file mode 100644 index 000000000..fac9cddd0 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductBaseVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +/** + * 拼团商品 Base VO,提供给添加、修改、详细的子 VO 使用 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 + */ +@Data +public class CombinationProductBaseVO { + + @Schema(description = "商品 spuId", requiredMode = Schema.RequiredMode.REQUIRED, example = "30563") + @NotNull(message = "商品 spuId 不能为空") + private Long spuId; + + @Schema(description = "商品 skuId", requiredMode = Schema.RequiredMode.REQUIRED, example = "30563") + @NotNull(message = "商品 skuId 不能为空") + private Long skuId; + + @Schema(description = "拼团价格,单位分", requiredMode = Schema.RequiredMode.REQUIRED, example = "27682") + @NotNull(message = "拼团价格,单位分不能为空") + private Integer activePrice; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductCreateReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductCreateReqVO.java new file mode 100644 index 000000000..ee805bccc --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductCreateReqVO.java @@ -0,0 +1,14 @@ +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 { + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductExcelVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductExcelVO.java new file mode 100644 index 000000000..831b7cfec --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductExcelVO.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product; + +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 拼团商品 Excel VO + * + * @author HUIHUI + */ +@Data +public class CombinationProductExcelVO { + + @ExcelProperty("编号") + private Long id; + + @ExcelProperty("拼团活动编号") + private Long activityId; + + @ExcelProperty("商品 SPU 编号") + private Long spuId; + + @ExcelProperty("商品 SKU 编号") + private Long skuId; + + @ExcelProperty("拼团商品状态") + private Integer activityStatus; + + @ExcelProperty("活动开始时间点") + private LocalDateTime activityStartTime; + + @ExcelProperty("活动结束时间点") + private LocalDateTime activityEndTime; + + @ExcelProperty("拼团价格,单位分") + private Integer activePrice; + + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductExportReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductExportReqVO.java new file mode 100644 index 000000000..45cdd49bb --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductExportReqVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 拼团商品 Excel 导出 Request VO,参数和 CombinationProductPageReqVO 是一致的") +@Data +public class CombinationProductExportReqVO { + + @Schema(description = "拼团活动编号", example = "6829") + private Long activityId; + + @Schema(description = "商品 SPU 编号", example = "18731") + private Long spuId; + + @Schema(description = "商品 SKU 编号", example = "31675") + private Long skuId; + + @Schema(description = "拼团商品状态", example = "2") + private Integer activityStatus; + + @Schema(description = "活动开始时间点") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] activityStartTime; + + @Schema(description = "活动结束时间点") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] activityEndTime; + + @Schema(description = "拼团价格,单位分", example = "27682") + private Integer activePrice; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductPageReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductPageReqVO.java new file mode 100644 index 000000000..02bcc070c --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductPageReqVO.java @@ -0,0 +1,47 @@ +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 拼团商品分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CombinationProductPageReqVO extends PageParam { + + @Schema(description = "拼团活动编号", example = "6829") + private Long activityId; + + @Schema(description = "商品 SPU 编号", example = "18731") + private Long spuId; + + @Schema(description = "商品 SKU 编号", example = "31675") + private Long skuId; + + @Schema(description = "拼团商品状态", example = "2") + private Integer activityStatus; + + @Schema(description = "活动开始时间点") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] activityStartTime; + + @Schema(description = "活动结束时间点") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] activityEndTime; + + @Schema(description = "拼团价格,单位分", example = "27682") + private Integer activePrice; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductRespVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductRespVO.java new file mode 100644 index 000000000..eeac5c3b5 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductRespVO.java @@ -0,0 +1,22 @@ +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; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 拼团商品 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CombinationProductRespVO extends CombinationProductBaseVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28322") + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductUpdateReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductUpdateReqVO.java new file mode 100644 index 000000000..5fab798da --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductUpdateReqVO.java @@ -0,0 +1,14 @@ +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 { + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/SeckillActivityController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/SeckillActivityController.java index ba750b296..0631bfc11 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/SeckillActivityController.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/SeckillActivityController.java @@ -2,6 +2,9 @@ package cn.iocoder.yudao.module.promotion.controller.admin.seckill; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +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.seckill.vo.activity.*; import cn.iocoder.yudao.module.promotion.convert.seckill.seckillactivity.SeckillActivityConvert; import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillActivityDO; @@ -18,6 +21,7 @@ import javax.annotation.Resource; import javax.validation.Valid; import java.util.Collection; import java.util.List; +import java.util.Set; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @@ -29,6 +33,8 @@ public class SeckillActivityController { @Resource private SeckillActivityService seckillActivityService; + @Resource + private ProductSpuApi spuApi; @PostMapping("/create") @Operation(summary = "创建秒杀活动") @@ -69,11 +75,8 @@ public class SeckillActivityController { @PreAuthorize("@ss.hasPermission('promotion:seckill-activity:query')") public CommonResult getSeckillActivity(@RequestParam("id") Long id) { SeckillActivityDO seckillActivity = seckillActivityService.getSeckillActivity(id); - if (seckillActivity == null) { - return success(null); - } - List seckillProducts = seckillActivityService.getSeckillProductListByActivityId(id); - return success(SeckillActivityConvert.INSTANCE.convert(seckillActivity,seckillProducts)); + List seckillProducts = seckillActivityService.getSeckillProductListByActivityId(id); + return success(SeckillActivityConvert.INSTANCE.convert(seckillActivity, seckillProducts)); } @GetMapping("/list") @@ -90,7 +93,11 @@ public class SeckillActivityController { @PreAuthorize("@ss.hasPermission('promotion:seckill-activity:query')") public CommonResult> getSeckillActivityPage(@Valid SeckillActivityPageReqVO pageVO) { PageResult pageResult = seckillActivityService.getSeckillActivityPage(pageVO); - return success(SeckillActivityConvert.INSTANCE.convertPage(pageResult)); + Set aIds = CollectionUtils.convertSet(pageResult.getList(), SeckillActivityDO::getId); + List seckillProducts = seckillActivityService.getSeckillProductListByActivityId(aIds); + Set spuIds = CollectionUtils.convertSet(pageResult.getList(), SeckillActivityDO::getSpuId); + List spuList = spuApi.getSpuList(spuIds); + return success(SeckillActivityConvert.INSTANCE.convertPage(pageResult, seckillProducts, spuList)); } } diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/SeckillConfigController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/SeckillConfigController.java index 628c6d1c2..671b1f61b 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/SeckillConfigController.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/SeckillConfigController.java @@ -19,11 +19,6 @@ import java.util.List; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -/** - * 管理后台 - 秒杀时段相关接口 - * - * @author HUIHUI - */ @Tag(name = "管理后台 - 秒杀时段") @RestController @RequestMapping("/promotion/seckill-config") diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/activity/SeckillActivityBaseVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/activity/SeckillActivityBaseVO.java index ac1126d8c..504d4a057 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/activity/SeckillActivityBaseVO.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/activity/SeckillActivityBaseVO.java @@ -20,10 +20,9 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_ @Data public class SeckillActivityBaseVO { - // TODO @puhui999:对应单 spuId 哈 @Schema(description = "秒杀活动商品id", requiredMode = Schema.RequiredMode.REQUIRED, example = "[121,1212]") @NotNull(message = "秒杀活动商品不能为空") - private List spuIds; + private Long spuId; @Schema(description = "秒杀活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "618大促") @NotNull(message = "秒杀活动名称不能为空") @@ -56,8 +55,4 @@ public class SeckillActivityBaseVO { @Schema(description = "单次限够数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "31683") private Integer singleLimitCount; - // TODO @puhui999:这个应该是计算出来的字段,只返回,create 和 update 不用哈 - @Schema(description = "秒杀总库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") - private Integer totalStock; - } diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/activity/SeckillActivityRespVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/activity/SeckillActivityRespVO.java index 64f5c301a..ad78a9464 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/activity/SeckillActivityRespVO.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/activity/SeckillActivityRespVO.java @@ -6,26 +6,46 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; +import java.time.LocalDateTime; import java.util.List; -/** - * 管理后台 - 秒杀活动 Response VO - * - * @author HUIHUI - */ @Schema(description = "管理后台 - 秒杀活动 Response VO") @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) public class SeckillActivityRespVO extends SeckillActivityBaseVO { + @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 = "秒杀活动id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Long id; @Schema(description = "秒杀商品", requiredMode = Schema.RequiredMode.REQUIRED) - private List products; // TODO puhui: 考虑是否去除 + private List products; @Schema(description = "活动状态 开启:0 禁用:1", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") private Integer status; + @Schema(description = "订单实付金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "22354") + private Integer totalPrice; + + @Schema(description = "秒杀库存", example = "10") + private Integer stock; + + @Schema(description = "秒杀总库存", example = "20") + private Integer totalStock; + + @Schema(description = "新增订单数", example = "20") + private Integer orderCount; + + @Schema(description = "付款人数", example = "20") + private Integer userCount; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + } diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/config/SeckillConfigBaseVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/config/SeckillConfigBaseVO.java index 27b69b2b1..78a1c51df 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/config/SeckillConfigBaseVO.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/config/SeckillConfigBaseVO.java @@ -1,9 +1,13 @@ package cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config; +import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import javax.validation.constraints.AssertTrue; import javax.validation.constraints.NotNull; +import java.time.LocalTime; +import java.util.List; /** * 秒杀时段 Base VO,提供给添加、修改、详细的子 VO 使用 @@ -26,12 +30,24 @@ public class SeckillConfigBaseVO { @NotNull(message = "结束时间点不能为空") private String endTime; - @Schema(description = "秒杀主图", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn") - @NotNull(message = "秒杀主图不能为空") - private String picUrl; + @Schema(description = "秒杀轮播图", requiredMode = Schema.RequiredMode.REQUIRED, example = "[https://www.iocoder.cn/xx.png]") + @NotNull(message = "秒杀轮播图不能为空") + private List sliderPicUrls; @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") @NotNull(message = "状态不能为空") private Integer status; + @AssertTrue(message = "秒杀时段开始时间和结束时间不能相等") + @JsonIgnore + public boolean isValidStartTimeValid() { + return !LocalTime.parse(startTime).equals(LocalTime.parse(endTime)); + } + + @AssertTrue(message = "秒杀时段开始时间不能在结束时间之后") + @JsonIgnore + public boolean isValidEndTimeValid() { + return !LocalTime.parse(startTime).isAfter(LocalTime.parse(endTime)); + } + } diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/config/SeckillConfigPageReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/config/SeckillConfigPageReqVO.java index e49a02837..92211385a 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/config/SeckillConfigPageReqVO.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/config/SeckillConfigPageReqVO.java @@ -6,12 +6,6 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; -// TODO @puhui:VO 上不写注释,已经有注解啦。 -/** - * 管理后台 - 秒杀时段分页 Request VO - * - * @author HUIHUI - */ @Schema(description = "管理后台 - 秒杀时段分页 Request VO") @Data @EqualsAndHashCode(callSuper = true) diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/product/SeckillProductBaseVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/product/SeckillProductBaseVO.java index 4dba74fe1..a11d78930 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/product/SeckillProductBaseVO.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/product/SeckillProductBaseVO.java @@ -14,11 +14,6 @@ import javax.validation.constraints.NotNull; @Data public class SeckillProductBaseVO { - // TODO @puhui:spuId 不用传递;因为一个秒杀活动只对应一个 SPU 哈; - @Schema(description = "商品spu_id", requiredMode = Schema.RequiredMode.REQUIRED, example = "30563") - @NotNull(message = "商品spu_id不能为空") - private Long spuId; - @Schema(description = "商品sku_id", requiredMode = Schema.RequiredMode.REQUIRED, example = "30563") @NotNull(message = "商品sku_id不能为空") private Long skuId; @@ -31,5 +26,4 @@ public class SeckillProductBaseVO { @NotNull(message = "秒杀库存不能为空") private Integer stock; - } diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/product/SeckillProductCreateReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/product/SeckillProductCreateReqVO.java index 9b06f98f6..8ee4884ea 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/product/SeckillProductCreateReqVO.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/product/SeckillProductCreateReqVO.java @@ -1,16 +1,9 @@ package cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product; -import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product.SeckillProductBaseVO; -import lombok.*; -import java.util.*; import io.swagger.v3.oas.annotations.media.Schema; -import javax.validation.constraints.*; - -/** - * 管理后台 - 秒杀参与商品创建 Request VO - * - * @author HUIHUI - */ +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; @Schema(description = "管理后台 - 秒杀参与商品创建 Request VO") @Data @EqualsAndHashCode(callSuper = true) diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/product/SeckillProductRespVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/product/SeckillProductRespVO.java index bb33fab58..96b7eec4c 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/product/SeckillProductRespVO.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/product/SeckillProductRespVO.java @@ -1,14 +1,12 @@ package cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + import java.time.LocalDateTime; -/** - * 管理后台 - 秒杀参与商品 Response VO - * - * @author HUIHUI - */ @Schema(description = "管理后台 - 秒杀参与商品 Response VO") @Data @EqualsAndHashCode(callSuper = true) diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/product/SeckillProductUpdateReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/product/SeckillProductUpdateReqVO.java index 3f964255a..5fdccf8d0 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/product/SeckillProductUpdateReqVO.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/product/SeckillProductUpdateReqVO.java @@ -1,22 +1,14 @@ package cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import javax.validation.constraints.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; -/** - * 管理后台 - 秒杀参与商品更新 Request VO - * - * @author HUIHUI - */ @Schema(description = "管理后台 - 秒杀参与商品更新 Request VO") @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) public class SeckillProductUpdateReqVO extends SeckillProductBaseVO { - @Schema(description = "秒杀参与商品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "256") - @NotNull(message = "秒杀参与商品编号不能为空") - private Long id; - } diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/combination/CombinationActivityConvert.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/combination/CombinationActivityConvert.java new file mode 100644 index 000000000..5cf99a18d --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/combination/CombinationActivityConvert.java @@ -0,0 +1,128 @@ +package cn.iocoder.yudao.module.promotion.convert.combination; + +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.spu.dto.ProductSpuRespDTO; +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO; +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityExcelVO; +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityRespVO; +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.combinationactivity.CombinationActivityDO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationProductDO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationRecordDO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.Named; +import org.mapstruct.factory.Mappers; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * 拼团活动 Convert + * + * @author HUIHUI + */ +@Mapper +public interface CombinationActivityConvert { + + CombinationActivityConvert INSTANCE = Mappers.getMapper(CombinationActivityConvert.class); + + @Mappings({ + @Mapping(target = "startTime", expression = "java(bean.getActivityTime()[0])"), + @Mapping(target = "endTime", expression = "java(bean.getActivityTime()[1])") + }) + CombinationActivityDO convert(CombinationActivityCreateReqVO bean); + + @Mappings({ + @Mapping(target = "startTime", expression = "java(bean.getActivityTime()[0])"), + @Mapping(target = "endTime", expression = "java(bean.getActivityTime()[1])") + }) + CombinationActivityDO convert(CombinationActivityUpdateReqVO bean); + + @Named("mergeTime") + default LocalDateTime[] mergeTime(LocalDateTime startTime, LocalDateTime endTime) { + // TODO 有点怪第一次这样写 hh + LocalDateTime[] localDateTime = new LocalDateTime[2]; + localDateTime[0] = startTime; + localDateTime[1] = endTime; + return localDateTime; + } + + @Mappings({ + @Mapping(target = "activityTime", expression = "java(mergeTime(bean.getStartTime(),bean.getEndTime()))") + }) + CombinationActivityRespVO convert(CombinationActivityDO bean); + + CombinationProductRespVO convert(CombinationProductDO bean); + + default CombinationActivityRespVO convert(CombinationActivityDO bean, List productDOs) { + CombinationActivityRespVO respVO = convert(bean); + respVO.setProducts(convertList2(productDOs)); + return respVO; + } + + List convertList(List list); + + PageResult convertPage(PageResult page); + + default PageResult convertPage(PageResult page, List productDOList, List spuList) { + Map spuMap = CollectionUtils.convertMap(spuList, ProductSpuRespDTO::getId, c -> c); + PageResult pageResult = convertPage(page); + pageResult.getList().forEach(item -> { + item.setSpuName(spuMap.get(item.getSpuId()).getName()); + item.setPicUrl(spuMap.get(item.getSpuId()).getPicUrl()); + item.setProducts(convertList2(productDOList)); + }); + return pageResult; + } + + List convertList2(List productDOs); + + List convertList02(List list); + + @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 convertList(CombinationActivityDO activityDO, List products) { + List list = new ArrayList<>(); + products.forEach(sku -> { + CombinationProductDO productDO = convert(activityDO, sku); + // TODO 状态设置 + productDO.setActivityStatus(CommonStatusEnum.ENABLE.getStatus()); + list.add(productDO); + }); + return list; + } + + default List convertList1(CombinationActivityDO activityDO, List vos, List productDOs) { + Map longMap = CollectionUtils.convertMap(productDOs, CombinationProductDO::getSkuId, CombinationProductDO::getId); + List list = new ArrayList<>(); + vos.forEach(sku -> { + CombinationProductDO productDO = convert(activityDO, sku); + productDO.setId(longMap.get(sku.getSkuId())); + productDO.setActivityStatus(CommonStatusEnum.ENABLE.getStatus()); + list.add(productDO); + }); + return list; + } + + CombinationRecordDO convert(CombinationRecordReqDTO reqDTO); + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/seckill/seckillactivity/SeckillActivityConvert.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/seckill/seckillactivity/SeckillActivityConvert.java index 7d62b4f2d..689ddb17c 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/seckill/seckillactivity/SeckillActivityConvert.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/seckill/seckillactivity/SeckillActivityConvert.java @@ -1,20 +1,26 @@ package cn.iocoder.yudao.module.promotion.convert.seckill.seckillactivity; -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.spu.dto.ProductSpuRespDTO; import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityCreateReqVO; import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityDetailRespVO; import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityRespVO; import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityUpdateReqVO; -import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product.SeckillProductCreateReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product.SeckillProductBaseVO; +import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product.SeckillProductRespVO; +import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product.SeckillProductUpdateReqVO; import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillActivityDO; import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillProductDO; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; import org.mapstruct.factory.Mappers; import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * 秒杀活动 Convert @@ -26,8 +32,6 @@ public interface SeckillActivityConvert { SeckillActivityConvert INSTANCE = Mappers.getMapper(SeckillActivityConvert.class); - SeckillProductDO convert(SeckillProductCreateReqVO product); - SeckillActivityDO convert(SeckillActivityCreateReqVO bean); SeckillActivityDO convert(SeckillActivityUpdateReqVO bean); @@ -38,53 +42,60 @@ public interface SeckillActivityConvert { PageResult convertPage(PageResult page); - SeckillActivityDetailRespVO convert(SeckillActivityDO seckillActivity, List seckillProducts); - - - /** - * 比较两个秒杀商品对象是否相等 - * - * @param productDO 数据库中的商品 - * @param productVO 前端传入的商品 - * @return 是否匹配 - */ - default boolean isEquals(SeckillProductDO productDO, SeckillProductCreateReqVO productVO) { - return ObjectUtil.equals(productDO.getSpuId(), 1) // TODO puhui:再看看 - && ObjectUtil.equals(productDO.getSkuId(), productVO.getSkuId()) - && ObjectUtil.equals(productDO.getSeckillPrice(), productVO.getSeckillPrice()); - //&& ObjectUtil.equals(productDO.getQuota(), productVO.getQuota()) - //&& ObjectUtil.equals(productDO.getLimitCount(), productVO.getLimitCount()); + default PageResult convertPage(PageResult page, List seckillProducts, List spuList) { + Map spuMap = CollectionUtils.convertMap(spuList, ProductSpuRespDTO::getId, c -> c); + PageResult pageResult = convertPage(page); + pageResult.getList().forEach(item -> { + item.setSpuName(spuMap.get(item.getSpuId()).getName()); + item.setPicUrl(spuMap.get(item.getSpuId()).getPicUrl()); + item.setProducts(convertList2(seckillProducts)); + }); + return pageResult; } - /** - * 比较两个秒杀商品对象是否相等 - * - * @param productDO 商品1 - * @param productVO 商品2 - * @return 是否匹配 - */ - default boolean isEquals(SeckillProductDO productDO, SeckillProductDO productVO) { - return ObjectUtil.equals(productDO.getSpuId(), productVO.getSpuId()) - && ObjectUtil.equals(productDO.getSkuId(), productVO.getSkuId()) - && ObjectUtil.equals(productDO.getSeckillPrice(), productVO.getSeckillPrice()); - //&& ObjectUtil.equals(productDO.getQuota(), productVO.getQuota()) - //&& ObjectUtil.equals(productDO.getLimitCount(), productVO.getLimitCount()); + SeckillActivityDetailRespVO convert1(SeckillActivityDO seckillActivity); + + default SeckillActivityDetailRespVO convert(SeckillActivityDO seckillActivity, List seckillProducts) { + SeckillActivityDetailRespVO respVO = convert1(seckillActivity); + respVO.setProducts(convertList2(seckillProducts)); + return respVO; } - default List convertList(SeckillActivityDO seckillActivity, List products) { + @Mappings({ + @Mapping(target = "id", ignore = true), + @Mapping(target = "activityId", source = "activityDO.id"), + @Mapping(target = "configIds", source = "activityDO.configIds"), + @Mapping(target = "spuId", source = "activityDO.spuId"), + @Mapping(target = "skuId", source = "vo.skuId"), + @Mapping(target = "seckillPrice", source = "vo.seckillPrice"), + @Mapping(target = "stock", source = "vo.stock"), + @Mapping(target = "activityStartTime", source = "activityDO.startTime"), + @Mapping(target = "activityEndTime", source = "activityDO.endTime") + }) + SeckillProductDO convert(SeckillActivityDO activityDO, SeckillProductBaseVO vo); + + default List convertList(SeckillActivityDO activityDO, List products) { List list = new ArrayList<>(); products.forEach(sku -> { - SeckillProductDO productDO = new SeckillProductDO(); - productDO.setActivityId(seckillActivity.getId()); - productDO.setConfigIds(seckillActivity.getConfigIds()); - productDO.setSpuId(sku.getSpuId()); - productDO.setSkuId(sku.getSkuId()); - productDO.setSeckillPrice(sku.getSeckillPrice()); - productDO.setStock(sku.getStock()); + SeckillProductDO productDO = convert(activityDO, sku); productDO.setActivityStatus(CommonStatusEnum.ENABLE.getStatus()); - productDO.setActivityStartTime(seckillActivity.getStartTime()); - productDO.setActivityEndTime(seckillActivity.getEndTime()); + list.add(productDO); }); return list; } + + default List convertList1(SeckillActivityDO activityDO, List vos, List productDOs) { + Map longMap = CollectionUtils.convertMap(productDOs, SeckillProductDO::getSkuId, SeckillProductDO::getId); + List list = new ArrayList<>(); + vos.forEach(sku -> { + SeckillProductDO productDO = convert(activityDO, sku); + productDO.setId(longMap.get(sku.getSkuId())); + productDO.setActivityStatus(CommonStatusEnum.ENABLE.getStatus()); + list.add(productDO); + }); + return list; + } + + List convertList2(List productDOs); + } diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/combinationactivity/CombinationActivityDO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/combinationactivity/CombinationActivityDO.java new file mode 100644 index 000000000..58e62d30b --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/combinationactivity/CombinationActivityDO.java @@ -0,0 +1,87 @@ +package cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.time.LocalDateTime; + +/** + * 拼团活动 DO + * + * @author HUIHUI + */ +@TableName("promotion_combination_activity") +@KeySequence("promotion_combination_activity_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CombinationActivityDO extends BaseDO { + + /** + * 活动编号 + */ + @TableId + private Long id; + /** + * 拼团名称 + */ + private String name; + /** + * 商品 SPU 编号关联 ProductSpuDO 的 id + */ + private Long spuId; + /** + * 总限购数量 + */ + private Integer totalLimitCount; + /** + * 单次限购数量 + */ + private Integer singleLimitCount; + /** + * 开始时间 + */ + private LocalDateTime startTime; + /** + * 结束时间 + */ + private LocalDateTime endTime; + /** + * 几人团 + */ + private Integer userSize; + /** + * 开团组数 + */ + private Integer totalNum; + /** + * 成团组数 + */ + private Integer successNum; + /** + * 参与人数 + */ + private Integer orderUserCount; + /** + * 虚拟成团 + */ + private Integer virtualGroup; + /** + * 活动状态:0开启 1关闭 + *

+ * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 限制时长(小时) + */ + private Integer limitDuration; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/combinationactivity/CombinationProductDO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/combinationactivity/CombinationProductDO.java new file mode 100644 index 000000000..5c4744ee9 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/combinationactivity/CombinationProductDO.java @@ -0,0 +1,60 @@ +package cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.time.LocalDateTime; + +/** + * 拼团商品 DO + * + * @author HUIHUI + */ +@TableName("promotion_combination_product") +@KeySequence("promotion_combination_product_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CombinationProductDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 拼团活动编号 + */ + private Long activityId; + /** + * 商品 SPU 编号 + */ + private Long spuId; + /** + * 商品 SKU 编号 + */ + private Long skuId; + /** + * 拼团商品状态 + */ + private Integer activityStatus; + /** + * 活动开始时间点 + */ + private LocalDateTime activityStartTime; + /** + * 活动结束时间点 + */ + private LocalDateTime activityEndTime; + /** + * 拼团价格,单位分 + */ + private Integer activePrice; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/combinationactivity/CombinationRecordDO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/combinationactivity/CombinationRecordDO.java new file mode 100644 index 000000000..842f4e9c9 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/combinationactivity/CombinationRecordDO.java @@ -0,0 +1,108 @@ +package cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.time.LocalDateTime; + +/** + * 拼团记录 DO + * + * @author HUIHUI + */ +@TableName("promotion_combination_record") +@KeySequence("promotion_combination_record_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CombinationRecordDO extends BaseDO { + + @TableId + private Long id; + /** + * 拼团活动编号 + */ + private Long activityId; + /** + * spu 编号 + */ + private Long spuId; + /** + * sku 编号 + */ + private Long skuId; + /** + * 用户编号 + */ + private Long userId; + /** + * 订单编号 + */ + private Long orderId; + /** + * 团长编号 + *

+ * 关联 {@link CombinationRecordDO#getUserId()} + */ + private Long headId; + /** + * 商品名字 + */ + private String spuName; + /** + * 商品图片 + */ + private String picUrl; + /** + * 拼团商品单价 + */ + private Integer combinationPrice; + /** + * 用户昵称 + */ + private String nickname; + /** + * 用户头像 + */ + private String avatar; + /** + * 开团状态 + * + * 关联 {@link CombinationRecordStatusEnum} + */ + private Integer status; + /** + * 是否虚拟成团 + */ + private Boolean virtualGroup; + /** + * 过期时间,单位:小时 + * 关联关联 {@link CombinationActivityDO#getLimitDuration()} + */ + private Integer expireTime; + /** + * 开始时间 (订单付款后开始的时间) + */ + private LocalDateTime startTime; + /** + * 结束时间(成团时间/失败时间) + */ + private LocalDateTime endTime; + /** + * 开团需要人数 + * 关联 {@link CombinationActivityDO#getUserSize()} + */ + private Integer userSize; + /** + * 已加入拼团人数 + */ + private Integer userCount; + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/package-info.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/package-info.java new file mode 100644 index 000000000..c8403140e --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.module.promotion.dal.dataobject.combination; \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/seckill/seckillactivity/SeckillActivityDO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/seckill/seckillactivity/SeckillActivityDO.java index be6663592..a8c7eb265 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/seckill/seckillactivity/SeckillActivityDO.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/seckill/seckillactivity/SeckillActivityDO.java @@ -34,8 +34,7 @@ public class SeckillActivityDO extends BaseDO { /** * 秒杀活动商品 */ - @TableField(typeHandler = LongListTypeHandler.class) - private List spuIds; + private Long spuId; /** * 秒杀活动名称 */ diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/seckill/seckillconfig/SeckillConfigDO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/seckill/seckillconfig/SeckillConfigDO.java index f8fe41a18..c741a9fb5 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/seckill/seckillconfig/SeckillConfigDO.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/seckill/seckillconfig/SeckillConfigDO.java @@ -3,22 +3,27 @@ package cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillconfig; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.*; + +import java.util.List; /** * 秒杀时段 DO * * @author 芋道源码 */ -@TableName("promotion_seckill_config") +@TableName(value = "promotion_seckill_config", autoResultMap = true) @KeySequence("promotion_seckill_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor public class SeckillConfigDO extends BaseDO { /** @@ -38,14 +43,14 @@ public class SeckillConfigDO extends BaseDO { * 结束时间点 */ private String endTime; - // TODO puhui999:应该是轮播图; private List sliderPicUrls; /** - * 秒杀主图 + * 秒杀轮播图 */ - private String picUrl; + @TableField(typeHandler = JacksonTypeHandler.class) + private List sliderPicUrls; /** * 状态 - * + *

* 枚举 {@link CommonStatusEnum 对应的类} */ private Integer status; diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/combinationactivity/CombinationActivityMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/combinationactivity/CombinationActivityMapper.java new file mode 100644 index 000000000..1053a6757 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/combinationactivity/CombinationActivityMapper.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityExportReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationActivityDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 拼团活动 Mapper + * + * @author HUIHUI + */ +@Mapper +public interface CombinationActivityMapper extends BaseMapperX { + + default PageResult selectPage(CombinationActivityPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(CombinationActivityDO::getName, reqVO.getName()) + .orderByDesc(CombinationActivityDO::getId)); + } + + default List selectList(CombinationActivityExportReqVO reqVO) { + return selectList(new LambdaQueryWrapperX() + .likeIfPresent(CombinationActivityDO::getName, reqVO.getName()) + .eqIfPresent(CombinationActivityDO::getStatus, reqVO.getStatus()) + .orderByDesc(CombinationActivityDO::getId)); + } + + default List selectListByStatus(Integer status) { + return selectList(CombinationActivityDO::getStatus, status); + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/combinationactivity/CombinationProductMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/combinationactivity/CombinationProductMapper.java new file mode 100644 index 000000000..ced6de6d4 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/combinationactivity/CombinationProductMapper.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductExportReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductPageReqVO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationProductDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * 拼团商品 Mapper + * + * @author HUIHUI + */ +@Mapper +public interface CombinationProductMapper extends BaseMapperX { + + default PageResult selectPage(CombinationProductPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(CombinationProductDO::getActivityId, reqVO.getActivityId()) + .eqIfPresent(CombinationProductDO::getSpuId, reqVO.getSpuId()) + .eqIfPresent(CombinationProductDO::getSkuId, reqVO.getSkuId()) + .eqIfPresent(CombinationProductDO::getActivityStatus, reqVO.getActivityStatus()) + .betweenIfPresent(CombinationProductDO::getActivityStartTime, reqVO.getActivityStartTime()) + .betweenIfPresent(CombinationProductDO::getActivityEndTime, reqVO.getActivityEndTime()) + .eqIfPresent(CombinationProductDO::getActivePrice, reqVO.getActivePrice()) + .betweenIfPresent(CombinationProductDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(CombinationProductDO::getId)); + } + + default List selectList(CombinationProductExportReqVO reqVO) { + return selectList(new LambdaQueryWrapperX() + .eqIfPresent(CombinationProductDO::getActivityId, reqVO.getActivityId()) + .eqIfPresent(CombinationProductDO::getSpuId, reqVO.getSpuId()) + .eqIfPresent(CombinationProductDO::getSkuId, reqVO.getSkuId()) + .eqIfPresent(CombinationProductDO::getActivityStatus, reqVO.getActivityStatus()) + .betweenIfPresent(CombinationProductDO::getActivityStartTime, reqVO.getActivityStartTime()) + .betweenIfPresent(CombinationProductDO::getActivityEndTime, reqVO.getActivityEndTime()) + .eqIfPresent(CombinationProductDO::getActivePrice, reqVO.getActivePrice()) + .betweenIfPresent(CombinationProductDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(CombinationProductDO::getId)); + } + + default List selectListByActivityIds(Collection ids) { + return selectList(CombinationProductDO::getActivityId, ids); + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/combinationactivity/CombinationRecordMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/combinationactivity/CombinationRecordMapper.java new file mode 100644 index 000000000..e121ae9d1 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/combinationactivity/CombinationRecordMapper.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationRecordDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 拼团记录 Mapper + * + * @author HUIHUI + */ +@Mapper +public interface CombinationRecordMapper extends BaseMapperX { + + default CombinationRecordDO selectRecord(Long userId, Long orderId) { + return selectOne(CombinationRecordDO::getUserId, userId, CombinationRecordDO::getOrderId, orderId); + } + + default List selectListByHeadIdAndStatus(Long headId, Integer status) { + return selectList(new LambdaQueryWrapperX().eq(CombinationRecordDO::getHeadId, headId) + .eq(CombinationRecordDO::getStatus, status)); + } + + default List selectListByStatus(Integer status) { + return selectList(new LambdaQueryWrapperX().eq(CombinationRecordDO::getStatus, status)); + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/package-info.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/package-info.java new file mode 100644 index 000000000..b63efd052 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.module.promotion.dal.mysql.combination; \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillProductMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillProductMapper.java index fd6d87bdf..625769901 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillProductMapper.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillProductMapper.java @@ -21,6 +21,10 @@ public interface SeckillProductMapper extends BaseMapperX { return selectList(SeckillProductDO::getActivityId, id); } + default List selectListByActivityId(Collection ids) { + return selectList(SeckillProductDO::getActivityId, ids); + } + default List selectListBySkuIds(Collection skuIds) { return selectList(SeckillProductDO::getSkuId, skuIds); } diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillconfig/SeckillConfigMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillconfig/SeckillConfigMapper.java index e94c61949..53da4b0bc 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillconfig/SeckillConfigMapper.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillconfig/SeckillConfigMapper.java @@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.Seck import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillconfig.SeckillConfigDO; import org.apache.ibatis.annotations.Mapper; +import java.util.List; + @Mapper public interface SeckillConfigMapper extends BaseMapperX { @@ -17,4 +19,9 @@ public interface SeckillConfigMapper extends BaseMapperX { .orderByDesc(SeckillConfigDO::getId)); } + default List selectListByStatus(Integer status) { + return selectList(SeckillConfigDO::getStatus, status); + } + + } diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityService.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityService.java new file mode 100644 index 000000000..0946d5956 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityService.java @@ -0,0 +1,121 @@ +package cn.iocoder.yudao.module.promotion.service.combination; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO; +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityExportReqVO; +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.dal.dataobject.combination.combinationactivity.CombinationActivityDO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationProductDO; + +import javax.validation.Valid; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.List; + +/** + * 拼团活动 Service 接口 + * + * @author HUIHUI + */ +public interface CombinationActivityService { + + /** + * 创建拼团活动 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createCombinationActivity(@Valid CombinationActivityCreateReqVO createReqVO); + + /** + * 更新拼团活动 + * + * @param updateReqVO 更新信息 + */ + void updateCombinationActivity(@Valid CombinationActivityUpdateReqVO updateReqVO); + + /** + * 删除拼团活动 + * + * @param id 编号 + */ + void deleteCombinationActivity(Long id); + + /** + * 获得拼团活动 + * + * @param id 编号 + * @return 拼团活动 + */ + CombinationActivityDO getCombinationActivity(Long id); + + /** + * 获得拼团活动列表 + * + * @param ids 编号 + * @return 拼团活动列表 + */ + List getCombinationActivityList(Collection ids); + + /** + * 获得拼团活动分页 + * + * @param pageReqVO 分页查询 + * @return 拼团活动分页 + */ + PageResult getCombinationActivityPage(CombinationActivityPageReqVO pageReqVO); + + /** + * 获得拼团活动列表, 用于 Excel 导出 + * + * @param exportReqVO 查询条件 + * @return 拼团活动列表 + */ + List getCombinationActivityList(CombinationActivityExportReqVO exportReqVO); + + /** + * 获得拼团活动商品列表 + * + * @param ids 拼团活动 ids + * @return 拼团活动的商品列表 + */ + List getProductsByActivityIds(Collection ids); + + /** + * 更新拼团状态 + * + * @param userId 用户编号 + * @param orderId 订单编号 + * @param status 状态 + */ + void updateRecordStatusByUserIdAndOrderId(Long userId, Long orderId, Integer status); + + /** + * 更新拼团状态和开始时间 + * + * @param userId 用户编号 + * @param orderId 订单编号 + * @param status 状态 + * @param startTime 开始时间 + * @return + */ + void updateRecordStatusAndStartTimeByUserIdAndOrderId(Long userId, Long orderId, Integer status, LocalDateTime startTime); + + /** + * 创建拼团记录 + * + * @param reqDTO 创建信息 + */ + void createRecord(CombinationRecordReqDTO reqDTO); + + /** + * 获得拼团状态 + * + * @param userId 用户编号 + * @param orderId 订单编号 + * @return 拼团状态 + */ + boolean validateRecordStatusIsSuccess(Long userId, Long orderId); +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityServiceImpl.java new file mode 100644 index 000000000..68886dabb --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityServiceImpl.java @@ -0,0 +1,281 @@ +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.CombinationRecordReqDTO; +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityExportReqVO; +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.combinationactivity.CombinationActivityDO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationProductDO; +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationRecordDO; +import cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity.CombinationActivityMapper; +import cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity.CombinationProductMapper; +import cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity.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.time.LocalDateTime; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS; +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.promotion.util.PromotionUtils.validateProductSkuExistence; + +/** + * 拼团活动 Service 实现类 + * + * @author HUIHUI + */ +@Service +@Validated +public class CombinationActivityServiceImpl implements CombinationActivityService { + + @Resource + private CombinationActivityMapper combinationActivityMapper; + @Resource + private CombinationRecordMapper recordMapper; + @Resource + private CombinationProductMapper combinationProductMapper; + @Resource + private ProductSpuApi productSpuApi; + @Resource + private ProductSkuApi productSkuApi; + + @Override + public Long createCombinationActivity(CombinationActivityCreateReqVO createReqVO) { + // 校验商品 SPU 是否存在是否参加的别的活动 + validateProductCombinationConflict(createReqVO.getSpuId(), null); + // 获取所选 spu下的所有 sku + List skus = productSkuApi.getSkuListBySpuId(CollectionUtil.newArrayList(createReqVO.getSpuId())); + // 校验商品 sku 是否存在 + validateProductSkuExistence(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 productDOs = CombinationActivityConvert.INSTANCE.convertList(activityDO, createReqVO.getProducts()); + combinationProductMapper.insertBatch(productDOs); + // 返回 + return activityDO.getId(); + } + + private void validateProductCombinationConflict(Long spuId, Long activityId) { + // 校验商品 spu 是否存在 + List spuList = productSpuApi.getSpuList(CollUtil.newArrayList(spuId)); + if (CollUtil.isEmpty(spuList)) { + throw exception(SPU_NOT_EXISTS); + } + // 查询所有开启的拼团活动 + List activityDOs = combinationActivityMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus()); + // 更新时排除自己 + if (activityId != null) { + activityDOs.removeIf(item -> ObjectUtil.equal(item.getId(), activityId)); + } + // 过滤出所有 spuIds 有交集的活动 + List doList = CollectionUtils.convertList(activityDOs, c -> c, s -> ObjectUtil.equal(s.getId(), spuId)); + if (CollUtil.isNotEmpty(doList)) { + throw exception(COMBINATION_ACTIVITY_SPU_CONFLICTS); + } + } + + @Override + 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 skus = productSkuApi.getSkuListBySpuId(CollectionUtil.newArrayList(updateReqVO.getSpuId())); + // 校验商品 sku 是否存在 + validateProductSkuExistence(skus, updateReqVO.getProducts(), CombinationProductUpdateReqVO::getSkuId); + + // 更新 + CombinationActivityDO updateObj = CombinationActivityConvert.INSTANCE.convert(updateReqVO); + combinationActivityMapper.updateById(updateObj); + // 更新商品 + updateCombinationProduct(updateObj, updateReqVO.getProducts()); + } + + /** + * 更新秒杀商品 + * TODO 更新商品要不要封装成通用方法 + * + * @param updateObj DO + * @param products 商品配置 + */ + private void updateCombinationProduct(CombinationActivityDO updateObj, List products) { + List combinationProductDOs = combinationProductMapper.selectListByActivityIds(CollUtil.newArrayList(updateObj.getId())); + // 数据库中的活动商品 + Set convertSet = CollectionUtils.convertSet(combinationProductDOs, CombinationProductDO::getSkuId); + // 前端传过来的活动商品 + Set convertSet1 = CollectionUtils.convertSet(products, CombinationProductUpdateReqVO::getSkuId); + // 删除后台存在的前端不存在的商品 + List d = CollectionUtils.filterList(convertSet, item -> !convertSet1.contains(item)); + if (CollUtil.isNotEmpty(d)) { + combinationProductMapper.deleteBatchIds(d); + } + // 前端存在的后端不存在的商品 + List c = CollectionUtils.filterList(convertSet1, item -> !convertSet.contains(item)); + if (CollUtil.isNotEmpty(c)) { + List vos = CollectionUtils.filterList(products, item -> c.contains(item.getSkuId())); + List productDOs = CombinationActivityConvert.INSTANCE.convertList(updateObj, vos); + combinationProductMapper.insertBatch(productDOs); + } + // 更新已存在的商品 + List u = CollectionUtils.filterList(convertSet1, convertSet::contains); + if (CollUtil.isNotEmpty(u)) { + List vos = CollectionUtils.filterList(products, item -> u.contains(item.getSkuId())); + List productDOs = CombinationActivityConvert.INSTANCE.convertList1(updateObj, vos, combinationProductDOs); + combinationProductMapper.updateBatch(productDOs); + } + } + + @Override + 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 getCombinationActivityList(Collection ids) { + return combinationActivityMapper.selectBatchIds(ids); + } + + @Override + public PageResult getCombinationActivityPage(CombinationActivityPageReqVO pageReqVO) { + return combinationActivityMapper.selectPage(pageReqVO); + } + + @Override + public List getCombinationActivityList(CombinationActivityExportReqVO exportReqVO) { + return combinationActivityMapper.selectList(exportReqVO); + } + + @Override + public List getProductsByActivityIds(Collection ids) { + return combinationProductMapper.selectListByActivityIds(ids); + } + + @Override + public void updateRecordStatusByUserIdAndOrderId(Long userId, Long orderId, Integer status) { + // 校验拼团是否存在 + CombinationRecordDO recordDO = validateCombinationRecord(userId, orderId); + + // 更新状态 + recordDO.setStatus(status); + recordMapper.updateById(recordDO); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateRecordStatusAndStartTimeByUserIdAndOrderId(Long userId, Long orderId, Integer status, LocalDateTime startTime) { + CombinationRecordDO recordDO = validateCombinationRecord(userId, orderId); + // 更新状态 + recordDO.setStatus(status); + // 更新开始时间 + recordDO.setStartTime(startTime); + recordMapper.updateById(recordDO); + + // 更新拼团参入人数 + List recordDOs = recordMapper.selectListByHeadIdAndStatus(recordDO.getHeadId(), status); + if (CollUtil.isNotEmpty(recordDOs)) { + recordDOs.forEach(item -> { + item.setUserCount(recordDOs.size()); + // 校验拼团是否满足要求 + if (recordDOs.size() >= recordDO.getUserSize()) { + item.setStatus(CombinationRecordStatusEnum.SUCCESS.getStatus()); + } + }); + } + recordMapper.updateBatch(recordDOs); + } + + private CombinationRecordDO validateCombinationRecord(Long userId, Long orderId) { + // 校验拼团是否存在 + CombinationRecordDO recordDO = recordMapper.selectRecord(userId, orderId); + if (recordDO == null) { + throw exception(COMBINATION_RECORD_NOT_EXISTS); + } + return recordDO; + } + + @Override + public void createRecord(CombinationRecordReqDTO reqDTO) { + // 校验拼团活动 + CombinationActivityDO activityDO = validateCombinationActivityExists(reqDTO.getActivityId()); + + CombinationRecordDO recordDO = CombinationActivityConvert.INSTANCE.convert(reqDTO); + recordDO.setVirtualGroup(false); + recordDO.setExpireTime(activityDO.getLimitDuration()); + recordDO.setUserSize(activityDO.getUserSize()); + recordMapper.insert(recordDO); + } + + @Override + public boolean validateRecordStatusIsSuccess(Long userId, Long orderId) { + CombinationRecordDO recordDO = validateCombinationRecord(userId, orderId); + return ObjectUtil.equal(recordDO.getStatus(), CombinationRecordStatusEnum.SUCCESS.getStatus()); + } + + /** + * APP 端获取开团记录 + * + * @return 开团记录 + */ + public List getRecordList() { + return recordMapper.selectListByStatus(CombinationRecordStatusEnum.ONGOING.getStatus()); + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/seckillactivity/SeckillActivityService.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/seckillactivity/SeckillActivityService.java index 1c3ed38fe..4096f0087 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/seckillactivity/SeckillActivityService.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/seckillactivity/SeckillActivityService.java @@ -79,4 +79,12 @@ public interface SeckillActivityService { */ List getSeckillProductListByActivityId(Long id); + /** + * 通过活动编号获取活动商品 + * + * @param ids 活动编号 + * @return 活动商品列表 + */ + List getSeckillProductListByActivityId(Collection ids); + } diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/seckillactivity/SeckillActivityServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/seckillactivity/SeckillActivityServiceImpl.java index e95449649..8221a2d46 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/seckillactivity/SeckillActivityServiceImpl.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/seckillactivity/SeckillActivityServiceImpl.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.promotion.service.seckill.seckillactivity; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -13,29 +12,28 @@ import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityCreateReqVO; import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityPageReqVO; import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityUpdateReqVO; -import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product.SeckillProductBaseVO; +import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product.SeckillProductCreateReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product.SeckillProductUpdateReqVO; 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.dal.mysql.seckill.seckillactivity.SeckillActivityMapper; import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity.SeckillProductMapper; -import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum; import cn.iocoder.yudao.module.promotion.service.seckill.seckillconfig.SeckillConfigService; import cn.iocoder.yudao.module.promotion.util.PromotionUtils; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; import java.util.Collection; import java.util.List; import java.util.Set; -import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -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.Arrays.asList; +import static cn.iocoder.yudao.module.promotion.util.PromotionUtils.validateProductSkuExistence; /** * 秒杀活动 Service 实现类 @@ -58,15 +56,19 @@ public class SeckillActivityServiceImpl implements SeckillActivityService { private ProductSkuApi productSkuApi; @Override + @Transactional(rollbackFor = Exception.class) public Long createSeckillActivity(SeckillActivityCreateReqVO createReqVO) { // 校验商品秒秒杀时段是否冲突 - validateProductSpuSeckillConflict(createReqVO.getConfigIds(), createReqVO.getSpuIds()); + validateProductSpuSeckillConflict(createReqVO.getConfigIds(), createReqVO.getSpuId(), null); + // 获取所选 spu下的所有 sku + List skus = productSkuApi.getSkuListBySpuId(CollUtil.newArrayList(createReqVO.getSpuId())); // 校验商品 sku 是否存在 - validateProductSkuExistence(createReqVO.getSpuIds(), createReqVO.getProducts()); + validateProductSkuExistence(skus, createReqVO.getProducts(), SeckillProductCreateReqVO::getSkuId); // 插入秒杀活动 SeckillActivityDO activity = SeckillActivityConvert.INSTANCE.convert(createReqVO) - .setStatus(PromotionUtils.calculateActivityStatus(createReqVO.getEndTime())); + .setStatus(PromotionUtils.calculateActivityStatus(createReqVO.getEndTime())) + .setTotalStock(CollectionUtils.getSumValue(createReqVO.getProducts(), SeckillProductCreateReqVO::getStock, Integer::sum)); seckillActivityMapper.insert(activity); // 插入商品 List product = SeckillActivityConvert.INSTANCE.convertList(activity, createReqVO.getProducts()); @@ -74,139 +76,124 @@ public class SeckillActivityServiceImpl implements SeckillActivityService { return activity.getId(); } - private void validateProductSkuExistence(List spuIds, List products) { - // 校验 spu 个数是否相等 - // TODO @puhui999:不用校验 SPU 哈,只校验 sku 对应的 spuId 是否一致; - Set convertedSpuIds = CollectionUtils.convertSet(products, T::getSpuId); - if (ObjectUtil.notEqual(spuIds.size(), convertedSpuIds.size())) { - throw exception(SKU_NOT_EXISTS); - } - // 获取所选 spu下的所有 sku - // TODO @puhui999:变量可以简单一点;skus - List skuRespDTOs = productSkuApi.getSkuListBySpuId(spuIds); - // 校验 sku 个数是否一致 - Set skuIdsSet = CollectionUtils.convertSet(products, T::getSkuId); - Set skuIdsSet1 = CollectionUtils.convertSet(skuRespDTOs, ProductSkuRespDTO::getId); - if (ObjectUtil.notEqual(skuIdsSet.size(), skuIdsSet1.size())) { - throw exception(SKU_NOT_EXISTS); - } - // 校验 skuId 是否存在 - if (!skuIdsSet1.containsAll(skuIdsSet) || !skuIdsSet.containsAll(skuIdsSet1)) { - throw exception(SKU_NOT_EXISTS); - } - } - - private void validateProductSpuSeckillConflict(List configIds, List spuIds) { + private void validateProductSpuSeckillConflict(List configIds, Long spuId, Long activityId) { // 校验秒杀时段是否存在 seckillConfigService.validateSeckillConfigExists(configIds); // 校验商品 spu 是否存在 - List spuList = productSpuApi.getSpuList(spuIds); - if (ObjectUtil.notEqual(spuIds.size(), spuList.size())) { + List spuList = productSpuApi.getSpuList(CollUtil.newArrayList(spuId)); + if (CollUtil.isEmpty(spuList)) { throw exception(SPU_NOT_EXISTS); } // 查询所有开启的秒杀活动 List activityDOs = seckillActivityMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus()); - // 过滤出所有 spuIds 有交集的活动 - List doList = activityDOs.stream().filter(s -> { - // 判断 spu 是否有交集 - List spuIdsClone = ArrayUtil.clone(s.getSpuIds()); - spuIdsClone.retainAll(spuIds); - if (CollUtil.isEmpty(spuIdsClone)) { - return false; - } + if (activityId != null) { + // 更新时移除本活动 + activityDOs.removeIf(item -> ObjectUtil.equal(item.getId(), activityId)); + } + // 过滤出所有 spuId 有交集的活动 + List activityDOs1 = CollectionUtils.convertList(activityDOs, c -> c, s -> ObjectUtil.equal(s.getSpuId(), spuId)); + if (CollUtil.isNotEmpty(activityDOs1)) { + throw exception(SECKILL_ACTIVITY_SPU_CONFLICTS); + } + List activityDOs2 = CollectionUtils.convertList(activityDOs, c -> c, s -> { // 判断秒杀时段是否有交集 - List configIdsClone = ArrayUtil.clone(s.getConfigIds()); + List configIdsClone = CollUtil.newArrayList(s.getConfigIds()); configIdsClone.retainAll(configIds); return CollUtil.isNotEmpty(configIdsClone); - }).collect(Collectors.toList()); - if (CollUtil.isNotEmpty(doList)) { - throw exception(SECKILL_ACTIVITY_SPU_CONFLICTS); + }); + if (CollUtil.isNotEmpty(activityDOs2)) { + throw exception(SECKILL_TIME_CONFLICTS); } } @Override + @Transactional(rollbackFor = Exception.class) public void updateSeckillActivity(SeckillActivityUpdateReqVO updateReqVO) { // 校验存在 SeckillActivityDO seckillActivity = validateSeckillActivityExists(updateReqVO.getId()); - if (CommonStatusEnum.ENABLE.getStatus().equals(seckillActivity.getStatus())) { + if (CommonStatusEnum.DISABLE.getStatus().equals(seckillActivity.getStatus())) { throw exception(SECKILL_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED); } // 校验商品是否冲突 - validateProductSpuSeckillConflict(updateReqVO.getConfigIds(), updateReqVO.getSpuIds()); + validateProductSpuSeckillConflict(updateReqVO.getConfigIds(), updateReqVO.getSpuId(), updateReqVO.getId()); + // 获取所选 spu下的所有 sku + List skus = productSkuApi.getSkuListBySpuId(CollUtil.newArrayList(updateReqVO.getSpuId())); // 校验商品 sku 是否存在 - validateProductSkuExistence(updateReqVO.getSpuIds(), updateReqVO.getProducts()); + validateProductSkuExistence(skus, updateReqVO.getProducts(), SeckillProductUpdateReqVO::getSkuId); // 更新活动 SeckillActivityDO updateObj = SeckillActivityConvert.INSTANCE.convert(updateReqVO) - .setStatus(PromotionUtils.calculateActivityStatus(updateReqVO.getEndTime())); + .setStatus(PromotionUtils.calculateActivityStatus(updateReqVO.getEndTime())) + .setTotalStock(CollectionUtils.getSumValue(updateReqVO.getProducts(), SeckillProductUpdateReqVO::getStock, Integer::sum)); seckillActivityMapper.updateById(updateObj); // 更新商品 - //updateSeckillProduct(updateReqVO); + updateSeckillProduct(updateObj, updateReqVO.getProducts()); } /** * 更新秒杀商品 - * 后台查出的数据和前台查出的数据进行遍历, - * 1. 对前台数据进行遍历:如果不存在于后台的 sku 中需要新增 - * 2. 对后台数据进行遍历:如果不存在于前台的 sku 中需要删除 - * 3. 最后对当前活动商品全部更新,更新秒杀时段id列表 * - * @param updateReqVO 更新的请求VO + * @param updateObj DO + * @param products 商品配置 */ - private void updateSeckillProduct(SeckillActivityUpdateReqVO updateReqVO) { - // TODO puhui999:要不这里简单一点;删除原本的,插入新增的;不做的这么细致 - // TODO puhui999:后续完善 - //List seckillProductDOs = seckillProductMapper.selectListByActivityId(updateReqVO.getId()); - //List products = updateReqVO.getProducts(); - - ////计算需要删除的数据 - //List deleteIds = CollectionUtils.convertList(seckillProductDOs, SeckillProductDO::getId, - // seckillProductDO -> products.stream() - // .noneMatch(product -> SeckillActivityConvert.INSTANCE.isEquals(seckillProductDO, product))); - //if (CollUtil.isNotEmpty(deleteIds)) { - // seckillProductMapper.deleteBatchIds(deleteIds); - //} - // - //// 计算需要新增的数据 - //List newSeckillProductDOs = CollectionUtils.convertList(products, - // product -> SeckillActivityConvert.INSTANCE.convert(product).setActivityId(updateReqVO.getId())); - //newSeckillProductDOs.removeIf(product -> seckillProductDOs.stream() - // .anyMatch(seckillProduct -> SeckillActivityConvert.INSTANCE.isEquals(seckillProduct, product))); - //if (CollUtil.isNotEmpty(newSeckillProductDOs)) { - // seckillProductMapper.insertBatch(newSeckillProductDOs); - //} - - //全量更新当前活动商品的秒杀时段id列表(timeIds) - seckillProductMapper.updateTimeIdsByActivityId(updateReqVO.getId(), updateReqVO.getConfigIds()); + private void updateSeckillProduct(SeckillActivityDO updateObj, List products) { + List seckillProductDOs = seckillProductMapper.selectListByActivityId(updateObj.getId()); + // 数据库中的活动商品 + Set convertSet = CollectionUtils.convertSet(seckillProductDOs, SeckillProductDO::getSkuId); + // 前端传过来的活动商品 + Set convertSet1 = CollectionUtils.convertSet(products, SeckillProductUpdateReqVO::getSkuId); + // 删除后台存在的前端不存在的商品 + List d = CollectionUtils.filterList(convertSet, item -> !convertSet1.contains(item)); + if (CollUtil.isNotEmpty(d)) { + seckillProductMapper.deleteBatchIds(d); + } + // 前端存在的后端不存在的商品 + List c = CollectionUtils.filterList(convertSet1, item -> !convertSet.contains(item)); + if (CollUtil.isNotEmpty(c)) { + List vos = CollectionUtils.filterList(products, item -> c.contains(item.getSkuId())); + List productDOs = SeckillActivityConvert.INSTANCE.convertList(updateObj, vos); + seckillProductMapper.insertBatch(productDOs); + } + // 更新已存在的商品 + List u = CollectionUtils.filterList(convertSet1, convertSet::contains); + if (CollUtil.isNotEmpty(u)) { + List vos = CollectionUtils.filterList(products, item -> u.contains(item.getSkuId())); + List productDOs = SeckillActivityConvert.INSTANCE.convertList1(updateObj, vos, seckillProductDOs); + seckillProductMapper.updateBatch(productDOs); + } } @Override + @Transactional(rollbackFor = Exception.class) public void closeSeckillActivity(Long id) { + // TODO 待验证没使用过 // 校验存在 - SeckillActivityDO seckillActivity = this.validateSeckillActivityExists(id); - if (PromotionActivityStatusEnum.CLOSE.getStatus().equals(seckillActivity.getStatus())) { + SeckillActivityDO seckillActivity = validateSeckillActivityExists(id); + if (CommonStatusEnum.DISABLE.getStatus().equals(seckillActivity.getStatus())) { throw exception(SECKILL_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED); } - if (PromotionActivityStatusEnum.END.getStatus().equals(seckillActivity.getStatus())) { - throw exception(SECKILL_ACTIVITY_CLOSE_FAIL_STATUS_END); - } + // 更新 - SeckillActivityDO updateObj = new SeckillActivityDO().setId(id).setStatus(PromotionActivityStatusEnum.CLOSE.getStatus()); + SeckillActivityDO updateObj = new SeckillActivityDO().setId(id).setStatus(CommonStatusEnum.DISABLE.getStatus()); seckillActivityMapper.updateById(updateObj); } @Override + @Transactional(rollbackFor = Exception.class) public void deleteSeckillActivity(Long id) { // 校验存在 SeckillActivityDO seckillActivity = this.validateSeckillActivityExists(id); - List statuses = asList(PromotionActivityStatusEnum.CLOSE.getStatus(), PromotionActivityStatusEnum.END.getStatus()); - if (!statuses.contains(seckillActivity.getStatus())) { + if (CommonStatusEnum.ENABLE.getStatus().equals(seckillActivity.getStatus())) { throw exception(SECKILL_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END); } - // 删除 + // 删除活动 seckillActivityMapper.deleteById(id); + // 删除活动商品 + List productDOs = seckillProductMapper.selectListByActivityId(id); + Set convertSet = CollectionUtils.convertSet(productDOs, SeckillProductDO::getSkuId); + seckillProductMapper.deleteBatchIds(convertSet); } private SeckillActivityDO validateSeckillActivityExists(Long id) { @@ -219,7 +206,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService { @Override public SeckillActivityDO getSeckillActivity(Long id) { - return seckillActivityMapper.selectById(id); + return validateSeckillActivityExists(id); } @Override @@ -237,4 +224,9 @@ public class SeckillActivityServiceImpl implements SeckillActivityService { return seckillProductMapper.selectListByActivityId(id); } + @Override + public List getSeckillProductListByActivityId(Collection ids) { + return seckillProductMapper.selectListByActivityId(ids); + } + } diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/seckillconfig/SeckillConfigServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/seckillconfig/SeckillConfigServiceImpl.java index 599973d5b..630001247 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/seckillconfig/SeckillConfigServiceImpl.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/seckillconfig/SeckillConfigServiceImpl.java @@ -1,8 +1,11 @@ package cn.iocoder.yudao.module.promotion.service.seckill.seckillconfig; 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.framework.common.util.date.LocalDateTimeUtils; import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigCreateReqVO; import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigPageReqVO; import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigUpdateReqVO; @@ -10,14 +13,13 @@ import cn.iocoder.yudao.module.promotion.convert.seckill.seckillconfig.SeckillCo import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillconfig.SeckillConfigDO; import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillconfig.SeckillConfigMapper; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; import java.time.LocalTime; import java.util.Collection; import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; @@ -35,9 +37,10 @@ public class SeckillConfigServiceImpl implements SeckillConfigService { private SeckillConfigMapper seckillConfigMapper; @Override + @Transactional(rollbackFor = Exception.class) public Long createSeckillConfig(SeckillConfigCreateReqVO createReqVO) { // 校验时间段是否冲突 - validateSeckillConfigConflict(createReqVO.getStartTime(), createReqVO.getEndTime()); + validateSeckillConfigConflict(createReqVO.getStartTime(), createReqVO.getEndTime(), null); // 插入 SeckillConfigDO seckillConfig = SeckillConfigConvert.INSTANCE.convert(createReqVO); @@ -47,18 +50,30 @@ public class SeckillConfigServiceImpl implements SeckillConfigService { } @Override + @Transactional(rollbackFor = Exception.class) public void updateSeckillConfig(SeckillConfigUpdateReqVO updateReqVO) { // 校验存在 validateSeckillConfigExists(updateReqVO.getId()); // 校验时间段是否冲突 - validateSeckillConfigConflict(updateReqVO.getStartTime(), updateReqVO.getEndTime()); + validateSeckillConfigConflict(updateReqVO.getStartTime(), updateReqVO.getEndTime(), updateReqVO.getId()); // 更新 SeckillConfigDO updateObj = SeckillConfigConvert.INSTANCE.convert(updateReqVO); seckillConfigMapper.updateById(updateObj); } + // TODO @puhui999: 这个要不合并到更新操作里? 不单独有个操作咧; 更新状态不用那么多必须的参数,更新的时候需要校验时间段 @Override + public void updateSeckillConfigStatus(Long id, Integer status) { + // 校验秒杀时段是否存在 + validateSeckillConfigExists(id); + + // 更新状态 + seckillConfigMapper.updateById(new SeckillConfigDO().setId(id).setStatus(status)); + } + + @Override + @Transactional(rollbackFor = Exception.class) public void deleteSeckillConfig(Long id) { // 校验存在 validateSeckillConfigExists(id); @@ -79,39 +94,29 @@ public class SeckillConfigServiceImpl implements SeckillConfigService { * @param startTime 开始时间 * @param endTime 结束时间 */ - private void validateSeckillConfigConflict(String startTime, String endTime) { + private void validateSeckillConfigConflict(String startTime, String endTime, Long seckillConfigId) { LocalTime startTime1 = LocalTime.parse(startTime); LocalTime endTime1 = LocalTime.parse(endTime); - // TODO @puhui999: 这个可以用 validator 里的 assertTrue 去做哈; - // 检查选择的时间是否相等 - if (startTime1.equals(endTime1)) { - throw exception(SECKILL_TIME_EQUAL); - } - // 检查开始时间是否在结束时间之前 - if (startTime1.isAfter(endTime1)) { - throw exception(SECKILL_START_TIME_BEFORE_END_TIME); - } // 查询出所有的时段配置 List configDOs = seckillConfigMapper.selectList(); + // 更新时排除自己 + if (seckillConfigId != null) { + configDOs.removeIf(item -> ObjectUtil.equal(item.getId(), seckillConfigId)); + } // 过滤出重叠的时段 ids - // TODO @puhui999:感觉 findOne 就可以了? - Set ids = configDOs.stream().filter((config) -> { + boolean hasConflict = configDOs.stream().anyMatch(config -> { LocalTime startTime2 = LocalTime.parse(config.getStartTime()); LocalTime endTime2 = LocalTime.parse(config.getEndTime()); // 判断时间是否重叠 - // 开始时间在已配置时段的结束时间之前 且 结束时间在已配置时段的开始时间之后 [] - // todo @puhui999:LocalDateUtils 可以写个工具类?是否是有重叠的时间?感觉别的场景,可能也会有需要 - return startTime1.isBefore(endTime2) && endTime1.isAfter(startTime2) - // 开始时间在已配置时段的开始时间之前 且 结束时间在已配置时段的开始时间之后 (] 或 () - || startTime1.isBefore(startTime2) && endTime1.isAfter(startTime2) - // 开始时间在已配置时段的结束时间之前 且 结束时间在已配值时段的结束时间之后 [) 或 () - || startTime1.isBefore(endTime2) && endTime1.isAfter(endTime2); - }).map(SeckillConfigDO::getId).collect(Collectors.toSet()); - if (CollUtil.isNotEmpty(ids)) { + return LocalDateTimeUtils.checkTimeOverlap(startTime1, endTime1, startTime2, endTime2); + }); + + if (hasConflict) { throw exception(SECKILL_TIME_CONFLICTS); } } + @Override public SeckillConfigDO getSeckillConfig(Long id) { return seckillConfigMapper.selectById(id); @@ -127,10 +132,18 @@ public class SeckillConfigServiceImpl implements SeckillConfigService { if (CollUtil.isEmpty(configIds)) { throw exception(SECKILL_TIME_NOT_EXISTS); } - if (seckillConfigMapper.selectBatchIds(configIds).size() != configIds.size()) { + List configDOs = seckillConfigMapper.selectBatchIds(configIds); + if (CollUtil.isEmpty(configDOs)) { + throw exception(SECKILL_TIME_NOT_EXISTS); + } + // 过滤出关闭的时段 + List filterList = CollectionUtils.filterList(configDOs, item -> ObjectUtil.equal(item.getStatus(), CommonStatusEnum.DISABLE.getStatus())); + if (CollUtil.isNotEmpty(filterList)) { + throw exception(SECKILL_TIME_DISABLE); + } + if (configDOs.size() != configIds.size()) { throw exception(SECKILL_TIME_NOT_EXISTS); } - // TODO @puhui999:应该要校验个 status 哈;如果有禁用的,也不行 } @Override @@ -138,23 +151,9 @@ public class SeckillConfigServiceImpl implements SeckillConfigService { return seckillConfigMapper.selectPage(pageVO); } - // TODO @puhui999:写个查询状态的; 尽可能通用哈 @Override public List getListAllSimple() { - return seckillConfigMapper.selectList(SeckillConfigDO::getStatus, CommonStatusEnum.ENABLE.getStatus()); - } - - // TODO @puhui999: 这个要不合并到更新操作里? 不单独有个操作咧; - @Override - public void updateSeckillConfigStatus(Long id, Integer status) { - // 校验秒杀时段是否存在 - validateSeckillConfigExists(id); - - SeckillConfigDO seckillConfigDO = new SeckillConfigDO(); - seckillConfigDO.setId(id); - seckillConfigDO.setStatus(status); - // 更新状态 - seckillConfigMapper.updateById(seckillConfigDO); + return seckillConfigMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus()); } } diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/util/PromotionUtils.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/util/PromotionUtils.java index 2ad362fe2..fbefe10a5 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/util/PromotionUtils.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/util/PromotionUtils.java @@ -1,9 +1,18 @@ package cn.iocoder.yudao.module.promotion.util; +import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; import java.time.LocalDateTime; +import java.util.List; +import java.util.Set; +import java.util.function.Function; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS; /** * 活动工具类 @@ -22,4 +31,15 @@ public class PromotionUtils { return LocalDateTimeUtils.beforeNow(endTime) ? CommonStatusEnum.DISABLE.getStatus() : CommonStatusEnum.ENABLE.getStatus(); } + public static void validateProductSkuExistence(List skus, List products, Function func) { + // 校验 sku 个数是否一致 + Set skuIdsSet = CollectionUtils.convertSet(products, func); + Set skuIdsSet1 = CollectionUtils.convertSet(skus, ProductSkuRespDTO::getId); + // 校验 skuId 是否存在 + List f = CollectionUtils.filterList(skuIdsSet, s -> !skuIdsSet1.contains(s)); + if (CollUtil.isNotEmpty(f)) { + throw exception(SKU_NOT_EXISTS); + } + } + } diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityServiceImplTest.java b/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityServiceImplTest.java new file mode 100644 index 000000000..28fcbdea4 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityServiceImplTest.java @@ -0,0 +1,251 @@ +package cn.iocoder.yudao.module.promotion.service.combination; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO; +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityExportReqVO; +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.dal.dataobject.combination.combinationactivity.CombinationActivityDO; +import cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity.CombinationActivityMapper; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import javax.annotation.Resource; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COMBINATION_ACTIVITY_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link CombinationActivityServiceImpl} 的单元测试类 + * + * @author HUIHUI + */ +@Import(CombinationActivityServiceImpl.class) +public class CombinationActivityServiceImplTest extends BaseDbUnitTest { + + @Resource + private CombinationActivityServiceImpl combinationActivityService; + + @Resource + private CombinationActivityMapper combinationActivityMapper; + + @Test + public void testCreateCombinationActivity_success() { + // 准备参数 + CombinationActivityCreateReqVO reqVO = randomPojo(CombinationActivityCreateReqVO.class); + + // 调用 + Long combinationActivityId = combinationActivityService.createCombinationActivity(reqVO); + // 断言 + assertNotNull(combinationActivityId); + // 校验记录的属性是否正确 + CombinationActivityDO combinationActivity = combinationActivityMapper.selectById(combinationActivityId); + assertPojoEquals(reqVO, combinationActivity); + } + + @Test + public void testUpdateCombinationActivity_success() { + // mock 数据 + CombinationActivityDO dbCombinationActivity = randomPojo(CombinationActivityDO.class); + combinationActivityMapper.insert(dbCombinationActivity);// @Sql: 先插入出一条存在的数据 + // 准备参数 + CombinationActivityUpdateReqVO reqVO = randomPojo(CombinationActivityUpdateReqVO.class, o -> { + o.setId(dbCombinationActivity.getId()); // 设置更新的 ID + }); + + // 调用 + combinationActivityService.updateCombinationActivity(reqVO); + // 校验是否更新正确 + CombinationActivityDO combinationActivity = combinationActivityMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, combinationActivity); + } + + @Test + public void testUpdateCombinationActivity_notExists() { + // 准备参数 + CombinationActivityUpdateReqVO reqVO = randomPojo(CombinationActivityUpdateReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> combinationActivityService.updateCombinationActivity(reqVO), COMBINATION_ACTIVITY_NOT_EXISTS); + } + + @Test + public void testDeleteCombinationActivity_success() { + // mock 数据 + CombinationActivityDO dbCombinationActivity = randomPojo(CombinationActivityDO.class); + combinationActivityMapper.insert(dbCombinationActivity);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbCombinationActivity.getId(); + + // 调用 + combinationActivityService.deleteCombinationActivity(id); + // 校验数据不存在了 + assertNull(combinationActivityMapper.selectById(id)); + } + + @Test + public void testDeleteCombinationActivity_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> combinationActivityService.deleteCombinationActivity(id), COMBINATION_ACTIVITY_NOT_EXISTS); + } + + @Test + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 + public void testGetCombinationActivityPage() { + // mock 数据 + CombinationActivityDO dbCombinationActivity = randomPojo(CombinationActivityDO.class, o -> { // 等会查询到 + o.setName(null); + //o.setSpuId(null); + o.setTotalLimitCount(null); + o.setSingleLimitCount(null); + o.setStartTime(null); + o.setEndTime(null); + o.setUserSize(null); + o.setTotalNum(null); + o.setSuccessNum(null); + o.setOrderUserCount(null); + o.setVirtualGroup(null); + o.setStatus(null); + o.setLimitDuration(null); + o.setCreateTime(null); + }); + combinationActivityMapper.insert(dbCombinationActivity); + // 测试 name 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setName(null))); + // 测试 spuId 不匹配 + //combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setSpuId(null))); + // 测试 totalLimitCount 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setTotalLimitCount(null))); + // 测试 singleLimitCount 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setSingleLimitCount(null))); + // 测试 startTime 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setStartTime(null))); + // 测试 endTime 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setEndTime(null))); + // 测试 userSize 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setUserSize(null))); + // 测试 totalNum 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setTotalNum(null))); + // 测试 successNum 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setSuccessNum(null))); + // 测试 orderUserCount 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setOrderUserCount(null))); + // 测试 virtualGroup 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setVirtualGroup(null))); + // 测试 status 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setStatus(null))); + // 测试 limitDuration 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setLimitDuration(null))); + // 测试 createTime 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setCreateTime(null))); + // 准备参数 + CombinationActivityPageReqVO reqVO = new CombinationActivityPageReqVO(); + reqVO.setName(null); + reqVO.setSpuId(null); + reqVO.setTotalLimitCount(null); + reqVO.setSingleLimitCount(null); + reqVO.setStartTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); + reqVO.setEndTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); + reqVO.setUserSize(null); + reqVO.setTotalNum(null); + reqVO.setSuccessNum(null); + reqVO.setOrderUserCount(null); + reqVO.setVirtualGroup(null); + reqVO.setStatus(null); + reqVO.setLimitDuration(null); + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); + + // 调用 + PageResult pageResult = combinationActivityService.getCombinationActivityPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbCombinationActivity, pageResult.getList().get(0)); + } + + @Test + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 + public void testGetCombinationActivityList() { + // mock 数据 + CombinationActivityDO dbCombinationActivity = randomPojo(CombinationActivityDO.class, o -> { // 等会查询到 + o.setName(null); + //o.setSpuId(null); + o.setTotalLimitCount(null); + o.setSingleLimitCount(null); + o.setStartTime(null); + o.setEndTime(null); + o.setUserSize(null); + o.setTotalNum(null); + o.setSuccessNum(null); + o.setOrderUserCount(null); + o.setVirtualGroup(null); + o.setStatus(null); + o.setLimitDuration(null); + o.setCreateTime(null); + }); + combinationActivityMapper.insert(dbCombinationActivity); + // 测试 name 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setName(null))); + // 测试 spuId 不匹配 + //combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setSpuId(null))); + // 测试 totalLimitCount 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setTotalLimitCount(null))); + // 测试 singleLimitCount 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setSingleLimitCount(null))); + // 测试 startTime 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setStartTime(null))); + // 测试 endTime 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setEndTime(null))); + // 测试 userSize 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setUserSize(null))); + // 测试 totalNum 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setTotalNum(null))); + // 测试 successNum 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setSuccessNum(null))); + // 测试 orderUserCount 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setOrderUserCount(null))); + // 测试 virtualGroup 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setVirtualGroup(null))); + // 测试 status 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setStatus(null))); + // 测试 limitDuration 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setLimitDuration(null))); + // 测试 createTime 不匹配 + combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setCreateTime(null))); + // 准备参数 + CombinationActivityExportReqVO reqVO = new CombinationActivityExportReqVO(); + reqVO.setName(null); + reqVO.setSpuId(null); + reqVO.setTotalLimitCount(null); + reqVO.setSingleLimitCount(null); + reqVO.setStartTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); + reqVO.setEndTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); + reqVO.setUserSize(null); + reqVO.setTotalNum(null); + reqVO.setSuccessNum(null); + reqVO.setOrderUserCount(null); + reqVO.setVirtualGroup(null); + reqVO.setStatus(null); + reqVO.setLimitDuration(null); + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); + + // 调用 + List list = combinationActivityService.getCombinationActivityList(reqVO); + // 断言 + assertEquals(1, list.size()); + assertPojoEquals(dbCombinationActivity, list.get(0)); + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/test/resources/sql/clean.sql b/yudao-module-mall/yudao-module-promotion-biz/src/test/resources/sql/clean.sql index d848bf556..defacab26 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/test/resources/sql/clean.sql +++ b/yudao-module-mall/yudao-module-promotion-biz/src/test/resources/sql/clean.sql @@ -1,7 +1,16 @@ -DELETE FROM "market_activity"; -DELETE FROM "promotion_coupon_template"; -DELETE FROM "promotion_coupon"; -DELETE FROM "promotion_reward_activity"; -DELETE FROM "promotion_discount_activity"; -DELETE FROM "promotion_discount_product"; -DELETE FROM "promotion_seckill_config"; +DELETE +FROM "market_activity"; +DELETE +FROM "promotion_coupon_template"; +DELETE +FROM "promotion_coupon"; +DELETE +FROM "promotion_reward_activity"; +DELETE +FROM "promotion_discount_activity"; +DELETE +FROM "promotion_discount_product"; +DELETE +FROM "promotion_seckill_config"; +DELETE +FROM "promotion_combination_activity"; diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/test/resources/sql/create_tables.sql b/yudao-module-mall/yudao-module-promotion-biz/src/test/resources/sql/create_tables.sql index 33b6edc48..c7a3b56da 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/test/resources/sql/create_tables.sql +++ b/yudao-module-mall/yudao-module-promotion-biz/src/test/resources/sql/create_tables.sql @@ -1,151 +1,183 @@ -CREATE TABLE IF NOT EXISTS "market_activity" ( - "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "title" varchar(50) NOT NULL, - "activity_type" tinyint(4) NOT NULL, - "status" tinyint(4) NOT NULL, - "start_time" datetime NOT NULL, - "end_time" datetime NOT NULL, - "invalid_time" datetime, - "delete_time" datetime, +CREATE TABLE IF NOT EXISTS "market_activity" +( + "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "title" varchar(50) NOT NULL, + "activity_type" tinyint(4) NOT NULL, + "status" tinyint(4) NOT NULL, + "start_time" datetime NOT NULL, + "end_time" datetime NOT NULL, + "invalid_time" datetime, + "delete_time" datetime, "time_limited_discount" varchar(2000), - "full_privilege" varchar(2000), - "creator" varchar(64) DEFAULT '', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - "tenant_id" bigint(20) NOT NULL, + "full_privilege" varchar(2000), + "creator" varchar(64) DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint(20) NOT NULL, PRIMARY KEY ("id") - ) COMMENT '促销活动'; +) COMMENT '促销活动'; -CREATE TABLE IF NOT EXISTS "promotion_coupon_template" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar NOT NULL, - "status" int NOT NULL, - "total_count" int NOT NULL, - "take_limit_count" int NOT NULL, - "take_type" int NOT NULL, - "use_price" int NOT NULL, - "product_scope" int NOT NULL, - "product_spu_ids" varchar, - "validity_type" int NOT NULL, - "valid_start_time" datetime, - "valid_end_time" datetime, - "fixed_start_term" int, - "fixed_end_term" int, - "discount_type" int NOT NULL, - "discount_percent" int, - "discount_price" int, - "discount_limit_price" int, - "take_count" int NOT NULL DEFAULT 0, - "use_count" int NOT NULL DEFAULT 0, - "creator" varchar DEFAULT '', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar DEFAULT '', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") +CREATE TABLE IF NOT EXISTS "promotion_coupon_template" +( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar NOT NULL, + "status" int NOT NULL, + "total_count" int NOT NULL, + "take_limit_count" int NOT NULL, + "take_type" int NOT NULL, + "use_price" int NOT NULL, + "product_scope" int NOT NULL, + "product_spu_ids" varchar, + "validity_type" int NOT NULL, + "valid_start_time" datetime, + "valid_end_time" datetime, + "fixed_start_term" int, + "fixed_end_term" int, + "discount_type" int NOT NULL, + "discount_percent" int, + "discount_price" int, + "discount_limit_price" int, + "take_count" int NOT NULL DEFAULT 0, + "use_count" int NOT NULL DEFAULT 0, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") ) COMMENT '优惠劵模板'; -CREATE TABLE IF NOT EXISTS "promotion_coupon" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "template_id" bigint NOT NULL, - "name" varchar NOT NULL, - "status" int NOT NULL, - "user_id" bigint NOT NULL, - "take_type" int NOT NULL, - "useprice" int NOT NULL, - "valid_start_time" datetime NOT NULL, - "valid_end_time" datetime NOT NULL, - "product_scope" int NOT NULL, - "product_spu_ids" varchar, - "discount_type" int NOT NULL, - "discount_percent" int, - "discount_price" int, +CREATE TABLE IF NOT EXISTS "promotion_coupon" +( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "template_id" bigint NOT NULL, + "name" varchar NOT NULL, + "status" int NOT NULL, + "user_id" bigint NOT NULL, + "take_type" int NOT NULL, + "useprice" int NOT NULL, + "valid_start_time" datetime NOT NULL, + "valid_end_time" datetime NOT NULL, + "product_scope" int NOT NULL, + "product_spu_ids" varchar, + "discount_type" int NOT NULL, + "discount_percent" int, + "discount_price" int, "discount_limit_price" int, - "use_order_id" bigint, - "use_time" datetime, - "creator" varchar DEFAULT '', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar DEFAULT '', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, + "use_order_id" bigint, + "use_time" datetime, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, PRIMARY KEY ("id") ) COMMENT '优惠劵'; -CREATE TABLE IF NOT EXISTS "promotion_reward_activity" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar NOT NULL, - "status" int NOT NULL, - "start_time" datetime NOT NULL, - "end_time" datetime NOT NULL, - "remark" varchar, - "condition_type" int NOT NULL, - "product_scope" int NOT NULL, - "product_spu_ids" varchar, - "rules" varchar, - "creator" varchar DEFAULT '', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar DEFAULT '', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") +CREATE TABLE IF NOT EXISTS "promotion_reward_activity" +( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar NOT NULL, + "status" int NOT NULL, + "start_time" datetime NOT NULL, + "end_time" datetime NOT NULL, + "remark" varchar, + "condition_type" int NOT NULL, + "product_scope" int NOT NULL, + "product_spu_ids" varchar, + "rules" varchar, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") ) COMMENT '满减送活动'; -CREATE TABLE IF NOT EXISTS "promotion_discount_activity" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar NOT NULL, - "status" int NOT NULL, - "start_time" datetime NOT NULL, - "end_time" datetime NOT NULL, - "remark" varchar, - "creator" varchar DEFAULT '', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar DEFAULT '', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") +CREATE TABLE IF NOT EXISTS "promotion_discount_activity" +( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar NOT NULL, + "status" int NOT NULL, + "start_time" datetime NOT NULL, + "end_time" datetime NOT NULL, + "remark" varchar, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") ) COMMENT '限时折扣活动'; -- 将该建表 SQL 语句,添加到 yudao-module-promotion-biz 模块的 test/resources/sql/create_tables.sql 文件里 -CREATE TABLE IF NOT EXISTS "promotion_seckill_activity" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "spu_id" bigint NOT NULL, - "name" varchar NOT NULL, - "status" int NOT NULL, - "remark" varchar, - "start_time" varchar NOT NULL, - "end_time" varchar NOT NULL, - "sort" int NOT NULL, - "config_ids" varchar NOT NULL, - "order_count" int NOT NULL, - "user_count" int NOT NULL, - "total_price" int NOT NULL, - "total_limit_count" int, +CREATE TABLE IF NOT EXISTS "promotion_seckill_activity" +( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "spu_id" bigint NOT NULL, + "name" varchar NOT NULL, + "status" int NOT NULL, + "remark" varchar, + "start_time" varchar NOT NULL, + "end_time" varchar NOT NULL, + "sort" int NOT NULL, + "config_ids" varchar NOT NULL, + "order_count" int NOT NULL, + "user_count" int NOT NULL, + "total_price" int NOT NULL, + "total_limit_count" int, "single_limit_count" int, - "stock" int, - "total_stock" int, - "creator" varchar DEFAULT '', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar DEFAULT '', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - "tenant_id" bigint NOT NULL, + "stock" int, + "total_stock" int, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL, PRIMARY KEY ("id") ) COMMENT '秒杀活动'; -CREATE TABLE IF NOT EXISTS "promotion_seckill_config" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar NOT NULL, - "start_time" varchar NOT NULL, - "end_time" varchar NOT NULL, - "pic_url" varchar NOT NULL, - "status" int NOT NULL, - "creator" varchar DEFAULT '', +CREATE TABLE IF NOT EXISTS "promotion_seckill_config" +( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar NOT NULL, + "start_time" varchar NOT NULL, + "end_time" varchar NOT NULL, + "pic_url" varchar NOT NULL, + "status" int NOT NULL, + "creator" varchar DEFAULT '', "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar DEFAULT '', + "updater" varchar DEFAULT '', "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - "tenant_id" bigint NOT NULL, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL, PRIMARY KEY ("id") ) COMMENT '秒杀时段配置'; + +CREATE TABLE IF NOT EXISTS "promotion_combination_activity" +( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar NOT NULL, + "spu_id" bigint, + "total_limit_count" int NOT NULL, + "single_limit_count" int NOT NULL, + "start_time" varchar NOT NULL, + "end_time" varchar NOT NULL, + "user_size" int NOT NULL, + "total_num" int NOT NULL, + "success_num" int NOT NULL, + "order_user_count" int NOT NULL, + "virtual_group" int NOT NULL, + "status" int NOT NULL, + "limit_duration" int NOT NULL, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL, + PRIMARY KEY ("id") +) COMMENT '拼团活动'; \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java index 1b3c73c30..5c007f7e5 100644 --- a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java +++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java @@ -27,6 +27,10 @@ public interface ErrorCodeConstants { ErrorCode ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH = new ErrorCode(1011000016, "交易订单更新支付状态失败,支付单金额不匹配"); ErrorCode ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED = new ErrorCode(1011000017, "交易订单发货失败,订单不是【待发货】状态"); ErrorCode ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED = new ErrorCode(1011000018, "交易订单收货失败,订单不是【待收货】状态"); + ErrorCode ORDER_COMMENT_FAIL_STATUS_NOT_COMPLETED = new ErrorCode(1011000019, "创建交易订单项的评价失败,订单不是【已完成】状态"); + ErrorCode ORDER_COMMENT_STATUS_NOT_FALSE = new ErrorCode(1011000020, "创建交易订单项的评价失败,订单已评价"); + ErrorCode ORDER_DELIVERY_FAIL_REFUND_STATUS_NOT_NONE = new ErrorCode(1011000021, "交易订单发货失败,订单已退款或部分退款"); + ErrorCode ORDER_DELIVERY_FAIL_COMBINATION_RECORD_STATUS_NOT_SUCCESS = new ErrorCode(1011000022, "交易订单发货失败,拼团未成功"); // ========== After Sale 模块 1011000100 ========== ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1011000100, "售后单不存在"); @@ -63,8 +67,10 @@ public interface ErrorCodeConstants { ErrorCode EXPRESS_TEMPLATE_NOT_EXISTS = new ErrorCode(1011005001, "运费模板不存在"); // ========== 物流 PICK_UP 模块 1011006000 ========== - ErrorCode PICK_UP_STORE_NOT_EXISTS = new ErrorCode(1011006000, "自提门店不存在"); - + // ========== 物流 PICK_UP 模块 1011007000 ========== + ErrorCode ORDER_DELIVERY_FAILED_ITEMS_NOT_EMPTY = new ErrorCode(1011007000, "订单发货失败,请选择发货商品"); + ErrorCode ORDER_DELIVERY_FAILED_ITEM_NOT_EXISTS = new ErrorCode(1011007001, "订单发货失败,所选发货商品不存在"); + ErrorCode ORDER_DELIVERY_FAILED_ITEM_ALREADY_DELIVERY = new ErrorCode(1011007001, "订单发货失败,所选商品已发货"); } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderDeliveryReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderDeliveryReqVO.java index 039fb2f09..887dd1975 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderDeliveryReqVO.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderDeliveryReqVO.java @@ -1,10 +1,12 @@ package cn.iocoder.yudao.module.trade.controller.admin.order.vo; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; +import java.util.List; @Schema(description = "管理后台 - 订单发货 Request VO") @Data @@ -14,12 +16,24 @@ public class TradeOrderDeliveryReqVO { @NotNull(message = "订单编号不能为空") private Long id; - @Schema(description = "发货物流公司编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "发货物流公司编号不能为空") + @Schema(description = "发货类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + @InEnum(DeliveryTypeEnum.class) + @NotNull(message = "发货类型不能为空") + private Integer type; + + @Schema(description = "发货物流公司编号", example = "1") private Long logisticsId; - @Schema(description = "发货物流单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "SF123456789") - @NotEmpty(message = "发货物流单号不能为空") + @Schema(description = "发货物流单号", example = "SF123456789") private String logisticsNo; + // TODO 订单项商品单独发货 + + @Schema(description = "发货订单项", example = "[1,2,3]") + @NotNull(message = "发货订单项不能为空") + private List orderItemIds; + + // =============== 同城配送 ================ + // TODO + } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java index 6567e3169..d46550765 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java @@ -4,8 +4,6 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO; -import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi; -import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO; import cn.iocoder.yudao.module.product.api.property.ProductPropertyValueApi; import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO; import cn.iocoder.yudao.module.trade.controller.app.order.vo.*; @@ -30,13 +28,10 @@ import javax.validation.Valid; 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.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; -import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_ITEM_NOT_FOUND; -import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_NOT_FOUND; @Tag(name = "用户 App - 交易订单") @RestController @@ -47,12 +42,8 @@ public class AppTradeOrderController { @Resource private TradeOrderService tradeOrderService; - @Resource private ProductPropertyValueApi productPropertyValueApi; - @Resource - private ProductCommentApi productCommentApi; - @Resource private TradeOrderProperties tradeOrderProperties; @@ -121,7 +112,7 @@ public class AppTradeOrderController { // 待发货 orderCount.put("undeliveredCount", tradeOrderService.getOrderCount(getLoginUserId(), TradeOrderStatusEnum.UNDELIVERED.getStatus(), null)); // 待收货 - orderCount.put("deliveredCount", tradeOrderService.getOrderCount(getLoginUserId(), TradeOrderStatusEnum.DELIVERED.getStatus(), null)); + orderCount.put("deliveredCount", tradeOrderService.getOrderCount(getLoginUserId(), TradeOrderStatusEnum.DELIVERED.getStatus(), null)); // 待评价 orderCount.put("uncommentedCount", tradeOrderService.getOrderCount(getLoginUserId(), TradeOrderStatusEnum.COMPLETED.getStatus(), false)); return success(orderCount); @@ -164,22 +155,7 @@ public class AppTradeOrderController { @PostMapping("/item/create-comment") @Operation(summary = "创建交易订单项的评价") public CommonResult createOrderItemComment(@RequestBody AppTradeOrderItemCommentCreateReqVO createReqVO) { - // TODO @puhui999:这个逻辑,最好写到 service 哈; - Long loginUserId = getLoginUserId(); - // 先通过订单项 ID 查询订单项是否存在 - TradeOrderItemDO orderItemDO = tradeOrderService.getOrderItemByIdAndUserId(createReqVO.getOrderItemId(), loginUserId); - if (orderItemDO == null) { - throw exception(ORDER_ITEM_NOT_FOUND); - } - // 校验订单 - TradeOrderDO orderDO = tradeOrderService.getOrderByIdAndUserId(orderItemDO.getOrderId(), loginUserId); - if (orderDO == null) { - throw exception(ORDER_NOT_FOUND); - } - // TODO @puhui999:要校验订单已完成,但是未评价; - - ProductCommentCreateReqDTO productCommentCreateReqDTO = TradeOrderConvert.INSTANCE.convert04(createReqVO, orderItemDO); - return success(productCommentApi.createComment(productCommentCreateReqDTO)); + return success(tradeOrderService.createOrderItemComment(createReqVO)); } } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementReqVO.java index f3a7d9120..3e322c786 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementReqVO.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementReqVO.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.trade.controller.app.order.vo; import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.framework.common.validation.Mobile; import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; -import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -19,11 +18,6 @@ import java.util.List; @Data public class AppTradeOrderSettlementReqVO { - @NotNull(message = "交易类型不能为空") - @InEnum(value = TradeOrderTypeEnum.class, message = "交易类型必须是 {value}") - @Deprecated // TODO 芋艿:后续干掉这个字段,对于前端不需要关注这个 - private Integer type = 1; - @Schema(description = "商品项数组", requiredMode = Schema.RequiredMode.REQUIRED) @NotEmpty(message = "商品不能为空") private List items; diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/item/AppTradeOrderItemCommentCreateReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/item/AppTradeOrderItemCommentCreateReqVO.java index 349b071d5..69ffef241 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/item/AppTradeOrderItemCommentCreateReqVO.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/item/AppTradeOrderItemCommentCreateReqVO.java @@ -19,11 +19,6 @@ public class AppTradeOrderItemCommentCreateReqVO { @NotNull(message = "交易订单项编号不能为空") private Long orderItemId; - // TODO @puhui999:貌似不用这个字段哈; - @Schema(description = "评分星级 1-5 分", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") - @NotNull(message = "评分星级 1-5 分不能为空") - private Integer scores; - @Schema(description = "描述星级 1-5 分", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") @NotNull(message = "描述星级 1-5 分不能为空") private Integer descriptionScores; diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java index 21534f47a..da9269d8d 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java @@ -13,10 +13,12 @@ import cn.iocoder.yudao.module.pay.enums.DictTypeConstants; import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO; import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO; import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO; +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO; import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO; import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO; import cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO; import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO; +import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO; import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDetailRespVO; import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageItemRespVO; import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO; @@ -25,6 +27,7 @@ import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderI import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO; import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO; import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDeliveryDO; import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum; import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; @@ -52,7 +55,6 @@ public interface TradeOrderConvert { @Mapping(source = "createReqVO.couponId", target = "couponId"), @Mapping(target = "remark", ignore = true), @Mapping(source = "createReqVO.remark", target = "userRemark"), - @Mapping(source = "createReqVO.type", target = "type"), @Mapping(source = "calculateRespBO.price.totalPrice", target = "totalPrice"), @Mapping(source = "calculateRespBO.price.discountPrice", target = "discountPrice"), @Mapping(source = "calculateRespBO.price.deliveryPrice", target = "deliveryPrice"), @@ -123,7 +125,7 @@ public interface TradeOrderConvert { // TODO 芋艿:可简化 default PageResult convertPage(PageResult pageResult, List orderItems, List propertyValueDetails, - Map memberUserRespDTOMap) { + Map memberUserRespDTOMap) { Map> orderItemMap = convertMultiMap(orderItems, TradeOrderItemDO::getOrderId); Map propertyValueDetailMap = convertMap(propertyValueDetails, ProductPropertyValueDetailRespDTO::getValueId); // 转化 List @@ -267,23 +269,23 @@ public interface TradeOrderConvert { AppTradeOrderItemRespVO convert03(TradeOrderItemDO bean); - @Mapping(target = "skuId", source = "tradeOrderItemDO.skuId") - @Mapping(target = "orderId", source = "tradeOrderItemDO.orderId") - @Mapping(target = "orderItemId", source = "tradeOrderItemDO.id") - @Mapping(target = "scores", source = "createReqVO.scores") - @Mapping(target = "descriptionScores", source = "createReqVO.descriptionScores") - @Mapping(target = "benefitScores", source = "createReqVO.benefitScores") - @Mapping(target = "content", source = "createReqVO.content") - @Mapping(target = "picUrls", source = "createReqVO.picUrls") - @Mapping(target = "anonymous", source = "createReqVO.anonymous") - @Mapping(target = "userId", source = "tradeOrderItemDO.userId") + @Mappings({ + @Mapping(target = "skuId", source = "tradeOrderItemDO.skuId"), + @Mapping(target = "orderId", source = "tradeOrderItemDO.orderId"), + @Mapping(target = "orderItemId", source = "tradeOrderItemDO.id"), + @Mapping(target = "descriptionScores", source = "createReqVO.descriptionScores"), + @Mapping(target = "benefitScores", source = "createReqVO.benefitScores"), + @Mapping(target = "content", source = "createReqVO.content"), + @Mapping(target = "picUrls", source = "createReqVO.picUrls"), + @Mapping(target = "anonymous", source = "createReqVO.anonymous"), + @Mapping(target = "userId", source = "tradeOrderItemDO.userId") + }) ProductCommentCreateReqDTO convert04(AppTradeOrderItemCommentCreateReqVO createReqVO, TradeOrderItemDO tradeOrderItemDO); default TradePriceCalculateReqBO convert(Long userId, AppTradeOrderSettlementReqVO settlementReqVO, List cartList) { TradePriceCalculateReqBO reqBO = new TradePriceCalculateReqBO(); - reqBO.setUserId(userId).setType(settlementReqVO.getType()) - .setCouponId(settlementReqVO.getCouponId()).setAddressId(settlementReqVO.getAddressId()) + reqBO.setUserId(userId).setCouponId(settlementReqVO.getCouponId()).setAddressId(settlementReqVO.getAddressId()) .setItems(new ArrayList<>(settlementReqVO.getItems().size())); // 商品项的构建 Map cartMap = convertMap(cartList, TradeCartDO::getId); @@ -318,4 +320,31 @@ public interface TradeOrderConvert { AppTradeOrderSettlementRespVO convert0(TradePriceCalculateRespBO calculate, AddressRespDTO address); + @Mappings({ + @Mapping(target = "activityId", source = "createReqVO.combinationActivityId"), + @Mapping(target = "spuId", source = "orderItem.spuId"), + @Mapping(target = "skuId", source = "orderItem.skuId"), + @Mapping(target = "userId", source = "order.userId"), + @Mapping(target = "orderId", source = "order.id"), + @Mapping(target = "headId", source = "createReqVO.combinationHeadId"), + @Mapping(target = "spuName", source = "orderItem.spuName"), + @Mapping(target = "picUrl", source = "orderItem.picUrl"), + @Mapping(target = "combinationPrice", source = "orderItem.payPrice"), + @Mapping(target = "nickname", source = "user.nickname"), + @Mapping(target = "avatar", source = "user.avatar"), + @Mapping(target = "status", ignore = true) + }) + CombinationRecordReqDTO convert(TradeOrderDO order, TradeOrderItemDO orderItem, AppTradeOrderCreateReqVO createReqVO, MemberUserRespDTO user); + + TradeOrderDeliveryDO covert(Long orderId, Long orderItemId, Long userId, Integer deliveryType, Long logisticsId, String logisticsNo); + + default List covert(TradeOrderDO order, TradeOrderDeliveryReqVO deliveryReqVO) { + ArrayList arrayList = new ArrayList<>(); + deliveryReqVO.getOrderItemIds().forEach(item -> { + arrayList.add(covert(order.getId(), item, order.getUserId(), deliveryReqVO.getType(), + deliveryReqVO.getLogisticsId(), deliveryReqVO.getLogisticsNo())); + }); + return arrayList; + } + } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDeliveryDO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDeliveryDO.java new file mode 100644 index 000000000..08e9a8402 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDeliveryDO.java @@ -0,0 +1,60 @@ +package cn.iocoder.yudao.module.trade.dal.dataobject.order; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + + +/** + * 交易订单发货记录 DO + * + * @author HUIHUI + */ +@TableName("trade_order_delivery") +@KeySequence("trade_order_delivery_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TradeOrderDeliveryDO extends BaseDO { + + /** + * 订单发货记录 id + */ + private Long id; + /** + * 订单 id + */ + private Long orderId; + /** + * 订单项 id TODO 要不要一个发货记录可对应多个订单项? + */ + private Long orderItemId; + /** + * 用户编号 + * + * 关联 MemberUserDO 的 id 编号 + */ + private Long userId; + /** + * 配送方式 + * + * 枚举 {@link DeliveryTypeEnum} + */ + private Integer deliveryType; + /** + * 发货物流公司编号 + */ + private Long logisticsId; + /** + * 发货物流单号 + */ + private String logisticsNo; + + // TODO 同城配送 + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderDeliveryMapper.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderDeliveryMapper.java new file mode 100644 index 000000000..d80e5ab0a --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderDeliveryMapper.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.trade.dal.mysql.order; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDeliveryDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 交易订单发货记录 Mapper + * + * @author HUIHUI + */ +@Mapper +public interface TradeOrderDeliveryMapper extends BaseMapperX { + + default List selsectListByOrderIdAndItemIds(Long orderId, List orderItemIds) { + return selectList(new LambdaQueryWrapperX() + .eq(TradeOrderDeliveryDO::getOrderId, orderId).in(TradeOrderDeliveryDO::getOrderItemId, orderItemIds)); + } + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderService.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderService.java index d49baf49c..c0ab69be1 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderService.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderService.java @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreate import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageReqVO; import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementReqVO; import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementRespVO; +import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO; import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; @@ -177,4 +178,14 @@ public interface TradeOrderService { * @return 得到订单 */ TradeOrderDO getOrderByIdAndUserId(Long orderId, Long loginUserId); + + /** + * 创建订单项评论 + * 创建交易订单项的评价 + * + * @param createReqVO 创建请求 + * @return 得到评价 id + */ + Long createOrderItemComment(AppTradeOrderItemCommentCreateReqVO createReqVO); + } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java index 48c709962..ee2feec1a 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java @@ -17,24 +17,30 @@ import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; +import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi; +import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO; import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO; +import cn.iocoder.yudao.module.promotion.api.combination.CombinationApi; import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi; import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO; +import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum; import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi; import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO; -import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO; import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO; import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageReqVO; import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementReqVO; import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementRespVO; +import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO; import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert; import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO; import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO; import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDeliveryDO; import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; +import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderDeliveryMapper; import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper; import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper; import cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants; @@ -57,6 +63,7 @@ import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.ORDER_NOT_FOUND; import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*; @@ -93,13 +100,17 @@ public class TradeOrderServiceImpl implements TradeOrderService { @Resource private MemberUserApi memberUserApi; @Resource - private AdminUserApi adminUserApi; + private ProductCommentApi productCommentApi; @Resource private NotifyMessageSendApi notifyMessageSendApi; @Resource private TradeOrderProperties tradeOrderProperties; + @Resource + private CombinationApi combinationApi; + @Resource + private TradeOrderDeliveryMapper orderDeliveryMapper; // =================== Order =================== @Override @@ -153,19 +164,27 @@ public class TradeOrderServiceImpl implements TradeOrderService { @Override @Transactional(rollbackFor = Exception.class) public TradeOrderDO createOrder(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO) { - // 1. 用户收件地址的校验 - AddressRespDTO address = validateAddress(userId, createReqVO.getAddressId()); - // 2. 价格计算 TradePriceCalculateRespBO calculateRespBO = calculatePrice(userId, createReqVO); - // 3.1 插入 TradeOrderDO 订单 - TradeOrderDO order = createTradeOrder(userId, userIp, createReqVO, calculateRespBO, address); + TradeOrderDO order = createTradeOrder(userId, userIp, createReqVO, calculateRespBO); // 3.2 插入 TradeOrderItemDO 订单项 List orderItems = createTradeOrderItems(order, calculateRespBO); - // 订单创建完后的逻辑 afterCreateTradeOrder(userId, createReqVO, order, orderItems, calculateRespBO); + // 3.3 校验订单类型 + // 拼团 + if (ObjectUtil.equal(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) { + MemberUserRespDTO user = memberUserApi.getUser(userId); + // TODO 拼团一次应该只能选择一种规格的商品 + combinationApi.createRecord(TradeOrderConvert.INSTANCE.convert(order, orderItems.get(0), createReqVO, user) + .setStatus(CombinationRecordStatusEnum.NOT_PAY.getStatus())); + } + // TODO 秒杀扣减库存是下单就扣除还是等待订单支付成功再扣除 + if (ObjectUtil.equal(TradeOrderTypeEnum.SECKILL.getType(), order.getType())) { + + } + // TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来! return order; } @@ -185,19 +204,42 @@ public class TradeOrderServiceImpl implements TradeOrderService { return address; } + /** + * 校验活动返回订单类型 + * + * @param createReqVO 请求参数 + * @return 订单类型 + */ + private Integer validateActivity(AppTradeOrderCreateReqVO createReqVO) { + if (createReqVO.getSeckillActivityId() != null) { + return TradeOrderTypeEnum.SECKILL.getType(); + } + if (createReqVO.getCombinationActivityId() != null) { + return TradeOrderTypeEnum.COMBINATION.getType(); + } + // TODO 砍价敬请期待 + return TradeOrderTypeEnum.NORMAL.getType(); + } + private TradeOrderDO createTradeOrder(Long userId, String clientIp, AppTradeOrderCreateReqVO createReqVO, - TradePriceCalculateRespBO calculateRespBO, AddressRespDTO address) { + TradePriceCalculateRespBO calculateRespBO) { + // 用户选择物流配送的时候才需要填写收货地址 + AddressRespDTO address = new AddressRespDTO(); + if (ObjectUtil.equal(createReqVO.getDeliveryType(), DeliveryTypeEnum.EXPRESS.getMode())) { + // 用户收件地址的校验 + address = validateAddress(userId, createReqVO.getAddressId()); + } TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(userId, clientIp, createReqVO, calculateRespBO, address); + order.setType(validateActivity(createReqVO)); order.setNo(IdUtil.getSnowflakeNextId() + ""); // TODO @LeeYan9: 思考下, 怎么生成好点哈; 这个是会展示给用户的; order.setStatus(TradeOrderStatusEnum.UNPAID.getStatus()); - order.setType(TradeOrderTypeEnum.NORMAL.getType()); order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()); order.setProductCount(getSumValue(calculateRespBO.getItems(), TradePriceCalculateRespBO.OrderItem::getCount, Integer::sum)); order.setTerminal(TerminalEnum.H5.getTerminal()); // todo 数据来源? // 支付信息 order.setAdjustPrice(0).setPayStatus(false); // 物流信息 TODO 芋艿:暂时写死物流方式;应该是前端选择的 - order.setDeliveryType(DeliveryTypeEnum.EXPRESS.getMode()).setDeliveryStatus(TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus()); + order.setDeliveryType(createReqVO.getDeliveryType()).setDeliveryStatus(TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus()); // 退款信息 order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()).setRefundPrice(0); tradeOrderMapper.insert(order); @@ -212,7 +254,7 @@ public class TradeOrderServiceImpl implements TradeOrderService { /** * 执行创建完创建完订单后的逻辑 - *

+ * * 例如说:优惠劵的扣减、积分的扣减、支付单的创建等等 * * @param userId 用户编号 @@ -254,6 +296,7 @@ public class TradeOrderServiceImpl implements TradeOrderService { } @Override + @Transactional(rollbackFor = Exception.class) public void updateOrderPaid(Long id, Long payOrderId) { // 校验并获得交易订单(可支付) KeyValue orderResult = validateOrderPayable(id, payOrderId); @@ -267,7 +310,12 @@ public class TradeOrderServiceImpl implements TradeOrderService { if (updateCount == 0) { throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID); } - + // 校验活动 + // 1、拼团活动 + if (ObjectUtil.equal(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) { + // 更新拼团状态 TODO puhui999:订单支付失败或订单支付过期删除这条拼团记录 + combinationApi.updateRecordStatusAndStartTime(order.getUserId(), order.getId(), CombinationRecordStatusEnum.ONGOING.getStatus()); + } // TODO 芋艿:发送订单变化的消息 // TODO 芋艿:发送站内信 @@ -277,7 +325,7 @@ public class TradeOrderServiceImpl implements TradeOrderService { /** * 校验交易订单满足被支付的条件 - *

+ * * 1. 交易订单未支付 * 2. 支付单已支付 * @@ -308,7 +356,7 @@ public class TradeOrderServiceImpl implements TradeOrderService { PayOrderRespDTO payOrder = payOrderApi.getOrder(payOrderId); if (payOrder == null) { log.error("[validateOrderPaid][order({}) payOrder({}) 不存在,请进行处理!]", id, payOrderId); - throw exception(cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.ORDER_NOT_FOUND); + throw exception(ORDER_NOT_FOUND); } // 校验支付单已支付 if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) { @@ -331,29 +379,50 @@ public class TradeOrderServiceImpl implements TradeOrderService { return new KeyValue<>(order, payOrder); } - // TODO 芋艿:如果无需发货,需要怎么存储? @Override + @Transactional(rollbackFor = Exception.class) public void deliveryOrder(Long userId, TradeOrderDeliveryReqVO deliveryReqVO) { // 校验并获得交易订单(可发货) TradeOrderDO order = validateOrderDeliverable(deliveryReqVO.getId()); - // TODO 芋艿:logisticsId 校验存在 发货物流公司 fix - DeliveryExpressDO deliveryExpress = deliveryExpressService.getDeliveryExpress(deliveryReqVO.getLogisticsId()); - if (deliveryExpress == null) { - throw exception(EXPRESS_NOT_EXISTS); + + TradeOrderDO tradeOrderDO = new TradeOrderDO(); + List deliveryDOs = new ArrayList<>(); + /* TODO + * fix: 首先需要店铺设置配送方式如: 自提 、配送、物流-配送、物流-配送-自提、商家配送 + * 1.如果店铺有设置配送方式用户只填写收货地址的情况下店家后台自己选择配送方式 + * 2.如果店铺只支持到店自提那么下单后默认发货不需要物流 + * 3.如果店铺支持 物流-配送-自提 的情况下后台不需要选择配送方式按前端用户选择的配送方式发货即可 + */ + // 判断发货类型 + // 快递发货 + if (ObjectUtil.equal(deliveryReqVO.getType(), DeliveryTypeEnum.EXPRESS.getMode())) { + deliveryDOs = express(order, deliveryReqVO); + tradeOrderDO.setLogisticsId(deliveryReqVO.getLogisticsId()).setLogisticsNo(deliveryReqVO.getLogisticsNo()); + } + // 用户自提 + if (ObjectUtil.equal(deliveryReqVO.getType(), DeliveryTypeEnum.PICK_UP.getMode())) { + deliveryDOs = pickUp(order, deliveryReqVO); + // 重置一下确保快递公司和快递单号为空 + tradeOrderDO.setLogisticsId(null).setLogisticsNo(""); + } + // TODO 芋艿:如果无需发货,需要怎么存储? + if (ObjectUtil.equal(deliveryReqVO.getType(), DeliveryTypeEnum.NULL.getMode())) { + // TODO 情况一:正常走发货逻辑和用户自提有点像 不同点:不需要自提门店只需要用户确认收货 + // TODO 情况二:用户下单付款后直接确认收货或等待用户确认收货 } // 更新 TradeOrderDO 状态为已发货,等待收货 - int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(), - new TradeOrderDO().setStatus(TradeOrderStatusEnum.DELIVERED.getStatus()) - .setLogisticsId(deliveryReqVO.getLogisticsId()).setLogisticsNo(deliveryReqVO.getLogisticsNo()) - .setDeliveryStatus(TradeOrderDeliveryStatusEnum.DELIVERED.getStatus()).setDeliveryTime(LocalDateTime.now())); + tradeOrderDO.setStatus(TradeOrderStatusEnum.DELIVERED.getStatus()) + .setDeliveryStatus(TradeOrderDeliveryStatusEnum.DELIVERED.getStatus()).setDeliveryTime(LocalDateTime.now()); + int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(), tradeOrderDO); if (updateCount == 0) { throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED); } - + // 发货成功记录发货表 + orderDeliveryMapper.insertBatch(deliveryDOs); // TODO 芋艿:发送订单变化的消息 - // TODO 芋艿:发送站内信 fix + // 发送站内信 // 1、构造消息 Map msgMap = new HashMap<>(); msgMap.put("orderId", deliveryReqVO.getId()); @@ -364,16 +433,59 @@ public class TradeOrderServiceImpl implements TradeOrderService { .setUserId(userId) .setTemplateCode("order_delivery") .setTemplateParams(msgMap)); - // TODO 芋艿:OrderLog - // TODO 设计:like:是否要单独一个 delivery 发货单表??? - // TODO 设计:niu:要不要支持一个订单下,多个 order item 单独发货,类似有赞 + // TODO 芋艿:OrderLog // TODO 设计:lili:是不是发货后,才支持售后? } + private List express(TradeOrderDO order, TradeOrderDeliveryReqVO deliveryReqVO) { + // 校验快递公司 + DeliveryExpressDO deliveryExpress = deliveryExpressService.getDeliveryExpress(deliveryReqVO.getLogisticsId()); + if (deliveryExpress == null) { + throw exception(EXPRESS_NOT_EXISTS); + } + // 校验发货商品 + validateDeliveryOrderItem(order, deliveryReqVO); + // 创建发货记录 + return TradeOrderConvert.INSTANCE.covert(order, deliveryReqVO); + } + + private List pickUp(TradeOrderDO order, TradeOrderDeliveryReqVO deliveryReqVO) { + // TODO 校验自提门店是否存在 + // 重置一下确保快递公司和快递单号为空 + deliveryReqVO.setLogisticsId(null); + deliveryReqVO.setLogisticsNo(""); + // 校验发货商品 + validateDeliveryOrderItem(order, deliveryReqVO); + // 创建发货记录 + return TradeOrderConvert.INSTANCE.covert(order, deliveryReqVO); + } + + private void validateDeliveryOrderItem(TradeOrderDO order, TradeOrderDeliveryReqVO deliveryReqVO) { + // TODO 设计:like:是否要单独一个 delivery 发货单表??? fix: 多商品可分开单独发货,添加 trade_order_delivery 交易订单发货日志表关联发货所选订单项设置物流单号 + // TODO 设计:niu:要不要支持一个订单下,多个 order item 单独发货,类似有赞 fix + // 校验发货商品 + if (CollUtil.isEmpty(deliveryReqVO.getOrderItemIds())) { + throw exception(ORDER_DELIVERY_FAILED_ITEMS_NOT_EMPTY); + } + // 校验发货商品是否存在 + List orderItemDOs = tradeOrderItemMapper.selectListByOrderId(order.getId()); + Set itemIds = convertSet(orderItemDOs, TradeOrderItemDO::getId); + if (!itemIds.containsAll(deliveryReqVO.getOrderItemIds())) { + throw exception(ORDER_DELIVERY_FAILED_ITEM_NOT_EXISTS); + } + // 校验所选订单项是否存在有已发货的 + List deliveryDOList = orderDeliveryMapper.selsectListByOrderIdAndItemIds(order.getId(), deliveryReqVO.getOrderItemIds()); + if (CollUtil.isNotEmpty(deliveryDOList)) { + HashSet hashSet = CollUtil.newHashSet(deliveryReqVO.getOrderItemIds()); + hashSet.retainAll(convertSet(deliveryDOList, TradeOrderDeliveryDO::getOrderItemId)); + throw exception(ORDER_DELIVERY_FAILED_ITEM_ALREADY_DELIVERY); + } + } + /** * 校验交易订单满足被发货的条件 - *

+ * * 1. 交易订单未发货 * * @param id 交易订单编号 @@ -390,6 +502,19 @@ public class TradeOrderServiceImpl implements TradeOrderService { || ObjectUtil.notEqual(order.getDeliveryStatus(), TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus())) { throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED); } + // 校验订单是否退款 + if (ObjectUtil.notEqual(TradeOrderRefundStatusEnum.NONE.getStatus(), order.getRefundStatus())) { + throw exception(ORDER_DELIVERY_FAIL_REFUND_STATUS_NOT_NONE); + } + // 订单类型:拼团 + if (ObjectUtil.equal(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) { + // 校验订单拼团是否成功 + // TODO 用户 ID 使用当前登录用户的还是订单保存的? + if (combinationApi.validateRecordStatusIsSuccess(order.getUserId(), order.getId())) { + throw exception(ORDER_DELIVERY_FAIL_COMBINATION_RECORD_STATUS_NOT_SUCCESS); + } + } + // TODO puhui999: 校验订单砍价是否成功 return order; } @@ -421,7 +546,7 @@ public class TradeOrderServiceImpl implements TradeOrderService { /** * 校验交易订单满足可售货的条件 - *

+ * * 1. 交易订单待收货 * * @param userId 用户编号 @@ -556,6 +681,30 @@ public class TradeOrderServiceImpl implements TradeOrderService { return tradeOrderMapper.selectOrderByIdAndUserId(orderId, loginUserId); } + @Override + public Long createOrderItemComment(AppTradeOrderItemCommentCreateReqVO createReqVO) { + Long loginUserId = getLoginUserId(); + // 先通过订单项 ID 查询订单项是否存在 + TradeOrderItemDO orderItemDO = getOrderItemByIdAndUserId(createReqVO.getOrderItemId(), loginUserId); + if (orderItemDO == null) { + throw exception(ORDER_ITEM_NOT_FOUND); + } + // 校验订单 + TradeOrderDO orderDO = getOrderByIdAndUserId(orderItemDO.getOrderId(), loginUserId); + if (orderDO == null) { + throw exception(ORDER_NOT_FOUND); + } + if (ObjectUtil.notEqual(orderDO.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus())) { + throw exception(ORDER_COMMENT_FAIL_STATUS_NOT_COMPLETED); + } + if (ObjectUtil.notEqual(orderDO.getCommentStatus(), Boolean.FALSE)) { + throw exception(ORDER_COMMENT_STATUS_NOT_FALSE); + } + + ProductCommentCreateReqDTO productCommentCreateReqDTO = TradeOrderConvert.INSTANCE.convert04(createReqVO, orderItemDO); + return productCommentApi.createComment(productCommentCreateReqDTO); + } + /** * 判断指定订单的所有订单项,是不是都售后成功 * diff --git a/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/address/AddressServiceImplTest.java b/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/address/AddressServiceImplTest.java index 77b40705e..c25b76ca6 100644 --- a/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/address/AddressServiceImplTest.java +++ b/yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/address/AddressServiceImplTest.java @@ -19,10 +19,10 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; /** -* {@link AddressServiceImpl} 的单元测试类 -* -* @author 芋道源码 -*/ + * {@link AddressServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ @Import(AddressServiceImpl.class) public class AddressServiceImplTest extends BaseDbUnitTest { @@ -82,8 +82,8 @@ public class AddressServiceImplTest extends BaseDbUnitTest { // 调用 addressService.deleteAddress(dbAddress.getUserId(), id); - // 校验数据不存在了 - assertNull(addressMapper.selectById(id)); + // 校验数据不存在了 + assertNull(addressMapper.selectById(id)); } @Test