Commit 259807600a57f4180842427b57bbb3597bc56b57

Authored by puhui999
1 parent d80bf5a3

feat:拼团订单集成

Showing 21 changed files with 498 additions and 42 deletions
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApi.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.api.combination;
  2 +
  3 +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO;
  4 +
  5 +import javax.validation.Valid;
  6 +
  7 +/**
  8 + * 拼团活动 API 接口
  9 + *
  10 + * @author HUIHUI
  11 + */
  12 +public interface CombinationApi {
  13 +
  14 + /**
  15 + * 创建开团记录
  16 + *
  17 + * @param reqDTO 请求 DTO
  18 + */
  19 + void createRecord(@Valid CombinationRecordReqDTO reqDTO);
  20 +
  21 + /**
  22 + * 更新开团记录状态
  23 + *
  24 + * @param userId 用户编号
  25 + * @param orderId 订单编号
  26 + * @param status 状态值
  27 + */
  28 + void updateRecordStatus(Long userId, Long orderId, Integer status);
  29 +
  30 + /**
  31 + * 更新开团记录状态和开始时间
  32 + *
  33 + * @param userId 用户编号
  34 + * @param orderId 订单编号
  35 + * @param status 状态值
  36 + */
  37 + void updateRecordStatusAndStartTime(Long userId, Long orderId, Integer status);
  38 +
  39 +}
... ...
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationRecordReqDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.api.combination.dto;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import javax.validation.constraints.NotEmpty;
  6 +import javax.validation.constraints.NotNull;
  7 +
  8 +/**
  9 + * 拼团记录 Request DTO
  10 + *
  11 + * @author HUIHUI
  12 + */
  13 +@Data
  14 +public class CombinationRecordReqDTO {
  15 +
  16 + /**
  17 + * 拼团活动编号
  18 + */
  19 + @NotNull(message = "拼团活动编号不能为空")
  20 + private Long activityId;
  21 + /**
  22 + * spu 编号
  23 + */
  24 + @NotNull(message = "spu 编号不能为空")
  25 + private Long spuId;
  26 + /**
  27 + * sku 编号
  28 + */
  29 + @NotNull(message = "sku 编号不能为空")
  30 + private Long skuId;
  31 + /**
  32 + * 用户编号
  33 + */
  34 + @NotNull(message = "用户编号不能为空")
  35 + private Long userId;
  36 + /**
  37 + * 订单编号
  38 + */
  39 + @NotNull(message = "订单编号不能为空")
  40 + private Long orderId;
  41 + /**
  42 + * 团长编号
  43 + */
  44 + @NotNull(message = "团长编号不能为空")
  45 + private Long headId;
  46 + /**
  47 + * 商品名字
  48 + */
  49 + @NotEmpty(message = "商品名字不能为空")
  50 + private String spuName;
  51 + /**
  52 + * 商品图片
  53 + */
  54 + @NotEmpty(message = "商品图片不能为空")
  55 + private String picUrl;
  56 + /**
  57 + * 拼团商品单价
  58 + */
  59 + @NotNull(message = "拼团商品单价不能为空")
  60 + private Integer combinationPrice;
  61 + /**
  62 + * 用户昵称
  63 + */
  64 + @NotEmpty(message = "用户昵称不能为空")
  65 + private String nickname;
  66 + /**
  67 + * 用户头像
  68 + */
  69 + @NotEmpty(message = "用户头像不能为空")
  70 + private String avatar;
  71 + /**
  72 + * 开团状态:正在开团 拼团成功 拼团失败 TODO 等待支付
  73 + */
  74 + @NotNull(message = "开团状态不能为空")
  75 + private Integer status;
  76 +
  77 +}
... ...
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 {
65 65 ErrorCode COMBINATION_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1013010001, "存在商品参加了其它拼团活动");
66 66 ErrorCode COMBINATION_ACTIVITY_STATUS_DISABLE = new ErrorCode(1013010002, "拼团活动已关闭不能修改");
67 67 ErrorCode COMBINATION_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013010003, "拼团活动未关闭或未结束,不能删除");
  68 + ErrorCode COMBINATION_RECORD_NOT_EXISTS = new ErrorCode(1013010004, "拼团不存在");
68 69 }
... ...
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/combination/CombinationRecordStatusEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.enums.combination;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Getter;
  6 +
  7 +import java.util.Arrays;
  8 +
  9 +/**
  10 + * 拼团状态枚举
  11 + *
  12 + * @author HUIHUI
  13 + */
  14 +@AllArgsConstructor
  15 +@Getter
  16 +public enum CombinationRecordStatusEnum implements IntArrayValuable {
  17 + NOT_PAY(0, "未付款"),
  18 + ONGOING(1, "进行中"),
  19 + SUCCESS(2, "拼团成功"),
  20 + FAILED(3, "拼团失败");
  21 +
  22 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CombinationRecordStatusEnum::getStatus).toArray();
  23 +
  24 + /**
  25 + * 状态值
  26 + */
  27 + private final Integer status;
  28 + /**
  29 + * 状态名
  30 + */
  31 + private final String name;
  32 +
  33 + @Override
  34 + public int[] array() {
  35 + return ARRAYS;
  36 + }
  37 +
  38 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApiImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.api.combination;
  2 +
  3 +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO;
  4 +import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService;
  5 +import org.springframework.stereotype.Service;
  6 +
  7 +import javax.annotation.Resource;
  8 +import java.time.LocalDateTime;
  9 +
  10 +/**
  11 + * 拼团活动 API 实现类
  12 + *
  13 + * @author HUIHUI
  14 + */
  15 +@Service
  16 +public class CombinationApiImpl implements CombinationApi {
  17 + @Resource
  18 + private CombinationActivityService activityService;
  19 +
  20 + @Override
  21 + public void createRecord(CombinationRecordReqDTO reqDTO) {
  22 + activityService.createRecord(reqDTO);
  23 + }
  24 +
  25 + @Override
  26 + public void updateRecordStatus(Long userId, Long orderId, Integer status) {
  27 + activityService.updateRecordStatusByUserIdAndOrderId(userId, orderId, status);
  28 + }
  29 +
  30 + @Override
  31 + public void updateRecordStatusAndStartTime(Long userId, Long orderId, Integer status) {
  32 + activityService.updateRecordStatusAndStartTimeByUserIdAndOrderId(userId, orderId, status, LocalDateTime.now());
  33 + }
  34 +
  35 +
  36 +}
... ...
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 {
37 37 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
38 38 private LocalDateTime[] activityTime;
39 39  
40   - @Schema(description = "参与人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "25222")
41   - @NotNull(message = "参与人数不能为空")
42   - private Integer orderUserCount;
  40 + @Schema(description = "开团人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "25222")
  41 + @NotNull(message = "开团人数不能为空")
  42 + private Integer userSize;
43 43  
44 44 @Schema(description = "限制时长(小时)", requiredMode = Schema.RequiredMode.REQUIRED)
45 45 @NotNull(message = "限制时长(小时)不能为空")
... ...
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 {
37 37 @ExcelProperty("结束时间")
38 38 private LocalDateTime endTime;
39 39  
40   - @ExcelProperty("购买人数")
  40 + @ExcelProperty("开团人数")
41 41 private Integer userSize;
42 42  
43 43 @ExcelProperty("开团组数")
... ...
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 {
32 32 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
33 33 private LocalDateTime[] endTime;
34 34  
35   - @Schema(description = "购买人数")
  35 + @Schema(description = "开团人数")
36 36 private Integer userSize;
37 37  
38 38 @Schema(description = "开团组数")
... ...
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 {
37 37 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
38 38 private LocalDateTime[] endTime;
39 39  
40   - @Schema(description = "购买人数")
  40 + @Schema(description = "开团人数")
41 41 private Integer userSize;
42 42  
43 43 @Schema(description = "开团组数")
... ...
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 {
29 29 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
30 30 private LocalDateTime createTime;
31 31  
32   - @Schema(description = "购买人数", requiredMode = Schema.RequiredMode.REQUIRED)
33   - @NotNull(message = "购买人数不能为空")
  32 + @Schema(description = "开团人数", requiredMode = Schema.RequiredMode.REQUIRED)
  33 + @NotNull(message = "开团人数不能为空")
34 34 private Integer userSize;
35 35  
36 36 @Schema(description = "开团组数", requiredMode = Schema.RequiredMode.REQUIRED)
... ... @@ -46,7 +46,7 @@ public class CombinationActivityRespVO extends CombinationActivityBaseVO {
46 46 private Integer virtualGroup;
47 47  
48 48 @Schema(description = "活动状态:0开启 1关闭", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
49   - @NotNull(message = "活动状态:0开启 1关闭不能为空")
  49 + @NotNull(message = "活动状态不能为空")
50 50 private Integer status;
51 51  
52 52 @Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED)
... ...
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;
4 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 5 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
6 6 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
  7 +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO;
7 8 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;
8 9 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityExcelVO;
9 10 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
13 14 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductUpdateReqVO;
14 15 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationActivityDO;
15 16 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationProductDO;
  17 +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationRecordDO;
16 18 import org.mapstruct.Mapper;
17 19 import org.mapstruct.Mapping;
18 20 import org.mapstruct.Mappings;
... ... @@ -121,4 +123,6 @@ public interface CombinationActivityConvert {
121 123 return list;
122 124 }
123 125  
  126 + CombinationRecordDO convert(CombinationRecordReqDTO reqDTO);
  127 +
124 128 }
... ...
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 {
54 54 */
55 55 private LocalDateTime endTime;
56 56 /**
57   - * 购买人数
  57 + * 几人团
58 58 */
59 59 private Integer userSize;
60 60 /**
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/combinationactivity/CombinationRecordDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import com.baomidou.mybatisplus.annotation.KeySequence;
  5 +import com.baomidou.mybatisplus.annotation.TableId;
  6 +import com.baomidou.mybatisplus.annotation.TableName;
  7 +import lombok.*;
  8 +
  9 +import java.time.LocalDateTime;
  10 +
  11 +/**
  12 + * 拼团记录 DO
  13 + *
  14 + * @author HUIHUI
  15 + */
  16 +@TableName("promotion_combination_record")
  17 +@KeySequence("promotion_combination_record_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  18 +@Data
  19 +@EqualsAndHashCode(callSuper = true)
  20 +@ToString(callSuper = true)
  21 +@Builder
  22 +@NoArgsConstructor
  23 +@AllArgsConstructor
  24 +public class CombinationRecordDO extends BaseDO {
  25 +
  26 + @TableId
  27 + private Long id;
  28 + /**
  29 + * 拼团活动编号
  30 + */
  31 + private Long activityId;
  32 + /**
  33 + * spu 编号
  34 + */
  35 + private Long spuId;
  36 + /**
  37 + * sku 编号
  38 + */
  39 + private Long skuId;
  40 + /**
  41 + * 用户编号
  42 + */
  43 + private Long userId;
  44 + /**
  45 + * 订单编号
  46 + */
  47 + private Long orderId;
  48 + /**
  49 + * 团长编号
  50 + * <p>
  51 + * 关联 {@link CombinationRecordDO#getUserId()}
  52 + */
  53 + private Long headId;
  54 + /**
  55 + * 商品名字
  56 + */
  57 + private String spuName;
  58 + /**
  59 + * 商品图片
  60 + */
  61 + private String picUrl;
  62 + /**
  63 + * 拼团商品单价
  64 + */
  65 + private Integer combinationPrice;
  66 + /**
  67 + * 用户昵称
  68 + */
  69 + private String nickname;
  70 + /**
  71 + * 用户头像
  72 + */
  73 + private String avatar;
  74 + /**
  75 + * 开团状态: 正在开团 拼团成功 拼团失败
  76 + */
  77 + private Integer status;
  78 + /**
  79 + * 是否虚拟成团
  80 + */
  81 + private Boolean virtualGroup;
  82 + /**
  83 + * 过期时间,单位:小时
  84 + * 关联关联 {@link CombinationActivityDO#getLimitDuration()}
  85 + */
  86 + private Integer expireTime;
  87 + /**
  88 + * 开始时间 (订单付款后开始的时间)
  89 + */
  90 + private LocalDateTime startTime;
  91 + /**
  92 + * 结束时间(成团时间/失败时间)
  93 + */
  94 + private LocalDateTime endTime;
  95 + /**
  96 + * 开团需要人数
  97 + * 关联 {@link CombinationActivityDO#getUserSize()}
  98 + */
  99 + private Integer userSize;
  100 + /**
  101 + * 已加入拼团人数
  102 + */
  103 + private Integer userCount;
  104 +
  105 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/combinationactivity/CombinationRecordMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationRecordDO;
  5 +import org.apache.ibatis.annotations.Mapper;
  6 +
  7 +/**
  8 + * 拼团记录 Mapper
  9 + *
  10 + * @author HUIHUI
  11 + */
  12 +@Mapper
  13 +public interface CombinationRecordMapper extends BaseMapperX<CombinationRecordDO> {
  14 +
  15 + default CombinationRecordDO selectRecord(Long userId, Long orderId) {
  16 + return selectOne(CombinationRecordDO::getUserId, userId, CombinationRecordDO::getOrderId, orderId);
  17 + }
  18 +
  19 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityService.java
1 1 package cn.iocoder.yudao.module.promotion.service.combination;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO;
4 5 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;
5 6 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityExportReqVO;
6 7 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
9 10 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationProductDO;
10 11  
11 12 import javax.validation.Valid;
  13 +import java.time.LocalDateTime;
12 14 import java.util.Collection;
13 15 import java.util.List;
14 16  
... ... @@ -80,4 +82,31 @@ public interface CombinationActivityService {
80 82 * @return 拼团活动的商品列表
81 83 */
82 84 List<CombinationProductDO> getProductsByActivityIds(Collection<Long> ids);
  85 +
  86 + /**
  87 + * 更新拼团状态
  88 + *
  89 + * @param userId 用户编号
  90 + * @param orderId 订单编号
  91 + * @param status 状态
  92 + */
  93 + void updateRecordStatusByUserIdAndOrderId(Long userId, Long orderId, Integer status);
  94 +
  95 + /**
  96 + * 更新拼团状态和开始时间
  97 + *
  98 + * @param userId 用户编号
  99 + * @param orderId 订单编号
  100 + * @param status 状态
  101 + * @param startTime 开始时间
  102 + */
  103 + void updateRecordStatusAndStartTimeByUserIdAndOrderId(Long userId, Long orderId, Integer status, LocalDateTime startTime);
  104 +
  105 + /**
  106 + * 创建拼团记录
  107 + *
  108 + * @param reqDTO 创建信息
  109 + */
  110 + void createRecord(CombinationRecordReqDTO reqDTO);
  111 +
83 112 }
... ...
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;
10 10 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
11 11 import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
12 12 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
  13 +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO;
13 14 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;
14 15 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityExportReqVO;
15 16 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
19 20 import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
20 21 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationActivityDO;
21 22 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationProductDO;
  23 +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationRecordDO;
22 24 import cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity.CombinationActivityMapper;
23 25 import cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity.CombinationProductMapper;
  26 +import cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity.CombinationRecordMapper;
24 27 import org.springframework.stereotype.Service;
25 28 import org.springframework.validation.annotation.Validated;
26 29  
27 30 import javax.annotation.Resource;
  31 +import java.time.LocalDateTime;
28 32 import java.util.Collection;
29 33 import java.util.List;
30 34 import java.util.Set;
... ... @@ -46,6 +50,8 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
46 50 @Resource
47 51 private CombinationActivityMapper combinationActivityMapper;
48 52 @Resource
  53 + private CombinationRecordMapper recordMapper;
  54 + @Resource
49 55 private CombinationProductMapper combinationProductMapper;
50 56 @Resource
51 57 private ProductSpuApi productSpuApi;
... ... @@ -66,7 +72,6 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
66 72 // 插入拼团活动
67 73 CombinationActivityDO activityDO = CombinationActivityConvert.INSTANCE.convert(createReqVO);
68 74 // TODO 营销相关属性初始化
69   - activityDO.setUserSize(0);
70 75 activityDO.setTotalNum(0);
71 76 activityDO.setSuccessNum(0);
72 77 activityDO.setOrderUserCount(0);
... ... @@ -201,4 +206,46 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
201 206 return combinationProductMapper.selectListByActivityIds(ids);
202 207 }
203 208  
  209 + @Override
  210 + public void updateRecordStatusByUserIdAndOrderId(Long userId, Long orderId, Integer status) {
  211 + // 校验拼团是否存在
  212 + CombinationRecordDO recordDO = validateCombinationRecord(userId, orderId);
  213 +
  214 + // 更新状态
  215 + recordDO.setStatus(status);
  216 + recordMapper.updateById(recordDO);
  217 + }
  218 +
  219 + @Override
  220 + public void updateRecordStatusAndStartTimeByUserIdAndOrderId(Long userId, Long orderId, Integer status, LocalDateTime startTime) {
  221 + CombinationRecordDO recordDO = validateCombinationRecord(userId, orderId);
  222 +
  223 + // 更新状态
  224 + recordDO.setStatus(status);
  225 + // 更新开始时间
  226 + recordDO.setStartTime(startTime);
  227 + recordMapper.updateById(recordDO);
  228 + }
  229 +
  230 + private CombinationRecordDO validateCombinationRecord(Long userId, Long orderId) {
  231 + // 校验拼团是否存在
  232 + CombinationRecordDO recordDO = recordMapper.selectRecord(userId, orderId);
  233 + if (recordDO == null) {
  234 + throw exception(COMBINATION_RECORD_NOT_EXISTS);
  235 + }
  236 + return recordDO;
  237 + }
  238 +
  239 + @Override
  240 + public void createRecord(CombinationRecordReqDTO reqDTO) {
  241 + // 校验拼团活动
  242 + CombinationActivityDO activityDO = validateCombinationActivityExists(reqDTO.getActivityId());
  243 +
  244 + CombinationRecordDO recordDO = CombinationActivityConvert.INSTANCE.convert(reqDTO);
  245 + recordDO.setVirtualGroup(false);
  246 + recordDO.setExpireTime(activityDO.getLimitDuration());
  247 + recordDO.setUserSize(activityDO.getUserSize());
  248 + recordMapper.insert(recordDO);
  249 + }
  250 +
204 251 }
... ...
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 {
29 29 ErrorCode ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED = new ErrorCode(1011000018, "交易订单收货失败,订单不是【待收货】状态");
30 30 ErrorCode ORDER_COMMENT_FAIL_STATUS_NOT_COMPLETED = new ErrorCode(1011000019, "创建交易订单项的评价失败,订单不是【已完成】状态");
31 31 ErrorCode ORDER_COMMENT_STATUS_NOT_FALSE = new ErrorCode(1011000019, "创建交易订单项的评价失败,订单已评价");
  32 + ErrorCode ORDER_DELIVERY_FAIL_REFUND_STATUS_NOT_NONE = new ErrorCode(1011000017, "交易订单发货失败,订单已退款或部分退款");
32 33  
33 34 // ========== After Sale 模块 1011000100 ==========
34 35 ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1011000100, "售后单不存在");
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderDeliveryReqVO.java
1 1 package cn.iocoder.yudao.module.trade.controller.admin.order.vo;
2 2  
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
3 5 import io.swagger.v3.oas.annotations.media.Schema;
4 6 import lombok.Data;
5 7  
6   -import javax.validation.constraints.NotEmpty;
7 8 import javax.validation.constraints.NotNull;
8 9  
9 10 @Schema(description = "管理后台 - 订单发货 Request VO")
... ... @@ -14,12 +15,18 @@ public class TradeOrderDeliveryReqVO {
14 15 @NotNull(message = "订单编号不能为空")
15 16 private Long id;
16 17  
17   - @Schema(description = "发货物流公司编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
18   - @NotNull(message = "发货物流公司编号不能为空")
  18 + @Schema(description = "发货类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
  19 + @InEnum(DeliveryTypeEnum.class)
  20 + @NotNull(message = "发货类型不能为空")
  21 + private Integer type;
  22 +
  23 + @Schema(description = "发货物流公司编号", example = "1")
19 24 private Long logisticsId;
20 25  
21   - @Schema(description = "发货物流单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "SF123456789")
22   - @NotEmpty(message = "发货物流单号不能为空")
  26 + @Schema(description = "发货物流单号", example = "SF123456789")
23 27 private String logisticsNo;
24 28  
  29 + // =============== 同城配送 ================
  30 + // TODO
  31 +
25 32 }
... ...
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;
3 3 import cn.iocoder.yudao.framework.common.validation.InEnum;
4 4 import cn.iocoder.yudao.framework.common.validation.Mobile;
5 5 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
6   -import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
7 6 import com.fasterxml.jackson.annotation.JsonIgnore;
8 7 import io.swagger.v3.oas.annotations.media.Schema;
9 8 import lombok.Data;
... ... @@ -19,11 +18,6 @@ import java.util.List;
19 18 @Data
20 19 public class AppTradeOrderSettlementReqVO {
21 20  
22   - @NotNull(message = "交易类型不能为空")
23   - @InEnum(value = TradeOrderTypeEnum.class, message = "交易类型必须是 {value}")
24   - @Deprecated // TODO 芋艿:后续干掉这个字段,对于前端不需要关注这个
25   - private Integer type = 1;
26   -
27 21 @Schema(description = "商品项数组", requiredMode = Schema.RequiredMode.REQUIRED)
28 22 @NotEmpty(message = "商品不能为空")
29 23 private List<Item> items;
... ...
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;
13 13 import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
14 14 import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
15 15 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
  16 +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO;
16 17 import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO;
17 18 import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;
18 19 import cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO;
... ... @@ -52,7 +53,6 @@ public interface TradeOrderConvert {
52 53 @Mapping(source = "createReqVO.couponId", target = "couponId"),
53 54 @Mapping(target = "remark", ignore = true),
54 55 @Mapping(source = "createReqVO.remark", target = "userRemark"),
55   - @Mapping(source = "createReqVO.type", target = "type"),
56 56 @Mapping(source = "calculateRespBO.price.totalPrice", target = "totalPrice"),
57 57 @Mapping(source = "calculateRespBO.price.discountPrice", target = "discountPrice"),
58 58 @Mapping(source = "calculateRespBO.price.deliveryPrice", target = "deliveryPrice"),
... ... @@ -123,7 +123,7 @@ public interface TradeOrderConvert {
123 123 // TODO 芋艿:可简化
124 124 default PageResult<TradeOrderPageItemRespVO> convertPage(PageResult<TradeOrderDO> pageResult, List<TradeOrderItemDO> orderItems,
125 125 List<ProductPropertyValueDetailRespDTO> propertyValueDetails,
126   - Map<Long,MemberUserRespDTO> memberUserRespDTOMap) {
  126 + Map<Long, MemberUserRespDTO> memberUserRespDTOMap) {
127 127 Map<Long, List<TradeOrderItemDO>> orderItemMap = convertMultiMap(orderItems, TradeOrderItemDO::getOrderId);
128 128 Map<Long, ProductPropertyValueDetailRespDTO> propertyValueDetailMap = convertMap(propertyValueDetails, ProductPropertyValueDetailRespDTO::getValueId);
129 129 // 转化 List
... ... @@ -267,22 +267,23 @@ public interface TradeOrderConvert {
267 267  
268 268 AppTradeOrderItemRespVO convert03(TradeOrderItemDO bean);
269 269  
270   - @Mapping(target = "skuId", source = "tradeOrderItemDO.skuId")
271   - @Mapping(target = "orderId", source = "tradeOrderItemDO.orderId")
272   - @Mapping(target = "orderItemId", source = "tradeOrderItemDO.id")
273   - @Mapping(target = "descriptionScores", source = "createReqVO.descriptionScores")
274   - @Mapping(target = "benefitScores", source = "createReqVO.benefitScores")
275   - @Mapping(target = "content", source = "createReqVO.content")
276   - @Mapping(target = "picUrls", source = "createReqVO.picUrls")
277   - @Mapping(target = "anonymous", source = "createReqVO.anonymous")
278   - @Mapping(target = "userId", source = "tradeOrderItemDO.userId")
  270 + @Mappings({
  271 + @Mapping(target = "skuId", source = "tradeOrderItemDO.skuId"),
  272 + @Mapping(target = "orderId", source = "tradeOrderItemDO.orderId"),
  273 + @Mapping(target = "orderItemId", source = "tradeOrderItemDO.id"),
  274 + @Mapping(target = "descriptionScores", source = "createReqVO.descriptionScores"),
  275 + @Mapping(target = "benefitScores", source = "createReqVO.benefitScores"),
  276 + @Mapping(target = "content", source = "createReqVO.content"),
  277 + @Mapping(target = "picUrls", source = "createReqVO.picUrls"),
  278 + @Mapping(target = "anonymous", source = "createReqVO.anonymous"),
  279 + @Mapping(target = "userId", source = "tradeOrderItemDO.userId")
  280 + })
279 281 ProductCommentCreateReqDTO convert04(AppTradeOrderItemCommentCreateReqVO createReqVO, TradeOrderItemDO tradeOrderItemDO);
280 282  
281 283 default TradePriceCalculateReqBO convert(Long userId, AppTradeOrderSettlementReqVO settlementReqVO,
282 284 List<TradeCartDO> cartList) {
283 285 TradePriceCalculateReqBO reqBO = new TradePriceCalculateReqBO();
284   - reqBO.setUserId(userId).setType(settlementReqVO.getType())
285   - .setCouponId(settlementReqVO.getCouponId()).setAddressId(settlementReqVO.getAddressId())
  286 + reqBO.setUserId(userId).setCouponId(settlementReqVO.getCouponId()).setAddressId(settlementReqVO.getAddressId())
286 287 .setItems(new ArrayList<>(settlementReqVO.getItems().size()));
287 288 // 商品项的构建
288 289 Map<Long, TradeCartDO> cartMap = convertMap(cartList, TradeCartDO::getId);
... ... @@ -317,4 +318,20 @@ public interface TradeOrderConvert {
317 318  
318 319 AppTradeOrderSettlementRespVO convert0(TradePriceCalculateRespBO calculate, AddressRespDTO address);
319 320  
  321 + @Mappings({
  322 + @Mapping(target = "activityId", source = "createReqVO.combinationActivityId"),
  323 + @Mapping(target = "spuId", source = "orderItem.spuId"),
  324 + @Mapping(target = "skuId", source = "orderItem.skuId"),
  325 + @Mapping(target = "userId", source = "order.userId"),
  326 + @Mapping(target = "orderId", source = "order.id"),
  327 + @Mapping(target = "headId", source = "createReqVO.combinationHeadId"),
  328 + @Mapping(target = "spuName", source = "orderItem.spuName"),
  329 + @Mapping(target = "picUrl", source = "orderItem.picUrl"),
  330 + @Mapping(target = "combinationPrice", source = "orderItem.payPrice"),
  331 + @Mapping(target = "nickname", source = "user.nickname"),
  332 + @Mapping(target = "avatar", source = "user.avatar"),
  333 + @Mapping(target = "status", ignore = true)
  334 + })
  335 + CombinationRecordReqDTO convert(TradeOrderDO order, TradeOrderItemDO orderItem, AppTradeOrderCreateReqVO createReqVO, MemberUserRespDTO user);
  336 +
320 337 }
... ...
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;
21 21 import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
22 22 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
23 23 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
  24 +import cn.iocoder.yudao.module.promotion.api.combination.CombinationApi;
24 25 import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
25 26 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO;
  27 +import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
26 28 import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
27 29 import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
28 30 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
... ... @@ -103,6 +105,8 @@ public class TradeOrderServiceImpl implements TradeOrderService {
103 105 @Resource
104 106 private TradeOrderProperties tradeOrderProperties;
105 107  
  108 + @Resource
  109 + private CombinationApi combinationApi;
106 110 // =================== Order ===================
107 111  
108 112 @Override
... ... @@ -158,7 +162,6 @@ public class TradeOrderServiceImpl implements TradeOrderService {
158 162 public TradeOrderDO createOrder(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO) {
159 163 // 1. 用户收件地址的校验
160 164 AddressRespDTO address = validateAddress(userId, createReqVO.getAddressId());
161   -
162 165 // 2. 价格计算
163 166 TradePriceCalculateRespBO calculateRespBO = calculatePrice(userId, createReqVO);
164 167  
... ... @@ -166,9 +169,17 @@ public class TradeOrderServiceImpl implements TradeOrderService {
166 169 TradeOrderDO order = createTradeOrder(userId, userIp, createReqVO, calculateRespBO, address);
167 170 // 3.2 插入 TradeOrderItemDO 订单项
168 171 List<TradeOrderItemDO> orderItems = createTradeOrderItems(order, calculateRespBO);
169   -
170 172 // 订单创建完后的逻辑
171 173 afterCreateTradeOrder(userId, createReqVO, order, orderItems, calculateRespBO);
  174 + // 3.3 校验订单类型
  175 + // 拼团
  176 + if (ObjectUtil.equal(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
  177 + MemberUserRespDTO user = memberUserApi.getUser(userId);
  178 + // TODO 拼团一次应该只能选择一种规格的商品
  179 + combinationApi.createRecord(TradeOrderConvert.INSTANCE.convert(order, orderItems.get(0), createReqVO, user)
  180 + .setStatus(CombinationRecordStatusEnum.NOT_PAY.getStatus()));
  181 + }
  182 +
172 183 // TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来!
173 184 return order;
174 185 }
... ... @@ -188,12 +199,29 @@ public class TradeOrderServiceImpl implements TradeOrderService {
188 199 return address;
189 200 }
190 201  
  202 + /**
  203 + * 校验活动返回订单类型
  204 + *
  205 + * @param createReqVO 请求参数
  206 + * @return 订单类型
  207 + */
  208 + private Integer validateActivity(AppTradeOrderCreateReqVO createReqVO) {
  209 + if (createReqVO.getSeckillActivityId() != null) {
  210 + return TradeOrderTypeEnum.SECKILL.getType();
  211 + }
  212 + if (createReqVO.getCombinationActivityId() != null) {
  213 + return TradeOrderTypeEnum.COMBINATION.getType();
  214 + }
  215 + // TODO 砍价敬请期待
  216 + return TradeOrderTypeEnum.NORMAL.getType();
  217 + }
  218 +
191 219 private TradeOrderDO createTradeOrder(Long userId, String clientIp, AppTradeOrderCreateReqVO createReqVO,
192 220 TradePriceCalculateRespBO calculateRespBO, AddressRespDTO address) {
193 221 TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(userId, clientIp, createReqVO, calculateRespBO, address);
  222 + order.setType(validateActivity(createReqVO));
194 223 order.setNo(IdUtil.getSnowflakeNextId() + ""); // TODO @LeeYan9: 思考下, 怎么生成好点哈; 这个是会展示给用户的;
195 224 order.setStatus(TradeOrderStatusEnum.UNPAID.getStatus());
196   - order.setType(TradeOrderTypeEnum.NORMAL.getType());
197 225 order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus());
198 226 order.setProductCount(getSumValue(calculateRespBO.getItems(), TradePriceCalculateRespBO.OrderItem::getCount, Integer::sum));
199 227 order.setTerminal(TerminalEnum.H5.getTerminal()); // todo 数据来源?
... ... @@ -270,7 +298,12 @@ public class TradeOrderServiceImpl implements TradeOrderService {
270 298 if (updateCount == 0) {
271 299 throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
272 300 }
273   -
  301 + // 校验活动
  302 + // 1、拼团活动
  303 + if (ObjectUtil.equal(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
  304 + // 更新拼团状态 TODO puhui999:订单支付失败或订单过期删除这条拼团记录
  305 + combinationApi.updateRecordStatusAndStartTime(order.getUserId(), order.getId(), CombinationRecordStatusEnum.ONGOING.getStatus());
  306 + }
274 307 // TODO 芋艿:发送订单变化的消息
275 308  
276 309 // TODO 芋艿:发送站内信
... ... @@ -334,12 +367,12 @@ public class TradeOrderServiceImpl implements TradeOrderService {
334 367 return new KeyValue<>(order, payOrder);
335 368 }
336 369  
337   - // TODO 芋艿:如果无需发货,需要怎么存储?
  370 + // TODO 芋艿:如果无需发货,需要怎么存储? fix:更改订单发货类型为无需发货更改发货类型为等待自提,等待用户自提后更改订单状态为已完成
338 371 @Override
339 372 public void deliveryOrder(Long userId, TradeOrderDeliveryReqVO deliveryReqVO) {
340 373 // 校验并获得交易订单(可发货)
341 374 TradeOrderDO order = validateOrderDeliverable(deliveryReqVO.getId());
342   - // TODO 芋艿:logisticsId 校验存在 发货物流公司 fix
  375 + // TODO 判断发货类型,根据发货类型发货
343 376 DeliveryExpressDO deliveryExpress = deliveryExpressService.getDeliveryExpress(deliveryReqVO.getLogisticsId());
344 377 if (deliveryExpress == null) {
345 378 throw exception(EXPRESS_NOT_EXISTS);
... ... @@ -369,7 +402,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
369 402 .setTemplateParams(msgMap));
370 403 // TODO 芋艿:OrderLog
371 404  
372   - // TODO 设计:like:是否要单独一个 delivery 发货单表???
  405 + // TODO 设计:like:是否要单独一个 delivery 发货单表??? fix: 确实单独一张发货表就能解决整单发货和单独发货的问题
373 406 // TODO 设计:niu:要不要支持一个订单下,多个 order item 单独发货,类似有赞
374 407 // TODO 设计:lili:是不是发货后,才支持售后?
375 408 }
... ... @@ -393,6 +426,15 @@ public class TradeOrderServiceImpl implements TradeOrderService {
393 426 || ObjectUtil.notEqual(order.getDeliveryStatus(), TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus())) {
394 427 throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED);
395 428 }
  429 + // 校验订单是否退款
  430 + if (ObjectUtil.notEqual(TradeOrderRefundStatusEnum.NONE.getStatus(), order.getRefundStatus())) {
  431 + throw exception(ORDER_DELIVERY_FAIL_REFUND_STATUS_NOT_NONE);
  432 + }
  433 + // 校验订单拼团是否成功
  434 + if (ObjectUtil.equal(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
  435 +
  436 + }
  437 + // TODO puhui999: 校验订单砍价是否成功
396 438 return order;
397 439 }
398 440  
... ...