浏览代码

fix: 完善拼团、秒杀、砍价活动管理

puhui999 1 年之前
父节点
当前提交
5f9184904a
共有 32 个文件被更改,包括 1284 次插入247 次删除
  1. 1 38
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
  2. 10 0
      yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java
  3. 91 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/BargainActivityController.java
  4. 59 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/activity/BargainActivityBaseVO.java
  5. 22 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/activity/BargainActivityCreateReqVO.java
  6. 65 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/activity/BargainActivityPageReqVO.java
  7. 56 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/activity/BargainActivityRespVO.java
  8. 27 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/activity/BargainActivityUpdateReqVO.java
  9. 35 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/product/BargainProductBaseVO.java
  10. 14 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/product/BargainProductCreateReqVO.java
  11. 47 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/product/BargainProductPageReqVO.java
  12. 22 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/product/BargainProductRespVO.java
  13. 14 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/product/BargainProductUpdateReqVO.java
  14. 0 11
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/CombinationActivityController.java
  15. 9 3
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityBaseVO.java
  16. 3 14
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/SeckillActivityController.java
  17. 95 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/bargain/BargainActivityConvert.java
  18. 4 35
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/combination/CombinationActivityConvert.java
  19. 3 24
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/seckill/seckillactivity/SeckillActivityConvert.java
  20. 5 15
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/bargain/BargainActivityDO.java
  21. 70 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/bargain/BargainProductDO.java
  22. 30 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/bargain/BargainActivityMapper.java
  23. 37 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/bargain/BargainProductMapper.java
  24. 48 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/bargain/BargainRecordMapper.java
  25. 4 3
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/CombinationRecordMapper.java
  26. 75 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainActivityService.java
  27. 51 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainRecordService.java
  28. 293 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainServiceImpl.java
  29. 32 35
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationServiceImpl.java
  30. 36 39
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/seckillactivity/SeckillActivityServiceImpl.java
  31. 4 7
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/util/PromotionUtils.java
  32. 22 23
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java

+ 1 - 38
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java

@@ -2,7 +2,6 @@ package cn.iocoder.yudao.framework.common.util.collection;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.CollectionUtil;
-import cn.hutool.core.map.MapUtil;
 import com.google.common.collect.ImmutableMap;
 
 import java.util.*;
@@ -26,8 +25,7 @@ public class CollectionUtils {
         return Arrays.stream(collections).anyMatch(CollectionUtil::isEmpty);
     }
 
-    // TODO @puhui999:anyMatch 更统一点
-    public static <T> boolean isAny(Collection<T> from, Predicate<T> predicate) {
+    public static <T> boolean anyMatch(Collection<T> from, Predicate<T> predicate) {
         return from.stream().anyMatch(predicate);
     }
 
@@ -154,41 +152,6 @@ public class CollectionUtils {
         return builder.build();
     }
 
-    /**
-     * 数据划分为需要新增的、还是删除的、还是更新的。
-     *
-     * @param list1 需要处理的数据的相关编号列表
-     * @param list2 数据库中存在的数据的相关编号列表
-     * @param func  比较出哪些记录是新增,还是修改,还是删除
-     * @return 包含需要新增、修改、删除的数据 Map 对象
-     */
-    public static <D> Map<String, List<D>> convertCDUMap(Collection<Long> list1, Collection<Long> list2,
-                                                         Function<Map<String, List<Long>>, Map<String, List<D>>> func) {
-        HashMap<String, List<Long>> mapData = MapUtil.newHashMap(3);
-        if (CollUtil.isEmpty(list1)) {
-            return func.apply(mapData);
-        }
-        if (CollUtil.isEmpty(list2)) {
-            return func.apply(mapData);
-        }
-        // 后台存在的前端不存在的
-        List<Long> d = CollectionUtils.filterList(list2, item -> !list1.contains(item));
-        if (CollUtil.isNotEmpty(d)) {
-            mapData.put("delete", d);
-        }
-        // 前端存在的后端不存在的
-        List<Long> c = CollectionUtils.filterList(list1, item -> !list2.contains(item));
-        if (CollUtil.isNotEmpty(c)) {
-            mapData.put("create", c);
-        }
-        // 更新已存在的
-        List<Long> u = CollectionUtils.filterList(list1, list2::contains);
-        if (CollUtil.isNotEmpty(u)) {
-            mapData.put("update", u);
-        }
-        return func.apply(mapData);
-    }
-
     /**
      * 对比老、新两个列表,找出新增、修改、删除的数据
      *

+ 10 - 0
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java

@@ -69,4 +69,14 @@ public interface ErrorCodeConstants {
     ErrorCode COMBINATION_RECORD_HEAD_NOT_EXISTS = new ErrorCode(1013010006, "拼团失败,父拼团不存在");
     ErrorCode COMBINATION_RECORD_USER_FULL = new ErrorCode(1013010006, "拼团失败,拼团人数已满");
 
+    // ========== 砍价活动 1013011000 ==========
+    ErrorCode BARGAIN_ACTIVITY_NOT_EXISTS = new ErrorCode(1013011000, "砍价活动不存在");
+    ErrorCode BARGAIN_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013011001, "存在商品参加了其它砍价活动");
+    ErrorCode BARGAIN_ACTIVITY_STATUS_DISABLE = new ErrorCode(1013011002, "砍价活动已关闭不能修改");
+    ErrorCode BARGAIN_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013011003, "砍价活动未关闭或未结束,不能删除");
+    ErrorCode BARGAIN_RECORD_NOT_EXISTS = new ErrorCode(1013011004, "砍价不存在");
+    ErrorCode BARGAIN_RECORD_EXISTS = new ErrorCode(1013011005, "砍价失败,已参与过该砍价");
+    ErrorCode BARGAIN_RECORD_HEAD_NOT_EXISTS = new ErrorCode(1013011006, "砍价失败,父砍价不存在");
+    ErrorCode BARGAIN_RECORD_USER_FULL = new ErrorCode(1013011007, "砍价失败,砍价人数已满");
+
 }

+ 91 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/BargainActivityController.java

@@ -0,0 +1,91 @@
+package cn.iocoder.yudao.module.promotion.controller.admin.bargain;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
+import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityCreateReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityPageReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityRespVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityUpdateReqVO;
+import cn.iocoder.yudao.module.promotion.convert.bargain.BargainActivityConvert;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainProductDO;
+import cn.iocoder.yudao.module.promotion.service.bargain.BargainActivityService;
+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.validation.Valid;
+import java.util.List;
+import java.util.Set;
+
+import static cn.hutool.core.collection.CollectionUtil.newArrayList;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
+
+@Tag(name = "管理后台 - 砍价活动")
+@RestController
+@RequestMapping("/promotion/bargain-activity")
+@Validated
+public class BargainActivityController {
+
+    @Resource
+    private BargainActivityService activityService;
+
+    @Resource
+    private ProductSpuApi spuApi;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建砍价活动")
+    @PreAuthorize("@ss.hasPermission('promotion:bargain-activity:create')")
+    public CommonResult<Long> createBargainActivity(@Valid @RequestBody BargainActivityCreateReqVO createReqVO) {
+        return success(activityService.createBargainActivity(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新砍价活动")
+    @PreAuthorize("@ss.hasPermission('promotion:bargain-activity:update')")
+    public CommonResult<Boolean> updateBargainActivity(@Valid @RequestBody BargainActivityUpdateReqVO updateReqVO) {
+        activityService.updateBargainActivity(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除砍价活动")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('promotion:bargain-activity:delete')")
+    public CommonResult<Boolean> deleteBargainActivity(@RequestParam("id") Long id) {
+        activityService.deleteBargainActivity(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得砍价活动")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('promotion:bargain-activity:query')")
+    public CommonResult<BargainActivityRespVO> getBargainActivity(@RequestParam("id") Long id) {
+        BargainActivityDO activity = activityService.getBargainActivity(id);
+        List<BargainProductDO> products = activityService.getBargainProductsByActivityIds(newArrayList(id));
+        return success(BargainActivityConvert.INSTANCE.convert(activity, products));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得砍价活动分页")
+    @PreAuthorize("@ss.hasPermission('promotion:bargain-activity:query')")
+    public CommonResult<PageResult<BargainActivityRespVO>> getBargainActivityPage(
+            @Valid BargainActivityPageReqVO pageVO) {
+        // 查询砍价活动
+        PageResult<BargainActivityDO> pageResult = activityService.getBargainActivityPage(pageVO);
+        // 拼接数据
+        Set<Long> activityIds = convertSet(pageResult.getList(), BargainActivityDO::getId);
+        Set<Long> spuIds = convertSet(pageResult.getList(), BargainActivityDO::getSpuId);
+        return success(BargainActivityConvert.INSTANCE.convertPage(pageResult,
+                activityService.getBargainProductsByActivityIds(activityIds),
+                spuApi.getSpuList(spuIds)));
+    }
+
+}

+ 59 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/activity/BargainActivityBaseVO.java

@@ -0,0 +1,59 @@
+package cn.iocoder.yudao.module.promotion.controller.admin.bargain.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 文档生成
+ *
+ * @author HUIHUI
+ */
+@Data
+public class BargainActivityBaseVO {
+
+    @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 = "[2022-07-01 23:59:59]")
+    @NotNull(message = "活动开始时间不能为空")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime startTime;
+
+    @Schema(description = "活动结束时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "[2022-07-01 23:59:59]")
+    @NotNull(message = "活动结束时间不能为空")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime endTime;
+
+    @Schema(description = "砍价人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "25222")
+    @NotNull(message = "砍价人数不能为空")
+    private Integer userSize;
+
+    @Schema(description = "最大帮砍次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "25222")
+    @NotNull(message = "最大帮砍次数不能为空")
+    private Integer bargainCount;
+
+    @Schema(description = "用户每次砍价的最小金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "25222")
+    @NotNull(message = "用户每次砍价的最小金额不能为空")
+    private Integer randomMinPrice;
+
+    @Schema(description = "用户每次砍价的最大金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "25222")
+    @NotNull(message = "用户每次砍价的最大金额不能为空")
+    private Integer randomMaxPrice;
+
+}

+ 22 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/activity/BargainActivityCreateReqVO.java

@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity;
+
+import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.product.BargainProductCreateReqVO;
+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 BargainActivityCreateReqVO extends BargainActivityBaseVO {
+
+    @Schema(description = "砍价商品", requiredMode = Schema.RequiredMode.REQUIRED)
+    @Valid
+    private List<BargainProductCreateReqVO> products;
+
+}

+ 65 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/activity/BargainActivityPageReqVO.java

@@ -0,0 +1,65 @@
+package cn.iocoder.yudao.module.promotion.controller.admin.bargain.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 BargainActivityPageReqVO 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;
+
+}

+ 56 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/activity/BargainActivityRespVO.java

@@ -0,0 +1,56 @@
+package cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity;
+
+import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.product.BargainProductRespVO;
+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 BargainActivityRespVO extends BargainActivityBaseVO {
+
+    @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 = "活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
+    @NotNull(message = "活动状态不能为空")
+    private Integer status;
+
+    @Schema(description = "砍价商品", requiredMode = Schema.RequiredMode.REQUIRED)
+    @Valid
+    private List<BargainProductRespVO> products;
+
+}

+ 27 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/activity/BargainActivityUpdateReqVO.java

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity;
+
+import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.product.BargainProductUpdateReqVO;
+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 BargainActivityUpdateReqVO extends BargainActivityBaseVO {
+
+    @Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22901")
+    @NotNull(message = "活动编号不能为空")
+    private Long id;
+
+    @Schema(description = "砍价商品", requiredMode = Schema.RequiredMode.REQUIRED)
+    @Valid
+    private List<BargainProductUpdateReqVO> products;
+
+}

+ 35 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/product/BargainProductBaseVO.java

@@ -0,0 +1,35 @@
+package cn.iocoder.yudao.module.promotion.controller.admin.bargain.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 BargainProductBaseVO {
+
+    @Schema(description = "商品 spuId", requiredMode = Schema.RequiredMode.REQUIRED, example = "123")
+    @NotNull(message = "商品 spuId 不能为空")
+    private Long spuId;
+
+    @Schema(description = "商品 skuId", requiredMode = Schema.RequiredMode.REQUIRED, example = "23")
+    @NotNull(message = "商品 skuId 不能为空")
+    private Long skuId;
+
+    @Schema(description = "砍价起始价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "23")
+    @NotNull(message = "砍价起始价格不能为空")
+    private Integer bargainFirstPrice;
+
+    @Schema(description = "砍价底价", requiredMode = Schema.RequiredMode.REQUIRED, example = "23")
+    @NotNull(message = "砍价底价不能为空")
+    private Integer bargainPrice;
+
+    @Schema(description = "活动库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "23")
+    @NotNull(message = "活动库存不能为空")
+    private Integer stock;
+
+}

+ 14 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/product/BargainProductCreateReqVO.java

@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.module.promotion.controller.admin.bargain.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 BargainProductCreateReqVO extends BargainProductBaseVO {
+
+}

+ 47 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/product/BargainProductPageReqVO.java

@@ -0,0 +1,47 @@
+package cn.iocoder.yudao.module.promotion.controller.admin.bargain.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 BargainProductPageReqVO 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;
+
+}

+ 22 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/product/BargainProductRespVO.java

@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.promotion.controller.admin.bargain.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 BargainProductRespVO extends BargainProductBaseVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28322")
+    private Long id;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime createTime;
+
+}

+ 14 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/bargain/vo/product/BargainProductUpdateReqVO.java

@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.module.promotion.controller.admin.bargain.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 BargainProductUpdateReqVO extends BargainProductBaseVO {
+
+}

+ 0 - 11
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/CombinationActivityController.java

@@ -20,7 +20,6 @@ import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
 import javax.validation.Valid;
-import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 
@@ -74,16 +73,6 @@ public class CombinationActivityController {
         return success(CombinationActivityConvert.INSTANCE.convert(activity, products));
     }
 
-    // TODO @puhui999:是不是可以删掉,貌似没用?
-    @GetMapping("/list")
-    @Operation(summary = "获得拼团活动列表")
-    @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
-    @PreAuthorize("@ss.hasPermission('promotion:combination-activity:query')")
-    public CommonResult<List<CombinationActivityRespVO>> getCombinationActivityList(@RequestParam("ids") Collection<Long> ids) {
-        List<CombinationActivityDO> list = combinationActivityService.getCombinationActivityList(ids);
-        return success(CombinationActivityConvert.INSTANCE.convertList(list));
-    }
-
     @GetMapping("/page")
     @Operation(summary = "获得拼团活动分页")
     @PreAuthorize("@ss.hasPermission('promotion:combination-activity:query')")

+ 9 - 3
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityBaseVO.java

@@ -12,6 +12,8 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
 /**
  * 拼团活动 Base VO,提供给添加、修改、详细的子 VO 使用
  * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+ *
+ * @author HUIHUI
  */
 @Data
 public class CombinationActivityBaseVO {
@@ -32,11 +34,15 @@ public class CombinationActivityBaseVO {
     @NotNull(message = "单次限购数量不能为空")
     private Integer singleLimitCount;
 
-    // TODO @puhui999:是不是弄成 2 个字段会好点哈。开始、结束
-    @Schema(description = "活动时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "[2022-07-01 00:00:00,2022-07-01 23:59:59]")
+    @Schema(description = "活动时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "[2022-07-01 23:59:59]")
     @NotNull(message = "活动时间不能为空")
     @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
-    private LocalDateTime[] activityTime;
+    private LocalDateTime startTime;
+
+    @Schema(description = "活动时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "[2022-07-01 23:59:59]")
+    @NotNull(message = "活动时间不能为空")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime endTime;
 
     @Schema(description = "开团人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "25222")
     @NotNull(message = "开团人数不能为空")

+ 3 - 14
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/SeckillActivityController.java

@@ -2,7 +2,6 @@ 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.*;
@@ -19,11 +18,11 @@ import org.springframework.web.bind.annotation.*;
 
 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;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
 
 @Tag(name = "管理后台 - 秒杀活动")
 @RestController
@@ -79,24 +78,14 @@ public class SeckillActivityController {
         return success(SeckillActivityConvert.INSTANCE.convert(seckillActivity, seckillProducts));
     }
 
-    // TODO @puhui999:是不是可以删掉,貌似没用?
-    @GetMapping("/list")
-    @Operation(summary = "获得秒杀活动列表")
-    @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
-    @PreAuthorize("@ss.hasPermission('promotion:seckill-activity:query')")
-    public CommonResult<List<SeckillActivityRespVO>> getSeckillActivityList(@RequestParam("ids") Collection<Long> ids) {
-        List<SeckillActivityDO> list = seckillActivityService.getSeckillActivityList(ids);
-        return success(SeckillActivityConvert.INSTANCE.complementList(list));
-    }
-
     @GetMapping("/page")
     @Operation(summary = "获得秒杀活动分页")
     @PreAuthorize("@ss.hasPermission('promotion:seckill-activity:query')")
     public CommonResult<PageResult<SeckillActivityRespVO>> getSeckillActivityPage(@Valid SeckillActivityPageReqVO pageVO) {
         PageResult<SeckillActivityDO> pageResult = seckillActivityService.getSeckillActivityPage(pageVO);
-        Set<Long> aIds = CollectionUtils.convertSet(pageResult.getList(), SeckillActivityDO::getId);
+        Set<Long> aIds = convertSet(pageResult.getList(), SeckillActivityDO::getId);
         List<SeckillProductDO> seckillProducts = seckillActivityService.getSeckillProductListByActivityId(aIds);
-        Set<Long> spuIds = CollectionUtils.convertSet(pageResult.getList(), SeckillActivityDO::getSpuId);
+        Set<Long> spuIds = convertSet(pageResult.getList(), SeckillActivityDO::getSpuId);
         List<ProductSpuRespDTO> spuList = spuApi.getSpuList(spuIds);
         return success(SeckillActivityConvert.INSTANCE.convertPage(pageResult, seckillProducts, spuList));
     }

+ 95 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/bargain/BargainActivityConvert.java

@@ -0,0 +1,95 @@
+package cn.iocoder.yudao.module.promotion.convert.bargain;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
+import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
+import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityCreateReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityRespVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityUpdateReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.product.BargainProductBaseVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.product.BargainProductRespVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.product.BargainProductUpdateReqVO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainProductDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Mappings;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+import java.util.Map;
+
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
+
+/**
+ * 拼团活动 Convert
+ *
+ * @author HUIHUI
+ */
+@Mapper
+public interface BargainActivityConvert {
+
+    BargainActivityConvert INSTANCE = Mappers.getMapper(BargainActivityConvert.class);
+
+    BargainActivityDO convert(BargainActivityCreateReqVO bean);
+
+    BargainActivityDO convert(BargainActivityUpdateReqVO bean);
+
+    BargainActivityRespVO convert(BargainActivityDO bean);
+
+    BargainProductRespVO convert(BargainProductDO bean);
+
+    default BargainActivityRespVO convert(BargainActivityDO bean, List<BargainProductDO> productDOs) {
+        return convert(bean).setProducts(convertList2(productDOs));
+    }
+
+    List<BargainActivityRespVO> convertList(List<BargainActivityDO> list);
+
+    PageResult<BargainActivityRespVO> convertPage(PageResult<BargainActivityDO> page);
+
+    default PageResult<BargainActivityRespVO> convertPage(PageResult<BargainActivityDO> page,
+                                                          List<BargainProductDO> productList,
+                                                          List<ProductSpuRespDTO> spuList) {
+        Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);
+        PageResult<BargainActivityRespVO> pageResult = convertPage(page);
+        pageResult.getList().forEach(item -> {
+            MapUtils.findAndThen(spuMap, item.getSpuId(), spu -> {
+                item.setSpuName(spu.getName());
+                item.setPicUrl(spu.getPicUrl());
+            });
+            item.setProducts(convertList2(productList));
+        });
+        return pageResult;
+    }
+
+    List<BargainProductRespVO> convertList2(List<BargainProductDO> productDOs);
+
+    @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 = "bargainFirstPrice", source = "vo.bargainFirstPrice"),
+            @Mapping(target = "bargainPrice", source = "vo.bargainPrice"),
+            @Mapping(target = "stock", source = "vo.stock"),
+            @Mapping(target = "activityStartTime", source = "activityDO.startTime"),
+            @Mapping(target = "activityEndTime", source = "activityDO.endTime")
+    })
+    BargainProductDO convert(BargainActivityDO activityDO, BargainProductBaseVO vo);
+
+    default List<BargainProductDO> convertList(List<? extends BargainProductBaseVO> products, BargainActivityDO activityDO) {
+        return CollectionUtils.convertList(products, item -> convert(activityDO, item).setActivityStatus(activityDO.getStatus()));
+    }
+
+    default List<BargainProductDO> convertList(List<BargainProductUpdateReqVO> updateProductVOs,
+                                               List<BargainProductDO> products, BargainActivityDO activity) {
+        Map<Long, Long> productMap = convertMap(products, BargainProductDO::getSkuId, BargainProductDO::getId);
+        return CollectionUtils.convertList(updateProductVOs, updateProductVO -> convert(activity, updateProductVO)
+                .setId(productMap.get(updateProductVO.getSkuId()))
+                .setActivityStatus(activity.getStatus()));
+    }
+
+    //BargainRecordDO convert(BargainRecordCreateReqDTO reqDTO);
+
+}

+ 4 - 35
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/combination/CombinationActivityConvert.java

@@ -17,11 +17,8 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationR
 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;
 
@@ -37,38 +34,16 @@ 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<CombinationProductDO> productDOs) {
-        CombinationActivityRespVO respVO = convert(bean);
-        respVO.setProducts(convertList2(productDOs));
-        return respVO;
+        return convert(bean).setProducts(convertList2(productDOs));
     }
 
     List<CombinationActivityRespVO> convertList(List<CombinationActivityDO> list);
@@ -104,21 +79,15 @@ public interface CombinationActivityConvert {
     CombinationProductDO convert(CombinationActivityDO activityDO, CombinationProductBaseVO vo);
 
     default List<CombinationProductDO> convertList(List<? extends CombinationProductBaseVO> products, CombinationActivityDO activityDO) {
-        List<CombinationProductDO> list = new ArrayList<>();
-        products.forEach(sku -> {
-            CombinationProductDO productDO = convert(activityDO, sku);
-            productDO.setActivityStatus(activityDO.getStatus());
-            list.add(productDO);
-        });
-        return list;
+        return CollectionUtils.convertList(products, item -> convert(activityDO, item).setActivityStatus(activityDO.getStatus()));
     }
 
     default List<CombinationProductDO> convertList(List<CombinationProductUpdateReqVO> updateProductVOs,
                                                    List<CombinationProductDO> products, CombinationActivityDO activity) {
         Map<Long, Long> productMap = convertMap(products, CombinationProductDO::getSkuId, CombinationProductDO::getId);
         return CollectionUtils.convertList(updateProductVOs, updateProductVO -> convert(activity, updateProductVO)
-                        .setId(productMap.get(updateProductVO.getSkuId()))
-                        .setActivityStatus(activity.getStatus()));
+                .setId(productMap.get(updateProductVO.getSkuId()))
+                .setActivityStatus(activity.getStatus()));
     }
 
     CombinationRecordDO convert(CombinationRecordCreateReqDTO reqDTO);

+ 3 - 24
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/seckill/seckillactivity/SeckillActivityConvert.java

@@ -9,7 +9,6 @@ import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.Se
 import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityUpdateReqVO;
 import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.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;
@@ -17,12 +16,9 @@ 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;
 
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
-
 /**
  * 秒杀活动 Convert
  *
@@ -39,8 +35,7 @@ public interface SeckillActivityConvert {
 
     SeckillActivityRespVO convert(SeckillActivityDO bean);
 
-    // TODO @puhui999:这个是不是还是 convertList 好点?
-    List<SeckillActivityRespVO> complementList(List<SeckillActivityDO> list);
+    List<SeckillActivityRespVO> convertList(List<SeckillActivityDO> list);
 
     PageResult<SeckillActivityRespVO> convertPage(PageResult<SeckillActivityDO> page);
 
@@ -58,9 +53,7 @@ public interface SeckillActivityConvert {
     SeckillActivityDetailRespVO convert1(SeckillActivityDO seckillActivity);
 
     default SeckillActivityDetailRespVO convert(SeckillActivityDO seckillActivity, List<SeckillProductDO> seckillProducts) {
-        SeckillActivityDetailRespVO respVO = convert1(seckillActivity);
-        respVO.setProducts(convertList2(seckillProducts));
-        return respVO;
+        return convert1(seckillActivity).setProducts(convertList2(seckillProducts));
     }
 
     @Mappings({
@@ -77,21 +70,7 @@ public interface SeckillActivityConvert {
     SeckillProductDO convert(SeckillActivityDO activityDO, SeckillProductBaseVO vo);
 
     default List<SeckillProductDO> convertList(List<? extends SeckillProductBaseVO> products, SeckillActivityDO activityDO) {
-        List<SeckillProductDO> list = new ArrayList<>();
-        products.forEach(sku -> {
-            SeckillProductDO productDO = convert(activityDO, sku);
-            productDO.setActivityStatus(activityDO.getStatus());
-            list.add(productDO);
-        });
-        return list;
-    }
-
-    default List<SeckillProductDO> convertList(List<SeckillProductUpdateReqVO> updateProductVOs,
-                                                   List<SeckillProductDO> products, SeckillActivityDO activity) {
-        Map<Long, Long> productMap = convertMap(products, SeckillProductDO::getSkuId, SeckillProductDO::getId);
-        return CollectionUtils.convertList(updateProductVOs, updateProductVO -> convert(activity, updateProductVO)
-                .setId(productMap.get(updateProductVO.getSkuId()))
-                .setActivityStatus(activity.getStatus()));
+        return CollectionUtils.convertList(products, item -> convert(activityDO, item).setActivityStatus(activityDO.getStatus()));
     }
 
     List<SeckillProductRespVO> convertList2(List<SeckillProductDO> productDOs);

+ 5 - 15
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/bargain/BargainActivityDO.java

@@ -54,21 +54,6 @@ public class BargainActivityDO extends BaseDO {
      */
     private Long spuId;
 
-    /**
-     * 商品 SKU 编号
-     */
-    private Long skuId;
-
-    /**
-     * 应付金额,单位分
-     */
-    private Integer bargainFirstPrice;
-
-    /**
-     * 砍价底价,单位:分
-     */
-    private Integer bargainPrice;
-
     /**
      * 达到该人数,才能砍到低价
      */
@@ -79,6 +64,11 @@ public class BargainActivityDO extends BaseDO {
      */
     private Integer bargainCount;
 
+    /**
+     * 总限购数量
+     */
+    private Integer totalLimitCount;
+
     /**
      * 砍价库存
      */

+ 70 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/bargain/BargainProductDO.java

@@ -0,0 +1,70 @@
+package cn.iocoder.yudao.module.promotion.dal.dataobject.bargain;
+
+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_bargain_product")
+@KeySequence("promotion_bargain_product_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class BargainProductDO extends BaseDO {
+
+    /**
+     * 编号
+     */
+    @TableId
+    private Long id;
+    /**
+     * 砍价活动编号
+     */
+    private Long activityId;
+    /**
+     * 商品 SPU 编号
+     */
+    private Long spuId;
+    /**
+     * 商品 SKU 编号
+     */
+    private Long skuId;
+    /**
+     * 砍价商品状态
+     *
+     * 关联 {@link BargainActivityDO#getStatus()}
+     */
+    private Integer activityStatus;
+    /**
+     * 活动开始时间点
+     */
+    private LocalDateTime activityStartTime;
+    /**
+     * 活动结束时间点
+     */
+    private LocalDateTime activityEndTime;
+    /**
+     * 砍价起始价格,单位分
+     */
+    private Integer bargainFirstPrice;
+    /**
+     * 砍价底价,单位:分
+     */
+    private Integer bargainPrice;
+    /**
+     * 活动库存
+     */
+    private Integer stock;
+
+}

+ 30 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/bargain/BargainActivityMapper.java

@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.promotion.dal.mysql.bargain;
+
+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.bargain.vo.activity.BargainActivityPageReqVO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 砍价活动 Mapper
+ *
+ * @author HUIHUI
+ */
+@Mapper
+public interface BargainActivityMapper extends BaseMapperX<BargainActivityDO> {
+
+    default PageResult<BargainActivityDO> selectPage(BargainActivityPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<BargainActivityDO>()
+                .likeIfPresent(BargainActivityDO::getName, reqVO.getName())
+                .orderByDesc(BargainActivityDO::getId));
+    }
+
+    default List<BargainActivityDO> selectListByStatus(Integer status) {
+        return selectList(BargainActivityDO::getStatus, status);
+    }
+
+}

+ 37 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/bargain/BargainProductMapper.java

@@ -0,0 +1,37 @@
+package cn.iocoder.yudao.module.promotion.dal.mysql.bargain;
+
+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.bargain.vo.product.BargainProductPageReqVO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainProductDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 砍价商品 Mapper
+ *
+ * @author HUIHUI
+ */
+@Mapper
+public interface BargainProductMapper extends BaseMapperX<BargainProductDO> {
+
+    default PageResult<BargainProductDO> selectPage(BargainProductPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<BargainProductDO>()
+                .eqIfPresent(BargainProductDO::getActivityId, reqVO.getActivityId())
+                .eqIfPresent(BargainProductDO::getSpuId, reqVO.getSpuId())
+                .eqIfPresent(BargainProductDO::getSkuId, reqVO.getSkuId())
+                .eqIfPresent(BargainProductDO::getActivityStatus, reqVO.getActivityStatus())
+                .betweenIfPresent(BargainProductDO::getActivityStartTime, reqVO.getActivityStartTime())
+                .betweenIfPresent(BargainProductDO::getActivityEndTime, reqVO.getActivityEndTime())
+                .betweenIfPresent(BargainProductDO::getCreateTime, reqVO.getCreateTime())
+                .orderByDesc(BargainProductDO::getId));
+    }
+
+    default List<BargainProductDO> selectListByActivityIds(Collection<Long> ids) {
+        return selectList(BargainProductDO::getActivityId, ids);
+    }
+
+}

+ 48 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/bargain/BargainRecordMapper.java

@@ -0,0 +1,48 @@
+package cn.iocoder.yudao.module.promotion.dal.mysql.bargain;
+
+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.bargain.BargainRecordDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 砍价记录 Mapper
+ *
+ * @author HUIHUI
+ */
+@Mapper
+public interface BargainRecordMapper extends BaseMapperX<BargainRecordDO> {
+
+    // TODO @puhui999:selectByUserIdAndOrderId
+    default BargainRecordDO selectRecord(Long userId, Long orderId) {
+        return selectOne(BargainRecordDO::getUserId, userId,
+                BargainRecordDO::getOrderId, orderId);
+    }
+
+    /**
+     * 查询砍价记录
+     *
+     * @param headId     团长编号
+     * @param activityId 活动编号
+     * @return 砍价记录
+     */
+    default BargainRecordDO selectRecordByHeadId(Long headId, Long activityId, Integer status) {
+        return selectOne(new LambdaQueryWrapperX<BargainRecordDO>()
+                .eq(BargainRecordDO::getUserId, headId)
+                .eq(BargainRecordDO::getActivityId, activityId)
+                .eq(BargainRecordDO::getStatus, status));
+    }
+
+    default List<BargainRecordDO> selectListByHeadIdAndStatus(Long headId, Integer status) {
+        return selectList(new LambdaQueryWrapperX<BargainRecordDO>()
+                //.eq(BargainRecordDO::getHeadId, headId)
+                .eq(BargainRecordDO::getStatus, status));
+    }
+
+    default List<BargainRecordDO> selectListByStatus(Integer status) {
+        return selectList(BargainRecordDO::getStatus, status);
+    }
+
+}

+ 4 - 3
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/CombinationRecordMapper.java

@@ -29,9 +29,10 @@ public interface CombinationRecordMapper extends BaseMapperX<CombinationRecordDO
      * @return 拼团记录
      */
     default CombinationRecordDO selectRecordByHeadId(Long headId, Long activityId, Integer status) {
-        return selectOne(CombinationRecordDO::getHeadId, headId,
-                CombinationRecordDO::getActivityId, activityId,
-                CombinationRecordDO::getStatus, status);
+        return selectOne(new LambdaQueryWrapperX<CombinationRecordDO>()
+                .eq(CombinationRecordDO::getUserId, headId)
+                .eq(CombinationRecordDO::getActivityId, activityId)
+                .eq(CombinationRecordDO::getStatus, status));
     }
 
     default List<CombinationRecordDO> selectListByHeadIdAndStatus(Long headId, Integer status) {

+ 75 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainActivityService.java

@@ -0,0 +1,75 @@
+package cn.iocoder.yudao.module.promotion.service.bargain;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityCreateReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityPageReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityUpdateReqVO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainProductDO;
+
+import javax.validation.Valid;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 砍价活动 Service 接口
+ *
+ * @author HUIHUI
+ */
+public interface BargainActivityService {
+
+    /**
+     * 创建砍价活动
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createBargainActivity(@Valid BargainActivityCreateReqVO createReqVO);
+
+    /**
+     * 更新砍价活动
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateBargainActivity(@Valid BargainActivityUpdateReqVO updateReqVO);
+
+    /**
+     * 删除砍价活动
+     *
+     * @param id 编号
+     */
+    void deleteBargainActivity(Long id);
+
+    /**
+     * 获得砍价活动
+     *
+     * @param id 编号
+     * @return 砍价活动
+     */
+    BargainActivityDO getBargainActivity(Long id);
+
+    /**
+     * 获得砍价活动列表
+     *
+     * @param ids 编号
+     * @return 砍价活动列表
+     */
+    List<BargainActivityDO> getBargainActivityList(Collection<Long> ids);
+
+    /**
+     * 获得砍价活动分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 砍价活动分页
+     */
+    PageResult<BargainActivityDO> getBargainActivityPage(BargainActivityPageReqVO pageReqVO);
+
+    /**
+     * 获得砍价活动商品列表
+     *
+     * @param ids 砍价活动 ids
+     * @return 砍价活动的商品列表
+     */
+    List<BargainProductDO> getBargainProductsByActivityIds(Collection<Long> ids);
+
+}

+ 51 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainRecordService.java

@@ -0,0 +1,51 @@
+package cn.iocoder.yudao.module.promotion.service.bargain;
+
+
+import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainRecordDO;
+
+import java.time.LocalDateTime;
+
+/**
+ * 商品活动记录 service
+ *
+ * @author HUIHUI
+ */
+public interface BargainRecordService {
+
+    /**
+     * 更新砍价状态
+     *
+     * @param userId  用户编号
+     * @param orderId 订单编号
+     * @param status  状态
+     */
+    void updateBargainRecordStatusByUserIdAndOrderId(Long userId, Long orderId, Integer status);
+
+    ///**
+    // * 创建砍价记录
+    // *
+    // * @param reqDTO 创建信息
+    // */
+    //void createBargainRecord(BargainRecordCreateReqDTO reqDTO);
+
+    /**
+     * 更新砍价状态和开始时间
+     *
+     * @param userId    用户编号
+     * @param orderId   订单编号
+     * @param status    状态
+     * @param startTime 开始时间
+     */
+    void updateBargainRecordStatusAndStartTimeByUserIdAndOrderId(Long userId, Long orderId,
+                                                                 Integer status, LocalDateTime startTime);
+
+    /**
+     * 获得砍价状态
+     *
+     * @param userId  用户编号
+     * @param orderId 订单编号
+     * @return 砍价状态
+     */
+    BargainRecordDO getBargainRecord(Long userId, Long orderId);
+
+}

+ 293 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainServiceImpl.java

@@ -0,0 +1,293 @@
+package cn.iocoder.yudao.module.promotion.service.bargain;
+
+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.controller.admin.bargain.vo.activity.BargainActivityCreateReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityPageReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityUpdateReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.product.BargainProductCreateReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.product.BargainProductUpdateReqVO;
+import cn.iocoder.yudao.module.promotion.convert.bargain.BargainActivityConvert;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainProductDO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainRecordDO;
+import cn.iocoder.yudao.module.promotion.dal.mysql.bargain.BargainActivityMapper;
+import cn.iocoder.yudao.module.promotion.dal.mysql.bargain.BargainProductMapper;
+import cn.iocoder.yudao.module.promotion.dal.mysql.bargain.BargainRecordMapper;
+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 static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.anyMatch;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue;
+import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS;
+import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
+import static cn.iocoder.yudao.module.promotion.util.PromotionUtils.validateProductSkuAllExists;
+
+/**
+ * 砍价活动 Service 实现类
+ *
+ * @author HUIHUI
+ */
+@Service
+@Validated
+public class BargainServiceImpl implements BargainActivityService, BargainRecordService {
+
+    @Resource
+    private BargainActivityMapper bargainActivityMapper;
+    @Resource
+    private BargainRecordMapper recordMapper;
+    @Resource
+    private BargainProductMapper bargainProductMapper;
+    @Resource
+    private ProductSpuApi productSpuApi;
+    @Resource
+    private ProductSkuApi productSkuApi;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Long createBargainActivity(BargainActivityCreateReqVO createReqVO) {
+        // 校验商品 SPU 是否存在是否参加的别的活动
+        validateProductBargainConflict(createReqVO.getSpuId(), null);
+        // 获取所选 spu下的所有 sku
+        List<ProductSkuRespDTO> skus = productSkuApi.getSkuListBySpuId(CollectionUtil.newArrayList(createReqVO.getSpuId()));
+        // 校验商品 sku 是否存在
+        validateProductSkuAllExists(skus, createReqVO.getProducts(), BargainProductCreateReqVO::getSkuId);
+
+        // 插入砍价活动
+        BargainActivityDO activityDO = BargainActivityConvert.INSTANCE.convert(createReqVO);
+        // TODO 营销相关属性初始化 砍价成功更新相关属性
+        activityDO.setSuccessCount(0);
+        // 活动总库存
+        activityDO.setStock(getSumValue(createReqVO.getProducts(), BargainProductCreateReqVO::getStock, Integer::sum));
+        activityDO.setStatus(CommonStatusEnum.ENABLE.getStatus());
+        bargainActivityMapper.insert(activityDO);
+        // 插入商品
+        List<BargainProductDO> productDOs = BargainActivityConvert.INSTANCE.convertList(createReqVO.getProducts(), activityDO);
+        bargainProductMapper.insertBatch(productDOs);
+        // 返回
+        return activityDO.getId();
+    }
+
+    private void validateProductBargainConflict(Long spuId, Long activityId) {
+        // 校验商品 spu 是否存在
+        List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(CollUtil.newArrayList(spuId));
+        if (CollUtil.isEmpty(spuList)) {
+            throw exception(SPU_NOT_EXISTS);
+        }
+        // 查询所有开启的砍价活动
+        List<BargainActivityDO> activityDOs = bargainActivityMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus());
+        // 更新时排除自己
+        if (activityId != null) {
+            activityDOs.removeIf(item -> ObjectUtil.equal(item.getId(), activityId));
+        }
+        // 校验商品 spu 是否参加了其它活动
+        if (anyMatch(activityDOs, s -> ObjectUtil.equal(s.getId(), spuId))) {
+            throw exception(BARGAIN_ACTIVITY_SPU_CONFLICTS);
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateBargainActivity(BargainActivityUpdateReqVO updateReqVO) {
+        // 校验存在
+        BargainActivityDO activityDO = validateBargainActivityExists(updateReqVO.getId());
+        // 校验状态
+        if (ObjectUtil.equal(activityDO.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
+            throw exception(BARGAIN_ACTIVITY_STATUS_DISABLE);
+        }
+        // 校验商品冲突
+        validateProductBargainConflict(updateReqVO.getSpuId(), updateReqVO.getId());
+        // 获取所选 spu下的所有 sku
+        List<ProductSkuRespDTO> skus = productSkuApi.getSkuListBySpuId(CollectionUtil.newArrayList(updateReqVO.getSpuId()));
+        // 校验商品 sku 是否存在
+        validateProductSkuAllExists(skus, updateReqVO.getProducts(), BargainProductUpdateReqVO::getSkuId);
+
+        // 更新
+        BargainActivityDO updateObj = BargainActivityConvert.INSTANCE.convert(updateReqVO);
+        // 更新活动库存
+        updateObj.setStock(getSumValue(updateReqVO.getProducts(), BargainProductUpdateReqVO::getStock, Integer::sum));
+        bargainActivityMapper.updateById(updateObj);
+        // 更新商品
+        updateBargainProduct(updateObj, updateReqVO.getProducts());
+    }
+
+    /**
+     * 更新砍价商品
+     *
+     * @param updateObj 更新的活动
+     * @param products  商品配置
+     */
+    private void updateBargainProduct(BargainActivityDO updateObj, List<BargainProductUpdateReqVO> products) {
+        // 默认全部新增
+        List<BargainProductDO> defaultNewList = BargainActivityConvert.INSTANCE.convertList(products, updateObj);
+        // 数据库中的老数据
+        List<BargainProductDO> oldList = bargainProductMapper.selectListByActivityIds(CollUtil.newArrayList(updateObj.getId()));
+        List<List<BargainProductDO>> lists = CollectionUtils.diffList(oldList, defaultNewList, (oldVal, newVal) -> {
+            boolean same = ObjectUtil.equal(oldVal.getSkuId(), newVal.getSkuId());
+            if (same) {
+                newVal.setId(oldVal.getId());
+            }
+            return same;
+        });
+
+        // create
+        if (CollUtil.isNotEmpty(lists.get(0))) {
+            bargainProductMapper.insertBatch(lists.get(0));
+        }
+        // update
+        if (CollUtil.isNotEmpty(lists.get(1))) {
+            bargainProductMapper.updateBatch(lists.get(1));
+        }
+        // delete
+        if (CollUtil.isNotEmpty(lists.get(2))) {
+            bargainProductMapper.deleteBatchIds(CollectionUtils.convertList(lists.get(2), BargainProductDO::getId));
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteBargainActivity(Long id) {
+        // 校验存在
+        BargainActivityDO activityDO = validateBargainActivityExists(id);
+        // 校验状态
+        if (ObjectUtil.equal(activityDO.getStatus(), CommonStatusEnum.ENABLE.getStatus())) {
+            throw exception(BARGAIN_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END);
+        }
+
+        // 删除
+        bargainActivityMapper.deleteById(id);
+    }
+
+    private BargainActivityDO validateBargainActivityExists(Long id) {
+        BargainActivityDO activityDO = bargainActivityMapper.selectById(id);
+        if (activityDO == null) {
+            throw exception(BARGAIN_ACTIVITY_NOT_EXISTS);
+        }
+        return activityDO;
+    }
+
+    @Override
+    public BargainActivityDO getBargainActivity(Long id) {
+        return validateBargainActivityExists(id);
+    }
+
+    @Override
+    public List<BargainActivityDO> getBargainActivityList(Collection<Long> ids) {
+        return bargainActivityMapper.selectBatchIds(ids);
+    }
+
+    @Override
+    public PageResult<BargainActivityDO> getBargainActivityPage(BargainActivityPageReqVO pageReqVO) {
+        return bargainActivityMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public List<BargainProductDO> getBargainProductsByActivityIds(Collection<Long> ids) {
+        return bargainProductMapper.selectListByActivityIds(ids);
+    }
+
+    @Override
+    public void updateBargainRecordStatusByUserIdAndOrderId(Long userId, Long orderId, Integer status) {
+        // 校验砍价是否存在
+        // 更新状态
+        recordMapper.updateById(validateBargainRecord(userId, orderId).setStatus(status));
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateBargainRecordStatusAndStartTimeByUserIdAndOrderId(Long userId, Long orderId, Integer status, LocalDateTime startTime) {
+        BargainRecordDO recordDO = validateBargainRecord(userId, orderId);
+        // 更新状态
+        recordDO.setStatus(status);
+        // 更新开始时间
+        //recordDO.setStartTime(startTime);
+        //recordMapper.updateById(recordDO);
+        //
+        //// 更新砍价参入人数
+        //List<BargainRecordDO> recordDOs = recordMapper.selectListByHeadIdAndStatus(recordDO.getHeadId(), status);
+        //if (CollUtil.isNotEmpty(recordDOs)) {
+        //    recordDOs.forEach(item -> {
+        //        item.setUserCount(recordDOs.size());
+        //        // 校验砍价是否满足要求
+        //        if (ObjectUtil.equal(recordDOs.size(), recordDO.getUserSize())) {
+        //            item.setStatus(BargainRecordStatusEnum.SUCCESS.getStatus());
+        //        }
+        //    });
+        //}
+        //recordMapper.updateBatch(recordDOs);
+    }
+
+    private BargainRecordDO validateBargainRecord(Long userId, Long orderId) {
+        // 校验砍价是否存在
+        BargainRecordDO recordDO = recordMapper.selectRecord(userId, orderId);
+        if (recordDO == null) {
+            throw exception(BARGAIN_RECORD_NOT_EXISTS);
+        }
+        return recordDO;
+    }
+
+    //@Override
+    //public void createBargainRecord(BargainRecordCreateReqDTO reqDTO) {
+    //    // 1.1 校验砍价活动
+    //    BargainActivityDO activity = validateBargainActivityExists(reqDTO.getActivityId());
+    //    // 1.2 需要校验下,他当前是不是已经参加了该砍价;
+    //    BargainRecordDO recordDO = recordMapper.selectRecord(reqDTO.getUserId(), reqDTO.getOrderId());
+    //    if (recordDO != null) {
+    //        throw exception(BARGAIN_RECORD_EXISTS);
+    //    }
+    //    // 1.3 父砍价是否存在,是否已经满了
+    //    if (reqDTO.getHeadId() != null) {
+    //        BargainRecordDO recordDO1 = recordMapper.selectRecordByHeadId(reqDTO.getHeadId(), reqDTO.getActivityId(), BargainRecordStatusEnum.IN_PROGRESS.getStatus());
+    //        if (recordDO1 == null) {
+    //            throw exception(BARGAIN_RECORD_HEAD_NOT_EXISTS);
+    //        }
+    //        // 校验砍价是否满足要求
+    //        if (ObjectUtil.equal(recordDO1.getUserCount(), recordDO1.getUserSize())) {
+    //            throw exception(BARGAIN_RECORD_USER_FULL);
+    //        }
+    //    }
+    //    // TODO @puhui999:应该还有一些校验,后续补噶;例如说,一个团,自己已经参与进去了,不能再参与进去;
+    //
+    //    // 2. 创建砍价记录
+    //    BargainRecordDO record = BargainActivityConvert.INSTANCE.convert(reqDTO);
+    //    if (reqDTO.getHeadId() == null) {
+    //        // TODO @puhui999:不是自己呀;headId 是父团长的 BargainRecordDO.id 哈
+    //        record.setHeadId(reqDTO.getUserId());
+    //    }
+    //    record.setVirtualGroup(false);
+    //    // TODO @puhui999:过期时间,应该是 Date 哈;
+    //    record.setExpireTime(activity.getLimitDuration());
+    //    record.setUserSize(activity.getUserSize());
+    //    recordMapper.insert(record);
+    //}
+
+    @Override
+    public BargainRecordDO getBargainRecord(Long userId, Long orderId) {
+        return validateBargainRecord(userId, orderId);
+    }
+
+    /**
+     * APP 端获取开团记录
+     *
+     * @return 开团记录
+     */
+    public List<BargainRecordDO> getRecordListByStatus(Integer status) {
+        return recordMapper.selectListByStatus(status);
+    }
+
+}

+ 32 - 35
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationServiceImpl.java

@@ -2,12 +2,10 @@ package cn.iocoder.yudao.module.promotion.service.combination;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.CollectionUtil;
-import cn.hutool.core.map.MapUtil;
 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.collection.MapUtils;
 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;
@@ -32,7 +30,8 @@ import org.springframework.validation.annotation.Validated;
 
 import javax.annotation.Resource;
 import java.time.LocalDateTime;
-import java.util.*;
+import java.util.Collection;
+import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS;
@@ -60,19 +59,20 @@ public class CombinationServiceImpl implements CombinationActivityService, Combi
     private ProductSkuApi productSkuApi;
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public Long createCombinationActivity(CombinationActivityCreateReqVO createReqVO) {
         // 校验商品 SPU 是否存在是否参加的别的活动
         validateProductCombinationConflict(createReqVO.getSpuId(), null);
         // 获取所选 spu下的所有 sku
         List<ProductSkuRespDTO> skus = productSkuApi.getSkuListBySpuId(CollectionUtil.newArrayList(createReqVO.getSpuId()));
         // 校验商品 sku 是否存在
-        validateProductSkuAllExists(createReqVO.getProducts(), skus, CombinationProductCreateReqVO::getSkuId);
+        validateProductSkuAllExists(skus, createReqVO.getProducts(), CombinationProductCreateReqVO::getSkuId);
 
         // TODO 艿艿 有个小问题:现在有活动时间和限制时长,活动时间的结束时间早于设置的限制时间怎么算状态比如:
         //  活动时间 2023-08-05 15:00:00 - 2023-08-05 15:20:00 限制时长 2小时,那么活动时间结束就结束还是加时到满两小时
         // 插入拼团活动
         CombinationActivityDO activityDO = CombinationActivityConvert.INSTANCE.convert(createReqVO);
-        // TODO 营销相关属性初始化
+        // TODO 营销相关属性初始化 拼团成功更新相关属性
         activityDO.setTotalNum(0);
         activityDO.setSuccessNum(0);
         activityDO.setOrderUserCount(0);
@@ -106,6 +106,7 @@ public class CombinationServiceImpl implements CombinationActivityService, Combi
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public void updateCombinationActivity(CombinationActivityUpdateReqVO updateReqVO) {
         // 校验存在
         CombinationActivityDO activityDO = validateCombinationActivityExists(updateReqVO.getId());
@@ -118,7 +119,7 @@ public class CombinationServiceImpl implements CombinationActivityService, Combi
         // 获取所选 spu下的所有 sku
         List<ProductSkuRespDTO> skus = productSkuApi.getSkuListBySpuId(CollectionUtil.newArrayList(updateReqVO.getSpuId()));
         // 校验商品 sku 是否存在
-        validateProductSkuAllExists(updateReqVO.getProducts(), skus, CombinationProductUpdateReqVO::getSkuId);
+        validateProductSkuAllExists(skus, updateReqVO.getProducts(), CombinationProductUpdateReqVO::getSkuId);
 
         // 更新
         CombinationActivityDO updateObj = CombinationActivityConvert.INSTANCE.convert(updateReqVO);
@@ -128,44 +129,40 @@ public class CombinationServiceImpl implements CombinationActivityService, Combi
     }
 
     /**
-     * 更新秒杀商品
+     * 更新拼团商品
      *
-     * @param updateObj DO
+     * @param updateObj 更新的活动
      * @param products  商品配置
      */
     private void updateCombinationProduct(CombinationActivityDO updateObj, List<CombinationProductUpdateReqVO> products) {
-        List<CombinationProductDO> combinationProductDOs = combinationProductMapper.selectListByActivityIds(CollUtil.newArrayList(updateObj.getId()));
-        // 数据库中的活动商品
-        Set<Long> convertSet = CollectionUtils.convertSet(combinationProductDOs, CombinationProductDO::getSkuId);
-        // 前端传过来的活动商品
-        Set<Long> convertSet1 = CollectionUtils.convertSet(products, CombinationProductUpdateReqVO::getSkuId);
-        // 分化数据
-        // TODO @芋艿:看下这个实现
-        Map<String, List<CombinationProductDO>> data = CollectionUtils.convertCDUMap(convertSet1, convertSet, mapData -> {
-            HashMap<String, List<CombinationProductDO>> cdu = MapUtil.newHashMap(3);
-            MapUtils.findAndThen(mapData, "create", list -> {
-                cdu.put("create", CombinationActivityConvert.INSTANCE.convertList(CollectionUtils.filterList(products, item -> list.contains(item.getSkuId())), updateObj));
-            });
-            MapUtils.findAndThen(mapData, "delete", list -> {
-                cdu.put("create", CollectionUtils.filterList(combinationProductDOs, item -> list.contains(item.getSkuId())));
-            });
-            // TODO @芋艿:临时注释,避免有问题
-//            MapUtils.findAndThen(mapData, "update", list -> {
-//                cdu.put("update", CombinationActivityConvert.INSTANCE.convertList(
-//                        combinationProductDOs,
-//                        CollectionUtils.filterList(products, item -> list.contains(item.getSkuId())), updateObj));
-//            });
-            return cdu;
+        // 默认全部新增
+        List<CombinationProductDO> defaultNewList = CombinationActivityConvert.INSTANCE.convertList(products, updateObj);
+        // 数据库中的老数据
+        List<CombinationProductDO> oldList = combinationProductMapper.selectListByActivityIds(CollUtil.newArrayList(updateObj.getId()));
+        List<List<CombinationProductDO>> lists = CollectionUtils.diffList(oldList, defaultNewList, (oldVal, newVal) -> {
+            boolean same = ObjectUtil.equal(oldVal.getSkuId(), newVal.getSkuId());
+            if (same) {
+                newVal.setId(oldVal.getId());
+            }
+            return same;
         });
 
-        // 执行增删改
-        MapUtils.findAndThen(data, "create", item -> combinationProductMapper.insertBatch(item));
-        MapUtils.findAndThen(data, "delete", item -> combinationProductMapper.deleteBatchIds(
-                CollectionUtils.convertSet(item, CombinationProductDO::getId)));
-        MapUtils.findAndThen(data, "update", item -> combinationProductMapper.updateBatch(item));
+        // create
+        if (CollUtil.isNotEmpty(lists.get(0))) {
+            combinationProductMapper.insertBatch(lists.get(0));
+        }
+        // update
+        if (CollUtil.isNotEmpty(lists.get(1))) {
+            combinationProductMapper.updateBatch(lists.get(1));
+        }
+        // delete
+        if (CollUtil.isNotEmpty(lists.get(2))) {
+            combinationProductMapper.deleteBatchIds(CollectionUtils.convertList(lists.get(2), CombinationProductDO::getId));
+        }
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public void deleteCombinationActivity(Long id) {
         // 校验存在
         CombinationActivityDO activityDO = validateCombinationActivityExists(id);

+ 36 - 39
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/seckillactivity/SeckillActivityServiceImpl.java

@@ -1,12 +1,9 @@
 package cn.iocoder.yudao.module.promotion.service.seckill.seckillactivity;
 
 import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.map.MapUtil;
 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.collection.MapUtils;
 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;
@@ -28,9 +25,13 @@ import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
 
 import javax.annotation.Resource;
-import java.util.*;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
 
+import static cn.hutool.core.collection.CollUtil.isNotEmpty;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
 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.*;
@@ -71,7 +72,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
         // 插入秒杀活动
         SeckillActivityDO activity = SeckillActivityConvert.INSTANCE.convert(createReqVO)
                 .setStatus(PromotionUtils.calculateActivityStatus(createReqVO.getEndTime()))
-                .setTotalStock(CollectionUtils.getSumValue(createReqVO.getProducts(), SeckillProductCreateReqVO::getStock, Integer::sum));
+                .setTotalStock(getSumValue(createReqVO.getProducts(), SeckillProductCreateReqVO::getStock, Integer::sum));
         seckillActivityMapper.insert(activity);
         // 插入商品
         List<SeckillProductDO> products = SeckillActivityConvert.INSTANCE.convertList(createReqVO.getProducts(), activity);
@@ -94,16 +95,16 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
             activityDOs.removeIf(item -> ObjectUtil.equal(item.getId(), activityId));
         }
         // 过滤出所有 spuId 有交集的活动
-        List<SeckillActivityDO> activityDOs1 = CollectionUtils.convertList(activityDOs, c -> c, s -> ObjectUtil.equal(s.getSpuId(), spuId));
-        if (CollUtil.isNotEmpty(activityDOs1)) {
+        List<SeckillActivityDO> activityDOs1 = convertList(activityDOs, c -> c, s -> ObjectUtil.equal(s.getSpuId(), spuId));
+        if (isNotEmpty(activityDOs1)) {
             throw exception(SECKILL_ACTIVITY_SPU_CONFLICTS);
         }
-        List<SeckillActivityDO> activityDOs2 = CollectionUtils.convertList(activityDOs, c -> c, s -> {
+        List<SeckillActivityDO> activityDOs2 = convertList(activityDOs, c -> c, s -> {
             // 判断秒杀时段是否有交集
-            return CollectionUtils.containsAny(s.getConfigIds(), configIds);
+            return containsAny(s.getConfigIds(), configIds);
         });
 
-        if (CollUtil.isNotEmpty(activityDOs2)) {
+        if (isNotEmpty(activityDOs2)) {
             throw exception(SECKILL_TIME_CONFLICTS);
         }
     }
@@ -121,12 +122,12 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
         // 获取所选 spu下的所有 sku
         List<ProductSkuRespDTO> skus = productSkuApi.getSkuListBySpuId(CollUtil.newArrayList(updateReqVO.getSpuId()));
         // 校验商品 sku 是否存在
-        validateProductSkuAllExists(updateReqVO.getProducts(), skus, SeckillProductUpdateReqVO::getSkuId);
+        validateProductSkuAllExists(skus, updateReqVO.getProducts(), SeckillProductUpdateReqVO::getSkuId);
 
         // 更新活动
         SeckillActivityDO updateObj = SeckillActivityConvert.INSTANCE.convert(updateReqVO)
                 .setStatus(PromotionUtils.calculateActivityStatus(updateReqVO.getEndTime()))
-                .setTotalStock(CollectionUtils.getSumValue(updateReqVO.getProducts(), SeckillProductUpdateReqVO::getStock, Integer::sum));
+                .setTotalStock(getSumValue(updateReqVO.getProducts(), SeckillProductUpdateReqVO::getStock, Integer::sum));
         seckillActivityMapper.updateById(updateObj);
         // 更新商品
         updateSeckillProduct(updateObj, updateReqVO.getProducts());
@@ -139,36 +140,32 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
      * @param updateObj 更新的活动
      * @param products  商品配置
      */
-    // TODO @puhui999:我在想,我们是不是可以封装一个 CollUtil 的方法,传入两个数组,判断出哪些是新增、哪些是修改、哪些是删除;
-    // 例如说,products 先转化成 SeckillProductDO;然后,基于一个 func(key1, key2) 做比对;
-    // 如果可以跑通,所有涉及到这种逻辑的,都可以服用哈。
     private void updateSeckillProduct(SeckillActivityDO updateObj, List<SeckillProductUpdateReqVO> products) {
+        // 默认全部新增
+        List<SeckillProductDO> defaultNewList = SeckillActivityConvert.INSTANCE.convertList(products, updateObj);
         // 数据库中的活动商品
-        List<SeckillProductDO> seckillProductDOs = seckillProductMapper.selectListByActivityId(updateObj.getId());
-        Set<Long> dbSkuIds = CollectionUtils.convertSet(seckillProductDOs, SeckillProductDO::getSkuId);
-        Set<Long> voSkuIds = CollectionUtils.convertSet(products, SeckillProductUpdateReqVO::getSkuId);
-        Map<String, List<SeckillProductDO>> data = CollectionUtils.convertCDUMap(voSkuIds, dbSkuIds, mapData -> {
-            HashMap<String, List<SeckillProductDO>> cdu = MapUtil.newHashMap(3);
-            MapUtils.findAndThen(mapData, "create", list -> {
-                cdu.put("create", SeckillActivityConvert.INSTANCE.convertList(
-                        CollectionUtils.filterList(products, item -> list.contains(item.getSkuId())), updateObj));
-            });
-            MapUtils.findAndThen(mapData, "delete", list -> {
-                cdu.put("create", CollectionUtils.filterList(seckillProductDOs, item -> list.contains(item.getSkuId())));
-            });
-            // TODO @芋艿:临时注释
-//            MapUtils.findAndThen(mapData, "update", list -> {
-//                cdu.put("update", SeckillActivityConvert.INSTANCE.convertList(seckillProductDOs,
-//                        CollectionUtils.filterList(products, item -> list.contains(item.getSkuId())), updateObj));
-//            });
-            return cdu;
+        List<SeckillProductDO> oldList = seckillProductMapper.selectListByActivityId(updateObj.getId());
+        // 对比老、新两个列表,找出新增、修改、删除的数据
+        List<List<SeckillProductDO>> lists = diffList(oldList, defaultNewList, (oldVal, newVal) -> {
+            boolean same = ObjectUtil.equal(oldVal.getSkuId(), newVal.getSkuId());
+            if (same) {
+                newVal.setId(oldVal.getId());
+            }
+            return same;
         });
 
-        // 执行增删改
-        MapUtils.findAndThen(data, "create", item -> seckillProductMapper.insertBatch(item));
-        MapUtils.findAndThen(data, "delete", item -> seckillProductMapper.deleteBatchIds(
-                CollectionUtils.convertSet(item, SeckillProductDO::getId)));
-        MapUtils.findAndThen(data, "update", item -> seckillProductMapper.updateBatch(item));
+        // create
+        if (isNotEmpty(lists.get(0))) {
+            seckillProductMapper.insertBatch(lists.get(0));
+        }
+        // update
+        if (isNotEmpty(lists.get(1))) {
+            seckillProductMapper.updateBatch(lists.get(1));
+        }
+        // delete
+        if (isNotEmpty(lists.get(2))) {
+            seckillProductMapper.deleteBatchIds(convertList(lists.get(2), SeckillProductDO::getId));
+        }
     }
 
     @Override
@@ -198,7 +195,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
         seckillActivityMapper.deleteById(id);
         // 删除活动商品
         List<SeckillProductDO> productDOs = seckillProductMapper.selectListByActivityId(id);
-        Set<Long> convertSet = CollectionUtils.convertSet(productDOs, SeckillProductDO::getSkuId);
+        Set<Long> convertSet = convertSet(productDOs, SeckillProductDO::getSkuId);
         seckillProductMapper.deleteBatchIds(convertSet);
     }
 

+ 4 - 7
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/util/PromotionUtils.java

@@ -1,6 +1,5 @@
 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;
@@ -12,6 +11,7 @@ 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.framework.common.util.collection.CollectionUtils.anyMatch;
 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
 
 /**
@@ -31,22 +31,19 @@ public class PromotionUtils {
         return LocalDateTimeUtils.beforeNow(endTime) ? CommonStatusEnum.DISABLE.getStatus() : CommonStatusEnum.ENABLE.getStatus();
     }
 
-    // TODO @puhui999:是不是第一个参数是 sku;然后是 products;这样关联性好点;
     /**
      * 校验商品 sku 是否都存在
      *
-     * @param products 需要校验的商品
      * @param skus     数据库中的商品 skus
+     * @param products 需要校验的商品
      * @param func     获取需要校验的商品的 skuId
      */
-    public static <T> void validateProductSkuAllExists(List<T> products, List<ProductSkuRespDTO> skus, Function<T, Long> func) {
+    public static <T> void validateProductSkuAllExists(List<ProductSkuRespDTO> skus, List<T> products, Function<T, Long> func) {
         // 校验 sku 个数是否一致
         Set<Long> skuIdsSet = CollectionUtils.convertSet(products, func);
         Set<Long> skuIdsSet1 = CollectionUtils.convertSet(skus, ProductSkuRespDTO::getId);
         // 校验 skuId 是否存在
-        // TODO @puhui999:findFirst
-        List<Long> f = CollectionUtils.filterList(skuIdsSet, s -> !skuIdsSet1.contains(s));
-        if (CollUtil.isNotEmpty(f)) {
+        if (anyMatch(skuIdsSet, s -> !skuIdsSet1.contains(s))) {
             throw exception(SKU_NOT_EXISTS);
         }
     }

+ 22 - 23
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java

@@ -3,12 +3,10 @@ package cn.iocoder.yudao.module.trade.service.order;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.IdUtil;
-import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.core.KeyValue;
 import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 import cn.iocoder.yudao.module.member.api.address.AddressApi;
 import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
@@ -59,9 +57,10 @@ import javax.annotation.Resource;
 import java.time.LocalDateTime;
 import java.util.*;
 
+import static cn.hutool.core.util.ObjectUtil.equal;
+import static cn.hutool.core.util.ObjectUtil.notEqual;
 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.common.util.collection.CollectionUtils.*;
 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.*;
@@ -171,7 +170,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
         afterCreateTradeOrder(userId, createReqVO, order, orderItems, calculateRespBO);
         // 3.3 校验订单类型
         // 拼团
-        if (ObjectUtil.equal(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
+        if (equal(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
             MemberUserRespDTO user = memberUserApi.getUser(userId);
             // TODO 拼团一次应该只能选择一种规格的商品
             // TODO @puhui999:应该是前置校验哈;然后不应该设置状态,而是交给拼团记录那处理;
@@ -179,7 +178,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
                     .setStatus(CombinationRecordStatusEnum.WAITING.getStatus()));
         }
         // TODO 秒杀扣减库存是下单就扣除还是等待订单支付成功再扣除
-        if (ObjectUtil.equal(TradeOrderTypeEnum.SECKILL.getType(), order.getType())) {
+        if (equal(TradeOrderTypeEnum.SECKILL.getType(), order.getType())) {
 
         }
 
@@ -206,7 +205,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
                                           TradePriceCalculateRespBO calculateRespBO) {
         // 用户选择物流配送的时候才需要填写收货地址
         AddressRespDTO address = new AddressRespDTO();
-        if (ObjectUtil.equal(createReqVO.getDeliveryType(), DeliveryTypeEnum.EXPRESS.getMode())) {
+        if (equal(createReqVO.getDeliveryType(), DeliveryTypeEnum.EXPRESS.getMode())) {
             // 用户收件地址的校验
             address = validateAddress(userId, createReqVO.getAddressId());
         }
@@ -310,7 +309,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
         }
         // 校验活动
         // 1、拼团活动
-        if (ObjectUtil.equal(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
+        if (equal(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
             // 更新拼团状态 TODO puhui999:订单支付失败或订单支付过期删除这条拼团记录
             combinationRecordApi.updateCombinationRecordStatusAndStartTime(order.getUserId(), order.getId(), CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
         }
@@ -344,7 +343,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
             throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
         }
         // 校验支付订单匹配
-        if (ObjectUtil.notEqual(order.getPayOrderId(), payOrderId)) { // 支付单号
+        if (notEqual(order.getPayOrderId(), payOrderId)) { // 支付单号
             log.error("[validateOrderPaid][order({}) 支付单不匹配({}),请进行处理!order 数据是:{}]",
                     id, payOrderId, JsonUtils.toJsonString(order));
             throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
@@ -363,13 +362,13 @@ public class TradeOrderServiceImpl implements TradeOrderService {
             throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS);
         }
         // 校验支付金额一致
-        if (ObjectUtil.notEqual(payOrder.getPrice(), order.getPayPrice())) {
+        if (notEqual(payOrder.getPrice(), order.getPayPrice())) {
             log.error("[validateOrderPaid][order({}) payOrder({}) 支付金额不匹配,请进行处理!order 数据是:{},payOrder 数据是:{}]",
                     id, payOrderId, JsonUtils.toJsonString(order), JsonUtils.toJsonString(payOrder));
             throw exception(ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH);
         }
         // 校验支付订单匹配(二次)
-        if (ObjectUtil.notEqual(payOrder.getMerchantOrderId(), id.toString())) {
+        if (notEqual(payOrder.getMerchantOrderId(), id.toString())) {
             log.error("[validateOrderPaid][order({}) 支付单不匹配({}),请进行处理!payOrder 数据是:{}]",
                     id, payOrderId, JsonUtils.toJsonString(payOrder));
             throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
@@ -393,7 +392,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
         TradeOrderDO updateOrderObj = new TradeOrderDO();
         // 判断发货类型
         // 2.1 快递发货
-        if (ObjectUtil.equal(deliveryReqVO.getType(), DeliveryTypeEnum.EXPRESS.getMode())) {
+        if (equal(deliveryReqVO.getType(), DeliveryTypeEnum.EXPRESS.getMode())) {
             // 校验快递公司
             // TODO @puhui999:getDeliveryExpress 直接封装一个校验的,会不会好点;因为还有开启关闭啥的;
             DeliveryExpressDO deliveryExpress = deliveryExpressService.getDeliveryExpress(deliveryReqVO.getLogisticsId());
@@ -403,13 +402,13 @@ public class TradeOrderServiceImpl implements TradeOrderService {
             updateOrderObj.setLogisticsId(deliveryReqVO.getLogisticsId()).setLogisticsNo(deliveryReqVO.getLogisticsNo());
         }
         // 2.2 用户自提
-        if (ObjectUtil.equal(deliveryReqVO.getType(), DeliveryTypeEnum.PICK_UP.getMode())) {
+        if (equal(deliveryReqVO.getType(), DeliveryTypeEnum.PICK_UP.getMode())) {
             // TODO 校验自提门店是否存在
             // 重置一下确保快递公司和快递单号为空
             updateOrderObj.setLogisticsId(null).setLogisticsNo("");
         }
         // 2.3 TODO 芋艿:如果无需发货,需要怎么存储?回复:需要把 deliverType 设置为 DeliveryTypeEnum.NULL
-        if (ObjectUtil.equal(deliveryReqVO.getType(), DeliveryTypeEnum.NULL.getMode())) {
+        if (equal(deliveryReqVO.getType(), DeliveryTypeEnum.NULL.getMode())) {
             // TODO 情况一:正常走发货逻辑和用户自提有点像 不同点:不需要自提门店只需要用户确认收货
             // TODO 情况二:用户下单付款后直接确认收货或等待用户确认收货
             // 重置一下确保快递公司和快递单号为空
@@ -449,15 +448,15 @@ public class TradeOrderServiceImpl implements TradeOrderService {
         }
         // 校验订单是否是待发货状态
         if (!TradeOrderStatusEnum.isUndelivered(order.getStatus())
-                || ObjectUtil.notEqual(order.getDeliveryStatus(), TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus())) {
+                || notEqual(order.getDeliveryStatus(), TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus())) {
             throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED);
         }
         // 校验订单是否退款
-        if (ObjectUtil.notEqual(TradeOrderRefundStatusEnum.NONE.getStatus(), order.getRefundStatus())) {
+        if (notEqual(TradeOrderRefundStatusEnum.NONE.getStatus(), order.getRefundStatus())) {
             throw exception(ORDER_DELIVERY_FAIL_REFUND_STATUS_NOT_NONE);
         }
         // 订单类型:拼团
-        if (ObjectUtil.equal(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
+        if (equal(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
             // 校验订单拼团是否成功
             // TODO 用户 ID 使用当前登录用户的还是订单保存的?
             if (combinationRecordApi.isCombinationRecordSuccess(order.getUserId(), order.getId())) {
@@ -510,7 +509,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
         }
         // 校验订单是否是待收货状态
         if (!TradeOrderStatusEnum.isDelivered(order.getStatus())
-                || ObjectUtil.notEqual(order.getDeliveryStatus(), TradeOrderDeliveryStatusEnum.DELIVERED.getStatus())) {
+                || notEqual(order.getDeliveryStatus(), TradeOrderDeliveryStatusEnum.DELIVERED.getStatus())) {
             throw exception(ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED);
         }
         return order;
@@ -520,7 +519,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
     public TradeOrderDO getOrder(Long userId, Long id) {
         TradeOrderDO order = tradeOrderMapper.selectById(id);
         if (order != null
-                && ObjectUtil.notEqual(order.getUserId(), userId)) {
+                && notEqual(order.getUserId(), userId)) {
             return null;
         }
         return order;
@@ -564,7 +563,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
     public TradeOrderItemDO getOrderItem(Long userId, Long itemId) {
         TradeOrderItemDO orderItem = tradeOrderItemMapper.selectById(itemId);
         if (orderItem != null
-                && ObjectUtil.notEqual(orderItem.getUserId(), userId)) {
+                && notEqual(orderItem.getUserId(), userId)) {
             return null;
         }
         return orderItem;
@@ -644,10 +643,10 @@ public class TradeOrderServiceImpl implements TradeOrderService {
         if (order == null) {
             throw exception(ORDER_NOT_FOUND);
         }
-        if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus())) {
+        if (notEqual(order.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus())) {
             throw exception(ORDER_COMMENT_FAIL_STATUS_NOT_COMPLETED);
         }
-        if (ObjectUtil.notEqual(order.getCommentStatus(), Boolean.FALSE)) {
+        if (notEqual(order.getCommentStatus(), Boolean.FALSE)) {
             throw exception(ORDER_COMMENT_STATUS_NOT_FALSE);
         }
 
@@ -657,7 +656,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
         // 更新订单项评价状态
         tradeOrderItemMapper.updateById(new TradeOrderItemDO().setId(orderItem.getId()).setCommentStatus(Boolean.TRUE));
         List<TradeOrderItemDO> orderItems = getOrderItemListByOrderId(CollUtil.newArrayList(order.getId()));
-        if (!CollectionUtils.isAny(orderItems, item -> ObjectUtil.equal(item.getCommentStatus(), Boolean.FALSE))) {
+        if (!anyMatch(orderItems, item -> equal(item.getCommentStatus(), Boolean.FALSE))) {
             // 对于 order 来说,就是评论完,把 order 更新完合理的 status 等字段。
             tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()).setCommentStatus(Boolean.TRUE));
         }