Browse Source

feat:拼团订单集成

puhui999 2 years ago
parent
commit
259807600a
21 changed files with 498 additions and 42 deletions
  1. 39 0
      yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApi.java
  2. 77 0
      yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationRecordReqDTO.java
  3. 1 0
      yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java
  4. 38 0
      yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/combination/CombinationRecordStatusEnum.java
  5. 36 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApiImpl.java
  6. 3 3
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityBaseVO.java
  7. 1 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityExcelVO.java
  8. 1 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityExportReqVO.java
  9. 1 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityPageReqVO.java
  10. 3 3
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityRespVO.java
  11. 4 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/combination/CombinationActivityConvert.java
  12. 1 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/combinationactivity/CombinationActivityDO.java
  13. 105 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/combinationactivity/CombinationRecordDO.java
  14. 19 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/combinationactivity/CombinationRecordMapper.java
  15. 29 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityService.java
  16. 48 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityServiceImpl.java
  17. 1 0
      yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
  18. 12 5
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderDeliveryReqVO.java
  19. 0 6
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementReqVO.java
  20. 30 13
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java
  21. 49 7
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java

+ 39 - 0
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApi.java

@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.promotion.api.combination;
+
+import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO;
+
+import javax.validation.Valid;
+
+/**
+ * 拼团活动 API 接口
+ *
+ * @author HUIHUI
+ */
+public interface CombinationApi {
+
+    /**
+     * 创建开团记录
+     *
+     * @param reqDTO 请求 DTO
+     */
+    void createRecord(@Valid CombinationRecordReqDTO reqDTO);
+
+    /**
+     * 更新开团记录状态
+     *
+     * @param userId  用户编号
+     * @param orderId 订单编号
+     * @param status  状态值
+     */
+    void updateRecordStatus(Long userId, Long orderId, Integer status);
+
+    /**
+     * 更新开团记录状态和开始时间
+     *
+     * @param userId  用户编号
+     * @param orderId 订单编号
+     * @param status  状态值
+     */
+    void updateRecordStatusAndStartTime(Long userId, Long orderId, Integer status);
+
+}

+ 77 - 0
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationRecordReqDTO.java

@@ -0,0 +1,77 @@
+package cn.iocoder.yudao.module.promotion.api.combination.dto;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+/**
+ * 拼团记录 Request DTO
+ *
+ * @author HUIHUI
+ */
+@Data
+public class CombinationRecordReqDTO {
+
+    /**
+     * 拼团活动编号
+     */
+    @NotNull(message = "拼团活动编号不能为空")
+    private Long activityId;
+    /**
+     * spu 编号
+     */
+    @NotNull(message = "spu 编号不能为空")
+    private Long spuId;
+    /**
+     * sku 编号
+     */
+    @NotNull(message = "sku 编号不能为空")
+    private Long skuId;
+    /**
+     * 用户编号
+     */
+    @NotNull(message = "用户编号不能为空")
+    private Long userId;
+    /**
+     * 订单编号
+     */
+    @NotNull(message = "订单编号不能为空")
+    private Long orderId;
+    /**
+     * 团长编号
+     */
+    @NotNull(message = "团长编号不能为空")
+    private Long headId;
+    /**
+     * 商品名字
+     */
+    @NotEmpty(message = "商品名字不能为空")
+    private String spuName;
+    /**
+     * 商品图片
+     */
+    @NotEmpty(message = "商品图片不能为空")
+    private String picUrl;
+    /**
+     * 拼团商品单价
+     */
+    @NotNull(message = "拼团商品单价不能为空")
+    private Integer combinationPrice;
+    /**
+     * 用户昵称
+     */
+    @NotEmpty(message = "用户昵称不能为空")
+    private String nickname;
+    /**
+     * 用户头像
+     */
+    @NotEmpty(message = "用户头像不能为空")
+    private String avatar;
+    /**
+     * 开团状态:正在开团 拼团成功 拼团失败 TODO 等待支付
+     */
+    @NotNull(message = "开团状态不能为空")
+    private Integer status;
+
+}

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

@@ -65,4 +65,5 @@ public interface ErrorCodeConstants {
     ErrorCode COMBINATION_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013010001, "存在商品参加了其它拼团活动");
     ErrorCode COMBINATION_ACTIVITY_STATUS_DISABLE = new ErrorCode(1013010002, "拼团活动已关闭不能修改");
     ErrorCode COMBINATION_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013010003, "拼团活动未关闭或未结束,不能删除");
+    ErrorCode COMBINATION_RECORD_NOT_EXISTS = new ErrorCode(1013010004, "拼团不存在");
 }

+ 38 - 0
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/combination/CombinationRecordStatusEnum.java

@@ -0,0 +1,38 @@
+package cn.iocoder.yudao.module.promotion.enums.combination;
+
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * 拼团状态枚举
+ *
+ * @author HUIHUI
+ */
+@AllArgsConstructor
+@Getter
+public enum CombinationRecordStatusEnum implements IntArrayValuable {
+    NOT_PAY(0, "未付款"),
+    ONGOING(1, "进行中"),
+    SUCCESS(2, "拼团成功"),
+    FAILED(3, "拼团失败");
+
+    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CombinationRecordStatusEnum::getStatus).toArray();
+
+    /**
+     * 状态值
+     */
+    private final Integer status;
+    /**
+     * 状态名
+     */
+    private final String name;
+
+    @Override
+    public int[] array() {
+        return ARRAYS;
+    }
+
+}

+ 36 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApiImpl.java

@@ -0,0 +1,36 @@
+package cn.iocoder.yudao.module.promotion.api.combination;
+
+import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO;
+import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.time.LocalDateTime;
+
+/**
+ * 拼团活动 API 实现类
+ *
+ * @author HUIHUI
+ */
+@Service
+public class CombinationApiImpl implements CombinationApi {
+    @Resource
+    private CombinationActivityService activityService;
+
+    @Override
+    public void createRecord(CombinationRecordReqDTO reqDTO) {
+        activityService.createRecord(reqDTO);
+    }
+
+    @Override
+    public void updateRecordStatus(Long userId, Long orderId, Integer status) {
+        activityService.updateRecordStatusByUserIdAndOrderId(userId, orderId, status);
+    }
+
+    @Override
+    public void updateRecordStatusAndStartTime(Long userId, Long orderId, Integer status) {
+        activityService.updateRecordStatusAndStartTimeByUserIdAndOrderId(userId, orderId, status, LocalDateTime.now());
+    }
+
+
+}

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

@@ -37,9 +37,9 @@ public class CombinationActivityBaseVO {
     @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
     private LocalDateTime[] activityTime;
 
-    @Schema(description = "参与人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "25222")
-    @NotNull(message = "参与人数不能为空")
-    private Integer orderUserCount;
+    @Schema(description = "开团人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "25222")
+    @NotNull(message = "开团人数不能为空")
+    private Integer userSize;
 
     @Schema(description = "限制时长(小时)", requiredMode = Schema.RequiredMode.REQUIRED)
     @NotNull(message = "限制时长(小时)不能为空")

+ 1 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityExcelVO.java

@@ -37,7 +37,7 @@ public class CombinationActivityExcelVO {
     @ExcelProperty("结束时间")
     private LocalDateTime endTime;
 
-    @ExcelProperty("购买人数")
+    @ExcelProperty("开团人数")
     private Integer userSize;
 
     @ExcelProperty("开团组数")

+ 1 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityExportReqVO.java

@@ -32,7 +32,7 @@ public class CombinationActivityExportReqVO {
     @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
     private LocalDateTime[] endTime;
 
-    @Schema(description = "购买人数")
+    @Schema(description = "开团人数")
     private Integer userSize;
 
     @Schema(description = "开团组数")

+ 1 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityPageReqVO.java

@@ -37,7 +37,7 @@ public class CombinationActivityPageReqVO extends PageParam {
     @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
     private LocalDateTime[] endTime;
 
-    @Schema(description = "购买人数")
+    @Schema(description = "开团人数")
     private Integer userSize;
 
     @Schema(description = "开团组数")

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

@@ -29,8 +29,8 @@ public class CombinationActivityRespVO extends CombinationActivityBaseVO {
     @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
     private LocalDateTime createTime;
 
-    @Schema(description = "购买人数", requiredMode = Schema.RequiredMode.REQUIRED)
-    @NotNull(message = "购买人数不能为空")
+    @Schema(description = "开团人数", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull(message = "开团人数不能为空")
     private Integer userSize;
 
     @Schema(description = "开团组数", requiredMode = Schema.RequiredMode.REQUIRED)
@@ -46,7 +46,7 @@ public class CombinationActivityRespVO extends CombinationActivityBaseVO {
     private Integer virtualGroup;
 
     @Schema(description = "活动状态:0开启 1关闭", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
-    @NotNull(message = "活动状态:0开启 1关闭不能为空")
+    @NotNull(message = "活动状态不能为空")
     private Integer status;
 
     @Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED)

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

@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
+import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO;
 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;
 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityExcelVO;
 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityRespVO;
@@ -13,6 +14,7 @@ import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product
 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductUpdateReqVO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationActivityDO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationProductDO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationRecordDO;
 import org.mapstruct.Mapper;
 import org.mapstruct.Mapping;
 import org.mapstruct.Mappings;
@@ -121,4 +123,6 @@ public interface CombinationActivityConvert {
         return list;
     }
 
+    CombinationRecordDO convert(CombinationRecordReqDTO reqDTO);
+
 }

+ 1 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/combinationactivity/CombinationActivityDO.java

@@ -54,7 +54,7 @@ public class CombinationActivityDO extends BaseDO {
      */
     private LocalDateTime endTime;
     /**
-     * 购买人数
+     * 几人团
      */
     private Integer userSize;
     /**

+ 105 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/combinationactivity/CombinationRecordDO.java

@@ -0,0 +1,105 @@
+package cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.time.LocalDateTime;
+
+/**
+ * 拼团记录 DO
+ *
+ * @author HUIHUI
+ */
+@TableName("promotion_combination_record")
+@KeySequence("promotion_combination_record_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class CombinationRecordDO extends BaseDO {
+
+    @TableId
+    private Long id;
+    /**
+     * 拼团活动编号
+     */
+    private Long activityId;
+    /**
+     * spu 编号
+     */
+    private Long spuId;
+    /**
+     * sku 编号
+     */
+    private Long skuId;
+    /**
+     * 用户编号
+     */
+    private Long userId;
+    /**
+     * 订单编号
+     */
+    private Long orderId;
+    /**
+     * 团长编号
+     * <p>
+     * 关联 {@link CombinationRecordDO#getUserId()}
+     */
+    private Long headId;
+    /**
+     * 商品名字
+     */
+    private String spuName;
+    /**
+     * 商品图片
+     */
+    private String picUrl;
+    /**
+     * 拼团商品单价
+     */
+    private Integer combinationPrice;
+    /**
+     * 用户昵称
+     */
+    private String nickname;
+    /**
+     * 用户头像
+     */
+    private String avatar;
+    /**
+     * 开团状态: 正在开团 拼团成功 拼团失败
+     */
+    private Integer status;
+    /**
+     * 是否虚拟成团
+     */
+    private Boolean virtualGroup;
+    /**
+     * 过期时间,单位:小时
+     * 关联关联 {@link CombinationActivityDO#getLimitDuration()}
+     */
+    private Integer expireTime;
+    /**
+     * 开始时间 (订单付款后开始的时间)
+     */
+    private LocalDateTime startTime;
+    /**
+     * 结束时间(成团时间/失败时间)
+     */
+    private LocalDateTime endTime;
+    /**
+     * 开团需要人数
+     * 关联 {@link CombinationActivityDO#getUserSize()}
+     */
+    private Integer userSize;
+    /**
+     * 已加入拼团人数
+     */
+    private Integer userCount;
+
+}

+ 19 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/combinationactivity/CombinationRecordMapper.java

@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity;
+
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationRecordDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 拼团记录 Mapper
+ *
+ * @author HUIHUI
+ */
+@Mapper
+public interface CombinationRecordMapper extends BaseMapperX<CombinationRecordDO> {
+
+    default CombinationRecordDO selectRecord(Long userId, Long orderId) {
+        return selectOne(CombinationRecordDO::getUserId, userId, CombinationRecordDO::getOrderId, orderId);
+    }
+
+}

+ 29 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityService.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.promotion.service.combination;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO;
 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;
 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityExportReqVO;
 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO;
@@ -9,6 +10,7 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationa
 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationProductDO;
 
 import javax.validation.Valid;
+import java.time.LocalDateTime;
 import java.util.Collection;
 import java.util.List;
 
@@ -80,4 +82,31 @@ public interface CombinationActivityService {
      * @return 拼团活动的商品列表
      */
     List<CombinationProductDO> getProductsByActivityIds(Collection<Long> ids);
+
+    /**
+     * 更新拼团状态
+     *
+     * @param userId  用户编号
+     * @param orderId 订单编号
+     * @param status  状态
+     */
+    void updateRecordStatusByUserIdAndOrderId(Long userId, Long orderId, Integer status);
+
+    /**
+     * 更新拼团状态和开始时间
+     *
+     * @param userId    用户编号
+     * @param orderId   订单编号
+     * @param status    状态
+     * @param startTime 开始时间
+     */
+    void updateRecordStatusAndStartTimeByUserIdAndOrderId(Long userId, Long orderId, Integer status, LocalDateTime startTime);
+
+    /**
+     * 创建拼团记录
+     *
+     * @param reqDTO 创建信息
+     */
+    void createRecord(CombinationRecordReqDTO reqDTO);
+
 }

+ 48 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityServiceImpl.java

@@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
 import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
+import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO;
 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;
 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityExportReqVO;
 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO;
@@ -19,12 +20,15 @@ import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product
 import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationActivityDO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationProductDO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationRecordDO;
 import cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity.CombinationActivityMapper;
 import cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity.CombinationProductMapper;
+import cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity.CombinationRecordMapper;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
 import javax.annotation.Resource;
+import java.time.LocalDateTime;
 import java.util.Collection;
 import java.util.List;
 import java.util.Set;
@@ -46,6 +50,8 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
     @Resource
     private CombinationActivityMapper combinationActivityMapper;
     @Resource
+    private CombinationRecordMapper recordMapper;
+    @Resource
     private CombinationProductMapper combinationProductMapper;
     @Resource
     private ProductSpuApi productSpuApi;
@@ -66,7 +72,6 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
         // 插入拼团活动
         CombinationActivityDO activityDO = CombinationActivityConvert.INSTANCE.convert(createReqVO);
         // TODO 营销相关属性初始化
-        activityDO.setUserSize(0);
         activityDO.setTotalNum(0);
         activityDO.setSuccessNum(0);
         activityDO.setOrderUserCount(0);
@@ -201,4 +206,46 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
         return combinationProductMapper.selectListByActivityIds(ids);
     }
 
+    @Override
+    public void updateRecordStatusByUserIdAndOrderId(Long userId, Long orderId, Integer status) {
+        // 校验拼团是否存在
+        CombinationRecordDO recordDO = validateCombinationRecord(userId, orderId);
+
+        // 更新状态
+        recordDO.setStatus(status);
+        recordMapper.updateById(recordDO);
+    }
+
+    @Override
+    public void updateRecordStatusAndStartTimeByUserIdAndOrderId(Long userId, Long orderId, Integer status, LocalDateTime startTime) {
+        CombinationRecordDO recordDO = validateCombinationRecord(userId, orderId);
+
+        // 更新状态
+        recordDO.setStatus(status);
+        // 更新开始时间
+        recordDO.setStartTime(startTime);
+        recordMapper.updateById(recordDO);
+    }
+
+    private CombinationRecordDO validateCombinationRecord(Long userId, Long orderId) {
+        // 校验拼团是否存在
+        CombinationRecordDO recordDO = recordMapper.selectRecord(userId, orderId);
+        if (recordDO == null) {
+            throw exception(COMBINATION_RECORD_NOT_EXISTS);
+        }
+        return recordDO;
+    }
+
+    @Override
+    public void createRecord(CombinationRecordReqDTO reqDTO) {
+        // 校验拼团活动
+        CombinationActivityDO activityDO = validateCombinationActivityExists(reqDTO.getActivityId());
+
+        CombinationRecordDO recordDO = CombinationActivityConvert.INSTANCE.convert(reqDTO);
+        recordDO.setVirtualGroup(false);
+        recordDO.setExpireTime(activityDO.getLimitDuration());
+        recordDO.setUserSize(activityDO.getUserSize());
+        recordMapper.insert(recordDO);
+    }
+
 }

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

@@ -29,6 +29,7 @@ public interface ErrorCodeConstants {
     ErrorCode ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED = new ErrorCode(1011000018, "交易订单收货失败,订单不是【待收货】状态");
     ErrorCode ORDER_COMMENT_FAIL_STATUS_NOT_COMPLETED = new ErrorCode(1011000019, "创建交易订单项的评价失败,订单不是【已完成】状态");
     ErrorCode ORDER_COMMENT_STATUS_NOT_FALSE = new ErrorCode(1011000019, "创建交易订单项的评价失败,订单已评价");
+    ErrorCode ORDER_DELIVERY_FAIL_REFUND_STATUS_NOT_NONE = new ErrorCode(1011000017, "交易订单发货失败,订单已退款或部分退款");
 
     // ==========  After Sale 模块 1011000100 ==========
     ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1011000100, "售后单不存在");

+ 12 - 5
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderDeliveryReqVO.java

@@ -1,9 +1,10 @@
 package cn.iocoder.yudao.module.trade.controller.admin.order.vo;
 
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
-import javax.validation.constraints.NotEmpty;
 import javax.validation.constraints.NotNull;
 
 @Schema(description = "管理后台 - 订单发货 Request VO")
@@ -14,12 +15,18 @@ public class TradeOrderDeliveryReqVO {
     @NotNull(message = "订单编号不能为空")
     private Long id;
 
-    @Schema(description = "发货物流公司编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
-    @NotNull(message = "发货物流公司编号不能为空")
+    @Schema(description = "发货类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
+    @InEnum(DeliveryTypeEnum.class)
+    @NotNull(message = "发货类型不能为空")
+    private Integer type;
+
+    @Schema(description = "发货物流公司编号", example = "1")
     private Long logisticsId;
 
-    @Schema(description = "发货物流单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "SF123456789")
-    @NotEmpty(message = "发货物流单号不能为空")
+    @Schema(description = "发货物流单号", example = "SF123456789")
     private String logisticsNo;
 
+    // =============== 同城配送  ================
+    // TODO
+
 }

+ 0 - 6
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementReqVO.java

@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.trade.controller.app.order.vo;
 import cn.iocoder.yudao.framework.common.validation.InEnum;
 import cn.iocoder.yudao.framework.common.validation.Mobile;
 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
-import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
@@ -19,11 +18,6 @@ import java.util.List;
 @Data
 public class AppTradeOrderSettlementReqVO {
 
-    @NotNull(message = "交易类型不能为空")
-    @InEnum(value = TradeOrderTypeEnum.class, message = "交易类型必须是 {value}")
-    @Deprecated // TODO 芋艿:后续干掉这个字段,对于前端不需要关注这个
-    private Integer type = 1;
-
     @Schema(description = "商品项数组", requiredMode = Schema.RequiredMode.REQUIRED)
     @NotEmpty(message = "商品不能为空")
     private List<Item> items;

+ 30 - 13
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java

@@ -13,6 +13,7 @@ import cn.iocoder.yudao.module.pay.enums.DictTypeConstants;
 import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
 import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
+import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO;
 import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO;
 import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;
 import cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO;
@@ -52,7 +53,6 @@ public interface TradeOrderConvert {
             @Mapping(source = "createReqVO.couponId", target = "couponId"),
             @Mapping(target = "remark", ignore = true),
             @Mapping(source = "createReqVO.remark", target = "userRemark"),
-            @Mapping(source = "createReqVO.type", target = "type"),
             @Mapping(source = "calculateRespBO.price.totalPrice", target = "totalPrice"),
             @Mapping(source = "calculateRespBO.price.discountPrice", target = "discountPrice"),
             @Mapping(source = "calculateRespBO.price.deliveryPrice", target = "deliveryPrice"),
@@ -123,7 +123,7 @@ public interface TradeOrderConvert {
     // TODO 芋艿:可简化
     default PageResult<TradeOrderPageItemRespVO> convertPage(PageResult<TradeOrderDO> pageResult, List<TradeOrderItemDO> orderItems,
                                                              List<ProductPropertyValueDetailRespDTO> propertyValueDetails,
-                                                             Map<Long,MemberUserRespDTO> memberUserRespDTOMap) {
+                                                             Map<Long, MemberUserRespDTO> memberUserRespDTOMap) {
         Map<Long, List<TradeOrderItemDO>> orderItemMap = convertMultiMap(orderItems, TradeOrderItemDO::getOrderId);
         Map<Long, ProductPropertyValueDetailRespDTO> propertyValueDetailMap = convertMap(propertyValueDetails, ProductPropertyValueDetailRespDTO::getValueId);
         // 转化 List
@@ -267,22 +267,23 @@ public interface TradeOrderConvert {
 
     AppTradeOrderItemRespVO convert03(TradeOrderItemDO bean);
 
-    @Mapping(target = "skuId", source = "tradeOrderItemDO.skuId")
-    @Mapping(target = "orderId", source = "tradeOrderItemDO.orderId")
-    @Mapping(target = "orderItemId", source = "tradeOrderItemDO.id")
-    @Mapping(target = "descriptionScores", source = "createReqVO.descriptionScores")
-    @Mapping(target = "benefitScores", source = "createReqVO.benefitScores")
-    @Mapping(target = "content", source = "createReqVO.content")
-    @Mapping(target = "picUrls", source = "createReqVO.picUrls")
-    @Mapping(target = "anonymous", source = "createReqVO.anonymous")
-    @Mapping(target = "userId", source = "tradeOrderItemDO.userId")
+    @Mappings({
+            @Mapping(target = "skuId", source = "tradeOrderItemDO.skuId"),
+            @Mapping(target = "orderId", source = "tradeOrderItemDO.orderId"),
+            @Mapping(target = "orderItemId", source = "tradeOrderItemDO.id"),
+            @Mapping(target = "descriptionScores", source = "createReqVO.descriptionScores"),
+            @Mapping(target = "benefitScores", source = "createReqVO.benefitScores"),
+            @Mapping(target = "content", source = "createReqVO.content"),
+            @Mapping(target = "picUrls", source = "createReqVO.picUrls"),
+            @Mapping(target = "anonymous", source = "createReqVO.anonymous"),
+            @Mapping(target = "userId", source = "tradeOrderItemDO.userId")
+    })
     ProductCommentCreateReqDTO convert04(AppTradeOrderItemCommentCreateReqVO createReqVO, TradeOrderItemDO tradeOrderItemDO);
 
     default TradePriceCalculateReqBO convert(Long userId, AppTradeOrderSettlementReqVO settlementReqVO,
                                              List<TradeCartDO> cartList) {
         TradePriceCalculateReqBO reqBO = new TradePriceCalculateReqBO();
-        reqBO.setUserId(userId).setType(settlementReqVO.getType())
-                .setCouponId(settlementReqVO.getCouponId()).setAddressId(settlementReqVO.getAddressId())
+        reqBO.setUserId(userId).setCouponId(settlementReqVO.getCouponId()).setAddressId(settlementReqVO.getAddressId())
                 .setItems(new ArrayList<>(settlementReqVO.getItems().size()));
         // 商品项的构建
         Map<Long, TradeCartDO> cartMap = convertMap(cartList, TradeCartDO::getId);
@@ -317,4 +318,20 @@ public interface TradeOrderConvert {
 
     AppTradeOrderSettlementRespVO convert0(TradePriceCalculateRespBO calculate, AddressRespDTO address);
 
+    @Mappings({
+            @Mapping(target = "activityId", source = "createReqVO.combinationActivityId"),
+            @Mapping(target = "spuId", source = "orderItem.spuId"),
+            @Mapping(target = "skuId", source = "orderItem.skuId"),
+            @Mapping(target = "userId", source = "order.userId"),
+            @Mapping(target = "orderId", source = "order.id"),
+            @Mapping(target = "headId", source = "createReqVO.combinationHeadId"),
+            @Mapping(target = "spuName", source = "orderItem.spuName"),
+            @Mapping(target = "picUrl", source = "orderItem.picUrl"),
+            @Mapping(target = "combinationPrice", source = "orderItem.payPrice"),
+            @Mapping(target = "nickname", source = "user.nickname"),
+            @Mapping(target = "avatar", source = "user.avatar"),
+            @Mapping(target = "status", ignore = true)
+    })
+    CombinationRecordReqDTO convert(TradeOrderDO order, TradeOrderItemDO orderItem, AppTradeOrderCreateReqVO createReqVO, MemberUserRespDTO user);
+
 }

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

@@ -21,8 +21,10 @@ import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi;
 import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
+import cn.iocoder.yudao.module.promotion.api.combination.CombinationApi;
 import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO;
+import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
 import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
 import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
@@ -103,6 +105,8 @@ public class TradeOrderServiceImpl implements TradeOrderService {
     @Resource
     private TradeOrderProperties tradeOrderProperties;
 
+    @Resource
+    private CombinationApi combinationApi;
     // =================== Order ===================
 
     @Override
@@ -158,7 +162,6 @@ public class TradeOrderServiceImpl implements TradeOrderService {
     public TradeOrderDO createOrder(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO) {
         // 1. 用户收件地址的校验
         AddressRespDTO address = validateAddress(userId, createReqVO.getAddressId());
-
         // 2. 价格计算
         TradePriceCalculateRespBO calculateRespBO = calculatePrice(userId, createReqVO);
 
@@ -166,9 +169,17 @@ public class TradeOrderServiceImpl implements TradeOrderService {
         TradeOrderDO order = createTradeOrder(userId, userIp, createReqVO, calculateRespBO, address);
         // 3.2 插入 TradeOrderItemDO 订单项
         List<TradeOrderItemDO> orderItems = createTradeOrderItems(order, calculateRespBO);
-
         // 订单创建完后的逻辑
         afterCreateTradeOrder(userId, createReqVO, order, orderItems, calculateRespBO);
+        // 3.3 校验订单类型
+        // 拼团
+        if (ObjectUtil.equal(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
+            MemberUserRespDTO user = memberUserApi.getUser(userId);
+            // TODO 拼团一次应该只能选择一种规格的商品
+            combinationApi.createRecord(TradeOrderConvert.INSTANCE.convert(order, orderItems.get(0), createReqVO, user)
+                    .setStatus(CombinationRecordStatusEnum.NOT_PAY.getStatus()));
+        }
+
         // TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来!
         return order;
     }
@@ -188,12 +199,29 @@ public class TradeOrderServiceImpl implements TradeOrderService {
         return address;
     }
 
+    /**
+     * 校验活动返回订单类型
+     *
+     * @param createReqVO 请求参数
+     * @return 订单类型
+     */
+    private Integer validateActivity(AppTradeOrderCreateReqVO createReqVO) {
+        if (createReqVO.getSeckillActivityId() != null) {
+            return TradeOrderTypeEnum.SECKILL.getType();
+        }
+        if (createReqVO.getCombinationActivityId() != null) {
+            return TradeOrderTypeEnum.COMBINATION.getType();
+        }
+        // TODO 砍价敬请期待
+        return TradeOrderTypeEnum.NORMAL.getType();
+    }
+
     private TradeOrderDO createTradeOrder(Long userId, String clientIp, AppTradeOrderCreateReqVO createReqVO,
                                           TradePriceCalculateRespBO calculateRespBO, AddressRespDTO address) {
         TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(userId, clientIp, createReqVO, calculateRespBO, address);
+        order.setType(validateActivity(createReqVO));
         order.setNo(IdUtil.getSnowflakeNextId() + ""); // TODO @LeeYan9: 思考下, 怎么生成好点哈; 这个是会展示给用户的;
         order.setStatus(TradeOrderStatusEnum.UNPAID.getStatus());
-        order.setType(TradeOrderTypeEnum.NORMAL.getType());
         order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus());
         order.setProductCount(getSumValue(calculateRespBO.getItems(), TradePriceCalculateRespBO.OrderItem::getCount, Integer::sum));
         order.setTerminal(TerminalEnum.H5.getTerminal()); // todo 数据来源?
@@ -270,7 +298,12 @@ public class TradeOrderServiceImpl implements TradeOrderService {
         if (updateCount == 0) {
             throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
         }
-
+        // 校验活动
+        // 1、拼团活动
+        if (ObjectUtil.equal(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
+            // 更新拼团状态 TODO puhui999:订单支付失败或订单过期删除这条拼团记录
+            combinationApi.updateRecordStatusAndStartTime(order.getUserId(), order.getId(), CombinationRecordStatusEnum.ONGOING.getStatus());
+        }
         // TODO 芋艿:发送订单变化的消息
 
         // TODO 芋艿:发送站内信
@@ -334,12 +367,12 @@ public class TradeOrderServiceImpl implements TradeOrderService {
         return new KeyValue<>(order, payOrder);
     }
 
-    // TODO 芋艿:如果无需发货,需要怎么存储?
+    // TODO 芋艿:如果无需发货,需要怎么存储? fix:更改订单发货类型为无需发货更改发货类型为等待自提,等待用户自提后更改订单状态为已完成
     @Override
     public void deliveryOrder(Long userId, TradeOrderDeliveryReqVO deliveryReqVO) {
         // 校验并获得交易订单(可发货)
         TradeOrderDO order = validateOrderDeliverable(deliveryReqVO.getId());
-        // TODO 芋艿:logisticsId 校验存在 发货物流公司 fix
+        // TODO 判断发货类型,根据发货类型发货
         DeliveryExpressDO deliveryExpress = deliveryExpressService.getDeliveryExpress(deliveryReqVO.getLogisticsId());
         if (deliveryExpress == null) {
             throw exception(EXPRESS_NOT_EXISTS);
@@ -369,7 +402,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
                         .setTemplateParams(msgMap));
         // TODO 芋艿:OrderLog
 
-        // TODO 设计:like:是否要单独一个 delivery 发货单表???
+        // TODO 设计:like:是否要单独一个 delivery 发货单表??? fix: 确实单独一张发货表就能解决整单发货和单独发货的问题
         // TODO 设计:niu:要不要支持一个订单下,多个 order item 单独发货,类似有赞
         // TODO 设计:lili:是不是发货后,才支持售后?
     }
@@ -393,6 +426,15 @@ public class TradeOrderServiceImpl implements TradeOrderService {
                 || ObjectUtil.notEqual(order.getDeliveryStatus(), TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus())) {
             throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED);
         }
+        // 校验订单是否退款
+        if (ObjectUtil.notEqual(TradeOrderRefundStatusEnum.NONE.getStatus(), order.getRefundStatus())) {
+            throw exception(ORDER_DELIVERY_FAIL_REFUND_STATUS_NOT_NONE);
+        }
+        // 校验订单拼团是否成功
+        if (ObjectUtil.equal(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
+
+        }
+        // TODO puhui999: 校验订单砍价是否成功
         return order;
     }